|  | 
|  | 1 | +import chalk from "chalk"; | 
|  | 2 | +import handlebars from "handlebars"; | 
|  | 3 | +import BaseGenerator from "./BaseGenerator.js"; | 
|  | 4 | +import hbhComparison from "handlebars-helpers/lib/comparison.js"; | 
|  | 5 | + | 
|  | 6 | +export default class extends BaseGenerator { | 
|  | 7 | +  constructor(params) { | 
|  | 8 | +    super(params); | 
|  | 9 | + | 
|  | 10 | +    handlebars.registerHelper("ifNotResource", function (item, options) { | 
|  | 11 | +      if (item === null) { | 
|  | 12 | +        return options.fn(this); | 
|  | 13 | +      } | 
|  | 14 | +      return options.inverse(this); | 
|  | 15 | +    }); | 
|  | 16 | + | 
|  | 17 | +    this.registerTemplates(`react-native-v2/`, [ | 
|  | 18 | +      "app/(tabs)/foos.tsx", | 
|  | 19 | +      "app/_layout.tsx.dist", | 
|  | 20 | +      "lib/hooks.ts", | 
|  | 21 | +      "lib/store.ts", | 
|  | 22 | +      "lib/types/ApiResource.ts", | 
|  | 23 | +      "lib/types/HydraView.ts", | 
|  | 24 | +      "lib/types/Logs.ts", | 
|  | 25 | +      "lib/types/foo.ts", | 
|  | 26 | +      "lib/factory/logFactory.ts", | 
|  | 27 | +      "lib/slices/fooSlice.ts", | 
|  | 28 | +      "lib/api/fooApi.ts", | 
|  | 29 | +      "components/Main.tsx", | 
|  | 30 | +      "components/Navigation.tsx", | 
|  | 31 | +      "components/StoreProvider.tsx", | 
|  | 32 | +      "components/foo/CreateEditModal.tsx", | 
|  | 33 | +      "components/foo/Form.tsx", | 
|  | 34 | +      "components/foo/LogsRenderer.tsx", | 
|  | 35 | +    ]); | 
|  | 36 | + | 
|  | 37 | +    handlebars.registerHelper("compare", hbhComparison.compare); | 
|  | 38 | +  } | 
|  | 39 | + | 
|  | 40 | +  help(resource) { | 
|  | 41 | +    const titleLc = resource.title.toLowerCase(); | 
|  | 42 | + | 
|  | 43 | +    console.log( | 
|  | 44 | +      'Code for the "%s" resource type has been generated!', | 
|  | 45 | +      resource.title | 
|  | 46 | +    ); | 
|  | 47 | + | 
|  | 48 | +    console.log("You must now configure the lib/store.ts"); | 
|  | 49 | +    console.log( | 
|  | 50 | +      chalk.green(` | 
|  | 51 | +            // imports for ${titleLc} | 
|  | 52 | +            import ${titleLc}Slice from './slices/${titleLc}Slice'; | 
|  | 53 | +            import { ${titleLc}Api } from './api/${titleLc}Api'; | 
|  | 54 | +
 | 
|  | 55 | +            // reducer for ${titleLc} | 
|  | 56 | +            reducer: { | 
|  | 57 | +                ... | 
|  | 58 | +                ${titleLc}: ${titleLc}Slice, | 
|  | 59 | +                [${titleLc}Api.reducerPath]: ${titleLc}Api.reducer, | 
|  | 60 | +            } | 
|  | 61 | +
 | 
|  | 62 | +            // middleware for ${titleLc} | 
|  | 63 | +            getDefaultMiddleware().concat(..., ${titleLc}Api.middleware) | 
|  | 64 | +            `) | 
|  | 65 | +    ); | 
|  | 66 | + | 
|  | 67 | +    console.log( | 
|  | 68 | +      "You should replace app/_layout.tsx by the generated one and add the following route:" | 
|  | 69 | +    ); | 
|  | 70 | +    console.log( | 
|  | 71 | +      chalk.green(` | 
|  | 72 | +            <Tabs.Screen | 
|  | 73 | +                name="(tabs)/${titleLc}s" | 
|  | 74 | +                options={options.tabs.${titleLc}} | 
|  | 75 | +            /> | 
|  | 76 | +
 | 
|  | 77 | +            tabs: { | 
|  | 78 | +                ... | 
|  | 79 | +                ${titleLc}: { | 
|  | 80 | +                    title: '${titleLc}', | 
|  | 81 | +                    headerShown: false, | 
|  | 82 | +                    tabBarIcon: ({ color }) => <TabBarIcon name="home" color={color} />, | 
|  | 83 | +                }, | 
|  | 84 | +            } | 
|  | 85 | +            `) | 
|  | 86 | +    ); | 
|  | 87 | +  } | 
|  | 88 | + | 
|  | 89 | +  generate(api, resource, dir) { | 
|  | 90 | +    const lc = resource.title.toLowerCase(); | 
|  | 91 | +    const titleUcFirst = | 
|  | 92 | +      resource.title.charAt(0).toUpperCase() + resource.title.slice(1); | 
|  | 93 | +    const fields = this.parseFields(resource); | 
|  | 94 | + | 
|  | 95 | +    const context = { | 
|  | 96 | +      title: resource.title, | 
|  | 97 | +      name: resource.name, | 
|  | 98 | +      lc, | 
|  | 99 | +      uc: resource.title.toUpperCase(), | 
|  | 100 | +      fields, | 
|  | 101 | +      formFields: this.buildFields(fields), | 
|  | 102 | +      hydraPrefix: this.hydraPrefix, | 
|  | 103 | +      ucf: titleUcFirst, | 
|  | 104 | +    }; | 
|  | 105 | + | 
|  | 106 | +    // Create directories | 
|  | 107 | +    // These directories may already exist | 
|  | 108 | +    [ | 
|  | 109 | +      `${dir}/app/(tabs)`, | 
|  | 110 | +      `${dir}/config`, | 
|  | 111 | +      `${dir}/components`, | 
|  | 112 | +      `${dir}/components/${lc}`, | 
|  | 113 | +      `${dir}/lib`, | 
|  | 114 | +      `${dir}/lib/api`, | 
|  | 115 | +      `${dir}/lib/factory`, | 
|  | 116 | +      `${dir}/lib/slices`, | 
|  | 117 | +      `${dir}/lib/types`, | 
|  | 118 | +    ].forEach((dir) => this.createDir(dir, false)); | 
|  | 119 | + | 
|  | 120 | +    // static files | 
|  | 121 | +    [ | 
|  | 122 | +      "lib/hooks.ts", | 
|  | 123 | +      "lib/store.ts", | 
|  | 124 | +      "lib/types/ApiResource.ts", | 
|  | 125 | +      "lib/types/HydraView.ts", | 
|  | 126 | +      "lib/types/Logs.ts", | 
|  | 127 | +      "lib/factory/logFactory.ts", | 
|  | 128 | +      "components/Main.tsx", | 
|  | 129 | +      "components/Navigation.tsx", | 
|  | 130 | +      "components/StoreProvider.tsx", | 
|  | 131 | +    ].forEach((file) => this.createFile(file, `${dir}/${file}`)); | 
|  | 132 | + | 
|  | 133 | +    // templated files ucFirst | 
|  | 134 | +    ["lib/types/%s.ts"].forEach((pattern) => | 
|  | 135 | +      this.createFileFromPattern(pattern, dir, [titleUcFirst], context) | 
|  | 136 | +    ); | 
|  | 137 | + | 
|  | 138 | +    // templated files lc | 
|  | 139 | +    [ | 
|  | 140 | +      "app/(tabs)/%ss.tsx", | 
|  | 141 | +      "app/_layout.tsx.dist", | 
|  | 142 | +      "lib/slices/%sSlice.ts", | 
|  | 143 | +      "lib/api/%sApi.ts", | 
|  | 144 | +      "components/%s/CreateEditModal.tsx", | 
|  | 145 | +      "components/%s/Form.tsx", | 
|  | 146 | +      "components/%s/LogsRenderer.tsx", | 
|  | 147 | +    ].forEach((pattern) => | 
|  | 148 | +      this.createFileFromPattern(pattern, dir, [lc], context) | 
|  | 149 | +    ); | 
|  | 150 | + | 
|  | 151 | +    this.createEntrypoint(api.entrypoint, `${dir}/config/entrypoint.js`); | 
|  | 152 | +  } | 
|  | 153 | + | 
|  | 154 | +  getDescription(field) { | 
|  | 155 | +    return field.description ? field.description.replace(/"/g, "'") : ""; | 
|  | 156 | +  } | 
|  | 157 | + | 
|  | 158 | +  parseFields(resource) { | 
|  | 159 | +    const fields = [ | 
|  | 160 | +      ...resource.writableFields, | 
|  | 161 | +      ...resource.readableFields, | 
|  | 162 | +    ].reduce((list, field) => { | 
|  | 163 | +      if (list[field.name]) { | 
|  | 164 | +        return list; | 
|  | 165 | +      } | 
|  | 166 | + | 
|  | 167 | +      const isReferences = Boolean( | 
|  | 168 | +        field.reference && field.maxCardinality !== 1 | 
|  | 169 | +      ); | 
|  | 170 | +      const isEmbeddeds = Boolean(field.embedded && field.maxCardinality !== 1); | 
|  | 171 | + | 
|  | 172 | +      return { | 
|  | 173 | +        ...list, | 
|  | 174 | +        [field.name]: { | 
|  | 175 | +          ...field, | 
|  | 176 | +          type: this.getType(field), | 
|  | 177 | +          description: this.getDescription(field), | 
|  | 178 | +          readonly: false, | 
|  | 179 | +          isReferences, | 
|  | 180 | +          isEmbeddeds, | 
|  | 181 | +          isRelations: isEmbeddeds || isReferences, | 
|  | 182 | +        }, | 
|  | 183 | +      }; | 
|  | 184 | +    }, {}); | 
|  | 185 | + | 
|  | 186 | +    return Object.values(fields); | 
|  | 187 | +  } | 
|  | 188 | +} | 
0 commit comments