diff --git a/package.json b/package.json index fb322574..9145f23a 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "build": "babel src -d lib --ignore '*.test.js'", "watch": "babel --watch src -d lib --ignore '*.test.js'", "test-gen": "rm -rf ./tmp && yarn build && ./lib/index.js https://demo.api-platform.com ./tmp/react -g react && ./lib/index.js https://demo.api-platform.com ./tmp/react-native -g react-native && ./lib/index.js https://demo.api-platform.com ./tmp/vue -g vue", + "test-gen-custom": "rm -rf ./tmp && yarn build && babel src/generators/ReactGenerator.js src/generators/BaseGenerator.js -d ./tmp/gens && cp -r ./templates/react ./templates/react-common ./templates/entrypoint.js ./tmp/gens && ./lib/index.js https://demo.api-platform.com ./tmp/react-custom -g \"$(pwd)/tmp/gens/ReactGenerator.js\" -t ./tmp/gens", "test-gen-swagger": "rm -rf ./tmp && yarn build && ./lib/index.js https://demo.api-platform.com/docs.json ./tmp/react -f swagger && ./lib/index.js https://demo.api-platform.com/docs.json ./tmp/react-native -g react-native -f swagger && ./lib/index.js https://demo.api-platform.com/docs.json ./tmp/vue -g vue -f swagger", "test-gen-env": "rm -rf ./tmp && yarn build && API_PLATFORM_CLIENT_GENERATOR_ENTRYPOINT=https://demo.api-platform.com API_PLATFORM_CLIENT_GENERATOR_OUTPUT=./tmp ./lib/index.js" }, diff --git a/src/generators.js b/src/generators.js index ee4c959c..c1b40185 100644 --- a/src/generators.js +++ b/src/generators.js @@ -1,3 +1,4 @@ +import fs from "fs"; import NextGenerator from "./generators/NextGenerator"; import NuxtGenerator from "./generators/NuxtGenerator"; import ReactGenerator from "./generators/ReactGenerator"; @@ -12,7 +13,12 @@ function wrap(cl) { new cl({ hydraPrefix, templateDirectory }); } -export default function generators(generator = "react") { +export default async function generators(generator = "react") { + if (fs.existsSync(generator)) { + const gen = await import(generator); + return wrap(gen.default); + } + switch (generator) { case "next": return wrap(NextGenerator); diff --git a/src/index.js b/src/index.js index 322bccdf..7372381c 100755 --- a/src/index.js +++ b/src/index.js @@ -8,129 +8,135 @@ import parseOpenApi3Documentation from "@api-platform/api-doc-parser/lib/openapi import { version } from "../package.json"; import generators from "./generators"; -program - .version(version) - .description( - "Generate apps built with Next, Nuxt, Quasar, React, React Native, Vue or Vuetify for any API documented using Hydra or OpenAPI" - ) - .usage("entrypoint outputDirectory") - .option( - "-r, --resource [resourceName]", - "Generate CRUD for the given resource" - ) - .option( - "-p, --hydra-prefix [hydraPrefix]", - "The hydra prefix used by the API", - "hydra:" - ) - .option("--username [username]", "Username for basic auth (Hydra only)") - .option("--password [password]", "Password for basic auth (Hydra only)") - .option("--bearer [bearer]", "Token for bearer auth (Hydra only)") - .option( - "-g, --generator [generator]", - 'The generator to use, one of "next", "nuxt", "quasar", "react", "react-native", "typescript", "vue", "vuetify"', - "next" - ) - .option( - "-t, --template-directory [templateDirectory]", - "The templates directory base to use. Final directory will be ${templateDirectory}/${generator}", - `${__dirname}/../templates/` - ) - .option( - "-f, --format [hydra|openapi3|openapi2]", - '"hydra", "openapi3" or "openapi2"', - "hydra" - ) - .option( - "-s, --server-path [serverPath]", - "Path to express server file to allow route dynamic addition (Next.js generator only)" - ) - .parse(process.argv); +async function main() { + program + .version(version) + .description( + "Generate apps built with Next, Nuxt, Quasar, React, React Native, Vue or Vuetify for any API documented using Hydra or OpenAPI" + ) + .usage("entrypoint outputDirectory") + .option( + "-r, --resource [resourceName]", + "Generate CRUD for the given resource" + ) + .option( + "-p, --hydra-prefix [hydraPrefix]", + "The hydra prefix used by the API", + "hydra:" + ) + .option("--username [username]", "Username for basic auth (Hydra only)") + .option("--password [password]", "Password for basic auth (Hydra only)") + .option("--bearer [bearer]", "Token for bearer auth (Hydra only)") + .option( + "-g, --generator [generator]", + 'The generator to use, one of "next", "nuxt", "quasar", "react", "react-native", "typescript", "vue", "vuetify" or a path to a custom generator of your choice', + "next" + ) + .option( + "-t, --template-directory [templateDirectory]", + "The templates directory base to use. Final directory will be ${templateDirectory}/${generator}", + `${__dirname}/../templates/` + ) + .option( + "-f, --format [hydra|openapi3|openapi2]", + '"hydra", "openapi3" or "openapi2"', + "hydra" + ) + .option( + "-s, --server-path [serverPath]", + "Path to express server file to allow route dynamic addition (Next.js generator only)" + ) + .parse(process.argv); -if ( - 2 !== program.args.length && - (!process.env.API_PLATFORM_CLIENT_GENERATOR_ENTRYPOINT || - !process.env.API_PLATFORM_CLIENT_GENERATOR_OUTPUT) -) { - program.help(); -} + if ( + 2 !== program.args.length && + (!process.env.API_PLATFORM_CLIENT_GENERATOR_ENTRYPOINT || + !process.env.API_PLATFORM_CLIENT_GENERATOR_OUTPUT) + ) { + program.help(); + } -const options = program.opts(); + const options = program.opts(); -const entrypoint = - program.args[0] || process.env.API_PLATFORM_CLIENT_GENERATOR_ENTRYPOINT; -const outputDirectory = - program.args[1] || process.env.API_PLATFORM_CLIENT_GENERATOR_OUTPUT; + const entrypoint = + program.args[0] || process.env.API_PLATFORM_CLIENT_GENERATOR_ENTRYPOINT; + const outputDirectory = + program.args[1] || process.env.API_PLATFORM_CLIENT_GENERATOR_OUTPUT; -const entrypointWithSlash = entrypoint.endsWith("/") - ? entrypoint - : entrypoint + "/"; + const entrypointWithSlash = entrypoint.endsWith("/") + ? entrypoint + : entrypoint + "/"; -const generator = generators(options.generator)({ - hydraPrefix: options.hydraPrefix, - templateDirectory: options.templateDirectory, -}); -const resourceToGenerate = options.resource - ? options.resource.toLowerCase() - : null; -const serverPath = options.serverPath ? options.serverPath.toLowerCase() : null; + const generator = (await generators(options.generator))({ + hydraPrefix: options.hydraPrefix, + templateDirectory: options.templateDirectory, + }); + const resourceToGenerate = options.resource + ? options.resource.toLowerCase() + : null; + const serverPath = options.serverPath + ? options.serverPath.toLowerCase() + : null; -const parser = (entrypointWithSlash) => { - const options = {}; - if (options.username && options.password) { - const encoded = Buffer.from( - `${options.username}:${options.password}` - ).toString("base64"); - options.headers = new Headers(); - options.headers.set("Authorization", `Basic ${encoded}`); - } - if (options.bearer) { - options.headers = new Headers(); - options.headers.set("Authorization", `Bearer ${options.bearer}`); - } - switch (options.format) { - case "swagger": // deprecated - case "openapi2": - return parseSwaggerDocumentation(entrypointWithSlash); - case "openapi3": - return parseOpenApi3Documentation(entrypointWithSlash); - default: - return parseHydraDocumentation(entrypointWithSlash, options); - } -}; + const parser = (entrypointWithSlash) => { + const options = {}; + if (options.username && options.password) { + const encoded = Buffer.from( + `${options.username}:${options.password}` + ).toString("base64"); + options.headers = new Headers(); + options.headers.set("Authorization", `Basic ${encoded}`); + } + if (options.bearer) { + options.headers = new Headers(); + options.headers.set("Authorization", `Bearer ${options.bearer}`); + } + switch (options.format) { + case "swagger": // deprecated + case "openapi2": + return parseSwaggerDocumentation(entrypointWithSlash); + case "openapi3": + return parseOpenApi3Documentation(entrypointWithSlash); + default: + return parseHydraDocumentation(entrypointWithSlash, options); + } + }; -// check generator dependencies -generator.checkDependencies(outputDirectory, serverPath); + // check generator dependencies + generator.checkDependencies(outputDirectory, serverPath); -parser(entrypointWithSlash) - .then((ret) => { - ret.api.resources - .filter(({ deprecated }) => !deprecated) - .filter((resource) => { - const nameLc = resource.name.toLowerCase(); - const titleLc = resource.title.toLowerCase(); + parser(entrypointWithSlash) + .then((ret) => { + ret.api.resources + .filter(({ deprecated }) => !deprecated) + .filter((resource) => { + const nameLc = resource.name.toLowerCase(); + const titleLc = resource.title.toLowerCase(); - return ( - null === resourceToGenerate || - nameLc === resourceToGenerate || - titleLc === resourceToGenerate - ); - }) - .map((resource) => { - const filterDeprecated = (list) => - list.filter(({ deprecated }) => !deprecated); + return ( + null === resourceToGenerate || + nameLc === resourceToGenerate || + titleLc === resourceToGenerate + ); + }) + .map((resource) => { + const filterDeprecated = (list) => + list.filter(({ deprecated }) => !deprecated); - resource.fields = filterDeprecated(resource.fields); - resource.readableFields = filterDeprecated(resource.readableFields); - resource.writableFields = filterDeprecated(resource.writableFields); + resource.fields = filterDeprecated(resource.fields); + resource.readableFields = filterDeprecated(resource.readableFields); + resource.writableFields = filterDeprecated(resource.writableFields); - generator.generate(ret.api, resource, outputDirectory, serverPath); + generator.generate(ret.api, resource, outputDirectory, serverPath); - return resource; - }) - // display helps after all resources have been generated to check relation dependency for example - .forEach((resource) => generator.help(resource, outputDirectory)); - }) - .catch((e) => { - console.log(e); - }); + return resource; + }) + // display helps after all resources have been generated to check relation dependency for example + .forEach((resource) => generator.help(resource, outputDirectory)); + }) + .catch((e) => { + console.log(e); + }); +} + +main();