diff --git a/.gitignore b/.gitignore index c974501ed9..e7926220be 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,5 @@ packages/create-redwood-app/create-redwood-app.tgz **/meta.*.json .nx/cache + +!packages/cli/dist diff --git a/packages/cli/dist/commands/build.js b/packages/cli/dist/commands/build.js new file mode 100644 index 0000000000..83ce13aab5 --- /dev/null +++ b/packages/cli/dist/commands/build.js @@ -0,0 +1,103 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var build_exports = {}; +__export(build_exports, { + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(build_exports); +var import_terminal_link = __toESM(require("terminal-link")); +var import_colors = __toESM(require("../lib/colors")); +var import_exit = require("../lib/exit"); +var import_project = require("../lib/project"); +var import_checkNodeVersion = require("../middleware/checkNodeVersion"); +const command = "build [side..]"; +const description = "Build for production"; +const builder = (yargs) => { + const choices = (0, import_project.sides)(); + yargs.positional("side", { + choices, + default: choices, + description: "Which side(s) to build", + type: "array" + }).option("stats", { + default: false, + description: `Use ${(0, import_terminal_link.default)( + "Webpack Bundle Analyzer", + "https://github.com/webpack-contrib/webpack-bundle-analyzer" + )}`, + type: "boolean" + }).option("verbose", { + alias: "v", + default: false, + description: "Print more", + type: "boolean" + }).option("prerender", { + default: true, + description: "Prerender after building web", + type: "boolean" + }).option("prisma", { + type: "boolean", + alias: "db", + default: true, + description: "Generate the Prisma client" + }).option("performance", { + alias: "perf", + type: "boolean", + default: false, + description: "Measure build performance" + }).middleware(() => { + const check = (0, import_checkNodeVersion.checkNodeVersion)(); + if (check.ok) { + return; + } + (0, import_exit.exitWithError)(void 0, { + message: `${import_colors.default.error("Error")}: ${check.message}`, + includeEpilogue: false + }); + }).epilogue( + `Also see the ${(0, import_terminal_link.default)( + "Redwood CLI Reference", + "https://redwoodjs.com/docs/cli-commands#build" + )}` + ); +}; +const handler = async (options) => { + const { handler: handler2 } = await import("./buildHandler.js"); + return handler2(options); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/buildHandler.js b/packages/cli/dist/commands/buildHandler.js new file mode 100644 index 0000000000..a9f7d126fe --- /dev/null +++ b/packages/cli/dist/commands/buildHandler.js @@ -0,0 +1,205 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var buildHandler_exports = {}; +__export(buildHandler_exports, { + handler: () => handler +}); +module.exports = __toCommonJS(buildHandler_exports); +var import_path = __toESM(require("path")); +var import_execa = __toESM(require("execa")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_listr2 = require("listr2"); +var import_rimraf = require("rimraf"); +var import_terminal_link = __toESM(require("terminal-link")); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_api = require("@redwoodjs/internal/dist/build/api"); +var import_generate = require("@redwoodjs/internal/dist/generate/generate"); +var import_validateSchema = require("@redwoodjs/internal/dist/validateSchema"); +var import_detection = require("@redwoodjs/prerender/detection"); +var import_telemetry = require("@redwoodjs/telemetry"); +var import_lib = require("../lib"); +var import_generatePrismaClient = require("../lib/generatePrismaClient"); +const handler = async ({ + side = ["api", "web"], + verbose = false, + performance = false, + stats = false, + prisma = true, + prerender +}) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "build", + side: JSON.stringify(side), + verbose, + performance, + stats, + prisma, + prerender + }); + const rwjsPaths = (0, import_lib.getPaths)(); + const rwjsConfig = (0, import_lib.getConfig)(); + const useFragments = rwjsConfig.graphql?.fragments; + const useTrustedDocuments = rwjsConfig.graphql?.trustedDocuments; + if (performance) { + console.log("Measuring Web Build Performance..."); + import_execa.default.sync( + `yarn cross-env NODE_ENV=production webpack --config ${require.resolve("@redwoodjs/core/config/webpack.perf.js")}`, + { stdio: "inherit", shell: true, cwd: rwjsPaths.web.base } + ); + return; + } + if (stats) { + console.log("Building Web Stats..."); + import_execa.default.sync( + `yarn cross-env NODE_ENV=production webpack --config ${require.resolve("@redwoodjs/core/config/webpack.stats.js")}`, + { stdio: "inherit", shell: true, cwd: rwjsPaths.web.base } + ); + return; + } + const prismaSchemaExists = import_fs_extra.default.existsSync(rwjsPaths.api.dbSchema); + const prerenderRoutes = prerender && side.includes("web") ? (0, import_detection.detectPrerenderRoutes)() : []; + const shouldGeneratePrismaClient = prisma && prismaSchemaExists && (side.includes("api") || prerenderRoutes.length > 0); + const tasks = [ + shouldGeneratePrismaClient && { + title: "Generating Prisma Client...", + task: () => { + const { cmd, args } = (0, import_generatePrismaClient.generatePrismaCommand)(rwjsPaths.api.dbSchema); + return (0, import_execa.default)(cmd, args, { + stdio: verbose ? "inherit" : "pipe", + shell: true, + cwd: rwjsPaths.api.base + }); + } + }, + // If using GraphQL Fragments or Trusted Documents, then we need to use + // codegen to generate the types needed for possible types and the + // trusted document store hashes + (useFragments || useTrustedDocuments) && { + title: `Generating types needed for ${[ + useFragments && "GraphQL Fragments", + useTrustedDocuments && "Trusted Documents" + ].filter(Boolean).join(" and ")} support...`, + task: async () => { + await (0, import_generate.generate)(); + } + }, + side.includes("api") && { + title: "Verifying graphql schema...", + task: import_validateSchema.loadAndValidateSdls + }, + side.includes("api") && { + title: "Building API...", + task: async () => { + await (0, import_api.cleanApiBuild)(); + const { errors, warnings } = await (0, import_api.buildApi)(); + if (errors.length) { + console.error(errors); + } + if (warnings.length) { + console.warn(warnings); + } + } + }, + side.includes("web") && { + // Clean web/dist before building + // Vite handles this internally + title: "Cleaning Web...", + task: () => { + return (0, import_rimraf.rimraf)(rwjsPaths.web.dist); + }, + enabled: (0, import_lib.getConfig)().web.bundler === "webpack" + }, + side.includes("web") && { + title: "Building Web...", + task: async () => { + if ((0, import_lib.getConfig)().web.bundler !== "webpack") { + process.env.VITE_CJS_IGNORE_WARNING = "true"; + await (0, import_execa.default)( + `node ${require.resolve("@redwoodjs/vite/bins/rw-vite-build.mjs")} --webDir="${rwjsPaths.web.base}" --verbose=${verbose}`, + { + stdio: verbose ? "inherit" : "pipe", + shell: true, + // `cwd` is needed for yarn to find the rw-vite-build binary + // It won't change process.cwd for anything else here, in this + // process + cwd: rwjsPaths.web.base + } + ); + } else { + await (0, import_execa.default)( + `yarn cross-env NODE_ENV=production webpack --config ${require.resolve("@redwoodjs/core/config/webpack.production.js")}`, + { + stdio: verbose ? "inherit" : "pipe", + shell: true, + cwd: rwjsPaths.web.base + } + ); + } + if (!(0, import_lib.getConfig)().experimental?.streamingSsr?.enabled) { + console.log("Creating 200.html..."); + const indexHtmlPath = import_path.default.join((0, import_lib.getPaths)().web.dist, "index.html"); + import_fs_extra.default.copyFileSync( + indexHtmlPath, + import_path.default.join((0, import_lib.getPaths)().web.dist, "200.html") + ); + } + } + } + ].filter(Boolean); + const triggerPrerender = async () => { + console.log("Starting prerendering..."); + if (prerenderRoutes.length === 0) { + console.log( + `You have not marked any routes to "prerender" in your ${(0, import_terminal_link.default)( + "Routes", + "file://" + rwjsPaths.web.routes + )}.` + ); + return; + } + await (0, import_execa.default)("yarn rw prerender", { + stdio: "inherit", + shell: true, + cwd: rwjsPaths.web.base + }); + }; + const jobs = new import_listr2.Listr(tasks, { + renderer: verbose && "verbose" + }); + await (0, import_telemetry.timedTelemetry)(process.argv, { type: "build" }, async () => { + await jobs.run(); + if (side.includes("web") && prerender && prismaSchemaExists) { + await triggerPrerender(); + } + }); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + handler +}); diff --git a/packages/cli/dist/commands/check.js b/packages/cli/dist/commands/check.js new file mode 100644 index 0000000000..72ba943b46 --- /dev/null +++ b/packages/cli/dist/commands/check.js @@ -0,0 +1,69 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var check_exports = {}; +__export(check_exports, { + aliases: () => aliases, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(check_exports); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_lib = require("../lib"); +var import_colors = __toESM(require("../lib/colors")); +const command = "check"; +const aliases = ["diagnostics"]; +const description = "Get structural diagnostics for a Redwood project (experimental)"; +const handler = () => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "check" + }); + const { + printDiagnostics, + DiagnosticSeverity + } = require("@redwoodjs/structure"); + printDiagnostics((0, import_lib.getPaths)().base, { + getSeverityLabel: (severity) => { + if (severity === DiagnosticSeverity.Error) { + return import_colors.default.error("error"); + } + if (severity === DiagnosticSeverity.Warning) { + return import_colors.default.warning("warning"); + } + return import_colors.default.info("info"); + } + }); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + aliases, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/console.js b/packages/cli/dist/commands/console.js new file mode 100644 index 0000000000..8ac611590c --- /dev/null +++ b/packages/cli/dist/commands/console.js @@ -0,0 +1,50 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var console_exports = {}; +__export(console_exports, { + aliases: () => aliases, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(console_exports); +const command = "console"; +const aliases = ["c"]; +const description = "Launch an interactive Redwood shell (experimental)"; +const handler = async (options) => { + const { handler: handler2 } = await import("./consoleHandler.js"); + return handler2(options); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + aliases, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/consoleHandler.js b/packages/cli/dist/commands/consoleHandler.js new file mode 100644 index 0000000000..07d26473da --- /dev/null +++ b/packages/cli/dist/commands/consoleHandler.js @@ -0,0 +1,100 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var consoleHandler_exports = {}; +__export(consoleHandler_exports, { + handler: () => handler +}); +module.exports = __toCommonJS(consoleHandler_exports); +var import_path = __toESM(require("path")); +var import_repl = __toESM(require("repl")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_babel_config = require("@redwoodjs/babel-config"); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_lib = require("../lib"); +const paths = (0, import_lib.getPaths)(); +const loadPrismaClient = (replContext) => { + const { db } = require(import_path.default.join(paths.api.lib, "db")); + db[Symbol.for("nodejs.util.inspect.custom")] = "PrismaClient"; + replContext.db = db; +}; +const consoleHistoryFile = import_path.default.join(paths.generated.base, "console_history"); +const persistConsoleHistory = (r) => { + import_fs_extra.default.appendFileSync( + consoleHistoryFile, + r.lines.filter((line) => line.trim()).join("\n") + "\n", + "utf8" + ); +}; +const loadConsoleHistory = async (r) => { + try { + const history = await import_fs_extra.default.promises.readFile(consoleHistoryFile, "utf8"); + history.split("\n").reverse().map((line) => r.history.push(line)); + } catch (e) { + } +}; +const handler = () => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "console" + }); + (0, import_babel_config.registerApiSideBabelHook)({ + plugins: [ + [ + "babel-plugin-module-resolver", + { + alias: { + src: paths.api.src + } + }, + "rwjs-console-module-resolver" + ] + ] + }); + const r = import_repl.default.start(); + const defaultEval = r.eval; + r.eval = (cmd, context, filename, callback) => { + defaultEval(cmd, context, filename, async (err, result) => { + if (err) { + callback(err); + } else { + try { + callback(null, await Promise.resolve(result)); + } catch (err2) { + callback(err2); + } + } + }); + }; + loadConsoleHistory(r); + r.addListener("close", () => persistConsoleHistory(r)); + loadPrismaClient(r.context); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + handler +}); diff --git a/packages/cli/dist/commands/deploy.js b/packages/cli/dist/commands/deploy.js new file mode 100644 index 0000000000..24af8474be --- /dev/null +++ b/packages/cli/dist/commands/deploy.js @@ -0,0 +1,51 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var deploy_exports = {}; +__export(deploy_exports, { + builder: () => builder, + command: () => command, + description: () => description +}); +module.exports = __toCommonJS(deploy_exports); +var import_terminal_link = __toESM(require("terminal-link")); +const command = "deploy "; +const description = "Deploy your Redwood project"; +const builder = (yargs) => yargs.commandDir("./deploy", { recurse: false }).demandCommand().epilogue( + `Also see the ${(0, import_terminal_link.default)( + "Redwood CLI Reference", + "https://redwoodjs.com/docs/cli-commands#deploy" + )} +` +); +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description +}); diff --git a/packages/cli/dist/commands/deploy/baremetal.js b/packages/cli/dist/commands/deploy/baremetal.js new file mode 100644 index 0000000000..f1de4937d1 --- /dev/null +++ b/packages/cli/dist/commands/deploy/baremetal.js @@ -0,0 +1,659 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var baremetal_exports = {}; +__export(baremetal_exports, { + DEFAULT_SERVER_CONFIG: () => DEFAULT_SERVER_CONFIG, + builder: () => builder, + command: () => command, + commandWithLifecycleEvents: () => commandWithLifecycleEvents, + commands: () => commands, + deployTasks: () => deployTasks, + description: () => description, + execaOptions: () => execaOptions, + handler: () => handler, + lifecycleTask: () => lifecycleTask, + maintenanceTasks: () => maintenanceTasks, + parseConfig: () => parseConfig, + rollbackTasks: () => rollbackTasks, + serverConfigWithDefaults: () => serverConfigWithDefaults, + throwMissingConfig: () => throwMissingConfig, + verifyConfig: () => verifyConfig, + verifyServerConfig: () => verifyServerConfig +}); +module.exports = __toCommonJS(baremetal_exports); +var import_path = __toESM(require("path")); +var import_toml = __toESM(require("@iarna/toml")); +var import_boxen = __toESM(require("boxen")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_listr2 = require("listr2"); +var import_string_env_interpolation = require("string-env-interpolation"); +var import_terminal_link = __toESM(require("terminal-link")); +var import_title_case = require("title-case"); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_lib = require("../../lib"); +var import_colors = __toESM(require("../../lib/colors")); +const CONFIG_FILENAME = "deploy.toml"; +const SYMLINK_FLAGS = "-nsf"; +const CURRENT_RELEASE_SYMLINK_NAME = "current"; +const LIFECYCLE_HOOKS = ["before", "after"]; +const DEFAULT_SERVER_CONFIG = { + port: 22, + branch: "main", + packageManagerCommand: "yarn", + monitorCommand: "pm2", + sides: ["api", "web"], + keepReleases: 5 +}; +const command = "baremetal [environment]"; +const description = "Deploy to baremetal server(s)"; +const pathJoin = import_path.default.posix.join; +const execaOptions = { + cwd: pathJoin((0, import_lib.getPaths)().base), + stdio: "inherit", + shell: true, + cleanup: true +}; +const builder = (yargs) => { + yargs.positional("environment", { + describe: "The environment to deploy to", + type: "string" + }); + yargs.option("first-run", { + describe: "Set this flag the first time you deploy: starts server processes from scratch", + default: false, + type: "boolean" + }); + yargs.option("update", { + describe: "Update code to latest revision", + default: true, + type: "boolean" + }); + yargs.option("install", { + describe: "Run `yarn install`", + default: true, + type: "boolean" + }); + yargs.option("migrate", { + describe: "Run database migration tasks", + default: true, + type: "boolean" + }); + yargs.option("build", { + describe: "Run build process for the deployed `sides`", + default: true, + type: "boolean" + }); + yargs.option("restart", { + describe: "Restart server processes", + default: true, + type: "boolean" + }); + yargs.option("cleanup", { + describe: "Remove old deploy directories", + default: true, + type: "boolean" + }); + yargs.option("releaseDir", { + describe: "Directory to create for the latest release, defaults to timestamp", + default: (/* @__PURE__ */ new Date()).toISOString().replace(/[:\-TZ]/g, "").replace(/\.\d+$/, ""), + type: "string" + }); + yargs.option("branch", { + describe: "The branch to deploy", + type: "string" + }); + yargs.option("maintenance", { + describe: "Add/remove the maintenance page", + choices: ["up", "down"], + help: "Put up a maintenance page by replacing the content of web/dist/index.html with the content of web/src/maintenance.html" + }); + yargs.option("rollback", { + describe: "Add/remove the maintenance page", + help: "Rollback [count] number of releases" + }); + yargs.option("verbose", { + describe: "Verbose mode, for debugging purposes", + default: false, + type: "boolean" + }); + yargs.epilogue( + `Also see the ${(0, import_terminal_link.default)( + "Redwood Baremetal Deploy Reference", + "https://redwoodjs.com/docs/cli-commands#deploy" + )} +` + ); +}; +const sshExec = async (ssh, path2, command2, args) => { + let sshCommand = command2; + if (args) { + sshCommand += ` ${args.join(" ")}`; + } + const result = await ssh.execCommand(sshCommand, { + cwd: path2 + }); + if (result.code !== 0) { + const error = new Error( + `Error while running command \`${command2} ${args.join(" ")}\`` + ); + error.exitCode = result.code; + throw error; + } + return result; +}; +const throwMissingConfig = (name) => { + throw new Error( + `"${name}" config option not set. See https://redwoodjs.com/docs/deployment/baremetal#deploytoml` + ); +}; +const verifyConfig = (config, yargs) => { + if (!yargs.environment) { + throw new Error( + "Must specify an environment to deploy to, ex: `yarn rw deploy baremetal production`" + ); + } + if (!config[yargs.environment]) { + throw new Error(`No servers found for environment "${yargs.environment}"`); + } + return true; +}; +const verifyServerConfig = (config) => { + if (!config.host) { + throwMissingConfig("host"); + } + if (!config.path) { + throwMissingConfig("path"); + } + if (!config.repo) { + throwMissingConfig("repo"); + } + return true; +}; +const symlinkCurrentCommand = async (dir, ssh, path2) => { + return await sshExec(ssh, path2, "ln", [ + SYMLINK_FLAGS, + dir, + CURRENT_RELEASE_SYMLINK_NAME + ]); +}; +const restartProcessCommand = async (processName, ssh, serverConfig, path2) => { + return await sshExec(ssh, path2, serverConfig.monitorCommand, [ + "restart", + processName + ]); +}; +const serverConfigWithDefaults = (serverConfig, yargs) => { + return { + ...DEFAULT_SERVER_CONFIG, + ...serverConfig, + branch: yargs.branch || serverConfig.branch || DEFAULT_SERVER_CONFIG.branch + }; +}; +const maintenanceTasks = (status, ssh, serverConfig) => { + const deployPath = pathJoin(serverConfig.path, CURRENT_RELEASE_SYMLINK_NAME); + const tasks = []; + if (status === "up") { + tasks.push({ + title: `Enabling maintenance page...`, + task: async () => { + await sshExec(ssh, deployPath, "cp", [ + pathJoin("web", "dist", "200.html"), + pathJoin("web", "dist", "200.html.orig") + ]); + await sshExec(ssh, deployPath, "ln", [ + SYMLINK_FLAGS, + pathJoin("..", "src", "maintenance.html"), + pathJoin("web", "dist", "200.html") + ]); + } + }); + if (serverConfig.processNames) { + tasks.push({ + title: `Stopping ${serverConfig.processNames.join(", ")} processes...`, + task: async () => { + await sshExec(ssh, serverConfig.path, serverConfig.monitorCommand, [ + "stop", + serverConfig.processNames.join(" ") + ]); + } + }); + } + } else if (status === "down") { + tasks.push({ + title: `Starting ${serverConfig.processNames.join(", ")} processes...`, + task: async () => { + await sshExec(ssh, serverConfig.path, serverConfig.monitorCommand, [ + "start", + serverConfig.processNames.join(" ") + ]); + } + }); + if (serverConfig.processNames) { + tasks.push({ + title: `Disabling maintenance page...`, + task: async () => { + await sshExec(ssh, deployPath, "rm", [ + pathJoin("web", "dist", "200.html") + ]); + await sshExec(ssh, deployPath, "cp", [ + pathJoin("web", "dist", "200.html.orig"), + pathJoin("web", "dist", "200.html") + ]); + } + }); + } + } + return tasks; +}; +const rollbackTasks = (count, ssh, serverConfig) => { + let rollbackCount = 1; + if (parseInt(count) === count) { + rollbackCount = count; + } + const tasks = [ + { + title: `Rolling back ${rollbackCount} release(s)...`, + task: async () => { + const currentLink = (await sshExec(ssh, serverConfig.path, "readlink", ["-f", "current"])).stdout.split("/").pop(); + const dirs = (await sshExec(ssh, serverConfig.path, "ls", ["-t"])).stdout.split("\n").filter((dirs2) => !dirs2.match(/current/)); + const deployedIndex = dirs.indexOf(currentLink); + const rollbackIndex = deployedIndex + rollbackCount; + if (dirs[rollbackIndex]) { + console.info("Setting symlink"); + await symlinkCurrentCommand( + dirs[rollbackIndex], + ssh, + serverConfig.path + ); + } else { + throw new Error( + `Cannot rollback ${rollbackCount} release(s): ${dirs.length - dirs.indexOf(currentLink) - 1} previous release(s) available` + ); + } + } + } + ]; + if (serverConfig.processNames) { + for (const processName of serverConfig.processNames) { + tasks.push({ + title: `Restarting ${processName} process...`, + task: async () => { + await restartProcessCommand( + processName, + ssh, + serverConfig, + serverConfig.path + ); + } + }); + } + } + return tasks; +}; +const lifecycleTask = (lifecycle, task, skip, { serverLifecycle, ssh, cmdPath }) => { + if (serverLifecycle[lifecycle]?.[task]) { + const tasks = []; + for (const command2 of serverLifecycle[lifecycle][task]) { + tasks.push({ + title: `${(0, import_title_case.titleCase)(lifecycle)} ${task}: \`${command2}\``, + task: async () => { + await sshExec(ssh, cmdPath, command2); + }, + skip: () => skip + }); + } + return tasks; + } +}; +const commandWithLifecycleEvents = ({ name, config, skip, command: command2 }) => { + const tasks = []; + tasks.push(lifecycleTask("before", name, skip, config)); + tasks.push({ ...command2, skip: () => skip }); + tasks.push(lifecycleTask("after", name, skip, config)); + return tasks.flat().filter((t) => t); +}; +const deployTasks = (yargs, ssh, serverConfig, serverLifecycle) => { + const cmdPath = pathJoin(serverConfig.path, yargs.releaseDir); + const config = { yargs, ssh, serverConfig, serverLifecycle, cmdPath }; + const tasks = []; + tasks.push( + commandWithLifecycleEvents({ + name: "update", + config: { ...config, cmdPath: serverConfig.path }, + skip: !yargs.update, + command: { + title: `Cloning \`${serverConfig.branch}\` branch...`, + task: async () => { + await sshExec(ssh, serverConfig.path, "git", [ + "clone", + `--branch=${serverConfig.branch}`, + `--depth=1`, + serverConfig.repo, + yargs.releaseDir + ]); + } + } + }) + ); + tasks.push( + commandWithLifecycleEvents({ + name: "symlinkEnv", + config, + skip: !yargs.update, + command: { + title: `Symlink .env...`, + task: async () => { + await sshExec(ssh, cmdPath, "ln", [SYMLINK_FLAGS, "../.env", ".env"]); + } + } + }) + ); + tasks.push( + commandWithLifecycleEvents({ + name: "install", + config, + skip: !yargs.install, + command: { + title: `Installing dependencies...`, + task: async () => { + await sshExec(ssh, cmdPath, serverConfig.packageManagerCommand, [ + "install" + ]); + } + } + }) + ); + tasks.push( + commandWithLifecycleEvents({ + name: "migrate", + config, + skip: !yargs.migrate || serverConfig?.migrate === false, + command: { + title: `DB Migrations...`, + task: async () => { + await sshExec(ssh, cmdPath, serverConfig.packageManagerCommand, [ + "rw", + "prisma", + "migrate", + "deploy" + ]); + await sshExec(ssh, cmdPath, serverConfig.packageManagerCommand, [ + "rw", + "prisma", + "generate" + ]); + await sshExec(ssh, cmdPath, serverConfig.packageManagerCommand, [ + "rw", + "dataMigrate", + "up" + ]); + } + } + }) + ); + for (const side of serverConfig.sides) { + tasks.push( + commandWithLifecycleEvents({ + name: "build", + config, + skip: !yargs.build, + command: { + title: `Building ${side}...`, + task: async () => { + await sshExec(ssh, cmdPath, serverConfig.packageManagerCommand, [ + "rw", + "build", + side + ]); + } + } + }) + ); + } + tasks.push( + commandWithLifecycleEvents({ + name: "symlinkCurrent", + config, + skip: !yargs.update, + command: { + title: `Symlinking current release...`, + task: async () => { + await symlinkCurrentCommand(yargs.releaseDir, ssh, serverConfig.path); + }, + skip: () => !yargs.update + } + }) + ); + if (serverConfig.processNames) { + for (const processName of serverConfig.processNames) { + if (yargs.firstRun) { + tasks.push( + commandWithLifecycleEvents({ + name: "restart", + config, + skip: !yargs.restart, + command: { + title: `Starting ${processName} process for the first time...`, + task: async () => { + await sshExec( + ssh, + serverConfig.path, + serverConfig.monitorCommand, + [ + "start", + pathJoin( + CURRENT_RELEASE_SYMLINK_NAME, + "ecosystem.config.js" + ), + "--only", + processName + ] + ); + } + } + }) + ); + tasks.push({ + title: `Saving ${processName} state for future startup...`, + task: async () => { + await sshExec(ssh, serverConfig.path, serverConfig.monitorCommand, [ + "save" + ]); + }, + skip: () => !yargs.restart + }); + } else { + tasks.push( + commandWithLifecycleEvents({ + name: "restart", + config, + skip: !yargs.restart, + command: { + title: `Restarting ${processName} process...`, + task: async () => { + await restartProcessCommand( + processName, + ssh, + serverConfig, + serverConfig.path + ); + } + } + }) + ); + } + } + } + tasks.push( + commandWithLifecycleEvents({ + name: "cleanup", + config: { ...config, cmdPath: serverConfig.path }, + skip: !yargs.cleanup, + command: { + title: `Cleaning up old deploys...`, + task: async () => { + const fileStartIndex = serverConfig.keepReleases + 2; + await sshExec( + ssh, + serverConfig.path, + `ls -t | tail -n +${fileStartIndex} | xargs rm -rf` + ); + } + } + }) + ); + return tasks.flat().filter((e) => e); +}; +const mergeLifecycleEvents = (lifecycle, other) => { + let lifecycleCopy = JSON.parse(JSON.stringify(lifecycle)); + for (const hook of LIFECYCLE_HOOKS) { + for (const key in other[hook]) { + lifecycleCopy[hook][key] = (lifecycleCopy[hook][key] || []).concat( + other[hook][key] + ); + } + } + return lifecycleCopy; +}; +const parseConfig = (yargs, rawConfigToml) => { + const configToml = (0, import_string_env_interpolation.env)(rawConfigToml); + const config = import_toml.default.parse(configToml); + let envConfig; + const emptyLifecycle = {}; + verifyConfig(config, yargs); + for (const hook of LIFECYCLE_HOOKS) { + emptyLifecycle[hook] = {}; + } + let envLifecycle = mergeLifecycleEvents(emptyLifecycle, config); + envConfig = config[yargs.environment]; + envLifecycle = mergeLifecycleEvents(envLifecycle, envConfig); + return { envConfig, envLifecycle }; +}; +const commands = (yargs, ssh) => { + const deployConfig = import_fs_extra.default.readFileSync(pathJoin((0, import_lib.getPaths)().base, CONFIG_FILENAME)).toString(); + let { envConfig, envLifecycle } = parseConfig(yargs, deployConfig); + let servers = []; + let tasks = []; + for (const config of envConfig.servers) { + const serverConfig = serverConfigWithDefaults(config, yargs); + verifyServerConfig(serverConfig); + const serverLifecycle = mergeLifecycleEvents(envLifecycle, serverConfig); + tasks.push({ + title: "Connecting...", + task: () => ssh.connect({ + host: serverConfig.host, + port: serverConfig.port, + username: serverConfig.username, + password: serverConfig.password, + privateKey: serverConfig.privateKey, + privateKeyPath: serverConfig.privateKeyPath, + passphrase: serverConfig.passphrase, + agent: serverConfig.agentForward && process.env.SSH_AUTH_SOCK, + agentForward: serverConfig.agentForward + }) + }); + if (yargs.maintenance) { + tasks = tasks.concat( + maintenanceTasks(yargs.maintenance, ssh, serverConfig) + ); + } else if (yargs.rollback) { + tasks = tasks.concat(rollbackTasks(yargs.rollback, ssh, serverConfig)); + } else { + tasks = tasks.concat( + deployTasks(yargs, ssh, serverConfig, serverLifecycle) + ); + } + tasks.push({ + title: "Disconnecting...", + task: () => ssh.dispose() + }); + servers.push({ + title: serverConfig.host, + task: () => { + return new import_listr2.Listr(tasks); + } + }); + } + return servers; +}; +const handler = async (yargs) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "deploy baremetal", + firstRun: yargs.firstRun, + update: yargs.update, + install: yargs.install, + migrate: yargs.migrate, + build: yargs.build, + restart: yargs.restart, + cleanup: yargs.cleanup, + maintenance: yargs.maintenance, + rollback: yargs.rollback, + verbose: yargs.verbose + }); + const { NodeSSH } = require("node-ssh"); + const ssh = new NodeSSH(); + try { + const tasks = new import_listr2.Listr(commands(yargs, ssh), { + concurrent: true, + exitOnError: true, + renderer: yargs.verbose && "verbose" + }); + await tasks.run(); + } catch (e) { + console.error(import_colors.default.error("\nDeploy failed:")); + console.error( + (0, import_boxen.default)(e.stderr || e.message, { + padding: { top: 0, bottom: 0, right: 1, left: 1 }, + margin: 0, + borderColor: "red" + }) + ); + process.exit(e?.exitCode || 1); + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + DEFAULT_SERVER_CONFIG, + builder, + command, + commandWithLifecycleEvents, + commands, + deployTasks, + description, + execaOptions, + handler, + lifecycleTask, + maintenanceTasks, + parseConfig, + rollbackTasks, + serverConfigWithDefaults, + throwMissingConfig, + verifyConfig, + verifyServerConfig +}); diff --git a/packages/cli/dist/commands/deploy/edgio.js b/packages/cli/dist/commands/deploy/edgio.js new file mode 100644 index 0000000000..cf7be4ccba --- /dev/null +++ b/packages/cli/dist/commands/deploy/edgio.js @@ -0,0 +1,151 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var edgio_exports = {}; +__export(edgio_exports, { + ERR_MESSAGE_MISSING_CLI: () => ERR_MESSAGE_MISSING_CLI, + ERR_MESSAGE_NOT_INITIALIZED: () => ERR_MESSAGE_NOT_INITIALIZED, + buildErrorMessage: () => buildErrorMessage, + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(edgio_exports); +var import_path = __toESM(require("path")); +var import_execa = __toESM(require("execa")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_lodash = require("lodash"); +var import_terminal_link = __toESM(require("terminal-link")); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_project_config = require("@redwoodjs/project-config"); +var import_colors = __toESM(require("../../lib/colors")); +var import_helpers = require("./helpers/helpers"); +const command = "edgio [...commands]"; +const description = "Build command for Edgio deploy"; +const builder = async (yargs) => { + const { builder: edgioBuilder } = require("@edgio/cli/commands/deploy"); + (0, import_helpers.deployBuilder)(yargs); + edgioBuilder["skip-init"] = { + type: "boolean", + description: [ + "Edgio will attempt to initialize your project before deployment.", + "If your project has already been initialized and you wish to skip", + "this step, set this to `true`" + ].join(" "), + default: false + }; + yargs.options(edgioBuilder).group( + Object.keys((0, import_lodash.omit)(edgioBuilder, ["skip-init"])), + "Edgio deploy options:" + ); +}; +const execaOptions = { + cwd: import_path.default.join((0, import_project_config.getPaths)().base), + shell: true, + stdio: "inherit", + cleanup: true +}; +const handler = async (args) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "deploy edgio", + skipInit: args.skipInit, + build: args.build, + prisma: args.prisma, + dataMigrate: args.dataMigrate + }); + const { builder: edgioBuilder } = require("@edgio/cli/commands/deploy"); + const cwd = import_path.default.join((0, import_project_config.getPaths)().base); + try { + await (0, import_execa.default)("yarn", ["edgio", "--version"], execaOptions); + } catch (e) { + logAndExit(ERR_MESSAGE_MISSING_CLI); + } + const configExists = await import_fs_extra.default.pathExists(import_path.default.join(cwd, "edgio.config.js")); + if (!configExists) { + if (args.skipInit) { + logAndExit(ERR_MESSAGE_NOT_INITIALIZED); + } + await (0, import_execa.default)("yarn", ["edgio", "init"], execaOptions); + } + await (0, import_helpers.deployHandler)(args); + const deployArgs = Object.keys(edgioBuilder).reduce((acc, key) => { + if (args[key]) { + acc.push(`--${key}=${args[key]}`); + } + return acc; + }, []); + if (!args.skipBuild) { + deployArgs.push("--skip-build"); + await (0, import_execa.default)("yarn", ["edgio", "build", "--skip-framework"], execaOptions); + } + await (0, import_execa.default)("yarn", ["edgio", "deploy", ...deployArgs], execaOptions); +}; +const ERR_MESSAGE_MISSING_CLI = buildErrorMessage( + "Edgio not found!", + [ + "It looks like Edgio is not configured for your project.", + "Run the following to add Edgio to your project:", + ` ${import_colors.default.info("yarn add -D @edgio/cli")}` + ].join("\n") +); +const ERR_MESSAGE_NOT_INITIALIZED = buildErrorMessage( + "Edgio not initialized!", + [ + "It looks like Edgio is not configured for your project.", + "Run the following to initialize Edgio on your project:", + ` ${import_colors.default.info("yarn edgio init")}` + ].join("\n") +); +function buildErrorMessage(title, message) { + return [ + import_colors.default.bold(import_colors.default.error(title)), + "", + message, + "", + `Also see the ${(0, import_terminal_link.default)( + "RedwoodJS on Edgio Guide", + "https://docs.edg.io/guides/redwoodjs" + )} for additional resources.`, + "" + ].join("\n"); +} +function logAndExit(message) { + console.log(message); + process.exit(1); +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + ERR_MESSAGE_MISSING_CLI, + ERR_MESSAGE_NOT_INITIALIZED, + buildErrorMessage, + builder, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/deploy/flightcontrol.js b/packages/cli/dist/commands/deploy/flightcontrol.js new file mode 100644 index 0000000000..1272e7e1ba --- /dev/null +++ b/packages/cli/dist/commands/deploy/flightcontrol.js @@ -0,0 +1,129 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var flightcontrol_exports = {}; +__export(flightcontrol_exports, { + alias: () => alias, + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(flightcontrol_exports); +var import_path = __toESM(require("path")); +var import_execa = __toESM(require("execa")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_terminal_link = __toESM(require("terminal-link")); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_project_config = require("@redwoodjs/project-config"); +const command = "flightcontrol "; +const alias = "fc"; +const description = "Build, Migrate, and Serve commands for Flightcontrol deploy"; +const builder = (yargs) => { + yargs.positional("side", { + choices: ["api", "web"], + description: "Side to deploy", + type: "string" + }).option("prisma", { + description: "Apply database migrations", + type: "boolean", + default: true + }).option("serve", { + description: "Run server for api in production", + type: "boolean", + default: false + }).option("data-migrate", { + description: "Apply data migrations", + type: "boolean", + default: true, + alias: "dm" + }).epilogue( + `For more commands, options, and examples, see ${(0, import_terminal_link.default)( + "Redwood CLI Reference", + "https://redwoodjs.com/docs/cli-commands#deploy" + )}` + ); +}; +const handler = async ({ side, serve, prisma, dm: dataMigrate }) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "deploy flightcontrol", + side, + prisma, + dataMigrate, + serve + }); + const rwjsPaths = (0, import_project_config.getPaths)(); + const execaConfig = { + cwd: rwjsPaths.base, + shell: true, + stdio: "inherit" + }; + async function runApiCommands() { + if (!serve) { + console.log("Building api..."); + import_execa.default.commandSync("yarn rw build api --verbose", execaConfig); + if (prisma) { + console.log("Running database migrations..."); + import_execa.default.commandSync( + `node_modules/.bin/prisma migrate deploy --schema "${rwjsPaths.api.dbSchema}"`, + execaConfig + ); + } + if (dataMigrate) { + console.log("Running data migrations..."); + import_execa.default.commandSync("yarn rw dataMigrate up", execaConfig); + } + return; + } + const serverFilePath = import_path.default.join(rwjsPaths.api.dist, "server.js"); + const hasServerFile = import_fs_extra.default.pathExistsSync(serverFilePath); + if (hasServerFile) { + (0, import_execa.default)(`yarn node ${serverFilePath}`, execaConfig); + } else { + const { handler: handler2 } = await import("@redwoodjs/api-server/dist/apiCLIConfigHandler.js"); + handler2(); + } + } + async function runWebCommands() { + console.log("Building web..."); + import_execa.default.commandSync("yarn rw build web --verbose", execaConfig); + } + if (side === "api") { + runApiCommands(); + } else if (side === "web") { + runWebCommands(); + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + alias, + builder, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/deploy/helpers/helpers.js b/packages/cli/dist/commands/deploy/helpers/helpers.js new file mode 100644 index 0000000000..b8b5dc2f97 --- /dev/null +++ b/packages/cli/dist/commands/deploy/helpers/helpers.js @@ -0,0 +1,89 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var helpers_exports = {}; +__export(helpers_exports, { + deployBuilder: () => deployBuilder, + deployHandler: () => deployHandler +}); +module.exports = __toCommonJS(helpers_exports); +var import_execa = __toESM(require("execa")); +var import_terminal_link = __toESM(require("terminal-link")); +var import_project_config = require("@redwoodjs/project-config"); +var import_colors = __toESM(require("../../../lib/colors")); +const deployBuilder = (yargs) => { + yargs.option("build", { + description: "Build for production", + type: "boolean", + default: "true" + }).option("prisma", { + description: "Apply database migrations", + type: "boolean", + default: "true" + }).option("data-migrate", { + description: "Migrate the data in your database", + type: "boolean", + default: "true", + alias: "dm" + }).epilogue( + `For more commands, options, and examples, see ${(0, import_terminal_link.default)( + "Redwood CLI Reference", + "https://redwoodjs.com/docs/cli-commands#deploy" + )}` + ); +}; +const deployHandler = async ({ build, prisma, dm: dataMigrate }) => { + const paths = (0, import_project_config.getPaths)(); + let commandSet = []; + if (build) { + commandSet.push("yarn rw build --verbose"); + } + if (prisma) { + commandSet.push("yarn rw prisma migrate deploy"); + } + if (dataMigrate) { + commandSet.push("yarn rw data-migrate up"); + } + const joinedCommands = commandSet.join(" && "); + console.log(import_colors.default.green(` +Running: +`) + `${joinedCommands} +`); + return (0, import_execa.default)(joinedCommands, { + shell: true, + stdio: "inherit", + cwd: paths.base, + extendEnv: true, + cleanup: true + }); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + deployBuilder, + deployHandler +}); diff --git a/packages/cli/dist/commands/deploy/netlify.js b/packages/cli/dist/commands/deploy/netlify.js new file mode 100644 index 0000000000..817718cc8c --- /dev/null +++ b/packages/cli/dist/commands/deploy/netlify.js @@ -0,0 +1,47 @@ +"use strict"; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var netlify_exports = {}; +__export(netlify_exports, { + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(netlify_exports); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_helpers = require("./helpers/helpers"); +const command = "netlify [...commands]"; +const description = "Build command for Netlify deploy"; +const builder = (yargs) => (0, import_helpers.deployBuilder)(yargs); +const handler = (yargs) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "deploy netlify", + build: yargs.build, + prisma: yargs.prisma, + dataMigrate: yargs.dataMigrate + }); + (0, import_helpers.deployHandler)(yargs); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/deploy/packing/nft.js b/packages/cli/dist/commands/deploy/packing/nft.js new file mode 100644 index 0000000000..1f09bbf5a9 --- /dev/null +++ b/packages/cli/dist/commands/deploy/packing/nft.js @@ -0,0 +1,97 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var nft_exports = {}; +__export(nft_exports, { + generateEntryFile: () => generateEntryFile, + nftPack: () => nftPack, + packageSingleFunction: () => packageSingleFunction, + zipDirectory: () => zipDirectory +}); +module.exports = __toCommonJS(nft_exports); +var import_path = __toESM(require("path")); +var import_nft = require("@vercel/nft"); +var import_archiver = __toESM(require("archiver")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_files = require("@redwoodjs/internal/dist/files"); +var import_project_config = require("@redwoodjs/project-config"); +const ZIPBALL_DIR = "./api/dist/zipball"; +function zipDirectory(source, out) { + const archive = (0, import_archiver.default)("zip", { zlib: { level: 5 } }); + const stream = import_fs_extra.default.createWriteStream(out); + return new Promise((resolve, reject) => { + archive.directory(source, false).on("error", (err) => reject(err)).pipe(stream); + stream.on("close", () => resolve()); + archive.finalize(); + }); +} +function generateEntryFile(functionAbsolutePath, name) { + const relativeImport = (0, import_project_config.ensurePosixPath)( + import_path.default.relative((0, import_project_config.getPaths)().base, functionAbsolutePath) + ); + return [ + `${ZIPBALL_DIR}/${name}/${name}.js`, + `module.exports = require('./${relativeImport}')` + ]; +} +async function packageSingleFunction(functionFile) { + const { name: functionName } = import_path.default.parse(functionFile); + const { fileList: functionDependencyFileList } = await (0, import_nft.nodeFileTrace)([ + functionFile + ]); + const copyPromises = []; + for (const singleDependencyPath of functionDependencyFileList) { + copyPromises.push( + import_fs_extra.default.copy( + "./" + singleDependencyPath, + `${ZIPBALL_DIR}/${functionName}/${singleDependencyPath}` + ) + ); + } + const [entryFilePath, content] = generateEntryFile(functionFile, functionName); + const functionEntryPromise = import_fs_extra.default.outputFile(entryFilePath, content); + copyPromises.push(functionEntryPromise); + await Promise.all(copyPromises); + await exports.zipDirectory( + `${ZIPBALL_DIR}/${functionName}`, + `${ZIPBALL_DIR}/${functionName}.zip` + ); + await import_fs_extra.default.remove(`${ZIPBALL_DIR}/${functionName}`); + return; +} +function nftPack() { + const filesToBePacked = (0, import_files.findApiDistFunctions)(); + return Promise.all(filesToBePacked.map(exports.packageSingleFunction)); +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + generateEntryFile, + nftPack, + packageSingleFunction, + zipDirectory +}); diff --git a/packages/cli/dist/commands/deploy/render.js b/packages/cli/dist/commands/deploy/render.js new file mode 100644 index 0000000000..c3fac1e013 --- /dev/null +++ b/packages/cli/dist/commands/deploy/render.js @@ -0,0 +1,137 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var render_exports = {}; +__export(render_exports, { + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(render_exports); +var import_path = __toESM(require("path")); +var import_execa = __toESM(require("execa")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_terminal_link = __toESM(require("terminal-link")); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_project_config = require("@redwoodjs/project-config"); +if (process.argv.slice(2).includes("api")) { + process.env.REDWOOD_DISABLE_TELEMETRY = 1; +} +const command = "render "; +const description = "Build, migrate, and serve command for Render deploy"; +const builder = (yargs) => { + yargs.positional("side", { + choices: ["api", "web"], + description: "Side to deploy", + type: "string" + }).option("prisma", { + description: "Apply database migrations", + type: "boolean", + default: true + }).option("data-migrate", { + description: "Apply data migrations", + type: "boolean", + default: true, + alias: "dm" + }).epilogue( + `For more commands, options, and examples, see ${(0, import_terminal_link.default)( + "Redwood CLI Reference", + "https://redwoodjs.com/docs/cli-commands#deploy" + )}` + ); +}; +const handler = async ({ side, prisma, dataMigrate }) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "deploy render", + side, + prisma, + dataMigrate + }); + const rwjsPaths = (0, import_project_config.getPaths)(); + const execaConfig = { + cwd: rwjsPaths.base, + shell: true, + stdio: "inherit" + }; + async function runApiCommands() { + if (prisma) { + console.log("Running database migrations..."); + import_execa.default.commandSync( + `node_modules/.bin/prisma migrate deploy --schema "${rwjsPaths.api.dbSchema}"`, + execaConfig + ); + } + if (dataMigrate) { + console.log("Running data migrations..."); + const packageJson = import_fs_extra.default.readJsonSync( + import_path.default.join(rwjsPaths.base, "package.json") + ); + const hasDataMigratePackage = !!packageJson.devDependencies["@redwoodjs/cli-data-migrate"]; + if (!hasDataMigratePackage) { + console.error( + [ + "Skipping data migrations; your project doesn't have the `@redwoodjs/cli-data-migrate` package as a dev dependency.", + "Without it installed, you're likely to run into memory issues during deploy.", + "If you want to run data migrations, add the package to your project's root package.json and deploy again:", + "", + "```", + "yarn add -D @redwoodjs/cli-data-migrate", + "```" + ].join("\n") + ); + } else { + import_execa.default.commandSync("yarn rw dataMigrate up", execaConfig); + } + } + const serverFilePath = import_path.default.join(rwjsPaths.api.dist, "server.js"); + const hasServerFile = import_fs_extra.default.pathExistsSync(serverFilePath); + if (hasServerFile) { + (0, import_execa.default)(`yarn node ${serverFilePath}`, execaConfig); + } else { + const { handler: handler2 } = await import("@redwoodjs/api-server/dist/apiCLIConfigHandler.js"); + handler2(); + } + } + async function runWebCommands() { + import_execa.default.commandSync("yarn install", execaConfig); + import_execa.default.commandSync("yarn rw build web --verbose", execaConfig); + } + if (side === "api") { + runApiCommands(); + } else if (side === "web") { + runWebCommands(); + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/deploy/serverless.js b/packages/cli/dist/commands/deploy/serverless.js new file mode 100644 index 0000000000..2bd246577c --- /dev/null +++ b/packages/cli/dist/commands/deploy/serverless.js @@ -0,0 +1,296 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var serverless_exports = {}; +__export(serverless_exports, { + aliases: () => aliases, + buildCommands: () => buildCommands, + builder: () => builder, + command: () => command, + deployCommands: () => deployCommands, + description: () => description, + handler: () => handler, + preRequisites: () => preRequisites +}); +module.exports = __toCommonJS(serverless_exports); +var import_path = __toESM(require("path")); +var import_boxen = __toESM(require("boxen")); +var import_chalk = __toESM(require("chalk")); +var import_dotenv_defaults = require("dotenv-defaults"); +var import_execa = __toESM(require("execa")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_listr2 = require("listr2"); +var import_prompts = __toESM(require("prompts")); +var import_terminal_link = __toESM(require("terminal-link")); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_lib = require("../../lib"); +var import_colors = __toESM(require("../../lib/colors")); +const command = "serverless"; +const aliases = ["aws serverless", "sls"]; +const description = "Deploy to AWS via the serverless framework"; +const builder = (yargs) => { + yargs.option("stage", { + describe: "serverless stage pass through param: https://www.serverless.com/blog/stages-and-environments", + default: "production", + type: "string" + }); + yargs.option("sides", { + describe: "which Side(s) to deploy", + choices: ["api", "web"], + default: ["api", "web"], + alias: "side", + type: "array" + }); + yargs.option("verbose", { + describe: "verbosity of logs", + default: true, + type: "boolean" + }); + yargs.option("pack-only", { + describe: "Only build and pack, and dont push code up using serverless", + default: false, + type: "boolean" + }); + yargs.option("first-run", { + describe: "Set this flag the first time you deploy, to configure your API URL on the webside", + default: false, + type: "boolean" + }); + yargs.epilogue( + `Also see the ${(0, import_terminal_link.default)( + "Redwood CLI Reference", + "https://redwoodjs.com/docs/cli-commands#deploy" + )} +` + ); +}; +const preRequisites = () => [ + { + title: "Checking if Serverless framework is installed...", + command: ["yarn serverless", ["--version"]], + errorMessage: [ + "Looks like Serverless is not installed.", + "Please run yarn add -W --dev serverless." + ] + } +]; +const buildCommands = ({ sides }) => { + return [ + { + title: `Building ${sides.join(" & ")}...`, + command: ["yarn", ["rw", "build", ...sides]] + }, + { + title: "Packing Functions...", + enabled: () => sides.includes("api"), + task: async () => { + const { nftPack } = await import("./packing/nft.js"); + await nftPack(); + } + } + ]; +}; +const deployCommands = ({ stage, sides, firstRun, packOnly }) => { + const slsStage = stage ? ["--stage", stage] : []; + return sides.map((side) => { + return { + title: `Deploying ${side}....`, + task: async () => { + await (0, import_execa.default)("yarn", ["serverless", "deploy", ...slsStage], { + cwd: import_path.default.join((0, import_lib.getPaths)().base, side), + shell: true, + stdio: "inherit", + cleanup: true + }); + }, + skip: () => { + if (firstRun && side === "web") { + return "Skipping web deploy, until environment configured"; + } + if (packOnly) { + return "Finishing early due to --pack-only flag. Your Redwood project is packaged and ready to deploy"; + } + } + }; + }); +}; +const loadDotEnvForStage = (dotEnvPath) => { + (0, import_dotenv_defaults.config)({ + path: dotEnvPath, + defaults: import_path.default.join((0, import_lib.getPaths)().base, ".env.defaults"), + encoding: "utf8" + }); +}; +const handler = async (yargs) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "deploy serverless", + sides: JSON.stringify(yargs.sides), + verbose: yargs.verbose, + packOnly: yargs.packOnly, + firstRun: yargs.firstRun + }); + const rwjsPaths = (0, import_lib.getPaths)(); + const dotEnvPath = import_path.default.join(rwjsPaths.base, `.env.${yargs.stage}`); + loadDotEnvForStage(dotEnvPath); + const tasks = new import_listr2.Listr( + [ + ...preRequisites(yargs).map(mapCommandsToListr), + ...buildCommands(yargs).map(mapCommandsToListr), + ...deployCommands(yargs).map(mapCommandsToListr) + ], + { + exitOnError: true, + renderer: yargs.verbose && "verbose" + } + ); + try { + await tasks.run(); + if (yargs.firstRun) { + const SETUP_MARKER = import_chalk.default.bgBlue(import_chalk.default.black("First Setup ")); + console.log(); + console.log(SETUP_MARKER, import_colors.default.green("Starting first setup wizard...")); + const { stdout: slsInfo } = await (0, import_execa.default)( + `yarn serverless info --verbose --stage=${yargs.stage}`, + { + shell: true, + cwd: (0, import_lib.getPaths)().api.base + } + ); + const deployedApiUrl = slsInfo.match(/HttpApiUrl: (https:\/\/.*)/)[1]; + console.log(); + console.log(SETUP_MARKER, `Found ${import_colors.default.green(deployedApiUrl)}`); + console.log(); + const { addDotEnv } = await (0, import_prompts.default)({ + type: "confirm", + name: "addDotEnv", + message: `Add API_URL to your .env.${yargs.stage}? This will be used if you deploy the web side from your machine` + }); + if (addDotEnv) { + import_fs_extra.default.writeFileSync(dotEnvPath, `API_URL=${deployedApiUrl}`); + loadDotEnvForStage(dotEnvPath); + } + if (yargs.sides.includes("web")) { + console.log(); + console.log(SETUP_MARKER, "Deploying web side with updated API_URL"); + console.log( + SETUP_MARKER, + "First deploys can take a good few minutes..." + ); + console.log(); + const webDeployTasks = new import_listr2.Listr( + [ + // Rebuild web with the new API_URL + ...buildCommands({ ...yargs, sides: ["web"], firstRun: false }).map( + mapCommandsToListr + ), + ...deployCommands({ + ...yargs, + sides: ["web"], + firstRun: false + }).map(mapCommandsToListr) + ], + { + exitOnError: true, + renderer: yargs.verbose && "verbose" + } + ); + await webDeployTasks.run(); + const { stdout: slsInfo2 } = await (0, import_execa.default)( + `yarn serverless info --verbose --stage=${yargs.stage}`, + { + shell: true, + cwd: (0, import_lib.getPaths)().web.base + } + ); + const deployedWebUrl = slsInfo2.match(/url: (https:\/\/.*)/)[1]; + const message = [ + import_colors.default.bold("Successful first deploy!"), + "", + `View your deployed site at: ${import_colors.default.green(deployedWebUrl)}`, + "", + "You can use serverless.com CI/CD by connecting/creating an app", + "To do this run `yarn serverless` on each of the sides, and connect your account", + "", + "Find more information in our docs:", + import_colors.default.underline("https://redwoodjs.com/docs/deploy#serverless") + ]; + console.log( + (0, import_boxen.default)(message.join("\n"), { + padding: { top: 0, bottom: 0, right: 1, left: 1 }, + margin: 1, + borderColor: "gray" + }) + ); + } + } + } catch (e) { + console.error(import_colors.default.error(e.message)); + process.exit(e?.exitCode || 1); + } +}; +const mapCommandsToListr = ({ + title, + command: command2, + task, + cwd, + errorMessage, + skip, + enabled +}) => { + return { + title, + task: task ? task : async () => { + try { + const executingCommand = (0, import_execa.default)(...command2, { + cwd: cwd || (0, import_lib.getPaths)().base, + shell: true + }); + executingCommand.stdout.pipe(process.stdout); + await executingCommand; + } catch (error) { + if (errorMessage) { + error.message = error.message + "\n" + errorMessage.join(" "); + } + throw error; + } + }, + skip, + enabled + }; +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + aliases, + buildCommands, + builder, + command, + deployCommands, + description, + handler, + preRequisites +}); diff --git a/packages/cli/dist/commands/deploy/vercel.js b/packages/cli/dist/commands/deploy/vercel.js new file mode 100644 index 0000000000..73ad8dc8ef --- /dev/null +++ b/packages/cli/dist/commands/deploy/vercel.js @@ -0,0 +1,47 @@ +"use strict"; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var vercel_exports = {}; +__export(vercel_exports, { + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(vercel_exports); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_helpers = require("./helpers/helpers"); +const command = "vercel [...commands]"; +const description = "Build command for Vercel deploy"; +const builder = (yargs) => (0, import_helpers.deployBuilder)(yargs); +const handler = (yargs) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "deploy vercel", + build: yargs.build, + prisma: yargs.prisma, + dataMigrate: yargs.dataMigrate + }); + (0, import_helpers.deployHandler)(yargs); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/destroy.js b/packages/cli/dist/commands/destroy.js new file mode 100644 index 0000000000..d04c46ee1f --- /dev/null +++ b/packages/cli/dist/commands/destroy.js @@ -0,0 +1,53 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var destroy_exports = {}; +__export(destroy_exports, { + aliases: () => aliases, + builder: () => builder, + command: () => command, + description: () => description +}); +module.exports = __toCommonJS(destroy_exports); +var import_terminal_link = __toESM(require("terminal-link")); +const command = "destroy "; +const aliases = ["d"]; +const description = "Rollback changes made by the generate command"; +const builder = (yargs) => yargs.commandDir("./destroy", { recurse: true }).demandCommand().epilogue( + `Also see the ${(0, import_terminal_link.default)( + "Redwood CLI Reference", + "https://redwoodjs.com/docs/cli-commands#destroy-alias-d" + )}` +); +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + aliases, + builder, + command, + description +}); diff --git a/packages/cli/dist/commands/destroy/cell/cell.js b/packages/cli/dist/commands/destroy/cell/cell.js new file mode 100644 index 0000000000..a12ddb9965 --- /dev/null +++ b/packages/cli/dist/commands/destroy/cell/cell.js @@ -0,0 +1,41 @@ +"use strict"; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var cell_exports = {}; +__export(cell_exports, { + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler, + tasks: () => tasks +}); +module.exports = __toCommonJS(cell_exports); +var import_cell = require("../../generate/cell/cell"); +var import_helpers = require("../helpers"); +const { command, description, builder, handler, tasks } = (0, import_helpers.createYargsForComponentDestroy)({ + componentName: "cell", + filesFn: import_cell.files +}); +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + handler, + tasks +}); diff --git a/packages/cli/dist/commands/destroy/component/component.js b/packages/cli/dist/commands/destroy/component/component.js new file mode 100644 index 0000000000..0647be46d4 --- /dev/null +++ b/packages/cli/dist/commands/destroy/component/component.js @@ -0,0 +1,42 @@ +"use strict"; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var component_exports = {}; +__export(component_exports, { + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler, + tasks: () => tasks +}); +module.exports = __toCommonJS(component_exports); +var import_component = require("../../generate/component/component"); +var import_helpers = require("../helpers"); +const description = "Destroy a component"; +const { command, builder, handler, tasks } = (0, import_helpers.createYargsForComponentDestroy)({ + componentName: "component", + filesFn: import_component.files +}); +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + handler, + tasks +}); diff --git a/packages/cli/dist/commands/destroy/directive/directive.js b/packages/cli/dist/commands/destroy/directive/directive.js new file mode 100644 index 0000000000..e1f2ab815b --- /dev/null +++ b/packages/cli/dist/commands/destroy/directive/directive.js @@ -0,0 +1,42 @@ +"use strict"; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var directive_exports = {}; +__export(directive_exports, { + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler, + tasks: () => tasks +}); +module.exports = __toCommonJS(directive_exports); +var import_directive = require("../../generate/directive/directive"); +var import_helpers = require("../helpers"); +const description = "Destroy a directive"; +const { command, handler, builder, tasks } = (0, import_helpers.createYargsForComponentDestroy)({ + componentName: "directive", + filesFn: (args) => (0, import_directive.files)({ ...args, type: "validator" }) +}); +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + handler, + tasks +}); diff --git a/packages/cli/dist/commands/destroy/function/function.js b/packages/cli/dist/commands/destroy/function/function.js new file mode 100644 index 0000000000..04c40b3999 --- /dev/null +++ b/packages/cli/dist/commands/destroy/function/function.js @@ -0,0 +1,48 @@ +"use strict"; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var function_exports = {}; +__export(function_exports, { + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler, + tasks: () => tasks +}); +module.exports = __toCommonJS(function_exports); +var import_function = require("../../generate/function/function"); +var import_helpers = require("../helpers"); +const description = "Destroy a Function"; +const builder = (yargs) => { + yargs.positional("name", { + description: "Name of the Function", + type: "string" + }); +}; +const { command, handler, tasks } = (0, import_helpers.createYargsForComponentDestroy)({ + componentName: "function", + filesFn: import_function.files +}); +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + handler, + tasks +}); diff --git a/packages/cli/dist/commands/destroy/helpers.js b/packages/cli/dist/commands/destroy/helpers.js new file mode 100644 index 0000000000..980f5eb386 --- /dev/null +++ b/packages/cli/dist/commands/destroy/helpers.js @@ -0,0 +1,66 @@ +"use strict"; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var helpers_exports = {}; +__export(helpers_exports, { + createYargsForComponentDestroy: () => createYargsForComponentDestroy +}); +module.exports = __toCommonJS(helpers_exports); +var import_listr2 = require("listr2"); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_lib = require("../../lib"); +const tasks = ({ componentName, filesFn, name }) => new import_listr2.Listr( + [ + { + title: `Destroying ${componentName} files...`, + task: async () => { + const f = await filesFn({ name, stories: true, tests: true }); + return (0, import_lib.deleteFilesTask)(f); + } + } + ], + { rendererOptions: { collapseSubtasks: false }, exitOnError: true } +); +const createYargsForComponentDestroy = ({ + componentName, + preTasksFn = (options) => options, + filesFn +}) => { + return { + command: `${componentName} `, + description: `Destroy a ${componentName} component`, + builder: (yargs) => { + yargs.positional("name", { + description: `Name of the ${componentName}`, + type: "string" + }); + }, + handler: async (options) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: `destroy ${componentName}` + }); + options = await preTasksFn({ ...options, isDestroyer: true }); + await tasks({ componentName, filesFn, name: options.name }).run(); + }, + tasks + }; +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + createYargsForComponentDestroy +}); diff --git a/packages/cli/dist/commands/destroy/layout/layout.js b/packages/cli/dist/commands/destroy/layout/layout.js new file mode 100644 index 0000000000..fdc4e3c5d8 --- /dev/null +++ b/packages/cli/dist/commands/destroy/layout/layout.js @@ -0,0 +1,41 @@ +"use strict"; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var layout_exports = {}; +__export(layout_exports, { + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler, + tasks: () => tasks +}); +module.exports = __toCommonJS(layout_exports); +var import_layout = require("../../generate/layout/layout"); +var import_helpers = require("../helpers"); +const { command, description, builder, handler, tasks } = (0, import_helpers.createYargsForComponentDestroy)({ + componentName: "layout", + filesFn: import_layout.files +}); +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + handler, + tasks +}); diff --git a/packages/cli/dist/commands/destroy/page/page.js b/packages/cli/dist/commands/destroy/page/page.js new file mode 100644 index 0000000000..0d71bd4699 --- /dev/null +++ b/packages/cli/dist/commands/destroy/page/page.js @@ -0,0 +1,98 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var page_exports = {}; +__export(page_exports, { + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler, + tasks: () => tasks +}); +module.exports = __toCommonJS(page_exports); +var import_camelcase = __toESM(require("camelcase")); +var import_listr2 = require("listr2"); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_lib = require("../../../lib"); +var import_colors = __toESM(require("../../../lib/colors")); +var import_helpers = require("../../generate/helpers"); +var import_page = require("../../generate/page/page"); +const command = "page [path]"; +const description = "Destroy a page and route component"; +const builder = (yargs) => { + yargs.positional("name", { + description: "Name of the page", + type: "string" + }); + yargs.positional("path", { + description: "URL path to the page. Defaults to name", + type: "string" + }); +}; +const tasks = ({ name, path }) => new import_listr2.Listr( + [ + { + title: "Destroying page files...", + task: async () => { + const p = (0, import_helpers.pathName)(path, name); + const f = (0, import_page.files)({ + name, + path: p, + stories: true, + tests: true, + ...(0, import_page.paramVariants)(p) + }); + return (0, import_lib.deleteFilesTask)(f); + } + }, + { + title: "Cleaning up routes file...", + task: async () => (0, import_lib.removeRoutesFromRouterTask)([(0, import_camelcase.default)(name)]) + } + ], + { rendererOptions: { collapseSubtasks: false }, exitOnError: true } +); +const handler = async ({ name, path }) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "destroy page" + }); + const t = tasks({ name, path }); + try { + await t.run(); + } catch (e) { + console.log(import_colors.default.error(e.message)); + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + handler, + tasks +}); diff --git a/packages/cli/dist/commands/destroy/scaffold/scaffold.js b/packages/cli/dist/commands/destroy/scaffold/scaffold.js new file mode 100644 index 0000000000..6d51036d13 --- /dev/null +++ b/packages/cli/dist/commands/destroy/scaffold/scaffold.js @@ -0,0 +1,144 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var scaffold_exports = {}; +__export(scaffold_exports, { + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler, + tasks: () => tasks +}); +module.exports = __toCommonJS(scaffold_exports); +var import_listr2 = require("listr2"); +var import_pascalcase = __toESM(require("pascalcase")); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_lib = require("../../../lib"); +var import_colors = __toESM(require("../../../lib/colors")); +var import_rwPluralize = require("../../../lib/rwPluralize"); +var import_schemaHelpers = require("../../../lib/schemaHelpers"); +var import_scaffold = require("../../generate/scaffold/scaffold"); +const command = "scaffold "; +const description = "Destroy pages, SDL, and Services files based on a given DB schema Model"; +const removeRoutesWithSet = async ({ model, path, nestScaffoldByModel }) => { + const routes = await (0, import_scaffold.routes)({ model, path, nestScaffoldByModel }); + const routeNames = routes.map(extractRouteName); + const pluralPascalName = (0, import_pascalcase.default)((0, import_rwPluralize.pluralize)(model)); + const layoutName = `${pluralPascalName}Layout`; + return (0, import_lib.removeRoutesFromRouterTask)(routeNames, layoutName); +}; +const removeSetImport = () => { + const routesPath = (0, import_lib.getPaths)().web.routes; + const routesContent = (0, import_lib.readFile)(routesPath).toString(); + if (routesContent.match(" { + const pluralPascalName = (0, import_pascalcase.default)((0, import_rwPluralize.pluralize)(name)); + const pascalScaffoldPath = scaffoldPath === "" ? scaffoldPath : scaffoldPath.split("/").map(import_pascalcase.default).join("/") + "/"; + const layoutName = `${pluralPascalName}Layout`; + const importLayout = `import ${pluralPascalName}Layout from 'src/layouts/${pascalScaffoldPath}${layoutName}'`; + const routesPath = (0, import_lib.getPaths)().web.routes; + const routesContent = (0, import_lib.readFile)(routesPath).toString(); + const newRoutesContent = routesContent.replace( + new RegExp(`\\s*${importLayout}`), + "" + ); + (0, import_lib.writeFile)(routesPath, newRoutesContent, { overwriteExisting: true }); + return "Removed layout import from Routes.{jsx,tsx}"; +}; +const builder = (yargs) => { + yargs.positional("model", { + description: "Model to destroy the scaffold of", + type: "string" + }); +}; +const tasks = ({ model, path, tests, nestScaffoldByModel }) => new import_listr2.Listr( + [ + { + title: "Destroying scaffold files...", + task: async () => { + const f = await (0, import_scaffold.files)({ + model, + path, + tests, + nestScaffoldByModel + }); + return (0, import_lib.deleteFilesTask)(f); + } + }, + { + title: "Cleaning up scaffold routes...", + task: async () => removeRoutesWithSet({ model, path, nestScaffoldByModel }) + }, + { + title: "Removing set import...", + task: () => removeSetImport() + }, + { + title: "Removing layout import...", + task: () => removeLayoutImport({ model, path }) + } + ], + { rendererOptions: { collapseSubtasks: false }, exitOnError: true } +); +const handler = async ({ model: modelArg }) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "destory scaffold" + }); + const { model, path } = (0, import_scaffold.splitPathAndModel)(modelArg); + try { + const { name } = await (0, import_schemaHelpers.verifyModelName)({ name: model, isDestroyer: true }); + await tasks({ model: name, path }).run(); + } catch (e) { + console.log(import_colors.default.error(e.message)); + } +}; +const extractRouteName = (route) => { + const { groups } = route.match(/.*name="?(?\w+)"?/); + return groups.routeName; +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + handler, + tasks +}); diff --git a/packages/cli/dist/commands/destroy/sdl/sdl.js b/packages/cli/dist/commands/destroy/sdl/sdl.js new file mode 100644 index 0000000000..ed3fff9df9 --- /dev/null +++ b/packages/cli/dist/commands/destroy/sdl/sdl.js @@ -0,0 +1,82 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var sdl_exports = {}; +__export(sdl_exports, { + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler, + tasks: () => tasks +}); +module.exports = __toCommonJS(sdl_exports); +var import_listr2 = require("listr2"); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_lib = require("../../../lib"); +var import_colors = __toESM(require("../../../lib/colors")); +var import_schemaHelpers = require("../../../lib/schemaHelpers"); +var import_sdl = require("../../generate/sdl/sdl"); +const command = "sdl "; +const description = "Destroy a GraphQL schema and service component based on a given DB schema Model"; +const builder = (yargs) => { + yargs.positional("model", { + description: "Model to destroy the sdl of", + type: "string" + }); +}; +const tasks = ({ model }) => new import_listr2.Listr( + [ + { + title: "Destroying GraphQL schema and service component files...", + task: async () => { + const f = await (0, import_sdl.files)({ name: model }); + return (0, import_lib.deleteFilesTask)(f); + } + } + ], + { rendererOptions: { collapseSubtasks: false }, exitOnError: true } +); +const handler = async ({ model }) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "destroy sdl" + }); + try { + const { name } = await (0, import_schemaHelpers.verifyModelName)({ name: model, isDestroyer: true }); + await tasks({ model: name }).run(); + } catch (e) { + console.log(import_colors.default.error(e.message)); + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + handler, + tasks +}); diff --git a/packages/cli/dist/commands/destroy/service/service.js b/packages/cli/dist/commands/destroy/service/service.js new file mode 100644 index 0000000000..aa917e5961 --- /dev/null +++ b/packages/cli/dist/commands/destroy/service/service.js @@ -0,0 +1,47 @@ +"use strict"; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var service_exports = {}; +__export(service_exports, { + command: () => command, + description: () => description, + filesWithTemplateVars: () => filesWithTemplateVars, + handler: () => handler, + tasks: () => tasks +}); +module.exports = __toCommonJS(service_exports); +var import_lib = require("../../../lib"); +var import_schemaHelpers = require("../../../lib/schemaHelpers"); +var import_service = require("../../generate/service/service"); +var import_helpers = require("../helpers"); +const filesWithTemplateVars = (templateVars) => { + return (args) => (0, import_service.files)({ ...args, ...templateVars }); +}; +const { command, description, handler, tasks } = (0, import_helpers.createYargsForComponentDestroy)({ + componentName: "service", + preTasksFn: import_schemaHelpers.verifyModelName, + filesFn: filesWithTemplateVars({ ...(0, import_lib.getDefaultArgs)(import_service.builder), crud: true }) +}); +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + command, + description, + filesWithTemplateVars, + handler, + tasks +}); diff --git a/packages/cli/dist/commands/dev.js b/packages/cli/dist/commands/dev.js new file mode 100644 index 0000000000..4ae5c12045 --- /dev/null +++ b/packages/cli/dist/commands/dev.js @@ -0,0 +1,86 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var dev_exports = {}; +__export(dev_exports, { + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(dev_exports); +var import_terminal_link = __toESM(require("terminal-link")); +var import_colors = __toESM(require("../lib/colors")); +var import_checkNodeVersion = require("../middleware/checkNodeVersion"); +const command = "dev [side..]"; +const description = "Start development servers for api, and web"; +const builder = (yargs) => { + yargs.positional("side", { + choices: ["api", "web"], + default: ["api", "web"], + description: "Which dev server(s) to start", + type: "array" + }).option("forward", { + alias: "fwd", + description: 'String of one or more Webpack DevServer config options, for example: `--fwd="--port=1234 --no-open"`', + type: "string" + }).option("generate", { + type: "boolean", + default: true, + description: "Generate artifacts" + }).option("watchNodeModules", { + type: "boolean", + description: "Reload on changes to node_modules" + }).option("apiDebugPort", { + type: "number", + description: "Port on which to expose API server debugger. If you supply the flag with no value it defaults to 18911." + }).middleware(() => { + const check = (0, import_checkNodeVersion.checkNodeVersion)(); + if (check.ok) { + return; + } + console.warn(`${import_colors.default.warning("Warning")}: ${check.message} +`); + }).epilogue( + `Also see the ${(0, import_terminal_link.default)( + "Redwood CLI Reference", + "https://redwoodjs.com/docs/cli-commands#dev" + )}` + ); +}; +const handler = async (options) => { + const { handler: handler2 } = await import("./devHandler.js"); + return handler2(options); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/devHandler.js b/packages/cli/dist/commands/devHandler.js new file mode 100644 index 0000000000..9ec0fec948 --- /dev/null +++ b/packages/cli/dist/commands/devHandler.js @@ -0,0 +1,246 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var devHandler_exports = {}; +__export(devHandler_exports, { + getDevNodeOptions: () => getDevNodeOptions, + handler: () => handler +}); +module.exports = __toCommonJS(devHandler_exports); +var import_process = require("process"); +var import_concurrently = __toESM(require("concurrently")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_dev = require("@redwoodjs/internal/dist/dev"); +var import_project_config = require("@redwoodjs/project-config"); +var import_telemetry = require("@redwoodjs/telemetry"); +var import_lib = require("../lib"); +var import_colors = __toESM(require("../lib/colors")); +var import_exit = require("../lib/exit"); +var import_generatePrismaClient = require("../lib/generatePrismaClient"); +var import_ports = require("../lib/ports"); +var import_project = require("../lib/project"); +const defaultApiDebugPort = 18911; +const handler = async ({ + side = ["api", "web"], + forward = "", + generate = true, + watchNodeModules = process.env.RWJS_WATCH_NODE_MODULES === "1", + apiDebugPort +}) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "dev", + side: JSON.stringify(side), + generate, + watchNodeModules + }); + const rwjsPaths = (0, import_lib.getPaths)(); + const serverFile = (0, import_project.serverFileExists)(); + let apiPreferredPort = parseInt((0, import_project_config.getConfig)().api.port); + let webPreferredPort = parseInt((0, import_project_config.getConfig)().web.port); + let apiAvailablePort = apiPreferredPort; + let apiPortChangeNeeded = false; + let webAvailablePort = webPreferredPort; + let webPortChangeNeeded = false; + if (side.includes("api") && !serverFile) { + apiAvailablePort = await (0, import_ports.getFreePort)(apiPreferredPort); + if (apiAvailablePort === -1) { + (0, import_exit.exitWithError)(void 0, { + message: `Could not determine a free port for the api server` + }); + } + apiPortChangeNeeded = apiAvailablePort !== apiPreferredPort; + } + if (side.includes("web")) { + const forwardedPortMatches = [ + ...forward.matchAll(/\-\-port(\=|\s)(?[^\s]*)/g) + ]; + if (forwardedPortMatches.length) { + webPreferredPort = parseInt(forwardedPortMatches.pop().groups.port); + } + webAvailablePort = await (0, import_ports.getFreePort)(webPreferredPort, [ + apiPreferredPort, + apiAvailablePort + ]); + if (webAvailablePort === -1) { + (0, import_exit.exitWithError)(void 0, { + message: `Could not determine a free port for the web server` + }); + } + webPortChangeNeeded = webAvailablePort !== webPreferredPort; + } + if (apiPortChangeNeeded || webPortChangeNeeded) { + const message = [ + "The currently configured ports for the development server are", + "unavailable. Suggested changes to your ports, which can be changed in", + "redwood.toml, are:\n", + apiPortChangeNeeded && ` - API to use port ${apiAvailablePort} instead`, + apiPortChangeNeeded && "of your currently configured", + apiPortChangeNeeded && `${apiPreferredPort} +`, + webPortChangeNeeded && ` - Web to use port ${webAvailablePort} instead`, + webPortChangeNeeded && "of your currently configured", + webPortChangeNeeded && `${webPreferredPort} +`, + "\nCannot run the development server until your configured ports are", + "changed or become available." + ].filter(Boolean).join(" "); + (0, import_exit.exitWithError)(void 0, { message }); + } + if (side.includes("api")) { + try { + await (0, import_generatePrismaClient.generatePrismaClient)({ + verbose: false, + force: false, + schema: rwjsPaths.api.dbSchema + }); + } catch (e) { + (0, import_telemetry.errorTelemetry)( + process.argv, + `Error generating prisma client: ${e.message}` + ); + console.error(import_colors.default.error(e.message)); + } + if (!serverFile) { + try { + await (0, import_dev.shutdownPort)(apiAvailablePort); + } catch (e) { + (0, import_telemetry.errorTelemetry)(process.argv, `Error shutting down "api": ${e.message}`); + console.error( + `Error whilst shutting down "api" port: ${import_colors.default.error(e.message)}` + ); + } + } + } + if (side.includes("web")) { + try { + await (0, import_dev.shutdownPort)(webAvailablePort); + } catch (e) { + (0, import_telemetry.errorTelemetry)(process.argv, `Error shutting down "web": ${e.message}`); + console.error( + `Error whilst shutting down "web" port: ${import_colors.default.error(e.message)}` + ); + } + } + const webpackDevConfig = require.resolve("@redwoodjs/core/config/webpack.development.js"); + const getApiDebugFlag = () => { + if (apiDebugPort) { + return `--debug-port ${apiDebugPort}`; + } else if (import_process.argv.includes("--apiDebugPort")) { + return `--debug-port ${defaultApiDebugPort}`; + } + const apiDebugPortInToml = (0, import_project_config.getConfig)().api.debugPort; + if (apiDebugPortInToml) { + return `--debug-port ${apiDebugPortInToml}`; + } + return ""; + }; + const redwoodConfigPath = (0, import_project_config.getConfigPath)(); + const streamingSsrEnabled = (0, import_project_config.getConfig)().experimental.streamingSsr?.enabled; + process.env.VITE_CJS_IGNORE_WARNING = "true"; + let webCommand = `yarn cross-env NODE_ENV=development rw-vite-dev ${forward}`; + if (streamingSsrEnabled) { + webCommand = `yarn cross-env NODE_ENV=development rw-dev-fe ${forward}`; + } + if ((0, import_project_config.getConfig)().web.bundler === "webpack") { + if (streamingSsrEnabled) { + throw new Error( + "Webpack does not support SSR. Please switch your bundler to Vite in redwood.toml first" + ); + } else { + webCommand = `yarn cross-env NODE_ENV=development RWJS_WATCH_NODE_MODULES=${watchNodeModules ? "1" : ""} webpack serve --config "${webpackDevConfig}" ${forward}`; + } + } + const jobs = { + api: { + name: "api", + command: [ + `yarn cross-env NODE_ENV=development NODE_OPTIONS="${getDevNodeOptions()}"`, + " yarn nodemon", + " --quiet", + ` --watch "${redwoodConfigPath}"`, + ' --exec "yarn rw-api-server-watch', + ` --port ${apiAvailablePort}`, + ` ${getApiDebugFlag()}`, + ' | rw-log-formatter"' + ].join(" "), + prefixColor: "cyan", + runWhen: () => import_fs_extra.default.existsSync(rwjsPaths.api.src) + }, + web: { + name: "web", + command: webCommand, + prefixColor: "blue", + cwd: rwjsPaths.web.base, + runWhen: () => import_fs_extra.default.existsSync(rwjsPaths.web.src) + }, + gen: { + name: "gen", + command: "yarn rw-gen-watch", + prefixColor: "green", + runWhen: () => generate + } + }; + const { result } = (0, import_concurrently.default)( + Object.keys(jobs).map((job) => { + if (side.includes(job) || job === "gen") { + return jobs[job]; + } + }).filter((job) => job && job.runWhen()), + { + prefix: "{name} |", + timestampFormat: "HH:mm:ss", + handleInput: true + } + ); + result.catch((e) => { + if (typeof e?.message !== "undefined") { + (0, import_telemetry.errorTelemetry)( + process.argv, + `Error concurrently starting sides: ${e.message}` + ); + (0, import_exit.exitWithError)(e); + } + }); +}; +function getDevNodeOptions() { + const { NODE_OPTIONS } = process.env; + const enableSourceMapsOption = "--enable-source-maps"; + if (!NODE_OPTIONS) { + return enableSourceMapsOption; + } + if (NODE_OPTIONS.includes(enableSourceMapsOption)) { + return NODE_OPTIONS; + } + return `${NODE_OPTIONS} ${enableSourceMapsOption}`; +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + getDevNodeOptions, + handler +}); diff --git a/packages/cli/dist/commands/exec.js b/packages/cli/dist/commands/exec.js new file mode 100644 index 0000000000..796feefa5a --- /dev/null +++ b/packages/cli/dist/commands/exec.js @@ -0,0 +1,70 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var exec_exports = {}; +__export(exec_exports, { + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(exec_exports); +var import_terminal_link = __toESM(require("terminal-link")); +const command = "exec [name]"; +const description = "Run scripts generated with yarn generate script"; +const builder = (yargs) => { + yargs.positional("name", { + description: "The file name (extension is optional) of the script to run", + type: "string" + }).option("prisma", { + type: "boolean", + default: true, + description: "Generate the Prisma client" + }).option("list", { + alias: "l", + type: "boolean", + default: false, + description: "List available scripts" + }).strict(false).epilogue( + `Also see the ${(0, import_terminal_link.default)( + "Redwood CLI Reference", + "https://redwoodjs.com/docs/cli-commands#up" + )}` + ); +}; +const handler = async (options) => { + const { handler: handler2 } = await import("./execHandler.js"); + return handler2(options); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/execHandler.js b/packages/cli/dist/commands/execHandler.js new file mode 100644 index 0000000000..bdd1d99500 --- /dev/null +++ b/packages/cli/dist/commands/execHandler.js @@ -0,0 +1,166 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var execHandler_exports = {}; +__export(execHandler_exports, { + handler: () => handler +}); +module.exports = __toCommonJS(execHandler_exports); +var import_path = __toESM(require("path")); +var import_api = require("@opentelemetry/api"); +var import_core = require("@opentelemetry/core"); +var import_listr2 = require("listr2"); +var import_babel_config = require("@redwoodjs/babel-config"); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_files = require("@redwoodjs/internal/dist/files"); +var import_lib = require("../lib"); +var import_colors = __toESM(require("../lib/colors")); +var import_exec = require("../lib/exec"); +var import_generatePrismaClient = require("../lib/generatePrismaClient"); +const printAvailableScriptsToConsole = () => { + console.log("Available scripts:"); + (0, import_files.findScripts)().forEach((scriptPath) => { + const { name } = import_path.default.parse(scriptPath); + console.log(import_colors.default.info(`- ${name}`)); + }); + console.log(); +}; +const handler = async (args) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "exec", + prisma: args.prisma, + list: args.list + }); + const { name, prisma, list, ...scriptArgs } = args; + if (list || !name) { + printAvailableScriptsToConsole(); + return; + } + const scriptPath = import_path.default.join((0, import_lib.getPaths)().scripts, name); + const { + overrides: _overrides, + plugins: webPlugins, + ...otherWebConfig + } = (0, import_babel_config.getWebSideDefaultBabelConfig)(); + (0, import_babel_config.registerApiSideBabelHook)({ + plugins: [ + [ + "babel-plugin-module-resolver", + { + alias: { + $api: (0, import_lib.getPaths)().api.base, + $web: (0, import_lib.getPaths)().web.base, + api: (0, import_lib.getPaths)().api.base, + web: (0, import_lib.getPaths)().web.base + }, + loglevel: "silent" + // to silence the unnecessary warnings + }, + "exec-$side-module-resolver" + ] + ], + overrides: [ + { + test: ["./api/"], + plugins: [ + [ + "babel-plugin-module-resolver", + { + alias: { + src: (0, import_lib.getPaths)().api.src + }, + loglevel: "silent" + }, + "exec-api-src-module-resolver" + ] + ] + }, + { + test: ["./web/"], + plugins: [ + ...webPlugins, + [ + "babel-plugin-module-resolver", + { + alias: { + src: (0, import_lib.getPaths)().web.src + }, + loglevel: "silent" + }, + "exec-web-src-module-resolver" + ] + ], + ...otherWebConfig + } + ] + }); + try { + require.resolve(scriptPath); + } catch { + console.error( + import_colors.default.error(` +No script called ${import_colors.default.underline(name)} in ./scripts folder. +`) + ); + printAvailableScriptsToConsole(); + process.exit(1); + } + const scriptTasks = [ + { + title: "Generating Prisma client", + enabled: () => prisma, + task: () => (0, import_generatePrismaClient.generatePrismaClient)({ force: false }) + }, + { + title: "Running script", + task: async () => { + try { + await (0, import_exec.runScriptFunction)({ + path: scriptPath, + functionName: "default", + args: { args: scriptArgs } + }); + } catch (e) { + console.error(import_colors.default.error(`Error in script: ${e.message}`)); + throw e; + } + } + } + ]; + const tasks = new import_listr2.Listr(scriptTasks, { + rendererOptions: { collapseSubtasks: false }, + renderer: "verbose" + }); + await import_api.context.with((0, import_core.suppressTracing)(import_api.context.active()), async () => { + await tasks.run(); + }); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + handler +}); diff --git a/packages/cli/dist/commands/experimental.js b/packages/cli/dist/commands/experimental.js new file mode 100644 index 0000000000..e38872779d --- /dev/null +++ b/packages/cli/dist/commands/experimental.js @@ -0,0 +1,64 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var experimental_exports = {}; +__export(experimental_exports, { + aliases: () => aliases, + builder: () => builder, + command: () => command, + description: () => description +}); +module.exports = __toCommonJS(experimental_exports); +var import_terminal_link = __toESM(require("terminal-link")); +var import_detectProjectRwVersion = __toESM(require("../middleware/detectProjectRwVersion")); +const command = "experimental "; +const aliases = ["exp"]; +const description = "Run or setup experimental features"; +const builder = (yargs) => yargs.commandDir("./experimental", { + recurse: true, + // @NOTE This regex will ignore all commands nested more than two + // levels deep. + // e.g. /setup/hi.js & setup/hi/hi.js are picked up, but + // setup/hi/hello/bazinga.js will be ignored + // The [/\\] bit is for supporting both windows and unix style paths + // Also take care to not trip up on paths that have "setup" earlier + // in the path by eagerly matching in the start of the regexp + exclude: /.*[/\\]experimental[/\\].*[/\\].*[/\\]/ +}).demandCommand().middleware(import_detectProjectRwVersion.default).epilogue( + `Also see the ${(0, import_terminal_link.default)( + "Redwood CLI Reference", + "https://redwoodjs.com/docs/cli-commands#experimental" + )}` +); +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + aliases, + builder, + command, + description +}); diff --git a/packages/cli/dist/commands/experimental/setupDocker.js b/packages/cli/dist/commands/experimental/setupDocker.js new file mode 100644 index 0000000000..b3c74320e2 --- /dev/null +++ b/packages/cli/dist/commands/experimental/setupDocker.js @@ -0,0 +1,67 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var setupDocker_exports = {}; +__export(setupDocker_exports, { + EXPERIMENTAL_TOPIC_ID: () => EXPERIMENTAL_TOPIC_ID, + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(setupDocker_exports); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_util = require("./util"); +const EXPERIMENTAL_TOPIC_ID = null; +const command = "setup-docker"; +const description = "Setup the experimental Dockerfile"; +function builder(yargs) { + yargs.option("force", { + alias: "f", + default: false, + description: "Overwrite existing configuration", + type: "boolean" + }).epilogue((0, import_util.getEpilogue)(command, description, EXPERIMENTAL_TOPIC_ID, true)); +} +async function handler(options) { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "experimental setup-docker", + force: options.force, + verbose: options.verbose + }); + const { handler: handler2 } = await import("./setupDockerHandler.js"); + return handler2(options); +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + EXPERIMENTAL_TOPIC_ID, + builder, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/experimental/setupDockerHandler.js b/packages/cli/dist/commands/experimental/setupDockerHandler.js new file mode 100644 index 0000000000..82ded753d1 --- /dev/null +++ b/packages/cli/dist/commands/experimental/setupDockerHandler.js @@ -0,0 +1,328 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var setupDockerHandler_exports = {}; +__export(setupDockerHandler_exports, { + getVersionOfRedwoodPackageToInstall: () => getVersionOfRedwoodPackageToInstall, + handler: () => handler +}); +module.exports = __toCommonJS(setupDockerHandler_exports); +var import_path = __toESM(require("path")); +var import_execa = __toESM(require("execa")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_listr2 = require("listr2"); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_project_config = require("@redwoodjs/project-config"); +var import_telemetry = require("@redwoodjs/telemetry"); +var import_colors = __toESM(require("../../lib/colors")); +async function handler({ force }) { + const TEMPLATE_DIR = import_path.default.join(__dirname, "templates", "docker"); + let dockerfileTemplateContent = import_fs_extra.default.readFileSync( + import_path.default.resolve(TEMPLATE_DIR, "Dockerfile"), + "utf-8" + ); + const dockerComposeDevTemplateContent = import_fs_extra.default.readFileSync( + import_path.default.resolve(TEMPLATE_DIR, "docker-compose.dev.yml"), + "utf-8" + ); + const dockerComposeProdTemplateContent = import_fs_extra.default.readFileSync( + import_path.default.resolve(TEMPLATE_DIR, "docker-compose.prod.yml"), + "utf-8" + ); + const dockerignoreTemplateContent = import_fs_extra.default.readFileSync( + import_path.default.resolve(TEMPLATE_DIR, "dockerignore"), + "utf-8" + ); + const dockerfilePath = import_path.default.join((0, import_project_config.getPaths)().base, "Dockerfile"); + const dockerComposeDevFilePath = import_path.default.join( + (0, import_project_config.getPaths)().base, + "docker-compose.dev.yml" + ); + const dockerComposeProdFilePath = import_path.default.join( + (0, import_project_config.getPaths)().base, + "docker-compose.prod.yml" + ); + const dockerignoreFilePath = import_path.default.join((0, import_project_config.getPaths)().base, ".dockerignore"); + const tasks = new import_listr2.Listr( + [ + { + title: "Confirmation", + task: async (_ctx, task) => { + const confirmation = await task.prompt({ + type: "Confirm", + message: "The Dockerfile is experimental. Continue?" + }); + if (!confirmation) { + throw new Error("User aborted"); + } + }, + skip: force + }, + { + title: "Adding the official yarn workspace-tools plugin...", + task: async (_ctx, task) => { + const { stdout } = await import_execa.default.command("yarn plugin runtime --json", { + cwd: (0, import_project_config.getPaths)().base + }); + const hasWorkspaceToolsPlugin = stdout.trim().split("\n").map(JSON.parse).some(({ name }) => name === "@yarnpkg/plugin-workspace-tools"); + if (hasWorkspaceToolsPlugin) { + task.skip( + "The official yarn workspace-tools plugin is already installed" + ); + return; + } + return import_execa.default.command("yarn plugin import workspace-tools", { + cwd: (0, import_project_config.getPaths)().base + }).stdout; + } + }, + { + title: "Adding @redwoodjs/api-server and @redwoodjs/web-server...", + task: async (_ctx, task) => { + const apiServerPackageName = "@redwoodjs/api-server"; + const { dependencies: apiDependencies } = import_fs_extra.default.readJSONSync( + import_path.default.join((0, import_project_config.getPaths)().api.base, "package.json") + ); + const hasApiServerPackage = Object.keys(apiDependencies).includes(apiServerPackageName); + const webServerPackageName = "@redwoodjs/web-server"; + const { dependencies: webDependencies } = import_fs_extra.default.readJSONSync( + import_path.default.join((0, import_project_config.getPaths)().web.base, "package.json") + ); + const hasWebServerPackage = Object.keys(webDependencies).includes(webServerPackageName); + if (hasApiServerPackage && hasWebServerPackage) { + task.skip( + `${apiServerPackageName} and ${webServerPackageName} are already installed` + ); + return; + } + if (!hasApiServerPackage) { + const apiServerPackageVersion = await getVersionOfRedwoodPackageToInstall(apiServerPackageName); + await import_execa.default.command( + `yarn workspace api add ${apiServerPackageName}@${apiServerPackageVersion}`, + { + cwd: (0, import_project_config.getPaths)().base + } + ); + } + if (!hasWebServerPackage) { + const webServerPackageVersion = await getVersionOfRedwoodPackageToInstall(webServerPackageName); + await import_execa.default.command( + `yarn workspace web add ${webServerPackageName}@${webServerPackageVersion}`, + { + cwd: (0, import_project_config.getPaths)().base + } + ); + } + return import_execa.default.command(`yarn dedupe`, { + cwd: (0, import_project_config.getPaths)().base + }).stdout; + } + }, + { + title: "Adding the experimental Dockerfile and compose files...", + task: (_ctx, task) => { + const shouldSkip = [ + dockerfilePath, + dockerComposeDevFilePath, + dockerComposeProdFilePath, + dockerignoreFilePath + ].every(import_fs_extra.default.existsSync); + if (!force && shouldSkip) { + task.skip("The Dockerfile and compose files already exist"); + return; + } + const config = (0, import_project_config.getConfig)(); + const { includeEnvironmentVariables } = config.web; + if (includeEnvironmentVariables.length) { + const webBuildWithPrerenderStageDelimeter = "FROM api_build as web_build_with_prerender\n"; + const webBuildStageDelimeter = "FROM base as web_build\n"; + const [ + beforeWebBuildWithPrerenderStageDelimeter, + afterWebBuildWithPrerenderStageDelimeter + ] = dockerfileTemplateContent.split( + webBuildWithPrerenderStageDelimeter + ); + const [beforeWebBuildStageDelimeter, afterWebBuildStageDelimeter] = afterWebBuildWithPrerenderStageDelimeter.split( + webBuildStageDelimeter + ); + dockerfileTemplateContent = [ + beforeWebBuildWithPrerenderStageDelimeter.trim(), + webBuildWithPrerenderStageDelimeter, + ...includeEnvironmentVariables.map((envVar) => `ARG ${envVar}`), + "", + beforeWebBuildStageDelimeter.trim(), + webBuildStageDelimeter, + ...includeEnvironmentVariables.map((envVar) => `ARG ${envVar}`), + afterWebBuildStageDelimeter + ].join("\n"); + } + (0, import_cli_helpers.writeFile)( + dockerfilePath, + dockerfileTemplateContent, + { + existingFiles: force ? "OVERWRITE" : "SKIP" + }, + task + ); + (0, import_cli_helpers.writeFile)( + dockerComposeDevFilePath, + dockerComposeDevTemplateContent, + { + existingFiles: force ? "OVERWRITE" : "SKIP" + }, + task + ); + (0, import_cli_helpers.writeFile)( + dockerComposeProdFilePath, + dockerComposeProdTemplateContent, + { existingFiles: force ? "OVERWRITE" : "SKIP" }, + task + ); + (0, import_cli_helpers.writeFile)( + dockerignoreFilePath, + dockerignoreTemplateContent, + { + existingFiles: force ? "OVERWRITE" : "SKIP" + }, + task + ); + } + }, + { + title: "Adding postgres to .gitignore...", + task: (_ctx, task) => { + const gitignoreFilePath = import_path.default.join((0, import_project_config.getPaths)().base, ".gitignore"); + const gitignoreFileContent = import_fs_extra.default.readFileSync( + gitignoreFilePath, + "utf-8" + ); + if (gitignoreFileContent.includes("postgres")) { + task.skip("postgres is already ignored by git"); + return; + } + (0, import_cli_helpers.writeFile)( + gitignoreFilePath, + gitignoreFileContent.concat("\npostgres\n"), + { existingFiles: "OVERWRITE" } + ); + } + }, + { + title: "Adding config to redwood.toml...", + task: (_ctx, task) => { + const redwoodTomlPath = (0, import_project_config.getConfigPath)(); + let configContent = import_fs_extra.default.readFileSync(redwoodTomlPath, "utf-8"); + const browserOpenRegExp = /open\s*=\s*true/; + const hasOpenSetToTrue = browserOpenRegExp.test(configContent); + const hasExperimentalDockerfileConfig = configContent.includes( + "[experimental.dockerfile]" + ); + if (!hasOpenSetToTrue && hasExperimentalDockerfileConfig) { + task.skip( + `The [experimental.dockerfile] config block already exists in your 'redwood.toml' file` + ); + return; + } + if (hasOpenSetToTrue) { + configContent = configContent.replace( + /open\s*=\s*true/, + "open = false" + ); + } + if (!hasExperimentalDockerfileConfig) { + configContent = configContent.concat( + ` +[experimental.dockerfile] + enabled = true +` + ); + } + (0, import_cli_helpers.writeFile)(redwoodTomlPath, configContent, { + existingFiles: "OVERWRITE" + }); + } + } + ], + { + renderer: process.env.NODE_ENV === "test" ? "verbose" : "default" + } + ); + try { + await tasks.run(); + console.log( + [ + "", + "We've written four files:", + "", + "- ./Dockerfile", + "- ./.dockerignore", + "- ./docker-compose.dev.yml", + "- ./docker-compose.prod.yml", + "", + "To start the docker compose dev:", + "", + " docker compose -f docker-compose.dev.yml up ", + "", + "Then, connect to the container and migrate your database:", + "", + " docker compose -f ./docker-compose.dev.yml run --rm -it console /bin/bash", + " root@...:/home/node/app# yarn rw prisma migrate dev", + "", + "Lastly, ensure you have Docker. If you don't, see https://docs.docker.com/desktop/", + "", + "There's a lot in the Dockerfile and there's a reason for every line.", + "Be sure to check out the docs: https://redwoodjs.com/docs/docker" + ].join("\n") + ); + } catch (e) { + (0, import_telemetry.errorTelemetry)(process.argv, e.message); + console.error(import_colors.default.error(e.message)); + process.exit(e?.exitCode || 1); + } +} +async function getVersionOfRedwoodPackageToInstall(module2) { + const packageJsonPath = require.resolve("@redwoodjs/cli/package.json", { + paths: [(0, import_project_config.getPaths)().base] + }); + let { version } = import_fs_extra.default.readJSONSync(packageJsonPath); + const packumentP = await fetch(`https://registry.npmjs.org/${module2}`); + const packument = await packumentP.json(); + if (version.includes("+")) { + version = version.split("+")[0]; + } + const versionIsPublished = Object.keys(packument.versions).includes(version); + if (!versionIsPublished) { + version = "canary"; + } + return version; +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + getVersionOfRedwoodPackageToInstall, + handler +}); diff --git a/packages/cli/dist/commands/experimental/setupInngest.js b/packages/cli/dist/commands/experimental/setupInngest.js new file mode 100644 index 0000000000..60ecfc05a3 --- /dev/null +++ b/packages/cli/dist/commands/experimental/setupInngest.js @@ -0,0 +1,66 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var setupInngest_exports = {}; +__export(setupInngest_exports, { + EXPERIMENTAL_TOPIC_ID: () => EXPERIMENTAL_TOPIC_ID, + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(setupInngest_exports); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_util = require("./util"); +const command = "setup-inngest"; +const description = "Setup Inngest for background, scheduled, delayed, multi-step, and fan-out jobs"; +const EXPERIMENTAL_TOPIC_ID = 4866; +const builder = (yargs) => { + yargs.option("force", { + alias: "f", + default: false, + description: "Overwrite existing configuration", + type: "boolean" + }).epilogue((0, import_util.getEpilogue)(command, description, EXPERIMENTAL_TOPIC_ID, true)); +}; +const handler = async (options) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "experimental setup-inngest", + force: options.force + }); + const { handler: handler2 } = await import("./setupInngestHandler.js"); + return handler2(options); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + EXPERIMENTAL_TOPIC_ID, + builder, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/experimental/setupInngestHandler.js b/packages/cli/dist/commands/experimental/setupInngestHandler.js new file mode 100644 index 0000000000..382cf72851 --- /dev/null +++ b/packages/cli/dist/commands/experimental/setupInngestHandler.js @@ -0,0 +1,80 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var setupInngestHandler_exports = {}; +__export(setupInngestHandler_exports, { + handler: () => handler +}); +module.exports = __toCommonJS(setupInngestHandler_exports); +var import_execa = __toESM(require("execa")); +var import_listr2 = require("listr2"); +var import_telemetry = require("@redwoodjs/telemetry"); +var import_lib = require("../../lib"); +var import_colors = __toESM(require("../../lib/colors")); +var import_setupInngest = require("./setupInngest"); +var import_util = require("./util"); +const handler = async ({ force }) => { + const tasks = new import_listr2.Listr([ + { + title: `Adding Inngest setup packages for RedwoodJS ...`, + task: async () => { + await (0, import_execa.default)("yarn", ["add", "-D", "inngest-setup-redwoodjs"], { + cwd: (0, import_lib.getPaths)().base + }); + } + }, + { + task: async () => { + const pluginCommands = ["inngest-setup-redwoodjs", "plugin"]; + if (force) { + pluginCommands.push("--force"); + } + await (0, import_execa.default)("yarn", [...pluginCommands], { + stdout: "inherit", + cwd: (0, import_lib.getPaths)().base + }); + } + }, + { + task: () => { + (0, import_util.printTaskEpilogue)(import_setupInngest.command, import_setupInngest.description, import_setupInngest.EXPERIMENTAL_TOPIC_ID); + } + } + ]); + try { + await tasks.run(); + } catch (e) { + (0, import_telemetry.errorTelemetry)(process.argv, e.message); + console.error(import_colors.default.error(e.message)); + process.exit(e?.exitCode || 1); + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + handler +}); diff --git a/packages/cli/dist/commands/experimental/setupOpentelemetry.js b/packages/cli/dist/commands/experimental/setupOpentelemetry.js new file mode 100644 index 0000000000..06d657acc9 --- /dev/null +++ b/packages/cli/dist/commands/experimental/setupOpentelemetry.js @@ -0,0 +1,72 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var setupOpentelemetry_exports = {}; +__export(setupOpentelemetry_exports, { + EXPERIMENTAL_TOPIC_ID: () => EXPERIMENTAL_TOPIC_ID, + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(setupOpentelemetry_exports); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_util = require("./util"); +const command = "setup-opentelemetry"; +const description = "Setup OpenTelemetry within the API side"; +const EXPERIMENTAL_TOPIC_ID = 4772; +const builder = (yargs) => { + yargs.option("force", { + alias: "f", + default: false, + description: "Overwrite existing configuration", + type: "boolean" + }).option("verbose", { + alias: "v", + default: false, + description: "Print more logs", + type: "boolean" + }).epilogue((0, import_util.getEpilogue)(command, description, EXPERIMENTAL_TOPIC_ID, true)); +}; +const handler = async (options) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "experimental setup-opentelemetry", + force: options.force, + verbose: options.verbose + }); + const { handler: handler2 } = await import("./setupOpentelemetryHandler.js"); + return handler2(options); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + EXPERIMENTAL_TOPIC_ID, + builder, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/experimental/setupOpentelemetryHandler.js b/packages/cli/dist/commands/experimental/setupOpentelemetryHandler.js new file mode 100644 index 0000000000..7b76571007 --- /dev/null +++ b/packages/cli/dist/commands/experimental/setupOpentelemetryHandler.js @@ -0,0 +1,239 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var setupOpentelemetryHandler_exports = {}; +__export(setupOpentelemetryHandler_exports, { + handler: () => handler +}); +module.exports = __toCommonJS(setupOpentelemetryHandler_exports); +var import_path = __toESM(require("path")); +var import_execa = __toESM(require("execa")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_listr2 = require("listr2"); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_project_config = require("@redwoodjs/project-config"); +var import_telemetry = require("@redwoodjs/telemetry"); +var import_lib = require("../../lib"); +var import_colors = __toESM(require("../../lib/colors")); +var import_project = require("../../lib/project"); +var import_setupOpentelemetry = require("./setupOpentelemetry"); +var import_util = require("./util"); +const handler = async ({ force, verbose }) => { + const ts = (0, import_project.isTypeScriptProject)(); + const opentelemetryScriptPath = `${(0, import_lib.getPaths)().api.src}/opentelemetry.${ts ? "ts" : "js"}`; + const opentelemetryPackages = [ + "@opentelemetry/api", + "@opentelemetry/instrumentation", + "@opentelemetry/exporter-trace-otlp-http", + "@opentelemetry/resources", + "@opentelemetry/sdk-node", + "@opentelemetry/semantic-conventions", + "@opentelemetry/instrumentation-http", + "@opentelemetry/instrumentation-fastify", + "@prisma/instrumentation" + ]; + const opentelemetryTasks = [ + { + title: `Adding OpenTelemetry setup files...`, + task: () => { + const setupTemplateContent = import_fs_extra.default.readFileSync( + import_path.default.resolve(__dirname, "templates", "opentelemetry.ts.template"), + "utf-8" + ); + const setupScriptContent = ts ? setupTemplateContent : (0, import_lib.transformTSToJS)(opentelemetryScriptPath, setupTemplateContent); + return [ + (0, import_lib.writeFile)(opentelemetryScriptPath, setupScriptContent, { + overwriteExisting: force + }) + ]; + } + }, + { + title: "Adding config to redwood.toml...", + task: (_ctx, task) => { + const redwoodTomlPath = (0, import_project_config.getConfigPath)(); + const configContent = import_fs_extra.default.readFileSync(redwoodTomlPath, "utf-8"); + if (!configContent.includes("[experimental.opentelemetry]")) { + (0, import_lib.writeFile)( + redwoodTomlPath, + configContent.concat( + ` +[experimental.opentelemetry] + enabled = true + wrapApi = true` + ), + { + overwriteExisting: true + // redwood.toml always exists + } + ); + } else { + task.skip( + `The [experimental.opentelemetry] config block already exists in your 'redwood.toml' file.` + ); + } + } + }, + { + title: "Notice: GraphQL function update...", + enabled: () => { + return import_fs_extra.default.existsSync( + (0, import_project_config.resolveFile)(import_path.default.join((0, import_lib.getPaths)().api.functions, "graphql")) + ); + }, + task: (_ctx, task) => { + task.output = [ + "Please add the following to your 'createGraphQLHandler' function options to enable OTel for your graphql", + "openTelemetryOptions: {", + " resolvers: true,", + " result: true,", + " variables: true,", + "}", + "", + `Which can found at ${import_colors.default.info( + import_path.default.join((0, import_lib.getPaths)().api.functions, "graphql") + )}` + ].join("\n"); + }, + options: { persistentOutput: true } + }, + { + title: "Notice: GraphQL function update (server file)...", + enabled: () => { + return import_fs_extra.default.existsSync( + (0, import_project_config.resolveFile)(import_path.default.join((0, import_lib.getPaths)().api.src, "server")) + ); + }, + task: (_ctx, task) => { + task.output = [ + "Please add the following to your 'redwoodFastifyGraphQLServer' plugin options to enable OTel for your graphql", + "openTelemetryOptions: {", + " resolvers: true,", + " result: true,", + " variables: true,", + "}", + "", + `Which can found at ${import_colors.default.info( + import_path.default.join((0, import_lib.getPaths)().api.src, "server") + )}` + ].join("\n"); + }, + options: { persistentOutput: true } + }, + (0, import_cli_helpers.addApiPackages)(opentelemetryPackages) + ]; + const prismaTasks = [ + { + title: "Setup Prisma OpenTelemetry...", + task: (_ctx, task) => { + const schemaPath = import_path.default.join((0, import_lib.getPaths)().api.db, "schema.prisma"); + const schemaContent = import_fs_extra.default.readFileSync(schemaPath, { + encoding: "utf-8", + flag: "r" + }); + const clientConfig = schemaContent.slice( + schemaContent.indexOf("generator client") + "generator client".length, + schemaContent.indexOf( + "}", + schemaContent.indexOf("generator client") + ) + 1 + ).trim(); + const previewLineExists = clientConfig.includes("previewFeatures"); + let newSchemaContents = schemaContent; + if (previewLineExists) { + task.skip( + 'Please add "tracing" to your previewFeatures in prisma.schema' + ); + } else { + const newClientConfig = clientConfig.trim().split("\n"); + newClientConfig.splice( + newClientConfig.length - 1, + 0, + 'previewFeatures = ["tracing"]' + ); + newSchemaContents = newSchemaContents.replace( + clientConfig, + newClientConfig.join("\n") + ); + } + return (0, import_lib.writeFile)(schemaPath, newSchemaContents, { + overwriteExisting: true + // We'll likely always already have this file in the project + }); + } + }, + { + title: "Regenerate the Prisma client...", + task: (_ctx, _task) => { + return (0, import_execa.default)(`yarn rw prisma generate`, { + stdio: "inherit", + shell: true, + cwd: (0, import_lib.getPaths)().web.base + }); + } + } + ]; + const tasks = new import_listr2.Listr( + [ + { + title: "Confirmation", + task: async (_ctx, task) => { + const confirmation = await task.prompt({ + type: "Confirm", + message: "OpenTelemetry support is experimental. Continue?" + }); + if (!confirmation) { + throw new Error("User aborted"); + } + } + }, + ...opentelemetryTasks, + ...prismaTasks, + { + task: () => { + (0, import_util.printTaskEpilogue)(import_setupOpentelemetry.command, import_setupOpentelemetry.description, import_setupOpentelemetry.EXPERIMENTAL_TOPIC_ID); + } + } + ], + { + rendererOptions: { collapseSubtasks: false, persistentOutput: true }, + renderer: verbose ? "verbose" : "default" + } + ); + try { + await tasks.run(); + } catch (e) { + (0, import_telemetry.errorTelemetry)(process.argv, e.message); + console.error(import_colors.default.error(e.message)); + process.exit(e?.exitCode || 1); + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + handler +}); diff --git a/packages/cli/dist/commands/experimental/setupRsc.js b/packages/cli/dist/commands/experimental/setupRsc.js new file mode 100644 index 0000000000..c96ef7fe59 --- /dev/null +++ b/packages/cli/dist/commands/experimental/setupRsc.js @@ -0,0 +1,66 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var setupRsc_exports = {}; +__export(setupRsc_exports, { + EXPERIMENTAL_TOPIC_ID: () => EXPERIMENTAL_TOPIC_ID, + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(setupRsc_exports); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_util = require("./util"); +const command = "setup-rsc"; +const description = "Enable React Server Components (RSC)"; +const EXPERIMENTAL_TOPIC_ID = 5081; +const builder = (yargs) => { + yargs.option("force", { + alias: "f", + default: false, + description: "Overwrite existing configuration", + type: "boolean" + }).epilogue((0, import_util.getEpilogue)(command, description, EXPERIMENTAL_TOPIC_ID, true)); +}; +const handler = async (options) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: ["experimental", command].join(" "), + force: options.force + }); + const { handler: handler2 } = await import("./setupRscHandler.js"); + return handler2(options); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + EXPERIMENTAL_TOPIC_ID, + builder, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/experimental/setupRscHandler.js b/packages/cli/dist/commands/experimental/setupRscHandler.js new file mode 100644 index 0000000000..af76059b28 --- /dev/null +++ b/packages/cli/dist/commands/experimental/setupRscHandler.js @@ -0,0 +1,332 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var setupRscHandler_exports = {}; +__export(setupRscHandler_exports, { + handler: () => handler +}); +module.exports = __toCommonJS(setupRscHandler_exports); +var import_path = __toESM(require("path")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_listr2 = require("listr2"); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_project_config = require("@redwoodjs/project-config"); +var import_telemetry = require("@redwoodjs/telemetry"); +var import_lib = require("../../lib"); +var import_colors = __toESM(require("../../lib/colors")); +var import_project = require("../../lib/project"); +var import_setupRsc = require("./setupRsc"); +var import_util = require("./util"); +const handler = async ({ force, verbose }) => { + const rwPaths = (0, import_lib.getPaths)(); + const redwoodTomlPath = (0, import_project_config.getConfigPath)(); + const configContent = import_fs_extra.default.readFileSync(redwoodTomlPath, "utf-8"); + const tasks = new import_listr2.Listr( + [ + { + title: "Check prerequisites", + task: () => { + if (!rwPaths.web.entryClient || !rwPaths.web.viteConfig) { + throw new Error("Vite needs to be setup before you can enable RSCs"); + } + if (!(0, import_project_config.getConfig)().experimental?.streamingSsr?.enabled) { + throw new Error( + "The Streaming SSR experimental feature must be enabled before you can enable RSCs" + ); + } + if (!(0, import_project.isTypeScriptProject)()) { + throw new Error( + "RSCs are only supported in TypeScript projects at this time" + ); + } + } + }, + { + title: "Adding config to redwood.toml...", + task: (_ctx, task) => { + if (!configContent.includes("[experimental.rsc]")) { + (0, import_lib.writeFile)( + redwoodTomlPath, + configContent.concat("\n[experimental.rsc]\n enabled = true\n"), + { + overwriteExisting: true + // redwood.toml always exists + } + ); + } else { + if (force) { + task.output = "Overwriting config in redwood.toml"; + (0, import_lib.writeFile)( + redwoodTomlPath, + configContent.replace( + // Enable if it's currently disabled + "\n[experimental.rsc]\n enabled = false\n", + "\n[experimental.rsc]\n enabled = true\n" + ), + { + overwriteExisting: true + // redwood.toml always exists + } + ); + } else { + task.skip( + "The [experimental.rsc] config block already exists in your `redwood.toml` file." + ); + } + } + }, + options: { persistentOutput: true } + }, + { + title: "Adding entries.ts...", + task: async () => { + const entriesTemplate = import_fs_extra.default.readFileSync( + import_path.default.resolve(__dirname, "templates", "rsc", "entries.ts.template"), + "utf-8" + ); + (0, import_lib.writeFile)(import_path.default.join(rwPaths.web.src, "entries.ts"), entriesTemplate, { + overwriteExisting: force + }); + } + }, + { + title: "Adding Pages...", + task: async () => { + const homePageTemplate = import_fs_extra.default.readFileSync( + import_path.default.resolve( + __dirname, + "templates", + "rsc", + "HomePage.tsx.template" + ), + "utf-8" + ); + const homePagePath = import_path.default.join( + rwPaths.web.pages, + "HomePage", + "HomePage.tsx" + ); + (0, import_lib.writeFile)(homePagePath, homePageTemplate, { + overwriteExisting: force + }); + const aboutPageTemplate = import_fs_extra.default.readFileSync( + import_path.default.resolve( + __dirname, + "templates", + "rsc", + "AboutPage.tsx.template" + ), + "utf-8" + ); + const aboutPagePath = import_path.default.join( + rwPaths.web.pages, + "AboutPage", + "AboutPage.tsx" + ); + (0, import_lib.writeFile)(aboutPagePath, aboutPageTemplate, { + overwriteExisting: force + }); + } + }, + { + title: "Adding Counter.tsx...", + task: async () => { + const counterTemplate = import_fs_extra.default.readFileSync( + import_path.default.resolve(__dirname, "templates", "rsc", "Counter.tsx.template"), + "utf-8" + ); + const counterPath = import_path.default.join( + rwPaths.web.components, + "Counter", + "Counter.tsx" + ); + (0, import_lib.writeFile)(counterPath, counterTemplate, { + overwriteExisting: force + }); + } + }, + { + title: "Adding AboutCounter.tsx...", + task: async () => { + const counterTemplate = import_fs_extra.default.readFileSync( + import_path.default.resolve( + __dirname, + "templates", + "rsc", + "AboutCounter.tsx.template" + ), + "utf-8" + ); + const counterPath = import_path.default.join( + rwPaths.web.components, + "Counter", + "AboutCounter.tsx" + ); + (0, import_lib.writeFile)(counterPath, counterTemplate, { + overwriteExisting: force + }); + } + }, + { + title: "Adding CSS files...", + task: async () => { + const files = [ + { + template: "Counter.css.template", + path: ["components", "Counter", "Counter.css"] + }, + { + template: "Counter.module.css.template", + path: ["components", "Counter", "Counter.module.css"] + }, + { + template: "HomePage.css.template", + path: ["pages", "HomePage", "HomePage.css"] + }, + { + template: "HomePage.module.css.template", + path: ["pages", "HomePage", "HomePage.module.css"] + }, + { + template: "AboutPage.css.template", + path: ["pages", "AboutPage", "AboutPage.css"] + } + ]; + files.forEach((file) => { + const template = import_fs_extra.default.readFileSync( + import_path.default.resolve(__dirname, "templates", "rsc", file.template), + "utf-8" + ); + const filePath = import_path.default.join(rwPaths.web.src, ...file.path); + (0, import_lib.writeFile)(filePath, template, { + overwriteExisting: force + }); + }); + } + }, + { + title: "Adding Layout...", + task: async () => { + const layoutTemplate = import_fs_extra.default.readFileSync( + import_path.default.resolve( + __dirname, + "templates", + "rsc", + "NavigationLayout.tsx.template" + ), + "utf-8" + ); + const layoutPath = import_path.default.join( + rwPaths.web.layouts, + "NavigationLayout", + "NavigationLayout.tsx" + ); + (0, import_lib.writeFile)(layoutPath, layoutTemplate, { overwriteExisting: force }); + const cssTemplate = import_fs_extra.default.readFileSync( + import_path.default.resolve( + __dirname, + "templates", + "rsc", + "NavigationLayout.css.template" + ), + "utf-8" + ); + const cssPath = import_path.default.join( + rwPaths.web.layouts, + "NavigationLayout", + "NavigationLayout.css" + ); + (0, import_lib.writeFile)(cssPath, cssTemplate, { overwriteExisting: force }); + } + }, + { + title: "Overwriting index.css...", + task: async () => { + const template = import_fs_extra.default.readFileSync( + import_path.default.resolve(__dirname, "templates", "rsc", "index.css.template"), + "utf-8" + ); + const filePath = import_path.default.join(rwPaths.web.src, "index.css"); + (0, import_lib.writeFile)(filePath, template, { + overwriteExisting: true + }); + } + }, + { + title: "Add React experimental types", + task: async () => { + const tsconfigPath = import_path.default.join(rwPaths.web.base, "tsconfig.json"); + const tsconfig = JSON.parse(import_fs_extra.default.readFileSync(tsconfigPath, "utf-8")); + if (tsconfig.compilerOptions.types.includes("react/experimental")) { + return; + } + tsconfig.compilerOptions.types.push("react/experimental"); + (0, import_lib.writeFile)( + tsconfigPath, + await (0, import_cli_helpers.prettify)("tsconfig.json", JSON.stringify(tsconfig, null, 2)), + { + overwriteExisting: true + } + ); + } + }, + { + title: "Overwriting routes...", + task: async () => { + const routesTemplate = import_fs_extra.default.readFileSync( + import_path.default.resolve(__dirname, "templates", "rsc", "Routes.tsx.template"), + "utf-8" + ); + (0, import_lib.writeFile)(rwPaths.web.routes, routesTemplate, { + overwriteExisting: true + }); + } + }, + { + task: () => { + (0, import_util.printTaskEpilogue)(import_setupRsc.command, import_setupRsc.description, import_setupRsc.EXPERIMENTAL_TOPIC_ID); + } + } + ], + { + rendererOptions: { collapseSubtasks: false, persistentOutput: true }, + renderer: verbose ? "verbose" : "default" + } + ); + try { + await tasks.run(); + } catch (e) { + (0, import_telemetry.errorTelemetry)(process.argv, e.message); + console.error(import_colors.default.error(e.message)); + process.exit(e?.exitCode || 1); + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + handler +}); diff --git a/packages/cli/dist/commands/experimental/setupStreamingSsr.js b/packages/cli/dist/commands/experimental/setupStreamingSsr.js new file mode 100644 index 0000000000..09d5580b73 --- /dev/null +++ b/packages/cli/dist/commands/experimental/setupStreamingSsr.js @@ -0,0 +1,66 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var setupStreamingSsr_exports = {}; +__export(setupStreamingSsr_exports, { + EXPERIMENTAL_TOPIC_ID: () => EXPERIMENTAL_TOPIC_ID, + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(setupStreamingSsr_exports); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_util = require("./util"); +const command = "setup-streaming-ssr"; +const description = "Enable React Streaming and Server Side Rendering (SSR)"; +const EXPERIMENTAL_TOPIC_ID = 5052; +const builder = (yargs) => { + yargs.option("force", { + alias: "f", + default: false, + description: "Overwrite existing configuration", + type: "boolean" + }).epilogue((0, import_util.getEpilogue)(command, description, EXPERIMENTAL_TOPIC_ID, true)); +}; +const handler = async (options) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: ["experimental", command].join(" "), + force: options.force + }); + const { handler: handler2 } = await import("./setupStreamingSsrHandler.js"); + return handler2(options); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + EXPERIMENTAL_TOPIC_ID, + builder, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/experimental/setupStreamingSsrHandler.js b/packages/cli/dist/commands/experimental/setupStreamingSsrHandler.js new file mode 100644 index 0000000000..fb38cef123 --- /dev/null +++ b/packages/cli/dist/commands/experimental/setupStreamingSsrHandler.js @@ -0,0 +1,206 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var setupStreamingSsrHandler_exports = {}; +__export(setupStreamingSsrHandler_exports, { + handler: () => handler +}); +module.exports = __toCommonJS(setupStreamingSsrHandler_exports); +var import_path = __toESM(require("path")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_listr2 = require("listr2"); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_project_config = require("@redwoodjs/project-config"); +var import_telemetry = require("@redwoodjs/telemetry"); +var import_lib = require("../../lib"); +var import_colors = __toESM(require("../../lib/colors")); +var import_project = require("../../lib/project"); +var import_setupStreamingSsr = require("./setupStreamingSsr"); +var import_util = require("./util"); +const handler = async ({ force, verbose }) => { + const rwPaths = (0, import_lib.getPaths)(); + const redwoodTomlPath = (0, import_project_config.getConfigPath)(); + const configContent = import_fs_extra.default.readFileSync(redwoodTomlPath, "utf-8"); + const ts = (0, import_project.isTypeScriptProject)(); + const ext = import_path.default.extname(rwPaths.web.entryClient || ""); + const tasks = new import_listr2.Listr( + [ + { + title: "Check prerequisites", + task: () => { + if (!rwPaths.web.entryClient || !rwPaths.web.viteConfig) { + throw new Error( + "Vite needs to be setup before you can enable Streaming SSR" + ); + } + } + }, + { + title: "Adding config to redwood.toml...", + task: (_ctx, task) => { + if (!configContent.includes("[experimental.streamingSsr]")) { + (0, import_lib.writeFile)( + redwoodTomlPath, + configContent.concat( + ` +[experimental.streamingSsr] + enabled = true +` + ), + { + overwriteExisting: true + // redwood.toml always exists + } + ); + } else { + if (force) { + task.output = "Overwriting config in redwood.toml"; + (0, import_lib.writeFile)( + redwoodTomlPath, + configContent.replace( + // Enable if it's currently disabled + ` +[experimental.streamingSsr] + enabled = false +`, + ` +[experimental.streamingSsr] + enabled = true +` + ), + { + overwriteExisting: true + // redwood.toml always exists + } + ); + } else { + task.skip( + `The [experimental.streamingSsr] config block already exists in your 'redwood.toml' file.` + ); + } + } + }, + options: { persistentOutput: true } + }, + { + title: `Adding entry.client${ext}...`, + task: async (_ctx, task) => { + const entryClientTemplate = import_fs_extra.default.readFileSync( + import_path.default.resolve( + __dirname, + "templates", + "streamingSsr", + "entry.client.tsx.template" + ), + "utf-8" + ); + let entryClientPath = rwPaths.web.entryClient; + const entryClientContent = ts ? entryClientTemplate : (0, import_lib.transformTSToJS)(entryClientPath, entryClientTemplate); + let overwriteExisting = force; + if (!force) { + overwriteExisting = await task.prompt({ + type: "Confirm", + message: `Overwrite ${entryClientPath}?` + }); + if (!overwriteExisting) { + entryClientPath = entryClientPath.replace(ext, `.new${ext}`); + task.output = `File will be written to ${entryClientPath} +You'll manually need to merge it with your existing entry.client${ext} file.`; + } + } + (0, import_lib.writeFile)(entryClientPath, entryClientContent, { overwriteExisting }); + }, + options: { persistentOutput: true } + }, + { + title: `Adding entry.server${ext}...`, + task: async () => { + const entryServerTemplate = import_fs_extra.default.readFileSync( + import_path.default.resolve( + __dirname, + "templates", + "streamingSsr", + "entry.server.tsx.template" + ), + "utf-8" + ); + const entryServerPath = import_path.default.join( + rwPaths.web.src, + `entry.server${ext}` + ); + const entryServerContent = ts ? entryServerTemplate : (0, import_lib.transformTSToJS)(entryServerPath, entryServerTemplate); + (0, import_lib.writeFile)(entryServerPath, entryServerContent, { + overwriteExisting: force + }); + } + }, + { + title: `Adding Document${ext}...`, + task: async () => { + const documentTemplate = import_fs_extra.default.readFileSync( + import_path.default.resolve( + __dirname, + "templates", + "streamingSsr", + "Document.tsx.template" + ), + "utf-8" + ); + const documentPath = import_path.default.join(rwPaths.web.src, `Document${ext}`); + const documentContent = ts ? documentTemplate : (0, import_lib.transformTSToJS)(documentPath, documentTemplate); + (0, import_lib.writeFile)(documentPath, documentContent, { + overwriteExisting: force + }); + } + }, + (0, import_cli_helpers.addWebPackages)([ + "@apollo/experimental-nextjs-app-support@0.0.0-commit-b8a73fe" + ]), + { + task: () => { + (0, import_util.printTaskEpilogue)(import_setupStreamingSsr.command, import_setupStreamingSsr.description, import_setupStreamingSsr.EXPERIMENTAL_TOPIC_ID); + } + } + ], + { + rendererOptions: { collapseSubtasks: false, persistentOutput: true }, + renderer: verbose ? "verbose" : "default" + } + ); + try { + await tasks.run(); + } catch (e) { + (0, import_telemetry.errorTelemetry)(process.argv, e.message); + console.error(import_colors.default.error(e.message)); + process.exit(e?.exitCode || 1); + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + handler +}); diff --git a/packages/cli/dist/commands/experimental/templates/docker/Dockerfile b/packages/cli/dist/commands/experimental/templates/docker/Dockerfile new file mode 100644 index 0000000000..46263f0c92 --- /dev/null +++ b/packages/cli/dist/commands/experimental/templates/docker/Dockerfile @@ -0,0 +1,149 @@ +# base +# ---- +FROM node:20-bookworm-slim as base + +RUN corepack enable + +# We tried to make the Dockerfile as lean as possible. In some cases, that means we excluded a dependency your project needs. +# By far the most common is Python. If you're running into build errors because `python3` isn't available, +# uncomment the line below here and in other stages as necessary: +RUN apt-get update && apt-get install -y \ + openssl \ + # python3 make gcc \ + && rm -rf /var/lib/apt/lists/* + +USER node +WORKDIR /home/node/app + +COPY --chown=node:node .yarnrc.yml . +COPY --chown=node:node package.json . +COPY --chown=node:node api/package.json api/ +COPY --chown=node:node web/package.json web/ +COPY --chown=node:node yarn.lock . + +RUN mkdir -p /home/node/.yarn/berry/index +RUN mkdir -p /home/node/.cache + +RUN --mount=type=cache,target=/home/node/.yarn/berry/cache,uid=1000 \ + --mount=type=cache,target=/home/node/.cache,uid=1000 \ + CI=1 yarn install + +COPY --chown=node:node redwood.toml . +COPY --chown=node:node graphql.config.js . +COPY --chown=node:node .env.defaults .env.defaults + +# api build +# --------- +FROM base as api_build + +# If your api side build relies on build-time environment variables, +# specify them here as ARGs. (But don't put secrets in your Dockerfile!) +# +# ARG MY_BUILD_TIME_ENV_VAR + +COPY --chown=node:node api api +RUN yarn rw build api + +# web prerender build +# ------------------- +FROM api_build as web_build_with_prerender + +COPY --chown=node:node web web +RUN yarn rw build web + +# web build +# --------- +FROM base as web_build + +COPY --chown=node:node web web +RUN yarn rw build web --no-prerender + +# api serve +# --------- +FROM node:20-bookworm-slim as api_serve + +RUN corepack enable + +RUN apt-get update && apt-get install -y \ + openssl \ + # python3 make gcc \ + && rm -rf /var/lib/apt/lists/* + +USER node +WORKDIR /home/node/app + +COPY --chown=node:node .yarnrc.yml . +COPY --chown=node:node package.json . +COPY --chown=node:node api/package.json api/ +COPY --chown=node:node yarn.lock . + +RUN mkdir -p /home/node/.yarn/berry/index +RUN mkdir -p /home/node/.cache + +RUN --mount=type=cache,target=/home/node/.yarn/berry/cache,uid=1000 \ + --mount=type=cache,target=/home/node/.cache,uid=1000 \ + CI=1 yarn workspaces focus api --production + +COPY --chown=node:node redwood.toml . +COPY --chown=node:node graphql.config.js . +COPY --chown=node:node .env.defaults .env.defaults + +COPY --chown=node:node --from=api_build /home/node/app/api/dist /home/node/app/api/dist +COPY --chown=node:node --from=api_build /home/node/app/api/db /home/node/app/api/db +COPY --chown=node:node --from=api_build /home/node/app/node_modules/.prisma /home/node/app/node_modules/.prisma + +ENV NODE_ENV=production + +CMD [ "node_modules/.bin/rw-server", "api" ] + +# web serve +# --------- +FROM node:20-bookworm-slim as web_serve + +RUN corepack enable + +USER node +WORKDIR /home/node/app + +COPY --chown=node:node .yarnrc.yml . +COPY --chown=node:node package.json . +COPY --chown=node:node web/package.json web/ +COPY --chown=node:node yarn.lock . + +RUN mkdir -p /home/node/.yarn/berry/index +RUN mkdir -p /home/node/.cache + +RUN --mount=type=cache,target=/home/node/.yarn/berry/cache,uid=1000 \ + --mount=type=cache,target=/home/node/.cache,uid=1000 \ + CI=1 yarn workspaces focus web --production + +COPY --chown=node:node redwood.toml . +COPY --chown=node:node graphql.config.js . +COPY --chown=node:node .env.defaults .env.defaults + +COPY --chown=node:node --from=web_build /home/node/app/web/dist /home/node/app/web/dist + +ENV NODE_ENV=production \ + API_PROXY_TARGET=http://api:8911 + +# We use the shell form here for variable expansion. +CMD "node_modules/.bin/rw-web-server" "--api-proxy-target" "$API_PROXY_TARGET" + +# console +# ------- +FROM base as console + +# To add more packages: +# +# ``` +# USER root +# +# RUN apt-get update && apt-get install -y \ +# curl +# +# USER node +# ``` + +COPY --chown=node:node api api +COPY --chown=node:node web web +COPY --chown=node:node scripts scripts diff --git a/packages/cli/dist/commands/experimental/templates/docker/docker-compose.dev.yml b/packages/cli/dist/commands/experimental/templates/docker/docker-compose.dev.yml new file mode 100644 index 0000000000..af3a04e975 --- /dev/null +++ b/packages/cli/dist/commands/experimental/templates/docker/docker-compose.dev.yml @@ -0,0 +1,60 @@ +version: "3.8" + +services: + redwood: + build: + context: . + dockerfile: ./Dockerfile + target: base + command: yarn rw dev + volumes: + - .:/home/node/app + - node_modules:/home/node/app/node_modules + ports: + - "8910:8910" + depends_on: + - db + environment: + - DATABASE_URL=postgresql://redwood:redwood@db:5432/redwood + - TEST_DATABASE_URL=postgresql://redwood:redwood@db:5432/redwood_test + - SESSION_SECRET=super_secret_session_key_change_me_in_production_please + - CI= + - NODE_ENV=development + - REDWOOD_API_HOST=0.0.0.0 + + db: + image: postgres:16-bookworm + environment: + POSTGRES_USER: redwood + POSTGRES_PASSWORD: redwood + POSTGRES_DB: redwood + ports: + - "5432:5432" + volumes: + - postgres:/var/lib/postgresql/data + + # After starting with `docker compose -f ./docker-compose.dev.yml up`, + # use the console to run commands in the container: + # + # ``` + # docker compose -f ./docker-compose.dev.yml run --rm -it console /bin/bash + # root@...:/home/node/app# yarn rw prisma migrate dev + # ``` + console: + user: root + build: + context: . + dockerfile: ./Dockerfile + target: console + tmpfs: + - /tmp + command: "true" + environment: + - DATABASE_URL=postgresql://redwood:redwood@db:5432/redwood + - TEST_DATABASE_URL=postgresql://redwood:redwood@db:5432/redwood_test + depends_on: + - db + +volumes: + node_modules: + postgres: diff --git a/packages/cli/dist/commands/experimental/templates/docker/docker-compose.prod.yml b/packages/cli/dist/commands/experimental/templates/docker/docker-compose.prod.yml new file mode 100644 index 0000000000..5a01e3651b --- /dev/null +++ b/packages/cli/dist/commands/experimental/templates/docker/docker-compose.prod.yml @@ -0,0 +1,60 @@ +version: "3.8" + +services: + api: + build: + context: . + dockerfile: ./Dockerfile + target: api_serve + ports: + - "8911:8911" + depends_on: + - db + environment: + - DATABASE_URL=postgresql://redwood:redwood@db:5432/redwood + - TEST_DATABASE_URL=postgresql://redwood:redwood@db:5432/redwood_test + - SESSION_SECRET=super_secret_session_key_change_me_in_production_please + + web: + build: + context: . + dockerfile: ./Dockerfile + target: web_serve + ports: + - "8910:8910" + depends_on: + - api + environment: + - API_PROXY_TARGET=http://api:8911 + + db: + image: postgres:16-bookworm + environment: + POSTGRES_USER: redwood + POSTGRES_PASSWORD: redwood + POSTGRES_DB: redwood + ports: + - "5432:5432" + volumes: + - ./postgres:/var/lib/postgresql/data + + # After starting with `docker compose -f ./docker-compose.prod.yml up`, + # use the console to run commands in the container: + # + # ``` + # docker compose -f ./docker-compose.prod.yml run --rm -it console /bin/bash + # ``` + console: + user: root + build: + context: . + dockerfile: ./Dockerfile + target: console + tmpfs: + - /tmp + command: "true" + environment: + - DATABASE_URL=postgresql://redwood:redwood@db:5432/redwood + - TEST_DATABASE_URL=postgresql://redwood:redwood@db:5432/redwood_test + depends_on: + - db diff --git a/packages/cli/dist/commands/experimental/templates/docker/dockerignore b/packages/cli/dist/commands/experimental/templates/docker/dockerignore new file mode 100644 index 0000000000..f1d225cca9 --- /dev/null +++ b/packages/cli/dist/commands/experimental/templates/docker/dockerignore @@ -0,0 +1,18 @@ +**/node_modules +**/dist +.redwood + +.env + +README.md +LICENSE + +.git +.gitignore + +.vscode +.editorconfig + +Dockerfile +docker-compose* +.dockerignore diff --git a/packages/cli/dist/commands/experimental/templates/opentelemetry.ts.template b/packages/cli/dist/commands/experimental/templates/opentelemetry.ts.template new file mode 100644 index 0000000000..26e9d28c8a --- /dev/null +++ b/packages/cli/dist/commands/experimental/templates/opentelemetry.ts.template @@ -0,0 +1,55 @@ +import { diag, DiagConsoleLogger, DiagLogLevel } from '@opentelemetry/api' +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http' +import { registerInstrumentations } from '@opentelemetry/instrumentation' +import { FastifyInstrumentation } from '@opentelemetry/instrumentation-fastify' +import { HttpInstrumentation } from '@opentelemetry/instrumentation-http' +import { Resource } from '@opentelemetry/resources' +import { + NodeTracerProvider, + SimpleSpanProcessor, +} from '@opentelemetry/sdk-trace-node' +import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions' +import { PrismaInstrumentation } from '@prisma/instrumentation' + +import { getConfig } from '@redwoodjs/project-config' + +// You may wish to set this to DiagLogLevel.DEBUG when you need to debug opentelemetry itself +diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.INFO) + +const resource = Resource.default().merge( + new Resource({ + [SemanticResourceAttributes.SERVICE_NAME]: 'redwood-app', + [SemanticResourceAttributes.SERVICE_VERSION]: '0.0.0', + }) +) + +const studioPort = getConfig().studio.basePort +const exporter = new OTLPTraceExporter({ + // Update this URL to point to where your OTLP compatible collector is listening + // The redwood development studio (`yarn rw exp studio`) can collect your + // telemetry at `http://127.0.0.1:/v1/traces` (default PORT is 4318) + url: `http://127.0.0.1:${studioPort}/.redwood/functions/otel-trace`, + concurrencyLimit: 64, +}) + +// You may wish to switch to BatchSpanProcessor in production as it is the recommended choice for performance reasons +const processor = new SimpleSpanProcessor(exporter) + +const provider = new NodeTracerProvider({ + resource: resource, +}) +provider.addSpanProcessor(processor) + +// Optionally register instrumentation libraries here +registerInstrumentations({ + tracerProvider: provider, + instrumentations: [ + new HttpInstrumentation(), + new FastifyInstrumentation(), + new PrismaInstrumentation({ + middleware: true, + }), + ], +}) + +provider.register() diff --git a/packages/cli/dist/commands/experimental/templates/rsc/AboutCounter.tsx.template b/packages/cli/dist/commands/experimental/templates/rsc/AboutCounter.tsx.template new file mode 100644 index 0000000000..c86915e87f --- /dev/null +++ b/packages/cli/dist/commands/experimental/templates/rsc/AboutCounter.tsx.template @@ -0,0 +1,20 @@ +'use client' + +import React from 'react' + +// @ts-expect-error no types +import styles from './Counter.module.css' +import './Counter.css' + +export const AboutCounter = () => { + const [count, setCount] = React.useState(0) + + return ( +
+

Count: {count}

+ +

This is a client component.

+

RSC on client: {globalThis.RWJS_EXP_RSC ? 'enabled' : 'disabled'}

+
+ ) +} diff --git a/packages/cli/dist/commands/experimental/templates/rsc/AboutPage.css.template b/packages/cli/dist/commands/experimental/templates/rsc/AboutPage.css.template new file mode 100644 index 0000000000..995b3bbde1 --- /dev/null +++ b/packages/cli/dist/commands/experimental/templates/rsc/AboutPage.css.template @@ -0,0 +1,2 @@ +.about-page { +} diff --git a/packages/cli/dist/commands/experimental/templates/rsc/AboutPage.tsx.template b/packages/cli/dist/commands/experimental/templates/rsc/AboutPage.tsx.template new file mode 100644 index 0000000000..5ad03a9ecb --- /dev/null +++ b/packages/cli/dist/commands/experimental/templates/rsc/AboutPage.tsx.template @@ -0,0 +1,17 @@ +import { AboutCounter } from 'src/components/Counter/AboutCounter' + +import './AboutPage.css' + +const AboutPage = () => { + return ( +
+
+

About Redwood

+ +

RSC on server: {globalThis.RWJS_EXP_RSC ? 'enabled' : 'disabled'}

+
+
+ ) +} + +export default AboutPage diff --git a/packages/cli/dist/commands/experimental/templates/rsc/Counter.css.template b/packages/cli/dist/commands/experimental/templates/rsc/Counter.css.template new file mode 100644 index 0000000000..4cbd74d7d5 --- /dev/null +++ b/packages/cli/dist/commands/experimental/templates/rsc/Counter.css.template @@ -0,0 +1,7 @@ +/** + * This should affect all h3 elements on the page, both server components and + * client components. This is just standard CSS stuff + */ +h3 { + color: orange; +} diff --git a/packages/cli/dist/commands/experimental/templates/rsc/Counter.module.css.template b/packages/cli/dist/commands/experimental/templates/rsc/Counter.module.css.template new file mode 100644 index 0000000000..736b0da868 --- /dev/null +++ b/packages/cli/dist/commands/experimental/templates/rsc/Counter.module.css.template @@ -0,0 +1,3 @@ +.header { + font-style: italic; +} diff --git a/packages/cli/dist/commands/experimental/templates/rsc/Counter.tsx.template b/packages/cli/dist/commands/experimental/templates/rsc/Counter.tsx.template new file mode 100644 index 0000000000..a6e38c4059 --- /dev/null +++ b/packages/cli/dist/commands/experimental/templates/rsc/Counter.tsx.template @@ -0,0 +1,19 @@ +'use client' + +import React from 'react' + +// @ts-expect-error no types +import styles from './Counter.module.css' +import './Counter.css' + +export const Counter = () => { + const [count, setCount] = React.useState(0) + + return ( +
+

Count: {count}

+ +

This is a client component.

+
+ ) +} diff --git a/packages/cli/dist/commands/experimental/templates/rsc/HomePage.css.template b/packages/cli/dist/commands/experimental/templates/rsc/HomePage.css.template new file mode 100644 index 0000000000..9be6b50cd8 --- /dev/null +++ b/packages/cli/dist/commands/experimental/templates/rsc/HomePage.css.template @@ -0,0 +1,2 @@ +.home-page { +} diff --git a/packages/cli/dist/commands/experimental/templates/rsc/HomePage.module.css.template b/packages/cli/dist/commands/experimental/templates/rsc/HomePage.module.css.template new file mode 100644 index 0000000000..29212ea814 --- /dev/null +++ b/packages/cli/dist/commands/experimental/templates/rsc/HomePage.module.css.template @@ -0,0 +1,3 @@ +.title { + color: green; +} diff --git a/packages/cli/dist/commands/experimental/templates/rsc/HomePage.tsx.template b/packages/cli/dist/commands/experimental/templates/rsc/HomePage.tsx.template new file mode 100644 index 0000000000..8acaa4eab6 --- /dev/null +++ b/packages/cli/dist/commands/experimental/templates/rsc/HomePage.tsx.template @@ -0,0 +1,19 @@ +import { Counter } from 'src/components/Counter' +// @ts-expect-error no types +import styles from './HomePage.module.css' + +import './HomePage.css' + +const HomePage = ({ name = 'Anonymous' }) => { + return ( +
+
+

Hello {name}!!

+

This is a server component.

+ +
+
+ ) +} + +export default HomePage diff --git a/packages/cli/dist/commands/experimental/templates/rsc/NavigationLayout.css.template b/packages/cli/dist/commands/experimental/templates/rsc/NavigationLayout.css.template new file mode 100644 index 0000000000..a3e7be665c --- /dev/null +++ b/packages/cli/dist/commands/experimental/templates/rsc/NavigationLayout.css.template @@ -0,0 +1,32 @@ +.navigation-layout { + & nav { + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px; + background-color: color-mix(in srgb, yellow 50%, transparent); + border-bottom: 2px dashed color-mix(in srgb, yellow 90%, black); + } + + & ul { + list-style: none; + display: flex; + margin: 0; + padding: 0; + } + + & li { + margin-right: 10px; + } + + & a { + text-decoration: none; + color: #333; + padding: 5px; + border-bottom: 2px solid transparent; + } + + & a:hover { + border-bottom: 2px solid #333; + } +} diff --git a/packages/cli/dist/commands/experimental/templates/rsc/NavigationLayout.tsx.template b/packages/cli/dist/commands/experimental/templates/rsc/NavigationLayout.tsx.template new file mode 100644 index 0000000000..4f13e19730 --- /dev/null +++ b/packages/cli/dist/commands/experimental/templates/rsc/NavigationLayout.tsx.template @@ -0,0 +1,27 @@ +import { Link, routes } from '@redwoodjs/router' + +import './NavigationLayout.css' + +type NavigationLayoutProps = { + children?: React.ReactNode +} + +const NavigationLayout = ({ children }: NavigationLayoutProps) => { + return ( +
+ +
{children}
+
+ ) +} + +export default NavigationLayout diff --git a/packages/cli/dist/commands/experimental/templates/rsc/Routes.tsx.template b/packages/cli/dist/commands/experimental/templates/rsc/Routes.tsx.template new file mode 100644 index 0000000000..2b99b964f0 --- /dev/null +++ b/packages/cli/dist/commands/experimental/templates/rsc/Routes.tsx.template @@ -0,0 +1,27 @@ +// In this file, all Page components from 'src/pages` are auto-imported. Nested +// directories are supported, and should be uppercase. Each subdirectory will be +// prepended onto the component name. +// +// Examples: +// +// 'src/pages/HomePage/HomePage.js' -> HomePage +// 'src/pages/Admin/BooksPage/BooksPage.js' -> AdminBooksPage + +import { Router, Route, Set } from '@redwoodjs/router' + +import NavigationLayout from 'src/layouts/NavigationLayout' +import NotFoundPage from 'src/pages/NotFoundPage' + +const Routes = () => { + return ( + + + + + + + + ) +} + +export default Routes diff --git a/packages/cli/dist/commands/experimental/templates/rsc/entries.ts.template b/packages/cli/dist/commands/experimental/templates/rsc/entries.ts.template new file mode 100644 index 0000000000..6259057e24 --- /dev/null +++ b/packages/cli/dist/commands/experimental/templates/rsc/entries.ts.template @@ -0,0 +1,15 @@ +import { defineEntries } from '@redwoodjs/vite/entries' + +export default defineEntries( + // getEntry + async (id) => { + switch (id) { + case 'AboutPage': + return import('./pages/AboutPage/AboutPage') + case 'HomePage': + return import('./pages/HomePage/HomePage') + default: + return null + } + } +) diff --git a/packages/cli/dist/commands/experimental/templates/rsc/index.css.template b/packages/cli/dist/commands/experimental/templates/rsc/index.css.template new file mode 100644 index 0000000000..57c14ee231 --- /dev/null +++ b/packages/cli/dist/commands/experimental/templates/rsc/index.css.template @@ -0,0 +1,4 @@ +html, body { + margin: 0; + padding: 0; +} diff --git a/packages/cli/dist/commands/experimental/templates/streamingSsr/Document.tsx.template b/packages/cli/dist/commands/experimental/templates/streamingSsr/Document.tsx.template new file mode 100644 index 0000000000..f989f21752 --- /dev/null +++ b/packages/cli/dist/commands/experimental/templates/streamingSsr/Document.tsx.template @@ -0,0 +1,27 @@ +import React from 'react' + +import { Css, Meta } from '@redwoodjs/web' +import type { TagDescriptor } from '@redwoodjs/web' + +interface DocumentProps { + children: React.ReactNode + css: string[] // array of css import strings + meta?: TagDescriptor[] +} + +export const Document: React.FC = ({ children, css, meta }) => { + return ( + + + + + + + + + +
{children}
+ + + ) +} diff --git a/packages/cli/dist/commands/experimental/templates/streamingSsr/entry.client.tsx.template b/packages/cli/dist/commands/experimental/templates/streamingSsr/entry.client.tsx.template new file mode 100644 index 0000000000..da6e98c196 --- /dev/null +++ b/packages/cli/dist/commands/experimental/templates/streamingSsr/entry.client.tsx.template @@ -0,0 +1,28 @@ +import { hydrateRoot, createRoot } from 'react-dom/client' + +import App from './App' +import { Document } from './Document' + +/** + * When `#redwood-app` isn't empty then it's very likely that you're using + * prerendering. So React attaches event listeners to the existing markup + * rather than replacing it. + * https://react.dev/reference/react-dom/client/hydrateRoot + */ +const redwoodAppElement = document.getElementById('redwood-app') + +if (redwoodAppElement.children?.length > 0) { + hydrateRoot( + document, + + + + ) +} else { + const root = createRoot(document) + root.render( + + + + ) +} diff --git a/packages/cli/dist/commands/experimental/templates/streamingSsr/entry.server.tsx.template b/packages/cli/dist/commands/experimental/templates/streamingSsr/entry.server.tsx.template new file mode 100644 index 0000000000..a52b268b77 --- /dev/null +++ b/packages/cli/dist/commands/experimental/templates/streamingSsr/entry.server.tsx.template @@ -0,0 +1,15 @@ +import App from './App' +import { Document } from './Document' + +interface Props { + css: string[] + meta?: any[] +} + +export const ServerEntry: React.FC = ({ css, meta }) => { + return ( + + + + ) +} diff --git a/packages/cli/dist/commands/experimental/util.js b/packages/cli/dist/commands/experimental/util.js new file mode 100644 index 0000000000..2238542a3d --- /dev/null +++ b/packages/cli/dist/commands/experimental/util.js @@ -0,0 +1,107 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var util_exports = {}; +__export(util_exports, { + getEpilogue: () => getEpilogue, + isRealtimeSetup: () => isRealtimeSetup, + isServerFileSetup: () => isServerFileSetup, + printTaskEpilogue: () => printTaskEpilogue, + realtimeExists: () => realtimeExists +}); +module.exports = __toCommonJS(util_exports); +var import_path = __toESM(require("path")); +var import_chalk = __toESM(require("chalk")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_terminal_link = __toESM(require("terminal-link")); +var import_lib = require("../../lib"); +var import_project = require("../../lib/project"); +const link = (topicId, isTerminal = false) => { + const communityLink = `https://community.redwoodjs.com/t/${topicId}`; + if (isTerminal) { + return (0, import_terminal_link.default)(communityLink, communityLink); + } else { + return communityLink; + } +}; +const getEpilogue = (command, description, topicId, isTerminal = false) => `This is an experimental feature to: ${description}. + +Please find documentation and links to provide feedback for ${command} at: + -> ${link( + topicId, + isTerminal +)}`; +const printTaskEpilogue = (command, description, topicId) => { + console.log( + `${import_chalk.default.hex("#ff845e")( + `------------------------------------------------------------------ + \u{1F9EA} ${import_chalk.default.green( + "Experimental Feature" + )} \u{1F9EA} +------------------------------------------------------------------` + )}` + ); + console.log(getEpilogue(command, description, topicId, false)); + console.log( + `${import_chalk.default.hex("#ff845e")( + "------------------------------------------------------------------" + )} +` + ); +}; +const isServerFileSetup = () => { + if (!(0, import_project.serverFileExists)()) { + throw new Error( + "RedwoodJS Realtime requires a serverful environment. Please run `yarn rw setup server-file` first." + ); + } + return true; +}; +const realtimeExists = () => { + const realtimePath = import_path.default.join( + (0, import_lib.getPaths)().api.lib, + `realtime.${(0, import_project.isTypeScriptProject)() ? "ts" : "js"}` + ); + return import_fs_extra.default.existsSync(realtimePath); +}; +const isRealtimeSetup = () => { + if (!realtimeExists) { + throw new Error( + "Adding realtime events requires that RedwoodJS Realtime be setup. Please run `yarn setup realtime` first." + ); + } + return true; +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + getEpilogue, + isRealtimeSetup, + isServerFileSetup, + printTaskEpilogue, + realtimeExists +}); diff --git a/packages/cli/dist/commands/generate.js b/packages/cli/dist/commands/generate.js new file mode 100644 index 0000000000..b574125484 --- /dev/null +++ b/packages/cli/dist/commands/generate.js @@ -0,0 +1,74 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var generate_exports = {}; +__export(generate_exports, { + aliases: () => aliases, + builder: () => builder, + command: () => command, + description: () => description +}); +module.exports = __toCommonJS(generate_exports); +var import_execa = __toESM(require("execa")); +var import_terminal_link = __toESM(require("terminal-link")); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +const command = "generate "; +const aliases = ["g"]; +const description = "Generate boilerplate code and type definitions"; +const builder = (yargs) => yargs.command("types", "Generate supplementary code", {}, () => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "generate types" + }); + try { + import_execa.default.sync("yarn rw-gen", { shell: true, stdio: "inherit" }); + } catch (error) { + process.exitCode = error.exitCode ?? 1; + } +}).commandDir("./generate", { + recurse: true, + // @NOTE This regex will ignore all commands nested more than two + // levels deep. + // e.g. /generate/hi.js & setup/hi/hi.js are picked up, but + // generate/hi/hello/bazinga.js will be ignored + // The [/\\] bit is for supporting both windows and unix style paths + // Also take care to not trip up on paths that have "setup" earlier + // in the path by eagerly matching in the start of the regexp + exclude: /.*[/\\]generate[/\\].*[/\\].*[/\\]/ +}).demandCommand().epilogue( + `Also see the ${(0, import_terminal_link.default)( + "Redwood CLI Reference", + "https://redwoodjs.com/docs/cli-commands#generate-alias-g" + )}` +); +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + aliases, + builder, + command, + description +}); diff --git a/packages/cli/dist/commands/generate/cell/cell.js b/packages/cli/dist/commands/generate/cell/cell.js new file mode 100644 index 0000000000..ebd93c0afe --- /dev/null +++ b/packages/cli/dist/commands/generate/cell/cell.js @@ -0,0 +1,192 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var cell_exports = {}; +__export(cell_exports, { + builder: () => builder, + command: () => command, + description: () => description, + files: () => files, + handler: () => handler +}); +module.exports = __toCommonJS(cell_exports); +var import_pascalcase = __toESM(require("pascalcase")); +var import_generate = require("@redwoodjs/internal/dist/generate/generate"); +var import_lib = require("../../../lib"); +var import_pluralHelpers = require("../../../lib/pluralHelpers"); +var import_rollback = require("../../../lib/rollback"); +var import_rwPluralize = require("../../../lib/rwPluralize"); +var import_schemaHelpers = require("../../../lib/schemaHelpers"); +var import_helpers = require("../helpers"); +var import_helpers2 = require("../helpers"); +var import_utils = require("./utils/utils"); +const COMPONENT_SUFFIX = "Cell"; +const REDWOOD_WEB_PATH_NAME = "components"; +const files = async ({ name, typescript, ...options }) => { + let cellName = (0, import_helpers2.removeGeneratorName)(name, "cell"); + let idName = "id"; + let idType, mockIdValues = [42, 43, 44], model = null; + let templateNameSuffix = ""; + const shouldGenerateList = ((0, import_pluralHelpers.isWordPluralizable)(cellName) ? (0, import_rwPluralize.isPlural)(cellName) : options.list) || options.list; + try { + model = await (0, import_schemaHelpers.getSchema)((0, import_pascalcase.default)((0, import_rwPluralize.singularize)(cellName))); + idName = (0, import_utils.getIdName)(model); + idType = (0, import_utils.getIdType)(model); + mockIdValues = idType === "String" ? mockIdValues.map((value) => `'${value}'`) : mockIdValues; + } catch { + idType = "Int"; + } + if (shouldGenerateList) { + cellName = (0, import_helpers2.forcePluralizeWord)(cellName); + templateNameSuffix = "List"; + } + let operationName = options.query; + if (operationName) { + const userSpecifiedOperationNameIsUnique = await (0, import_utils.operationNameIsUnique)(operationName); + if (!userSpecifiedOperationNameIsUnique) { + throw new Error(`Specified query name: "${operationName}" is not unique!`); + } + } else { + operationName = await (0, import_utils.uniqueOperationName)(cellName, { + list: shouldGenerateList + }); + } + const extension = typescript ? ".tsx" : ".jsx"; + const cellFile = await (0, import_helpers2.templateForComponentFile)({ + name: cellName, + suffix: COMPONENT_SUFFIX, + extension, + webPathSection: REDWOOD_WEB_PATH_NAME, + generator: "cell", + templatePath: `cell${templateNameSuffix}.tsx.template`, + templateVars: { + operationName, + idName, + idType + } + }); + const testFile = await (0, import_helpers2.templateForComponentFile)({ + name: cellName, + suffix: COMPONENT_SUFFIX, + extension: `.test${extension}`, + webPathSection: REDWOOD_WEB_PATH_NAME, + generator: "cell", + templatePath: "test.js.template" + }); + const storiesFile = await (0, import_helpers2.templateForComponentFile)({ + name: cellName, + suffix: COMPONENT_SUFFIX, + extension: `.stories${extension}`, + webPathSection: REDWOOD_WEB_PATH_NAME, + generator: "cell", + templatePath: "stories.tsx.template" + }); + const mockFile = await (0, import_helpers2.templateForComponentFile)({ + name: cellName, + suffix: COMPONENT_SUFFIX, + extension: typescript ? ".mock.ts" : ".mock.js", + webPathSection: REDWOOD_WEB_PATH_NAME, + generator: "cell", + templatePath: `mock${templateNameSuffix}.js.template`, + templateVars: { + idName, + mockIdValues + } + }); + const files2 = [cellFile]; + if (options.stories) { + files2.push(storiesFile); + } + if (options.tests) { + files2.push(testFile); + } + if (options.stories || options.tests) { + files2.push(mockFile); + } + return files2.reduce(async (accP, [outputPath, content]) => { + const acc = await accP; + const template = typescript ? content : await (0, import_lib.transformTSToJS)(outputPath, content); + return { + [outputPath]: template, + ...acc + }; + }, Promise.resolve({})); +}; +const { command, description, builder, handler } = (0, import_helpers2.createYargsForComponentGeneration)({ + componentName: "cell", + filesFn: files, + optionsObj: { + ...import_helpers.yargsDefaults, + list: { + alias: "l", + default: false, + description: "Use when you want to generate a cell for a list of the model name.", + type: "boolean" + }, + query: { + default: "", + description: "Use to enforce a specific query name within the generated cell - must be unique.", + type: "string" + } + }, + includeAdditionalTasks: ({ name: cellName }) => { + return [ + { + title: `Generating types ...`, + task: async (_ctx, task) => { + const queryFieldName = (0, import_lib.nameVariants)( + (0, import_helpers2.removeGeneratorName)(cellName, "cell") + ).camelName; + const projectHasSdl = await (0, import_utils.checkProjectForQueryField)(queryFieldName); + if (projectHasSdl) { + const { errors } = await (0, import_generate.generate)(); + for (const { message, error } of errors) { + console.error(message); + console.log(); + console.error(error); + console.log(); + } + (0, import_rollback.addFunctionToRollback)(import_generate.generate, true); + } else { + task.skip( + `Skipping type generation: no SDL defined for "${queryFieldName}". To generate types, run 'yarn rw g sdl ${queryFieldName}'.` + ); + } + } + } + ]; + } +}); +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + files, + handler +}); diff --git a/packages/cli/dist/commands/generate/cell/templates/cell.tsx.template b/packages/cli/dist/commands/generate/cell/templates/cell.tsx.template new file mode 100644 index 0000000000..e5d2ce24f3 --- /dev/null +++ b/packages/cli/dist/commands/generate/cell/templates/cell.tsx.template @@ -0,0 +1,34 @@ +import type { ${operationName}, ${operationName}Variables } from 'types/graphql' + +import type { + CellSuccessProps, + CellFailureProps, + TypedDocumentNode, +} from '@redwoodjs/web' + +export const QUERY: TypedDocumentNode< + ${operationName}, + ${operationName}Variables +> = gql` + query ${operationName}($id: ${idType}!) { + ${camelName}: ${camelName}(${idName}: $id) { + ${idName} + } + } +` + +export const Loading = () =>
Loading...
+ +export const Empty = () =>
Empty
+ +export const Failure = ({ + error, +}: CellFailureProps<${operationName}Variables>) => ( +
Error: {error?.message}
+) + +export const Success = ({ + ${camelName}, +}: CellSuccessProps<${operationName}, ${operationName}Variables>) => { + return
{JSON.stringify(${camelName})}
+} diff --git a/packages/cli/dist/commands/generate/cell/templates/cellList.tsx.template b/packages/cli/dist/commands/generate/cell/templates/cellList.tsx.template new file mode 100644 index 0000000000..51c823d137 --- /dev/null +++ b/packages/cli/dist/commands/generate/cell/templates/cellList.tsx.template @@ -0,0 +1,36 @@ +import type { ${operationName}, ${operationName}Variables } from 'types/graphql' + +import type { + CellSuccessProps, + CellFailureProps, + TypedDocumentNode, +} from '@redwoodjs/web' + +export const QUERY: TypedDocumentNode< + ${operationName}, + ${operationName}Variables +> = gql` + query ${operationName} { + ${camelName} { + ${idName} + } + } +` + +export const Loading = () =>
Loading...
+ +export const Empty = () =>
Empty
+ +export const Failure = ({ error }: CellFailureProps) => ( +
Error: {error?.message}
+) + +export const Success = ({ ${camelName} }: CellSuccessProps<${operationName}>) => { + return ( +
    + {${camelName}.map((item) => { + return
  • {JSON.stringify(item)}
  • + })} +
+ ) +} diff --git a/packages/cli/dist/commands/generate/cell/templates/mock.js.template b/packages/cli/dist/commands/generate/cell/templates/mock.js.template new file mode 100644 index 0000000000..63db0e4616 --- /dev/null +++ b/packages/cli/dist/commands/generate/cell/templates/mock.js.template @@ -0,0 +1,6 @@ +// Define your own mock data here: +export const standard = (/* vars, { ctx, req } */) => ({ + ${camelName}: { + ${idName}: ${mockIdValues[0]} + } + }) diff --git a/packages/cli/dist/commands/generate/cell/templates/mockList.js.template b/packages/cli/dist/commands/generate/cell/templates/mockList.js.template new file mode 100644 index 0000000000..237dc4a8ae --- /dev/null +++ b/packages/cli/dist/commands/generate/cell/templates/mockList.js.template @@ -0,0 +1,8 @@ +// Define your own mock data here: +export const standard = (/* vars, { ctx, req } */) => ({ + ${camelName}: [ + { ${idName}: ${mockIdValues[0]} }, + { ${idName}: ${mockIdValues[1]} }, + { ${idName}: ${mockIdValues[2]} } + ] + }) diff --git a/packages/cli/dist/commands/generate/cell/templates/stories.tsx.template b/packages/cli/dist/commands/generate/cell/templates/stories.tsx.template new file mode 100644 index 0000000000..1194bd07b1 --- /dev/null +++ b/packages/cli/dist/commands/generate/cell/templates/stories.tsx.template @@ -0,0 +1,35 @@ +import type { Meta, StoryObj } from '@storybook/react' + +import { Loading, Empty, Failure, Success } from './${pascalName}Cell' +import { standard } from './${pascalName}Cell.mock' + +const meta: Meta = { + title: 'Cells/${pascalName}Cell', + tags: ['autodocs'] +} + +export default meta + +export const loading: StoryObj = { + render: () => { + return Loading ? : <> + }, +} + +export const empty: StoryObj = { + render: () => { + return Empty ? : <> + } +} + +export const failure: StoryObj = { + render: (args) => { + return Failure ? : <> + } +} + +export const success: StoryObj = { + render: (args) => { + return Success ? : <> + } +} diff --git a/packages/cli/dist/commands/generate/cell/templates/test.js.template b/packages/cli/dist/commands/generate/cell/templates/test.js.template new file mode 100644 index 0000000000..31bf1dbcd5 --- /dev/null +++ b/packages/cli/dist/commands/generate/cell/templates/test.js.template @@ -0,0 +1,41 @@ +import { render } from '@redwoodjs/testing/web' +import { Loading, Empty, Failure, Success } from './${pascalName}Cell' +import { standard } from './${pascalName}Cell.mock' + +// Generated boilerplate tests do not account for all circumstances +// and can fail without adjustments, e.g. Float and DateTime types. +// Please refer to the RedwoodJS Testing Docs: +// https://redwoodjs.com/docs/testing#testing-cells +// https://redwoodjs.com/docs/testing#jest-expect-type-considerations + +describe('${pascalName}Cell', () => { + it('renders Loading successfully', () => { + expect(() => { + render() + }).not.toThrow() + }) + + it('renders Empty successfully', async () => { + expect(() => { + render() + }).not.toThrow() + }) + + it('renders Failure successfully', async () => { + expect(() => { + render() + }).not.toThrow() + }) + + // When you're ready to test the actual output of your component render + // you could test that, for example, certain text is present: + // + // 1. import { screen } from '@redwoodjs/testing/web' + // 2. Add test: expect(screen.getByText('Hello, world')).toBeInTheDocument() + + it('renders Success successfully', async () => { + expect(() => { + render() + }).not.toThrow() + }) +}) diff --git a/packages/cli/dist/commands/generate/cell/utils/utils.js b/packages/cli/dist/commands/generate/cell/utils/utils.js new file mode 100644 index 0000000000..f06170b92c --- /dev/null +++ b/packages/cli/dist/commands/generate/cell/utils/utils.js @@ -0,0 +1,82 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var utils_exports = {}; +__export(utils_exports, { + checkProjectForQueryField: () => checkProjectForQueryField, + getCellOperationNames: () => getCellOperationNames, + getIdName: () => getIdName, + getIdType: () => getIdType, + operationNameIsUnique: () => operationNameIsUnique, + uniqueOperationName: () => uniqueOperationName +}); +module.exports = __toCommonJS(utils_exports); +var import_pascalcase = __toESM(require("pascalcase")); +var import_gql = require("@redwoodjs/internal/dist/gql"); +const getCellOperationNames = async () => { + const { getProject } = await import("@redwoodjs/structure"); + return getProject().cells.map((x) => { + return x.queryOperationName; + }).filter(Boolean); +}; +const uniqueOperationName = async (name, { index = 1, list = false }) => { + let operationName = (0, import_pascalcase.default)( + index <= 1 ? `find_${name}_query` : `find_${name}_query_${index}` + ); + if (list) { + operationName = index <= 1 ? `${(0, import_pascalcase.default)(name)}Query` : `${(0, import_pascalcase.default)(name)}Query_${index}`; + } + const cellOperationNames = await getCellOperationNames(); + if (!cellOperationNames.includes(operationName)) { + return operationName; + } + return uniqueOperationName(name, { index: index + 1 }); +}; +const operationNameIsUnique = async (operationName) => { + const cellOperationNames = await getCellOperationNames(); + return !cellOperationNames.includes(operationName); +}; +const getIdType = (model) => { + return model.fields.find((field) => field.isId)?.type; +}; +const getIdName = (model) => { + return model.fields.find((field) => field.isId)?.name; +}; +const checkProjectForQueryField = async (queryFieldName) => { + const queryFields = await (0, import_gql.listQueryTypeFieldsInProject)(); + return queryFields.includes(queryFieldName); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + checkProjectForQueryField, + getCellOperationNames, + getIdName, + getIdType, + operationNameIsUnique, + uniqueOperationName +}); diff --git a/packages/cli/dist/commands/generate/component/component.js b/packages/cli/dist/commands/generate/component/component.js new file mode 100644 index 0000000000..586b0b3b20 --- /dev/null +++ b/packages/cli/dist/commands/generate/component/component.js @@ -0,0 +1,84 @@ +"use strict"; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var component_exports = {}; +__export(component_exports, { + builder: () => builder, + command: () => command, + description: () => description, + files: () => files, + handler: () => handler +}); +module.exports = __toCommonJS(component_exports); +var import_lib = require("../../../lib"); +var import_helpers = require("../helpers"); +const REDWOOD_WEB_PATH_NAME = "components"; +const files = async ({ name, typescript = false, ...options }) => { + const extension = typescript ? ".tsx" : ".jsx"; + const componentFile = await (0, import_helpers.templateForComponentFile)({ + name, + webPathSection: REDWOOD_WEB_PATH_NAME, + extension, + generator: "component", + templatePath: "component.tsx.template" + }); + const testFile = await (0, import_helpers.templateForComponentFile)({ + name, + extension: `.test${extension}`, + webPathSection: REDWOOD_WEB_PATH_NAME, + generator: "component", + templatePath: "test.tsx.template" + }); + const storiesFile = await (0, import_helpers.templateForComponentFile)({ + name, + extension: `.stories${extension}`, + webPathSection: REDWOOD_WEB_PATH_NAME, + generator: "component", + // Using two different template files here because we have a TS-specific + // information in a comment in the .tsx template + templatePath: typescript ? "stories.tsx.template" : "stories.jsx.template" + }); + const files2 = [componentFile]; + if (options.stories) { + files2.push(storiesFile); + } + if (options.tests) { + files2.push(testFile); + } + return files2.reduce(async (accP, [outputPath, content]) => { + const acc = await accP; + const template = typescript ? content : await (0, import_lib.transformTSToJS)(outputPath, content); + return { + [outputPath]: template, + ...acc + }; + }, Promise.resolve({})); +}; +const description = "Generate a component"; +const { command, builder, handler } = (0, import_helpers.createYargsForComponentGeneration)({ + componentName: "component", + filesFn: files +}); +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + files, + handler +}); diff --git a/packages/cli/dist/commands/generate/component/templates/component.tsx.template b/packages/cli/dist/commands/generate/component/templates/component.tsx.template new file mode 100644 index 0000000000..0f477bbd83 --- /dev/null +++ b/packages/cli/dist/commands/generate/component/templates/component.tsx.template @@ -0,0 +1,10 @@ +const ${pascalName} = () => { + return ( +
+

{'${pascalName}'}

+

{'Find me in ${outputPath}'}

+
+ ) +} + +export default ${pascalName} diff --git a/packages/cli/dist/commands/generate/component/templates/stories.jsx.template b/packages/cli/dist/commands/generate/component/templates/stories.jsx.template new file mode 100644 index 0000000000..dc614fa1a1 --- /dev/null +++ b/packages/cli/dist/commands/generate/component/templates/stories.jsx.template @@ -0,0 +1,17 @@ +// Pass props to your component by passing an `args` object to your story +// +// ```jsx +// export const Primary = { +// args: { +// propName: propValue +// } +// } +// ``` +// +// See https://storybook.js.org/docs/react/writing-stories/args. + +import ${pascalName} from './${pascalName}' + +export default { component: ${pascalName} } + +export const Primary = {} diff --git a/packages/cli/dist/commands/generate/component/templates/stories.tsx.template b/packages/cli/dist/commands/generate/component/templates/stories.tsx.template new file mode 100644 index 0000000000..5311ada81b --- /dev/null +++ b/packages/cli/dist/commands/generate/component/templates/stories.tsx.template @@ -0,0 +1,26 @@ +// Pass props to your component by passing an `args` object to your story +// +// ```tsx +// export const Primary: Story = { +// args: { +// propName: propValue +// } +// } +// ``` +// +// See https://storybook.js.org/docs/react/writing-stories/args. + +import type { Meta, StoryObj } from '@storybook/react' + +import ${pascalName} from './${pascalName}' + +const meta: Meta = { + component: ${pascalName}, + tags: ['autodocs'] +} + +export default meta + +type Story = StoryObj + +export const Primary: Story = {} diff --git a/packages/cli/dist/commands/generate/component/templates/test.tsx.template b/packages/cli/dist/commands/generate/component/templates/test.tsx.template new file mode 100644 index 0000000000..ba3294b425 --- /dev/null +++ b/packages/cli/dist/commands/generate/component/templates/test.tsx.template @@ -0,0 +1,14 @@ +import { render } from '@redwoodjs/testing/web' + +import ${pascalName} from './${pascalName}' + +// Improve this test with help from the Redwood Testing Doc: +// https://redwoodjs.com/docs/testing#testing-components + +describe('${pascalName}', () => { + it('renders successfully', () => { + expect(() => { + render(<${pascalName} />) + }).not.toThrow() + }) +}) diff --git a/packages/cli/dist/commands/generate/dataMigration/dataMigration.js b/packages/cli/dist/commands/generate/dataMigration/dataMigration.js new file mode 100644 index 0000000000..06502b9caa --- /dev/null +++ b/packages/cli/dist/commands/generate/dataMigration/dataMigration.js @@ -0,0 +1,135 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var dataMigration_exports = {}; +__export(dataMigration_exports, { + aliases: () => aliases, + builder: () => builder, + command: () => command, + description: () => description, + files: () => files, + handler: () => handler +}); +module.exports = __toCommonJS(dataMigration_exports); +var import_path = __toESM(require("path")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_listr2 = require("listr2"); +var import_param_case = require("param-case"); +var import_terminal_link = __toESM(require("terminal-link")); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_lib = require("../../../lib"); +var import_colors = __toESM(require("../../../lib/colors")); +var import_rollback = require("../../../lib/rollback"); +var import_helpers = require("../helpers"); +const POST_RUN_INSTRUCTIONS = `Next steps... + + ${import_colors.default.warning( + "After writing your migration, you can run it with:" +)} + + yarn rw dataMigrate up +`; +const TEMPLATE_PATHS = { + js: import_path.default.resolve(__dirname, "templates", "dataMigration.js.template"), + ts: import_path.default.resolve(__dirname, "templates", "dataMigration.ts.template") +}; +const files = ({ name, typescript }) => { + const now = (/* @__PURE__ */ new Date()).toISOString(); + const timestamp = now.split(".")[0].replace(/\D/g, ""); + const basename = `${timestamp}-${(0, import_param_case.paramCase)(name)}`; + const extension = typescript ? "ts" : "js"; + const outputFilename = basename + "." + extension; + const outputPath = import_path.default.join((0, import_lib.getPaths)().api.dataMigrations, outputFilename); + return { + [outputPath]: import_fs_extra.default.readFileSync(TEMPLATE_PATHS[extension]).toString() + }; +}; +const command = "data-migration "; +const aliases = ["dataMigration", "dm"]; +const description = "Generate a data migration"; +const builder = (yargs) => { + yargs.positional("name", { + description: "A descriptor of what this data migration does", + type: "string" + }).option("rollback", { + description: "Revert all generator actions if an error occurs", + type: "boolean", + default: true + }).epilogue( + `Also see the ${(0, import_terminal_link.default)( + "Redwood CLI Reference", + "https://redwoodjs.com/docs/cli-commands#generate-datamigration" + )}` + ); + Object.entries(import_helpers.yargsDefaults).forEach(([option, config]) => { + yargs.option(option, config); + }); +}; +const handler = async (args) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "generate data-migration", + force: args.force, + rollback: args.rollback + }); + (0, import_helpers.validateName)(args.name); + const tasks = new import_listr2.Listr( + [ + { + title: "Generating data migration file...", + task: () => { + return (0, import_lib.writeFilesTask)(files(args)); + } + }, + { + title: "Next steps...", + task: (_ctx, task) => { + task.title = POST_RUN_INSTRUCTIONS; + } + } + ].filter(Boolean), + { rendererOptions: { collapseSubtasks: false } } + ); + try { + if (args.rollback && !args.force) { + (0, import_rollback.prepareForRollback)(tasks); + } + await tasks.run(); + } catch (e) { + console.log(import_colors.default.error(e.message)); + process.exit(1); + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + aliases, + builder, + command, + description, + files, + handler +}); diff --git a/packages/cli/dist/commands/generate/dataMigration/templates/dataMigration.js.template b/packages/cli/dist/commands/generate/dataMigration/templates/dataMigration.js.template new file mode 100644 index 0000000000..97c911ed07 --- /dev/null +++ b/packages/cli/dist/commands/generate/dataMigration/templates/dataMigration.js.template @@ -0,0 +1,7 @@ +/** + * @typedef { import("@prisma/client").PrismaClient } PrismaClient + * @param {{db: PrismaClient}} db + */ +export default async ({ db }) => { + // Migration here... +} diff --git a/packages/cli/dist/commands/generate/dataMigration/templates/dataMigration.ts.template b/packages/cli/dist/commands/generate/dataMigration/templates/dataMigration.ts.template new file mode 100644 index 0000000000..ee17f451c9 --- /dev/null +++ b/packages/cli/dist/commands/generate/dataMigration/templates/dataMigration.ts.template @@ -0,0 +1,5 @@ +import type { PrismaClient } from '@prisma/client' + +export default async ({ db }: { db: PrismaClient }) => { + // Migration here... +} diff --git a/packages/cli/dist/commands/generate/dbAuth/dbAuth.js b/packages/cli/dist/commands/generate/dbAuth/dbAuth.js new file mode 100644 index 0000000000..571b532d26 --- /dev/null +++ b/packages/cli/dist/commands/generate/dbAuth/dbAuth.js @@ -0,0 +1,403 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var dbAuth_exports = {}; +__export(dbAuth_exports, { + builder: () => builder, + command: () => command, + description: () => description, + files: () => files, + handler: () => handler +}); +module.exports = __toCommonJS(dbAuth_exports); +var import_path = __toESM(require("path")); +var import_camel_case = require("camel-case"); +var import_enquirer = __toESM(require("enquirer")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_listr2 = require("listr2"); +var import_terminal_link = __toESM(require("terminal-link")); +var import_title_case = require("title-case"); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_lib = require("../../../lib"); +var import_colors = __toESM(require("../../../lib/colors")); +var import_rollback = require("../../../lib/rollback"); +var import_helpers = require("../helpers"); +var import_helpers2 = require("../helpers"); +const ROUTES = [ + ``, + ``, + ``, + `` +]; +const POST_INSTALL = ` ${import_colors.default.warning("Pages created! But you're not done yet:")} + + You'll need to tell your pages where to redirect after a user has logged in, + signed up, or reset their password. Look in LoginPage, SignupPage, + ForgotPasswordPage and ResetPasswordPage for these lines: + + if (isAuthenticated) { + navigate(routes.home()) + } + + and change the route to where you want them to go if the user is already + logged in. Also take a look in the onSubmit() functions in ForgotPasswordPage + and ResetPasswordPage to change where the user redirects to after submitting + those forms. + + Oh, and if you haven't already, add the necessary dbAuth functions and + app setup by running: + + yarn rw setup auth dbAuth + + Happy authenticating! +`; +const WEBAUTHN_POST_INSTALL = ` ${import_colors.default.warning("Pages created! But you're not done yet:")} + + You'll need to tell your pages where to redirect after a user has logged in, + signed up, or reset their password. In LoginPage, look for the \`REDIRECT\` + constant and change the route if it's something other than home(). + In SignupPage, ForgotPasswordPage and ResetPasswordPage look for these lines: + + if (isAuthenticated) { + navigate(routes.home()) + } + + and change the route to where you want them to go if the user is already + logged in. Also take a look in the onSubmit() functions in ForgotPasswordPage + and ResetPasswordPage to change where the user redirects to after submitting + those forms. + + Oh, and if you haven't already, add the necessary dbAuth functions and + app setup by running: + + yarn rw setup auth dbAuth + + Happy authenticating! +`; +const command = "dbAuth"; +const description = "Generate Login, Signup and Forgot Password pages for dbAuth"; +const builder = (yargs) => { + yargs.option("skip-forgot", { + description: "Skip generating the Forgot Password page", + type: "boolean", + default: false + }).option("skip-login", { + description: "Skip generating the login page", + type: "boolean", + default: false + }).option("skip-reset", { + description: "Skip generating the Reset Password page", + type: "boolean", + default: false + }).option("skip-signup", { + description: "Skip generating the signup page", + type: "boolean", + default: false + }).option("webauthn", { + alias: "w", + default: null, + description: "Include WebAuthn support (TouchID/FaceID)", + type: "boolean" + }).option("username-label", { + default: null, + description: "Override default form label for username field", + type: "string" + }).option("password-label", { + default: null, + description: "Override default form label for password field", + type: "string" + }).option("rollback", { + description: "Revert all generator actions if an error occurs", + type: "boolean", + default: true + }).epilogue( + `Also see the ${(0, import_terminal_link.default)( + "Redwood CLI Reference", + "https://redwoodjs.com/docs/authentication#self-hosted-auth-installation-and-setup" + )}` + ); + Object.entries(import_helpers.yargsDefaults).forEach(([option, config]) => { + yargs.option(option, config); + }); +}; +const files = async ({ + _tests, + typescript, + skipForgot, + skipLogin, + skipReset, + skipSignup, + webauthn, + usernameLabel, + passwordLabel +}) => { + const files2 = []; + usernameLabel = usernameLabel || "username"; + passwordLabel = passwordLabel || "password"; + const templateVars = { + usernameLowerCase: usernameLabel.toLowerCase(), + usernameCamelCase: (0, import_camel_case.camelCase)(usernameLabel), + usernameTitleCase: (0, import_title_case.titleCase)(usernameLabel), + passwordLowerCase: passwordLabel.toLowerCase(), + passwordCamelCase: (0, import_camel_case.camelCase)(passwordLabel), + passwordTitleCase: (0, import_title_case.titleCase)(passwordLabel) + }; + if (!skipForgot) { + files2.push( + await (0, import_helpers2.templateForComponentFile)({ + name: "ForgotPassword", + suffix: "Page", + extension: typescript ? ".tsx" : ".jsx", + webPathSection: "pages", + generator: "dbAuth", + templatePath: "forgotPassword.tsx.template", + templateVars + }) + ); + } + if (!skipLogin) { + files2.push( + await (0, import_helpers2.templateForComponentFile)({ + name: "Login", + suffix: "Page", + extension: typescript ? ".tsx" : ".jsx", + webPathSection: "pages", + generator: "dbAuth", + templatePath: webauthn ? "login.webAuthn.tsx.template" : "login.tsx.template", + templateVars + }) + ); + } + if (!skipReset) { + files2.push( + await (0, import_helpers2.templateForComponentFile)({ + name: "ResetPassword", + suffix: "Page", + extension: typescript ? ".tsx" : ".jsx", + webPathSection: "pages", + generator: "dbAuth", + templatePath: "resetPassword.tsx.template", + templateVars + }) + ); + } + if (!skipSignup) { + files2.push( + await (0, import_helpers2.templateForComponentFile)({ + name: "Signup", + suffix: "Page", + extension: typescript ? ".tsx" : ".jsx", + webPathSection: "pages", + generator: "dbAuth", + templatePath: "signup.tsx.template", + templateVars + }) + ); + } + if (files2.length === 0) { + console.info(import_colors.default.error("\nNo files to generate.\n")); + process.exit(0); + } + const scaffoldOutputPath = import_path.default.join((0, import_lib.getPaths)().web.src, "scaffold.css"); + if (!import_fs_extra.default.existsSync(scaffoldOutputPath)) { + const scaffoldTemplate = await (0, import_lib.generateTemplate)( + import_path.default.join( + __dirname, + "../scaffold/templates/assets/scaffold.css.template" + ), + { name: "scaffold" } + ); + files2.push([scaffoldOutputPath, scaffoldTemplate]); + } + return files2.reduce(async (accP, [outputPath, content]) => { + const acc = await accP; + let template = content; + if (outputPath.match(/\.[jt]sx?/) && !typescript) { + template = await (0, import_lib.transformTSToJS)(outputPath, content); + } + return { + [outputPath]: template, + ...acc + }; + }, Promise.resolve({})); +}; +const tasks = ({ + enquirer, + listr2, + force, + tests, + typescript, + skipForgot, + skipLogin, + skipReset, + skipSignup, + webauthn, + usernameLabel, + passwordLabel +}) => { + return new import_listr2.Listr( + [ + { + title: "Determining UI labels...", + skip: () => { + return usernameLabel && passwordLabel; + }, + task: async (ctx, task) => { + return task.newListr([ + { + title: "Username label", + task: async (subctx, subtask) => { + if (usernameLabel) { + subtask.skip( + `Argument username-label is set, using: "${usernameLabel}"` + ); + return; + } + usernameLabel = await subtask.prompt({ + type: "input", + name: "username", + message: "What would you like the username label to be:", + default: "Username" + }); + subtask.title = `Username label: "${usernameLabel}"`; + } + }, + { + title: "Password label", + task: async (subctx, subtask) => { + if (passwordLabel) { + subtask.skip( + `Argument password-label passed, using: "${passwordLabel}"` + ); + return; + } + passwordLabel = await subtask.prompt({ + type: "input", + name: "password", + message: "What would you like the password label to be:", + default: "Password" + }); + subtask.title = `Password label: "${passwordLabel}"`; + } + } + ]); + } + }, + { + title: "Querying WebAuthn addition...", + task: async (ctx, task) => { + if (webauthn != null) { + task.skip( + `Querying WebAuthn addition: argument webauthn passed, WebAuthn ${webauthn ? "" : "not"} included` + ); + return; + } + const response = await task.prompt({ + type: "confirm", + name: "answer", + message: `Enable WebAuthn support (TouchID/FaceID) on LoginPage? See https://redwoodjs.com/docs/auth/dbAuth#webAuthn`, + default: false + }); + webauthn = response; + task.title = `Querying WebAuthn addition: WebAuthn addition ${webauthn ? "" : "not"} included`; + } + }, + { + title: "Creating pages...", + task: async () => { + const filesObj = await files({ + tests, + typescript, + skipForgot, + skipLogin, + skipReset, + skipSignup, + webauthn, + usernameLabel, + passwordLabel + }); + return (0, import_lib.writeFilesTask)(filesObj, { + overwriteExisting: force + }); + } + }, + { + title: "Adding routes...", + task: async () => { + (0, import_lib.addRoutesToRouterTask)(ROUTES); + } + }, + { + title: "Adding scaffold import...", + task: () => (0, import_lib.addScaffoldImport)() + }, + { + title: "One more thing...", + task: () => { + } + } + ], + { + silentRendererCondition: () => listr2?.silentRendererCondition, + rendererOptions: { collapseSubtasks: false }, + injectWrapper: { enquirer: enquirer || new import_enquirer.default() }, + exitOnError: true + } + ); +}; +const handler = async (yargs) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "generate dbAuth", + skipForgot: yargs.skipForgot, + skipLogin: yargs.skipLogin, + skipReset: yargs.skipReset, + skipSignup: yargs.skipSignup, + webauthn: yargs.webauthn, + force: yargs.force, + rollback: yargs.rollback + }); + const t = tasks({ ...yargs }); + try { + if (yargs.rollback && !yargs.force) { + (0, import_rollback.prepareForRollback)(t); + } + await t.run(); + console.log(""); + console.log(yargs.webauthn ? WEBAUTHN_POST_INSTALL : POST_INSTALL); + } catch (e) { + console.log(import_colors.default.error(e.message)); + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + files, + handler +}); diff --git a/packages/cli/dist/commands/generate/dbAuth/templates/forgotPassword.tsx.template b/packages/cli/dist/commands/generate/dbAuth/templates/forgotPassword.tsx.template new file mode 100644 index 0000000000..06c34f0eaf --- /dev/null +++ b/packages/cli/dist/commands/generate/dbAuth/templates/forgotPassword.tsx.template @@ -0,0 +1,94 @@ +import { useEffect, useRef } from 'react' + +import { Form, Label, TextField, Submit, FieldError } from '@redwoodjs/forms' +import { navigate, routes } from '@redwoodjs/router' +import { Metadata } from '@redwoodjs/web' +import { toast, Toaster } from '@redwoodjs/web/toast' + +import { useAuth } from 'src/auth' + +const ForgotPasswordPage = () => { + const { isAuthenticated, forgotPassword } = useAuth() + + useEffect(() => { + if (isAuthenticated) { + navigate(routes.home()) + } + }, [isAuthenticated]) + + const ${usernameCamelCase}Ref = useRef(null) + useEffect(() => { + ${usernameCamelCase}Ref?.current?.focus() + }, []) + + const onSubmit = async (data: { ${usernameCamelCase}: string }) => { + const response = await forgotPassword(data.${usernameCamelCase}) + + if (response.error) { + toast.error(response.error) + } else { + // The function `forgotPassword.handler` in api/src/functions/auth.js has + // been invoked, let the user know how to get the link to reset their + // password (sent in email, perhaps?) + toast.success( + 'A link to reset your ${passwordLowerCase} was sent to ' + response.email + ) + navigate(routes.login()) + } + } + + return ( + <> + + +
+ +
+
+
+

+ Forgot ${passwordTitleCase} +

+
+ +
+
+
+
+ + + + +
+ +
+ Submit +
+
+
+
+
+
+
+ + ) +} + +export default ForgotPasswordPage diff --git a/packages/cli/dist/commands/generate/dbAuth/templates/login.tsx.template b/packages/cli/dist/commands/generate/dbAuth/templates/login.tsx.template new file mode 100644 index 0000000000..88fac90acf --- /dev/null +++ b/packages/cli/dist/commands/generate/dbAuth/templates/login.tsx.template @@ -0,0 +1,131 @@ +import { useRef } from 'react' +import { useEffect } from 'react' + +import { + Form, + Label, + TextField, + PasswordField, + Submit, + FieldError, +} from '@redwoodjs/forms' +import { Link, navigate, routes } from '@redwoodjs/router' +import { Metadata } from '@redwoodjs/web' +import { toast, Toaster } from '@redwoodjs/web/toast' + +import { useAuth } from 'src/auth' + +const LoginPage = () => { + const { isAuthenticated, logIn } = useAuth() + + useEffect(() => { + if (isAuthenticated) { + navigate(routes.home()) + } + }, [isAuthenticated]) + + const ${usernameCamelCase}Ref = useRef(null) + useEffect(() => { + ${usernameCamelCase}Ref.current?.focus() + }, []) + + const onSubmit = async (data: Record) => { + const response = await logIn({ username: data.${usernameCamelCase}, password: data.${passwordCamelCase} }) + + if (response.message) { + toast(response.message) + } else if (response.error) { + toast.error(response.error) + } else { + toast.success('Welcome back!') + } + } + + return ( + <> + + +
+ +
+
+
+

Login

+
+ +
+
+
+ + + + + + + + +
+ + Forgot ${passwordTitleCase}? + +
+ + + +
+ Login +
+ +
+
+
+
+ Don't have an account?{' '} + + Sign up! + +
+
+
+ + ) +} + +export default LoginPage diff --git a/packages/cli/dist/commands/generate/dbAuth/templates/login.webAuthn.tsx.template b/packages/cli/dist/commands/generate/dbAuth/templates/login.webAuthn.tsx.template new file mode 100644 index 0000000000..bf25bef4cb --- /dev/null +++ b/packages/cli/dist/commands/generate/dbAuth/templates/login.webAuthn.tsx.template @@ -0,0 +1,265 @@ +import { useRef, useState } from 'react' +import { useEffect } from 'react' + +import { + Form, + Label, + TextField, + PasswordField, + Submit, + FieldError, +} from '@redwoodjs/forms' +import { Link, navigate, routes } from '@redwoodjs/router' +import { Metadata } from '@redwoodjs/web' +import { toast, Toaster } from '@redwoodjs/web/toast' + +import { useAuth } from 'src/auth' + +const WELCOME_MESSAGE = 'Welcome back!' +const REDIRECT = routes.home() + +const LoginPage = ({ type }) => { + const { + isAuthenticated, + client: webAuthn, + loading, + logIn, + reauthenticate, + } = useAuth() + const [shouldShowWebAuthn, setShouldShowWebAuthn] = useState(false) + const [showWebAuthn, setShowWebAuthn] = useState( + webAuthn.isEnabled() && type !== 'password' + ) + + // should redirect right after login or wait to show the webAuthn prompts? + useEffect(() => { + if (isAuthenticated && (!shouldShowWebAuthn || webAuthn.isEnabled())) { + navigate(REDIRECT) + } + }, [isAuthenticated, shouldShowWebAuthn]) + + // if WebAuthn is enabled, show the prompt as soon as the page loads + useEffect(() => { + if (!loading && !isAuthenticated && showWebAuthn) { + onAuthenticate() + } + }, [loading, isAuthenticated]) + + // focus on the ${usernameLowerCase} field as soon as the page loads + const ${usernameCamelCase}Ref = useRef() + useEffect(() => { + ${usernameCamelCase}Ref.current && ${usernameCamelCase}Ref.current.focus() + }, []) + + const onSubmit = async (data) => { + const webAuthnSupported = await webAuthn.isSupported() + + if (webAuthnSupported) { + setShouldShowWebAuthn(true) + } + const response = await logIn({ username: data.${usernameCamelCase}, password: data.${passwordCamelCase} }) + + if (response.message) { + // auth details good, but user not logged in + toast(response.message) + } else if (response.error) { + // error while authenticating + toast.error(response.error) + } else { + // user logged in + if (webAuthnSupported) { + setShowWebAuthn(true) + } else { + toast.success(WELCOME_MESSAGE) + } + } + } + + const onAuthenticate = async () => { + try { + await webAuthn.authenticate() + await reauthenticate() + toast.success(WELCOME_MESSAGE) + navigate(REDIRECT) + } catch (e) { + if (e.name === 'WebAuthnDeviceNotFoundError') { + toast.error( + 'Device not found, log in with ${usernameTitleCase}/${passwordTitleCase} to continue' + ) + setShowWebAuthn(false) + } else { + toast.error(e.message) + } + } + } + + const onRegister = async () => { + try { + await webAuthn.register() + toast.success(WELCOME_MESSAGE) + navigate(REDIRECT) + } catch (e) { + toast.error(e.message) + } + } + + const onSkip = () => { + toast.success(WELCOME_MESSAGE) + setShouldShowWebAuthn(false) + } + + const AuthWebAuthnPrompt = () => { + return ( +
+

WebAuthn Login Enabled

+

Log in with your fingerprint, face or PIN

+
+ +
+
+ ) + } + + const RegisterWebAuthnPrompt = () => ( +
+

No more ${passwordTitleCase}s!

+

+ Depending on your device you can log in with your fingerprint, face or + PIN next time. +

+
+ + +
+
+ ) + + const PasswordForm = () => ( +
+ + + + + + + + +
+ + Forgot ${passwordTitleCase}? + +
+ + + +
+ Login +
+ + ) + + const formToRender = () => { + if (showWebAuthn) { + if (webAuthn.isEnabled()) { + return + } else { + return + } + } else { + return + } + } + + const linkToRender = () => { + if (showWebAuthn) { + if (webAuthn.isEnabled()) { + return ( + + ) + } + } else { + return ( +
+ Don't have an account?{' '} + + Sign up! + +
+ ) + } + } + + if (loading) { + return null + } + + return ( + <> + + +
+ +
+
+
+

Login

+
+ +
+
{formToRender()}
+
+
+ {linkToRender()} +
+
+ + ) +} + +export default LoginPage diff --git a/packages/cli/dist/commands/generate/dbAuth/templates/resetPassword.tsx.template b/packages/cli/dist/commands/generate/dbAuth/templates/resetPassword.tsx.template new file mode 100644 index 0000000000..bd84a9783d --- /dev/null +++ b/packages/cli/dist/commands/generate/dbAuth/templates/resetPassword.tsx.template @@ -0,0 +1,121 @@ +import { useEffect, useRef, useState } from 'react' + +import { + Form, + Label, + PasswordField, + Submit, + FieldError, +} from '@redwoodjs/forms' +import { navigate, routes } from '@redwoodjs/router' +import { Metadata } from '@redwoodjs/web' +import { toast, Toaster } from '@redwoodjs/web/toast' + +import { useAuth } from 'src/auth' + +const ResetPasswordPage = ({ resetToken }: { resetToken: string }) => { + const { isAuthenticated, reauthenticate, validateResetToken, resetPassword } = + useAuth() + const [enabled, setEnabled] = useState(true) + + useEffect(() => { + if (isAuthenticated) { + navigate(routes.home()) + } + }, [isAuthenticated]) + + useEffect(() => { + const validateToken = async () => { + const response = await validateResetToken(resetToken) + if (response.error) { + setEnabled(false) + toast.error(response.error) + } else { + setEnabled(true) + } + } + validateToken() + }, [resetToken, validateResetToken]) + + const ${passwordCamelCase}Ref = useRef(null) + useEffect(() => { + ${passwordCamelCase}Ref.current?.focus() + }, []) + + const onSubmit = async (data: Record) => { + const response = await resetPassword({ + resetToken, + password: data.${passwordCamelCase}, + }) + + if (response.error) { + toast.error(response.error) + } else { + toast.success('${passwordTitleCase} changed!') + await reauthenticate() + navigate(routes.login()) + } + } + + return ( + <> + + +
+ +
+
+
+

+ Reset ${passwordTitleCase} +

+
+ +
+
+
+
+ + + + +
+ +
+ + Submit + +
+
+
+
+
+
+
+ + ) +} + +export default ResetPasswordPage diff --git a/packages/cli/dist/commands/generate/dbAuth/templates/signup.tsx.template b/packages/cli/dist/commands/generate/dbAuth/templates/signup.tsx.template new file mode 100644 index 0000000000..e3e1196f9b --- /dev/null +++ b/packages/cli/dist/commands/generate/dbAuth/templates/signup.tsx.template @@ -0,0 +1,124 @@ +import { useRef } from 'react' +import { useEffect } from 'react' + +import { + Form, + Label, + TextField, + PasswordField, + FieldError, + Submit, +} from '@redwoodjs/forms' +import { Link, navigate, routes } from '@redwoodjs/router' +import { Metadata } from '@redwoodjs/web' +import { toast, Toaster } from '@redwoodjs/web/toast' + +import { useAuth } from 'src/auth' + +const SignupPage = () => { + const { isAuthenticated, signUp } = useAuth() + + useEffect(() => { + if (isAuthenticated) { + navigate(routes.home()) + } + }, [isAuthenticated]) + + // focus on ${usernameLowerCase} box on page load + const ${usernameCamelCase}Ref = useRef(null) + useEffect(() => { + ${usernameCamelCase}Ref.current?.focus() + }, []) + + const onSubmit = async (data: Record) => { + const response = await signUp({ username: data.${usernameCamelCase}, password: data.${passwordCamelCase} }) + + if (response.message) { + toast(response.message) + } else if (response.error) { + toast.error(response.error) + } else { + // user is signed in automatically + toast.success('Welcome!') + } + } + + return ( + <> + + +
+ +
+
+
+

Signup

+
+ +
+
+
+ + + + + + + + +
+ + Sign Up + +
+ +
+
+
+
+ Already have an account?{' '} + + Log in! + +
+
+
+ + ) +} + +export default SignupPage diff --git a/packages/cli/dist/commands/generate/directive/directive.js b/packages/cli/dist/commands/generate/directive/directive.js new file mode 100644 index 0000000000..56a1e37735 --- /dev/null +++ b/packages/cli/dist/commands/generate/directive/directive.js @@ -0,0 +1,209 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var directive_exports = {}; +__export(directive_exports, { + builder: () => builder, + command: () => command, + description: () => description, + files: () => files, + handler: () => handler +}); +module.exports = __toCommonJS(directive_exports); +var import_path = __toESM(require("path")); +var import_camelcase = __toESM(require("camelcase")); +var import_execa = __toESM(require("execa")); +var import_listr2 = require("listr2"); +var import_prompts = __toESM(require("prompts")); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_project_config = require("@redwoodjs/project-config"); +var import_lib = require("../../../lib"); +var import_colors = __toESM(require("../../../lib/colors")); +var import_rollback = require("../../../lib/rollback"); +var import_helpers = require("../helpers"); +var import_helpers2 = require("../helpers"); +const files = async ({ name, typescript = false, type, tests }) => { + if (tests === void 0) { + tests = (0, import_project_config.getConfig)().generate.tests; + } + if (!type) { + throw new Error("You must specify a directive type"); + } + const camelName = (0, import_camelcase.default)(name); + const outputFilename = `${camelName}.${typescript ? "ts" : "js"}`; + const directiveFile = await (0, import_helpers2.templateForComponentFile)({ + name, + extension: typescript ? ".ts" : ".js", + generator: "directive", + templatePath: `${type}.directive.ts.template`, + outputPath: import_path.default.join((0, import_lib.getPaths)().api.directives, camelName, outputFilename), + templateVars: { camelName } + }); + const files2 = [directiveFile]; + if (tests) { + const testOutputFilename = `${(0, import_camelcase.default)(name)}.test.${typescript ? "ts" : "js"}`; + const testFile = await (0, import_helpers2.templateForComponentFile)({ + name, + extension: typescript ? ".test.ts" : ".test.js", + generator: "directive", + templatePath: `${type}.directive.test.ts.template`, + outputPath: import_path.default.join( + (0, import_lib.getPaths)().api.directives, + camelName, + testOutputFilename + ), + templateVars: { camelName } + }); + files2.push(testFile); + } + return files2.reduce(async (accP, [outputPath, content]) => { + const acc = await accP; + const template = typescript ? content : await (0, import_lib.transformTSToJS)(outputPath, content); + return { + [outputPath]: template, + ...acc + }; + }, Promise.resolve({})); +}; +const positionalsObj = { + name: { + description: "Name of your directive", + type: "string" + } +}; +const { command, description, builder } = (0, import_helpers2.createYargsForComponentGeneration)({ + componentName: "directive", + filesFn: files, + positionalsObj, + optionsObj: { + ...import_helpers.yargsDefaults, + type: { + type: "string", + choices: ["validator", "transformer"], + description: "Whether to generate a validator or transformer directive" + } + } +}); +const handler = async (args) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "generate directive", + type: args.type, + force: args.force, + rollback: args.rollback + }); + const POST_RUN_INSTRUCTIONS = `Next steps... + + ${import_colors.default.warning( + "After modifying your directive, you can add it to your SDLs e.g.:" + )} + ${import_colors.default.info("// example todo.sdl.js")} + ${import_colors.default.info("# Option A: Add it to a field")} + type Todo { + id: Int! + body: String! ${import_colors.default.green(`@${args.name}`)} + } + + ${import_colors.default.info("# Option B: Add it to query/mutation")} + type Query { + todos: [Todo] ${import_colors.default.green(`@${args.name}`)} + } +`; + (0, import_helpers2.validateName)(args.name); + let directiveType = args.type; + if (!directiveType) { + const response = await (0, import_prompts.default)({ + type: "select", + name: "directiveType", + choices: [ + { + value: "validator", + title: "Validator", + description: "Implement a validation: throw an error if criteria not met to stop execution" + }, + { + value: "transformer", + title: "Transformer", + description: "Modify values of fields or query responses" + } + ], + message: "What type of directive would you like to generate?" + }); + directiveType = response.directiveType; + } + const tasks = new import_listr2.Listr( + [ + { + title: "Generating directive file ...", + task: () => { + return (0, import_lib.writeFilesTask)(files({ ...args, type: directiveType }), { + overwriteExisting: args.force + }); + } + }, + { + title: "Generating TypeScript definitions and GraphQL schemas ...", + task: () => { + (0, import_rollback.addFunctionToRollback)(async () => { + await (0, import_execa.default)("yarn rw-gen", [], { + stdio: "pipe", + shell: true + }); + }, true); + return (0, import_execa.default)("yarn rw-gen", [], { + stdio: "inherit", + shell: true + }); + } + }, + { + title: "Next steps...", + task: (_ctx, task) => { + task.title = POST_RUN_INSTRUCTIONS; + } + } + ].filter(Boolean), + { rendererOptions: { collapseSubtasks: false } } + ); + try { + if (args.rollback && !args.force) { + (0, import_rollback.prepareForRollback)(tasks); + } + await tasks.run(); + } catch (e) { + console.log(import_colors.default.error(e.message)); + process.exit(1); + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + files, + handler +}); diff --git a/packages/cli/dist/commands/generate/directive/templates/transformer.directive.test.ts.template b/packages/cli/dist/commands/generate/directive/templates/transformer.directive.test.ts.template new file mode 100644 index 0000000000..2c67f09d0c --- /dev/null +++ b/packages/cli/dist/commands/generate/directive/templates/transformer.directive.test.ts.template @@ -0,0 +1,18 @@ +import { mockRedwoodDirective, getDirectiveName } from '@redwoodjs/testing/api' + +import ${camelName} from './${camelName}' + +describe('${camelName} directive', () => { + it('declares the directive sdl as schema, with the correct name', () => { + expect(${camelName}.schema).toBeTruthy() + expect(getDirectiveName(${camelName}.schema)).toBe('${camelName}') + }) + + it('has a ${camelName} implementation transforms the value', () => { + const mockExecution = mockRedwoodDirective(${camelName}, { + mockedResolvedValue: 'foo', + }) + + expect(mockExecution()).toBe('bar') + }) +}) diff --git a/packages/cli/dist/commands/generate/directive/templates/transformer.directive.ts.template b/packages/cli/dist/commands/generate/directive/templates/transformer.directive.ts.template new file mode 100644 index 0000000000..a6b3088cc6 --- /dev/null +++ b/packages/cli/dist/commands/generate/directive/templates/transformer.directive.ts.template @@ -0,0 +1,38 @@ +import { + createTransformerDirective, + TransformerDirectiveFunc, +} from '@redwoodjs/graphql-server' +import { logger } from 'src/lib/logger' + +export const schema = gql` + """ + Use @${camelName} to transform the resolved value to return a modified result. + """ + directive @${camelName} on FIELD_DEFINITION +` + +const transform: TransformerDirectiveFunc = ({ context, resolvedValue }) => { + /** + * Write your transformation logic inside this function. + * Transformer directives run **after** resolving the value + * + * - You can also throw an error, if you want to stop executing, but note that the value has already been resolved + * - Transformer directives **must** be synchronous, and return a value + **/ + + // currentUser is only available when auth is setup. + logger.debug({ currentUser: context.currentUser }, 'currentUser in ${camelName} directive') + + // ... you can modify the resolvedValue and return it + logger.debug(resolvedValue, 'resolvedValue in ${camelName} directive') + + // You can also modify your directive to take arguments + // and use the directiveArgs object provided to this function to get values + // See documentation here: https://redwoodjs.com/docs/directives + + return resolvedValue.replace('foo', 'bar') +} + +const ${camelName} = createTransformerDirective(schema, transform) + +export default ${camelName} diff --git a/packages/cli/dist/commands/generate/directive/templates/validator.directive.test.ts.template b/packages/cli/dist/commands/generate/directive/templates/validator.directive.test.ts.template new file mode 100644 index 0000000000..d67e012b60 --- /dev/null +++ b/packages/cli/dist/commands/generate/directive/templates/validator.directive.test.ts.template @@ -0,0 +1,17 @@ +import { mockRedwoodDirective, getDirectiveName } from '@redwoodjs/testing/api' + +import ${camelName} from './${camelName}' + +describe('${camelName} directive', () => { + it('declares the directive sdl as schema, with the correct name', () => { + expect(${camelName}.schema).toBeTruthy() + expect(getDirectiveName(${camelName}.schema)).toBe('${camelName}') + }) + + + it('has a ${camelName} throws an error if validation does not pass', () => { + const mockExecution = mockRedwoodDirective(${camelName}, {}) + + expect(mockExecution).toThrowError('Implementation missing for ${camelName}') + }) +}) diff --git a/packages/cli/dist/commands/generate/directive/templates/validator.directive.ts.template b/packages/cli/dist/commands/generate/directive/templates/validator.directive.ts.template new file mode 100644 index 0000000000..db582b604c --- /dev/null +++ b/packages/cli/dist/commands/generate/directive/templates/validator.directive.ts.template @@ -0,0 +1,39 @@ +import { + createValidatorDirective, + ValidatorDirectiveFunc, +} from '@redwoodjs/graphql-server' + +import { logger } from 'src/lib/logger' + +export const schema = gql` + """ + Use @${camelName} to validate access to a field, query or mutation. + """ + directive @${camelName} on FIELD_DEFINITION +` + +const validate: ValidatorDirectiveFunc = ({ context, directiveArgs }) => { + /** + * Write your validation logic inside this function. + * Validator directives do not have access to the field value, i.e. they are called before resolving the value + * + * - Throw an error, if you want to stop executing e.g. not sufficient permissions + * - Validator directives can be async or sync + * - Returned value will be ignored + */ + + // currentUser is only available when auth is setup. + logger.debug({ currentUser: context.currentUser }, 'currentUser in ${camelName} directive') + + // You can also modify your directive to take arguments + // and use the directiveArgs object provided to this function to get values + // See documentation here: https://redwoodjs.com/docs/directives + logger.debug(directiveArgs, 'directiveArgs in ${camelName} directive') + + + throw new Error('Implementation missing for ${camelName}') +} + +const ${camelName} = createValidatorDirective(schema, validate) + +export default ${camelName} diff --git a/packages/cli/dist/commands/generate/function/function.js b/packages/cli/dist/commands/generate/function/function.js new file mode 100644 index 0000000000..0218534e1f --- /dev/null +++ b/packages/cli/dist/commands/generate/function/function.js @@ -0,0 +1,188 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var function_exports = {}; +__export(function_exports, { + builder: () => builder, + command: () => command, + description: () => description, + files: () => files, + handler: () => handler +}); +module.exports = __toCommonJS(function_exports); +var import_path = __toESM(require("path")); +var import_camelcase = __toESM(require("camelcase")); +var import_listr2 = require("listr2"); +var import_terminal_link = __toESM(require("terminal-link")); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_telemetry = require("@redwoodjs/telemetry"); +var import_lib = require("../../../lib"); +var import_colors = __toESM(require("../../../lib/colors")); +var import_rollback = require("../../../lib/rollback"); +var import_helpers = require("../helpers"); +var import_helpers2 = require("../helpers"); +const files = async ({ + name, + typescript: generateTypescript = false, + tests: generateTests = true, + ...rest +}) => { + const extension = generateTypescript ? ".ts" : ".js"; + const functionName = (0, import_camelcase.default)(name); + const outputFiles = []; + const functionFiles = await (0, import_helpers2.templateForComponentFile)({ + name: functionName, + componentName: functionName, + extension, + apiPathSection: "functions", + generator: "function", + templatePath: "function.ts.template", + templateVars: { ...rest }, + outputPath: import_path.default.join( + (0, import_lib.getPaths)().api.functions, + functionName, + `${functionName}${extension}` + ) + }); + outputFiles.push(functionFiles); + if (generateTests) { + const testFile = await (0, import_helpers2.templateForComponentFile)({ + name: functionName, + componentName: functionName, + extension, + apiPathSection: "functions", + generator: "function", + templatePath: "test.ts.template", + templateVars: { ...rest }, + outputPath: import_path.default.join( + (0, import_lib.getPaths)().api.functions, + functionName, + `${functionName}.test${extension}` + ) + }); + const scenarioFile = await (0, import_helpers2.templateForComponentFile)({ + name: functionName, + componentName: functionName, + extension, + apiPathSection: "functions", + generator: "function", + templatePath: "scenarios.ts.template", + templateVars: { ...rest }, + outputPath: import_path.default.join( + (0, import_lib.getPaths)().api.functions, + functionName, + `${functionName}.scenarios${extension}` + ) + }); + outputFiles.push(testFile); + outputFiles.push(scenarioFile); + } + return outputFiles.reduce(async (accP, [outputPath, content]) => { + const acc = await accP; + const template = generateTypescript ? content : await (0, import_lib.transformTSToJS)(outputPath, content); + return { + [outputPath]: template, + ...acc + }; + }, Promise.resolve({})); +}; +const command = "function "; +const description = "Generate a Function"; +const builder = (yargs) => { + yargs.positional("name", { + description: "Name of the Function", + type: "string" + }).option("rollback", { + description: "Revert all generator actions if an error occurs", + type: "boolean", + default: true + }).epilogue( + `Also see the ${(0, import_terminal_link.default)( + "Redwood CLI Reference", + "https://redwoodjs.com/docs/cli-commands#generate-function" + )}` + ); + Object.entries(import_helpers.yargsDefaults).forEach(([option, config]) => { + yargs.option(option, config); + }); +}; +const handler = async ({ name, force, ...rest }) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "generate function", + force, + rollback: rest.rollback + }); + (0, import_helpers2.validateName)(name); + const tasks = new import_listr2.Listr( + [ + { + title: "Generating function files...", + task: async () => { + return (0, import_lib.writeFilesTask)(await files({ name, ...rest }), { + overwriteExisting: force + }); + } + } + ], + { rendererOptions: { collapseSubtasks: false }, exitOnError: true } + ); + try { + if (rest.rollback && !force) { + (0, import_rollback.prepareForRollback)(tasks); + } + await tasks.run(); + console.info(""); + console.info(import_colors.default.warning("\u26A0 Important:")); + console.info(""); + console.info( + import_colors.default.bold( + "When deployed, a custom serverless function is an open API endpoint and is your responsibility to secure appropriately." + ) + ); + console.info(""); + console.info( + `Please consult the ${(0, import_terminal_link.default)( + "Serverless Function Considerations", + "https://redwoodjs.com/docs/serverless-functions#security-considerations" + )} in the RedwoodJS documentation for more information.` + ); + console.info(""); + } catch (e) { + (0, import_telemetry.errorTelemetry)(process.argv, e.message); + console.error(import_colors.default.error(e.message)); + process.exit(e?.exitCode || 1); + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + files, + handler +}); diff --git a/packages/cli/dist/commands/generate/function/templates/function.ts.template b/packages/cli/dist/commands/generate/function/templates/function.ts.template new file mode 100644 index 0000000000..25a302ec0f --- /dev/null +++ b/packages/cli/dist/commands/generate/function/templates/function.ts.template @@ -0,0 +1,33 @@ +import type { APIGatewayEvent, Context } from 'aws-lambda' + +import { logger } from 'src/lib/logger' + +/** + * The handler function is your code that processes http request events. + * You can use return and throw to send a response or error, respectively. + * + * Important: When deployed, a custom serverless function is an open API endpoint and + * is your responsibility to secure appropriately. + * + * @see {@link https://redwoodjs.com/docs/serverless-functions#security-considerations|Serverless Function Considerations} + * in the RedwoodJS documentation for more information. + * + * @typedef { import('aws-lambda').APIGatewayEvent } APIGatewayEvent + * @typedef { import('aws-lambda').Context } Context + * @param { APIGatewayEvent } event - an object which contains information from the invoker. + * @param { Context } context - contains information about the invocation, + * function, and execution environment. + */ +export const handler = async (event: APIGatewayEvent, _context: Context) => { + logger.info(`<%= '$' %>{event.httpMethod} <%= '$' %>{event.path}: <%= name %> function`) + + return { + statusCode: 200, + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + data: '${name} function', + }), + } +} diff --git a/packages/cli/dist/commands/generate/function/templates/scenarios.ts.template b/packages/cli/dist/commands/generate/function/templates/scenarios.ts.template new file mode 100644 index 0000000000..d24ff747f6 --- /dev/null +++ b/packages/cli/dist/commands/generate/function/templates/scenarios.ts.template @@ -0,0 +1,8 @@ +import type { ScenarioData } from '@redwoodjs/testing/api' + +export const standard = defineScenario({ + // Define the "fixture" to write into your test database here + // See guide: https://redwoodjs.com/docs/testing#scenarios +}) + +export type StandardScenario = ScenarioData diff --git a/packages/cli/dist/commands/generate/function/templates/test.ts.template b/packages/cli/dist/commands/generate/function/templates/test.ts.template new file mode 100644 index 0000000000..f990bae705 --- /dev/null +++ b/packages/cli/dist/commands/generate/function/templates/test.ts.template @@ -0,0 +1,30 @@ +import { mockHttpEvent } from '@redwoodjs/testing/api' + +import { handler } from './${name}' + +// Improve this test with help from the Redwood Testing Doc: +// https://redwoodjs.com/docs/testing#testing-functions + +describe('${name} function', () => { + + it('Should respond with 200', async () => { + const httpEvent = mockHttpEvent({ + queryStringParameters: { + id: '42', // Add parameters here + }, + }) + + const response = await handler(httpEvent, null) + const { data } = JSON.parse(response.body) + + expect(response.statusCode).toBe(200) + expect(data).toBe('${name} function') + }) + +// You can also use scenarios to test your api functions +// See guide here: https://redwoodjs.com/docs/testing#scenarios +// +// scenario('Scenario test', async () => { +// +// }) +}) diff --git a/packages/cli/dist/commands/generate/helpers.js b/packages/cli/dist/commands/generate/helpers.js new file mode 100644 index 0000000000..78dcde17cc --- /dev/null +++ b/packages/cli/dist/commands/generate/helpers.js @@ -0,0 +1,291 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var helpers_exports = {}; +__export(helpers_exports, { + createYargsForComponentGeneration: () => createYargsForComponentGeneration, + customOrDefaultTemplatePath: () => customOrDefaultTemplatePath, + forcePluralizeWord: () => forcePluralizeWord, + intForeignKeysForModel: () => intForeignKeysForModel, + mapPrismaScalarToPagePropTsType: () => mapPrismaScalarToPagePropTsType, + mapRouteParamTypeToTsType: () => mapRouteParamTypeToTsType, + pathName: () => pathName, + relationsForModel: () => relationsForModel, + removeGeneratorName: () => removeGeneratorName, + templateForComponentFile: () => templateForComponentFile, + validateName: () => validateName, + yargsDefaults: () => yargsDefaults +}); +module.exports = __toCommonJS(helpers_exports); +var import_path = __toESM(require("path")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_listr2 = require("listr2"); +var import_param_case = require("param-case"); +var import_pascalcase = __toESM(require("pascalcase")); +var import_terminal_link = __toESM(require("terminal-link")); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_project_config = require("@redwoodjs/project-config"); +var import_telemetry = require("@redwoodjs/telemetry"); +var import_lib = require("../../lib"); +var import_colors = __toESM(require("../../lib/colors")); +var import_project = require("../../lib/project"); +var import_rollback = require("../../lib/rollback"); +var import_rwPluralize = require("../../lib/rwPluralize"); +const customOrDefaultTemplatePath = ({ + side, + generator, + templatePath +}) => { + const defaultPath = import_path.default.join(__dirname, generator, "templates", templatePath); + const customPath = import_path.default.join( + (0, import_lib.getPaths)()[side].generators, + generator, + templatePath + ); + if (import_fs_extra.default.existsSync(customPath)) { + return customPath; + } else { + return defaultPath; + } +}; +const templateForComponentFile = async ({ + name, + suffix = "", + extension = ".js", + webPathSection, + apiPathSection, + generator, + templatePath, + templateVars, + componentName, + outputPath +}) => { + const basePath = webPathSection ? (0, import_lib.getPaths)().web[webPathSection] : (0, import_lib.getPaths)().api[apiPathSection]; + const outputComponentName = componentName || (0, import_pascalcase.default)(name) + suffix; + const componentOutputPath = outputPath || import_path.default.join(basePath, outputComponentName, outputComponentName + extension); + const fullTemplatePath = customOrDefaultTemplatePath({ + generator, + templatePath, + side: webPathSection ? "web" : "api" + }); + const content = await (0, import_lib.generateTemplate)(fullTemplatePath, { + name, + outputPath: (0, import_project_config.ensurePosixPath)( + `./${import_path.default.relative((0, import_lib.getPaths)().base, componentOutputPath)}` + ), + ...templateVars + }); + return [componentOutputPath, content]; +}; +const pathName = (path2, name) => { + let routePath = path2; + if (path2 && path2.startsWith("{") && path2.endsWith("}")) { + routePath = `/${(0, import_param_case.paramCase)(name)}/${path2}`; + } + if (!routePath) { + routePath = `/${(0, import_param_case.paramCase)(name)}`; + } + return routePath; +}; +const appendPositionalsToCmd = (commandString, positionalsObj) => { + if (Object.keys(positionalsObj).length > 0) { + const positionalNames = Object.keys(positionalsObj).map((positionalName) => `[${positionalName}]`).join(" "); + return `${commandString} ${positionalNames}`; + } else { + return commandString; + } +}; +function removeGeneratorName(name, generatorName) { + const pascalComponentName = (0, import_pascalcase.default)(generatorName); + const coercedName = name.replace(new RegExp(pascalComponentName + "$"), ""); + return coercedName; +} +const yargsDefaults = { + force: { + alias: "f", + default: false, + description: "Overwrite existing files", + type: "boolean" + }, + typescript: { + alias: "ts", + default: (0, import_project.isTypeScriptProject)(), + description: "Generate TypeScript files", + type: "boolean" + } +}; +const validateName = (name) => { + if (name.match(/^\W/)) { + throw new Error( + "The argument must start with a letter, number or underscore." + ); + } +}; +const createYargsForComponentGeneration = ({ + componentName, + preTasksFn = (options) => options, + /** filesFn is not used if generator implements its own `handler` */ + filesFn = () => ({}), + optionsObj = yargsDefaults, + positionalsObj = {}, + /** function that takes the options object and returns an array of listr tasks */ + includeAdditionalTasks = () => [] +}) => { + return { + command: appendPositionalsToCmd(`${componentName} `, positionalsObj), + description: `Generate a ${componentName} component`, + builder: (yargs) => { + yargs.positional("name", { + description: `Name of the ${componentName}`, + type: "string" + }).epilogue( + `Also see the ${(0, import_terminal_link.default)( + "Redwood CLI Reference", + `https://redwoodjs.com/docs/cli-commands#generate-${componentName}` + )}` + ).option("tests", { + description: "Generate test files", + type: "boolean" + }).option("stories", { + description: "Generate storybook files", + type: "boolean" + }).option("verbose", { + description: "Print all logs", + type: "boolean", + default: false + }).option("rollback", { + description: "Revert all generator actions if an error occurs", + type: "boolean", + default: true + }); + Object.entries(positionalsObj).forEach(([option, config]) => { + yargs.positional(option, config); + }); + Object.entries(optionsObj).forEach(([option, config]) => { + yargs.option(option, config); + }); + }, + handler: async (options) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: `generate ${componentName}`, + tests: options.tests, + stories: options.stories, + verbose: options.verbose, + rollback: options.rollback, + force: options.force + // TODO: This does not cover the specific options that each generator might pass in + }); + if (options.tests === void 0) { + options.tests = (0, import_project_config.getConfig)().generate.tests; + } + if (options.stories === void 0) { + options.stories = (0, import_project_config.getConfig)().generate.stories; + } + validateName(options.name); + try { + options = await preTasksFn(options); + const tasks = new import_listr2.Listr( + [ + { + title: `Generating ${componentName} files...`, + task: async () => { + const f = await filesFn(options); + return (0, import_lib.writeFilesTask)(f, { overwriteExisting: options.force }); + } + }, + ...includeAdditionalTasks(options) + ], + { + rendererOptions: { collapseSubtasks: false }, + exitOnError: true, + renderer: options.verbose && "verbose" + } + ); + if (options.rollback && !options.force) { + (0, import_rollback.prepareForRollback)(tasks); + } + await tasks.run(); + } catch (e) { + (0, import_telemetry.errorTelemetry)(process.argv, e.message); + console.error(import_colors.default.error(e.message)); + process.exit(e?.exitCode || 1); + } + } + }; +}; +const relationsForModel = (model) => { + return model.fields.filter((f) => f.relationName).map((field) => { + return field.name; + }); +}; +const intForeignKeysForModel = (model) => { + return model.fields.filter((f) => f.name.match(/Id$/) && f.type === "Int").map((f) => f.name); +}; +const forcePluralizeWord = (word) => { + if ((0, import_rwPluralize.isPlural)(word) && (0, import_rwPluralize.isSingular)(word)) { + return (0, import_pascalcase.default)(`${word}_list`); + } + return (0, import_rwPluralize.pluralize)(word); +}; +const mapRouteParamTypeToTsType = (paramType) => { + const routeParamToTsType = { + Int: "number", + Float: "number", + Boolean: "boolean", + String: "string" + }; + return routeParamToTsType[paramType] || "unknown"; +}; +const mapPrismaScalarToPagePropTsType = (scalarType) => { + const prismaScalarToTsType = { + String: "string", + Boolean: "boolean", + Int: "number", + BigInt: "number", + Float: "number", + Decimal: "number", + DateTime: "string", + Bytes: "Buffer" + }; + return prismaScalarToTsType[scalarType] || "unknown"; +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + createYargsForComponentGeneration, + customOrDefaultTemplatePath, + forcePluralizeWord, + intForeignKeysForModel, + mapPrismaScalarToPagePropTsType, + mapRouteParamTypeToTsType, + pathName, + relationsForModel, + removeGeneratorName, + templateForComponentFile, + validateName, + yargsDefaults +}); diff --git a/packages/cli/dist/commands/generate/layout/layout.js b/packages/cli/dist/commands/generate/layout/layout.js new file mode 100644 index 0000000000..1e3a9f36cc --- /dev/null +++ b/packages/cli/dist/commands/generate/layout/layout.js @@ -0,0 +1,96 @@ +"use strict"; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var layout_exports = {}; +__export(layout_exports, { + builder: () => builder, + command: () => command, + description: () => description, + files: () => files, + handler: () => handler +}); +module.exports = __toCommonJS(layout_exports); +var import_lib = require("../../../lib"); +var import_helpers = require("../helpers"); +var import_helpers2 = require("../helpers"); +const COMPONENT_SUFFIX = "Layout"; +const REDWOOD_WEB_PATH_NAME = "layouts"; +const files = async ({ name, typescript = false, ...options }) => { + const layoutName = (0, import_helpers2.removeGeneratorName)(name, "layout"); + const extension = typescript ? ".tsx" : ".jsx"; + const layoutFile = await (0, import_helpers2.templateForComponentFile)({ + name: layoutName, + suffix: COMPONENT_SUFFIX, + webPathSection: REDWOOD_WEB_PATH_NAME, + extension, + generator: "layout", + templatePath: options.skipLink ? "layout.tsx.a11y.template" : "layout.tsx.template" + }); + const testFile = await (0, import_helpers2.templateForComponentFile)({ + name: layoutName, + suffix: COMPONENT_SUFFIX, + extension: `.test${extension}`, + webPathSection: REDWOOD_WEB_PATH_NAME, + generator: "layout", + templatePath: "test.tsx.template" + }); + const storyFile = await (0, import_helpers2.templateForComponentFile)({ + name: layoutName, + suffix: COMPONENT_SUFFIX, + extension: `.stories${extension}`, + webPathSection: REDWOOD_WEB_PATH_NAME, + generator: "layout", + templatePath: "stories.tsx.template" + }); + const files2 = [layoutFile]; + if (options.stories) { + files2.push(storyFile); + } + if (options.tests) { + files2.push(testFile); + } + return files2.reduce(async (accP, [outputPath, content]) => { + const acc = await accP; + const template = typescript ? content : await (0, import_lib.transformTSToJS)(outputPath, content); + return { + [outputPath]: template, + ...acc + }; + }, Promise.resolve({})); +}; +const optionsObj = { + skipLink: { + default: false, + description: "Generate with skip link", + type: "boolean" + }, + ...import_helpers.yargsDefaults +}; +const { command, description, builder, handler } = (0, import_helpers2.createYargsForComponentGeneration)({ + componentName: "layout", + filesFn: files, + optionsObj +}); +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + files, + handler +}); diff --git a/packages/cli/dist/commands/generate/layout/templates/layout.tsx.a11y.template b/packages/cli/dist/commands/generate/layout/templates/layout.tsx.a11y.template new file mode 100644 index 0000000000..3294ae6e67 --- /dev/null +++ b/packages/cli/dist/commands/generate/layout/templates/layout.tsx.a11y.template @@ -0,0 +1,27 @@ +import { SkipNavLink, SkipNavContent } from '@redwoodjs/router' +import '@redwoodjs/router/skip-nav.css' + +/** + * since the main content isn't usually the first thing in the document, + * it's important to provide a shortcut for keyboard and screen-reader users to skip to the main content + * API docs: https://reach.tech/skip-nav/#reach-skip-nav + */ + +type ${singularPascalName}LayoutProps = { + children?: React.ReactNode +} + +const ${singularPascalName}Layout = ({ children }: ${singularPascalName}LayoutProps) => { + return ( + <> + {/* renders a link that remains hidden till focused; put it at the top of your layout */} + + + {/* renders a div as the target for the link; put it next to your main content */} + +
{children}
+ + ) +} + +export default ${singularPascalName}Layout diff --git a/packages/cli/dist/commands/generate/layout/templates/layout.tsx.template b/packages/cli/dist/commands/generate/layout/templates/layout.tsx.template new file mode 100644 index 0000000000..a4fd6e4646 --- /dev/null +++ b/packages/cli/dist/commands/generate/layout/templates/layout.tsx.template @@ -0,0 +1,9 @@ +type ${singularPascalName}LayoutProps = { + children?: React.ReactNode +} + +const ${singularPascalName}Layout = ({ children }: ${singularPascalName}LayoutProps) => { + return <>{children} +} + +export default ${singularPascalName}Layout diff --git a/packages/cli/dist/commands/generate/layout/templates/stories.tsx.template b/packages/cli/dist/commands/generate/layout/templates/stories.tsx.template new file mode 100644 index 0000000000..2ad220d06c --- /dev/null +++ b/packages/cli/dist/commands/generate/layout/templates/stories.tsx.template @@ -0,0 +1,13 @@ +import type { Meta, StoryObj } from '@storybook/react' + +import ${singularPascalName}Layout from './${pascalName}Layout' + +const meta: Meta = { + component: ${singularPascalName}Layout, +} + +export default meta + +type Story = StoryObj + +export const Primary: Story = {} diff --git a/packages/cli/dist/commands/generate/layout/templates/test.tsx.template b/packages/cli/dist/commands/generate/layout/templates/test.tsx.template new file mode 100644 index 0000000000..3e0d79ab95 --- /dev/null +++ b/packages/cli/dist/commands/generate/layout/templates/test.tsx.template @@ -0,0 +1,14 @@ +import { render } from '@redwoodjs/testing/web' + +import ${pascalName}Layout from './${pascalName}Layout' + +// Improve this test with help from the Redwood Testing Doc: +// https://redwoodjs.com/docs/testing#testing-pages-layouts + +describe('${pascalName}Layout', () => { + it('renders successfully', () => { + expect(() => { + render(<${pascalName}Layout />) + }).not.toThrow() + }) +}) diff --git a/packages/cli/dist/commands/generate/model/model.js b/packages/cli/dist/commands/generate/model/model.js new file mode 100644 index 0000000000..092a4c3144 --- /dev/null +++ b/packages/cli/dist/commands/generate/model/model.js @@ -0,0 +1,118 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var model_exports = {}; +__export(model_exports, { + builder: () => builder, + command: () => command, + description: () => description, + files: () => files, + handler: () => handler +}); +module.exports = __toCommonJS(model_exports); +var import_path = __toESM(require("path")); +var import_listr2 = require("listr2"); +var import_terminal_link = __toESM(require("terminal-link")); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_lib = require("../../../lib"); +var import_colors = __toESM(require("../../../lib/colors")); +var import_rollback = require("../../../lib/rollback"); +var import_schemaHelpers = require("../../../lib/schemaHelpers"); +var import_helpers = require("../helpers"); +const TEMPLATE_PATH = import_path.default.resolve(__dirname, "templates", "model.js.template"); +const files = ({ name, typescript = false }) => { + const outputFilename = `${name}.${typescript ? "ts" : "js"}`; + const outputPath = import_path.default.join((0, import_lib.getPaths)().api.models, outputFilename); + return { + [outputPath]: (0, import_lib.generateTemplate)(TEMPLATE_PATH, { name }) + }; +}; +const command = "model "; +const description = "Generate a RedwoodRecord model"; +const builder = (yargs) => { + yargs.positional("name", { + description: "Name of the model to create", + type: "string" + }).option("rollback", { + description: "Revert all generator actions if an error occurs", + type: "boolean", + default: true + }).epilogue( + `Also see the ${(0, import_terminal_link.default)( + "RedwoodRecord Reference", + "https://redwoodjs.com/docs/redwoodrecord" + )}` + ); + Object.entries(import_helpers.yargsDefaults).forEach(([option, config]) => { + yargs.option(option, config); + }); +}; +const handler = async ({ force, ...args }) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "generate model", + force, + rollback: args.rollback + }); + (0, import_helpers.validateName)(args.name); + const tasks = new import_listr2.Listr( + [ + { + title: "Generating model file...", + task: () => { + return (0, import_lib.writeFilesTask)(files(args), { overwriteExisting: force }); + } + }, + { + title: "Parsing datamodel, generating api/src/models/index.js...", + task: async () => { + const redwoodRecordModule = await import("@redwoodjs/record"); + await redwoodRecordModule.default.parseDatamodel(); + } + } + ].filter(Boolean), + { rendererOptions: { collapseSubtasks: false } } + ); + try { + await (0, import_schemaHelpers.verifyModelName)({ name: args.name }); + if (args.rollback && !force) { + (0, import_rollback.prepareForRollback)(tasks); + } + await tasks.run(); + } catch (e) { + console.log(import_colors.default.error(e.message)); + process.exit(1); + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + files, + handler +}); diff --git a/packages/cli/dist/commands/generate/model/templates/model.js.template b/packages/cli/dist/commands/generate/model/templates/model.js.template new file mode 100644 index 0000000000..bf67d64af8 --- /dev/null +++ b/packages/cli/dist/commands/generate/model/templates/model.js.template @@ -0,0 +1,3 @@ +import { RedwoodRecord } from '@redwoodjs/record' + +export default class ${name} extends RedwoodRecord {} diff --git a/packages/cli/dist/commands/generate/page/page.js b/packages/cli/dist/commands/generate/page/page.js new file mode 100644 index 0000000000..21d76253e7 --- /dev/null +++ b/packages/cli/dist/commands/generate/page/page.js @@ -0,0 +1,261 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var page_exports = {}; +__export(page_exports, { + builder: () => builder, + command: () => command, + description: () => description, + files: () => files, + handler: () => handler, + paramVariants: () => paramVariants, + routes: () => routes +}); +module.exports = __toCommonJS(page_exports); +var import_child_process = require("child_process"); +var import_camelcase = __toESM(require("camelcase")); +var import_listr2 = require("listr2"); +var import_pascalcase = __toESM(require("pascalcase")); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_generate = require("@redwoodjs/internal/dist/generate/generate"); +var import_project_config = require("@redwoodjs/project-config"); +var import_telemetry = require("@redwoodjs/telemetry"); +var import_lib = require("../../../lib"); +var import_colors = __toESM(require("../../../lib/colors")); +var import_rollback = require("../../../lib/rollback"); +var import_helpers = require("../helpers"); +const COMPONENT_SUFFIX = "Page"; +const REDWOOD_WEB_PATH_NAME = "pages"; +const mapRouteParamTypeToDefaultValue = (paramType) => { + switch (paramType) { + case "Int": + return 42; + case "Float": + return 42.1; + case "Boolean": + return true; + default: + return "42"; + } +}; +const paramVariants = (path) => { + const param = path?.match(/(\{[\w:]+\})/)?.[1]; + const paramName = param?.replace(/:[^}]+/, "").slice(1, -1); + if (param === void 0) { + return { + propParam: "", + propValueParam: "", + argumentParam: "", + paramName: "", + paramValue: "", + paramType: "" + }; + } + const routeParamType = param?.match(/:/) ? param?.replace(/[^:]+/, "").slice(1, -1) : "String"; + const defaultValue = mapRouteParamTypeToDefaultValue(routeParamType); + const defaultValueAsProp = routeParamType === "String" ? `'${defaultValue}'` : defaultValue; + return { + propParam: `{ ${paramName} }`, + propValueParam: `${paramName}={${defaultValueAsProp}}`, + // used in story + argumentParam: `{ ${paramName}: ${defaultValueAsProp} }`, + paramName, + paramValue: defaultValue, + paramType: (0, import_helpers.mapRouteParamTypeToTsType)(routeParamType) + }; +}; +const files = async ({ name, tests, stories, typescript, ...rest }) => { + const extension = typescript ? ".tsx" : ".jsx"; + const pageFile = await (0, import_helpers.templateForComponentFile)({ + name, + suffix: COMPONENT_SUFFIX, + extension, + webPathSection: REDWOOD_WEB_PATH_NAME, + generator: "page", + templatePath: "page.tsx.template", + templateVars: rest + }); + const testFile = await (0, import_helpers.templateForComponentFile)({ + name, + suffix: COMPONENT_SUFFIX, + extension: `.test${extension}`, + webPathSection: REDWOOD_WEB_PATH_NAME, + generator: "page", + templatePath: "test.tsx.template", + templateVars: rest + }); + const storiesFile = await (0, import_helpers.templateForComponentFile)({ + name, + suffix: COMPONENT_SUFFIX, + extension: `.stories${extension}`, + webPathSection: REDWOOD_WEB_PATH_NAME, + generator: "page", + templatePath: rest.paramName !== "" ? "stories.tsx.parameters.template" : "stories.tsx.template", + templateVars: rest + }); + const files2 = [pageFile]; + if (tests) { + files2.push(testFile); + } + if (stories) { + files2.push(storiesFile); + } + return files2.reduce(async (accP, [outputPath, content]) => { + const acc = await accP; + const template = typescript ? content : await (0, import_lib.transformTSToJS)(outputPath, content); + return { + [outputPath]: template, + ...acc + }; + }, Promise.resolve({})); +}; +const routes = ({ name, path }) => { + return [ + `` + ]; +}; +const positionalsObj = { + path: { + description: "URL path to the page, or just {param}. Defaults to name", + type: "string" + } +}; +const { command, description, builder } = (0, import_helpers.createYargsForComponentGeneration)({ componentName: "page", positionalsObj }); +const handler = async ({ + name, + path, + force, + tests, + stories, + typescript = false, + rollback +}) => { + const pageName = (0, import_helpers.removeGeneratorName)(name, "page"); + (0, import_helpers.validateName)(pageName); + if (tests === void 0) { + tests = (0, import_project_config.getConfig)().generate.tests; + } + if (stories === void 0) { + stories = (0, import_project_config.getConfig)().generate.stories; + } + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "generate page", + force, + tests, + stories, + typescript, + rollback + }); + if (process.platform === "win32") { + try { + const slashPath = (0, import_child_process.execSync)("cygpath -m /", { + stdio: ["ignore", "pipe", "ignore"] + }).toString().trim(); + path = path.replace(new RegExp(`^${slashPath}?`), "/"); + } catch { + } + } + const tasks = new import_listr2.Listr( + [ + { + title: "Generating page files...", + task: async () => { + path = (0, import_helpers.pathName)(path, pageName); + const f = await files({ + name: pageName, + path, + tests, + stories, + typescript, + ...paramVariants(path) + }); + return (0, import_lib.writeFilesTask)(f, { overwriteExisting: force }); + } + }, + { + title: "Updating routes file...", + task: async () => { + (0, import_lib.addRoutesToRouterTask)( + routes({ name: pageName, path: (0, import_helpers.pathName)(path, pageName) }) + ); + } + }, + { + title: `Generating types...`, + task: async () => { + const { errors } = await (0, import_generate.generate)(); + for (const { message, error } of errors) { + console.error(message); + console.log(); + console.error(error); + console.log(); + } + (0, import_rollback.addFunctionToRollback)(import_generate.generate, true); + } + }, + { + title: "One more thing...", + task: (ctx, task) => { + task.title = `One more thing... + + ${import_colors.default.warning("Page created! A note about :")} + + At the top of your newly created page is a component, + which contains the title and description for your page, essential + to good SEO. Check out this page for best practices: + + https://developers.google.com/search/docs/advanced/appearance/good-titles-snippets +`; + } + } + ].filter(Boolean), + { rendererOptions: { collapseSubtasks: false } } + ); + try { + if (rollback && !force) { + (0, import_rollback.prepareForRollback)(tasks); + } + await tasks.run(); + } catch (e) { + (0, import_telemetry.errorTelemetry)(process.argv, e.message); + console.error(import_colors.default.error(e.message)); + process.exit(e?.exitCode || 1); + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + files, + handler, + paramVariants, + routes +}); diff --git a/packages/cli/dist/commands/generate/page/templates/page.tsx.template b/packages/cli/dist/commands/generate/page/templates/page.tsx.template new file mode 100644 index 0000000000..0468433736 --- /dev/null +++ b/packages/cli/dist/commands/generate/page/templates/page.tsx.template @@ -0,0 +1,26 @@ +import { Link, routes } from '@redwoodjs/router' +import { Metadata } from '@redwoodjs/web' +<% if (paramName !== '') { %> +type ${pascalName}PageProps = { + ${paramName}: ${paramType} +} +<% } %> +const ${pascalName}Page = (<%- paramName === '' ? '' : `${propParam}: ${pascalName}PageProps` %>) => { + return ( + <> + + +

${pascalName}Page

+

+ Find me in ${outputPath} +

+

+ My default route is named ${camelName}, link to me with ` + ${pascalName}<%= argumentParam !== '' ? ' 42' : '' %>` +

<% if (paramName !== '') { %> +

The parameter passed to me is {${ paramName }}

<% } %> + + ) +} + +export default ${pascalName}Page diff --git a/packages/cli/dist/commands/generate/page/templates/stories.tsx.parameters.template b/packages/cli/dist/commands/generate/page/templates/stories.tsx.parameters.template new file mode 100644 index 0000000000..69427f309a --- /dev/null +++ b/packages/cli/dist/commands/generate/page/templates/stories.tsx.parameters.template @@ -0,0 +1,13 @@ +import type { Meta, StoryObj } from '@storybook/react' + +import ${pascalName}Page from './${pascalName}Page' + +const meta: Meta = { + component: ${pascalName}Page, +} + +export default meta + +type Story = StoryObj + +export const Primary: Story = {} diff --git a/packages/cli/dist/commands/generate/page/templates/stories.tsx.template b/packages/cli/dist/commands/generate/page/templates/stories.tsx.template new file mode 100644 index 0000000000..69427f309a --- /dev/null +++ b/packages/cli/dist/commands/generate/page/templates/stories.tsx.template @@ -0,0 +1,13 @@ +import type { Meta, StoryObj } from '@storybook/react' + +import ${pascalName}Page from './${pascalName}Page' + +const meta: Meta = { + component: ${pascalName}Page, +} + +export default meta + +type Story = StoryObj + +export const Primary: Story = {} diff --git a/packages/cli/dist/commands/generate/page/templates/test.tsx.template b/packages/cli/dist/commands/generate/page/templates/test.tsx.template new file mode 100644 index 0000000000..79d9e6744c --- /dev/null +++ b/packages/cli/dist/commands/generate/page/templates/test.tsx.template @@ -0,0 +1,14 @@ +import { render } from '@redwoodjs/testing/web' + +import ${pascalName}Page from './${pascalName}Page' + +// Improve this test with help from the Redwood Testing Doc: +// https://redwoodjs.com/docs/testing#testing-pages-layouts + +describe('${pascalName}Page', () => { + it('renders successfully', () => { + expect(() => { + render(<${pascalName}Page ${propValueParam}/>) + }).not.toThrow() + }) +}) diff --git a/packages/cli/dist/commands/generate/realtime/realtime.js b/packages/cli/dist/commands/generate/realtime/realtime.js new file mode 100644 index 0000000000..70019de952 --- /dev/null +++ b/packages/cli/dist/commands/generate/realtime/realtime.js @@ -0,0 +1,78 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var realtime_exports = {}; +__export(realtime_exports, { + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(realtime_exports); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +const command = "realtime "; +const description = "Generate a subscription or live query used with RedwoodJS Realtime"; +function builder(yargs) { + yargs.positional("name", { + type: "string", + description: "Name of the realtime event to setup. This should be a type or model name like: Widget, Sprocket, etc.", + demandOption: true + }).option("type", { + alias: "t", + type: "string", + choices: ["liveQuery", "subscription"], + description: "Type of realtime event to setup" + }).option("force", { + alias: "f", + default: false, + description: "Overwrite existing configuration", + type: "boolean" + }).option("verbose", { + alias: "v", + default: false, + description: "Print more logs", + type: "boolean" + }); +} +async function handler(options) { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "generate realtime", + type: options.type, + force: options.force, + verbose: options.verbose + }); + const { handler: handler2 } = await import("./realtimeHandler.js"); + return handler2(options); +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/generate/realtime/realtimeHandler.js b/packages/cli/dist/commands/generate/realtime/realtimeHandler.js new file mode 100644 index 0000000000..a61441018e --- /dev/null +++ b/packages/cli/dist/commands/generate/realtime/realtimeHandler.js @@ -0,0 +1,238 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var realtimeHandler_exports = {}; +__export(realtimeHandler_exports, { + handler: () => handler +}); +module.exports = __toCommonJS(realtimeHandler_exports); +var import_path = __toESM(require("path")); +var import_camelcase = __toESM(require("camelcase")); +var import_listr2 = require("listr2"); +var import_pascalcase = __toESM(require("pascalcase")); +var import_pluralize = __toESM(require("pluralize")); +var import_prompts = __toESM(require("prompts")); +var import_generate = require("@redwoodjs/internal/dist/generate/generate"); +var import_telemetry = require("@redwoodjs/telemetry"); +var import_lib = require("../../../lib"); +var import_colors = __toESM(require("../../../lib/colors")); +var import_project = require("../../../lib/project"); +var import_util = require("../../experimental/util.js"); +const templateVariables = (name) => { + name = (0, import_pluralize.singular)(name.toLowerCase()); + return { + name, + collectionName: (0, import_pluralize.default)(name), + pluralName: (0, import_pluralize.default)(name), + pluralPascalName: (0, import_pascalcase.default)((0, import_pluralize.default)(name)), + camelName: (0, import_camelcase.default)(name), + functionName: (0, import_camelcase.default)(name), + liveQueryName: `recent${(0, import_pascalcase.default)((0, import_pluralize.default)(name))}`, + subscriptionQueryName: `recent${(0, import_pascalcase.default)((0, import_pluralize.default)(name))}`, + subscriptionName: `listenTo${(0, import_pascalcase.default)(name)}Channel`, + modelName: (0, import_pascalcase.default)(name), + typeName: (0, import_pascalcase.default)(name), + channelName: `${(0, import_pascalcase.default)(name)}Channel`, + subscriptionInputType: `Publish${(0, import_pascalcase.default)(name)}Input`, + subscriptionServiceResolver: `publishTo${(0, import_pascalcase.default)(name)}Channel` + }; +}; +async function handler({ name, type, force, verbose }) { + const redwoodPaths = (0, import_lib.getPaths)(); + const ts = (0, import_project.isTypeScriptProject)(); + name = (0, import_pluralize.singular)(name.toLowerCase()); + let functionType = type; + if (!functionType) { + const response = await (0, import_prompts.default)({ + type: "select", + name: "functionType", + choices: [ + { + value: "liveQuery", + title: "Live Query", + description: "Create a Live Query to watch for changes in data" + }, + { + value: "subscription", + title: "Subscription", + description: "Create a Subscription to watch for events" + } + ], + message: "What type of realtime event would you like to create?" + }); + functionType = response.functionType; + } + const tasks = new import_listr2.Listr( + [ + { + title: "Checking for realtime environment prerequisites ...", + task: () => { + (0, import_util.isServerFileSetup)() && (0, import_util.isRealtimeSetup)(); + } + }, + { + title: `Adding ${name} example subscription ...`, + enabled: () => functionType === "subscription", + task: () => { + const exampleSdlTemplateContent = import_path.default.resolve( + __dirname, + "templates", + "subscriptions", + "blank", + `blank.sdl.ts.template` + ); + const sdlFile = import_path.default.join( + redwoodPaths.api.graphql, + `${name}.sdl.${(0, import_project.isTypeScriptProject)() ? "ts" : "js"}` + ); + const sdlContent = ts ? exampleSdlTemplateContent : (0, import_lib.transformTSToJS)(sdlFile, exampleSdlTemplateContent); + const exampleServiceTemplateContent = import_path.default.resolve( + __dirname, + "templates", + "subscriptions", + "blank", + `blank.service.ts.template` + ); + const serviceFile = import_path.default.join( + redwoodPaths.api.services, + `${name}`, + `${name}.${(0, import_project.isTypeScriptProject)() ? "ts" : "js"}` + ); + const serviceContent = ts ? exampleServiceTemplateContent : (0, import_lib.transformTSToJS)(serviceFile, exampleServiceTemplateContent); + const exampleSubscriptionTemplateContent = import_path.default.resolve( + __dirname, + "templates", + "subscriptions", + "blank", + `blank.ts.template` + ); + const exampleFile = import_path.default.join( + redwoodPaths.api.subscriptions, + `${name}`, + `${name}.${(0, import_project.isTypeScriptProject)() ? "ts" : "js"}` + ); + const setupScriptContent = ts ? exampleSubscriptionTemplateContent : (0, import_lib.transformTSToJS)(exampleFile, exampleSubscriptionTemplateContent); + return [ + (0, import_lib.writeFile)( + sdlFile, + (0, import_lib.generateTemplate)(sdlContent, templateVariables(name)), + { + overwriteExisting: force + } + ), + (0, import_lib.writeFile)( + serviceFile, + (0, import_lib.generateTemplate)(serviceContent, templateVariables(name)), + { + overwriteExisting: force + } + ), + (0, import_lib.writeFile)( + exampleFile, + (0, import_lib.generateTemplate)(setupScriptContent, templateVariables(name)), + { + overwriteExisting: force + } + ) + ]; + } + }, + { + title: `Adding ${name} example live query ...`, + enabled: () => functionType === "liveQuery", + task: () => { + const exampleSdlTemplateContent = import_path.default.resolve( + __dirname, + "templates", + "liveQueries", + "blank", + `blank.sdl.ts.template` + ); + const sdlFile = import_path.default.join( + redwoodPaths.api.graphql, + `${name}.sdl.${(0, import_project.isTypeScriptProject)() ? "ts" : "js"}` + ); + const sdlContent = ts ? exampleSdlTemplateContent : (0, import_lib.transformTSToJS)(sdlFile, exampleSdlTemplateContent); + const exampleServiceTemplateContent = import_path.default.resolve( + __dirname, + "templates", + "liveQueries", + "blank", + "blank.service.ts.template" + ); + const serviceFile = import_path.default.join( + redwoodPaths.api.services, + `${name}`, + `${name}.${(0, import_project.isTypeScriptProject)() ? "ts" : "js"}` + ); + const serviceContent = ts ? exampleServiceTemplateContent : (0, import_lib.transformTSToJS)(serviceFile, exampleServiceTemplateContent); + return [ + (0, import_lib.writeFile)( + sdlFile, + (0, import_lib.generateTemplate)(sdlContent, templateVariables(name)), + { + overwriteExisting: force + } + ), + (0, import_lib.writeFile)( + serviceFile, + (0, import_lib.generateTemplate)(serviceContent, templateVariables(name)), + { + overwriteExisting: force + } + ) + ]; + } + }, + { + title: `Generating types ...`, + task: async () => { + await (0, import_generate.generate)(); + console.log( + "Note: You may need to manually restart GraphQL in VSCode to see the new types take effect.\n\n" + ); + } + } + ], + { + rendererOptions: { collapseSubtasks: false, persistentOutput: true }, + renderer: verbose ? "verbose" : "default" + } + ); + try { + await tasks.run(); + } catch (e) { + (0, import_telemetry.errorTelemetry)(process.argv, e.message); + console.error(import_colors.default.error(e.message)); + process.exit(e?.exitCode || 1); + } +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + handler +}); diff --git a/packages/cli/dist/commands/generate/realtime/templates/liveQueries/blank/blank.sdl.ts.template b/packages/cli/dist/commands/generate/realtime/templates/liveQueries/blank/blank.sdl.ts.template new file mode 100644 index 0000000000..a373de5f7d --- /dev/null +++ b/packages/cli/dist/commands/generate/realtime/templates/liveQueries/blank/blank.sdl.ts.template @@ -0,0 +1,27 @@ +// api/src/graphql/auctions.sdl.ts + +export const schema = gql` + type Query { + ${liveQueryName}(id: ID!): ${typeName} @requireAuth + } + + type ${typeName} { + id: ID! + title: String! + largest${typeName}Item: ${typeName}Item + items: [${typeName}Item!]! + } + + type ${typeName}Item { + amount: Int! + } + + type Mutation { + create${typeName}Item(input: ${typeName}ItemInput!): ${typeName}Item @requireAuth + } + + input ${typeName}ItemInput { + ${camelName}Id: ID! + amount: Int! + } +` diff --git a/packages/cli/dist/commands/generate/realtime/templates/liveQueries/blank/blank.service.ts.template b/packages/cli/dist/commands/generate/realtime/templates/liveQueries/blank/blank.service.ts.template new file mode 100644 index 0000000000..a14fb04551 --- /dev/null +++ b/packages/cli/dist/commands/generate/realtime/templates/liveQueries/blank/blank.service.ts.template @@ -0,0 +1,73 @@ +// api/src/services/${name}s/${name}s.ts +import type { LiveQueryStorageMechanism } from '@redwoodjs/realtime' + +import { logger } from 'src/lib/logger' + +const ${collectionName} = [ + { id: '1', title: 'First ${modelName}', items: [{ amount: 11 }] }, + { id: '2', title: '2nd ${modelName}', items: [{ amount: 22 }] }, + { id: '3', title: 'Third ${modelName}', items: [{ amount: 33 }] }, + { id: '4', title: '4th ${modelName}', items: [{ amount: 44 }] }, + { id: '5', title: 'Fifth ${modelName}', items: [{ amount: 55 }] }, +] + +/** + * To test this live query, run the following in the GraphQL Playground: + * + * query GetRecent${pluralPascalName} @live { + * ${liveQueryName}(id: "1") { + * items { + * amount + * } + * largest${typeName}Item { + * amount + * } + * id + * title + * } + * } + * + * And then make a related item with the following mutation: + * + * mutation Create${typeName}Item { + * create${typeName}Item(input: {${camelName}Id: "1", amount: 100}) { + * amount + * } + * } + */ +export const ${liveQueryName} = async ({ id }) => { + const found${modelName} = ${collectionName}.find((a) => a.id === id) + logger.debug({ id, ${name}: found${modelName} }, `${name} details`) + return found${modelName} +} + +export const create${typeName}Item = async ( + { input }, + { context }: { context: { liveQueryStore: LiveQueryStorageMechanism } } +) => { + const { ${camelName}Id, amount } = input + + const index = ${collectionName}.findIndex((a) => a.id === ${camelName}Id) + + const item = { amount } + + ${collectionName}[index].items.push(item) + logger.debug({ ${camelName}Id, item }, 'Added item to ${collectionName}') + + const key = `${typeName}:<%= '$' %>{${camelName}Id}` + context.liveQueryStore.invalidate(key) + + logger.debug({ key }, 'Invalidated ${typeName} key in liveQueryStore') + + return item +} + +export const ${modelName} = { + largest${typeName}Item: (obj, { root }) => { + const [largest] = root.items.sort((a, b) => b.amount - a.amount) + + logger.debug({ obj, root }, 'largest ${typeName}Item') + + return largest + }, +} diff --git a/packages/cli/dist/commands/generate/realtime/templates/realtime.ts.template b/packages/cli/dist/commands/generate/realtime/templates/realtime.ts.template new file mode 100644 index 0000000000..33028f3417 --- /dev/null +++ b/packages/cli/dist/commands/generate/realtime/templates/realtime.ts.template @@ -0,0 +1,42 @@ +import { RedwoodRealtimeOptions } from '@redwoodjs/realtime' + +import subscriptions from 'src/subscriptions/**/*.{js,ts}' + +// if using a Redis store +// import { Redis } from 'ioredis' +// const publishClient = new Redis() +// const subscribeClient = new Redis() + +/** + * Configure RedwoodJS Realtime + * + * See https://redwoodjs.com/docs/realtime + * + * Realtime supports Live Queries and Subscriptions over GraphQL SSE. + * + * Live Queries are GraphQL queries that are automatically re-run when the data they depend on changes. + * + * Subscriptions are GraphQL queries that are run when a client subscribes to a channel. + * + * Redwood Realtime + * - uses a publish/subscribe model to broadcast data to clients. + * - uses a store to persist Live Query and Subscription data. + * + * Redwood Realtime supports in-memory and Redis stores: + * - In-memory stores are useful for development and testing. + * - Redis stores are useful for production. + * + */ +export const realtime: RedwoodRealtimeOptions = { + subscriptions: { + subscriptions, + store: 'in-memory', + // if using a Redis store + // store: { redis: { publishClient, subscribeClient } }, + }, + liveQueries: { + store: 'in-memory', + // if using a Redis store + // store: { redis: { publishClient, subscribeClient } }, + }, +} diff --git a/packages/cli/dist/commands/generate/realtime/templates/subscriptions/blank/blank.sdl.ts.template b/packages/cli/dist/commands/generate/realtime/templates/subscriptions/blank/blank.sdl.ts.template new file mode 100644 index 0000000000..225ee258ce --- /dev/null +++ b/packages/cli/dist/commands/generate/realtime/templates/subscriptions/blank/blank.sdl.ts.template @@ -0,0 +1,20 @@ +export const schema = gql` + type ${typeName} { + from: String + body: String + } + + type Query { + ${subscriptionQueryName}(id: ID!): [${typeName}!]! @skipAuth + } + + input ${subscriptionInputType} { + id: ID! + from: String! + body: String! + } + + type Mutation { + ${subscriptionServiceResolver}(input: ${subscriptionInputType}!): ${typeName}! @skipAuth + } +` diff --git a/packages/cli/dist/commands/generate/realtime/templates/subscriptions/blank/blank.service.ts.template b/packages/cli/dist/commands/generate/realtime/templates/subscriptions/blank/blank.service.ts.template new file mode 100644 index 0000000000..f5269228d9 --- /dev/null +++ b/packages/cli/dist/commands/generate/realtime/templates/subscriptions/blank/blank.service.ts.template @@ -0,0 +1,19 @@ +import type { ${subscriptionInputType} } from 'types/graphql' + +import { logger } from 'src/lib/logger' +import type { Publish${typeName}ChannelType } from 'src/subscriptions/${name}/${name}' + +export const ${subscriptionQueryName} = ({ id }) => [id] + +export const ${subscriptionServiceResolver} = async ( + { input }: { input: ${subscriptionInputType} }, + { context }: { context: { pubSub: Publish${typeName}ChannelType } } +) => { + logger.debug({ input }, 'publishing ${name} ....') + + const { id, from, body } = input + +context.pubSub.publish('${typeName}', id, { from, body }) + + return input +} diff --git a/packages/cli/dist/commands/generate/realtime/templates/subscriptions/blank/blank.ts.template b/packages/cli/dist/commands/generate/realtime/templates/subscriptions/blank/blank.ts.template new file mode 100644 index 0000000000..4118e11226 --- /dev/null +++ b/packages/cli/dist/commands/generate/realtime/templates/subscriptions/blank/blank.ts.template @@ -0,0 +1,58 @@ +import gql from 'graphql-tag' + +import type { PubSub } from '@redwoodjs/realtime' + +import { logger } from 'src/lib/logger' + +export const schema = gql` + type Subscription { + ${subscriptionName}(id: ID!): ${typeName}! @requireAuth + } +` + +export type Publish${typeName}Channel = { + ${typeName}: [id: string, payload: { from: string; body: string }] +} + +export type Publish${typeName}ChannelType = PubSub + +/** + * To test this ${typeName} subscription, run the following in one GraphQL Playground to subscribe: + * + * subscription ListenTo${channelName} { + * ${subscriptionName}(id: "1") { + * body + * from + * } + * } + * + * + * And run the following in another GraphQL Playground to publish and send a new ${name} to the channel: + * + * mutation PublishTo${channelName} { + * ${subscriptionServiceResolver}(input: {id: "1", from: "hello", body: "bob"}) { + * body + * from + * } + * } + */ +const ${subscriptionName}Subscription = { + ${subscriptionName}: { + subscribe: ( + _, + { id }, + { pubSub }: { pubSub: Publish${typeName}ChannelType } + ) => { + logger.debug({ id }, '${name} subscription') + + return pubSub.subscribe('${typeName}', id) + }, + resolve: (payload) => { + logger.debug({ payload }, '${name} subscription resolve') + + return payload + }, + }, +} + +export default ${subscriptionName}Subscription diff --git a/packages/cli/dist/commands/generate/scaffold/scaffold.js b/packages/cli/dist/commands/generate/scaffold/scaffold.js new file mode 100644 index 0000000000..3e56adc889 --- /dev/null +++ b/packages/cli/dist/commands/generate/scaffold/scaffold.js @@ -0,0 +1,718 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var scaffold_exports = {}; +__export(scaffold_exports, { + builder: () => builder, + command: () => command, + description: () => description, + files: () => files, + handler: () => handler, + routes: () => routes, + shouldUseTailwindCSS: () => shouldUseTailwindCSS, + splitPathAndModel: () => splitPathAndModel, + tasks: () => tasks +}); +module.exports = __toCommonJS(scaffold_exports); +var import_path = __toESM(require("path")); +var import_camelcase = __toESM(require("camelcase")); +var import_execa = __toESM(require("execa")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_humanize_string = __toESM(require("humanize-string")); +var import_listr2 = require("listr2"); +var import_param_case = require("param-case"); +var import_pascalcase = __toESM(require("pascalcase")); +var import_terminal_link = __toESM(require("terminal-link")); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_generate = require("@redwoodjs/internal/dist/generate/generate"); +var import_project_config = require("@redwoodjs/project-config"); +var import_lib = require("../../../lib"); +var import_colors = __toESM(require("../../../lib/colors")); +var import_rollback = require("../../../lib/rollback"); +var import_rwPluralize = require("../../../lib/rwPluralize"); +var import_schemaHelpers = require("../../../lib/schemaHelpers"); +var import_helpers = require("../helpers"); +var import_helpers2 = require("../helpers"); +var import_sdl = require("../sdl/sdl"); +var import_service = require("../service/service"); +const NON_EDITABLE_COLUMNS = ["id", "createdAt", "updatedAt"]; +const SKIPPABLE_ASSETS = ["scaffold.css"]; +const PACKAGE_SET = "Set"; +const getIdType = (model) => { + return model.fields.find((field) => field.isId)?.type; +}; +const getIdName = (model) => { + return model.fields.find((field) => field.isId)?.name; +}; +const filterAutoGeneratedColumnsForScaffold = (column) => { + const autoGeneratedFunctions = ["now", "autoincrement"]; + return !(column.isId || column.isUpdatedAt || autoGeneratedFunctions.includes(column?.default?.name)); +}; +const getImportComponentNames = (name, scaffoldPath, nestScaffoldByModel = true) => { + const pluralName = (0, import_pascalcase.default)((0, import_rwPluralize.pluralize)(name)); + const singularName = (0, import_pascalcase.default)((0, import_rwPluralize.singularize)(name)); + let componentPath; + if (scaffoldPath === "") { + componentPath = nestScaffoldByModel ? `src/components/${singularName}` : `src/components`; + } else { + const sP = scaffoldPath.split("/").map(import_pascalcase.default).join("/"); + componentPath = nestScaffoldByModel ? `src/components/${sP}/${singularName}` : `src/components/${sP}`; + } + return { + importComponentName: `${componentPath}/${singularName}`, + importComponentNameCell: `${componentPath}/${singularName}Cell`, + importComponentEditNameCell: `${componentPath}/Edit${singularName}Cell`, + importComponentNameForm: `${componentPath}/${singularName}Form`, + importComponentNewName: `${componentPath}/New${singularName}`, + importComponentNames: `${componentPath}/${pluralName}`, + importComponentNamesCell: `${componentPath}/${pluralName}Cell`, + importLayoutNames: `src/layouts/ScaffoldLayout` + }; +}; +const getTemplateStrings = (name, scaffoldPath, nestScaffoldByModel = true) => { + const nameVars = (0, import_lib.nameVariants)(name); + const camelScaffoldPath = (0, import_camelcase.default)((0, import_pascalcase.default)(scaffoldPath)); + return { + pluralRouteName: scaffoldPath === "" ? nameVars.pluralCamelName : `${camelScaffoldPath}${nameVars.pluralPascalName}`, + editRouteName: scaffoldPath === "" ? `edit${nameVars.singularPascalName}` : `${camelScaffoldPath}Edit${nameVars.singularPascalName}`, + singularRouteName: scaffoldPath === "" ? nameVars.singularCamelName : `${camelScaffoldPath}${nameVars.singularPascalName}`, + newRouteName: scaffoldPath === "" ? `new${nameVars.singularPascalName}` : `${camelScaffoldPath}New${nameVars.singularPascalName}`, + ...getImportComponentNames(name, scaffoldPath, nestScaffoldByModel) + }; +}; +const shouldUseTailwindCSS = (flag) => { + if (flag === void 0) { + return import_fs_extra.default.existsSync(import_path.default.join((0, import_lib.getPaths)().web.config, "tailwind.config.js")); + } else { + return flag; + } +}; +const files = async ({ + docs, + model: name, + path: scaffoldPath = "", + tests = true, + typescript = false, + tailwind = false, + force = false, + nestScaffoldByModel +}) => { + const model = await (0, import_schemaHelpers.getSchema)(name); + if (typeof nestScaffoldByModel === "undefined") { + nestScaffoldByModel = (0, import_project_config.getConfig)().generate.nestScaffoldByModel; + } + const templateStrings = getTemplateStrings( + name, + scaffoldPath, + nestScaffoldByModel + ); + const pascalScaffoldPath = scaffoldPath === "" ? scaffoldPath : scaffoldPath.split("/").map(import_pascalcase.default).join("/") + "/"; + return { + ...await componentFiles( + name, + pascalScaffoldPath, + typescript, + nestScaffoldByModel, + templateStrings + ), + ...await (0, import_sdl.files)({ + ...(0, import_lib.getDefaultArgs)(import_sdl.builder), + docs, + name, + typescript + }), + ...await (0, import_service.files)({ + ...(0, import_lib.getDefaultArgs)(import_service.builder), + name, + crud: true, + relations: (0, import_helpers2.relationsForModel)(model), + tests, + typescript + }), + ...await assetFiles(name, tailwind), + ...await formatters(name, typescript), + ...await layoutFiles(name, force, typescript, templateStrings), + ...await pageFiles( + name, + pascalScaffoldPath, + typescript, + nestScaffoldByModel, + templateStrings + ) + }; +}; +const assetFiles = async (name, tailwind) => { + let fileList = {}; + const assets = import_fs_extra.default.readdirSync( + (0, import_helpers2.customOrDefaultTemplatePath)({ + side: "web", + generator: "scaffold", + templatePath: "assets" + }) + ); + for (const asset of assets) { + if (tailwind && asset.match(/tailwind/) || !tailwind && !asset.match(/tailwind/)) { + const outputAssetName = asset.replace(/\.template/, "").replace(/\.tailwind/, ""); + const outputPath = import_path.default.join((0, import_lib.getPaths)().web.src, outputAssetName); + if (!SKIPPABLE_ASSETS.includes(import_path.default.basename(outputPath)) || !import_fs_extra.default.existsSync(outputPath)) { + const template = await (0, import_lib.generateTemplate)( + (0, import_helpers2.customOrDefaultTemplatePath)({ + side: "web", + generator: "scaffold", + templatePath: import_path.default.join("assets", asset) + }), + { + name + } + ); + fileList[outputPath] = template; + } + } + } + return fileList; +}; +const formatters = async (name, isTypescript) => { + const outputPath = import_path.default.join( + (0, import_lib.getPaths)().web.src, + "lib", + isTypescript ? "formatters.tsx" : "formatters.jsx" + ); + const outputPathTest = import_path.default.join( + (0, import_lib.getPaths)().web.src, + "lib", + isTypescript ? "formatters.test.tsx" : "formatters.test.jsx" + ); + if (import_fs_extra.default.existsSync(outputPath)) { + return; + } + const template = await (0, import_lib.generateTemplate)( + (0, import_helpers2.customOrDefaultTemplatePath)({ + side: "web", + generator: "scaffold", + templatePath: import_path.default.join("lib", "formatters.tsx.template") + }), + { + name + } + ); + const templateTest = await (0, import_lib.generateTemplate)( + (0, import_helpers2.customOrDefaultTemplatePath)({ + side: "web", + generator: "scaffold", + templatePath: import_path.default.join("lib", "formatters.test.tsx.template") + }), + { + name + } + ); + return { + [outputPath]: isTypescript ? template : await (0, import_lib.transformTSToJS)(outputPath, template), + [outputPathTest]: isTypescript ? templateTest : await (0, import_lib.transformTSToJS)(outputPathTest, templateTest) + }; +}; +const modelRelatedVariables = (model) => { + const componentMetadata = { + Enum: { + componentName: "RadioField", + defaultProp: "defaultChecked", + validation: () => false, + listDisplayFunction: "formatEnum", + displayFunction: "formatEnum" + }, + EnumList: { + componentName: "CheckboxField", + defaultProp: "defaultChecked", + validation: () => false, + listDisplayFunction: "formatEnum", + displayFunction: "formatEnum" + }, + Boolean: { + componentName: "CheckboxField", + defaultProp: "defaultChecked", + validation: () => false, + listDisplayFunction: "checkboxInputTag", + displayFunction: "checkboxInputTag" + }, + DateTime: { + componentName: "DatetimeLocalField", + deserializeFunction: "formatDatetime", + listDisplayFunction: "timeTag", + displayFunction: "timeTag" + }, + Int: { + componentName: "NumberField" + }, + Json: { + componentName: "TextAreaField", + validation: (isRequired) => `{{ valueAsJSON: true${isRequired ? ", required: true" : ""} }}`, + displayFunction: "jsonDisplay", + listDisplayFunction: "jsonTruncate", + deserializeFunction: "JSON.stringify" + }, + Float: { + validation: (isRequired) => `{{ valueAsNumber: true${isRequired ? ", required: true" : ""} }}` + }, + Decimal: { + validation: (isRequired) => `{{ valueAsNumber: true${isRequired ? ", required: true" : ""} }}` + }, + default: { + componentName: "TextField", + defaultProp: "defaultValue", + deserializeFunction: "", + validation: "{{ required: true }}", + displayFunction: void 0, + listDisplayFunction: "truncate" + } + }; + const relations = (0, import_helpers2.relationsForModel)(model).map((relation) => relation); + const columns = model.fields.filter((field) => field.kind !== "object").map((column) => { + let validation; + if (componentMetadata[column.type]?.validation) { + validation = componentMetadata[column.type]?.validation( + column?.isRequired + ); + } else { + validation = column?.isRequired ? componentMetadata.default.validation : null; + } + const isRelationalField = column.name.endsWith("Id") && relations.some((relation) => column.name.includes(relation)); + const isRequired = column.isRequired; + const isEnum = column.kind === "enum"; + const isList = column.isList; + const enumType = isEnum && isList ? "EnumList" : "Enum"; + const metadataKey = isEnum ? enumType : column.type; + return { + ...column, + label: (0, import_humanize_string.default)(column.name), + component: componentMetadata[metadataKey]?.componentName || componentMetadata.default.componentName, + defaultProp: componentMetadata[metadataKey]?.defaultProp || componentMetadata.default.defaultProp, + deserializeFunction: componentMetadata[metadataKey]?.deserializeFunction || componentMetadata.default.deserializeFunction, + validation, + listDisplayFunction: componentMetadata[metadataKey]?.listDisplayFunction || componentMetadata.default.listDisplayFunction, + displayFunction: componentMetadata[metadataKey]?.displayFunction || componentMetadata.default.displayFunction, + values: column.enumValues || [], + isList, + isEnum, + isRequired, + isRelationalField + }; + }); + const editableColumns = columns.filter((column) => { + return NON_EDITABLE_COLUMNS.indexOf(column.name) === -1; + }).filter(filterAutoGeneratedColumnsForScaffold); + const fieldsToImport = Object.keys( + editableColumns.reduce((accumulator, column) => { + accumulator[column.component] = true; + return accumulator; + }, {}) + ); + if (!fieldsToImport.length) { + throw new Error(`There are no editable fields in the ${model.name} model`); + } + const formattersImports = columns.map((column) => column.displayFunction).sort().filter((name, index, array) => array.indexOf(name) === index).join(", "); + const listFormattersImports = columns.map((column) => column.listDisplayFunction).sort().filter((name, index, array) => array.indexOf(name) === index).join(", "); + return { + columns, + fieldsToImport, + editableColumns, + listFormattersImports, + formattersImports + }; +}; +const layoutFiles = async (name, force, generateTypescript, templateStrings) => { + let fileList = {}; + const layouts = import_fs_extra.default.readdirSync( + (0, import_helpers2.customOrDefaultTemplatePath)({ + side: "web", + generator: "scaffold", + templatePath: "layouts" + }) + ); + for (const layout of layouts) { + const outputLayoutName = layout.replace( + /\.tsx\.template/, + generateTypescript ? ".tsx" : ".jsx" + ); + const outputPath = import_path.default.join( + (0, import_lib.getPaths)().web.layouts, + "ScaffoldLayout", + outputLayoutName + ); + if (!import_fs_extra.default.existsSync(outputPath) || force) { + const template = await (0, import_lib.generateTemplate)( + (0, import_helpers2.customOrDefaultTemplatePath)({ + side: "web", + generator: "scaffold", + templatePath: import_path.default.join("layouts", layout) + }), + { + name, + pascalScaffoldPath: "", + ...templateStrings + } + ); + fileList[outputPath] = generateTypescript ? template : await (0, import_lib.transformTSToJS)(outputPath, template); + } + } + return fileList; +}; +const pageFiles = async (name, pascalScaffoldPath = "", generateTypescript, nestScaffoldByModel = true, templateStrings) => { + const pluralName = (0, import_pascalcase.default)((0, import_rwPluralize.pluralize)(name)); + const singularName = (0, import_pascalcase.default)((0, import_rwPluralize.singularize)(name)); + const model = await (0, import_schemaHelpers.getSchema)(singularName); + const idType = getIdType(model); + const idTsType = (0, import_helpers2.mapPrismaScalarToPagePropTsType)(idType); + const idName = getIdName(model); + let fileList = {}; + const pages = import_fs_extra.default.readdirSync( + (0, import_helpers2.customOrDefaultTemplatePath)({ + side: "web", + generator: "scaffold", + templatePath: "pages" + }) + ); + for (const page of pages) { + const outputPageName = page.replace(/Names/, pluralName).replace(/Name/, singularName).replace(/\.tsx\.template/, generateTypescript ? ".tsx" : ".jsx"); + const finalFolder = (nestScaffoldByModel ? singularName + "/" : "") + outputPageName.replace(/\.[jt]sx?/, ""); + const outputPath = import_path.default.join( + (0, import_lib.getPaths)().web.pages, + pascalScaffoldPath, + finalFolder, + outputPageName + ); + const template = await (0, import_lib.generateTemplate)( + (0, import_helpers2.customOrDefaultTemplatePath)({ + side: "web", + generator: "scaffold", + templatePath: import_path.default.join("pages", page) + }), + { + idTsType, + idName, + name, + pascalScaffoldPath, + ...templateStrings, + ...modelRelatedVariables(model) + } + ); + fileList[outputPath] = generateTypescript ? template : await (0, import_lib.transformTSToJS)(outputPath, template); + } + return fileList; +}; +const componentFiles = async (name, pascalScaffoldPath = "", generateTypescript, nestScaffoldByModel = true, templateStrings) => { + const pluralName = (0, import_pascalcase.default)((0, import_rwPluralize.pluralize)(name)); + const singularName = (0, import_pascalcase.default)((0, import_rwPluralize.singularize)(name)); + const model = await (0, import_schemaHelpers.getSchema)(singularName); + const idType = getIdType(model); + const idName = getIdName(model); + const pascalIdName = (0, import_pascalcase.default)(idName); + const intForeignKeys = (0, import_helpers2.intForeignKeysForModel)(model); + let fileList = {}; + const components = import_fs_extra.default.readdirSync( + (0, import_helpers2.customOrDefaultTemplatePath)({ + side: "web", + generator: "scaffold", + templatePath: "components" + }) + ); + for (const component of components) { + const outputComponentName = component.replace(/Names/, pluralName).replace(/Name/, singularName).replace(/\.tsx\.template/, generateTypescript ? ".tsx" : ".jsx"); + const finalFolder = (nestScaffoldByModel ? singularName + "/" : "") + outputComponentName.replace(/\.[jt]sx?/, ""); + const outputPath = import_path.default.join( + (0, import_lib.getPaths)().web.components, + pascalScaffoldPath, + finalFolder, + outputComponentName + ); + const useClientDirective = (0, import_project_config.getConfig)().experimental?.rsc?.enabled ? "'use client'\n\n" : ""; + const template = await (0, import_lib.generateTemplate)( + (0, import_helpers2.customOrDefaultTemplatePath)({ + side: "web", + generator: "scaffold", + templatePath: import_path.default.join("components", component) + }), + { + name, + idType, + idName, + pascalIdName, + intForeignKeys, + pascalScaffoldPath, + useClientDirective, + ...templateStrings, + ...modelRelatedVariables(model) + } + ); + fileList[outputPath] = generateTypescript ? template : await (0, import_lib.transformTSToJS)(outputPath, template); + } + return fileList; +}; +const routes = async ({ + model: name, + path: scaffoldPath = "", + nestScaffoldByModel +}) => { + if (typeof nestScaffoldByModel === "undefined") { + nestScaffoldByModel = (0, import_project_config.getConfig)().generate.nestScaffoldByModel; + } + const templateNames = getTemplateStrings(name, scaffoldPath); + const nameVars = (0, import_lib.nameVariants)(name); + const model = await (0, import_schemaHelpers.getSchema)(nameVars.singularPascalName); + const idRouteParam = getIdType(model) === "Int" ? ":Int" : ""; + const idName = getIdName(model); + const paramScaffoldPath = scaffoldPath === "" ? scaffoldPath : scaffoldPath.split("/").map(import_param_case.paramCase).join("/") + "/"; + const pascalScaffoldPath = (0, import_pascalcase.default)(scaffoldPath); + const pageRoot = pascalScaffoldPath + (nestScaffoldByModel ? nameVars.singularPascalName : ""); + return [ + // new + ``, + // edit + ``, + // singular + ``, + // plural + `` + ]; +}; +const addLayoutImport = () => { + const importLayout = `import ScaffoldLayout from 'src/layouts/ScaffoldLayout'`; + const routesPath = (0, import_lib.getPaths)().web.routes; + const routesContent = (0, import_lib.readFile)(routesPath).toString(); + if (!routesContent.match(importLayout)) { + const newRoutesContent = routesContent.replace( + /['"]@redwoodjs\/router['"](\s*)/, + `'@redwoodjs/router' + +${importLayout}$1` + ); + (0, import_lib.writeFile)(routesPath, newRoutesContent, { overwriteExisting: true }); + return "Added layout import to Routes.{jsx,tsx}"; + } else { + return "Layout import already exists in Routes.{jsx,tsx}"; + } +}; +const addHelperPackages = async (task) => { + const packageJsonPath = import_path.default.join((0, import_lib.getPaths)().web.base, "package.json"); + const packageJson = require(packageJsonPath); + if (packageJson.dependencies["humanize-string"]) { + return task.skip("Skipping. Already installed"); + } + await (0, import_execa.default)("yarn", ["workspace", "web", "add", "humanize-string@2.1.0"]); + (0, import_rollback.addFunctionToRollback)(async () => { + await (0, import_execa.default)("yarn", ["workspace", "web", "remove", "humanize-string"]); + }); +}; +const addSetImport = (task) => { + const routesPath = (0, import_lib.getPaths)().web.routes; + const routesContent = (0, import_lib.readFile)(routesPath).toString(); + const [redwoodRouterImport, importStart, spacing, importContent, importEnd] = routesContent.match( + /(import {)(\s*)([^]*)(} from ['"]@redwoodjs\/router['"])/ + ) || []; + if (!redwoodRouterImport) { + task.skip( + "Couldn't add Set import from @redwoodjs/router to Routes.{jsx,tsx}" + ); + return void 0; + } + const routerImports = importContent.replace(/\s/g, "").split(","); + if (routerImports.includes(PACKAGE_SET)) { + return "Skipping Set import"; + } + const newRoutesContent = routesContent.replace( + redwoodRouterImport, + importStart + spacing + PACKAGE_SET + `,` + spacing + importContent + importEnd + ); + (0, import_lib.writeFile)(routesPath, newRoutesContent, { overwriteExisting: true }); + return "Added Set import to Routes.{jsx,tsx}"; +}; +const addScaffoldSetToRouter = async (model, path2) => { + const templateNames = getTemplateStrings(model, path2); + const nameVars = (0, import_lib.nameVariants)(model); + const title = nameVars.pluralPascalName; + const titleTo = templateNames.pluralRouteName; + const buttonLabel = `New ${nameVars.singularPascalName}`; + const buttonTo = templateNames.newRouteName; + return (0, import_lib.addRoutesToRouterTask)( + await routes({ model, path: path2 }), + "ScaffoldLayout", + { title, titleTo, buttonLabel, buttonTo } + ); +}; +const command = "scaffold "; +const description = "Generate Pages, SDL, and Services files based on a given DB schema Model. Also accepts "; +const builder = (yargs) => { + yargs.positional("model", { + description: "Model to scaffold. You can also use to nest files by type at the given path directory (or directories). For example, 'rw g scaffold admin/post'" + }).option("docs", { + description: "Generate SDL and GraphQL comments to use in documentation", + type: "boolean", + default: false + }).option("tests", { + description: "Generate test files", + type: "boolean" + }).option("tailwind", { + description: "Generate TailwindCSS version of scaffold.css (automatically set to `true` if TailwindCSS config exists)", + type: "boolean" + }).option("rollback", { + description: "Revert all generator actions if an error occurs", + type: "boolean", + default: true + }).epilogue( + `Also see the ${(0, import_terminal_link.default)( + "Redwood CLI Reference", + "https://redwoodjs.com/docs/cli-commands#generate-scaffold" + )}` + ); + Object.entries(import_helpers.yargsDefaults).forEach(([option, config]) => { + yargs.option(option, config); + }); +}; +const tasks = ({ + docs, + model, + path: path2, + force, + tests, + typescript, + javascript, + tailwind +}) => { + return new import_listr2.Listr( + [ + { + title: "Generating scaffold files...", + task: async () => { + const f = await files({ + docs, + model, + path: path2, + tests, + typescript, + javascript, + tailwind, + force + }); + return (0, import_lib.writeFilesTask)(f, { overwriteExisting: force }); + } + }, + { + title: "Install helper packages", + task: (_, task) => addHelperPackages(task) + }, + { + title: "Adding layout import...", + task: async () => addLayoutImport() + }, + { + title: "Adding set import...", + task: async (_, task) => addSetImport(task) + }, + { + title: "Adding scaffold routes...", + task: async () => addScaffoldSetToRouter(model, path2) + }, + { + title: "Adding scaffold asset imports...", + task: () => (0, import_lib.addScaffoldImport)() + }, + { + title: `Generating types ...`, + task: async () => { + const { errors } = await (0, import_generate.generate)(); + for (const { message, error } of errors) { + console.error(message); + console.log(); + console.error(error); + console.log(); + } + (0, import_rollback.addFunctionToRollback)(import_generate.generate, true); + } + } + ], + { rendererOptions: { collapseSubtasks: false }, exitOnError: true } + ); +}; +const handler = async ({ + model: modelArg, + force, + tests, + typescript, + tailwind, + docs = false, + rollback +}) => { + if (tests === void 0) { + tests = (0, import_project_config.getConfig)().generate.tests; + } + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "generate scaffold", + force, + tests, + typescript, + tailwind, + docs, + rollback + }); + const { model, path: path2 } = splitPathAndModel(modelArg); + tailwind = shouldUseTailwindCSS(tailwind); + try { + const { name } = await (0, import_schemaHelpers.verifyModelName)({ name: model }); + const t = tasks({ + docs, + model: name, + path: path2, + force, + tests, + typescript, + tailwind + }); + if (rollback && !force) { + (0, import_rollback.prepareForRollback)(t); + } + await t.run(); + } catch (e) { + console.log(import_colors.default.error(e.message)); + process.exit(e?.existCode || 1); + } +}; +const splitPathAndModel = (pathSlashModel) => { + const path2 = pathSlashModel.split("/").slice(0, -1).join("/"); + const model = pathSlashModel.split("/").pop(); + return { model, path: path2 }; +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + files, + handler, + routes, + shouldUseTailwindCSS, + splitPathAndModel, + tasks +}); diff --git a/packages/cli/dist/commands/generate/scaffold/templates/assets/scaffold.css.template b/packages/cli/dist/commands/generate/scaffold/templates/assets/scaffold.css.template new file mode 100644 index 0000000000..fe365752a2 --- /dev/null +++ b/packages/cli/dist/commands/generate/scaffold/templates/assets/scaffold.css.template @@ -0,0 +1,398 @@ +/* + normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css +*/ + +.rw-scaffold *, +.rw-scaffold ::after, +.rw-scaffold ::before { + box-sizing: inherit; +} +.rw-scaffold main { + color: #4a5568; + display: block; +} +.rw-scaffold h1, +.rw-scaffold h2 { + margin: 0; +} +.rw-scaffold a { + background-color: transparent; +} +.rw-scaffold ul { + margin: 0; + padding: 0; +} +.rw-scaffold input { + font-family: inherit; + font-size: 100%; + overflow: visible; +} +.rw-scaffold input:-ms-input-placeholder { + color: #a0aec0; +} +.rw-scaffold input::-ms-input-placeholder { + color: #a0aec0; +} +.rw-scaffold input::placeholder { + color: #a0aec0; +} +.rw-scaffold table { + border-collapse: collapse; +} + +/* + Style +*/ + +.rw-scaffold, .rw-toast { + background-color: #fff; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, + 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', + 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; +} +.rw-header { + display: flex; + justify-content: space-between; + padding: 1rem 2rem 1rem 2rem; +} +.rw-main { + margin-left: 1rem; + margin-right: 1rem; + padding-bottom: 1rem; +} +.rw-segment { + border-radius: 0.5rem; + border-width: 1px; + border-color: #e5e7eb; + overflow: hidden; + width: 100%; + scrollbar-color: #a1a1aa transparent; +} +.rw-segment::-webkit-scrollbar { + height: initial; +} +.rw-segment::-webkit-scrollbar-track { + background-color: transparent; + border-color: #e2e8f0; + border-style: solid; + border-radius: 0 0 10px 10px; + border-width: 1px 0 0 0; + padding: 2px; +} +.rw-segment::-webkit-scrollbar-thumb { + background-color: #a1a1aa; + background-clip: content-box; + border: 3px solid transparent; + border-radius: 10px; +} +.rw-segment-header { + background-color: #e2e8f0; + color: #4a5568; + padding: 0.75rem 1rem; +} +.rw-segment-main { + background-color: #f7fafc; + padding: 1rem; +} +.rw-link { + color: #4299e1; + text-decoration: underline; +} +.rw-link:hover { + color: #2b6cb0; +} +.rw-forgot-link { + font-size: 0.75rem; + color: #a0aec0; + text-align: right; + margin-top: 0.1rem; +} +.rw-forgot-link:hover { + font-size: 0.75rem; + color: #4299e1; +} +.rw-heading { + font-weight: 600; +} +.rw-heading.rw-heading-primary { + font-size: 1.25rem; +} +.rw-heading.rw-heading-secondary { + font-size: 0.875rem; +} +.rw-heading .rw-link { + color: #4a5568; + text-decoration: none; +} +.rw-heading .rw-link:hover { + color: #1a202c; + text-decoration: underline; +} +.rw-cell-error { + font-size: 90%; + font-weight: 600; +} +.rw-form-wrapper { + box-sizing: border-box; + font-size: 0.875rem; + margin-top: -1rem; +} +.rw-cell-error, +.rw-form-error-wrapper { + padding: 1rem; + background-color: #fff5f5; + color: #c53030; + border-width: 1px; + border-color: #feb2b2; + border-radius: 0.25rem; + margin: 1rem 0; +} +.rw-form-error-title { + margin-top: 0; + margin-bottom: 0; + font-weight: 600; +} +.rw-form-error-list { + margin-top: 0.5rem; + list-style-type: disc; + list-style-position: inside; +} +.rw-button { + border: none; + color: #718096; + cursor: pointer; + display: flex; + justify-content: center; + font-size: 0.75rem; + font-weight: 600; + padding: 0.25rem 1rem; + text-transform: uppercase; + text-decoration: none; + letter-spacing: 0.025em; + border-radius: 0.25rem; + line-height: 2; + border: 0; +} +.rw-button:hover { + background-color: #718096; + color: #fff; +} +.rw-button.rw-button-small { + font-size: 0.75rem; + border-radius: 0.125rem; + padding: 0.25rem 0.5rem; + line-height: inherit; +} +.rw-button.rw-button-green { + background-color: #48bb78; + color: #fff; +} +.rw-button.rw-button-green:hover { + background-color: #38a169; + color: #fff; +} +.rw-button.rw-button-blue { + background-color: #3182ce; + color: #fff; +} +.rw-button.rw-button-blue:hover { + background-color: #2b6cb0; +} +.rw-button.rw-button-red { + background-color: #e53e3e; + color: #fff; +} +.rw-button.rw-button-red:hover { + background-color: #c53030; +} +.rw-button-icon { + font-size: 1.25rem; + line-height: 1; + margin-right: 0.25rem; +} +.rw-button-group { + display: flex; + justify-content: center; + margin: 0.75rem 0.5rem; +} +.rw-button-group .rw-button { + margin: 0 0.25rem; +} +.rw-form-wrapper .rw-button-group { + margin-top: 2rem; + margin-bottom: 0; +} +.rw-label { + display: block; + margin-top: 1.5rem; + color: #4a5568; + font-weight: 600; + text-align: left; +} +.rw-label.rw-label-error { + color: #c53030; +} +.rw-input { + display: block; + margin-top: 0.5rem; + width: 100%; + padding: 0.5rem; + border-width: 1px; + border-style: solid; + border-color: #e2e8f0; + color: #4a5568; + border-radius: 0.25rem; + outline: none; +} +.rw-check-radio-item-none { + color: #4a5568 +} +.rw-check-radio-items { + display: flex; + justify-items: center; +} +.rw-input[type="checkbox"] { + display: inline; + width: 1rem; + margin-left: 0; + margin-right: 0.5rem; + margin-top: 0.25rem; +} +.rw-input[type="radio"] { + display: inline; + width: 1rem; + margin-left: 0; + margin-right: 0.5rem; + margin-top: 0.25rem; +} +.rw-input:focus { + border-color: #a0aec0; +} +.rw-input-error { + border-color: #c53030; + color: #c53030; +} + +.rw-input-error:focus{ + outline: none; + border-color: #c53030; + box-shadow: 0 0 5px #c53030; +} + +.rw-field-error { + display: block; + margin-top: 0.25rem; + font-weight: 600; + text-transform: uppercase; + font-size: 0.75rem; + color: #c53030; +} +.rw-table-wrapper-responsive { + overflow-x: auto; +} +.rw-table-wrapper-responsive .rw-table { + min-width: 48rem; +} +.rw-table { + + + table-layout: auto; + width: 100%; + font-size: 0.875rem; +} +.rw-table th, +.rw-table td { + padding: 0.75rem; +} +.rw-table td { + background-color: #ffffff; + color: #1a202c; +} +.rw-table tr:nth-child(odd) td, +.rw-table tr:nth-child(odd) th { + background-color: #f7fafc; +} +.rw-table thead tr { + color: #4a5568; +} +.rw-table th { + font-weight: 600; + text-align: left; +} +.rw-table thead th { + background-color: #e2e8f0; + text-align: left; +} +.rw-table tbody th { + text-align: right; +} +@media (min-width: 768px) { + .rw-table tbody th { + width: 20%; + } +} +.rw-table tbody tr { + border-top-width: 1px; +} +.rw-table input { + margin-left: 0; +} +.rw-table-actions { + display: flex; + justify-content: flex-end; + align-items: center; + height: 17px; + padding-right: 0.25rem; +} +.rw-table-actions .rw-button { + background-color: transparent; +} +.rw-table-actions .rw-button:hover { + background-color: #718096; + color: #fff; +} +.rw-table-actions .rw-button-blue { + color: #3182ce; +} +.rw-table-actions .rw-button-blue:hover { + background-color: #3182ce; + color: #fff; +} +.rw-table-actions .rw-button-red { + color: #e53e3e; +} +.rw-table-actions .rw-button-red:hover { + background-color: #e53e3e; + color: #fff; +} +.rw-text-center { + text-align: center; +} +.rw-login-container { + display: flex; + align-items: center; + justify-content: center; + width: 24rem; + margin: 4rem auto; + flex-wrap: wrap; +} +.rw-login-container .rw-form-wrapper { + width: 100%; + text-align: center; +} +.rw-login-link { + margin-top: 1rem; + color: #4a5568; + font-size: 90%; + text-align: center; + flex-basis: 100%; +} +.rw-webauthn-wrapper { + margin: 1.5rem 1rem 1rem; + line-height: 1.4; +} +.rw-webauthn-wrapper h2 { + font-size: 150%; + font-weight: bold; + margin-bottom: 1rem; +} diff --git a/packages/cli/dist/commands/generate/scaffold/templates/assets/scaffold.tailwind.css.template b/packages/cli/dist/commands/generate/scaffold/templates/assets/scaffold.tailwind.css.template new file mode 100644 index 0000000000..395138bf6f --- /dev/null +++ b/packages/cli/dist/commands/generate/scaffold/templates/assets/scaffold.tailwind.css.template @@ -0,0 +1,243 @@ +.rw-scaffold { + @apply bg-white text-gray-600; +} +.rw-scaffold h1, +.rw-scaffold h2 { + @apply m-0; +} +.rw-scaffold a { + @apply bg-transparent; +} +.rw-scaffold ul { + @apply m-0 p-0; +} +.rw-scaffold input:-ms-input-placeholder { + @apply text-gray-500; +} +.rw-scaffold input::-ms-input-placeholder { + @apply text-gray-500; +} +.rw-scaffold input::placeholder { + @apply text-gray-500; +} +.rw-header { + @apply flex justify-between py-4 px-8; +} +.rw-main { + @apply mx-4 pb-4; +} +.rw-segment { + @apply rounded-lg overflow-hidden w-full border border-gray-200; + scrollbar-color: theme('colors.zinc.400') transparent; +} +.rw-segment::-webkit-scrollbar { + height: initial; +} +.rw-segment::-webkit-scrollbar-track { + @apply border-gray-200 bg-transparent border-solid rounded-t-none rounded-b-[10px] border-0 border-t p-[2px]; +} +.rw-segment::-webkit-scrollbar-thumb { + @apply bg-zinc-400 bg-clip-content border-[3px] border-solid border-transparent rounded-full; +} +.rw-segment-header { + @apply bg-gray-200 text-gray-700 py-3 px-4; +} +.rw-segment-main { + @apply bg-gray-100 p-4; +} +.rw-link { + @apply text-blue-400 underline; +} +.rw-link:hover { + @apply text-blue-500; +} +.rw-forgot-link { + @apply text-xs text-gray-400 text-right mt-1 underline; +} +.rw-forgot-link:hover { + @apply text-blue-500; +} +.rw-heading { + @apply font-semibold; +} +.rw-heading.rw-heading-primary { + @apply text-xl; +} +.rw-heading.rw-heading-secondary { + @apply text-sm; +} +.rw-heading .rw-link { + @apply text-gray-600 no-underline; +} +.rw-heading .rw-link:hover { + @apply text-gray-900 underline; +} +.rw-cell-error { + @apply text-sm font-semibold; +} +.rw-form-wrapper { + @apply text-sm -mt-4; +} +.rw-cell-error, +.rw-form-error-wrapper { + @apply p-4 bg-red-50 text-red-600 border border-red-100 rounded my-4; +} +.rw-form-error-title { + @apply m-0 font-semibold; +} +.rw-form-error-list { + @apply mt-2 list-disc list-inside; +} +.rw-button { + @apply flex justify-center py-1 px-4 border-0 rounded bg-gray-200 text-gray-500 text-xs font-semibold uppercase tracking-wide leading-loose no-underline cursor-pointer transition duration-100; +} +.rw-button:hover { + @apply bg-gray-500 text-white; +} +.rw-button.rw-button-small { + @apply text-xs rounded-sm py-1 px-2; +} +.rw-button.rw-button-green { + @apply bg-green-500 text-white; +} +.rw-button.rw-button-green:hover { + @apply bg-green-700; +} +.rw-button.rw-button-blue { + @apply bg-blue-500 text-white; +} +.rw-button.rw-button-blue:hover { + @apply bg-blue-700; +} +.rw-button.rw-button-red { + @apply bg-red-500 text-white; +} +.rw-button.rw-button-red:hover { + @apply bg-red-700 text-white; +} +.rw-button-icon { + @apply text-xl leading-5 mr-1; +} +.rw-button-group { + @apply flex justify-center my-3 mx-2; +} +.rw-button-group .rw-button { + @apply mx-1; +} +.rw-form-wrapper .rw-button-group { + @apply mt-8; +} +.rw-label { + @apply block mt-6 text-gray-600 font-semibold text-left; +} +.rw-label.rw-label-error { + @apply text-red-600; +} +.rw-input { + @apply block mt-2 w-full p-2 bg-white border border-gray-200 rounded outline-none; +} +.rw-check-radio-items { + @apply flex justify-items-center; +} +.rw-check-radio-item-none { + @apply text-gray-600; +} +.rw-input[type='checkbox'], +.rw-input[type='radio'] { + @apply inline w-4 ml-0 mr-1 mt-1; +} +.rw-input:focus { + @apply border-gray-400; +} +.rw-input-error { + @apply border-red-600 text-red-600; +} +.rw-input-error:focus { + @apply border-red-600 outline-none; + box-shadow: 0 0 5px #c53030; +} +.rw-field-error { + @apply block mt-1 font-semibold text-xs text-red-600 uppercase; +} +.rw-table-wrapper-responsive { + @apply overflow-x-auto; +} +.rw-table-wrapper-responsive .rw-table { + min-width: 48rem; +} +.rw-table { + @apply w-full text-sm; +} +.rw-table th, +.rw-table td { + @apply p-3; +} +.rw-table td { + @apply bg-white text-gray-900; +} +.rw-table tr:nth-child(odd) td, +.rw-table tr:nth-child(odd) th { + @apply bg-gray-50; +} +.rw-table thead tr { + @apply bg-gray-200 text-gray-600; +} +.rw-table th { + @apply font-semibold text-left; +} +.rw-table thead th { + @apply text-left; +} +.rw-table tbody th { + @apply text-right; +} +@media (min-width: 768px) { + .rw-table tbody th { + @apply w-1/5; + } +} +.rw-table tbody tr { + @apply border-t border-gray-200; +} +.rw-table input { + @apply ml-0; +} +.rw-table-actions { + @apply flex justify-end items-center h-4 pr-1; +} +.rw-table-actions .rw-button { + @apply bg-transparent; +} +.rw-table-actions .rw-button:hover { + @apply bg-gray-500 text-white; +} +.rw-table-actions .rw-button-blue { + @apply text-blue-500; +} +.rw-table-actions .rw-button-blue:hover { + @apply bg-blue-500 text-white; +} +.rw-table-actions .rw-button-red { + @apply text-red-600; +} +.rw-table-actions .rw-button-red:hover { + @apply bg-red-600 text-white; +} +.rw-text-center { + @apply text-center; +} +.rw-login-container { + @apply flex items-center justify-center flex-wrap mx-auto w-96 my-16; +} +.rw-login-container .rw-form-wrapper { + @apply w-full text-center; +} +.rw-login-link { + @apply mt-4 text-gray-600 text-sm text-center w-full; +} +.rw-webauthn-wrapper { + @apply mt-6 mx-4 leading-6; +} +.rw-webauthn-wrapper h2 { + @apply mb-4 text-xl font-bold; +} diff --git a/packages/cli/dist/commands/generate/scaffold/templates/components/EditNameCell.tsx.template b/packages/cli/dist/commands/generate/scaffold/templates/components/EditNameCell.tsx.template new file mode 100644 index 0000000000..227b079a1a --- /dev/null +++ b/packages/cli/dist/commands/generate/scaffold/templates/components/EditNameCell.tsx.template @@ -0,0 +1,74 @@ +${useClientDirective}import type { + Edit${singularPascalName}By${pascalIdName}, + Update${singularPascalName}Input, + Update${singularPascalName}MutationVariables +} from 'types/graphql' + +import { navigate, routes } from '@redwoodjs/router' +import type { + CellSuccessProps, + CellFailureProps, + TypedDocumentNode, +} from '@redwoodjs/web' +import { useMutation } from '@redwoodjs/web' +import { toast } from '@redwoodjs/web/toast' + +import ${singularPascalName}Form from '${importComponentNameForm}' + +export const QUERY: TypedDocumentNode = gql` + query Edit${singularPascalName}By${pascalIdName}($${idName}: ${idType}!) { + ${singularCamelName}: ${singularCamelName}(${idName}: $${idName}) {<% columns.forEach(column => { %> + <%= column.name %><% }) %> + } + } +` + +const UPDATE_${singularConstantName}_MUTATION: TypedDocumentNode< + Edit${singularPascalName}ById, + Update${singularPascalName}MutationVariables +> = gql` + mutation Update${singularPascalName}Mutation($${idName}: ${idType}!, $input: Update${singularPascalName}Input!) { + update${singularPascalName}(${idName}: $${idName}, input: $input) {<% columns.forEach(column => { %> + <%= column.name %><% }) %> + } + } +` + +export const Loading = () =>
Loading...
+ +export const Failure = ({ error }: CellFailureProps) => ( +
{error?.message}
+) + +export const Success = ({ ${singularCamelName} }: CellSuccessProps) => { + const [update${singularPascalName}, { loading, error }] = useMutation( + UPDATE_${singularConstantName}_MUTATION, + { + onCompleted: () => { + toast.success('${singularPascalName} updated') + navigate(routes.${pluralRouteName}()) + }, + onError: (error) => { + toast.error(error.message) + }, + } + ) + + const onSave = ( + input: Update${singularPascalName}Input, + id: Edit${singularPascalName}By${pascalIdName}['${singularCamelName}']['id'] + ) => { + update${singularPascalName}({ variables: { id, input } }) + } + + return ( +
+
+

Edit ${singularPascalName} {${singularCamelName}?.id}

+
+
+ <${singularPascalName}Form ${singularCamelName}={${singularCamelName}} onSave={onSave} error={error} loading={loading} /> +
+
+ ) +} diff --git a/packages/cli/dist/commands/generate/scaffold/templates/components/Name.tsx.template b/packages/cli/dist/commands/generate/scaffold/templates/components/Name.tsx.template new file mode 100644 index 0000000000..f95e5e0465 --- /dev/null +++ b/packages/cli/dist/commands/generate/scaffold/templates/components/Name.tsx.template @@ -0,0 +1,79 @@ +import type { Delete${singularPascalName}Mutation, Delete${singularPascalName}MutationVariables, Find${singularPascalName}By${pascalIdName} } from 'types/graphql' + +import { Link, routes, navigate } from '@redwoodjs/router' +import { useMutation } from '@redwoodjs/web' +import type { TypedDocumentNode } from '@redwoodjs/web' +import { toast } from '@redwoodjs/web/toast' + +import { ${formattersImports} } from 'src/lib/formatters' + +const DELETE_${singularConstantName}_MUTATION: TypedDocumentNode = gql` + mutation Delete${singularPascalName}Mutation($${idName}: ${idType}!) { + delete${singularPascalName}(${idName}: $${idName}) { + ${idName} + } + } +` + +interface Props { + ${singularCamelName}: NonNullable +} + +const ${singularPascalName} = ({ ${singularCamelName} }: Props) => { + const [delete${singularPascalName}] = useMutation(DELETE_${singularConstantName}_MUTATION, { + onCompleted: () => { + toast.success('${singularPascalName} deleted') + navigate(routes.${pluralRouteName}()) + }, + onError: (error) => { + toast.error(error.message) + }, + }) + + const onDeleteClick = (${idName}: Delete${singularPascalName}MutationVariables['${idName}']) => { + if (confirm('Are you sure you want to delete ${singularCamelName} ' + ${idName} + '?')) { + delete${singularPascalName}({ variables: { ${idName} } }) + } + } + + return ( + <> +
+
+

+ ${singularPascalName} {${singularCamelName}.${idName}} Detail +

+
+ + + <% columns.forEach(column => { %> + <% + if (column.displayFunction) { %> + <% + } else { %> + <% + } %> + <% }) %> + +
<%= column.label %>{${column.displayFunction}(${singularCamelName}.${column.name})}{${singularCamelName}.${column.name}}
+
+ + + ) +} + +export default ${singularPascalName} diff --git a/packages/cli/dist/commands/generate/scaffold/templates/components/NameCell.tsx.template b/packages/cli/dist/commands/generate/scaffold/templates/components/NameCell.tsx.template new file mode 100644 index 0000000000..d804292f4f --- /dev/null +++ b/packages/cli/dist/commands/generate/scaffold/templates/components/NameCell.tsx.template @@ -0,0 +1,32 @@ +${useClientDirective}import type { Find${singularPascalName}By${pascalIdName}, Find${singularPascalName}By${pascalIdName}Variables } from 'types/graphql' + +import type { + CellSuccessProps, + CellFailureProps, + TypedDocumentNode, +} from '@redwoodjs/web' + +import ${singularPascalName} from '${importComponentName}' + +export const QUERY: TypedDocumentNode< + Find${singularPascalName}By${pascalIdName}, + Find${singularPascalName}By${pascalIdName}Variables +> = gql` + query Find${singularPascalName}By${pascalIdName}($${idName}: ${idType}!) { + ${singularCamelName}: ${singularCamelName}(${idName}: $${idName}) {<% columns.forEach(column => { %> + <%= column.name %><% }) %> + } + } +` + +export const Loading = () =>
Loading...
+ +export const Empty = () =>
${singularPascalName} not found
+ +export const Failure = ({ error }: CellFailureProps) => ( +
{error?.message}
+) + +export const Success = ({ ${singularCamelName} }: CellSuccessProps) => { + return <${singularPascalName} ${singularCamelName}={${singularCamelName}} /> +} diff --git a/packages/cli/dist/commands/generate/scaffold/templates/components/NameForm.tsx.template b/packages/cli/dist/commands/generate/scaffold/templates/components/NameForm.tsx.template new file mode 100644 index 0000000000..1acfcedc75 --- /dev/null +++ b/packages/cli/dist/commands/generate/scaffold/templates/components/NameForm.tsx.template @@ -0,0 +1,123 @@ +import type { Edit${singularPascalName}By${pascalIdName}, Update${singularPascalName}Input } from 'types/graphql' + +import type { RWGqlError } from '@redwoodjs/forms' +import { + Form, + FormError, + FieldError, + Label, + ${fieldsToImport.join(',\n ')}, + Submit, +} from '@redwoodjs/forms' + +<% if (fieldsToImport.includes('DatetimeLocalField')) { %> +const formatDatetime = (value) => { + if (value) { + return value.replace(/:\d{2}\.\d{3}\w/, '') + } +} +<% } %> + +type Form${singularPascalName} = NonNullable + +interface ${singularPascalName}FormProps { + ${singularCamelName}?: Edit${singularPascalName}By${pascalIdName}['${singularCamelName}'] + onSave: (data: Update${singularPascalName}Input, ${idName}?: Form${singularPascalName}['${idName}']) => void + error: RWGqlError + loading: boolean +} + +const ${singularPascalName}Form = (props: ${singularPascalName}FormProps) => { + const onSubmit = (data: Form${singularPascalName}) => { + <% editableColumns.forEach(column => { %> + <% if (column.isEnum && !column.isList && !column.isRequired) { %> + if (data.${column.name} === '') { + data.${column.name} = null + } + <% } %> + <% if (column.isEnum && column.isList) { %> + if (data.${column.name}) { + data.${column.name} = data.${column.name}.filter((value) => !!value) + } + <% } %> + <% }) %> + props.onSave(data, props?.${singularCamelName}?.${idName}) + } + + return ( +
+ onSubmit={onSubmit} error={props.error}> + + <% editableColumns.forEach(column => { %> + + <% if (column.isEnum) { %> + <% if (!column.isRequired) { %> +
+ <${column.component} + ${idName}="${singularCamelName}-${column.name}-none" + name="${column.name}" + defaultValue="" + ${column.defaultProp}={!props.spot?.spotType} + className="rw-input" + errorClassName="rw-input rw-input-error" + /> +
+ None +
+
+ <% } %> + <% column.values?.forEach((value, i) => { + const columnComponentName = column.isList ? `${column.name}[${i}]` : column.name + %> +
+ <${column.component} + ${idName}="${singularCamelName}-${column.name}-${i}" + name="${columnComponentName}" + defaultValue="${value.name}" + ${column.defaultProp}={props.${singularCamelName}?.${column.name}?.includes('${value.name}')} + className="rw-input" + errorClassName="rw-input rw-input-error" + /> +
+ ${value.name.replace('_', ' ').toLowerCase().replace(/\b\w/g, l => l.toUpperCase())} +
+
+ <% }) %> + <% } else { %> + <${column.component} + name="${column.name}" + ${column.defaultProp}={${column.deserializeFunction && column.deserializeFunction + '('}props.${singularCamelName}?.<%= column.name %>${column.deserializeFunction && ')'}} + className="rw-input" + errorClassName="rw-input rw-input-error" + <%= !column.validation ? '' : `validation=${column.validation}` %> + <%= column.isRelationalField && !column.isRequired ? `emptyAs={'undefined'}` : '' %> + /> + <% } %> + + +<% }) %> +
+ + Save + +
+ +
+ ) +} + +export default ${singularPascalName}Form diff --git a/packages/cli/dist/commands/generate/scaffold/templates/components/Names.tsx.template b/packages/cli/dist/commands/generate/scaffold/templates/components/Names.tsx.template new file mode 100644 index 0000000000..eb23f726cd --- /dev/null +++ b/packages/cli/dist/commands/generate/scaffold/templates/components/Names.tsx.template @@ -0,0 +1,88 @@ +import type { Delete${singularPascalName}Mutation, Delete${singularPascalName}MutationVariables, Find${pluralPascalName} } from 'types/graphql' + +import { Link, routes } from '@redwoodjs/router' +import { useMutation } from '@redwoodjs/web' +import type { TypedDocumentNode } from '@redwoodjs/web' +import { toast } from '@redwoodjs/web/toast' + +import { QUERY } from '${importComponentNamesCell}' +import { ${listFormattersImports} } from 'src/lib/formatters' + + +const DELETE_${singularConstantName}_MUTATION: TypedDocumentNode = gql` + mutation Delete${singularPascalName}Mutation($${idName}: ${idType}!) { + delete${singularPascalName}(${idName}: $${idName}) { + ${idName} + } + } +` + +const ${pluralPascalName}List = ({ ${pluralCamelName} }: Find${pluralPascalName}) => { + const [delete${singularPascalName}] = useMutation(DELETE_${singularConstantName}_MUTATION, { + onCompleted: () => { + toast.success('${singularPascalName} deleted') + }, + onError: (error) => { + toast.error(error.message) + }, + // This refetches the query on the list page. Read more about other ways to + // update the cache over here: + // https://www.apollographql.com/docs/react/data/mutations/#making-all-other-cache-updates + refetchQueries: [{ query: QUERY }], + awaitRefetchQueries: true, + }) + + const onDeleteClick = (${idName}: Delete${singularPascalName}MutationVariables['${idName}']) => { + if (confirm('Are you sure you want to delete ${singularCamelName} ' + ${idName} + '?')) { + delete${singularPascalName}({ variables: { ${idName} } }) + } + } + + return ( +
+ + + <% columns.forEach(column => { %> + <% }) %> + + + + + {${pluralCamelName}.map((${singularCamelName}) => ( + <% columns.forEach(column => { %> + <% }) %> + + + ))} + +
${column.label} 
{${column.listDisplayFunction}(${singularCamelName}.${column.name})} + +
+
+ ) +} + +export default ${pluralPascalName}List diff --git a/packages/cli/dist/commands/generate/scaffold/templates/components/NamesCell.tsx.template b/packages/cli/dist/commands/generate/scaffold/templates/components/NamesCell.tsx.template new file mode 100644 index 0000000000..b71f4dcef5 --- /dev/null +++ b/packages/cli/dist/commands/generate/scaffold/templates/components/NamesCell.tsx.template @@ -0,0 +1,45 @@ +${useClientDirective}import type { Find${pluralPascalName}, Find${pluralPascalName}Variables } from 'types/graphql' + +import { Link, routes } from '@redwoodjs/router' +import type { + CellSuccessProps, + CellFailureProps, + TypedDocumentNode, +} from '@redwoodjs/web' + +import ${pluralPascalName} from '${importComponentNames}' + +export const QUERY: TypedDocumentNode< + Find${pluralPascalName}, + Find${pluralPascalName}Variables +> = gql` + query Find${pluralPascalName} { + ${pluralCamelName} {<% columns.forEach(column => { %> + <%= column.name %><% }) %> + } + } +` + +export const Loading = () =>
Loading...
+ +export const Empty = () => { + return ( +
+ {'No ${pluralCamelName} yet. '} + + {'Create one?'} + +
+ ) +} + +export const Failure = ({ error }: CellFailureProps) => ( +
{error?.message}
+) + +export const Success = ({ ${pluralCamelName} }: CellSuccessProps) => { + return <${pluralPascalName} ${pluralCamelName}={${pluralCamelName}} /> +} diff --git a/packages/cli/dist/commands/generate/scaffold/templates/components/NewName.tsx.template b/packages/cli/dist/commands/generate/scaffold/templates/components/NewName.tsx.template new file mode 100644 index 0000000000..36fe7b5532 --- /dev/null +++ b/packages/cli/dist/commands/generate/scaffold/templates/components/NewName.tsx.template @@ -0,0 +1,55 @@ +${useClientDirective}import type { + Create${singularPascalName}Mutation, + Create${singularPascalName}Input, + Create${singularPascalName}MutationVariables +} from 'types/graphql' + +import { navigate, routes } from '@redwoodjs/router' +import { useMutation } from '@redwoodjs/web' +import type { TypedDocumentNode } from '@redwoodjs/web' +import { toast } from '@redwoodjs/web/toast' + +import ${singularPascalName}Form from '${importComponentNameForm}' + +const CREATE_${singularConstantName}_MUTATION: TypedDocumentNode< + Create${singularPascalName}Mutation, + Create${singularPascalName}MutationVariables +> = gql` + mutation Create${singularPascalName}Mutation($input: Create${singularPascalName}Input!) { + create${singularPascalName}(input: $input) { + ${idName} + } + } +` + +const New${singularPascalName} = () => { + const [create${singularPascalName}, { loading, error }] = useMutation( + CREATE_${singularConstantName}_MUTATION, + { + onCompleted: () => { + toast.success('${singularPascalName} created') + navigate(routes.${pluralRouteName}()) + }, + onError: (error) => { + toast.error(error.message) + }, + } + ) + + const onSave = (input: Create${singularPascalName}Input) => { + create${singularPascalName}({ variables: { input } }) + } + + return ( +
+
+

New ${singularPascalName}

+
+
+ <${singularPascalName}Form onSave={onSave} loading={loading} error={error} /> +
+
+ ) +} + +export default New${singularPascalName} diff --git a/packages/cli/dist/commands/generate/scaffold/templates/layouts/ScaffoldLayout.tsx.template b/packages/cli/dist/commands/generate/scaffold/templates/layouts/ScaffoldLayout.tsx.template new file mode 100644 index 0000000000..2912b56706 --- /dev/null +++ b/packages/cli/dist/commands/generate/scaffold/templates/layouts/ScaffoldLayout.tsx.template @@ -0,0 +1,37 @@ +import { Link, routes } from '@redwoodjs/router' +import { Toaster } from '@redwoodjs/web/toast' + +type LayoutProps = { + title: string + titleTo: string + buttonLabel: string + buttonTo: string + children: React.ReactNode +} + +const ScaffoldLayout = ({ + title, + titleTo, + buttonLabel, + buttonTo, + children, +}: LayoutProps) => { + return ( +
+ +
+

+ + {title} + +

+ +
+
{buttonLabel} + +
+
{children}
+
+ ) +} + +export default ScaffoldLayout diff --git a/packages/cli/dist/commands/generate/scaffold/templates/lib/formatters.test.tsx.template b/packages/cli/dist/commands/generate/scaffold/templates/lib/formatters.test.tsx.template new file mode 100644 index 0000000000..56593386e4 --- /dev/null +++ b/packages/cli/dist/commands/generate/scaffold/templates/lib/formatters.test.tsx.template @@ -0,0 +1,192 @@ +import { render, waitFor, screen } from '@redwoodjs/testing/web' + +import { + formatEnum, + jsonTruncate, + truncate, + timeTag, + jsonDisplay, + checkboxInputTag, +} from './formatters' + +describe('formatEnum', () => { + it('handles nullish values', () => { + expect(formatEnum(null)).toEqual('') + expect(formatEnum('')).toEqual('') + expect(formatEnum(undefined)).toEqual('') + }) + + it('formats a list of values', () => { + expect( + formatEnum(['RED', 'ORANGE', 'YELLOW', 'GREEN', 'BLUE', 'VIOLET']) + ).toEqual('Red, Orange, Yellow, Green, Blue, Violet') + }) + + it('formats a single value', () => { + expect(formatEnum('DARK_BLUE')).toEqual('Dark blue') + }) + + it('returns an empty string for values of the wrong type (for JS projects)', () => { + // @ts-expect-error - Testing JS scenario + expect(formatEnum(5)).toEqual('') + }) +}) + +describe('truncate', () => { + it('truncates really long strings', () => { + expect(truncate('na '.repeat(1000) + 'batman').length).toBeLessThan(1000) + expect(truncate('na '.repeat(1000) + 'batman')).not.toMatch(/batman/) + }) + + it('does not modify short strings', () => { + expect(truncate('Short strinG')).toEqual('Short strinG') + }) + + it('adds ... to the end of truncated strings', () => { + expect(truncate('repeat'.repeat(1000))).toMatch(/\w\.\.\.$/) + }) + + it('accepts numbers', () => { + expect(truncate(123)).toEqual('123') + expect(truncate(0)).toEqual('0') + expect(truncate(0o000)).toEqual('0') + }) + + it('handles arguments of invalid type', () => { + // @ts-expect-error - Testing JS scenario + expect(truncate(false)).toEqual('false') + + expect(truncate(undefined)).toEqual('') + expect(truncate(null)).toEqual('') + }) +}) + +describe('jsonTruncate', () => { + it('truncates large json structures', () => { + expect( + jsonTruncate({ + foo: 'foo', + bar: 'bar', + baz: 'baz', + kittens: 'kittens meow', + bazinga: 'Sheldon', + nested: { + foobar: 'I have no imagination', + two: 'Second nested item', + }, + five: 5, + bool: false, + }) + ).toMatch(/.+\n.+\w\.\.\.$/s) + }) +}) + +describe('timeTag', () => { + it('renders a date', async () => { + render(
{timeTag(new Date('1970-08-20').toUTCString())}
) + + await waitFor(() => screen.getByText(/1970.*00:00:00/)) + }) + + it('can take an empty input string', async () => { + expect(timeTag('')).toEqual('') + }) +}) + +describe('jsonDisplay', () => { + it('produces the correct output', () => { + expect( + jsonDisplay({ + title: 'TOML Example (but in JSON)', + database: { + data: [['delta', 'phi'], [3.14]], + enabled: true, + ports: [8000, 8001, 8002], + temp_targets: { + case: 72.0, + cpu: 79.5, + }, + }, + owner: { + dob: '1979-05-27T07:32:00-08:00', + name: 'Tom Preston-Werner', + }, + servers: { + alpha: { + ip: '10.0.0.1', + role: 'frontend', + }, + beta: { + ip: '10.0.0.2', + role: 'backend', + }, + }, + }) + ).toMatchInlineSnapshot(` +
+        
+          {
+        "title": "TOML Example (but in JSON)",
+        "database": {
+          "data": [
+            [
+              "delta",
+              "phi"
+            ],
+            [
+              3.14
+            ]
+          ],
+          "enabled": true,
+          "ports": [
+            8000,
+            8001,
+            8002
+          ],
+          "temp_targets": {
+            "case": 72,
+            "cpu": 79.5
+          }
+        },
+        "owner": {
+          "dob": "1979-05-27T07:32:00-08:00",
+          "name": "Tom Preston-Werner"
+        },
+        "servers": {
+          "alpha": {
+            "ip": "10.0.0.1",
+            "role": "frontend"
+          },
+          "beta": {
+            "ip": "10.0.0.2",
+            "role": "backend"
+          }
+        }
+      }
+        
+      
+ `) + }) +}) + +describe('checkboxInputTag', () => { + it('can be checked', () => { + render(checkboxInputTag(true)) + expect(screen.getByRole('checkbox')).toBeChecked() + }) + + it('can be unchecked', () => { + render(checkboxInputTag(false)) + expect(screen.getByRole('checkbox')).not.toBeChecked() + }) + + it('is disabled when checked', () => { + render(checkboxInputTag(true)) + expect(screen.getByRole('checkbox')).toBeDisabled() + }) + + it('is disabled when unchecked', () => { + render(checkboxInputTag(false)) + expect(screen.getByRole('checkbox')).toBeDisabled() + }) +}) diff --git a/packages/cli/dist/commands/generate/scaffold/templates/lib/formatters.tsx.template b/packages/cli/dist/commands/generate/scaffold/templates/lib/formatters.tsx.template new file mode 100644 index 0000000000..8ab9e806e3 --- /dev/null +++ b/packages/cli/dist/commands/generate/scaffold/templates/lib/formatters.tsx.template @@ -0,0 +1,58 @@ +import React from 'react' + +import humanize from 'humanize-string' + +const MAX_STRING_LENGTH = 150 + +export const formatEnum = (values: string | string[] | null | undefined) => { + let output = '' + + if (Array.isArray(values)) { + const humanizedValues = values.map((value) => humanize(value)) + output = humanizedValues.join(', ') + } else if (typeof values === 'string') { + output = humanize(values) + } + + return output +} + +export const jsonDisplay = (obj: unknown) => { + return ( +
+      {JSON.stringify(obj, null, 2)}
+    
+ ) +} + +export const truncate = (value: string | number) => { + let output = value?.toString() ?? '' + + if (output.length > MAX_STRING_LENGTH) { + output = output.substring(0, MAX_STRING_LENGTH) + '...' + } + + return output +} + +export const jsonTruncate = (obj: unknown) => { + return truncate(JSON.stringify(obj, null, 2)) +} + +export const timeTag = (dateTime?: string) => { + let output: string | JSX.Element = '' + + if (dateTime) { + output = ( + + ) + } + + return output +} + +export const checkboxInputTag = (checked: boolean) => { + return +} diff --git a/packages/cli/dist/commands/generate/scaffold/templates/pages/EditNamePage.tsx.template b/packages/cli/dist/commands/generate/scaffold/templates/pages/EditNamePage.tsx.template new file mode 100644 index 0000000000..305b2a0dee --- /dev/null +++ b/packages/cli/dist/commands/generate/scaffold/templates/pages/EditNamePage.tsx.template @@ -0,0 +1,11 @@ +import Edit${singularPascalName}Cell from '${importComponentEditNameCell}' + +type ${pascalName}PageProps = { + ${idName}: ${idTsType} +} + +const Edit${singularPascalName}Page = ({ ${idName} }: ${pascalName}PageProps) => { + return +} + +export default Edit${singularPascalName}Page diff --git a/packages/cli/dist/commands/generate/scaffold/templates/pages/NamePage.tsx.template b/packages/cli/dist/commands/generate/scaffold/templates/pages/NamePage.tsx.template new file mode 100644 index 0000000000..c1a94d5f47 --- /dev/null +++ b/packages/cli/dist/commands/generate/scaffold/templates/pages/NamePage.tsx.template @@ -0,0 +1,11 @@ +import ${singularPascalName}Cell from '${importComponentNameCell}' + +type ${pascalName}PageProps = { + ${idName}: ${idTsType} +} + +const ${singularPascalName}Page = ({ ${idName} }: ${pascalName}PageProps) => { + return <${singularPascalName}Cell ${idName}={${idName}} /> +} + +export default ${singularPascalName}Page diff --git a/packages/cli/dist/commands/generate/scaffold/templates/pages/NamesPage.tsx.template b/packages/cli/dist/commands/generate/scaffold/templates/pages/NamesPage.tsx.template new file mode 100644 index 0000000000..f4c5a5e541 --- /dev/null +++ b/packages/cli/dist/commands/generate/scaffold/templates/pages/NamesPage.tsx.template @@ -0,0 +1,7 @@ +import ${pluralPascalName}Cell from '${importComponentNamesCell}' + +const ${pluralPascalName}Page = () => { + return <${pluralPascalName}Cell /> +} + +export default ${pluralPascalName}Page diff --git a/packages/cli/dist/commands/generate/scaffold/templates/pages/NewNamePage.tsx.template b/packages/cli/dist/commands/generate/scaffold/templates/pages/NewNamePage.tsx.template new file mode 100644 index 0000000000..afe1c6d178 --- /dev/null +++ b/packages/cli/dist/commands/generate/scaffold/templates/pages/NewNamePage.tsx.template @@ -0,0 +1,7 @@ +import New${singularPascalName} from '${importComponentNewName}' + +const New${singularPascalName}Page = () => { + return +} + +export default New${singularPascalName}Page diff --git a/packages/cli/dist/commands/generate/script/script.js b/packages/cli/dist/commands/generate/script/script.js new file mode 100644 index 0000000000..3aa44049c1 --- /dev/null +++ b/packages/cli/dist/commands/generate/script/script.js @@ -0,0 +1,138 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var script_exports = {}; +__export(script_exports, { + builder: () => builder, + command: () => command, + description: () => description, + files: () => files, + handler: () => handler +}); +module.exports = __toCommonJS(script_exports); +var import_path = __toESM(require("path")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_listr2 = require("listr2"); +var import_terminal_link = __toESM(require("terminal-link")); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_telemetry = require("@redwoodjs/telemetry"); +var import_lib = require("../../../lib"); +var import_colors = __toESM(require("../../../lib/colors")); +var import_rollback = require("../../../lib/rollback"); +var import_helpers = require("../helpers"); +const TEMPLATE_PATH = import_path.default.resolve(__dirname, "templates", "script.js.template"); +const TSCONFIG_TEMPLATE = import_path.default.resolve( + __dirname, + "templates", + "tsconfig.json.template" +); +const files = ({ name, typescript = false }) => { + const outputFilename = `${name}.${typescript ? "ts" : "js"}`; + const outputPath = import_path.default.join((0, import_lib.getPaths)().scripts, outputFilename); + const scriptTsConfigPath = import_path.default.join((0, import_lib.getPaths)().scripts, "tsconfig.json"); + return { + [outputPath]: import_fs_extra.default.readFileSync(TEMPLATE_PATH, "utf-8"), + // Add tsconfig for type and cmd+click support if project is TS + ...typescript && !import_fs_extra.default.existsSync(scriptTsConfigPath) && { + [scriptTsConfigPath]: import_fs_extra.default.readFileSync(TSCONFIG_TEMPLATE, "utf-8") + } + }; +}; +const command = "script "; +const description = "Generate a command line script"; +const builder = (yargs) => { + yargs.positional("name", { + description: "A descriptor of what this script does", + type: "string" + }).option("rollback", { + description: "Revert all generator actions if an error occurs", + type: "boolean", + default: true + }).epilogue( + `Also see the ${(0, import_terminal_link.default)( + "Redwood CLI Reference", + "https://redwoodjs.com/docs/cli-commands#generate-script" + )}` + ); + Object.entries(import_helpers.yargsDefaults).forEach(([option, config]) => { + yargs.option(option, config); + }); +}; +const handler = async ({ force, ...args }) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "generate script", + force, + rollback: args.rollback + }); + const POST_RUN_INSTRUCTIONS = `Next steps... + + ${import_colors.default.warning( + "After modifying your script, you can invoke it like:" + )} + + yarn rw exec ${args.name} + + yarn rw exec ${args.name} --param1 true +`; + (0, import_helpers.validateName)(args.name); + const tasks = new import_listr2.Listr( + [ + { + title: "Generating script file...", + task: () => { + return (0, import_lib.writeFilesTask)(files(args), { overwriteExisting: force }); + } + }, + { + title: "Next steps...", + task: (_ctx, task) => { + task.title = POST_RUN_INSTRUCTIONS; + } + } + ].filter(Boolean), + { rendererOptions: { collapseSubtasks: false } } + ); + try { + if (args.rollback && !force) { + (0, import_rollback.prepareForRollback)(tasks); + } + await tasks.run(); + } catch (e) { + (0, import_telemetry.errorTelemetry)(process.argv, e.message); + console.log(import_colors.default.error(e.message)); + process.exit(1); + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + files, + handler +}); diff --git a/packages/cli/dist/commands/generate/script/templates/script.js.template b/packages/cli/dist/commands/generate/script/templates/script.js.template new file mode 100644 index 0000000000..36bcc950d2 --- /dev/null +++ b/packages/cli/dist/commands/generate/script/templates/script.js.template @@ -0,0 +1,10 @@ +// To access your database +// Append api/* to import from api and web/* to import from web +import { db } from 'api/src/lib/db' + + +export default async ({ args }) => { + // Your script here... + console.log(':: Executing script with args ::') + console.log(args) +} diff --git a/packages/cli/dist/commands/generate/script/templates/tsconfig.json.template b/packages/cli/dist/commands/generate/script/templates/tsconfig.json.template new file mode 100644 index 0000000000..33b037d45d --- /dev/null +++ b/packages/cli/dist/commands/generate/script/templates/tsconfig.json.template @@ -0,0 +1,33 @@ +{ + "compilerOptions": { + "noEmit": true, + "allowJs": true, + "esModuleInterop": true, + "baseUrl": "./", + "paths": { + "$api/*": [ + "../api/*", + ], + "api/*": [ + "../api/*", + ], + "$web/*": [ + "../web/*", + ], + "web/*": [ + "../web/*", + ], + "$web/src/*": [ + "../web/src/*", + "../.redwood/types/mirror/web/src/*" + ], + "web/src/*": [ + "../web/src/*", + "../.redwood/types/mirror/web/src/*" + ], + "types/*": ["../types/*", "../web/types/*", "../api/types/*"], + }, + "typeRoots": ["../node_modules/@types"], + "jsx": "preserve", + }, +} diff --git a/packages/cli/dist/commands/generate/sdl/sdl.js b/packages/cli/dist/commands/generate/sdl/sdl.js new file mode 100644 index 0000000000..e82c78a69a --- /dev/null +++ b/packages/cli/dist/commands/generate/sdl/sdl.js @@ -0,0 +1,331 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var sdl_exports = {}; +__export(sdl_exports, { + builder: () => builder, + command: () => command, + defaults: () => defaults, + description: () => description, + files: () => files, + handler: () => handler +}); +module.exports = __toCommonJS(sdl_exports); +var import_path = __toESM(require("path")); +var import_boxen = __toESM(require("boxen")); +var import_camelcase = __toESM(require("camelcase")); +var import_chalk = __toESM(require("chalk")); +var import_listr2 = require("listr2"); +var import_terminal_link = __toESM(require("terminal-link")); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_generate = require("@redwoodjs/internal/dist/generate/generate"); +var import_project_config = require("@redwoodjs/project-config"); +var import_telemetry = require("@redwoodjs/telemetry"); +var import_lib = require("../../../lib"); +var import_colors = __toESM(require("../../../lib/colors")); +var import_rollback = require("../../../lib/rollback"); +var import_rwPluralize = require("../../../lib/rwPluralize"); +var import_schemaHelpers = require("../../../lib/schemaHelpers"); +var import_helpers = require("../helpers"); +var import_helpers2 = require("../helpers"); +var import_service = require("../service/service"); +const DEFAULT_IGNORE_FIELDS_FOR_INPUT = ["createdAt", "updatedAt"]; +const missingIdConsoleMessage = () => { + const line1 = import_chalk.default.bold.yellow("WARNING") + ": Cannot generate CRUD SDL without an `@id` database column."; + const line2 = "If you are trying to generate for a many-to-many join table "; + const line3 = "you'll need to update your schema definition to include"; + const line4 = "an `@id` column. Read more here: "; + const line5 = import_chalk.default.underline.blue( + "https://redwoodjs.com/docs/schema-relations" + ); + console.error( + (0, import_boxen.default)(line1 + "\n\n" + line2 + "\n" + line3 + "\n" + line4 + "\n" + line5, { + padding: 1, + margin: { top: 1, bottom: 3, right: 1, left: 2 }, + borderStyle: "single" + }) + ); +}; +const addFieldGraphQLComment = (field, str) => { + const description2 = field.documentation || `Description for ${field.name}.`; + return ` + "${description2}" + ${str}`; +}; +const modelFieldToSDL = ({ + field, + required = true, + types = {}, + docs = false +}) => { + if (Object.entries(types).length) { + field.type = field.kind === "object" ? idType(types[field.type]) : field.type; + } + const prismaTypeToGraphqlType = { + Json: "JSON", + Decimal: "Float", + Bytes: "Byte" + }; + const fieldContent = `${field.name}: ${field.isList ? "[" : ""}${prismaTypeToGraphqlType[field.type] || field.type}${field.isList ? "]" : ""}${(field.isRequired && required) | field.isList ? "!" : ""}`; + if (docs) { + return addFieldGraphQLComment(field, fieldContent); + } else { + return fieldContent; + } +}; +const querySDL = (model, docs = false) => { + return model.fields.map((field) => modelFieldToSDL({ field, docs })); +}; +const inputSDL = (model, required, types = {}, docs = false) => { + const ignoredFields = DEFAULT_IGNORE_FIELDS_FOR_INPUT; + return model.fields.filter((field) => { + const idField = model.fields.find((field2) => field2.isId); + if (idField) { + ignoredFields.push(idField.name); + } + return ignoredFields.indexOf(field.name) === -1 && field.kind !== "object"; + }).map((field) => modelFieldToSDL({ field, required, types, docs })); +}; +const createInputSDL = (model, types = {}, docs = false) => { + return inputSDL(model, true, types, docs); +}; +const updateInputSDL = (model, types = {}, docs = false) => { + return inputSDL(model, false, types, docs); +}; +const idType = (model, crud) => { + if (!crud) { + return void 0; + } + const idField = model.fields.find((field) => field.isId); + if (!idField) { + missingIdConsoleMessage(); + throw new Error("Failed: Could not generate SDL"); + } + return idField.type; +}; +const idName = (model, crud) => { + if (!crud) { + return void 0; + } + const idField = model.fields.find((field) => field.isId); + if (!idField) { + missingIdConsoleMessage(); + throw new Error("Failed: Could not generate SDL"); + } + return idField.name; +}; +const sdlFromSchemaModel = async (name, crud, docs = false) => { + const model = await (0, import_schemaHelpers.getSchema)(name); + const types = (await Promise.all( + model.fields.filter((field) => field.kind === "object").map(async (field) => { + const model2 = await (0, import_schemaHelpers.getSchema)(field.type); + return model2; + }) + )).reduce((acc, cur) => ({ ...acc, [cur.name]: cur }), {}); + const enums = (await Promise.all( + model.fields.filter((field) => field.kind === "enum").map(async (field) => { + const enumDef = await (0, import_schemaHelpers.getEnum)(field.type); + return enumDef; + }) + )).reduce((acc, curr) => acc.concat(curr), []); + const modelName = model.name; + const modelDescription = model.documentation || `Representation of ${modelName}.`; + return { + modelName, + modelDescription, + query: querySDL(model, docs).join("\n "), + createInput: createInputSDL(model, types, docs).join("\n "), + updateInput: updateInputSDL(model, types, docs).join("\n "), + idType: idType(model, crud), + idName: idName(model, crud), + relations: (0, import_helpers2.relationsForModel)(model), + enums + }; +}; +const files = async ({ + name, + crud = true, + docs = false, + tests, + typescript +}) => { + const { + modelName, + modelDescription, + query, + createInput, + updateInput, + idType: idType2, + idName: idName2, + relations, + enums + } = await sdlFromSchemaModel(name, crud, docs); + const templatePath = (0, import_helpers2.customOrDefaultTemplatePath)({ + side: "api", + generator: "sdl", + templatePath: "sdl.ts.template" + }); + let template = await (0, import_lib.generateTemplate)(templatePath, { + docs, + modelName, + modelDescription, + name, + crud, + query, + createInput, + updateInput, + idType: idType2, + idName: idName2, + enums + }); + const extension = typescript ? "ts" : "js"; + let outputPath = import_path.default.join( + (0, import_lib.getPaths)().api.graphql, + `${(0, import_camelcase.default)((0, import_rwPluralize.pluralize)(name))}.sdl.${extension}` + ); + if (typescript) { + template = await (0, import_lib.transformTSToJS)(outputPath, template); + } + return { + [outputPath]: template, + ...await (0, import_service.files)({ + name, + crud, + tests, + relations, + typescript + }) + }; +}; +const defaults = { + ...import_helpers.yargsDefaults, + crud: { + default: true, + description: "Also generate mutations", + type: "boolean" + } +}; +const command = "sdl "; +const description = "Generate a GraphQL schema and service component based on a given DB schema Model"; +const builder = (yargs) => { + yargs.positional("model", { + description: "Model to generate the sdl for", + type: "string" + }).option("tests", { + description: "Generate test files", + type: "boolean" + // don't give it a default value, it gets overwritten in first few lines + // of the handler + }).option("docs", { + description: "Generate SDL and GraphQL comments to use in documentation", + type: "boolean" + }).option("rollback", { + description: "Revert all generator actions if an error occurs", + type: "boolean", + default: true + }).epilogue( + `Also see the ${(0, import_terminal_link.default)( + "Redwood CLI Reference", + "https://redwoodjs.com/docs/cli-commands#generate-sdl" + )}` + ); + Object.entries(defaults).forEach(([option, config]) => { + yargs.option(option, config); + }); +}; +const handler = async ({ + model, + crud, + force, + tests, + typescript, + docs, + rollback +}) => { + if (tests === void 0) { + tests = (0, import_project_config.getConfig)().generate.tests; + } + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "generate sdl", + crud, + force, + tests, + typescript, + docs, + rollback + }); + try { + const { name } = await (0, import_schemaHelpers.verifyModelName)({ name: model }); + const tasks = new import_listr2.Listr( + [ + { + title: "Generating SDL files...", + task: async () => { + const f = await files({ name, tests, crud, typescript, docs }); + return (0, import_lib.writeFilesTask)(f, { overwriteExisting: force }); + } + }, + { + title: `Generating types ...`, + task: async () => { + const { errors } = await (0, import_generate.generate)(); + for (const { message, error } of errors) { + console.error(message); + console.log(); + console.error(error); + console.log(); + } + (0, import_rollback.addFunctionToRollback)(import_generate.generate, true); + } + } + ].filter(Boolean), + { + rendererOptions: { collapseSubtasks: false }, + exitOnError: true, + silentRendererCondition: process.env.NODE_ENV === "test" + } + ); + if (rollback && !force) { + (0, import_rollback.prepareForRollback)(tasks); + } + await tasks.run(); + } catch (e) { + (0, import_telemetry.errorTelemetry)(process.argv, e.message); + console.error(import_colors.default.error(e.message)); + process.exit(e?.exitCode || 1); + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + defaults, + description, + files, + handler +}); diff --git a/packages/cli/dist/commands/generate/sdl/templates/sdl.js.template b/packages/cli/dist/commands/generate/sdl/templates/sdl.js.template new file mode 100644 index 0000000000..2dbecc317d --- /dev/null +++ b/packages/cli/dist/commands/generate/sdl/templates/sdl.js.template @@ -0,0 +1,61 @@ +export const schema = gql` + <% if (docs) { %> + """${modelDescription}""" + <% } %> + type ${singularPascalName} { + ${query} + } + +<% if (enums.length > 0) {%> +<% enums.forEach((enumDef, idx)=> { + const enumName = enums[idx].name + const enumDescription = enums[idx].documentation || `Possible values for ${enums[idx].name}` + %> + <% if (docs) { %> + """${enumDescription}""" + <% } %> + enum ${enums[idx].name} {<% enums[idx].values.forEach((enumDefValue, idk)=> { %> + ${enums[idx].values[idk].name}<% }) %> + } +<%}) %><% } %> + <% if (docs) { %> + """About queries""" + <% } %> + type Query { + <% if (docs) { %> + "Fetch ${pluralPascalName}." + <% } %>${pluralCamelName}: [${singularPascalName}!]! @requireAuth<% if (crud) { %> + <% if (docs) { %> + "Fetch a ${singularPascalName} by id." + <% } %>${singularCamelName}(${idName}: ${idType}!): ${singularPascalName} @requireAuth<% } %> + } + + <% if (docs) { %> + """Autogenerated input type of Input${singularPascalName}.""" + <% } %> + input Create${singularPascalName}Input { + ${createInput} + } + + <% if (docs) { %> + """Autogenerated input type of Update${singularPascalName}.""" + <% } %> + input Update${singularPascalName}Input { + ${updateInput} + }<% if (crud) { %> + + <% if (docs) { %> + """About mutations""" + <% } %> + type Mutation { + <% if (docs) { %> + "Creates a new ${singularPascalName}." + <% } %>create${singularPascalName}(input: Create${singularPascalName}Input!): ${singularPascalName}! @requireAuth + <% if (docs) { %> + "Updates an existing ${singularPascalName}." + <% } %>update${singularPascalName}(${idName}: ${idType}!, input: Update${singularPascalName}Input!): ${singularPascalName}! @requireAuth + <% if (docs) { %> + "Deletes an existing ${singularPascalName}." + <% } %>delete${singularPascalName}(${idName}: ${idType}!): ${singularPascalName}! @requireAuth + }<% } %> +` diff --git a/packages/cli/dist/commands/generate/sdl/templates/sdl.ts.template b/packages/cli/dist/commands/generate/sdl/templates/sdl.ts.template new file mode 100644 index 0000000000..2dbecc317d --- /dev/null +++ b/packages/cli/dist/commands/generate/sdl/templates/sdl.ts.template @@ -0,0 +1,61 @@ +export const schema = gql` + <% if (docs) { %> + """${modelDescription}""" + <% } %> + type ${singularPascalName} { + ${query} + } + +<% if (enums.length > 0) {%> +<% enums.forEach((enumDef, idx)=> { + const enumName = enums[idx].name + const enumDescription = enums[idx].documentation || `Possible values for ${enums[idx].name}` + %> + <% if (docs) { %> + """${enumDescription}""" + <% } %> + enum ${enums[idx].name} {<% enums[idx].values.forEach((enumDefValue, idk)=> { %> + ${enums[idx].values[idk].name}<% }) %> + } +<%}) %><% } %> + <% if (docs) { %> + """About queries""" + <% } %> + type Query { + <% if (docs) { %> + "Fetch ${pluralPascalName}." + <% } %>${pluralCamelName}: [${singularPascalName}!]! @requireAuth<% if (crud) { %> + <% if (docs) { %> + "Fetch a ${singularPascalName} by id." + <% } %>${singularCamelName}(${idName}: ${idType}!): ${singularPascalName} @requireAuth<% } %> + } + + <% if (docs) { %> + """Autogenerated input type of Input${singularPascalName}.""" + <% } %> + input Create${singularPascalName}Input { + ${createInput} + } + + <% if (docs) { %> + """Autogenerated input type of Update${singularPascalName}.""" + <% } %> + input Update${singularPascalName}Input { + ${updateInput} + }<% if (crud) { %> + + <% if (docs) { %> + """About mutations""" + <% } %> + type Mutation { + <% if (docs) { %> + "Creates a new ${singularPascalName}." + <% } %>create${singularPascalName}(input: Create${singularPascalName}Input!): ${singularPascalName}! @requireAuth + <% if (docs) { %> + "Updates an existing ${singularPascalName}." + <% } %>update${singularPascalName}(${idName}: ${idType}!, input: Update${singularPascalName}Input!): ${singularPascalName}! @requireAuth + <% if (docs) { %> + "Deletes an existing ${singularPascalName}." + <% } %>delete${singularPascalName}(${idName}: ${idType}!): ${singularPascalName}! @requireAuth + }<% } %> +` diff --git a/packages/cli/dist/commands/generate/secret/secret.js b/packages/cli/dist/commands/generate/secret/secret.js new file mode 100644 index 0000000000..69b8e19332 --- /dev/null +++ b/packages/cli/dist/commands/generate/secret/secret.js @@ -0,0 +1,91 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var secret_exports = {}; +__export(secret_exports, { + DEFAULT_LENGTH: () => DEFAULT_LENGTH, + builder: () => builder, + command: () => command, + description: () => description, + generateSecret: () => generateSecret, + handler: () => handler +}); +module.exports = __toCommonJS(secret_exports); +var import_node_crypto = __toESM(require("node:crypto")); +var import_terminal_link = __toESM(require("terminal-link")); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +const DEFAULT_LENGTH = 32; +const generateSecret = (length = DEFAULT_LENGTH) => { + return import_node_crypto.default.randomBytes(length).toString("base64"); +}; +const command = "secret"; +const description = "Generates a secret key using a cryptographically-secure source of entropy"; +const builder = (yargs) => yargs.option("length", { + description: "Length of the generated secret", + type: "integer", + required: false, + default: DEFAULT_LENGTH +}).option("raw", { + description: "Prints just the raw secret", + type: "boolean", + required: false, + default: false +}).epilogue( + `Also see the ${(0, import_terminal_link.default)( + "Redwood CLI Reference", + "https://redwoodjs.com/docs/cli-commands#generate-secret" + )}` +); +const handler = ({ length, raw }) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "generate secret", + length, + raw + }); + if (raw) { + console.log(generateSecret(length)); + return; + } + console.info(""); + console.info(` ${generateSecret(length)}`); + console.info(""); + console.info( + "If you're using this with dbAuth, set a SESSION_SECRET environment variable to this value." + ); + console.info(""); + console.info("Keep it secret, keep it safe!"); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + DEFAULT_LENGTH, + builder, + command, + description, + generateSecret, + handler +}); diff --git a/packages/cli/dist/commands/generate/service/service.js b/packages/cli/dist/commands/generate/service/service.js new file mode 100644 index 0000000000..ed7755a36b --- /dev/null +++ b/packages/cli/dist/commands/generate/service/service.js @@ -0,0 +1,378 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var service_exports = {}; +__export(service_exports, { + buildScenario: () => buildScenario, + buildStringifiedScenario: () => buildStringifiedScenario, + builder: () => builder, + command: () => command, + defaults: () => defaults, + description: () => description, + fieldTypes: () => fieldTypes, + fieldsToInput: () => fieldsToInput, + fieldsToScenario: () => fieldsToScenario, + fieldsToUpdate: () => fieldsToUpdate, + files: () => files, + handler: () => handler, + parseSchema: () => parseSchema, + scenarioFieldValue: () => scenarioFieldValue +}); +module.exports = __toCommonJS(service_exports); +var import_camelcase = __toESM(require("camelcase")); +var import_terminal_link = __toESM(require("terminal-link")); +var import_lib = require("../../../lib"); +var import_rwPluralize = require("../../../lib/rwPluralize"); +var import_schemaHelpers = require("../../../lib/schemaHelpers"); +var import_helpers = require("../helpers"); +var import_helpers2 = require("../helpers"); +const DEFAULT_SCENARIO_NAMES = ["one", "two"]; +const parseSchema = async (model) => { + const schema = await (0, import_schemaHelpers.getSchema)(model); + const relations = {}; + let foreignKeys = []; + let scalarFields = schema.fields.filter((field) => { + if (field.relationFromFields) { + if (field.isRequired && field.relationFromFields.length !== 0) { + relations[field.name] = { + foreignKey: field.relationFromFields, + type: field.type + }; + } + foreignKeys = foreignKeys.concat(field.relationFromFields); + } + return field.isRequired && !field.hasDefaultValue && // don't include fields that the database will default + !field.relationName; + }); + return { scalarFields, relations, foreignKeys }; +}; +const scenarioFieldValue = (field) => { + const randFloat = Math.random() * 1e7; + const randInt = parseInt(Math.random() * 1e7); + const randIntArray = [ + parseInt(Math.random() * 300), + parseInt(Math.random() * 300), + parseInt(Math.random() * 300) + ]; + switch (field.type) { + case "BigInt": + return `${BigInt(randInt)}n`; + case "Boolean": + return true; + case "DateTime": + return /* @__PURE__ */ new Date(); + case "Decimal": + case "Float": + return randFloat; + case "Int": + return randInt; + case "Json": + return { foo: "bar" }; + case "String": + return field.isUnique ? `String${randInt}` : "String"; + case "Bytes": + return `Buffer.from([${randIntArray}])`; + default: { + if (field.kind === "enum" && field.enumValues[0]) { + return field.enumValues[0].dbName || field.enumValues[0].name; + } + } + } +}; +const fieldsToScenario = async (scalarFields, relations, foreignKeys) => { + const data = {}; + scalarFields.forEach((field) => { + if (!foreignKeys.length || !foreignKeys.includes(field.name)) { + data[field.name] = scenarioFieldValue(field); + } + }); + for (const [relationName, relData] of Object.entries(relations)) { + const relationModelName = relData.type; + const { + scalarFields: relScalarFields, + relations: relRelations, + foreignKeys: relForeignKeys + } = await parseSchema(relationModelName); + data[relationName] = { + create: await fieldsToScenario( + relScalarFields, + relRelations, + relForeignKeys + ) + }; + } + return data; +}; +const buildScenario = async (model) => { + const scenarioModelName = (0, import_camelcase.default)(model); + const standardScenario = { + [scenarioModelName]: {} + }; + const { scalarFields, relations, foreignKeys } = await parseSchema(model); + for (const name of DEFAULT_SCENARIO_NAMES) { + standardScenario[scenarioModelName][name] = {}; + const scenarioData = await fieldsToScenario( + scalarFields, + relations, + foreignKeys + ); + Object.keys(scenarioData).forEach((key) => { + const value = scenarioData[key]; + if (value && typeof value === "string" && value.match(/^\d+n$/)) { + scenarioData[key] = `${value.slice(0, value.length - 1)}n`; + } + }); + standardScenario[scenarioModelName][name].data = scenarioData; + } + return standardScenario; +}; +const buildStringifiedScenario = async (model) => { + const scenario = await buildScenario(model); + const jsonString = JSON.stringify(scenario, (_key, value) => { + if (typeof value === "bigint") { + return value.toString(); + } + if (typeof value === "string" && value.match(/^\d+n$/)) { + return Number(value.slice(0, value.length - 1)); + } + return value; + }); + return jsonString.replace(/"Buffer\.from\(([^)]+)\)"/g, "Buffer.from($1)"); +}; +const fieldTypes = async (model) => { + const { scalarFields } = await parseSchema(model); + return scalarFields.reduce((acc, value) => { + acc[value.name] = value.type; + return acc; + }, {}); +}; +const fieldsToInput = async (model) => { + const { scalarFields, foreignKeys } = await parseSchema(model); + const modelName = (0, import_camelcase.default)((0, import_rwPluralize.singularize)(model)); + let inputObj = {}; + scalarFields.forEach((field) => { + if (foreignKeys.includes(field.name)) { + inputObj[field.name] = `scenario.${modelName}.two.${field.name}`; + } else { + inputObj[field.name] = scenarioFieldValue(field); + } + }); + if (Object.keys(inputObj).length > 0) { + return inputObj; + } else { + return false; + } +}; +const fieldsToUpdate = async (model) => { + const { scalarFields, relations, foreignKeys } = await parseSchema(model); + const modelName = (0, import_camelcase.default)((0, import_rwPluralize.singularize)(model)); + let field, newValue, fieldName; + field = scalarFields.find((scalar) => !foreignKeys.includes(scalar.name)); + if (!field) { + field = scalarFields[0]; + } + if (!field) { + return false; + } + if (foreignKeys.includes(field.name)) { + fieldName = Object.values(relations)[0].foreignKey; + newValue = `scenario.${modelName}.two.${field.name}`; + } else { + fieldName = field.name; + const value = scenarioFieldValue(field); + newValue = value; + switch (field.type) { + case "BigInt": + newValue = `${newValue + 1n}`; + break; + case "Boolean": { + newValue = !value; + break; + } + case "DateTime": { + let date = /* @__PURE__ */ new Date(); + date.setDate(date.getDate() + 1); + newValue = date; + break; + } + case "Decimal": + case "Float": { + newValue = newValue + 1.1; + break; + } + case "Int": { + newValue = newValue + 1; + break; + } + case "Json": { + newValue = { foo: "baz" }; + break; + } + case "String": { + newValue = newValue + "2"; + break; + } + default: { + if (field.kind === "enum" && field.enumValues[field.enumValues.length - 1]) { + const enumVal = field.enumValues[field.enumValues.length - 1]; + newValue = enumVal.dbName || enumVal.name; + } + break; + } + } + } + return { [fieldName]: newValue }; +}; +const getIdName = async (model) => { + const schema = await (0, import_schemaHelpers.getSchema)(model); + return schema.fields.find((field) => field.isId)?.name; +}; +const files = async ({ + name, + tests, + relations, + typescript, + ...rest +}) => { + const componentName = (0, import_camelcase.default)((0, import_rwPluralize.pluralize)(name)); + const model = name; + const idName = await getIdName(model); + const extension = "ts"; + const serviceFile = await (0, import_helpers2.templateForComponentFile)({ + name, + componentName, + extension: `.${extension}`, + apiPathSection: "services", + generator: "service", + templatePath: `service.${extension}.template`, + templateVars: { relations: relations || [], idName, ...rest } + }); + const testFile = await (0, import_helpers2.templateForComponentFile)({ + name, + componentName, + extension: `.test.${extension}`, + apiPathSection: "services", + generator: "service", + templatePath: `test.${extension}.template`, + templateVars: { + relations: relations || [], + create: await fieldsToInput(model), + update: await fieldsToUpdate(model), + types: await fieldTypes(model), + prismaImport: (await parseSchema(model)).scalarFields.some( + (field) => field.type === "Decimal" + ), + prismaModel: model, + idName, + ...rest + } + }); + const scenariosFile = await (0, import_helpers2.templateForComponentFile)({ + name, + componentName, + extension: `.scenarios.${extension}`, + apiPathSection: "services", + generator: "service", + templatePath: `scenarios.${extension}.template`, + templateVars: { + scenario: await buildScenario(model), + stringifiedScenario: await buildStringifiedScenario(model), + prismaModel: model, + idName, + ...rest + } + }); + const files2 = [serviceFile]; + if (tests) { + files2.push(testFile); + files2.push(scenariosFile); + } + return files2.reduce(async (accP, [outputPath, content]) => { + const acc = await accP; + if (!typescript) { + content = await (0, import_lib.transformTSToJS)(outputPath, content); + outputPath = outputPath.replace(".ts", ".js"); + } + return { + [outputPath]: content, + ...acc + }; + }, Promise.resolve({})); +}; +const defaults = { + ...import_helpers.yargsDefaults, + tests: { + description: "Generate test files", + type: "boolean" + }, + crud: { + default: true, + description: "Create CRUD functions", + type: "boolean" + } +}; +const builder = (yargs) => { + yargs.positional("name", { + description: "Name of the service", + type: "string" + }).option("rollback", { + description: "Revert all generator actions if an error occurs", + type: "boolean", + default: true + }).epilogue( + `Also see the ${(0, import_terminal_link.default)( + "Redwood CLI Reference", + "https://redwoodjs.com/docs/cli-commands#generate-service" + )}` + ); + Object.entries(defaults).forEach(([option, config]) => { + yargs.option(option, config); + }); +}; +const { command, description, handler } = (0, import_helpers2.createYargsForComponentGeneration)({ + componentName: "service", + preTasksFn: import_schemaHelpers.verifyModelName, + filesFn: files +}); +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + buildScenario, + buildStringifiedScenario, + builder, + command, + defaults, + description, + fieldTypes, + fieldsToInput, + fieldsToScenario, + fieldsToUpdate, + files, + handler, + parseSchema, + scenarioFieldValue +}); diff --git a/packages/cli/dist/commands/generate/service/templates/scenarios.ts.template b/packages/cli/dist/commands/generate/service/templates/scenarios.ts.template new file mode 100644 index 0000000000..9b19d5e6df --- /dev/null +++ b/packages/cli/dist/commands/generate/service/templates/scenarios.ts.template @@ -0,0 +1,7 @@ +import type { Prisma, ${prismaModel} } from '@prisma/client' +import type { ScenarioData } from '@redwoodjs/testing/api' + + +export const standard = defineScenario(${stringifiedScenario}) + +export type StandardScenario = ScenarioData<${prismaModel}, '${camelName}'> diff --git a/packages/cli/dist/commands/generate/service/templates/service.ts.template b/packages/cli/dist/commands/generate/service/templates/service.ts.template new file mode 100644 index 0000000000..cbd5ddd312 --- /dev/null +++ b/packages/cli/dist/commands/generate/service/templates/service.ts.template @@ -0,0 +1,38 @@ +import type { QueryResolvers<% if (crud) { %>, MutationResolvers<% } %><% if (relations.length) { %>, ${singularPascalName}RelationResolvers<% } %> } from 'types/graphql' + +import { db } from 'src/lib/db' + +export const ${pluralCamelName}: QueryResolvers['${pluralCamelName}'] = () => { + return db.${singularCamelName}.findMany() +}<% if (crud || relations.length) { %> + +export const ${singularCamelName}: QueryResolvers['${singularCamelName}'] = ({ ${idName} }) => { + return db.${singularCamelName}.findUnique({ + where: { ${idName} }, + }) +}<% } %><% if (crud) { %> + +export const create${singularPascalName}: MutationResolvers['create${singularPascalName}'] = ({ input }) => { + return db.${singularCamelName}.create({ + data: input, + }) +} + +export const update${singularPascalName}: MutationResolvers['update${singularPascalName}'] = ({ ${idName}, input }) => { + return db.${singularCamelName}.update({ + data: input, + where: { ${idName} }, + }) +} + +export const delete${singularPascalName}: MutationResolvers['delete${singularPascalName}'] = ({ ${idName} }) => { + return db.${singularCamelName}.delete({ + where: { ${idName} }, + }) +}<% } %><% if (relations.length) { %> + +export const ${singularPascalName}: ${singularPascalName}RelationResolvers = {<% relations.forEach(relation => { %> + ${relation}: (_obj, { root }) => { + return db.${singularCamelName}.findUnique({ where: { ${idName}: root?.${idName} } }).${relation}() + },<% }) %> +}<% } %> diff --git a/packages/cli/dist/commands/generate/service/templates/test.ts.template b/packages/cli/dist/commands/generate/service/templates/test.ts.template new file mode 100644 index 0000000000..e073d8952b --- /dev/null +++ b/packages/cli/dist/commands/generate/service/templates/test.ts.template @@ -0,0 +1,84 @@ +<% // Transforms an object or single value into something that's more suitable + // for generating test cases + // If a `type` is passed in, the string for creating an object of that type + // will be generated + // If no type, or a type we don't support, is passed in we'll default to + // generating regular strings + // Looks for quoted strings, either by single (') or double (") quotes. + // When found + // - Removes the quotes around `scenario` variables. + // - Removes the quotes around `BigInt` fields. + const transformValue = (obj, type) => { + if (type === 'DateTime') { + return `new Date('${obj.toISOString()}')` + } else if (type === 'Decimal') { + return `new Prisma.Decimal(${obj})` + } + + const jsonString = JSON.stringify(obj).replace(/['"].*?['"]/g, (string) => { + if (string.match(/scenario\./)) { + return string.replace(/['"]/g, '') + } + + // BigInt + if (string.match(/^\"\d+n\"$/)) { + return string.slice(1, string.length - 1) + } + + return string + }) + + // Not all values can be represented as JSON, like function invocations + return jsonString.replace(/"Buffer\.from\(([^)]+)\)"/g, 'Buffer.from($1)') + } %> +<% if (prismaImport) { %>import { Prisma, ${prismaModel} } from '@prisma/client'<% } else { %>import type { ${prismaModel} } from '@prisma/client'<% } %> + +import { ${pluralCamelName}<% if (crud) { %>,${singularCamelName}, create${singularPascalName}, update${singularPascalName}, delete${singularPascalName}<% } %> } from './${pluralCamelName}' +import type { StandardScenario } from './${pluralCamelName}.scenarios' + +// Generated boilerplate tests do not account for all circumstances +// and can fail without adjustments, e.g. Float. +// Please refer to the RedwoodJS Testing Docs: +// https://redwoodjs.com/docs/testing#testing-services +// https://redwoodjs.com/docs/testing#jest-expect-type-considerations + +describe('${pluralCamelName}', () => { + scenario('returns all ${pluralCamelName}', async (scenario: StandardScenario) => { + const result = await ${pluralCamelName}() + + expect(result.length).toEqual(Object.keys(scenario.${singularCamelName}).length) + })<% if (crud) { %> + + scenario('returns a single ${singularCamelName}', async (scenario: StandardScenario) => { + const result = await ${singularCamelName}({ ${idName}: scenario.${singularCamelName}.one.${idName} }) + + expect(result).toEqual(scenario.${singularCamelName}.one) + }) + + <% if (create) { %>scenario('creates a ${singularCamelName}', async (${transformValue(create).includes('scenario.') ? 'scenario: StandardScenario' : ''}) => { + const result = await create${singularPascalName}({ + input: ${transformValue(create)}, + }) + + <% for (const [name, value] of Object.entries(create)) { %> + expect(result.${name}).toEqual(${transformValue(value, types[name])})<% } %> + })<% } %> + + <% if (update) { %>scenario('updates a ${singularCamelName}', async (scenario: StandardScenario) => {<% rand = parseInt(Math.random() * 10000000) %> + const original = await (${singularCamelName}({ ${idName}: scenario.${singularCamelName}.one.${idName} })) as ${prismaModel} + const result = await update${singularPascalName}({ + ${idName}: original.${idName}, + input: ${transformValue(update)}, + }) + + <% for (const [name, value] of Object.entries(update)) { %> + expect(result.${name}).toEqual(${transformValue(value, types[name])})<% } %> + })<% } %> + + scenario('deletes a ${singularCamelName}', async (scenario: StandardScenario) => { + const original = (await delete${singularPascalName}({ ${idName}: scenario.${singularCamelName}.one.${idName} })) as ${prismaModel} + const result = await ${singularCamelName}({ ${idName}: original.${idName} }) + + expect(result).toEqual(null) + })<% } %> +}) diff --git a/packages/cli/dist/commands/info.js b/packages/cli/dist/commands/info.js new file mode 100644 index 0000000000..506b3699aa --- /dev/null +++ b/packages/cli/dist/commands/info.js @@ -0,0 +1,70 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var info_exports = {}; +__export(info_exports, { + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(info_exports); +var import_envinfo = __toESM(require("envinfo")); +var import_terminal_link = __toESM(require("terminal-link")); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +const command = "info"; +const description = "Print your system environment information"; +const builder = (yargs) => { + yargs.epilogue( + `Also see the ${(0, import_terminal_link.default)( + "Redwood CLI Reference", + "https://redwoodjs.com/docs/cli-commands#info" + )}` + ); +}; +const handler = async () => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "info" + }); + const output = await import_envinfo.default.run({ + System: ["OS", "Shell"], + Binaries: ["Node", "Yarn"], + Browsers: ["Chrome", "Edge", "Firefox", "Safari"], + // yarn workspaces not supported :-/ + npmPackages: "@redwoodjs/*", + Databases: ["SQLite"] + }); + console.log(output); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/lint.js b/packages/cli/dist/commands/lint.js new file mode 100644 index 0000000000..d840d9fc91 --- /dev/null +++ b/packages/cli/dist/commands/lint.js @@ -0,0 +1,93 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var lint_exports = {}; +__export(lint_exports, { + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(lint_exports); +var import_execa = __toESM(require("execa")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_terminal_link = __toESM(require("terminal-link")); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_lib = require("../lib"); +const command = "lint [path..]"; +const description = "Lint your files"; +const builder = (yargs) => { + yargs.positional("path", { + description: "Specify file(s) or directory(ies) to lint relative to project root", + type: "array" + }).option("fix", { + default: false, + description: "Try to fix errors", + type: "boolean" + }).epilogue( + `Also see the ${(0, import_terminal_link.default)( + "Redwood CLI Reference", + "https://redwoodjs.com/docs/cli-commands#lint" + )}` + ); +}; +const handler = async ({ path, fix }) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "lint", + fix + }); + try { + const pathString = path?.join(" "); + const result = await (0, import_execa.default)( + "yarn eslint", + [ + fix && "--fix", + !pathString && import_fs_extra.default.existsSync((0, import_lib.getPaths)().web.src) && "web/src", + !pathString && import_fs_extra.default.existsSync((0, import_lib.getPaths)().web.config) && "web/config", + !pathString && import_fs_extra.default.existsSync((0, import_lib.getPaths)().scripts) && "scripts", + !pathString && import_fs_extra.default.existsSync((0, import_lib.getPaths)().api.src) && "api/src", + pathString + ].filter(Boolean), + { + cwd: (0, import_lib.getPaths)().base, + shell: true, + stdio: "inherit" + } + ); + process.exitCode = result.exitCode; + } catch (error) { + process.exitCode = error.exitCode ?? 1; + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/prerender.js b/packages/cli/dist/commands/prerender.js new file mode 100644 index 0000000000..12ad92182a --- /dev/null +++ b/packages/cli/dist/commands/prerender.js @@ -0,0 +1,72 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var prerender_exports = {}; +__export(prerender_exports, { + aliases: () => aliases, + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(prerender_exports); +const command = "prerender"; +const aliases = ["render"]; +const description = "Prerender pages of your Redwood app at build time"; +const builder = (yargs) => { + yargs.showHelpOnFail(false); + yargs.option("path", { + alias: ["p", "route"], + description: "Router path to prerender. Especially useful for debugging", + type: "string" + }); + yargs.option("dry-run", { + alias: ["d", "dryrun"], + default: false, + description: "Run prerender and output to console", + type: "boolean" + }); + yargs.option("verbose", { + alias: "v", + default: false, + description: "Print more", + type: "boolean" + }); +}; +const handler = async (options) => { + const { handler: handler2 } = await import("./prerenderHandler.js"); + return handler2(options); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + aliases, + builder, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/prerenderHandler.js b/packages/cli/dist/commands/prerenderHandler.js new file mode 100644 index 0000000000..849f80828b --- /dev/null +++ b/packages/cli/dist/commands/prerenderHandler.js @@ -0,0 +1,280 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var prerenderHandler_exports = {}; +__export(prerenderHandler_exports, { + getTasks: () => getTasks, + handler: () => handler +}); +module.exports = __toCommonJS(prerenderHandler_exports); +var import_path = __toESM(require("path")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_listr2 = require("listr2"); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_prerender = require("@redwoodjs/prerender"); +var import_detection = require("@redwoodjs/prerender/detection"); +var import_project_config = require("@redwoodjs/project-config"); +var import_telemetry = require("@redwoodjs/telemetry"); +var import_colors = __toESM(require("../lib/colors")); +var import_exec = require("../lib/exec"); +class PathParamError extends Error { +} +const mapRouterPathToHtml = (routerPath) => { + if (routerPath === "/") { + return "web/dist/index.html"; + } else { + return `web/dist${routerPath}.html`; + } +}; +function getRouteHooksFilePath(routeFilePath) { + const routeHooksFilePathTs = routeFilePath.replace( + /\.[jt]sx?$/, + ".routeHooks.ts" + ); + if (import_fs_extra.default.existsSync(routeHooksFilePathTs)) { + return routeHooksFilePathTs; + } + const routeHooksFilePathJs = routeFilePath.replace( + /\.[jt]sx?$/, + ".routeHooks.js" + ); + if (import_fs_extra.default.existsSync(routeHooksFilePathJs)) { + return routeHooksFilePathJs; + } + return void 0; +} +async function expandRouteParameters(route) { + const routeHooksFilePath = getRouteHooksFilePath(route.filePath); + if (!routeHooksFilePath) { + return [route]; + } + try { + const routeParameters = await (0, import_exec.runScriptFunction)({ + path: routeHooksFilePath, + functionName: "routeParameters", + args: { + name: route.name, + path: route.path, + routePath: route.routePath, + filePath: route.filePath + } + }); + if (routeParameters) { + return routeParameters.map((pathParamValues) => { + let newPath = route.path; + Object.entries(pathParamValues).forEach(([paramName, paramValue]) => { + newPath = newPath.replace( + new RegExp(`{${paramName}:?[^}]*}`), + paramValue + ); + }); + return { ...route, path: newPath }; + }); + } + } catch (e) { + console.error(import_colors.default.error(e.stack)); + return [route]; + } + return [route]; +} +const getTasks = async (dryrun, routerPathFilter = null) => { + const prerenderRoutes = (0, import_detection.detectPrerenderRoutes)().filter((route) => route.path); + const indexHtmlPath = import_path.default.join((0, import_project_config.getPaths)().web.dist, "index.html"); + if (prerenderRoutes.length === 0) { + console.log("\nSkipping prerender..."); + console.log( + import_colors.default.warning( + "You have not marked any routes with a path as `prerender` in `Routes.{jsx,tsx}` \n" + ) + ); + return []; + } + if (!import_fs_extra.default.existsSync(indexHtmlPath)) { + console.error( + "You must run `yarn rw build web` before trying to prerender." + ); + process.exit(1); + } + (0, import_exec.configureBabel)(); + const expandedRouteParameters = await Promise.all( + prerenderRoutes.map((route) => expandRouteParameters(route)) + ); + const listrTasks = expandedRouteParameters.flat().flatMap((routeToPrerender) => { + if (routerPathFilter && routeToPrerender.path !== routerPathFilter) { + return []; + } + const outputHtmlPath = mapRouterPathToHtml(routeToPrerender.path); + const queryCache = {}; + return [ + { + title: `Prerendering ${routeToPrerender.path} -> ${outputHtmlPath}`, + task: async () => { + if (/\{.*}/.test(routeToPrerender.path)) { + throw new PathParamError( + `Could not retrieve route parameters for ${routeToPrerender.path}` + ); + } + try { + const prerenderedHtml = await (0, import_prerender.runPrerender)({ + queryCache, + renderPath: routeToPrerender.path + }); + if (!dryrun) { + (0, import_prerender.writePrerenderedHtmlFile)(outputHtmlPath, prerenderedHtml); + } + } catch (e) { + console.log(); + console.log( + import_colors.default.warning("You can use `yarn rw prerender --dry-run` to debug") + ); + console.log(); + console.log( + `${import_colors.default.info("-".repeat(10))} Error rendering path "${routeToPrerender.path}" ${import_colors.default.info("-".repeat(10))}` + ); + (0, import_telemetry.errorTelemetry)(process.argv, `Error prerendering: ${e.message}`); + console.error(import_colors.default.error(e.stack)); + console.log(); + throw new Error(`Failed to render "${routeToPrerender.filePath}"`); + } + } + } + ]; + }); + return listrTasks; +}; +const diagnosticCheck = () => { + const checks = [ + { + message: "Duplicate React version found in web/node_modules", + failure: import_fs_extra.default.existsSync( + import_path.default.join((0, import_project_config.getPaths)().web.base, "node_modules/react") + ) + }, + { + message: "Duplicate react-dom version found in web/node_modules", + failure: import_fs_extra.default.existsSync( + import_path.default.join((0, import_project_config.getPaths)().web.base, "node_modules/react-dom") + ) + }, + { + message: "Duplicate core-js version found in web/node_modules", + failure: import_fs_extra.default.existsSync( + import_path.default.join((0, import_project_config.getPaths)().web.base, "node_modules/core-js") + ) + }, + { + message: "Duplicate @redwoodjs/web version found in web/node_modules", + failure: import_fs_extra.default.existsSync( + import_path.default.join((0, import_project_config.getPaths)().web.base, "node_modules/@redwoodjs/web") + ) + } + ]; + console.log("Running diagnostic checks"); + if (checks.some((checks2) => checks2.failure)) { + console.error(import_colors.default.error("node_modules are being duplicated in `./web` \n")); + console.log("\u26A0\uFE0F Issues found: "); + console.log("-".repeat(50)); + checks.filter((check) => check.failure).forEach((check, i) => { + console.log(`${i + 1}. ${check.message}`); + }); + console.log("-".repeat(50)); + console.log( + "Diagnostic check found issues. See the Redwood Forum link below for help:" + ); + console.log( + import_colors.default.underline( + "https://community.redwoodjs.com/search?q=duplicate%20package%20found" + ) + ); + console.log(); + process.exit(1); + } else { + console.log("\u2714 Diagnostics checks passed \n"); + } +}; +const handler = async ({ path: routerPath, dryRun, verbose }) => { + if ((0, import_project_config.getConfig)().experimental?.streamingSsr?.enabled) { + console.log( + import_colors.default.warning( + "Prerendering is not yet supported with Streaming SSR. Skipping prerender..." + ) + ); + return; + } + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "prerender", + dryRun, + verbose + }); + const listrTasks = await getTasks(dryRun, routerPath); + const tasks = new import_listr2.Listr(listrTasks, { + renderer: verbose ? "verbose" : "default", + rendererOptions: { collapseSubtasks: false }, + concurrent: false + }); + try { + if (dryRun) { + console.log(import_colors.default.info("::: Dry run, not writing changes :::")); + } + await tasks.run(); + } catch (e) { + console.log(); + await diagnosticCheck(); + console.log(import_colors.default.warning("Tips:")); + if (e instanceof PathParamError) { + console.log( + import_colors.default.info( + "- You most likely need to add or update a *.routeHooks.{js,ts} file next to the Page you're trying to prerender" + ) + ); + } else { + console.log( + import_colors.default.info( + `- This could mean that a library you're using does not support SSR.` + ) + ); + console.log( + import_colors.default.info( + "- Avoid using `window` in the initial render path through your React components without checks. \n See https://redwoodjs.com/docs/prerender#prerender-utils" + ) + ); + console.log( + import_colors.default.info( + "- Avoid prerendering Cells with authenticated queries, by conditionally rendering them.\n See https://redwoodjs.com/docs/prerender#common-warnings--errors" + ) + ); + } + console.log(); + process.exit(1); + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + getTasks, + handler +}); diff --git a/packages/cli/dist/commands/prisma.js b/packages/cli/dist/commands/prisma.js new file mode 100644 index 0000000000..d93e65cc6e --- /dev/null +++ b/packages/cli/dist/commands/prisma.js @@ -0,0 +1,54 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var prisma_exports = {}; +__export(prisma_exports, { + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(prisma_exports); +const command = "prisma [commands..]"; +const description = "Run Prisma CLI with experimental features"; +const builder = (yargs) => { + yargs.strictOptions(false).strictCommands(false).strict(false).parserConfiguration({ + "camel-case-expansion": false + }).help(false).version(false); +}; +const handler = async (options) => { + const { handler: handler2 } = await import("./prismaHandler.js"); + return handler2(options); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/prismaHandler.js b/packages/cli/dist/commands/prismaHandler.js new file mode 100644 index 0000000000..241afdece0 --- /dev/null +++ b/packages/cli/dist/commands/prismaHandler.js @@ -0,0 +1,121 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var prismaHandler_exports = {}; +__export(prismaHandler_exports, { + handler: () => handler +}); +module.exports = __toCommonJS(prismaHandler_exports); +var import_path = __toESM(require("path")); +var import_boxen = __toESM(require("boxen")); +var import_execa = __toESM(require("execa")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_telemetry = require("@redwoodjs/telemetry"); +var import_colors = __toESM(require("../lib/colors")); +var import_lib = require("../lib/index"); +const handler = async ({ _, $0, commands = [], ...options }) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "prisma" + }); + const rwjsPaths = (0, import_lib.getPaths)(); + const helpIndex = commands.indexOf("help"); + if (helpIndex !== -1) { + options.help = true; + commands.splice(helpIndex, 1); + } + const hasHelpOption = options.help || options.h; + if (!hasHelpOption) { + if (["generate", "introspect", "db", "migrate", "studio", "format"].includes( + commands[0] + )) { + if (!import_fs_extra.default.existsSync(rwjsPaths.api.dbSchema)) { + console.error(); + console.error(import_colors.default.error("No Prisma Schema found.")); + console.error(`Redwood searched here '${rwjsPaths.api.dbSchema}'`); + console.error(); + process.exit(1); + } + options.schema = `${rwjsPaths.api.dbSchema}`; + if (["seed", "diff"].includes(commands[1])) { + delete options.schema; + } + } + } + const args = commands; + for (const [name, value] of Object.entries(options)) { + args.push(name.length > 1 ? `--${name}` : `-${name}`); + if (typeof value !== "boolean") { + value.split(" ").length > 1 ? args.push(`"${value}"`) : args.push(value); + } + } + console.log(); + console.log(import_colors.default.green("Running Prisma CLI...")); + console.log(import_colors.default.underline("$ yarn prisma " + args.join(" "))); + console.log(); + try { + import_execa.default.sync( + `"${import_path.default.join(rwjsPaths.base, "node_modules/.bin/prisma")}"`, + args, + { + shell: true, + cwd: rwjsPaths.base, + stdio: "inherit", + cleanup: true + } + ); + if (hasHelpOption || commands.length === 0) { + printWrapInfo(); + } + } catch (e) { + (0, import_telemetry.errorTelemetry)(process.argv, `Error generating prisma client: ${e.message}`); + process.exit(e?.exitCode || 1); + } +}; +const printWrapInfo = () => { + const message = [ + import_colors.default.bold("Redwood CLI wraps Prisma CLI"), + "", + "Use `yarn rw prisma` to automatically pass `--schema` and `--preview-feature` options.", + "Use `yarn prisma` to skip Redwood CLI automatic options.", + "", + "Find more information in our docs:", + import_colors.default.underline("https://redwoodjs.com/docs/cli-commands#prisma") + ]; + console.log( + (0, import_boxen.default)(message.join("\n"), { + padding: { top: 0, bottom: 0, right: 1, left: 1 }, + margin: 1, + borderColor: "gray" + }) + ); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + handler +}); diff --git a/packages/cli/dist/commands/record.js b/packages/cli/dist/commands/record.js new file mode 100644 index 0000000000..52cf41325c --- /dev/null +++ b/packages/cli/dist/commands/record.js @@ -0,0 +1,51 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var record_exports = {}; +__export(record_exports, { + builder: () => builder, + command: () => command, + description: () => description +}); +module.exports = __toCommonJS(record_exports); +var import_terminal_link = __toESM(require("terminal-link")); +const command = "record "; +const description = "Setup RedwoodRecord for your project. Caches a JSON version of your data model and adds api/src/models/index.js with some config."; +const builder = (yargs) => yargs.commandDir("./record", { recurse: false }).demandCommand().epilogue( + `Also see the ${(0, import_terminal_link.default)( + "RedwoodRecord Docs", + "https://redwoodjs.com/docs/redwoodrecord" + )} +` +); +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description +}); diff --git a/packages/cli/dist/commands/record/init.js b/packages/cli/dist/commands/record/init.js new file mode 100644 index 0000000000..acad50496e --- /dev/null +++ b/packages/cli/dist/commands/record/init.js @@ -0,0 +1,35 @@ +"use strict"; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var init_exports = {}; +__export(init_exports, { + handler: () => handler +}); +module.exports = __toCommonJS(init_exports); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_record = require("@redwoodjs/record"); +const handler = async () => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "record" + }); + await (0, import_record.parseDatamodel)(); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + handler +}); diff --git a/packages/cli/dist/commands/serve.js b/packages/cli/dist/commands/serve.js new file mode 100644 index 0000000000..91274aa733 --- /dev/null +++ b/packages/cli/dist/commands/serve.js @@ -0,0 +1,175 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var serve_exports = {}; +__export(serve_exports, { + builder: () => builder, + command: () => command, + description: () => description +}); +module.exports = __toCommonJS(serve_exports); +var import_path = __toESM(require("path")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_terminal_link = __toESM(require("terminal-link")); +var apiServerCLIConfig = __toESM(require("@redwoodjs/api-server/dist/apiCLIConfig")); +var bothServerCLIConfig = __toESM(require("@redwoodjs/api-server/dist/bothCLIConfig")); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var webServerCLIConfig = __toESM(require("@redwoodjs/web-server")); +var import_lib = require("../lib"); +var import_colors = __toESM(require("../lib/colors")); +var import_project = require("../lib/project.js"); +var import_serveWebHandler = require("./serveWebHandler"); +const command = "serve [side]"; +const description = "Start a server for serving both the api and web sides"; +const builder = async (yargs) => { + const rscEnabled = (0, import_lib.getConfig)().experimental?.rsc?.enabled; + const streamingEnabled = (0, import_lib.getConfig)().experimental?.streamingSsr?.enabled; + yargs.command({ + command: "$0", + description: bothServerCLIConfig.description, + builder: bothServerCLIConfig.builder, + handler: async (argv) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "serve", + port: argv.port, + host: argv.host, + socket: argv.socket + }); + if ((0, import_project.serverFileExists)()) { + const { bothServerFileHandler } = await import("./serveBothHandler.js"); + await bothServerFileHandler(argv); + } else if (rscEnabled || streamingEnabled) { + const { bothSsrRscServerHandler } = await import("./serveBothHandler.js"); + await bothSsrRscServerHandler(argv); + } else { + await bothServerCLIConfig.handler(argv); + } + } + }).command({ + command: "api", + description: apiServerCLIConfig.description, + builder: apiServerCLIConfig.builder, + handler: async (argv) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "serve", + port: argv.port, + host: argv.host, + socket: argv.socket, + apiRootPath: argv.apiRootPath + }); + if ((0, import_project.serverFileExists)()) { + const { apiServerFileHandler } = await import("./serveApiHandler.js"); + await apiServerFileHandler(argv); + } else { + await apiServerCLIConfig.handler(argv); + } + } + }).command({ + command: "web", + description: webServerCLIConfig.description, + builder: webServerCLIConfig.builder, + handler: async (argv) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "serve", + port: argv.port, + host: argv.host, + socket: argv.socket, + apiHost: argv.apiHost + }); + if (streamingEnabled) { + await (0, import_serveWebHandler.webSsrServerHandler)(); + } else { + await webServerCLIConfig.handler(argv); + } + } + }).middleware((argv) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "serve" + }); + const positionalArgs = argv._; + if (positionalArgs.includes("web") && !import_fs_extra.default.existsSync(import_path.default.join((0, import_lib.getPaths)().web.dist), "index.html")) { + console.error( + import_colors.default.error( + "\n Please run `yarn rw build web` before trying to serve web. \n" + ) + ); + process.exit(1); + } + const apiSideExists = import_fs_extra.default.existsSync((0, import_lib.getPaths)().api.base); + if (positionalArgs.includes("api")) { + if (!apiSideExists) { + console.error( + import_colors.default.error( + "\n Unable to serve the api side as no `api` folder exists. \n" + ) + ); + process.exit(1); + } + if (!import_fs_extra.default.existsSync(import_path.default.join((0, import_lib.getPaths)().api.dist))) { + console.error( + import_colors.default.error( + "\n Please run `yarn rw build api` before trying to serve api. \n" + ) + ); + process.exit(1); + } + } + if (positionalArgs.length === 1) { + if (!apiSideExists && !rscEnabled) { + console.error( + import_colors.default.error( + "\n Unable to serve the both sides as no `api` folder exists. Please use `yarn rw serve web` instead. \n" + ) + ); + process.exit(1); + } + if (import_fs_extra.default.existsSync(import_path.default.join((0, import_lib.getPaths)().api.base)) && !import_fs_extra.default.existsSync(import_path.default.join((0, import_lib.getPaths)().api.dist)) || !import_fs_extra.default.existsSync(import_path.default.join((0, import_lib.getPaths)().web.dist), "index.html")) { + console.error( + import_colors.default.error( + "\n Please run `yarn rw build` before trying to serve your redwood app. \n" + ) + ); + process.exit(1); + } + } + if (!process.env.NODE_ENV) { + process.env.NODE_ENV = "production"; + } + }).epilogue( + `Also see the ${(0, import_terminal_link.default)( + "Redwood CLI Reference", + "https://redwoodjs.com/docs/cli-commands#serve" + )}` + ); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description +}); diff --git a/packages/cli/dist/commands/serveApiHandler.js b/packages/cli/dist/commands/serveApiHandler.js new file mode 100644 index 0000000000..a664c812cc --- /dev/null +++ b/packages/cli/dist/commands/serveApiHandler.js @@ -0,0 +1,49 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var serveApiHandler_exports = {}; +__export(serveApiHandler_exports, { + apiServerFileHandler: () => apiServerFileHandler +}); +module.exports = __toCommonJS(serveApiHandler_exports); +var import_execa = __toESM(require("execa")); +var import_project_config = require("@redwoodjs/project-config"); +const apiServerFileHandler = async (argv) => { + const args = ["node", "server.js", "--apiRootPath", argv.apiRootPath]; + if (argv.port) { + args.push("--apiPort", argv.port); + } + await (0, import_execa.default)("yarn", args, { + cwd: (0, import_project_config.getPaths)().api.dist, + stdio: "inherit" + }); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + apiServerFileHandler +}); diff --git a/packages/cli/dist/commands/serveBothHandler.js b/packages/cli/dist/commands/serveBothHandler.js new file mode 100644 index 0000000000..85687c77b2 --- /dev/null +++ b/packages/cli/dist/commands/serveBothHandler.js @@ -0,0 +1,120 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var serveBothHandler_exports = {}; +__export(serveBothHandler_exports, { + bothServerFileHandler: () => bothServerFileHandler, + bothSsrRscServerHandler: () => bothSsrRscServerHandler +}); +module.exports = __toCommonJS(serveBothHandler_exports); +var import_path = __toESM(require("path")); +var import_concurrently = __toESM(require("concurrently")); +var import_execa = __toESM(require("execa")); +var import_apiCLIConfigHandler = require("@redwoodjs/api-server/dist/apiCLIConfigHandler"); +var import_cliHelpers = require("@redwoodjs/api-server/dist/cliHelpers"); +var import_project_config = require("@redwoodjs/project-config"); +var import_telemetry = require("@redwoodjs/telemetry"); +var import_exit = require("../lib/exit"); +const bothServerFileHandler = async (argv) => { + if ((0, import_project_config.getConfig)().experimental?.rsc?.enabled || (0, import_project_config.getConfig)().experimental?.streamingSsr?.enabled) { + logSkippingFastifyWebServer(); + await (0, import_execa.default)("yarn", ["rw-serve-fe"], { + cwd: (0, import_project_config.getPaths)().web.base, + stdio: "inherit", + shell: true + }); + } else { + argv.apiPort ??= (0, import_cliHelpers.getAPIPort)(); + argv.apiHost ??= (0, import_cliHelpers.getAPIHost)(); + argv.webPort ??= (0, import_cliHelpers.getWebPort)(); + argv.webHost ??= (0, import_cliHelpers.getWebHost)(); + const apiProxyTarget = [ + "http://", + argv.apiHost.includes(":") ? `[${argv.apiHost}]` : argv.apiHost, + ":", + argv.apiPort, + argv.apiRootPath + ].join(""); + const { result } = (0, import_concurrently.default)( + [ + { + name: "api", + command: `yarn node ${import_path.default.join("dist", "server.js")} --apiPort ${argv.apiPort} --apiHost ${argv.apiHost} --apiRootPath ${argv.apiRootPath}`, + cwd: (0, import_project_config.getPaths)().api.base, + prefixColor: "cyan" + }, + { + name: "web", + command: `yarn rw-web-server --port ${argv.webPort} --host ${argv.webHost} --api-proxy-target ${apiProxyTarget}`, + cwd: (0, import_project_config.getPaths)().base, + prefixColor: "blue" + } + ], + { + prefix: "{name} |", + timestampFormat: "HH:mm:ss", + handleInput: true + } + ); + try { + await result; + } catch (error) { + if (typeof error?.message !== "undefined") { + (0, import_telemetry.errorTelemetry)( + process.argv, + `Error concurrently starting sides: ${error.message}` + ); + (0, import_exit.exitWithError)(error); + } + } + } +}; +const bothSsrRscServerHandler = async (argv) => { + const apiPromise = (0, import_apiCLIConfigHandler.handler)({ + apiRootPath: argv.apiRootPath, + host: argv.apiHost, + port: argv.apiPort + }); + const fePromise = (0, import_execa.default)("yarn", ["rw-serve-fe"], { + cwd: (0, import_project_config.getPaths)().web.base, + stdio: "inherit", + shell: true + }); + await Promise.all([apiPromise, fePromise]); +}; +function logSkippingFastifyWebServer() { + console.warn(""); + console.warn("\u26A0\uFE0F Skipping Fastify web server \u26A0\uFE0F"); + console.warn("\u26A0\uFE0F Using new RSC server instead \u26A0\uFE0F"); + console.warn(""); +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + bothServerFileHandler, + bothSsrRscServerHandler +}); diff --git a/packages/cli/dist/commands/serveWebHandler.js b/packages/cli/dist/commands/serveWebHandler.js new file mode 100644 index 0000000000..af14d3e5b2 --- /dev/null +++ b/packages/cli/dist/commands/serveWebHandler.js @@ -0,0 +1,46 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var serveWebHandler_exports = {}; +__export(serveWebHandler_exports, { + webSsrServerHandler: () => webSsrServerHandler +}); +module.exports = __toCommonJS(serveWebHandler_exports); +var import_execa = __toESM(require("execa")); +var import_project_config = require("@redwoodjs/project-config"); +const webSsrServerHandler = async () => { + await (0, import_execa.default)("yarn", ["rw-serve-fe"], { + cwd: (0, import_project_config.getPaths)().web.base, + stdio: "inherit", + shell: true + }); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + webSsrServerHandler +}); diff --git a/packages/cli/dist/commands/setup.js b/packages/cli/dist/commands/setup.js new file mode 100644 index 0000000000..ec0403443e --- /dev/null +++ b/packages/cli/dist/commands/setup.js @@ -0,0 +1,61 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var setup_exports = {}; +__export(setup_exports, { + builder: () => builder, + command: () => command, + description: () => description +}); +module.exports = __toCommonJS(setup_exports); +var import_terminal_link = __toESM(require("terminal-link")); +var import_detectProjectRwVersion = __toESM(require("../middleware/detectProjectRwVersion")); +const command = "setup "; +const description = "Initialize project config and install packages"; +const builder = (yargs) => yargs.commandDir("./setup", { + recurse: true, + // @NOTE This regex will ignore all commands nested more than two + // levels deep. + // e.g. /setup/hi.js & setup/hi/hi.js are picked up, but + // setup/hi/hello/bazinga.js will be ignored + // The [/\\] bit is for supporting both windows and unix style paths + // Also take care to not trip up on paths that have "setup" earlier + // in the path by eagerly matching in the start of the regexp + exclude: /.*[/\\]setup[/\\].*[/\\].*[/\\]/ +}).demandCommand().middleware(import_detectProjectRwVersion.default).epilogue( + `Also see the ${(0, import_terminal_link.default)( + "Redwood CLI Reference", + "https://redwoodjs.com/docs/cli-commands#setup" + )}` +); +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description +}); diff --git a/packages/cli/dist/commands/setup/auth/auth.js b/packages/cli/dist/commands/setup/auth/auth.js new file mode 100644 index 0000000000..f9e7d30af6 --- /dev/null +++ b/packages/cli/dist/commands/setup/auth/auth.js @@ -0,0 +1,263 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var auth_exports = {}; +__export(auth_exports, { + builder: () => builder, + command: () => command, + description: () => description +}); +module.exports = __toCommonJS(auth_exports); +var import_path = __toESM(require("path")); +var import_execa = __toESM(require("execa")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_terminal_link = __toESM(require("terminal-link")); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_lib = require("../../../lib/"); +const command = "auth "; +const description = "Set up an auth configuration"; +async function builder(yargs) { + yargs.demandCommand().epilogue( + `Also see the ${(0, import_terminal_link.default)( + "Redwood CLI Reference", + "https://redwoodjs.com/docs/cli-commands#setup-auth" + )}` + ).command(...redirectCommand("ethereum")).command(...redirectCommand("goTrue")).command(...redirectCommand("magicLink")).command(...redirectCommand("nhost")).command(...redirectCommand("okta")).command( + "auth0", + "Set up auth for Auth0", + (yargs2) => (0, import_cli_helpers.standardAuthBuilder)(yargs2), + async (args) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "setup auth auth0", + force: args.force, + verbose: args.verbose + }); + const handler = await getAuthHandler("@redwoodjs/auth-auth0-setup"); + console.log(); + handler(args); + } + ).command( + ["azure-active-directory", "azureActiveDirectory"], + "Set up auth for Azure Active Directory", + (yargs2) => (0, import_cli_helpers.standardAuthBuilder)(yargs2), + async (args) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "setup auth azure-active-directory", + force: args.force, + verbose: args.verbose + }); + const handler = await getAuthHandler( + "@redwoodjs/auth-azure-active-directory-setup" + ); + console.log(); + handler(args); + } + ).command( + "clerk", + "Set up auth for Clerk", + (yargs2) => (0, import_cli_helpers.standardAuthBuilder)(yargs2), + async (args) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "setup auth clerk", + force: args.force, + verbose: args.verbose + }); + const handler = await getAuthHandler("@redwoodjs/auth-clerk-setup"); + console.log(); + handler(args); + } + ).command( + "custom", + "Set up a custom auth provider", + (yargs2) => (0, import_cli_helpers.standardAuthBuilder)(yargs2), + async (args) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "setup auth custom", + force: args.force, + verbose: args.verbose + }); + const handler = await getAuthHandler("@redwoodjs/auth-custom-setup"); + console.log(); + handler(args); + } + ).command( + "dbAuth", + "Set up auth for dbAuth", + (yargs2) => { + return (0, import_cli_helpers.standardAuthBuilder)(yargs2).option("webauthn", { + alias: "w", + default: null, + description: "Include WebAuthn support (TouchID/FaceID)", + type: "boolean" + }); + }, + async (args) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "setup auth dbAuth", + force: args.force, + verbose: args.verbose, + webauthn: args.webauthn + }); + const handler = await getAuthHandler("@redwoodjs/auth-dbauth-setup"); + console.log(); + handler(args); + } + ).command( + "firebase", + "Set up auth for Firebase", + (yargs2) => (0, import_cli_helpers.standardAuthBuilder)(yargs2), + async (args) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "setup auth firebase", + force: args.force, + verbose: args.verbose + }); + const handler = await getAuthHandler("@redwoodjs/auth-firebase-setup"); + console.log(); + handler(args); + } + ).command( + "netlify", + "Set up auth for Netlify", + (yargs2) => (0, import_cli_helpers.standardAuthBuilder)(yargs2), + async (args) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "setup auth netlify", + force: args.force, + verbose: args.verbose + }); + const handler = await getAuthHandler("@redwoodjs/auth-netlify-setup"); + console.log(); + handler(args); + } + ).command( + "supabase", + "Set up auth for Supabase", + (yargs2) => (0, import_cli_helpers.standardAuthBuilder)(yargs2), + async (args) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "setup auth supabase", + force: args.force, + verbose: args.verbose + }); + const handler = await getAuthHandler("@redwoodjs/auth-supabase-setup"); + console.log(); + handler(args); + } + ).command( + "supertokens", + "Set up auth for SuperTokens", + (yargs2) => (0, import_cli_helpers.standardAuthBuilder)(yargs2), + async (args) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "setup auth supertokens", + force: args.force, + verbose: args.verbose + }); + const handler = await getAuthHandler( + "@redwoodjs/auth-supertokens-setup" + ); + console.log(); + handler(args); + } + ); +} +function redirectCommand(provider) { + return [ + provider, + false, + () => { + }, + () => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: `setup auth ${provider}` + }); + console.log(getRedirectMessage(provider)); + } + ]; +} +function getRedirectMessage(provider) { + return `${provider} is no longer supported out of the box. But you can still integrate it yourself with ${(0, import_terminal_link.default)( + "Custom Auth", + "https://redwoodjs.com/docs/canary/auth/custom" + )}`; +} +async function getAuthHandler(module2) { + const packageJsonPath = require.resolve("@redwoodjs/cli/package.json"); + let { version } = import_fs_extra.default.readJSONSync(packageJsonPath); + if (!isInstalled(module2)) { + if (version.includes("+")) { + version = version.split("+")[0]; + } + let packument; + try { + const packumentResponse = await fetch( + `https://registry.npmjs.org/${module2}` + ); + packument = await packumentResponse.json(); + if (packument.error) { + throw new Error(packument.error); + } + } catch (error) { + throw new Error( + `Couldn't fetch packument for ${module2}: ${error.message}` + ); + } + const versionIsPublished = Object.keys(packument.versions).includes(version); + if (!versionIsPublished) { + version = "canary"; + } + await import_execa.default.command(`yarn add -D ${module2}@${version}`, { + stdio: "inherit", + cwd: (0, import_lib.getPaths)().base + }); + } + const setupModule = await import(module2); + return setupModule.default.handler; +} +function isInstalled(module2) { + const { dependencies, devDependencies } = import_fs_extra.default.readJSONSync( + import_path.default.join((0, import_lib.getPaths)().base, "package.json") + ); + const deps = { + ...dependencies, + ...devDependencies + }; + if (deps[module2]) { + return true; + } + return require.resolve.paths(`${module2}/package.json`).some((requireResolvePath) => { + return import_fs_extra.default.existsSync(import_path.default.join(requireResolvePath, module2)); + }); +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description +}); diff --git a/packages/cli/dist/commands/setup/cache/cache.js b/packages/cli/dist/commands/setup/cache/cache.js new file mode 100644 index 0000000000..9be9102394 --- /dev/null +++ b/packages/cli/dist/commands/setup/cache/cache.js @@ -0,0 +1,74 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var cache_exports = {}; +__export(cache_exports, { + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(cache_exports); +var import_terminal_link = __toESM(require("terminal-link")); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +const command = "cache "; +const description = "Sets up an init file for service caching"; +const builder = (yargs) => { + yargs.positional("client", { + choices: ["memcached", "redis"], + description: "Cache client", + type: "string", + required: true + }).option("force", { + alias: "f", + default: false, + description: "Overwrite existing cache.js file", + type: "boolean" + }).epilogue( + `Also see the ${(0, import_terminal_link.default)( + "Redwood CLI Reference", + "https://redwoodjs.com/docs/cli-commands#setup-cache" + )}` + ); +}; +const handler = async (options) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "setup cache", + client: options.client, + force: options.force + }); + const { handler: handler2 } = await import("./cacheHandler.js"); + return handler2(options); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/setup/cache/cacheHandler.js b/packages/cli/dist/commands/setup/cache/cacheHandler.js new file mode 100644 index 0000000000..e62e8bcdfa --- /dev/null +++ b/packages/cli/dist/commands/setup/cache/cacheHandler.js @@ -0,0 +1,100 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var cacheHandler_exports = {}; +__export(cacheHandler_exports, { + handler: () => handler +}); +module.exports = __toCommonJS(cacheHandler_exports); +var import_path = __toESM(require("path")); +var import_chalk = __toESM(require("chalk")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_listr2 = require("listr2"); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_telemetry = require("@redwoodjs/telemetry"); +var import_lib = require("../../../lib"); +var import_colors = __toESM(require("../../../lib/colors")); +var import_project = require("../../../lib/project"); +const CLIENT_PACKAGE_MAP = { + memcached: "memjs", + redis: "redis" +}; +const CLIENT_HOST_MAP = { + memcached: "localhost:11211", + redis: "redis://localhost:6379" +}; +const handler = async ({ client, force }) => { + const extension = import_project.isTypeScriptProject ? "ts" : "js"; + const tasks = new import_listr2.Listr([ + (0, import_lib.addPackagesTask)({ + packages: [CLIENT_PACKAGE_MAP[client]], + side: "api" + }), + { + title: `Writing api/src/lib/cache.js`, + task: () => { + const template = import_fs_extra.default.readFileSync( + import_path.default.join(__dirname, "templates", `${client}.ts.template`) + ).toString(); + return (0, import_lib.writeFile)( + import_path.default.join((0, import_lib.getPaths)().api.lib, `cache.${extension}`), + template, + { + overwriteExisting: force + } + ); + } + }, + (0, import_cli_helpers.addEnvVarTask)( + "CACHE_HOST", + CLIENT_HOST_MAP[client], + `Where your ${client} server lives for service caching` + ), + { + title: "One more thing...", + task: (_ctx, task) => { + task.title = `One more thing... + + ${import_colors.default.green("Check out the Service Cache docs for config and usage:")} + ${import_chalk.default.hex("#e8e8e8")("https://redwoodjs.com/docs/services#caching")} + `; + } + } + ]); + try { + await tasks.run(); + } catch (e) { + (0, import_telemetry.errorTelemetry)(process.argv, e.message); + console.error(import_colors.default.error(e.message)); + process.exit(e?.exitCode || 1); + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + handler +}); diff --git a/packages/cli/dist/commands/setup/cache/templates/memcached.ts.template b/packages/cli/dist/commands/setup/cache/templates/memcached.ts.template new file mode 100644 index 0000000000..66c13ca7b9 --- /dev/null +++ b/packages/cli/dist/commands/setup/cache/templates/memcached.ts.template @@ -0,0 +1,30 @@ +import { + createCache, + InMemoryClient, + MemcachedClient, +} from '@redwoodjs/api/cache' + +import { logger } from './logger' + +const memJsFormattedLogger = { + log: (msg: string) => logger.error(msg), +} + +export let client: InMemoryClient | MemcachedClient + +if (process.env.NODE_ENV === 'test') { + client = new InMemoryClient() +} else { + try { + client = new MemcachedClient(process.env.CACHE_HOST, { + logger: memJsFormattedLogger, + }) + } catch (e) { + logger.error(`Could not connect to cache: ${e.message}`) + } +} + +export const { cache, cacheFindMany, cacheClient, deleteCacheKey } = createCache(client, { + logger, + timeout: 500, +}) diff --git a/packages/cli/dist/commands/setup/cache/templates/redis.ts.template b/packages/cli/dist/commands/setup/cache/templates/redis.ts.template new file mode 100644 index 0000000000..71144cd7a1 --- /dev/null +++ b/packages/cli/dist/commands/setup/cache/templates/redis.ts.template @@ -0,0 +1,24 @@ +import { + createCache, + InMemoryClient, + RedisClient, +} from '@redwoodjs/api/cache' + +import { logger } from './logger' + +export let client: InMemoryClient | RedisClient + +if (process.env.NODE_ENV === 'test') { + client = new InMemoryClient() +} else { + try { + client = new RedisClient({ url: process.env.CACHE_HOST, logger }) + } catch (e) { + logger.error(`Could not connect to cache: ${e.message}`) + } +} + +export const { cache, cacheFindMany, cacheClient, deleteCacheKey } = createCache(client, { + logger, + timeout: 500, +}) diff --git a/packages/cli/dist/commands/setup/custom-web-index/custom-web-index-handler.js b/packages/cli/dist/commands/setup/custom-web-index/custom-web-index-handler.js new file mode 100644 index 0000000000..cd418c2b78 --- /dev/null +++ b/packages/cli/dist/commands/setup/custom-web-index/custom-web-index-handler.js @@ -0,0 +1,93 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var custom_web_index_handler_exports = {}; +__export(custom_web_index_handler_exports, { + handler: () => handler +}); +module.exports = __toCommonJS(custom_web_index_handler_exports); +var import_path = __toESM(require("path")); +var import_chalk = __toESM(require("chalk")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_listr2 = require("listr2"); +var import_telemetry = require("@redwoodjs/telemetry"); +var import_lib = require("../../../lib"); +var import_colors = __toESM(require("../../../lib/colors")); +const handler = async ({ force }) => { + if ((0, import_lib.getPaths)().web.viteConfig) { + console.warn( + import_colors.default.warning("Warning: This command only applies to projects using webpack") + ); + return; + } + const tasks = new import_listr2.Listr( + [ + { + title: "Creating new entry point in `web/src/index.js`.", + task: () => { + const entryPointFile = (0, import_lib.getPaths)().web.index ?? import_path.default.join((0, import_lib.getPaths)().web.src, "index.js"); + return (0, import_lib.writeFile)( + entryPointFile, + import_fs_extra.default.readFileSync( + import_path.default.join( + (0, import_lib.getPaths)().base, + // NOTE we're copying over the index.js before babel transform + "node_modules/@redwoodjs/web/src/entry/index.js" + ) + ).toString().replace("~redwood-app-root", "./App"), + { overwriteExisting: force } + ); + } + }, + { + title: "One more thing...", + task: (_ctx, task) => { + task.title = `One more thing... + + ${import_colors.default.green( + "Quick link to the docs on configuring a custom entry point for your RW app" + )} + ${import_chalk.default.hex("#e8e8e8")("https://redwoodjs.com/docs/custom-web-index")} + `; + } + } + ], + { rendererOptions: { collapseSubtasks: false } } + ); + try { + await tasks.run(); + } catch (e) { + (0, import_telemetry.errorTelemetry)(process.argv, e.message); + console.error(import_colors.default.error(e.message)); + process.exit(e?.exitCode || 1); + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + handler +}); diff --git a/packages/cli/dist/commands/setup/custom-web-index/custom-web-index.js b/packages/cli/dist/commands/setup/custom-web-index/custom-web-index.js new file mode 100644 index 0000000000..5f3050f8be --- /dev/null +++ b/packages/cli/dist/commands/setup/custom-web-index/custom-web-index.js @@ -0,0 +1,62 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var custom_web_index_exports = {}; +__export(custom_web_index_exports, { + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(custom_web_index_exports); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +const command = "custom-web-index"; +const description = "Set up a custom index.js file, so you can customise how Redwood web is mounted in your browser (webpack only)"; +const builder = (yargs) => { + yargs.option("force", { + alias: "f", + default: false, + description: "Overwrite existing index.js file", + type: "boolean" + }); +}; +const handler = async (options) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "setup custom-web-index", + force: options.force + }); + const { handler: handler2 } = await import("./custom-web-index-handler.js"); + return handler2(options); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/setup/deploy/deploy.js b/packages/cli/dist/commands/setup/deploy/deploy.js new file mode 100644 index 0000000000..a222c74432 --- /dev/null +++ b/packages/cli/dist/commands/setup/deploy/deploy.js @@ -0,0 +1,55 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var deploy_exports = {}; +__export(deploy_exports, { + builder: () => builder, + command: () => command, + description: () => description +}); +module.exports = __toCommonJS(deploy_exports); +var import_terminal_link = __toESM(require("terminal-link")); +const command = "deploy "; +const description = "Setup deployment to various targets"; +const builder = (yargs) => yargs.commandDir("./providers", { recurse: true }).demandCommand().option("force", { + alias: "f", + default: false, + description: "Overwrite existing configuration", + type: "boolean" +}).epilogue( + `Also see the ${(0, import_terminal_link.default)( + "Redwood CLI Reference", + "https://redwoodjs.com/docs/cli-commands#setup-deploy-config" + )}` +); +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description +}); diff --git a/packages/cli/dist/commands/setup/deploy/helpers/index.js b/packages/cli/dist/commands/setup/deploy/helpers/index.js new file mode 100644 index 0000000000..bdb1d029d8 --- /dev/null +++ b/packages/cli/dist/commands/setup/deploy/helpers/index.js @@ -0,0 +1,148 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var helpers_exports = {}; +__export(helpers_exports, { + addFilesTask: () => addFilesTask, + addToDotEnvTask: () => addToDotEnvTask, + addToGitIgnoreTask: () => addToGitIgnoreTask, + preRequisiteCheckTask: () => preRequisiteCheckTask, + updateApiURLTask: () => updateApiURLTask +}); +module.exports = __toCommonJS(helpers_exports); +var import_path = __toESM(require("path")); +var import_execa = __toESM(require("execa")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_listr2 = require("listr2"); +var import_lib = require("../../../../lib"); +const REDWOOD_TOML_PATH = import_path.default.join((0, import_lib.getPaths)().base, "redwood.toml"); +const updateApiURLTask = (apiUrl) => { + return { + title: "Updating API URL in redwood.toml...", + task: () => { + const redwoodToml = import_fs_extra.default.readFileSync(REDWOOD_TOML_PATH).toString(); + let newRedwoodToml = redwoodToml; + if (redwoodToml.match(/apiUrl/)) { + newRedwoodToml = newRedwoodToml.replace( + /apiUrl.*/g, + `apiUrl = "${apiUrl}"` + ); + } else if (redwoodToml.match(/\[web\]/)) { + newRedwoodToml = newRedwoodToml.replace( + /\[web\]/, + `[web] + apiUrl = "${apiUrl}"` + ); + } else { + newRedwoodToml += `[web] + apiUrl = "${apiUrl}"`; + } + import_fs_extra.default.writeFileSync(REDWOOD_TOML_PATH, newRedwoodToml); + } + }; +}; +const preRequisiteCheckTask = (preRequisites) => { + return { + title: "Checking pre-requisites", + task: () => new import_listr2.Listr( + preRequisites.map((preReq) => { + return { + title: preReq.title, + task: async () => { + try { + await (0, import_execa.default)(...preReq.command); + } catch (error) { + error.message = error.message + "\n" + preReq.errorMessage; + throw error; + } + } + }; + }) + ) + }; +}; +const addFilesTask = ({ + files, + force = false, + title = "Adding config" +}) => { + return { + title: `${title}...`, + task: () => { + let fileNameToContentMap = {}; + files.forEach((fileData) => { + fileNameToContentMap[fileData.path] = fileData.content; + }); + return (0, import_lib.writeFilesTask)(fileNameToContentMap, { overwriteExisting: force }); + } + }; +}; +const addToGitIgnoreTask = ({ paths }) => { + return { + title: "Updating .gitignore...", + skip: () => { + if (!import_fs_extra.default.existsSync(import_path.default.resolve((0, import_lib.getPaths)().base, ".gitignore"))) { + return "No gitignore present, skipping."; + } + }, + task: async (_ctx, task) => { + const gitIgnore = import_path.default.resolve((0, import_lib.getPaths)().base, ".gitignore"); + const content = import_fs_extra.default.readFileSync(gitIgnore).toString(); + if (paths.every((item) => content.includes(item))) { + task.skip(".gitignore already includes the additions."); + } + import_fs_extra.default.appendFileSync(gitIgnore, ["\n", "# Deployment", ...paths].join("\n")); + } + }; +}; +const addToDotEnvTask = ({ lines }) => { + return { + title: "Updating .env...", + skip: () => { + if (!import_fs_extra.default.existsSync(import_path.default.resolve((0, import_lib.getPaths)().base, ".env"))) { + return "No .env present, skipping."; + } + }, + task: async (_ctx, task) => { + const env = import_path.default.resolve((0, import_lib.getPaths)().base, ".env"); + const content = import_fs_extra.default.readFileSync(env).toString(); + if (lines.every((line) => content.includes(line.split("=")[0]))) { + task.skip(".env already includes the additions."); + } + import_fs_extra.default.appendFileSync(env, lines.join("\n")); + } + }; +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + addFilesTask, + addToDotEnvTask, + addToGitIgnoreTask, + preRequisiteCheckTask, + updateApiURLTask +}); diff --git a/packages/cli/dist/commands/setup/deploy/providers/baremetal.js b/packages/cli/dist/commands/setup/deploy/providers/baremetal.js new file mode 100644 index 0000000000..11f0292efc --- /dev/null +++ b/packages/cli/dist/commands/setup/deploy/providers/baremetal.js @@ -0,0 +1,101 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var baremetal_exports = {}; +__export(baremetal_exports, { + command: () => command, + configFilename: () => configFilename, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(baremetal_exports); +var import_path = __toESM(require("path")); +var import_listr2 = require("listr2"); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_telemetry = require("@redwoodjs/telemetry"); +var import_lib = require("../../../../lib"); +var import_colors = __toESM(require("../../../../lib/colors")); +var import_helpers = require("../helpers"); +var import_baremetal = require("../templates/baremetal"); +const command = "baremetal"; +const description = "Setup Baremetal deploy"; +const configFilename = "deploy.toml"; +const files = [ + { + path: import_path.default.join((0, import_lib.getPaths)().base, configFilename), + content: import_baremetal.DEPLOY + }, + { + path: import_path.default.join((0, import_lib.getPaths)().base, "ecosystem.config.js"), + content: import_baremetal.ECOSYSTEM + }, + { + path: import_path.default.join((0, import_lib.getPaths)().web.src, "maintenance.html"), + content: import_baremetal.MAINTENANCE + } +]; +const notes = [ + "You are almost ready to go BAREMETAL!", + "", + "See https://redwoodjs.com/docs/deploy/baremetal for the remaining", + "config and setup required before you can perform your first deploy." +]; +const handler = async ({ force }) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "setup deploy baremetal", + force + }); + const tasks = new import_listr2.Listr( + [ + (0, import_lib.addPackagesTask)({ + packages: ["node-ssh"], + devDependency: true + }), + (0, import_helpers.addFilesTask)({ + files, + force + }), + (0, import_lib.printSetupNotes)(notes) + ], + { rendererOptions: { collapseSubtasks: false } } + ); + try { + await tasks.run(); + } catch (e) { + (0, import_telemetry.errorTelemetry)(process.argv, e.message); + console.error(import_colors.default.error(e.message)); + process.exit(e?.exitCode || 1); + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + command, + configFilename, + description, + handler +}); diff --git a/packages/cli/dist/commands/setup/deploy/providers/coherence.js b/packages/cli/dist/commands/setup/deploy/providers/coherence.js new file mode 100644 index 0000000000..cda2abc693 --- /dev/null +++ b/packages/cli/dist/commands/setup/deploy/providers/coherence.js @@ -0,0 +1,61 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var coherence_exports = {}; +__export(coherence_exports, { + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(coherence_exports); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +const command = "coherence"; +const description = "Setup Coherence deploy"; +function builder(yargs) { + yargs.option("force", { + description: "Overwrite existing configuration", + type: "boolean", + default: false + }); +} +async function handler(options) { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "setup deploy coherence", + force: options.force + }); + const { handler: handler2 } = await import("./coherenceHandler.js"); + return handler2(options); +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/setup/deploy/providers/coherenceHandler.js b/packages/cli/dist/commands/setup/deploy/providers/coherenceHandler.js new file mode 100644 index 0000000000..cfcfa4c8ea --- /dev/null +++ b/packages/cli/dist/commands/setup/deploy/providers/coherenceHandler.js @@ -0,0 +1,232 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var coherenceHandler_exports = {}; +__export(coherenceHandler_exports, { + handler: () => handler +}); +module.exports = __toCommonJS(coherenceHandler_exports); +var import_path = __toESM(require("path")); +var import_toml = __toESM(require("@iarna/toml")); +var import_internals = require("@prisma/internals"); +var import_fs_extra = __toESM(require("fs-extra")); +var import_listr2 = require("listr2"); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_telemetry = require("@redwoodjs/telemetry"); +var import_lib = require("../../../../lib"); +var import_project = require("../../../../lib/project"); +var import_helpers = require("../helpers"); +const redwoodProjectPaths = (0, import_cli_helpers.getPaths)(); +const EXTENSION = import_cli_helpers.isTypeScriptProject ? "ts" : "js"; +async function handler({ force }) { + try { + const addCoherenceFilesTask = await getAddCoherenceFilesTask(force); + const tasks = new import_listr2.Listr( + [ + addCoherenceFilesTask, + updateRedwoodTOMLTask(), + (0, import_lib.printSetupNotes)([ + "You're ready to deploy to Coherence! \u2728\n", + "Go to https://app.withcoherence.com to create your account and setup your cloud or GitHub connections.", + "Check out the deployment docs at https://docs.withcoherence.com for detailed instructions and more information.\n", + "Reach out to redwood@withcoherence.com with any questions! We're here to support you." + ]) + ], + { rendererOptions: { collapse: false } } + ); + await tasks.run(); + } catch (e) { + (0, import_telemetry.errorTelemetry)(process.argv, e.message); + console.error(import_cli_helpers.colors.error(e.message)); + process.exit(e?.exitCode || 1); + } +} +async function getAddCoherenceFilesTask(force) { + const files = [ + { + path: import_path.default.join(redwoodProjectPaths.api.functions, `health.${EXTENSION}`), + content: coherenceFiles.healthCheck + } + ]; + const coherenceConfigFile = { + path: import_path.default.join(redwoodProjectPaths.base, "coherence.yml") + }; + coherenceConfigFile.content = await getCoherenceConfigFileContent(); + files.push(coherenceConfigFile); + return (0, import_helpers.addFilesTask)({ + title: `Adding coherence.yml and health.${EXTENSION}`, + files, + force + }); +} +async function getCoherenceConfigFileContent() { + const prismaSchema = await (0, import_internals.getSchema)(redwoodProjectPaths.api.dbSchema); + const prismaConfig = await (0, import_internals.getConfig)({ datamodel: prismaSchema }); + let db = prismaConfig.datasources[0].activeProvider; + if (!SUPPORTED_DATABASES.includes(db)) { + throw new Error( + [ + `Coherence doesn't support the "${db}" provider in your Prisma schema.`, + `To proceed, switch to one of the following: ${SUPPORTED_DATABASES.join( + ", " + )}.` + ].join("\n") + ); + } + if (db === "postgresql") { + db = "postgres"; + } + const apiProdCommand = ["yarn", "rw", "build", "api", "&&"]; + if ((0, import_project.serverFileExists)()) { + apiProdCommand.push( + "yarn", + "node", + "api/dist/server.js", + "--apiRootPath=/api" + ); + } else { + apiProdCommand.push("yarn", "rw", "serve", "api", "--apiRootPath=/api"); + } + return coherenceFiles.yamlTemplate({ + db, + apiProdCommand: `[${apiProdCommand.map((cmd) => `"${cmd}"`).join(", ")}]` + }); +} +const SUPPORTED_DATABASES = ["mysql", "postgresql"]; +function updateRedwoodTOMLTask() { + return { + title: "Updating redwood.toml...", + task: () => { + const redwoodTOMLPath = import_path.default.join( + redwoodProjectPaths.base, + "redwood.toml" + ); + let redwoodTOMLContent = import_fs_extra.default.readFileSync(redwoodTOMLPath, "utf-8"); + const redwoodTOMLObject = import_toml.default.parse(redwoodTOMLContent); + if (!redwoodTOMLObject.web.host) { + const [beforeWeb, afterWeb] = redwoodTOMLContent.split(/\[web\]\s/); + redwoodTOMLContent = [ + beforeWeb, + '[web]\n host = "0.0.0.0"\n', + afterWeb + ].join(""); + } + if (!redwoodTOMLObject.api.host) { + const [beforeApi, afterApi] = redwoodTOMLContent.split(/\[api\]\s/); + redwoodTOMLContent = [ + beforeApi, + '[api]\n host = "0.0.0.0"\n', + afterApi + ].join(""); + } + redwoodTOMLContent = redwoodTOMLContent.replaceAll( + HOST_REGEXP, + (match, spaceBeforeAssign, spaceAfterAssign) => ["host", spaceBeforeAssign, "=", spaceAfterAssign, '"0.0.0.0"'].join( + "" + ) + ); + redwoodTOMLContent = redwoodTOMLContent.replace( + API_URL_REGEXP, + (match, spaceBeforeAssign, spaceAfterAssign) => ["apiUrl", spaceBeforeAssign, "=", spaceAfterAssign, '"/api"'].join( + "" + ) + ); + redwoodTOMLContent = redwoodTOMLContent.replaceAll( + PORT_REGEXP, + (_match, spaceBeforeAssign, spaceAfterAssign, port) => [ + "port", + spaceBeforeAssign, + "=", + spaceAfterAssign, + `"\${PORT:${port}}"` + ].join("") + ); + import_fs_extra.default.writeFileSync(redwoodTOMLPath, redwoodTOMLContent); + } + }; +} +const HOST_REGEXP = /host(\s*)=(\s*)\".+\"/g; +const API_URL_REGEXP = /apiUrl(\s*)=(\s*)\".+\"/; +const PORT_REGEXP = /port(\s*)=(\s*)(?\d{4})/g; +const coherenceFiles = { + yamlTemplate({ db, apiProdCommand }) { + return `api: + type: backend + url_path: "/api" + prod: + command: ${apiProdCommand} + dev: + command: ["yarn", "rw", "build", "api", "&&", "yarn", "rw", "dev", "api", "--apiRootPath=/api"] + local_packages: ["node_modules"] + + system: + cpu: 2 + memory: 2G + health_check: "/api/health" + + resources: + - name: ${import_path.default.basename(redwoodProjectPaths.base)}-db + engine: ${db} + version: 13 + type: database + ${db === "postgres" ? "adapter: postgresql" : ""} + + # If you use data migrations, use the following instead: + # migration: ["yarn", "rw", "prisma", "migrate", "deploy", "&&", "yarn", "rw", "data-migrate", "up"] + migration: ["yarn", "rw", "prisma", "migrate", "deploy"] + +web: + type: frontend + assets_path: "web/dist" + prod: + command: ["yarn", "rw", "serve", "web"] + dev: + command: ["yarn", "rw", "dev", "web", "--fwd=\\"--allowed-hosts all\\""] + + # Heads up: Redwood's prerender doesn't work with Coherence yet. + # For current status and updates, see https://github.com/redwoodjs/redwood/issues/8333. + build: ["yarn", "rw", "build", "web", "--no-prerender"] + local_packages: ["node_modules"] + + system: + cpu: 2 + memory: 2G +`; + }, + healthCheck: `// Coherence health check +export const handler = async () => { + return { + statusCode: 200, + } +} +` +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + handler +}); diff --git a/packages/cli/dist/commands/setup/deploy/providers/edgio.js b/packages/cli/dist/commands/setup/deploy/providers/edgio.js new file mode 100644 index 0000000000..173b9377c4 --- /dev/null +++ b/packages/cli/dist/commands/setup/deploy/providers/edgio.js @@ -0,0 +1,106 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var edgio_exports = {}; +__export(edgio_exports, { + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(edgio_exports); +var import_fs_extra = __toESM(require("fs-extra")); +var import_listr2 = require("listr2"); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_telemetry = require("@redwoodjs/telemetry"); +var import_lib = require("../../../../lib"); +var import_colors = __toESM(require("../../../../lib/colors")); +var import_edgio = require("../../../deploy/edgio"); +var import_helpers = require("../helpers"); +const command = "edgio"; +const description = "Setup Edgio deploy"; +const notes = [ + "You are almost ready to deploy to Edgio!", + "", + "See https://redwoodjs.com/docs/deploy#edgio-deploy for the remaining", + "config and setup required before you can perform your first deploy." +]; +const prismaBinaryTargetAdditions = () => { + const content = import_fs_extra.default.readFileSync((0, import_lib.getPaths)().api.dbSchema).toString(); + if (!content.includes("rhel-openssl-1.0.x")) { + const result = content.replace( + /binaryTargets =.*\n/, + `binaryTargets = ["native", "rhel-openssl-1.0.x"] +` + ); + import_fs_extra.default.writeFileSync((0, import_lib.getPaths)().api.dbSchema, result); + } +}; +const handler = async () => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "setup deploy edgio" + }); + const tasks = new import_listr2.Listr( + [ + (0, import_lib.addPackagesTask)({ + packages: ["@edgio/cli"], + devDependency: true + }), + (0, import_helpers.preRequisiteCheckTask)([ + { + title: "Checking if Edgio is installed...", + command: ["yarn", ["edgio", "--version"]], + errorMessage: import_edgio.ERR_MESSAGE_MISSING_CLI + }, + { + title: "Initializing with Edgio", + command: ["yarn", ["edgio", "init"]], + errorMessage: import_edgio.ERR_MESSAGE_NOT_INITIALIZED + } + ]), + { + title: "Adding necessary Prisma binaries...", + task: () => prismaBinaryTargetAdditions() + }, + (0, import_lib.printSetupNotes)(notes) + ], + { rendererOptions: { collapseSubtasks: false } } + ); + try { + await tasks.run(); + } catch (e) { + (0, import_telemetry.errorTelemetry)(process.argv, e.message); + console.error(import_colors.default.error(e.message)); + process.exit(e?.exitCode || 1); + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/setup/deploy/providers/flightcontrol.js b/packages/cli/dist/commands/setup/deploy/providers/flightcontrol.js new file mode 100644 index 0000000000..24523704ec --- /dev/null +++ b/packages/cli/dist/commands/setup/deploy/providers/flightcontrol.js @@ -0,0 +1,338 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var flightcontrol_exports = {}; +__export(flightcontrol_exports, { + alias: () => alias, + builder: () => builder, + command: () => command, + description: () => description, + getFlightcontrolJson: () => getFlightcontrolJson, + handler: () => handler +}); +module.exports = __toCommonJS(flightcontrol_exports); +var import_os = require("os"); +var import_path = __toESM(require("path")); +var import_internals = require("@prisma/internals"); +var import_fs_extra = __toESM(require("fs-extra")); +var import_listr2 = require("listr2"); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_telemetry = require("@redwoodjs/telemetry"); +var import_lib = require("../../../../lib"); +var import_colors = __toESM(require("../../../../lib/colors")); +var import_helpers = require("../helpers"); +var import_flightcontrol = require("../templates/flightcontrol"); +const command = "flightcontrol"; +const alias = "fc"; +const description = "Setup Flightcontrol deploy"; +const getFlightcontrolJson = async (database) => { + if (database === "none") { + return { + path: import_path.default.join((0, import_lib.getPaths)().base, "flightcontrol.json"), + content: import_flightcontrol.flightcontrolConfig + }; + } + if (!import_fs_extra.default.existsSync(import_path.default.join((0, import_lib.getPaths)().base, "api/db/schema.prisma"))) { + throw new Error("Could not find prisma schema at 'api/db/schema.prisma'"); + } + const schema = await (0, import_internals.getSchema)( + import_path.default.join((0, import_lib.getPaths)().base, "api/db/schema.prisma") + ); + const config = await (0, import_internals.getConfig)({ datamodel: schema }); + const detectedDatabase = config.datasources[0].activeProvider; + if (detectedDatabase === database) { + let dbService; + switch (database) { + case "postgresql": + dbService = import_flightcontrol.postgresDatabaseService; + break; + case "mysql": + dbService = import_flightcontrol.mysqlDatabaseService; + break; + default: + throw new Error(` + Unexpected datasource provider found: ${database}`); + } + return { + path: import_path.default.join((0, import_lib.getPaths)().base, "flightcontrol.json"), + content: { + ...import_flightcontrol.flightcontrolConfig, + environments: [ + { + ...import_flightcontrol.flightcontrolConfig.environments[0], + services: [ + ...import_flightcontrol.flightcontrolConfig.environments[0].services.map((service) => { + if (service.id === "redwood-api") { + return { + ...service, + envVariables: { + ...service.envVariables, + ...import_flightcontrol.databaseEnvVariables + } + }; + } + return service; + }), + dbService + ] + } + ] + } + }; + } else { + throw new Error(` + Prisma datasource provider is detected to be ${detectedDatabase}. + + Update your schema.prisma provider to be postgresql or mysql, then run + yarn rw prisma migrate dev + yarn rw setup deploy flightcontrol + `); + } +}; +const updateGraphQLFunction = () => { + return { + title: "Adding CORS config to createGraphQLHandler...", + task: (_ctx) => { + const graphqlTsPath = import_path.default.join( + (0, import_lib.getPaths)().base, + "api/src/functions/graphql.ts" + ); + const graphqlJsPath = import_path.default.join( + (0, import_lib.getPaths)().base, + "api/src/functions/graphql.js" + ); + let graphqlFunctionsPath; + if (import_fs_extra.default.existsSync(graphqlTsPath)) { + graphqlFunctionsPath = graphqlTsPath; + } else if (import_fs_extra.default.existsSync(graphqlJsPath)) { + graphqlFunctionsPath = graphqlJsPath; + } else { + console.log(` + Couldn't find graphql handler in api/src/functions/graphql.js. + You'll have to add the following cors config manually: + + cors: { origin: process.env.REDWOOD_WEB_URL, credentials: true} + `); + return; + } + const graphqlContent = import_fs_extra.default.readFileSync(graphqlFunctionsPath, "utf8").split(import_os.EOL); + const graphqlHanderIndex = graphqlContent.findIndex( + (line) => line.includes("createGraphQLHandler({") + ); + if (graphqlHanderIndex === -1) { + console.log(` + Couldn't find graphql handler in api/src/functions/graphql.js. + You'll have to add the following cors config manually: + + cors: { origin: process.env.REDWOOD_WEB_URL, credentials: true} + `); + return; + } + graphqlContent.splice( + graphqlHanderIndex + 1, + 0, + " cors: { origin: process.env.REDWOOD_WEB_URL, credentials: true }," + ); + import_fs_extra.default.writeFileSync(graphqlFunctionsPath, graphqlContent.join(import_os.EOL)); + } + }; +}; +const updateDbAuth = () => { + return { + title: "Updating dbAuth cookie config (if used)...", + task: (_ctx) => { + const authTsPath = import_path.default.join((0, import_lib.getPaths)().base, "api/src/functions/auth.ts"); + const authJsPath = import_path.default.join((0, import_lib.getPaths)().base, "api/src/functions/auth.js"); + let authFnPath; + if (import_fs_extra.default.existsSync(authTsPath)) { + authFnPath = authTsPath; + } else if (import_fs_extra.default.existsSync(authJsPath)) { + authFnPath = authJsPath; + } else { + console.log(`Skipping, did not detect api/src/functions/auth.js`); + return; + } + const authContent = import_fs_extra.default.readFileSync(authFnPath, "utf8").split(import_os.EOL); + const sameSiteLineIndex = authContent.findIndex( + (line) => line.match(/SameSite:.*,/) + ); + if (sameSiteLineIndex === -1) { + console.log(` + Couldn't find cookie SameSite config in api/src/functions/auth.js. + + You need to ensure SameSite is set to "None" + `); + return; + } + authContent[sameSiteLineIndex] = ` SameSite: process.env.NODE_ENV === 'development' ? 'Strict' : 'None',`; + const dbHandlerIndex = authContent.findIndex( + (line) => line.includes("new DbAuthHandler(") + ); + if (dbHandlerIndex === -1) { + console.log(` + Couldn't find DbAuthHandler in api/src/functions/auth.js. + You'll have to add the following cors config manually: + + cors: { origin: process.env.REDWOOD_WEB_URL, credentials: true} + `); + return; + } + authContent.splice( + dbHandlerIndex + 1, + 0, + " cors: { origin: process.env.REDWOOD_WEB_URL, credentials: true }," + ); + import_fs_extra.default.writeFileSync(authFnPath, authContent.join(import_os.EOL)); + } + }; +}; +const updateApp = () => { + return { + title: "Updating App.jsx fetch config...", + task: (_ctx) => { + const appTsPath = import_path.default.join((0, import_lib.getPaths)().base, "web/src/App.tsx"); + const appJsPath = import_path.default.join((0, import_lib.getPaths)().base, "web/src/App.jsx"); + let appPath; + if (import_fs_extra.default.existsSync(appTsPath)) { + appPath = appTsPath; + } else if (import_fs_extra.default.existsSync(appJsPath)) { + appPath = appJsPath; + } else { + console.log(`Skipping, did not detect web/src/App.jsx|tsx`); + return; + } + const appContent = import_fs_extra.default.readFileSync(appPath, "utf8").split(import_os.EOL); + const authLineIndex = appContent.findIndex( + (line) => line.includes(" in web/src/App.js + If (and when) you use *dbAuth*, you'll have to add the following fetch config to : + + config={{ fetchConfig: { credentials: 'include' } }} + `); + } else if (appContent.toString().match(/dbAuth/)) { + appContent[authLineIndex] = ` +`; + } + const gqlLineIndex = appContent.findIndex( + (line) => line.includes(" +`; + } + import_fs_extra.default.writeFileSync(appPath, appContent.join(import_os.EOL)); + } + }; +}; +const addToDotEnvDefaultTask = () => { + return { + title: "Updating .env.defaults...", + skip: () => { + if (!import_fs_extra.default.existsSync(import_path.default.resolve((0, import_lib.getPaths)().base, ".env.defaults"))) { + return ` + WARNING: could not update .env.defaults + + You'll have to add the following env var manually: + + REDWOOD_API_URL=/.redwood/functions + `; + } + }, + task: async (_ctx) => { + const env = import_path.default.resolve((0, import_lib.getPaths)().base, ".env.defaults"); + const line = "\n\nREDWOOD_API_URL=/.redwood/functions\n"; + import_fs_extra.default.appendFileSync(env, line); + } + }; +}; +const builder = (yargs) => yargs.option("database", { + alias: "d", + choices: ["none", "postgresql", "mysql"], + description: "Database deployment for Flightcontrol only", + default: "postgresql", + type: "string" +}); +const notes = [ + "You are ready to deploy to Flightcontrol!\n", + "\u{1F449} Create your project at https://app.flightcontrol.dev/signup?ref=redwood\n", + "Check out the deployment docs at https://app.flightcontrol.dev/docs for detailed instructions\n", + "NOTE: If you are using yarn v1, remove the installCommand's from flightcontrol.json" +]; +const handler = async ({ force, database }) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "setup deploy flightcontrol", + force, + database + }); + const tasks = new import_listr2.Listr( + [ + { + title: "Adding flightcontrol.json", + task: async () => { + const fileData = await getFlightcontrolJson(database); + let files = {}; + files[fileData.path] = JSON.stringify(fileData.content, null, 2); + return (0, import_lib.writeFilesTask)(files, { overwriteExisting: force }); + } + }, + updateGraphQLFunction(), + updateDbAuth(), + updateApp(), + (0, import_helpers.updateApiURLTask)("${REDWOOD_API_URL}"), + addToDotEnvDefaultTask(), + (0, import_lib.printSetupNotes)(notes) + ], + { rendererOptions: { collapseSubtasks: false } } + ); + try { + await tasks.run(); + } catch (e) { + (0, import_telemetry.errorTelemetry)(process.argv, e.message); + console.error(import_colors.default.error(e.message)); + process.exit(e?.exitCode || 1); + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + alias, + builder, + command, + description, + getFlightcontrolJson, + handler +}); diff --git a/packages/cli/dist/commands/setup/deploy/providers/netlify.js b/packages/cli/dist/commands/setup/deploy/providers/netlify.js new file mode 100644 index 0000000000..a0d8d27019 --- /dev/null +++ b/packages/cli/dist/commands/setup/deploy/providers/netlify.js @@ -0,0 +1,82 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var netlify_exports = {}; +__export(netlify_exports, { + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(netlify_exports); +var import_path = __toESM(require("path")); +var import_listr2 = require("listr2"); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_telemetry = require("@redwoodjs/telemetry"); +var import_lib = require("../../../../lib"); +var import_colors = __toESM(require("../../../../lib/colors")); +var import_helpers = require("../helpers"); +var import_netlify = require("../templates/netlify"); +const command = "netlify"; +const description = "Setup Netlify deploy"; +const files = [ + { + path: import_path.default.join((0, import_lib.getPaths)().base, "netlify.toml"), + content: import_netlify.NETLIFY_TOML + } +]; +const notes = [ + "You are ready to deploy to Netlify!", + "See: https://redwoodjs.com/docs/deploy/netlify" +]; +const handler = async ({ force }) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "setup deploy netlify", + force + }); + const tasks = new import_listr2.Listr( + [ + (0, import_helpers.updateApiURLTask)("/.netlify/functions"), + (0, import_helpers.addFilesTask)({ files, force }), + (0, import_lib.printSetupNotes)(notes) + ], + { rendererOptions: { collapseSubtasks: false } } + ); + try { + await tasks.run(); + } catch (e) { + (0, import_telemetry.errorTelemetry)(process.argv, e.message); + console.error(import_colors.default.error(e.message)); + process.exit(e?.exitCode || 1); + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/setup/deploy/providers/render.js b/packages/cli/dist/commands/setup/deploy/providers/render.js new file mode 100644 index 0000000000..aac583ed92 --- /dev/null +++ b/packages/cli/dist/commands/setup/deploy/providers/render.js @@ -0,0 +1,152 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var render_exports = {}; +__export(render_exports, { + builder: () => builder, + command: () => command, + description: () => description, + getRenderYamlContent: () => getRenderYamlContent, + handler: () => handler +}); +module.exports = __toCommonJS(render_exports); +var import_path = __toESM(require("path")); +var import_internals = require("@prisma/internals"); +var import_fs_extra = __toESM(require("fs-extra")); +var import_listr2 = require("listr2"); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_telemetry = require("@redwoodjs/telemetry"); +var import_lib = require("../../../../lib"); +var import_colors = __toESM(require("../../../../lib/colors")); +var import_helpers = require("../helpers"); +var import_render = require("../templates/render"); +const command = "render"; +const description = "Setup Render deploy"; +const getRenderYamlContent = async (database) => { + if (database === "none") { + return { + path: import_path.default.join((0, import_lib.getPaths)().base, "render.yaml"), + content: (0, import_render.RENDER_YAML)("") + }; + } + if (!import_fs_extra.default.existsSync("api/db/schema.prisma")) { + throw new Error("Could not find prisma schema at 'api/db/schema.prisma'"); + } + const schema = await (0, import_internals.getSchema)("api/db/schema.prisma"); + const config = await (0, import_internals.getConfig)({ datamodel: schema }); + const detectedDatabase = config.datasources[0].activeProvider; + if (detectedDatabase === database) { + switch (database) { + case "postgresql": + return { + path: import_path.default.join((0, import_lib.getPaths)().base, "render.yaml"), + content: (0, import_render.RENDER_YAML)(import_render.POSTGRES_YAML) + }; + case "sqlite": + return { + path: import_path.default.join((0, import_lib.getPaths)().base, "render.yaml"), + content: (0, import_render.RENDER_YAML)(import_render.SQLITE_YAML) + }; + default: + throw new Error(` + Unexpected datasource provider found: ${database}`); + } + } else { + throw new Error(` + Prisma datasource provider is detected to be ${detectedDatabase}. + + Option 1: Update your schema.prisma provider to be ${database}, then run + yarn rw prisma migrate dev + yarn rw setup deploy render --database ${database} + + Option 2: Rerun setup deploy command with current schema.prisma provider: + yarn rw setup deploy render --database ${detectedDatabase}`); + } +}; +const builder = (yargs) => yargs.option("database", { + alias: "d", + choices: ["none", "postgresql", "sqlite"], + description: "Database deployment for Render only", + default: "postgresql", + type: "string" +}); +const notes = [ + "You are ready to deploy to Render!\n", + "Go to https://dashboard.render.com/iacs to create your account and deploy to Render", + "Check out the deployment docs at https://render.com/docs/deploy-redwood for detailed instructions", + "Note: After first deployment to Render update the rewrite rule destination in `./render.yaml`" +]; +const additionalFiles = [ + { + path: import_path.default.join((0, import_lib.getPaths)().base, "api/src/functions/healthz.js"), + content: import_render.RENDER_HEALTH_CHECK + } +]; +const handler = async ({ force, database }) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "setup deploy render", + force, + database + }); + const tasks = new import_listr2.Listr( + [ + { + title: "Adding render.yaml", + task: async () => { + const fileData = await getRenderYamlContent(database); + let files = {}; + files[fileData.path] = fileData.content; + return (0, import_lib.writeFilesTask)(files, { overwriteExisting: force }); + } + }, + (0, import_helpers.updateApiURLTask)("/.redwood/functions"), + // Add health check api function + (0, import_helpers.addFilesTask)({ + files: additionalFiles, + force + }), + (0, import_lib.printSetupNotes)(notes) + ], + { rendererOptions: { collapseSubtasks: false } } + ); + try { + await tasks.run(); + } catch (e) { + (0, import_telemetry.errorTelemetry)(process.argv, e.message); + console.error(import_colors.default.error(e.message)); + process.exit(e?.exitCode || 1); + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + getRenderYamlContent, + handler +}); diff --git a/packages/cli/dist/commands/setup/deploy/providers/serverless.js b/packages/cli/dist/commands/setup/deploy/providers/serverless.js new file mode 100644 index 0000000000..7323421349 --- /dev/null +++ b/packages/cli/dist/commands/setup/deploy/providers/serverless.js @@ -0,0 +1,178 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var serverless_exports = {}; +__export(serverless_exports, { + aliases: () => aliases, + command: () => command, + description: () => description, + handler: () => handler, + notes: () => notes +}); +module.exports = __toCommonJS(serverless_exports); +var import_path = __toESM(require("path")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_listr2 = require("listr2"); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_telemetry = require("@redwoodjs/telemetry"); +var import_lib = require("../../../../lib"); +var import_colors = __toESM(require("../../../../lib/colors")); +var import_helpers = require("../helpers"); +var import_api = require("../templates/serverless/api"); +var import_web = require("../templates/serverless/web"); +const command = "serverless"; +const description = "[DEPRECATED]\nSetup Serverless Framework AWS deploy\nFor more information:\nhttps://redwoodjs.com/docs/deploy/serverless"; +const aliases = ["aws-serverless"]; +const notes = [ + import_colors.default.error("DEPRECATED option not officially supported"), + "", + "For more information:", + "https://redwoodjs.com/docs/deploy/serverless", + "", + "", + import_colors.default.green("You're almost ready to deploy using the Serverless framework!"), + "", + "\u2022 See https://redwoodjs.com/docs/deploy#serverless-deploy for more info. If you ", + " want to give it a shot, open your `.env` file and add your AWS credentials,", + " then run: ", + "", + " yarn rw deploy serverless --first-run", + "", + " For subsequent deploys you can just run `yarn rw deploy serverless`.", + "", + "\u2022 If you want to use the Serverless Dashboard to manage your app, plug in", + " the values for `org` and `app` in `web/serverless.yml` and `api/serverless.yml`", + "", + "\u2022 If you haven't already, familiarize yourself with the docs for your", + " preferred provider: https://www.serverless.com/framework/docs/providers" +]; +const projectDevPackages = [ + "serverless", + "serverless-lift", + "@vercel/nft", + "archiver", + "fs-extra" +]; +const files = [ + { + path: import_path.default.join((0, import_lib.getPaths)().api.base, "serverless.yml"), + content: import_api.SERVERLESS_API_YML + }, + { + path: import_path.default.join((0, import_lib.getPaths)().web.base, "serverless.yml"), + content: import_web.SERVERLESS_WEB_YML + } +]; +const prismaBinaryTargetAdditions = () => { + const content = import_fs_extra.default.readFileSync((0, import_lib.getPaths)().api.dbSchema).toString(); + if (!content.includes("rhel-openssl-1.0.x")) { + const result = content.replace( + /binaryTargets =.*\n/, + `binaryTargets = ["native", "rhel-openssl-1.0.x"] +` + ); + import_fs_extra.default.writeFileSync((0, import_lib.getPaths)().api.dbSchema, result); + } +}; +const updateRedwoodTomlTask = () => { + return { + title: "Updating redwood.toml apiUrl...", + task: () => { + const configPath = import_path.default.join((0, import_lib.getPaths)().base, "redwood.toml"); + const content = import_fs_extra.default.readFileSync(configPath).toString(); + const newContent = content.replace( + /apiUrl.*?\n/m, + 'apiUrl = "${API_URL:/api}" # Set API_URL in production to the Serverless deploy endpoint of your api service, see https://redwoodjs.com/docs/deploy/serverless-deploy\n' + ); + import_fs_extra.default.writeFileSync(configPath, newContent); + } + }; +}; +const handler = async ({ force }) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "setup deploy serverless", + force + }); + const [serverless, serverlessLift, ...rest] = projectDevPackages; + const tasks = new import_listr2.Listr( + [ + (0, import_lib.addPackagesTask)({ + packages: [serverless, ...rest], + devDependency: true + }), + (0, import_lib.addPackagesTask)({ + packages: [serverless, serverlessLift], + side: "web", + devDependency: true + }), + (0, import_lib.addPackagesTask)({ + packages: [serverless], + side: "api", + devDependency: true + }), + (0, import_helpers.addFilesTask)({ + files, + force + }), + updateRedwoodTomlTask(), + (0, import_helpers.addToGitIgnoreTask)({ + paths: [".serverless"] + }), + (0, import_helpers.addToDotEnvTask)({ + lines: [ + "AWS_ACCESS_KEY_ID=", + "AWS_SECRET_ACCESS_KEY=" + ] + }), + { + title: "Adding necessary Prisma binaries...", + task: () => prismaBinaryTargetAdditions() + }, + (0, import_lib.printSetupNotes)(notes) + ], + { + exitOnError: true, + rendererOptions: { collapseSubtasks: false } + } + ); + try { + await tasks.run(); + } catch (e) { + (0, import_telemetry.errorTelemetry)(process.argv, e.message); + console.error(import_colors.default.error(e.message)); + process.exit(e?.exitCode || 1); + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + aliases, + command, + description, + handler, + notes +}); diff --git a/packages/cli/dist/commands/setup/deploy/providers/vercel.js b/packages/cli/dist/commands/setup/deploy/providers/vercel.js new file mode 100644 index 0000000000..bcf425ac22 --- /dev/null +++ b/packages/cli/dist/commands/setup/deploy/providers/vercel.js @@ -0,0 +1,68 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var vercel_exports = {}; +__export(vercel_exports, { + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(vercel_exports); +var import_listr2 = require("listr2"); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_telemetry = require("@redwoodjs/telemetry"); +var import_lib = require("../../../../lib"); +var import_colors = __toESM(require("../../../../lib/colors")); +var import_helpers = require("../helpers"); +const command = "vercel"; +const description = "Setup Vercel deploy"; +const notes = [ + "You are ready to deploy to Vercel!", + "See: https://redwoodjs.com/docs/deploy#vercel-deploy" +]; +const handler = async () => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "setup deploy vercel" + }); + const tasks = new import_listr2.Listr([(0, import_helpers.updateApiURLTask)("/api"), (0, import_lib.printSetupNotes)(notes)], { + rendererOptions: { collapseSubtasks: false } + }); + try { + await tasks.run(); + } catch (e) { + (0, import_telemetry.errorTelemetry)(process.argv, e.message); + console.error(import_colors.default.error(e.message)); + process.exit(e?.exitCode || 1); + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/setup/deploy/templates/baremetal.js b/packages/cli/dist/commands/setup/deploy/templates/baremetal.js new file mode 100644 index 0000000000..2b39b15093 --- /dev/null +++ b/packages/cli/dist/commands/setup/deploy/templates/baremetal.js @@ -0,0 +1,153 @@ +"use strict"; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var baremetal_exports = {}; +__export(baremetal_exports, { + DEPLOY: () => DEPLOY, + ECOSYSTEM: () => ECOSYSTEM, + MAINTENANCE: () => MAINTENANCE +}); +module.exports = __toCommonJS(baremetal_exports); +const ECOSYSTEM = `module.exports = { + apps: [ + { + name: 'serve', + cwd: 'current', + script: 'node_modules/.bin/rw', + args: 'serve', + instances: 'max', + exec_mode: 'cluster', + wait_ready: true, + listen_timeout: 10000, + }, + ], +}`; +const DEPLOY = `# This file contains config for a baremetal deployment +# +# SSH connection options include: +# +# * host - the remote server hostname/IP +# * port - defaults to 22 +# * username - required, the user you're connecting as +# * password - only set if you're not using key-based authentication +# * privateKey - a Buffer containing the private key (use this _or_ 'privateKeyPath', not both) +# * privateKeyPath - local file path to a private key that will be sent with the connection request +# * passphrase - used if your private key has a passphrase +# * agentForward - set to \`true\` to forward the client machine's ssh credentials +# +# See https://redwoodjs.com/docs/deploy/baremetal for more info + +[[production.servers]] +host = "server.com" +username = "user" +agentForward = true +sides = ["api","web"] +packageManagerCommand = "yarn" +monitorCommand = "pm2" +path = "/var/www/app" +processNames = ["serve"] +repo = "git@github.com:myorg/myapp.git" +branch = "main" +keepReleases = 5 + +# If you have separate api and web servers: +# +# [[production.servers]] +# host = "api.server.com" +# username = "user" +# agentForward = true +# sides = ["api"] +# path = "/var/www/app" +# repo = "git@github.com:redwoodjs/redwood.git" +# branch = "main" +# processNames = ["api"] +# +# [[production.servers]] +# host = "web.server.com" +# username = "user" +# agentForward = true +# sides = ["web"] +# path = "/var/www/app" +# repo = "git@github.com:redwoodjs/redwood.git" +# branch = "main" +# migrate = false # only one server in a cluster needs to migrate +# processNames = ["web"] +`; +const MAINTENANCE = ` + + + + + Maintenance + + + +
+
+

+ Maintenance Mode: Be Back Soon +

+
+
+ + +`; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + DEPLOY, + ECOSYSTEM, + MAINTENANCE +}); diff --git a/packages/cli/dist/commands/setup/deploy/templates/flightcontrol.js b/packages/cli/dist/commands/setup/deploy/templates/flightcontrol.js new file mode 100644 index 0000000000..38835f5c21 --- /dev/null +++ b/packages/cli/dist/commands/setup/deploy/templates/flightcontrol.js @@ -0,0 +1,108 @@ +"use strict"; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var flightcontrol_exports = {}; +__export(flightcontrol_exports, { + databaseEnvVariables: () => databaseEnvVariables, + flightcontrolConfig: () => flightcontrolConfig, + mysqlDatabaseService: () => mysqlDatabaseService, + postgresDatabaseService: () => postgresDatabaseService +}); +module.exports = __toCommonJS(flightcontrol_exports); +const flightcontrolConfig = { + $schema: "https://app.flightcontrol.dev/schema.json", + environments: [ + { + id: "development", + name: "Development", + region: "us-east-1", + source: { + branch: "main" + }, + services: [ + { + id: "redwood-api", + name: "Redwood API", + type: "fargate", + buildType: "nixpacks", + cpu: 0.5, + memory: 1, + installCommand: "corepack enable && yarn install", + buildCommand: "yarn rw deploy flightcontrol api", + startCommand: "yarn rw deploy flightcontrol api --serve", + port: 8911, + healthCheckPath: "/graphql/health", + envVariables: { + REDWOOD_WEB_URL: { + fromService: { id: "redwood-web", value: "origin" } + } + } + }, + { + id: "redwood-web", + name: "Redwood Web", + type: "static", + buildType: "nixpacks", + singlePageApp: true, + installCommand: "corepack enable && yarn install", + buildCommand: "yarn rw deploy flightcontrol web", + outputDirectory: "web/dist", + envVariables: { + REDWOOD_API_URL: { + fromService: { id: "redwood-api", value: "origin" } + } + } + } + ] + } + ] +}; +const postgresDatabaseService = { + id: "db", + name: "Database", + type: "rds", + engine: "postgres", + engineVersion: "12", + instanceSize: "db.t2.micro", + port: 5432, + storage: 20, + private: false +}; +const mysqlDatabaseService = { + id: "db", + name: "Mysql", + type: "rds", + engine: "mysql", + engineVersion: "8", + instanceSize: "db.t2.micro", + port: 3306, + storage: 20, + private: false +}; +const databaseEnvVariables = { + DATABASE_URL: { + fromService: { id: "db", value: "dbConnectionString" } + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + databaseEnvVariables, + flightcontrolConfig, + mysqlDatabaseService, + postgresDatabaseService +}); diff --git a/packages/cli/dist/commands/setup/deploy/templates/netlify.js b/packages/cli/dist/commands/setup/deploy/templates/netlify.js new file mode 100644 index 0000000000..99a1316e36 --- /dev/null +++ b/packages/cli/dist/commands/setup/deploy/templates/netlify.js @@ -0,0 +1,56 @@ +"use strict"; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var netlify_exports = {}; +__export(netlify_exports, { + NETLIFY_TOML: () => NETLIFY_TOML +}); +module.exports = __toCommonJS(netlify_exports); +var import_lib = require("../../../../lib"); +const config = (0, import_lib.getConfig)(); +const NETLIFY_TOML = `[build] + command = "yarn rw deploy netlify" + publish = "web/dist" + functions = "api/dist/functions" + + [build.environment] + NODE_VERSION = "20" + +[[redirects]] + from = "/*" + to = "/200.html" + status = 200 + +# To use Netlify Dev, install Netlify's CLI (\`netlify-cli\`) from NPM and use \`netlify link\` +# to connect your local project to a site on Netlify. Then run \`netlify dev\`. +# +# Quick links to the docs: +# - Netlfy Dev https://www.netlify.com/products/dev +# - Netlify's CLI https://docs.netlify.com/cli/get-started/#installation +# - \`netlify link\` https://docs.netlify.com/cli/get-started/#link-and-unlink-sites +[dev] + framework = "redwoodjs" + # Make sure \`targetPort\` matches \`web.port\` in the \`redwood.toml\`: + targetPort = ${config.web.port} + # Point your browser to this port to access your app: + port = 8888 +`; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + NETLIFY_TOML +}); diff --git a/packages/cli/dist/commands/setup/deploy/templates/render.js b/packages/cli/dist/commands/setup/deploy/templates/render.js new file mode 100644 index 0000000000..12a107d731 --- /dev/null +++ b/packages/cli/dist/commands/setup/deploy/templates/render.js @@ -0,0 +1,110 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var render_exports = {}; +__export(render_exports, { + POSTGRES_YAML: () => POSTGRES_YAML, + PROJECT_NAME: () => PROJECT_NAME, + RENDER_HEALTH_CHECK: () => RENDER_HEALTH_CHECK, + RENDER_YAML: () => RENDER_YAML, + SQLITE_YAML: () => SQLITE_YAML +}); +module.exports = __toCommonJS(render_exports); +var import_path = __toESM(require("path")); +var import_lib = require("../../../../lib"); +const PROJECT_NAME = import_path.default.basename((0, import_lib.getPaths)().base); +const RENDER_YAML = (database) => { + return `# Quick links to the docs: +# - Redwood on Render: https://render.com/docs/deploy-redwood +# - Render's Blueprint spec: https://render.com/docs/yaml-spec + +services: +- name: ${PROJECT_NAME}-web + type: web + env: static + buildCommand: corepack enable && yarn install && yarn rw deploy render web + staticPublishPath: ./web/dist + + envVars: + - key: SKIP_INSTALL_DEPS + value: true + + routes: + - type: rewrite + source: /.redwood/functions/* + # Replace \`destination\` here after your first deploy: + # + # \`\`\` + # destination: https://my-redwood-project-api.onrender.com/* + # \`\`\` + destination: replace_with_api_url/* + - type: rewrite + source: /* + destination: /200.html + +- name: ${PROJECT_NAME}-api + type: web + plan: free + env: node + region: oregon + buildCommand: corepack enable && yarn install && yarn rw build api + startCommand: yarn rw deploy render api + + envVars: +${database} +`; +}; +const POSTGRES_YAML = ` - key: DATABASE_URL + fromDatabase: + name: ${PROJECT_NAME}-db + property: connectionString + +databases: + - name: ${PROJECT_NAME}-db + region: oregon`; +const SQLITE_YAML = ` - key: DATABASE_URL + value: file:./data/sqlite.db + disk: + name: sqlite-data + mountPath: /opt/render/project/src/api/db/data + sizeGB: 1`; +const RENDER_HEALTH_CHECK = `// render-health-check +export const handler = async () => { + return { + statusCode: 200, + } +} +`; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + POSTGRES_YAML, + PROJECT_NAME, + RENDER_HEALTH_CHECK, + RENDER_YAML, + SQLITE_YAML +}); diff --git a/packages/cli/dist/commands/setup/deploy/templates/serverless/api.js b/packages/cli/dist/commands/setup/deploy/templates/serverless/api.js new file mode 100644 index 0000000000..3b35610e78 --- /dev/null +++ b/packages/cli/dist/commands/setup/deploy/templates/serverless/api.js @@ -0,0 +1,113 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var api_exports = {}; +__export(api_exports, { + PROJECT_NAME: () => PROJECT_NAME, + SERVERLESS_API_YML: () => SERVERLESS_API_YML +}); +module.exports = __toCommonJS(api_exports); +var import_path = __toESM(require("path")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_lib = require("../../../../../lib"); +const PROJECT_NAME = import_path.default.basename((0, import_lib.getPaths)().base); +const SERVERLESS_API_YML = `# See the full yml reference at https://www.serverless.com/framework/docs/providers/aws/guide/serverless.yml/ +service: ${PROJECT_NAME}-api + +# Uncomment \`org\` and \`app\` and enter manually if you want to integrate your +# deployment with the Serverless dashboard, or run \`yarn serverless\` in ./api to be +# prompted to connect to an app and these will be filled in for you. +# See https://www.serverless.com/framework/docs/dashboard/ for more details. +# org: your-org +# app: your-app + +useDotenv: true + +provider: + name: aws + runtime: nodejs18.x + region: us-east-1 # AWS region where the service will be deployed, defaults to N. Virginia + httpApi: # HTTP API is used by default. To learn about the available options in API Gateway, see https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-vs-rest.html + cors: + allowedOrigins: + - '*' # This is the default value. You can remove this line if you want to restrict the CORS to a specific origin. + # allowCredentials: true # allowCredentials should only be used when allowedOrigins doesn't include '*' + allowedHeaders: + - authorization + - auth-provider + - content-type + - X-Amz-Date + - X-Api-Key + - X-Amz-Security-Token + - X-Amz-User-Agent + payload: '1.0' + stackTags: + source: serverless + name: Redwood Lambda API with HTTP API Gateway + tags: + name: Redwood Lambda API with HTTP API Gateway + environment: + # Add environment variables here, either in the form + # VARIABLE_NAME: \${env:VARIABLE_NAME} for vars in your local environment, or + # VARIABLE_NAME: \${param:VARIABLE_NAME} for vars from the Serverless dashboard + +package: + individually: true + patterns: + - "!node_modules/.prisma/client/libquery_engine-*" + - "node_modules/.prisma/client/libquery_engine-rhel-*" + - "!node_modules/prisma/libquery_engine-*" + - "!node_modules/@prisma/engines/**" + +${import_fs_extra.default.existsSync(import_path.default.resolve((0, import_lib.getPaths)().api.functions)) ? `functions: + ${import_fs_extra.default.readdirSync(import_path.default.resolve((0, import_lib.getPaths)().api.functions)).map((file) => { + const basename = import_path.default.parse(file).name; + return `${basename}: + description: ${basename} function deployed on AWS Lambda + package: + artifact: dist/zipball/${basename}.zip + memorySize: 1024 # in megabytes + timeout: 25 # seconds (max: 900 [15 minutes]) + tags: # tags for this specific lambda function + endpoint: /${basename} + handler: ${basename}.handler + events: + - httpApi: # if a function should be limited to only GET or POST you can remove one or the other here + path: /${basename} + method: GET + - httpApi: + path: /${basename} + method: POST +`; +}).join(" ")}` : ""} +`; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + PROJECT_NAME, + SERVERLESS_API_YML +}); diff --git a/packages/cli/dist/commands/setup/deploy/templates/serverless/web.js b/packages/cli/dist/commands/setup/deploy/templates/serverless/web.js new file mode 100644 index 0000000000..8bd1156fae --- /dev/null +++ b/packages/cli/dist/commands/setup/deploy/templates/serverless/web.js @@ -0,0 +1,67 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var web_exports = {}; +__export(web_exports, { + PROJECT_NAME: () => PROJECT_NAME, + SERVERLESS_WEB_YML: () => SERVERLESS_WEB_YML +}); +module.exports = __toCommonJS(web_exports); +var import_path = __toESM(require("path")); +var import_lib = require("../../../../../lib"); +const PROJECT_NAME = import_path.default.basename((0, import_lib.getPaths)().base); +const SERVERLESS_WEB_YML = `# See the full yml reference at https://www.serverless.com/framework/docs/providers/aws/guide/serverless.yml/ +service: ${PROJECT_NAME}-web + +# Uncomment \`org\` and \`app\` and enter manually if you want to integrate your +# deployment with the Serverless dashboard, or run \`yarn serverless\` in ./web to be +# prompted to connect to an app and these will be filled in for you. +# See https://www.serverless.com/framework/docs/dashboard/ for more details. +# org: your-org +# app: your-app + +useDotenv: true + +plugins: + - serverless-lift + +constructs: + web: + type: static-website + path: dist + +provider: + name: aws + runtime: nodejs18.x + region: us-east-1 # AWS region where the service will be deployed, defaults to N. Virgina +`; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + PROJECT_NAME, + SERVERLESS_WEB_YML +}); diff --git a/packages/cli/dist/commands/setup/generator/generator.js b/packages/cli/dist/commands/setup/generator/generator.js new file mode 100644 index 0000000000..664bf5fb74 --- /dev/null +++ b/packages/cli/dist/commands/setup/generator/generator.js @@ -0,0 +1,86 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var generator_exports = {}; +__export(generator_exports, { + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(generator_exports); +var import_path = __toESM(require("path")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_terminal_link = __toESM(require("terminal-link")); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +const command = "generator "; +const description = "Copies generator templates locally for customization"; +const EXCLUDE_GENERATORS = [ + "dataMigration", + "dbAuth", + "generator", + "script", + "secret" +]; +const builder = (yargs) => { + const availableGenerators = import_fs_extra.default.readdirSync(import_path.default.join(__dirname, "../../generate"), { + withFileTypes: true + }).filter((dir) => dir.isDirectory() && !dir.name.match(/__/)).map((dir) => dir.name); + yargs.positional("name", { + description: "Name of the generator to copy templates from", + choices: availableGenerators.filter( + (dir) => !EXCLUDE_GENERATORS.includes(dir) + ) + }).option("force", { + alias: "f", + default: false, + description: "Overwrite existing files", + type: "boolean" + }).epilogue( + `Also see the ${(0, import_terminal_link.default)( + "Redwood CLI Reference", + "https://redwoodjs.com/docs/cli-commands#setup-generator" + )}` + ); +}; +const handler = async (options) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "setup generator", + name: options.name, + force: options.force + }); + const { handler: handler2 } = await import("./generatorHandler.js"); + return handler2(options); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/setup/generator/generatorHandler.js b/packages/cli/dist/commands/setup/generator/generatorHandler.js new file mode 100644 index 0000000000..a49a6068b7 --- /dev/null +++ b/packages/cli/dist/commands/setup/generator/generatorHandler.js @@ -0,0 +1,84 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var generatorHandler_exports = {}; +__export(generatorHandler_exports, { + handler: () => handler +}); +module.exports = __toCommonJS(generatorHandler_exports); +var import_path = __toESM(require("path")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_listr2 = require("listr2"); +var import_lib = require("../../../lib"); +var import_colors = __toESM(require("../../../lib/colors")); +const SIDE_MAP = { + web: ["cell", "component", "layout", "page", "scaffold"], + api: ["function", "sdl", "service"] +}; +const copyGenerator = (name, { force }) => { + const side = SIDE_MAP["web"].includes(name) ? "web" : "api"; + const from = import_path.default.join(__dirname, "../../generate", name, "templates"); + const to = import_path.default.join((0, import_lib.getPaths)()[side].generators, name); + import_fs_extra.default.copySync(from, to, { overwrite: force, errorOnExist: true }); + return to; +}; +let destination; +const tasks = ({ name, force }) => { + return new import_listr2.Listr( + [ + { + title: "Copying generator templates...", + task: () => { + destination = copyGenerator(name, { force }); + } + }, + { + title: "Destination:", + task: (ctx, task) => { + task.title = ` Wrote templates to ${destination.replace( + (0, import_lib.getPaths)().base, + "" + )}`; + } + } + ], + { rendererOptions: { collapseSubtasks: false }, errorOnExist: true } + ); +}; +const handler = async ({ name, force }) => { + const t = tasks({ name, force }); + try { + await t.run(); + } catch (e) { + console.log(import_colors.default.error(e.message)); + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + handler +}); diff --git a/packages/cli/dist/commands/setup/graphql/features/fragments/appGqlConfigTransform.js b/packages/cli/dist/commands/setup/graphql/features/fragments/appGqlConfigTransform.js new file mode 100644 index 0000000000..dc68124274 --- /dev/null +++ b/packages/cli/dist/commands/setup/graphql/features/fragments/appGqlConfigTransform.js @@ -0,0 +1,137 @@ +"use strict"; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var appGqlConfigTransform_exports = {}; +__export(appGqlConfigTransform_exports, { + default: () => transform +}); +module.exports = __toCommonJS(appGqlConfigTransform_exports); +function isJsxExpressionContainer(node) { + return node.type === "JSXExpressionContainer"; +} +function isObjectExpression(node) { + return node.type === "ObjectExpression"; +} +function isObjectProperty(node) { + return node.type === "ObjectProperty"; +} +function isIdentifier(node) { + return node.type === "Identifier"; +} +function isPropertyWithName(node, name) { + return isObjectProperty(node) && node.key.type === "Identifier" && node.key.name === name; +} +function transform(file, api) { + const j = api.jscodeshift; + const root = j(file.source); + const redwoodApolloProvider = root.findJSXElements("RedwoodApolloProvider"); + const graphQLClientConfigCollection = redwoodApolloProvider.find( + j.JSXAttribute, + { + name: { name: "graphQLClientConfig" } + } + ); + let graphQLClientConfig; + if (graphQLClientConfigCollection.length === 0) { + graphQLClientConfig = j.jsxAttribute( + j.jsxIdentifier("graphQLClientConfig"), + j.jsxExpressionContainer(j.objectExpression([])) + ); + } else { + graphQLClientConfig = graphQLClientConfigCollection.get(0).node; + } + const graphQLClientConfigExpression = isJsxExpressionContainer( + graphQLClientConfig.value + ) ? graphQLClientConfig.value.expression : j.jsxEmptyExpression(); + let graphQLClientConfigVariableName = ""; + if (isIdentifier(graphQLClientConfigExpression)) { + graphQLClientConfigVariableName = graphQLClientConfigExpression.name; + } + if (!graphQLClientConfigVariableName && !isObjectExpression(graphQLClientConfigExpression)) { + throw new Error( + "Error configuring possibleTypes. You'll have to do it manually. (Could not find a graphQLClientConfigExpression of the correct type, it's a " + graphQLClientConfigExpression.type + ")" + ); + } + if (isObjectExpression(graphQLClientConfigExpression)) { + graphQLClientConfigVariableName = "graphQLClientConfig"; + root.find(j.VariableDeclaration, { + declarations: [ + { + type: "VariableDeclarator", + id: { type: "Identifier", name: "App" } + } + ] + }).insertBefore( + j.variableDeclaration("const", [ + j.variableDeclarator( + j.identifier(graphQLClientConfigVariableName), + graphQLClientConfigExpression + ) + ]) + ); + } + const configVariableDeclarators = root.findVariableDeclarators( + graphQLClientConfigVariableName + ); + const configExpression = configVariableDeclarators.get(0)?.node.init; + if (!isObjectExpression(configExpression)) { + throw new Error( + "Error configuring possibleTypes. You'll have to do it manually. (Could not find a graphQLClientConfig variable ObjectExpression)" + ); + } + let cacheConfig = configExpression.properties.find( + (prop) => isPropertyWithName(prop, "cacheConfig") + ); + if (!cacheConfig) { + cacheConfig = j.objectProperty( + j.identifier("cacheConfig"), + j.objectExpression([]) + ); + configExpression.properties.push(cacheConfig); + } + if (!isObjectProperty(cacheConfig)) { + throw new Error( + "Error configuring possibleTypes. You'll have to do it manually. (cacheConfig is not an ObjectProperty)" + ); + } + const cacheConfigValue = cacheConfig.value; + if (!isObjectExpression(cacheConfigValue)) { + throw new Error( + "Error configuring possibleTypes. You'll have to do it manually. (cacheConfigValue is not an ObjectExpression)" + ); + } + const possibleTypes = cacheConfigValue.properties.find( + (prop) => isPropertyWithName(prop, "possibleTypes") + ); + if (!possibleTypes) { + const property = j.property( + "init", + j.identifier("possibleTypes"), + j.identifier("possibleTypes.possibleTypes") + ); + cacheConfigValue.properties.push(property); + } + graphQLClientConfigCollection.remove(); + redwoodApolloProvider.get(0).node.openingElement.attributes.push( + j.jsxAttribute( + j.jsxIdentifier("graphQLClientConfig"), + j.jsxExpressionContainer(j.identifier(graphQLClientConfigVariableName)) + ) + ); + return root.toSource(); +} diff --git a/packages/cli/dist/commands/setup/graphql/features/fragments/appImportTransform.js b/packages/cli/dist/commands/setup/graphql/features/fragments/appImportTransform.js new file mode 100644 index 0000000000..5f080bd1df --- /dev/null +++ b/packages/cli/dist/commands/setup/graphql/features/fragments/appImportTransform.js @@ -0,0 +1,40 @@ +"use strict"; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var appImportTransform_exports = {}; +__export(appImportTransform_exports, { + default: () => transform +}); +module.exports = __toCommonJS(appImportTransform_exports); +function transform(file, api) { + const j = api.jscodeshift; + const root = j(file.source); + const possibleTypesImports = root.find(j.ImportDeclaration); + const hasPossibleTypesImport = possibleTypesImports.some((i) => { + return i.get("source").value.value === "src/graphql/possibleTypes" || i.get("source").value.value === "./graphql/possibleTypes"; + }); + if (!hasPossibleTypesImport) { + possibleTypesImports.at(1).insertAfter( + j.importDeclaration( + [j.importDefaultSpecifier(j.identifier("possibleTypes"))], + j.literal("src/graphql/possibleTypes") + ) + ); + } + return root.toSource(); +} diff --git a/packages/cli/dist/commands/setup/graphql/features/fragments/fragments.js b/packages/cli/dist/commands/setup/graphql/features/fragments/fragments.js new file mode 100644 index 0000000000..a320949aad --- /dev/null +++ b/packages/cli/dist/commands/setup/graphql/features/fragments/fragments.js @@ -0,0 +1,62 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var fragments_exports = {}; +__export(fragments_exports, { + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(fragments_exports); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +const command = "fragments"; +const description = "Set up Fragments for GraphQL"; +function builder(yargs) { + return yargs.option("force", { + alias: "f", + default: false, + description: "Overwrite existing configuration", + type: "boolean" + }); +} +async function handler({ force }) { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "setup graphql fragments", + force + }); + const { handler: handler2 } = await import("./fragmentsHandler.js"); + return handler2({ force }); +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/setup/graphql/features/fragments/fragmentsHandler.js b/packages/cli/dist/commands/setup/graphql/features/fragments/fragmentsHandler.js new file mode 100644 index 0000000000..78b9bf9da3 --- /dev/null +++ b/packages/cli/dist/commands/setup/graphql/features/fragments/fragmentsHandler.js @@ -0,0 +1,115 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var fragmentsHandler_exports = {}; +__export(fragmentsHandler_exports, { + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(fragmentsHandler_exports); +var import_node_fs = __toESM(require("node:fs")); +var import_node_path = __toESM(require("node:path")); +var import_execa = __toESM(require("execa")); +var import_listr2 = require("listr2"); +var import_prettier = require("prettier"); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_project_config = require("@redwoodjs/project-config"); +var import_runTransform = require("./runTransform"); +const command = "fragments"; +const description = "Set up Fragments for GraphQL"; +async function handler({ force }) { + const tasks = new import_listr2.Listr( + [ + { + title: "Update Redwood Project Configuration to enable GraphQL Fragments", + skip: () => { + if (force) { + return false; + } + const config = (0, import_project_config.getConfig)(); + if (config.graphql.fragments) { + return "GraphQL Fragments are already enabled."; + } + return false; + }, + task: () => { + (0, import_cli_helpers.setTomlSetting)("graphql", "fragments", true); + } + }, + { + title: "Generate possibleTypes.ts", + task: () => { + import_execa.default.commandSync("yarn redwood generate types", { stdio: "ignore" }); + } + }, + { + title: "Import possibleTypes in App.tsx", + task: () => { + return (0, import_runTransform.runTransform)({ + transformPath: import_node_path.default.join(__dirname, "appImportTransform.js"), + targetPaths: [(0, import_project_config.getPaths)().web.app] + }); + } + }, + { + title: "Add possibleTypes to the GraphQL cache config", + task: async () => { + const transformResult = await (0, import_runTransform.runTransform)({ + transformPath: import_node_path.default.join(__dirname, "appGqlConfigTransform.js"), + targetPaths: [(0, import_project_config.getPaths)().web.app] + }); + if (transformResult.error) { + throw new Error(transformResult.error); + } + const appPath = (0, import_project_config.getPaths)().web.app; + const source = import_node_fs.default.readFileSync(appPath, "utf-8"); + const prettierOptions = await (0, import_cli_helpers.getPrettierOptions)(); + const prettifiedApp = (0, import_prettier.format)(source, { + ...prettierOptions, + parser: "babel-ts" + }); + import_node_fs.default.writeFileSync((0, import_project_config.getPaths)().web.app, prettifiedApp, "utf-8"); + } + } + ], + { rendererOptions: { collapseSubtasks: false } } + ); + try { + await tasks.run(); + } catch (e) { + console.error(import_cli_helpers.colors.error(e.message)); + process.exit(e?.exitCode || 1); + } +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/setup/graphql/features/fragments/runTransform.js b/packages/cli/dist/commands/setup/graphql/features/fragments/runTransform.js new file mode 100644 index 0000000000..c3d28a750b --- /dev/null +++ b/packages/cli/dist/commands/setup/graphql/features/fragments/runTransform.js @@ -0,0 +1,100 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var runTransform_exports = {}; +__export(runTransform_exports, { + runTransform: () => runTransform +}); +module.exports = __toCommonJS(runTransform_exports); +var jscodeshift = __toESM(require("jscodeshift/src/Runner")); +const defaultJscodeshiftOpts = { + // 0, 1 or 2 + verbose: 0, + dry: false, + // Doesn't do anything when running programmatically + print: false, + babel: true, + extensions: "js,ts,jsx,tsx", + ignorePattern: "**/node_modules/**", + ignoreConfig: [], + runInBand: false, + silent: true, + parser: "babel", + parserConfig: {}, + // `silent` has to be `false` for this option to do anything + failOnError: false, + stdin: false +}; +const runTransform = async ({ + transformPath, + targetPaths, + parser = "tsx", + options = {} +}) => { + if (process.env.NODE_ENV === "test" && process.env.RWJS_CWD) { + process.chdir(process.env.RWJS_CWD); + } + const { output, stdoutWrite } = patchStdoutWrite(); + const result = await jscodeshift.run(transformPath, targetPaths, { + ...defaultJscodeshiftOpts, + parser, + babel: process.env.NODE_ENV === "test", + ...options + // Putting options here lets users override all the defaults. + }); + restoreStdoutWrite(stdoutWrite); + let error; + if (result.error) { + error = output.value.split("\n").find((line) => line.startsWith("Error: "))?.slice("Error: ".length); + } + return { + ...result, + error, + output: output.value + }; +}; +function patchStdoutWrite() { + const stdoutWrite = process.stdout.write; + const output = { + value: "" + }; + process.stdout.write = (chunk) => { + if (typeof chunk === "string") { + output.value += chunk; + } + return true; + }; + return { output, stdoutWrite }; +} +function restoreStdoutWrite(stdoutWrite) { + process.stdout.write = stdoutWrite; +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + runTransform +}); diff --git a/packages/cli/dist/commands/setup/graphql/features/trustedDocuments/graphqlTransform.js b/packages/cli/dist/commands/setup/graphql/features/trustedDocuments/graphqlTransform.js new file mode 100644 index 0000000000..914bbd813c --- /dev/null +++ b/packages/cli/dist/commands/setup/graphql/features/trustedDocuments/graphqlTransform.js @@ -0,0 +1,71 @@ +"use strict"; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var graphqlTransform_exports = {}; +__export(graphqlTransform_exports, { + default: () => transform +}); +module.exports = __toCommonJS(graphqlTransform_exports); +function transform(file, api) { + const j = api.jscodeshift; + const root = j(file.source); + const allImports = root.find(j.ImportDeclaration); + const hasStoreImport = allImports.some((i) => { + return i.get("source").value.value === "src/lib/trustedDocumentsStore"; + }); + if (!hasStoreImport) { + allImports.at(-1).insertAfter( + j.importDeclaration( + [j.importSpecifier(j.identifier("store"))], + j.literal("src/lib/trustedDocumentsStore") + ) + ); + } + const createGraphQLHandlerCalls = root.find(j.CallExpression, { + callee: { + name: "createGraphQLHandler" + } + }); + if (createGraphQLHandlerCalls.length === 0) { + throw new Error( + "Error updating your graphql handler function. You'll have to do it manually. (Couldn't find a call to `createGraphQLHandler`)" + ); + } + const existingTrustedDocumentsProperty = createGraphQLHandlerCalls.find( + j.ObjectProperty, + { + key: { + name: "trustedDocuments" + } + } + ); + if (existingTrustedDocumentsProperty.length === 0) { + const storeProperty = j.objectProperty( + j.identifier("store"), + j.identifier("store") + ); + storeProperty.shorthand = true; + createGraphQLHandlerCalls.get(0).node.arguments[0].properties.push( + j.objectProperty( + j.identifier("trustedDocuments"), + j.objectExpression([storeProperty]) + ) + ); + } + return root.toSource(); +} diff --git a/packages/cli/dist/commands/setup/graphql/features/trustedDocuments/trustedDocuments.js b/packages/cli/dist/commands/setup/graphql/features/trustedDocuments/trustedDocuments.js new file mode 100644 index 0000000000..c09243a502 --- /dev/null +++ b/packages/cli/dist/commands/setup/graphql/features/trustedDocuments/trustedDocuments.js @@ -0,0 +1,62 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var trustedDocuments_exports = {}; +__export(trustedDocuments_exports, { + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(trustedDocuments_exports); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +const command = "trusted-documents"; +const description = "Set up Trusted Documents for GraphQL"; +function builder(yargs) { + return yargs.option("force", { + alias: "f", + default: false, + description: "Overwrite existing configuration", + type: "boolean" + }); +} +async function handler({ force }) { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "setup graphql trusted-documents", + force + }); + const { handler: handler2 } = await import("./trustedDocumentsHandler.js"); + return handler2({ force }); +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/setup/graphql/features/trustedDocuments/trustedDocumentsHandler.js b/packages/cli/dist/commands/setup/graphql/features/trustedDocuments/trustedDocumentsHandler.js new file mode 100644 index 0000000000..fd7e580747 --- /dev/null +++ b/packages/cli/dist/commands/setup/graphql/features/trustedDocuments/trustedDocumentsHandler.js @@ -0,0 +1,105 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var trustedDocumentsHandler_exports = {}; +__export(trustedDocumentsHandler_exports, { + handler: () => handler +}); +module.exports = __toCommonJS(trustedDocumentsHandler_exports); +var import_node_fs = __toESM(require("node:fs")); +var import_node_path = __toESM(require("node:path")); +var import_execa = __toESM(require("execa")); +var import_listr2 = require("listr2"); +var import_prettier = require("prettier"); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_project_config = require("@redwoodjs/project-config"); +var import_runTransform = require("../fragments/runTransform.js"); +async function handler({ force }) { + const tasks = new import_listr2.Listr( + [ + { + title: "Update Redwood Project Configuration to enable GraphQL Trusted Documents ...", + skip: () => { + if (force) { + return false; + } + const config = (0, import_project_config.getConfig)(); + if (config.graphql.trustedDocuments) { + return "GraphQL Trusted Documents are already enabled in your Redwood project."; + } + return false; + }, + task: () => { + (0, import_cli_helpers.setTomlSetting)("graphql", "trustedDocuments", true); + } + }, + { + title: "Generating Trusted Documents store ...", + task: () => { + import_execa.default.commandSync("yarn redwood generate types", { stdio: "ignore" }); + } + }, + { + title: "Configuring the GraphQL Handler to use a Trusted Documents store ...", + task: async () => { + const graphqlPath = (0, import_project_config.resolveFile)( + import_node_path.default.join((0, import_project_config.getPaths)().api.functions, "graphql") + ); + if (!graphqlPath) { + throw new Error("Could not find a GraphQL handler in your project."); + } + const transformResult = await (0, import_runTransform.runTransform)({ + transformPath: import_node_path.default.join(__dirname, "graphqlTransform.js"), + targetPaths: [graphqlPath] + }); + if (transformResult.error) { + throw new Error(transformResult.error); + } + const source = import_node_fs.default.readFileSync(graphqlPath, "utf-8"); + const prettierOptions = await (0, import_cli_helpers.getPrettierOptions)(); + const prettifiedApp = await (0, import_prettier.format)(source, { + ...prettierOptions, + parser: "babel-ts" + }); + import_node_fs.default.writeFileSync(graphqlPath, prettifiedApp, "utf-8"); + } + } + ], + { rendererOptions: { collapseSubtasks: false } } + ); + try { + await tasks.run(); + } catch (e) { + console.error(e.message); + process.exit(e?.exitCode || 1); + } +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + handler +}); diff --git a/packages/cli/dist/commands/setup/graphql/graphql.js b/packages/cli/dist/commands/setup/graphql/graphql.js new file mode 100644 index 0000000000..a91504c128 --- /dev/null +++ b/packages/cli/dist/commands/setup/graphql/graphql.js @@ -0,0 +1,54 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var graphql_exports = {}; +__export(graphql_exports, { + builder: () => builder, + command: () => command, + description: () => description +}); +module.exports = __toCommonJS(graphql_exports); +var import_terminal_link = __toESM(require("terminal-link")); +var fragmentsCommand = __toESM(require("./features/fragments/fragments")); +var trustedDocumentsCommand = __toESM(require("./features/trustedDocuments/trustedDocuments")); +const command = "graphql "; +const description = "Set up GraphQL feature support"; +function builder(yargs) { + return yargs.command(fragmentsCommand).command(trustedDocumentsCommand).epilogue( + `Also see the ${(0, import_terminal_link.default)( + "Redwood CLI Reference", + "https://redwoodjs.com/docs/cli-commands#setup-graphql" + )}` + ); +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description +}); diff --git a/packages/cli/dist/commands/setup/i18n/i18n.js b/packages/cli/dist/commands/setup/i18n/i18n.js new file mode 100644 index 0000000000..cae7a96015 --- /dev/null +++ b/packages/cli/dist/commands/setup/i18n/i18n.js @@ -0,0 +1,62 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var i18n_exports = {}; +__export(i18n_exports, { + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(i18n_exports); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +const command = "i18n"; +const description = "Set up i18n"; +const builder = (yargs) => { + yargs.option("force", { + alias: "f", + default: false, + description: "Overwrite existing configuration", + type: "boolean" + }); +}; +const handler = async (options) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "setup i18n", + force: options.force + }); + const { handler: handler2 } = await import("./i18nHandler.js"); + return handler2(options); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/setup/i18n/i18nHandler.js b/packages/cli/dist/commands/setup/i18n/i18nHandler.js new file mode 100644 index 0000000000..4d2a331139 --- /dev/null +++ b/packages/cli/dist/commands/setup/i18n/i18nHandler.js @@ -0,0 +1,191 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var i18nHandler_exports = {}; +__export(i18nHandler_exports, { + handler: () => handler +}); +module.exports = __toCommonJS(i18nHandler_exports); +var import_path = __toESM(require("path")); +var import_chalk = __toESM(require("chalk")); +var import_execa = __toESM(require("execa")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_listr2 = require("listr2"); +var import_telemetry = require("@redwoodjs/telemetry"); +var import_lib = require("../../../lib"); +var import_colors = __toESM(require("../../../lib/colors")); +var import_configureStorybook = __toESM(require("../../../lib/configureStorybook.js")); +var import_extendFile = require("../../../lib/extendFile"); +const APP_JS_PATH = (0, import_lib.getPaths)().web.app; +const i18nImportExist = (appJS) => { + let content = appJS.toString(); + const hasBaseImport = () => /import '.\/i18n'/.test(content); + return hasBaseImport(); +}; +const addI18nImport = (appJS) => { + var content = appJS.toString().split("\n").reverse(); + const index = content.findIndex((value) => /import/.test(value)); + content.splice(index, 0, "import './i18n'"); + return content.reverse().join(` +`); +}; +const i18nConfigExists = () => { + return import_fs_extra.default.existsSync(import_path.default.join((0, import_lib.getPaths)().web.src, "i18n.js")); +}; +const localesExists = (lng) => { + return import_fs_extra.default.existsSync(import_path.default.join((0, import_lib.getPaths)().web.src, "locales", lng + ".json")); +}; +const handler = async ({ force }) => { + const rwPaths = (0, import_lib.getPaths)(); + const tasks = new import_listr2.Listr( + [ + { + title: "Installing packages...", + task: async () => { + return new import_listr2.Listr( + [ + { + title: "Install i18n, i18next, react-i18next and i18next-browser-languagedetector", + task: async () => { + await (0, import_execa.default)("yarn", [ + "workspace", + "web", + "add", + "i18n", + "i18next", + "react-i18next", + "i18next-browser-languagedetector" + ]); + } + } + ], + { rendererOptions: { collapseSubtasks: false } } + ); + } + }, + { + title: "Configure i18n...", + task: () => { + if (!force && i18nConfigExists()) { + throw new Error( + "i18n config already exists.\nUse --force to override existing config." + ); + } else { + return (0, import_lib.writeFile)( + import_path.default.join((0, import_lib.getPaths)().web.src, "i18n.js"), + import_fs_extra.default.readFileSync( + import_path.default.resolve(__dirname, "templates", "i18n.js.template") + ).toString(), + { overwriteExisting: force } + ); + } + } + }, + { + title: "Adding locale file for French...", + task: () => { + if (!force && localesExists("fr")) { + throw new Error( + "fr.json config already exists.\nUse --force to override existing config." + ); + } else { + return (0, import_lib.writeFile)( + import_path.default.join((0, import_lib.getPaths)().web.src, "/locales/fr.json"), + import_fs_extra.default.readFileSync( + import_path.default.resolve(__dirname, "templates", "fr.json.template") + ).toString(), + { overwriteExisting: force } + ); + } + } + }, + { + title: "Adding locale file for English...", + task: () => { + if (!force && localesExists("en")) { + throw new Error( + "en.json already exists.\nUse --force to override existing config." + ); + } else { + return (0, import_lib.writeFile)( + import_path.default.join((0, import_lib.getPaths)().web.src, "/locales/en.json"), + import_fs_extra.default.readFileSync( + import_path.default.resolve(__dirname, "templates", "en.json.template") + ).toString(), + { overwriteExisting: force } + ); + } + } + }, + { + title: "Adding import to App.{jsx,tsx}...", + task: (_ctx, task) => { + let appJS = import_fs_extra.default.readFileSync(APP_JS_PATH); + if (i18nImportExist(appJS)) { + task.skip("Import already exists in App.js"); + } else { + import_fs_extra.default.writeFileSync(APP_JS_PATH, addI18nImport(appJS)); + } + } + }, + { + title: "Configuring Storybook...", + // skip this task if the user's storybook config already includes "withI18n" + skip: () => (0, import_extendFile.fileIncludes)(rwPaths.web.storybookConfig, "withI18n"), + task: async () => (0, import_configureStorybook.default)( + import_path.default.join(__dirname, "templates", "storybook.preview.tsx.template") + ) + }, + { + title: "One more thing...", + task: (_ctx, task) => { + task.title = `One more thing... + + ${import_colors.default.green("Quick link to the docs:")} + + ${import_chalk.default.hex("#e8e8e8")( + "https://react.i18next.com/guides/quick-start/" + )} + `; + } + } + ], + { rendererOptions: { collapseSubtasks: false } } + ); + try { + await tasks.run(); + } catch (e) { + (0, import_telemetry.errorTelemetry)(process.argv, e.message); + console.error(import_colors.default.error(e.message)); + process.exit(e?.exitCode || 1); + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + handler +}); diff --git a/packages/cli/dist/commands/setup/i18n/templates/en.json.template b/packages/cli/dist/commands/setup/i18n/templates/en.json.template new file mode 100644 index 0000000000..74ffacc1f1 --- /dev/null +++ b/packages/cli/dist/commands/setup/i18n/templates/en.json.template @@ -0,0 +1,11 @@ +{ + "Welcome to RedwoodJS": "Welcome to RedwoodJS", + "info": "This is your English translation file", + "see": "https://www.i18next.com/translation-function/essentials", + "HomePage": { + "title": "Home Page", + "info": "Find me in", + "route": "My default route is named", + "link": "link to me with" + } +} diff --git a/packages/cli/dist/commands/setup/i18n/templates/fr.json.template b/packages/cli/dist/commands/setup/i18n/templates/fr.json.template new file mode 100644 index 0000000000..7fd9454d70 --- /dev/null +++ b/packages/cli/dist/commands/setup/i18n/templates/fr.json.template @@ -0,0 +1,11 @@ +{ + "Welcome to RedwoodJS": "Bienvenu sur RedwoodJS", + "info": "Ceci est votre fichier de traduction", + "see": "https://www.i18next.com/translation-function/essentials", + "HomePage": { + "title": "Page d'accueil", + "info": "Trouve moi dans", + "route": "Ma route par default se nomme", + "link": "le lien vers moi avec" + } +} diff --git a/packages/cli/dist/commands/setup/i18n/templates/i18n.js.template b/packages/cli/dist/commands/setup/i18n/templates/i18n.js.template new file mode 100644 index 0000000000..d41592902d --- /dev/null +++ b/packages/cli/dist/commands/setup/i18n/templates/i18n.js.template @@ -0,0 +1,52 @@ +import i18n from 'i18next' +import { initReactI18next } from 'react-i18next' +import LanguageDetector from 'i18next-browser-languagedetector' +import fr from './locales/fr.json' +import en from './locales/en.json' + +// This is a simple i18n configuration with English and French translations. +// You can find the translations in web/src/locales/{language}.json +// see: https://react.i18next.com +// Here's an example of how to use it in your components, pages or layouts: +/* +import { Link, routes } from '@redwoodjs/router' +import { useTranslation } from 'react-i18next' + +const HomePage = () => { + const { t, i18n } = useTranslation() + return ( + <> +

{t('HomePage.title')}

+ + +

+ {t('HomePage.info')} ./web/src/pages/HomePage/HomePage.js +

+

+ {t('HomePage.route')} home, {t('HomePage.link')}` + Home` +

+ + ) +} + +export default HomePage +*/ + +i18n + // learn more: https://github.com/i18next/i18next-browser-languageDetector + .use(LanguageDetector) + .use(initReactI18next) + .init({ + interpolation: { escapeValue: false }, // React already does escaping + fallbackLng: 'en', + resources: { + en: { + translation: en, + }, + fr: { + translation: fr, + }, + }, + }) +export default i18n diff --git a/packages/cli/dist/commands/setup/i18n/templates/storybook.preview.tsx.template b/packages/cli/dist/commands/setup/i18n/templates/storybook.preview.tsx.template new file mode 100644 index 0000000000..a481f82c01 --- /dev/null +++ b/packages/cli/dist/commands/setup/i18n/templates/storybook.preview.tsx.template @@ -0,0 +1,44 @@ +import * as React from 'react' +import { I18nextProvider } from 'react-i18next' +import type { GlobalTypes } from '@storybook/csf' +import type { StoryFn, StoryContext } from '@storybook/react' +import i18n from 'web/src/i18n' + +/** @type { import("@storybook/csf").GlobalTypes } */ +export const globalTypes: GlobalTypes = { + locale: { + name: 'Locale', + description: 'Internationalization locale', + defaultValue: 'en', + toolbar: { + icon: 'globe', + items: [ + { value: 'en', right: '🇺🇸', title: 'English' }, + { value: 'fr', right: '🇫🇷', title: 'Français' }, + ], + }, + }, +} + +/** + * We're following Storybook's naming convention here. See for example + * https://github.com/storybookjs/addon-kit/blob/main/src/withGlobals.ts + * Unfortunately that will make eslint complain, so we have to disable it when + * using a hook below + * @param { import("@storybook/react").StoryFn} StoryFn + * @param { import("@storybook/react").StoryContext} context + */ +const withI18n = (StoryFn: StoryFn, context: StoryContext) => { + // eslint-disable-next-line react-hooks/rules-of-hooks + React.useEffect(() => { + i18n.changeLanguage(context.globals.locale) + }, [context.globals.locale]) + + return ( + + + + ) +} + +export const decorators = [withI18n] diff --git a/packages/cli/dist/commands/setup/mailer/mailer.js b/packages/cli/dist/commands/setup/mailer/mailer.js new file mode 100644 index 0000000000..2dbb992e52 --- /dev/null +++ b/packages/cli/dist/commands/setup/mailer/mailer.js @@ -0,0 +1,67 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var mailer_exports = {}; +__export(mailer_exports, { + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(mailer_exports); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +const command = "mailer"; +const description = "Setup the redwood mailer. This will install the required packages and add the required initial configuration to your redwood app."; +const builder = (yargs) => { + yargs.option("force", { + alias: "f", + default: false, + description: "Overwrite existing configuration", + type: "boolean" + }).option("skip-examples", { + default: false, + description: "Only include required files and exclude any examples", + type: "boolean" + }); +}; +const handler = async (options) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "setup mailer", + force: options.force, + skipExamples: options.skipExamples + }); + const { handler: handler2 } = await import("./mailerHandler.js"); + return handler2(options); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/setup/mailer/mailerHandler.js b/packages/cli/dist/commands/setup/mailer/mailerHandler.js new file mode 100644 index 0000000000..f87160ce28 --- /dev/null +++ b/packages/cli/dist/commands/setup/mailer/mailerHandler.js @@ -0,0 +1,138 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var mailerHandler_exports = {}; +__export(mailerHandler_exports, { + handler: () => handler +}); +module.exports = __toCommonJS(mailerHandler_exports); +var import_path = __toESM(require("path")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_listr2 = require("listr2"); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_telemetry = require("@redwoodjs/telemetry"); +var import_lib = require("../../../lib"); +var import_colors = __toESM(require("../../../lib/colors")); +var import_project = require("../../../lib/project"); +const handler = async ({ force, skipExamples }) => { + const projectIsTypescript = (0, import_project.isTypeScriptProject)(); + const redwoodVersion = require(import_path.default.join((0, import_lib.getPaths)().base, "package.json")).devDependencies["@redwoodjs/core"] ?? "latest"; + const tasks = new import_listr2.Listr( + [ + { + title: `Adding api/src/lib/mailer.${projectIsTypescript ? "ts" : "js"}...`, + task: () => { + const templatePath = import_path.default.resolve( + __dirname, + "templates", + "mailer.ts.template" + ); + const templateContent = import_fs_extra.default.readFileSync(templatePath, { + encoding: "utf8", + flag: "r" + }); + const mailerPath = import_path.default.join( + (0, import_lib.getPaths)().api.lib, + `mailer.${projectIsTypescript ? "ts" : "js"}` + ); + const mailerContent = projectIsTypescript ? templateContent : (0, import_lib.transformTSToJS)(mailerPath, templateContent); + return (0, import_lib.writeFile)(mailerPath, mailerContent, { + overwriteExisting: force + }); + } + }, + { + title: "Adding api/src/mail directory...", + task: () => { + const mailDir = import_path.default.join((0, import_lib.getPaths)().api.mail); + if (!import_fs_extra.default.existsSync(mailDir)) { + import_fs_extra.default.mkdirSync(mailDir); + } + } + }, + { + title: `Adding example ReactEmail mail template`, + skip: () => skipExamples, + task: () => { + const templatePath = import_path.default.resolve( + __dirname, + "templates", + "re-example.tsx.template" + ); + const templateContent = import_fs_extra.default.readFileSync(templatePath, { + encoding: "utf8", + flag: "r" + }); + const exampleTemplatePath = import_path.default.join( + (0, import_lib.getPaths)().api.mail, + "Example", + `Example.${projectIsTypescript ? "tsx" : "jsx"}` + ); + const exampleTemplateContent = projectIsTypescript ? templateContent : (0, import_lib.transformTSToJS)(exampleTemplatePath, templateContent); + return (0, import_lib.writeFile)(exampleTemplatePath, exampleTemplateContent, { + overwriteExisting: force + }); + } + }, + { + // Add production dependencies + ...(0, import_cli_helpers.addApiPackages)([ + `@redwoodjs/mailer-core@${redwoodVersion}`, + `@redwoodjs/mailer-handler-nodemailer@${redwoodVersion}`, + `@redwoodjs/mailer-renderer-react-email@${redwoodVersion}`, + `@react-email/components` + // NOTE: Unpinned dependency here + ]), + title: "Adding production dependencies to your api side..." + }, + { + // Add development dependencies + ...(0, import_cli_helpers.addApiPackages)([ + "-D", + `@redwoodjs/mailer-handler-in-memory@${redwoodVersion}`, + `@redwoodjs/mailer-handler-studio@${redwoodVersion}` + ]), + title: "Adding development dependencies to your api side..." + } + ], + { + rendererOptions: { collapseSubtasks: false } + } + ); + try { + await tasks.run(); + } catch (e) { + (0, import_telemetry.errorTelemetry)(process.argv, e.message); + console.error(import_colors.default.error(e.message)); + process.exit(e?.exitCode || 1); + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + handler +}); diff --git a/packages/cli/dist/commands/setup/mailer/templates/mailer.ts.template b/packages/cli/dist/commands/setup/mailer/templates/mailer.ts.template new file mode 100644 index 0000000000..eac5fed86e --- /dev/null +++ b/packages/cli/dist/commands/setup/mailer/templates/mailer.ts.template @@ -0,0 +1,30 @@ +import { Mailer } from '@redwoodjs/mailer-core' +import { NodemailerMailHandler } from '@redwoodjs/mailer-handler-nodemailer' +import { ReactEmailRenderer } from '@redwoodjs/mailer-renderer-react-email' + +import { logger } from 'src/lib/logger' + +export const mailer = new Mailer({ + handling: { + handlers: { + // TODO: Update this handler config or switch it out for a different handler completely + nodemailer: new NodemailerMailHandler({ + transport: { + host: 'localhost', + port: 4319, + secure: false, + }, + }), + }, + default: 'nodemailer', + }, + + rendering: { + renderers: { + reactEmail: new ReactEmailRenderer(), + }, + default: 'reactEmail', + }, + + logger, +}) diff --git a/packages/cli/dist/commands/setup/mailer/templates/re-example.tsx.template b/packages/cli/dist/commands/setup/mailer/templates/re-example.tsx.template new file mode 100644 index 0000000000..9a13cf19eb --- /dev/null +++ b/packages/cli/dist/commands/setup/mailer/templates/re-example.tsx.template @@ -0,0 +1,40 @@ +import React from 'react' + +import { + Html, + Text, + Hr, + Body, + Head, + Tailwind, + Preview, + Container, + Heading, +} from '@react-email/components' + +export function ExampleEmail( + { when }: { when: string } = { when: new Date().toLocaleString() } +) { + return ( + + + An example email + + + + + Example Email + + + This is an example email which you can customise to your needs. + +
+ + Message was sent on {when} + +
+ +
+ + ) +} diff --git a/packages/cli/dist/commands/setup/monitoring/monitoring.js b/packages/cli/dist/commands/setup/monitoring/monitoring.js new file mode 100644 index 0000000000..026461a634 --- /dev/null +++ b/packages/cli/dist/commands/setup/monitoring/monitoring.js @@ -0,0 +1,53 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var monitoring_exports = {}; +__export(monitoring_exports, { + builder: () => builder, + command: () => command, + description: () => description +}); +module.exports = __toCommonJS(monitoring_exports); +var import_terminal_link = __toESM(require("terminal-link")); +var sentryCommand = __toESM(require("./sentry/sentry.js")); +const command = "monitoring "; +const description = "Set up monitoring in your Redwood app"; +function builder(yargs) { + return yargs.command(sentryCommand).epilogue( + `Also see the ${(0, import_terminal_link.default)( + "Redwood CLI Reference", + "https://redwoodjs.com/docs/cli-commands#setup-graphql" + )}` + ); +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description +}); diff --git a/packages/cli/dist/commands/setup/monitoring/sentry/sentry.js b/packages/cli/dist/commands/setup/monitoring/sentry/sentry.js new file mode 100644 index 0000000000..41555f271a --- /dev/null +++ b/packages/cli/dist/commands/setup/monitoring/sentry/sentry.js @@ -0,0 +1,62 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var sentry_exports = {}; +__export(sentry_exports, { + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(sentry_exports); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +const command = "sentry"; +const description = "Setup Sentry error and performance tracking"; +const builder = (yargs) => { + return yargs.option("force", { + alias: "f", + default: false, + description: "Overwrite existing Sentry config files", + type: "boolean" + }); +}; +async function handler({ force }) { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "setup monitoring sentry", + force + }); + const { handler: handler2 } = await import("./sentryHandler.js"); + return handler2({ force }); +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/setup/monitoring/sentry/sentryHandler.js b/packages/cli/dist/commands/setup/monitoring/sentry/sentryHandler.js new file mode 100644 index 0000000000..869eb895ee --- /dev/null +++ b/packages/cli/dist/commands/setup/monitoring/sentry/sentryHandler.js @@ -0,0 +1,182 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var sentryHandler_exports = {}; +__export(sentryHandler_exports, { + handler: () => handler +}); +module.exports = __toCommonJS(sentryHandler_exports); +var import_path = __toESM(require("path")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_listr2 = require("listr2"); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_telemetry = require("@redwoodjs/telemetry"); +const rwPaths = (0, import_cli_helpers.getPaths)(); +const handler = async ({ force }) => { + const extension = (0, import_cli_helpers.isTypeScriptProject)() ? "ts" : "js"; + const notes = []; + const tasks = new import_listr2.Listr([ + (0, import_cli_helpers.addApiPackages)(["@envelop/sentry@5", "@sentry/node@7"]), + (0, import_cli_helpers.addWebPackages)(["@sentry/react@7", "@sentry/browser@7"]), + (0, import_cli_helpers.addEnvVarTask)( + "SENTRY_DSN", + "", + "https://docs.sentry.io/product/sentry-basics/dsn-explainer/" + ), + { + title: "Setting up Sentry on the API and web sides", + task: () => { + return (0, import_cli_helpers.writeFilesTask)( + { + [import_path.default.join(rwPaths.api.lib, `sentry.${extension}`)]: import_fs_extra.default.readFileSync( + import_path.default.join(__dirname, "templates/sentryApi.ts.template") + ).toString(), + [import_path.default.join(rwPaths.web.src, "lib", `sentry.${extension}`)]: import_fs_extra.default.readFileSync( + import_path.default.join(__dirname, "templates/sentryWeb.ts.template") + ).toString() + }, + { existingFiles: force ? "OVERWRITE" : "SKIP" } + ); + } + }, + { + title: "Implementing the Envelop plugin", + task: async (ctx) => { + const graphqlHandlerPath = import_path.default.join( + rwPaths.api.functions, + `graphql.${extension}` + ); + const contentLines = import_fs_extra.default.readFileSync(graphqlHandlerPath).toString().split("\n"); + const handlerIndex = contentLines.findLastIndex( + (line) => /^export const handler = createGraphQLHandler\({/.test(line) + ); + const pluginsIndex = contentLines.findLastIndex( + (line) => /extraPlugins:/.test(line) + ); + if (handlerIndex === -1 || pluginsIndex !== -1) { + ctx.addEnvelopPluginSkipped = true; + return; + } + contentLines.splice( + handlerIndex, + 1, + "import 'src/lib/sentry'", + "", + "export const handler = createGraphQLHandler({", + "extraPlugins: [useSentry({", + " includeRawResult: true,", + " includeResolverArgs: true,", + " includeExecuteVariables: true,", + "})]," + ); + contentLines.splice(0, 0, "import { useSentry } from '@envelop/sentry'"); + import_fs_extra.default.writeFileSync( + graphqlHandlerPath, + await (0, import_cli_helpers.prettify)("graphql.ts", contentLines.join("\n")) + ); + } + }, + { + title: "Replacing Redwood's Error boundary", + task: async () => { + const contentLines = import_fs_extra.default.readFileSync(rwPaths.web.app).toString().split("\n"); + const webImportIndex = contentLines.findLastIndex( + (line) => /^import { FatalErrorBoundary, RedwoodProvider } from '@redwoodjs\/web'$/.test( + line + ) + ); + contentLines.splice( + webImportIndex, + 1, + "import { RedwoodProvider } from '@redwoodjs/web'" + ); + const boundaryOpenIndex = contentLines.findLastIndex( + (line) => //.test(line) + ); + contentLines.splice( + boundaryOpenIndex, + 1, + "" + ); + const boundaryCloseIndex = contentLines.findLastIndex( + (line) => /<\/FatalErrorBoundary>/.test(line) + ); + contentLines.splice(boundaryCloseIndex, 1, ""); + contentLines.splice(0, 0, "import Sentry from 'src/lib/sentry'"); + import_fs_extra.default.writeFileSync( + rwPaths.web.app, + await (0, import_cli_helpers.prettify)("App.tsx", contentLines.join("\n")) + ); + } + }, + { + title: "One more thing...", + task: (ctx) => { + notes.push( + import_cli_helpers.colors.green( + "You will need to add `SENTRY_DSN` to `includeEnvironmentVariables` in redwood.toml." + ) + ); + if (ctx.addEnvelopPluginSkipped) { + notes.push( + `${import_cli_helpers.colors.underline( + "Make sure you implement the Sentry Envelop plugin:" + )} https://redwoodjs.com/docs/cli-commands#sentry-envelop-plugin` + ); + } else { + notes.push( + "Check out the RedwoodJS forums for more: https://community.redwoodjs.com/t/sentry-error-and-performance-monitoring-experimental/4880" + ); + } + } + } + ]); + try { + await tasks.run(); + console.log(notes.join("\n")); + } catch (e) { + if (isErrorWithMessage(e)) { + (0, import_telemetry.errorTelemetry)(process.argv, e.message); + console.error(import_cli_helpers.colors.error(e.message)); + } + if (isErrorWithExitCode(e)) { + process.exit(e.exitCode); + } + process.exit(1); + } +}; +function isErrorWithMessage(e) { + return !!e && typeof e === "object" && "message" in e; +} +function isErrorWithExitCode(e) { + return !!e && typeof e === "object" && "exitCode" in e && typeof e.exitCode === "number"; +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + handler +}); diff --git a/packages/cli/dist/commands/setup/monitoring/sentry/templates/sentryApi.ts.template b/packages/cli/dist/commands/setup/monitoring/sentry/templates/sentryApi.ts.template new file mode 100644 index 0000000000..dcb781fcb6 --- /dev/null +++ b/packages/cli/dist/commands/setup/monitoring/sentry/templates/sentryApi.ts.template @@ -0,0 +1,15 @@ +import * as Sentry from '@sentry/node' + +import { db as client } from 'src/lib/db' + +Sentry.init({ + dsn: process.env.SENTRY_DSN, + environment: process.env.NODE_ENV, + integrations: [ + new Sentry.Integrations.Prisma({ client }), + new Sentry.Integrations.Http({ tracing: true }), + ], + tracesSampleRate: 1.0, +}) + +export default Sentry diff --git a/packages/cli/dist/commands/setup/monitoring/sentry/templates/sentryWeb.ts.template b/packages/cli/dist/commands/setup/monitoring/sentry/templates/sentryWeb.ts.template new file mode 100644 index 0000000000..1bf3051a22 --- /dev/null +++ b/packages/cli/dist/commands/setup/monitoring/sentry/templates/sentryWeb.ts.template @@ -0,0 +1,31 @@ +import * as Sentry from '@sentry/react' + +let dsn = '' +let environment = 'development' + +if (typeof process === 'undefined' || !process.env?.SENTRY_DSN) { + console.error( + 'Missing SENTRY_DSN environment variable. Did you forget to add it to ' + + 'your redwood.toml file in `includeEnvironmentVariables`?' + ) + console.info(`Copy this into your redwood.toml file:`) + console.info(` + includeEnvironmentVariables = [ + "SENTRY_DSN" + ] + + `) + console.error('Sentry is disabled for now') +} else { + dsn = process.env.SENTRY_DSN + environment = process.env.NODE_ENV +} + +Sentry.init({ + dsn, + environment, + integrations: [new Sentry.BrowserTracing()], + tracesSampleRate: 1.0, +}) + +export default Sentry diff --git a/packages/cli/dist/commands/setup/package/package.js b/packages/cli/dist/commands/setup/package/package.js new file mode 100644 index 0000000000..75f4bc752b --- /dev/null +++ b/packages/cli/dist/commands/setup/package/package.js @@ -0,0 +1,70 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var package_exports = {}; +__export(package_exports, { + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(package_exports); +var import_terminal_link = __toESM(require("terminal-link")); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +const command = "package "; +const description = "Run a bin from an NPM package with version compatibility checks"; +const builder = (yargs) => { + yargs.positional("npm-package", { + description: "The NPM package to run. This can be a package name or a package name with a version or tag.", + type: "string" + }).option("force", { + default: false, + description: "Proceed with a potentially incompatible version of the package", + type: "boolean", + alias: "f" + }).epilogue( + `Also see the ${(0, import_terminal_link.default)( + "Redwood CLI Reference", + "https://redwoodjs.com/docs/cli-commands#lint" + )}` + ); +}; +const handler = async (options) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "setup package" + }); + const { handler: handler2 } = await import("./packageHandler.js"); + return handler2(options); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/setup/package/packageHandler.js b/packages/cli/dist/commands/setup/package/packageHandler.js new file mode 100644 index 0000000000..01cde27120 --- /dev/null +++ b/packages/cli/dist/commands/setup/package/packageHandler.js @@ -0,0 +1,168 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var packageHandler_exports = {}; +__export(packageHandler_exports, { + handler: () => handler +}); +module.exports = __toCommonJS(packageHandler_exports); +var import_enquirer = __toESM(require("enquirer")); +var import_execa = __toESM(require("execa")); +var import_semver = __toESM(require("semver")); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_project_config = require("@redwoodjs/project-config"); +async function handler({ npmPackage, force, _: _args }) { + const isScoped = npmPackage.startsWith("@"); + const packageName = (isScoped ? "@" : "") + npmPackage.split("@")[isScoped ? 1 : 0]; + const packageVersion = npmPackage.split("@")[isScoped ? 2 : 1] ?? "latest"; + const additionalOptionsToForward = _args.slice(2) ?? []; + if (force) { + console.log( + "No compatibility check will be performed because you used the --force flag." + ); + if (import_semver.default.parse(packageVersion) !== null && import_semver.default.lt(packageVersion, "1.0.0")) { + console.log( + "Be aware that this package is under version 1.0.0 and so should be considered experimental." + ); + } + await runPackage(packageName, packageVersion, additionalOptionsToForward); + return; + } + console.log("Checking compatibility..."); + let compatibilityData; + try { + compatibilityData = await (0, import_cli_helpers.getCompatibilityData)(packageName, packageVersion); + } catch (error) { + console.log("The following error occurred while checking compatibility:"); + const errorMessage = error.message ?? error; + console.log(errorMessage); + if (errorMessage.includes("does not have a tag") || errorMessage.includes("does not have a version")) { + process.exit(1); + } + const decision2 = await promptWithChoices("What would you like to do?", [ + { + name: "cancel", + message: "Cancel" + }, + { + name: "continue", + message: "Continue regardless of potential incompatibility" + } + ]); + if (decision2 === "continue") { + await runPackage(packageName, packageVersion, additionalOptionsToForward); + } + return; + } + const { preferred, compatible } = compatibilityData; + const preferredVersionIsCompatible = preferred.version === compatible.version; + if (preferredVersionIsCompatible) { + await showExperimentalWarning(preferred.version); + await runPackage(packageName, preferred.version, additionalOptionsToForward); + return; + } + const preferredVersionText = `${preferred.version}${preferred.tag ? ` (${preferred.tag})` : ""}`; + const latestCompatibleVersionText = `${compatible.version}${compatible.tag ? ` (${compatible.tag})` : ""}`; + console.log( + `The version ${preferredVersionText} of '${packageName}' is not compatible with your RedwoodJS project version. +The latest version compatible with your project is ${latestCompatibleVersionText}.` + ); + const decision = await promptWithChoices("What would you like to do?", [ + { + name: "useLatestCompatibleVersion", + message: `Use the latest compatible version: ${latestCompatibleVersionText}` + }, + { + name: "usePreferredVersion", + message: `Continue anyway with version: ${preferredVersionText}` + }, + { + name: "cancel", + message: "Cancel" + } + ]); + if (decision === "cancel") { + process.exitCode = 1; + return; + } + const versionToUse = decision === "useLatestCompatibleVersion" ? compatible.version : preferred.version; + await showExperimentalWarning(versionToUse); + await runPackage(packageName, versionToUse, additionalOptionsToForward); +} +async function showExperimentalWarning(version) { + if (version === void 0 || import_semver.default.parse(version) === null || import_semver.default.gte(version, "1.0.0")) { + return; + } + const decision = await promptWithChoices( + "This package is under version 1.0.0 and so should be considered experimental. Would you like to continue?", + [ + { + name: "yes", + message: "Yes" + }, + { + name: "no", + message: "No" + } + ] + ); + if (decision === "no") { + process.exit(); + } +} +async function runPackage(packageName, version, options = []) { + const versionString = version === void 0 ? "" : `@${version}`; + console.log(`Running ${packageName}${versionString}...`); + try { + await (0, import_execa.default)("yarn", ["dlx", `${packageName}${versionString}`, ...options], { + stdio: "inherit", + cwd: (0, import_project_config.getPaths)().base + }); + } catch (error) { + process.exitCode = error.exitCode ?? 1; + } +} +async function promptWithChoices(message, choices) { + try { + const prompt = new import_enquirer.default.Select({ + name: message.substring(0, 8).toLowerCase(), + message, + choices + }); + return await prompt.run(); + } catch (error) { + if (error) { + throw error; + } + } + return null; +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + handler +}); diff --git a/packages/cli/dist/commands/setup/realtime/realtime.js b/packages/cli/dist/commands/setup/realtime/realtime.js new file mode 100644 index 0000000000..e7b35af972 --- /dev/null +++ b/packages/cli/dist/commands/setup/realtime/realtime.js @@ -0,0 +1,74 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var realtime_exports = {}; +__export(realtime_exports, { + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(realtime_exports); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +const command = "realtime"; +const description = "Setup RedwoodJS Realtime"; +function builder(yargs) { + yargs.option("includeExamples", { + alias: ["e", "examples"], + default: true, + description: "Include examples of how to implement liveQueries and subscriptions", + type: "boolean" + }).option("force", { + alias: "f", + default: false, + description: "Overwrite existing configuration", + type: "boolean" + }).option("verbose", { + alias: "v", + default: false, + description: "Print more logs", + type: "boolean" + }); +} +async function handler(options) { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "setup realtime", + includeExamples: options.includeExamples, + force: options.force, + verbose: options.verbose + }); + const { handler: handler2 } = await import("./realtimeHandler.js"); + return handler2(options); +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/setup/realtime/realtimeHandler.js b/packages/cli/dist/commands/setup/realtime/realtimeHandler.js new file mode 100644 index 0000000000..0294d1dd6d --- /dev/null +++ b/packages/cli/dist/commands/setup/realtime/realtimeHandler.js @@ -0,0 +1,327 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var realtimeHandler_exports = {}; +__export(realtimeHandler_exports, { + handler: () => handler +}); +module.exports = __toCommonJS(realtimeHandler_exports); +var import_path = __toESM(require("path")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_listr2 = require("listr2"); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_generate = require("@redwoodjs/internal/dist/generate/generate"); +var import_telemetry = require("@redwoodjs/telemetry"); +var import_lib = require("../../../lib"); +var import_colors = __toESM(require("../../../lib/colors")); +var import_project = require("../../../lib/project"); +var import_serverFileHandler = require("../server-file/serverFileHandler"); +const { version } = JSON.parse( + import_fs_extra.default.readFileSync(import_path.default.resolve(__dirname, "../../../../package.json"), "utf-8") +); +async function handler({ force, includeExamples, verbose }) { + const redwoodPaths = (0, import_lib.getPaths)(); + const ts = (0, import_project.isTypeScriptProject)(); + const realtimeLibFilePath = import_path.default.join( + redwoodPaths.api.lib, + `realtime.${(0, import_project.isTypeScriptProject)() ? "ts" : "js"}` + ); + const tasks = new import_listr2.Listr( + [ + (0, import_cli_helpers.addApiPackages)(["ioredis@^5", `@redwoodjs/realtime@${version}`]), + { + title: "Adding the realtime api lib ...", + task: () => { + const serverFileTemplateContent = import_fs_extra.default.readFileSync( + import_path.default.resolve(__dirname, "templates", "realtime.ts.template"), + "utf-8" + ); + const setupScriptContent = ts ? serverFileTemplateContent : (0, import_lib.transformTSToJS)(realtimeLibFilePath, serverFileTemplateContent); + return [ + (0, import_lib.writeFile)(realtimeLibFilePath, setupScriptContent, { + overwriteExisting: force + }) + ]; + } + }, + { + title: "Adding Countdown example subscription ...", + enabled: () => includeExamples, + task: () => { + const exampleSubscriptionTemplateContent = import_fs_extra.default.readFileSync( + import_path.default.resolve( + __dirname, + "templates", + "subscriptions", + "countdown", + `countdown.ts.template` + ), + "utf-8" + ); + const exampleFile = import_path.default.join( + redwoodPaths.api.subscriptions, + "countdown", + `countdown.${(0, import_project.isTypeScriptProject)() ? "ts" : "js"}` + ); + const setupScriptContent = ts ? exampleSubscriptionTemplateContent : (0, import_lib.transformTSToJS)(exampleFile, exampleSubscriptionTemplateContent); + return [ + (0, import_lib.writeFile)(exampleFile, setupScriptContent, { + overwriteExisting: force + }) + ]; + } + }, + { + title: "Adding NewMessage example subscription ...", + enabled: () => includeExamples, + task: () => { + const exampleSdlTemplateContent = import_fs_extra.default.readFileSync( + import_path.default.resolve( + __dirname, + "templates", + "subscriptions", + "newMessage", + `rooms.sdl.ts.template` + ), + "utf-8" + ); + const sdlFile = import_path.default.join( + redwoodPaths.api.graphql, + `rooms.sdl.${(0, import_project.isTypeScriptProject)() ? "ts" : "js"}` + ); + const sdlContent = ts ? exampleSdlTemplateContent : (0, import_lib.transformTSToJS)(sdlFile, exampleSdlTemplateContent); + const exampleServiceTemplateContent = import_fs_extra.default.readFileSync( + import_path.default.resolve( + __dirname, + "templates", + "subscriptions", + "newMessage", + `rooms.ts.template` + ), + "utf-8" + ); + const serviceFile = import_path.default.join( + redwoodPaths.api.services, + "rooms", + `rooms.${(0, import_project.isTypeScriptProject)() ? "ts" : "js"}` + ); + const serviceContent = ts ? exampleServiceTemplateContent : (0, import_lib.transformTSToJS)(serviceFile, exampleServiceTemplateContent); + const exampleSubscriptionTemplateContent = import_fs_extra.default.readFileSync( + import_path.default.resolve( + __dirname, + "templates", + "subscriptions", + "newMessage", + `newMessage.ts.template` + ), + "utf-8" + ); + const exampleFile = import_path.default.join( + redwoodPaths.api.subscriptions, + "newMessage", + `newMessage.${(0, import_project.isTypeScriptProject)() ? "ts" : "js"}` + ); + const setupScriptContent = ts ? exampleSubscriptionTemplateContent : (0, import_lib.transformTSToJS)(exampleFile, exampleSubscriptionTemplateContent); + return [ + (0, import_lib.writeFile)(sdlFile, sdlContent, { + overwriteExisting: force + }), + (0, import_lib.writeFile)(serviceFile, serviceContent, { + overwriteExisting: force + }), + (0, import_lib.writeFile)(exampleFile, setupScriptContent, { + overwriteExisting: force + }) + ]; + } + }, + { + title: "Adding Auctions example live query ...", + enabled: () => includeExamples, + task: () => { + const exampleSdlTemplateContent = import_fs_extra.default.readFileSync( + import_path.default.resolve( + __dirname, + "templates", + "liveQueries", + "auctions", + `auctions.sdl.ts.template` + ), + "utf-8" + ); + const sdlFile = import_path.default.join( + redwoodPaths.api.graphql, + `auctions.sdl.${(0, import_project.isTypeScriptProject)() ? "ts" : "js"}` + ); + const sdlContent = ts ? exampleSdlTemplateContent : (0, import_lib.transformTSToJS)(sdlFile, exampleSdlTemplateContent); + const exampleServiceTemplateContent = import_fs_extra.default.readFileSync( + import_path.default.resolve( + __dirname, + "templates", + "liveQueries", + "auctions", + `auctions.ts.template` + ), + "utf-8" + ); + const serviceFile = import_path.default.join( + redwoodPaths.api.services, + "auctions", + `auctions.${(0, import_project.isTypeScriptProject)() ? "ts" : "js"}` + ); + const serviceContent = ts ? exampleServiceTemplateContent : (0, import_lib.transformTSToJS)(serviceFile, exampleServiceTemplateContent); + return [ + (0, import_lib.writeFile)(sdlFile, sdlContent, { + overwriteExisting: force + }), + (0, import_lib.writeFile)(serviceFile, serviceContent, { + overwriteExisting: force + }) + ]; + } + }, + { + title: "Adding Defer example queries ...", + enabled: () => includeExamples, + task: () => { + const exampleSdlTemplateContent = import_fs_extra.default.readFileSync( + import_path.default.resolve( + __dirname, + "templates", + "defer", + "fastAndSlowFields", + `fastAndSlowFields.sdl.template` + ), + "utf-8" + ); + const sdlFile = import_path.default.join( + redwoodPaths.api.graphql, + `fastAndSlowFields.sdl.${(0, import_project.isTypeScriptProject)() ? "ts" : "js"}` + ); + const sdlContent = ts ? exampleSdlTemplateContent : (0, import_lib.transformTSToJS)(sdlFile, exampleSdlTemplateContent); + const exampleServiceTemplateContent = import_fs_extra.default.readFileSync( + import_path.default.resolve( + __dirname, + "templates", + "defer", + "fastAndSlowFields", + `fastAndSlowFields.ts.template` + ), + "utf-8" + ); + const serviceFile = import_path.default.join( + redwoodPaths.api.services, + "fastAndSlowFields", + `fastAndSlowFields.${(0, import_project.isTypeScriptProject)() ? "ts" : "js"}` + ); + const serviceContent = ts ? exampleServiceTemplateContent : (0, import_lib.transformTSToJS)(serviceFile, exampleServiceTemplateContent); + return [ + (0, import_lib.writeFile)(sdlFile, sdlContent, { + overwriteExisting: force + }), + (0, import_lib.writeFile)(serviceFile, serviceContent, { + overwriteExisting: force + }) + ]; + } + }, + { + title: "Adding Stream example queries ...", + enabled: () => includeExamples, + task: () => { + const exampleSdlTemplateContent = import_fs_extra.default.readFileSync( + import_path.default.resolve( + __dirname, + "templates", + "stream", + "alphabet", + `alphabet.sdl.template` + ), + "utf-8" + ); + const sdlFile = import_path.default.join( + redwoodPaths.api.graphql, + `alphabet.sdl.${(0, import_project.isTypeScriptProject)() ? "ts" : "js"}` + ); + const sdlContent = ts ? exampleSdlTemplateContent : (0, import_lib.transformTSToJS)(sdlFile, exampleSdlTemplateContent); + const exampleServiceTemplateContent = import_fs_extra.default.readFileSync( + import_path.default.resolve( + __dirname, + "templates", + "stream", + "alphabet", + `alphabet.ts.template` + ), + "utf-8" + ); + const serviceFile = import_path.default.join( + redwoodPaths.api.services, + "alphabet", + `alphabet.${(0, import_project.isTypeScriptProject)() ? "ts" : "js"}` + ); + const serviceContent = ts ? exampleServiceTemplateContent : (0, import_lib.transformTSToJS)(serviceFile, exampleServiceTemplateContent); + return [ + (0, import_lib.writeFile)(sdlFile, sdlContent, { + overwriteExisting: force + }), + (0, import_lib.writeFile)(serviceFile, serviceContent, { + overwriteExisting: force + }) + ]; + } + }, + { + title: `Generating types ...`, + task: async () => { + await (0, import_generate.generate)(); + console.log( + "Note: You may need to manually restart GraphQL in VSCode to see the new types take effect.\n\n" + ); + } + } + ], + { + rendererOptions: { collapseSubtasks: false, persistentOutput: true }, + renderer: verbose ? "verbose" : "default" + } + ); + try { + if (!(0, import_project.serverFileExists)()) { + tasks.add((0, import_serverFileHandler.setupServerFileTasks)({ force })); + } + await tasks.run(); + } catch (e) { + (0, import_telemetry.errorTelemetry)(process.argv, e.message); + console.error(import_colors.default.error(e.message)); + process.exit(e?.exitCode || 1); + } +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + handler +}); diff --git a/packages/cli/dist/commands/setup/realtime/templates/defer/fastAndSlowFields/fastAndSlowFields.sdl.template b/packages/cli/dist/commands/setup/realtime/templates/defer/fastAndSlowFields/fastAndSlowFields.sdl.template new file mode 100644 index 0000000000..67cedf7119 --- /dev/null +++ b/packages/cli/dist/commands/setup/realtime/templates/defer/fastAndSlowFields/fastAndSlowFields.sdl.template @@ -0,0 +1,14 @@ +export const schema = gql` + type Query { + """ + A field that resolves fast. + """ + fastField: String! @skipAuth + + """ + A field that resolves slowly. + Maybe you want to @defer this field ;) + """ + slowField(waitFor: Int! = 5000): String @skipAuth + } +` diff --git a/packages/cli/dist/commands/setup/realtime/templates/defer/fastAndSlowFields/fastAndSlowFields.ts.template b/packages/cli/dist/commands/setup/realtime/templates/defer/fastAndSlowFields/fastAndSlowFields.ts.template new file mode 100644 index 0000000000..d8e25c46de --- /dev/null +++ b/packages/cli/dist/commands/setup/realtime/templates/defer/fastAndSlowFields/fastAndSlowFields.ts.template @@ -0,0 +1,14 @@ +import { logger } from 'src/lib/logger' + +const wait = (time: number) => + new Promise((resolve) => setTimeout(resolve, time)) + +export const fastField = async () => { + return 'I am fast' +} + +export const slowField = async (_, { waitFor = 5000 }) => { + logger.debug('waiting on slowField') + await wait(waitFor) + return 'I am slow' +} diff --git a/packages/cli/dist/commands/setup/realtime/templates/liveQueries/auctions/auctions.sdl.ts.template b/packages/cli/dist/commands/setup/realtime/templates/liveQueries/auctions/auctions.sdl.ts.template new file mode 100644 index 0000000000..791ed36e3d --- /dev/null +++ b/packages/cli/dist/commands/setup/realtime/templates/liveQueries/auctions/auctions.sdl.ts.template @@ -0,0 +1,27 @@ +// api/src/graphql/auctions.sdl.ts + +export const schema = gql` + type Query { + auction(id: ID!): Auction @requireAuth + } + + type Auction { + id: ID! + title: String! + highestBid: Bid + bids: [Bid!]! + } + + type Bid { + amount: Int! + } + + type Mutation { + bid(input: BidInput!): Bid @requireAuth + } + + input BidInput { + auctionId: ID! + amount: Int! + } +` diff --git a/packages/cli/dist/commands/setup/realtime/templates/liveQueries/auctions/auctions.ts.template b/packages/cli/dist/commands/setup/realtime/templates/liveQueries/auctions/auctions.ts.template new file mode 100644 index 0000000000..0f062065a6 --- /dev/null +++ b/packages/cli/dist/commands/setup/realtime/templates/liveQueries/auctions/auctions.ts.template @@ -0,0 +1,73 @@ +// api/src/services/auctions/auctions.ts +import type { LiveQueryStorageMechanism } from '@redwoodjs/realtime' + +import { logger } from 'src/lib/logger' + +const auctions = [ + { id: '1', title: 'RedwoodJS Logo Shirt', bids: [{ amount: 20 }] }, + { id: '2', title: 'RedwoodJS Lapel Pin', bids: [{ amount: 5 }] }, + { id: '3', title: 'RedwoodJS Beanie', bids: [{ amount: 15 }] }, + { id: '4', title: 'RedwoodJS Dad Hat', bids: [{ amount: 20 }] }, + { id: '5', title: 'RedwoodJS Skater Hat', bids: [{ amount: 20 }] }, +] + +/** + * To test this live query, run the following in the GraphQL Playground: + * + * query GetCurrentAuctionBids @live { + * auction(id: "1") { + * bids { + * amount + * } + * highestBid { + * amount + * } + * id + * title + * } + * } + * + * And then make a bid with the following mutation: + * + * mutation MakeBid { + * bid(input: {auctionId: "1", amount: 10}) { + * amount + * } + * } + */ +export const auction = async ({ id }) => { + const foundAuction = auctions.find((a) => a.id === id) + logger.debug({ id, auction: foundAuction }, 'auction') + return foundAuction +} + +export const bid = async ( + { input }, + { context }: { context: { liveQueryStore: LiveQueryStorageMechanism } } +) => { + const { auctionId, amount } = input + + const index = auctions.findIndex((a) => a.id === auctionId) + + const bid = { amount } + + auctions[index].bids.push(bid) + logger.debug({ auctionId, bid }, 'Added bid to auction') + + const key = `Auction:${auctionId}` + context.liveQueryStore.invalidate(key) + + logger.debug({ key }, 'Invalidated auction key in liveQueryStore') + + return bid +} + +export const Auction = { + highestBid: (obj, { root }) => { + const [max] = root.bids.sort((a, b) => b.amount - a.amount) + + logger.debug({ obj, root }, 'highestBid') + + return max + }, +} diff --git a/packages/cli/dist/commands/setup/realtime/templates/realtime.ts.template b/packages/cli/dist/commands/setup/realtime/templates/realtime.ts.template new file mode 100644 index 0000000000..db55b686b6 --- /dev/null +++ b/packages/cli/dist/commands/setup/realtime/templates/realtime.ts.template @@ -0,0 +1,46 @@ +import { RedwoodRealtimeOptions } from '@redwoodjs/realtime' + +import subscriptions from 'src/subscriptions/**/*.{js,ts}' + +// if using a Redis store +// import { Redis } from 'ioredis' +// const publishClient = new Redis() +// const subscribeClient = new Redis() + +/** + * Configure RedwoodJS Realtime + * + * See https://redwoodjs.com/docs/realtime + * + * Realtime supports Live Queries and Subscriptions over GraphQL SSE. + * + * Live Queries are GraphQL queries that are automatically re-run when the data they depend on changes. + * + * Subscriptions are GraphQL queries that are run when a client subscribes to a channel. + * + * Redwood Realtime + * - uses a publish/subscribe model to broadcast data to clients. + * - uses a store to persist Live Query and Subscription data. + * - and enable defer and stream directives to improve latency + * for clients by sending data the most important data as soon as it's ready. + * + * Redwood Realtime supports in-memory and Redis stores: + * - In-memory stores are useful for development and testing. + * - Redis stores are useful for production. + * + */ +export const realtime: RedwoodRealtimeOptions = { + subscriptions: { + subscriptions, + store: 'in-memory', + // if using a Redis store + // store: { redis: { publishClient, subscribeClient } }, + }, + liveQueries: { + store: 'in-memory', + // if using a Redis store + // store: { redis: { publishClient, subscribeClient } }, + }, + // To enable defer and streaming, set to true. + // enableDeferStream: true, +} diff --git a/packages/cli/dist/commands/setup/realtime/templates/stream/alphabet/alphabet.sdl.template b/packages/cli/dist/commands/setup/realtime/templates/stream/alphabet/alphabet.sdl.template new file mode 100644 index 0000000000..44e478cb84 --- /dev/null +++ b/packages/cli/dist/commands/setup/realtime/templates/stream/alphabet/alphabet.sdl.template @@ -0,0 +1,9 @@ +export const schema = gql` + type Query { + """ + A field that spells out the letters of the alphabet + Maybe you want to @stream this field ;) + """ + alphabet: [String!]! @skipAuth + } +` diff --git a/packages/cli/dist/commands/setup/realtime/templates/stream/alphabet/alphabet.ts.template b/packages/cli/dist/commands/setup/realtime/templates/stream/alphabet/alphabet.ts.template new file mode 100644 index 0000000000..a3ffde7e1d --- /dev/null +++ b/packages/cli/dist/commands/setup/realtime/templates/stream/alphabet/alphabet.ts.template @@ -0,0 +1,31 @@ +import { Repeater } from '@redwoodjs/realtime' + +import { logger } from 'src/lib/logger' + +export const alphabet = async () => { + return new Repeater(async (push, stop) => { + const letters = 'abcdefghijklmnopqrstuvwxyz'.split('') + + const publish = () => { + const letter = letters.shift() + + if (letter) { + logger.debug({ letter }, 'publishing letter...') + push(letter) + } + + if (letters.length === 0) { + stop() + } + } + + const interval = setInterval(publish, 1000) + + stop.then(() => { + logger.debug('cancel') + clearInterval(interval) + }) + + publish() + }) +} diff --git a/packages/cli/dist/commands/setup/realtime/templates/subscriptions/countdown/countdown.ts.template b/packages/cli/dist/commands/setup/realtime/templates/subscriptions/countdown/countdown.ts.template new file mode 100644 index 0000000000..2c87356fcf --- /dev/null +++ b/packages/cli/dist/commands/setup/realtime/templates/subscriptions/countdown/countdown.ts.template @@ -0,0 +1,58 @@ +import gql from 'graphql-tag' + +import { Repeater } from '@redwoodjs/realtime' + +import { logger } from 'src/lib/logger' + +export const schema = gql` + type Subscription { + countdown(from: Int!, interval: Int!): Int! @requireAuth + } +` + +/** + * To test this Countdown subscription, run the following in the GraphQL Playground: + * + * subscription CountdownFromInterval { + * countdown(from: 100, interval: 10) + * } + */ +const countdown = { + countdown: { + subscribe: ( + _, + { + from = 100, + interval = 10, + }: { + from: number + interval: number + } + ) => + new Repeater((push, stop) => { + function decrement() { + from -= interval + + if (from < 0) { + logger.debug({ from }, 'stopping as countdown is less than 0') + stop() + } + + logger.debug({ from }, 'pushing countdown value ...') + push(from) + } + + decrement() + + const delay = setInterval(decrement, 500) + + stop.then(() => { + clearInterval(delay) + logger.debug('stopping countdown') + }) + }), + resolve: (payload: number) => payload, + }, +} + +export default countdown diff --git a/packages/cli/dist/commands/setup/realtime/templates/subscriptions/newMessage/newMessage.ts.template b/packages/cli/dist/commands/setup/realtime/templates/subscriptions/newMessage/newMessage.ts.template new file mode 100644 index 0000000000..2d5085a905 --- /dev/null +++ b/packages/cli/dist/commands/setup/realtime/templates/subscriptions/newMessage/newMessage.ts.template @@ -0,0 +1,57 @@ +import gql from 'graphql-tag' + +import type { PubSub } from '@redwoodjs/realtime' + +import { logger } from 'src/lib/logger' + +export const schema = gql` + type Subscription { + newMessage(roomId: ID!): Message! @requireAuth + } +` +export type NewMessageChannel = { + newMessage: [roomId: string, payload: { from: string; body: string }] +} + +export type NewMessageChannelType = PubSub + +/** + * To test this NewMessage subscription, run the following in one GraphQL Playground to subscribe: + * + * subscription ListenForNewMessagesInRoom { + * newMessage(roomId: "1") { + * body + * from + * } + * } + * + * + * And run the following in another GraphQL Playground to publish and send a message to the room: + * + * mutation SendMessageToRoom { + * sendMessage(input: {roomId: "1", from: "hello", body: "bob"}) { + * body + * from + * } + * } + */ +const newMessage = { + newMessage: { + subscribe: ( + _, + { roomId }, + { pubSub }: { pubSub: NewMessageChannelType } + ) => { + logger.debug({ roomId }, 'newMessage subscription') + + return pubSub.subscribe('newMessage', roomId) + }, + resolve: (payload) => { + logger.debug({ payload }, 'newMessage subscription resolve') + + return payload + }, + }, +} + +export default newMessage diff --git a/packages/cli/dist/commands/setup/realtime/templates/subscriptions/newMessage/rooms.sdl.ts.template b/packages/cli/dist/commands/setup/realtime/templates/subscriptions/newMessage/rooms.sdl.ts.template new file mode 100644 index 0000000000..fa2336888a --- /dev/null +++ b/packages/cli/dist/commands/setup/realtime/templates/subscriptions/newMessage/rooms.sdl.ts.template @@ -0,0 +1,20 @@ +export const schema = gql` + type Message { + from: String + body: String + } + + type Query { + room(id: ID!): [Message!]! @skipAuth + } + + input SendMessageInput { + roomId: ID! + from: String! + body: String! + } + + type Mutation { + sendMessage(input: SendMessageInput!): Message! @skipAuth + } +` diff --git a/packages/cli/dist/commands/setup/realtime/templates/subscriptions/newMessage/rooms.ts.template b/packages/cli/dist/commands/setup/realtime/templates/subscriptions/newMessage/rooms.ts.template new file mode 100644 index 0000000000..1eacea090f --- /dev/null +++ b/packages/cli/dist/commands/setup/realtime/templates/subscriptions/newMessage/rooms.ts.template @@ -0,0 +1,20 @@ +import type { SendMessageInput } from 'types/graphql' + +import type { NewMessageChannelType } from 'src/subscriptions/newMessage/newMessage' + +import { logger } from 'src/lib/logger' + +export const room = ({ id }) => [id] + +export const sendMessage = async ( + { input }: { input: SendMessageInput }, + { context }: { context: { pubSub: NewMessageChannelType } } +) => { + logger.debug({ input }, 'sending message ....') + + const { roomId, from, body } = input + + context.pubSub.publish('newMessage', roomId, { from, body }) + + return input +} diff --git a/packages/cli/dist/commands/setup/server-file/serverFile.js b/packages/cli/dist/commands/setup/server-file/serverFile.js new file mode 100644 index 0000000000..6f06b5ac05 --- /dev/null +++ b/packages/cli/dist/commands/setup/server-file/serverFile.js @@ -0,0 +1,68 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var serverFile_exports = {}; +__export(serverFile_exports, { + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(serverFile_exports); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +const command = "server-file"; +const description = "Setup the server file"; +function builder(yargs) { + yargs.option("force", { + alias: "f", + default: false, + description: "Overwrite existing configuration", + type: "boolean" + }).option("verbose", { + alias: "v", + default: false, + description: "Print more logs", + type: "boolean" + }); +} +async function handler(options) { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "setup server-file", + force: options.force, + verbose: options.verbose + }); + const { handler: handler2 } = await import("./serverFileHandler.js"); + return handler2(options); +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/setup/server-file/serverFileHandler.js b/packages/cli/dist/commands/setup/server-file/serverFileHandler.js new file mode 100644 index 0000000000..a3112110a0 --- /dev/null +++ b/packages/cli/dist/commands/setup/server-file/serverFileHandler.js @@ -0,0 +1,88 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var serverFileHandler_exports = {}; +__export(serverFileHandler_exports, { + handler: () => handler, + setupServerFileTasks: () => setupServerFileTasks +}); +module.exports = __toCommonJS(serverFileHandler_exports); +var import_path = __toESM(require("path")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_listr2 = require("listr2"); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_telemetry = require("@redwoodjs/telemetry"); +var import_lib = require("../../../lib"); +var import_colors = __toESM(require("../../../lib/colors")); +var import_project = require("../../../lib/project"); +const { version } = JSON.parse( + import_fs_extra.default.readFileSync(import_path.default.resolve(__dirname, "../../../../package.json"), "utf-8") +); +function setupServerFileTasks({ force = false } = {}) { + return [ + { + title: "Adding the server file...", + task: () => { + const ts = (0, import_project.isTypeScriptProject)(); + const serverFilePath = import_path.default.join( + (0, import_lib.getPaths)().api.src, + `server.${ts ? "ts" : "js"}` + ); + const serverFileTemplateContent = import_fs_extra.default.readFileSync( + import_path.default.join(__dirname, "templates", "server.ts.template"), + "utf-8" + ); + const setupScriptContent = ts ? serverFileTemplateContent : (0, import_lib.transformTSToJS)(serverFilePath, serverFileTemplateContent); + return [ + (0, import_lib.writeFile)(serverFilePath, setupScriptContent, { + overwriteExisting: force + }) + ]; + } + }, + (0, import_cli_helpers.addApiPackages)([`@redwoodjs/api-server@${version}`]) + ]; +} +async function handler({ force, verbose }) { + const tasks = new import_listr2.Listr(setupServerFileTasks({ force }), { + rendererOptions: { collapseSubtasks: false, persistentOutput: true }, + renderer: verbose ? "verbose" : "default" + }); + try { + await tasks.run(); + } catch (e) { + (0, import_telemetry.errorTelemetry)(process.argv, e.message); + console.error(import_colors.default.error(e.message)); + process.exit(e?.exitCode || 1); + } +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + handler, + setupServerFileTasks +}); diff --git a/packages/cli/dist/commands/setup/server-file/templates/server.ts.template b/packages/cli/dist/commands/setup/server-file/templates/server.ts.template new file mode 100644 index 0000000000..429b9430a9 --- /dev/null +++ b/packages/cli/dist/commands/setup/server-file/templates/server.ts.template @@ -0,0 +1,13 @@ +import { createServer } from '@redwoodjs/api-server' + +import { logger } from 'src/lib/logger' + +async function main() { + const server = await createServer({ + logger, + }) + + await server.start() +} + +main() diff --git a/packages/cli/dist/commands/setup/tsconfig/tsconfig.js b/packages/cli/dist/commands/setup/tsconfig/tsconfig.js new file mode 100644 index 0000000000..3a551e60ed --- /dev/null +++ b/packages/cli/dist/commands/setup/tsconfig/tsconfig.js @@ -0,0 +1,62 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var tsconfig_exports = {}; +__export(tsconfig_exports, { + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(tsconfig_exports); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +const command = "tsconfig"; +const description = "Set up tsconfig for web and api sides"; +const builder = (yargs) => { + yargs.option("force", { + alias: "f", + default: false, + description: "Overwrite existing tsconfig.json files", + type: "boolean" + }); +}; +const handler = async (options) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "setup tsconfig", + force: options.force + }); + const { handler: handler2 } = await import("./tsconfigHandler.js"); + return handler2(options); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/setup/tsconfig/tsconfigHandler.js b/packages/cli/dist/commands/setup/tsconfig/tsconfigHandler.js new file mode 100644 index 0000000000..276636d3dd --- /dev/null +++ b/packages/cli/dist/commands/setup/tsconfig/tsconfigHandler.js @@ -0,0 +1,90 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var tsconfigHandler_exports = {}; +__export(tsconfigHandler_exports, { + handler: () => handler +}); +module.exports = __toCommonJS(tsconfigHandler_exports); +var import_path = __toESM(require("path")); +var import_chalk = __toESM(require("chalk")); +var import_listr2 = require("listr2"); +var import_telemetry = require("@redwoodjs/telemetry"); +var import_lib = require("../../../lib"); +var import_colors = __toESM(require("../../../lib/colors")); +const handler = async ({ force }) => { + const installedRwVersion = (0, import_lib.getInstalledRedwoodVersion)(); + const GITHUB_VERSION_TAG = installedRwVersion.match("canary") ? "main" : `v${installedRwVersion}`; + const CRWA_TEMPLATE_URL = `https://raw.githubusercontent.com/redwoodjs/redwood/${GITHUB_VERSION_TAG}/packages/create-redwood-app/templates/ts`; + const tasks = new import_listr2.Listr( + [ + { + title: "Creating tsconfig in web", + task: () => { + const webConfigPath = import_path.default.join((0, import_lib.getPaths)().web.base, "tsconfig.json"); + const templateUrl = `${CRWA_TEMPLATE_URL}/web/tsconfig.json`; + return (0, import_lib.saveRemoteFileToDisk)(templateUrl, webConfigPath, { + overwriteExisting: force + }); + } + }, + { + title: "Creating tsconfig in api", + task: () => { + const webConfigPath = import_path.default.join((0, import_lib.getPaths)().api.base, "tsconfig.json"); + const templateUrl = `${CRWA_TEMPLATE_URL}/api/tsconfig.json`; + return (0, import_lib.saveRemoteFileToDisk)(templateUrl, webConfigPath, { + overwriteExisting: force + }); + } + }, + { + title: "One more thing...", + task: (_ctx, task) => { + task.title = `One more thing... + + ${import_colors.default.green("Quick link to the docs on configuring TypeScript")} + ${import_chalk.default.hex("#e8e8e8")("https://redwoodjs.com/docs/typescript")} + `; + } + } + ], + { rendererOptions: { collapseSubtasks: false } } + ); + try { + await tasks.run(); + } catch (e) { + (0, import_telemetry.errorTelemetry)(process.argv, e.message); + console.error(import_colors.default.error(e.message)); + process.exit(e?.exitCode || 1); + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + handler +}); diff --git a/packages/cli/dist/commands/setup/ui/libraries/chakra-ui.js b/packages/cli/dist/commands/setup/ui/libraries/chakra-ui.js new file mode 100644 index 0000000000..c8ef88cd7a --- /dev/null +++ b/packages/cli/dist/commands/setup/ui/libraries/chakra-ui.js @@ -0,0 +1,154 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var chakra_ui_exports = {}; +__export(chakra_ui_exports, { + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(chakra_ui_exports); +var import_path = __toESM(require("path")); +var import_execa = __toESM(require("execa")); +var import_listr2 = require("listr2"); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_lib = require("../../../../lib"); +var import_colors = __toESM(require("../../../../lib/colors")); +var import_configureStorybook = __toESM(require("../../../../lib/configureStorybook.js")); +var import_extendFile = require("../../../../lib/extendFile"); +const command = "chakra-ui"; +const description = "Set up Chakra UI"; +function builder(yargs) { + yargs.option("force", { + alias: "f", + default: false, + description: "Overwrite existing configuration", + type: "boolean" + }); + yargs.option("install", { + alias: "i", + default: true, + description: "Install packages", + type: "boolean" + }); +} +const CHAKRA_THEME_AND_COMMENTS = `// This object will be used to override Chakra-UI theme defaults. +// See https://chakra-ui.com/docs/styled-system/theming/theme for theming options +const theme = {} +export default theme +`; +async function handler({ force, install }) { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "setup ui chakra-ui", + force, + install + }); + const rwPaths = (0, import_lib.getPaths)(); + const packages = [ + "@chakra-ui/react@^2", + "@emotion/react@^11", + "@emotion/styled@^11", + "framer-motion@^9" + ]; + const tasks = new import_listr2.Listr( + [ + { + title: "Installing packages...", + skip: () => !install, + task: () => { + return new import_listr2.Listr( + [ + { + title: `Install ${packages.join(", ")}`, + task: async () => { + await (0, import_execa.default)("yarn", ["workspace", "web", "add", ...packages]); + } + } + ], + { rendererOptions: { collapseSubtasks: false } } + ); + } + }, + { + title: "Setting up Chakra UI...", + skip: () => (0, import_extendFile.fileIncludes)(rwPaths.web.app, "ChakraProvider"), + task: () => (0, import_extendFile.extendJSXFile)(rwPaths.web.app, { + insertComponent: { + name: "ChakraProvider", + props: { theme: "extendedTheme" }, + within: "RedwoodProvider", + insertBefore: "" + }, + imports: [ + "import { ChakraProvider, ColorModeScript, extendTheme } from '@chakra-ui/react'", + "import * as theme from 'config/chakra.config'" + ], + moduleScopeLines: ["const extendedTheme = extendTheme(theme)"] + }) + }, + { + title: `Creating Theme File...`, + task: () => { + (0, import_lib.writeFile)( + import_path.default.join(rwPaths.web.config, "chakra.config.js"), + CHAKRA_THEME_AND_COMMENTS, + { overwriteExisting: force } + ); + } + }, + { + title: "Configure Storybook...", + // skip this task if the user's storybook config already includes "withChakra" + skip: () => (0, import_extendFile.fileIncludes)(rwPaths.web.storybookConfig, "withChakra"), + task: async () => (0, import_configureStorybook.default)( + import_path.default.join( + __dirname, + "..", + "templates", + "chakra.storybook.preview.tsx.template" + ) + ) + } + ], + { rendererOptions: { collapseSubtasks: false } } + ); + try { + await tasks.run(); + } catch (e) { + console.error(import_colors.default.error(e.message)); + process.exit(e?.exitCode || 1); + } +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/setup/ui/libraries/mantine.js b/packages/cli/dist/commands/setup/ui/libraries/mantine.js new file mode 100644 index 0000000000..3c661b2a15 --- /dev/null +++ b/packages/cli/dist/commands/setup/ui/libraries/mantine.js @@ -0,0 +1,200 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var mantine_exports = {}; +__export(mantine_exports, { + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(mantine_exports); +var import_path = __toESM(require("path")); +var import_execa = __toESM(require("execa")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_listr2 = require("listr2"); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_lib = require("../../../../lib"); +var import_colors = __toESM(require("../../../../lib/colors")); +var import_configureStorybook = __toESM(require("../../../../lib/configureStorybook.js")); +var import_extendFile = require("../../../../lib/extendFile"); +const command = "mantine"; +const description = "Set up Mantine UI"; +const ALL_KEYWORD = "all"; +const ALL_MANTINE_PACKAGES = [ + "core", + "dates", + "dropzone", + "form", + "hooks", + "modals", + "notifications", + "prism", + "rte", + "spotlight" +]; +const MANTINE_THEME_AND_COMMENTS = `import { createTheme } from '@mantine/core' + +/** + * This object will be used to override Mantine theme defaults. + * See https://mantine.dev/theming/mantine-provider/#theme-object for theming options + * @type {import("@mantine/core").MantineThemeOverride} + */ +const theme = {} + +export default createTheme(theme) +`; +function builder(yargs) { + yargs.option("force", { + alias: "f", + default: false, + description: "Overwrite existing configuration", + type: "boolean" + }); + yargs.option("install", { + alias: "i", + default: true, + description: "Install packages", + type: "boolean" + }); + yargs.option("packages", { + alias: "p", + default: ["core", "hooks"], + description: `Mantine packages to install. Specify '${ALL_KEYWORD}' to install all packages. Default: ['core', 'hooks']`, + type: "array" + }); +} +async function handler({ force, install, packages }) { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "setup ui mantine", + force, + install, + packages + }); + const rwPaths = (0, import_lib.getPaths)(); + const configFilePath = import_path.default.join(rwPaths.web.config, "mantine.config.js"); + const installPackages = (packages.includes(ALL_KEYWORD) ? ALL_MANTINE_PACKAGES : packages).map((pack) => `@mantine/${pack}`).concat("postcss", "postcss-preset-mantine", "postcss-simple-vars"); + const tasks = new import_listr2.Listr( + [ + { + title: "Installing packages...", + skip: () => !install, + task: () => { + return new import_listr2.Listr( + [ + { + title: `Install ${installPackages.join(", ")}`, + task: async () => { + await (0, import_execa.default)("yarn", [ + "workspace", + "web", + "add", + "-D", + "@emotion/react", + ...installPackages + ]); + } + } + ], + { rendererOptions: { collapseSubtasks: false } } + ); + } + }, + { + title: "Setting up Mantine...", + skip: () => (0, import_extendFile.fileIncludes)(rwPaths.web.app, "MantineProvider"), + task: () => (0, import_extendFile.extendJSXFile)(rwPaths.web.app, { + insertComponent: { + name: "MantineProvider", + props: { theme: "theme" }, + within: "RedwoodProvider" + }, + imports: [ + "import { MantineProvider } from '@mantine/core'", + "import theme from 'config/mantine.config'", + "import '@mantine/core/styles.css'" + ] + }) + }, + { + title: "Configuring PostCSS...", + task: () => { + const postCSSConfigPath = rwPaths.web.postcss; + if (!force && import_fs_extra.default.existsSync(postCSSConfigPath)) { + throw new Error( + "PostCSS config already exists.\nUse --force to override existing config." + ); + } else { + const postCSSConfig = import_fs_extra.default.readFileSync( + import_path.default.join( + __dirname, + "../templates/mantine-postcss.config.js.template" + ), + "utf-8" + ); + return import_fs_extra.default.outputFileSync(postCSSConfigPath, postCSSConfig); + } + } + }, + { + title: `Creating Theme File...`, + task: () => { + (0, import_lib.writeFile)(configFilePath, MANTINE_THEME_AND_COMMENTS, { + overwriteExisting: force + }); + } + }, + { + title: "Configure Storybook...", + skip: () => (0, import_extendFile.fileIncludes)(rwPaths.web.storybookPreviewConfig, "withMantine"), + task: async () => (0, import_configureStorybook.default)( + import_path.default.join( + __dirname, + "..", + "templates", + "mantine.storybook.preview.tsx.template" + ) + ) + } + ], + { rendererOptions: { collapseSubtasks: false } } + ); + try { + await tasks.run(); + } catch (e) { + console.error(import_colors.default.error(e.message)); + process.exit(e?.exitCode || 1); + } +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/setup/ui/libraries/tailwindcss.js b/packages/cli/dist/commands/setup/ui/libraries/tailwindcss.js new file mode 100644 index 0000000000..94494980d4 --- /dev/null +++ b/packages/cli/dist/commands/setup/ui/libraries/tailwindcss.js @@ -0,0 +1,394 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var tailwindcss_exports = {}; +__export(tailwindcss_exports, { + aliases: () => aliases, + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(tailwindcss_exports); +var import_path = __toESM(require("path")); +var import_execa = __toESM(require("execa")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_fs_extra2 = require("fs-extra"); +var import_listr2 = require("listr2"); +var import_terminal_link = __toESM(require("terminal-link")); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_telemetry = require("@redwoodjs/telemetry"); +var import_lib = require("../../../../lib"); +var import_colors = __toESM(require("../../../../lib/colors")); +const command = "tailwindcss"; +const aliases = ["tailwind", "tw"]; +const description = "Set up tailwindcss and PostCSS"; +const builder = (yargs) => { + yargs.option("force", { + alias: "f", + default: false, + description: "Overwrite existing configuration", + type: "boolean" + }); + yargs.option("install", { + alias: "i", + default: true, + description: "Install packages", + type: "boolean" + }); +}; +const tailwindDirectives = [ + "@tailwind base;", + "@tailwind components;", + "@tailwind utilities;" +]; +const tailwindDirectivesExist = (indexCSS) => tailwindDirectives.every( + (tailwindDirective) => indexCSS.includes(tailwindDirective) +); +const tailwindImportsAndNotes = [ + "/**", + " * START --- SETUP TAILWINDCSS EDIT", + " *", + " * `yarn rw setup ui tailwindcss` placed these directives here", + " * to inject Tailwind's styles into your CSS.", + " * For more information, see: https://tailwindcss.com/docs/installation", + " */", + ...tailwindDirectives, + "/**", + " * END --- SETUP TAILWINDCSS EDIT", + " */\n" +]; +const recommendedVSCodeExtensions = [ + "csstools.postcss", + "bradlc.vscode-tailwindcss" +]; +const recommendationTexts = { + "csstools.postcss": (0, import_terminal_link.default)( + "PostCSS Language Support", + "https://marketplace.visualstudio.com/items?itemName=csstools.postcss" + ), + "bradlc.vscode-tailwindcss": (0, import_terminal_link.default)( + "Tailwind CSS IntelliSense", + "https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss" + ) +}; +async function recommendExtensionsToInstall() { + if (!(0, import_lib.usingVSCode)()) { + return; + } + let recommendations = []; + try { + const { stdout } = await (0, import_execa.default)("code", ["--list-extensions"]); + const installedExtensions = stdout.split("\n").map((ext) => ext.trim()); + recommendations = recommendedVSCodeExtensions.filter( + (ext) => !installedExtensions.includes(ext) + ); + } catch { + recommendations = recommendedVSCodeExtensions; + } + if (recommendations.length > 0) { + console.log(); + console.log( + import_colors.default.info( + "For the best experience we recommend that you install the following " + (recommendations.length === 1 ? "extension:" : "extensions:") + ) + ); + recommendations.forEach((extension) => { + console.log(import_colors.default.info(" " + recommendationTexts[extension])); + }); + } +} +const handler = async ({ force, install }) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "setup ui tailwindcss", + force, + install + }); + const rwPaths = (0, import_lib.getPaths)(); + const projectPackages = ["prettier-plugin-tailwindcss@^0.5.12"]; + const webWorkspacePackages = [ + "postcss", + "postcss-loader", + "tailwindcss", + "autoprefixer" + ]; + const tasks = new import_listr2.Listr( + [ + { + title: "Installing project-wide packages...", + skip: () => !install, + task: () => { + return new import_listr2.Listr( + [ + { + title: `Install ${projectPackages.join(", ")}`, + task: async () => { + const yarnVersion = await (0, import_execa.default)("yarn", ["--version"]); + const isYarnV1 = yarnVersion.stdout.trim().startsWith("1"); + await (0, import_execa.default)("yarn", [ + "add", + "-D", + ...isYarnV1 ? ["-W"] : [], + ...projectPackages + ]); + } + } + ], + { rendererOptions: { collapseSubtasks: false } } + ); + } + }, + { + title: "Installing web side packages...", + skip: () => !install, + task: () => { + return new import_listr2.Listr( + [ + { + title: `Install ${webWorkspacePackages.join(", ")}`, + task: async () => { + await (0, import_execa.default)("yarn", [ + "workspace", + "web", + "add", + "-D", + ...webWorkspacePackages + ]); + } + } + ], + { rendererOptions: { collapseSubtasks: false } } + ); + } + }, + { + title: "Configuring PostCSS...", + task: () => { + const postCSSConfigPath = rwPaths.web.postcss; + if (!force && import_fs_extra.default.existsSync(postCSSConfigPath)) { + throw new Error( + "PostCSS config already exists.\nUse --force to override existing config." + ); + } else { + const postCSSConfig = import_fs_extra.default.readFileSync( + import_path.default.join(__dirname, "../templates/postcss.config.js.template"), + "utf-8" + ); + return (0, import_fs_extra2.outputFileSync)(postCSSConfigPath, postCSSConfig); + } + } + }, + { + title: "Initializing Tailwind CSS...", + task: async () => { + const tailwindConfigPath = import_path.default.join( + rwPaths.web.config, + "tailwind.config.js" + ); + if (import_fs_extra.default.existsSync(tailwindConfigPath)) { + if (force) { + import_fs_extra.default.unlinkSync(tailwindConfigPath); + } else { + throw new Error( + "Tailwind CSS config already exists.\nUse --force to override existing config." + ); + } + } + await (0, import_execa.default)("yarn", ["tailwindcss", "init", tailwindConfigPath], { + cwd: rwPaths.web.base + }); + const tailwindConfig = import_fs_extra.default.readFileSync(tailwindConfigPath, "utf-8"); + const newTailwindConfig = tailwindConfig.replace( + "content: []", + "content: ['src/**/*.{js,jsx,ts,tsx}']" + ); + import_fs_extra.default.writeFileSync(tailwindConfigPath, newTailwindConfig); + } + }, + { + title: "Adding directives to index.css...", + task: (_ctx, task) => { + const INDEX_CSS_PATH = import_path.default.join(rwPaths.web.src, "index.css"); + const indexCSS = import_fs_extra.default.readFileSync(INDEX_CSS_PATH, "utf-8"); + if (tailwindDirectivesExist(indexCSS)) { + task.skip("Directives already exist in index.css"); + } else { + const newIndexCSS = tailwindImportsAndNotes.join("\n") + indexCSS; + import_fs_extra.default.writeFileSync(INDEX_CSS_PATH, newIndexCSS); + } + } + }, + { + title: "Updating 'scaffold.css' to use tailwind classes...", + skip: () => { + return !import_fs_extra.default.existsSync(import_path.default.join(rwPaths.web.src, "scaffold.css")) && "No 'scaffold.css' file to update"; + }, + task: async (_ctx, task) => { + const overrideScaffoldCss = force || await task.prompt({ + type: "Confirm", + message: "Do you want to override your 'scaffold.css' to use tailwind classes?" + }); + if (!overrideScaffoldCss) { + return task.skip("Skipping 'scaffold.css' update"); + } + const tailwindScaffoldTemplate = import_fs_extra.default.readFileSync( + import_path.default.join( + __dirname, + "..", + "..", + "..", + "generate", + "scaffold", + "templates", + "assets", + "scaffold.tailwind.css.template" + ) + ); + import_fs_extra.default.writeFileSync( + import_path.default.join(rwPaths.web.src, "scaffold.css"), + tailwindScaffoldTemplate + ); + } + }, + { + title: "Adding recommended VS Code extensions to project settings...", + skip: () => !(0, import_lib.usingVSCode)() && "Looks like you're not using VS Code", + task: () => { + const VS_CODE_EXTENSIONS_PATH = import_path.default.join( + rwPaths.base, + ".vscode/extensions.json" + ); + let originalExtensionsJson = { recommendations: [] }; + if (import_fs_extra.default.existsSync(VS_CODE_EXTENSIONS_PATH)) { + const originalExtensionsFile = import_fs_extra.default.readFileSync( + VS_CODE_EXTENSIONS_PATH, + "utf-8" + ); + originalExtensionsJson = JSON.parse(originalExtensionsFile); + } + const newExtensionsJson = { + ...originalExtensionsJson, + recommendations: [ + ...originalExtensionsJson.recommendations, + ...recommendedVSCodeExtensions + ] + }; + import_fs_extra.default.writeFileSync( + VS_CODE_EXTENSIONS_PATH, + JSON.stringify(newExtensionsJson, null, 2) + ); + } + }, + { + title: "Adding tailwind config entry in prettier...", + task: async (_ctx) => { + const prettierConfigPath = import_path.default.join( + rwPaths.base, + "prettier.config.js" + ); + const prettierConfig = import_fs_extra.default.readFileSync(prettierConfigPath, "utf-8"); + const tailwindConfigPath = import_path.default.relative( + rwPaths.base, + import_path.default.posix.join(rwPaths.web.config, "tailwind.config.js") + ).replaceAll("\\", "/"); + let newPrettierConfig = prettierConfig; + if (newPrettierConfig.includes("tailwindConfig: ")) { + if (force) { + newPrettierConfig = newPrettierConfig.replace( + /tailwindConfig: .*(,)?/, + `tailwindConfig: './${tailwindConfigPath}',` + ); + } else { + throw new Error( + "tailwindConfig setting already exists in prettier configuration.\nUse --force to override existing config." + ); + } + } else { + newPrettierConfig = newPrettierConfig.replace( + /,(\n\s*)(\}\n?)$/, + `, + tailwindConfig: './${tailwindConfigPath}',$1$2` + ); + } + import_fs_extra.default.writeFileSync(prettierConfigPath, newPrettierConfig); + } + }, + { + title: "Adding tailwind prettier plugin...", + task: async (_ctx, task) => { + const prettierConfigPath = import_path.default.join( + rwPaths.base, + "prettier.config.js" + ); + const prettierConfig = import_fs_extra.default.readFileSync(prettierConfigPath, "utf-8"); + let newPrettierConfig = prettierConfig; + if (newPrettierConfig.includes("plugins: [")) { + const pluginsMatch = newPrettierConfig.match( + /plugins: \[[\sa-z\(\)'\-,]*]/ + ); + const matched = pluginsMatch && pluginsMatch[0]; + if (matched && (matched.includes("'prettier-plugin-tailwindcss'") || matched.includes('"prettier-plugin-tailwindcss"'))) { + task.skip( + "tailwindcss-plugin-prettier already required in plugins" + ); + } else { + newPrettierConfig = newPrettierConfig.replace( + /plugins: \[(\n\s+)*/, + `plugins: [$'prettier-plugin-tailwindcss',$1` + ); + } + } else { + newPrettierConfig = newPrettierConfig.replace( + /,(\n\s*)(\}\n?)$/, + `, + plugins: ['prettier-plugin-tailwindcss'],$1$2` + ); + } + import_fs_extra.default.writeFileSync(prettierConfigPath, newPrettierConfig); + } + } + ], + { rendererOptions: { collapseSubtasks: false } } + ); + try { + await tasks.run(); + await recommendExtensionsToInstall(); + } catch (e) { + (0, import_telemetry.errorTelemetry)(process.argv, e.message); + console.error(import_colors.default.error(e.message)); + process.exit(e?.exitCode || 1); + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + aliases, + builder, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/setup/ui/templates/chakra.storybook.preview.tsx.template b/packages/cli/dist/commands/setup/ui/templates/chakra.storybook.preview.tsx.template new file mode 100644 index 0000000000..627ffd2f02 --- /dev/null +++ b/packages/cli/dist/commands/setup/ui/templates/chakra.storybook.preview.tsx.template @@ -0,0 +1,20 @@ +import * as React from 'react' + +import { ChakraProvider, extendTheme } from '@chakra-ui/react' +import type { StoryFn } from '@storybook/react' +import theme from 'config/chakra.config' + +const extendedTheme = extendTheme(theme) + +/** + * @param { import("@storybook/react").StoryFn} StoryFn + */ +const withChakra = (StoryFn: StoryFn) => { + return ( + + + + ) +} + +export const decorators = [withChakra] diff --git a/packages/cli/dist/commands/setup/ui/templates/mantine-postcss.config.js.template b/packages/cli/dist/commands/setup/ui/templates/mantine-postcss.config.js.template new file mode 100644 index 0000000000..a49b840e2f --- /dev/null +++ b/packages/cli/dist/commands/setup/ui/templates/mantine-postcss.config.js.template @@ -0,0 +1,35 @@ +/** + * `postcss-preset-mantine` includes the following PostCSS plugins: + * - postcss-nested + * - postcss-mixins with Mantine specific mixins + * - Custom plugin with em/rem functions + * Read more: https://mantine.dev/styles/postcss-preset/ + + * `postcss-simple-vars` enables use of SCSS-like variables inside CSS files: + * ```postcss + * $blue : #056ef0; + * $width-sm: rem(37.5); + * $selector: .my-component + * + * @media (min-width: $width-sm) { + * $selector { + * background: $blue; + * } + * } + * ``` + * Read more: https://github.com/postcss/postcss-simple-vars +*/ +module.exports = { + plugins: [ + require('postcss-preset-mantine'), + require('postcss-simple-vars')({ + variables: { + 'mantine-breakpoint-xs': '36em', + 'mantine-breakpoint-sm': '48em', + 'mantine-breakpoint-md': '62em', + 'mantine-breakpoint-lg': '75em', + 'mantine-breakpoint-xl': '88em', + }, + }), + ], +} diff --git a/packages/cli/dist/commands/setup/ui/templates/mantine.storybook.preview.tsx.template b/packages/cli/dist/commands/setup/ui/templates/mantine.storybook.preview.tsx.template new file mode 100644 index 0000000000..08c50d4785 --- /dev/null +++ b/packages/cli/dist/commands/setup/ui/templates/mantine.storybook.preview.tsx.template @@ -0,0 +1,20 @@ +import * as React from 'react' + +import { MantineProvider } from '@mantine/core' +import type { StoryFn } from '@storybook/react' +import theme from 'config/mantine.config' + +import '@mantine/core/styles.css' + +/** + * @param { import("@storybook/react").StoryFn} StoryFn + */ +const withMantine = (StoryFn: StoryFn) => { + return ( + + + + ) +} + +export const decorators = [withMantine] diff --git a/packages/cli/dist/commands/setup/ui/templates/postcss.config.js.template b/packages/cli/dist/commands/setup/ui/templates/postcss.config.js.template new file mode 100644 index 0000000000..3cdce19941 --- /dev/null +++ b/packages/cli/dist/commands/setup/ui/templates/postcss.config.js.template @@ -0,0 +1,9 @@ +const path = require('path') + +module.exports = { + plugins: [ + require('tailwindcss/nesting'), + require('tailwindcss')(path.resolve(__dirname, 'tailwind.config.js')), + require('autoprefixer'), + ], +} diff --git a/packages/cli/dist/commands/setup/ui/ui.js b/packages/cli/dist/commands/setup/ui/ui.js new file mode 100644 index 0000000000..3b654f7f74 --- /dev/null +++ b/packages/cli/dist/commands/setup/ui/ui.js @@ -0,0 +1,50 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var ui_exports = {}; +__export(ui_exports, { + builder: () => builder, + command: () => command, + description: () => description +}); +module.exports = __toCommonJS(ui_exports); +var import_terminal_link = __toESM(require("terminal-link")); +const command = "ui "; +const description = "Set up a UI design or style library"; +const builder = (yargs) => yargs.commandDir("./libraries").demandCommand().epilogue( + `Also see the ${(0, import_terminal_link.default)( + "Redwood CLI Reference", + "https://redwoodjs.com/docs/cli-commands#setup-ui" + )}` +); +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description +}); diff --git a/packages/cli/dist/commands/setup/vite/templates/vite.config.ts.template b/packages/cli/dist/commands/setup/vite/templates/vite.config.ts.template new file mode 100644 index 0000000000..9a5f6b2ecc --- /dev/null +++ b/packages/cli/dist/commands/setup/vite/templates/vite.config.ts.template @@ -0,0 +1,19 @@ +import dns from 'dns' + +import { defineConfig, UserConfig } from 'vite' + +import redwood from '@redwoodjs/vite' + +// See: https://vitejs.dev/config/server-options.html#server-host +// So that Vite will load on local instead of 127.0.0.1 +dns.setDefaultResultOrder('verbatim') + +/** + * https://vitejs.dev/config/ + * @type {import('vite').UserConfig} + */ +const viteConfig: UserConfig = { + plugins: [redwood()], +} + +export default defineConfig(viteConfig) diff --git a/packages/cli/dist/commands/setup/vite/vite.js b/packages/cli/dist/commands/setup/vite/vite.js new file mode 100644 index 0000000000..4bd21dd901 --- /dev/null +++ b/packages/cli/dist/commands/setup/vite/vite.js @@ -0,0 +1,75 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var vite_exports = {}; +__export(vite_exports, { + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(vite_exports); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +const command = "vite"; +const description = "Configure the web side to use Vite, instead of Webpack"; +const builder = (yargs) => { + yargs.option("force", { + alias: "f", + default: false, + description: "Overwrite existing configuration", + type: "boolean" + }); + yargs.option("verbose", { + alias: "v", + default: false, + description: "Print more logs", + type: "boolean" + }); + yargs.option("add-package", { + default: true, + description: "Allows you to skip adding the @redwoodjs/vite package. Useful for testing", + type: "boolean" + }); +}; +const handler = async (options) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "setup vite", + force: options.force, + verbose: options.verbose, + addPackage: options.addPackage + }); + const { handler: handler2 } = await import("./viteHandler.js"); + return handler2(options); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/setup/vite/viteHandler.js b/packages/cli/dist/commands/setup/vite/viteHandler.js new file mode 100644 index 0000000000..f08f3fea5d --- /dev/null +++ b/packages/cli/dist/commands/setup/vite/viteHandler.js @@ -0,0 +1,125 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var viteHandler_exports = {}; +__export(viteHandler_exports, { + handler: () => handler +}); +module.exports = __toCommonJS(viteHandler_exports); +var import_path = __toESM(require("path")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_listr2 = require("listr2"); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_project_config = require("@redwoodjs/project-config"); +var import_telemetry = require("@redwoodjs/telemetry"); +var import_lib = require("../../../lib"); +var import_colors = __toESM(require("../../../lib/colors")); +var import_project = require("../../../lib/project"); +const { version } = JSON.parse( + import_fs_extra.default.readFileSync(import_path.default.resolve(__dirname, "../../../../package.json"), "utf-8") +); +const handler = async ({ force, verbose, addPackage }) => { + const ts = (0, import_project.isTypeScriptProject)(); + const tasks = new import_listr2.Listr( + [ + { + title: "Adding vite.config.js...", + task: () => { + const viteConfigPath = `${(0, import_lib.getPaths)().web.base}/vite.config.${ts ? "ts" : "js"}`; + const templateContent = import_fs_extra.default.readFileSync( + import_path.default.resolve(__dirname, "templates", "vite.config.ts.template"), + "utf-8" + ); + const viteConfigContent = ts ? templateContent : (0, import_lib.transformTSToJS)(viteConfigPath, templateContent); + return (0, import_lib.writeFile)(viteConfigPath, viteConfigContent, { + overwriteExisting: force + }); + } + }, + { + title: "Checking bundler isn't set to webpack...", + task: (_ctx, task) => { + const redwoodTomlPath = (0, import_project_config.getConfigPath)(); + const configContent = import_fs_extra.default.readFileSync(redwoodTomlPath, "utf-8"); + if (configContent.includes('bundler = "webpack"')) { + throw new Error( + 'You have the bundler set to webpack in your redwood.toml. Remove this line, or change it to "vite" and try again.' + ); + } else { + task.skip("Vite already configured as the bundler"); + } + } + }, + { + title: "Creating new entry point in `web/src/entry.client.{jsx,tsx}`...", + task: () => { + const entryPointFile = import_path.default.join( + (0, import_lib.getPaths)().web.src, + `entry.client.${ts ? "tsx" : "jsx"}` + ); + const content = import_fs_extra.default.readFileSync( + import_path.default.join( + (0, import_lib.getPaths)().base, + // NOTE we're copying over the index.js before babel transform + "node_modules/@redwoodjs/web/src/entry/index.js" + ), + "utf-8" + ).replace("~redwood-app-root", "./App"); + return (0, import_lib.writeFile)(entryPointFile, content, { + overwriteExisting: force + }); + } + }, + { + // @NOTE: make sure its added as a dev package. + ...(0, import_cli_helpers.addWebPackages)(["-D", `@redwoodjs/vite@${version}`]), + title: "Adding @redwoodjs/vite dev dependency to web side...", + skip: () => { + if (!addPackage) { + return "Skipping package install, you will need to add @redwoodjs/vite manaually as a dev-dependency on the web workspace"; + } + } + } + ], + { + rendererOptions: { collapseSubtasks: false }, + renderer: verbose ? "verbose" : "default" + } + ); + try { + await tasks.run(); + } catch (e) { + (0, import_telemetry.errorTelemetry)(process.argv, e.message); + console.error(import_colors.default.error(e.message)); + process.exit(e?.exitCode || 1); + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + handler +}); diff --git a/packages/cli/dist/commands/setup/webpack/templates/webpack.config.js.template b/packages/cli/dist/commands/setup/webpack/templates/webpack.config.js.template new file mode 100644 index 0000000000..a38ffcb928 --- /dev/null +++ b/packages/cli/dist/commands/setup/webpack/templates/webpack.config.js.template @@ -0,0 +1,14 @@ +/** @returns {import('webpack').Configuration} Webpack Configuration */ +module.exports = (config, { mode }) => { + if (mode === 'development') { + // Add dev plugin + } + + // Add custom rules for your project + // config.module.rules.push(YOUR_RULE) + + // Add custom plugins for your project + // config.plugins.push(YOUR_PLUGIN) + + return config +} diff --git a/packages/cli/dist/commands/setup/webpack/webpack.js b/packages/cli/dist/commands/setup/webpack/webpack.js new file mode 100644 index 0000000000..c52a83a073 --- /dev/null +++ b/packages/cli/dist/commands/setup/webpack/webpack.js @@ -0,0 +1,62 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var webpack_exports = {}; +__export(webpack_exports, { + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(webpack_exports); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +const command = "webpack"; +const description = "Set up webpack in your project so you can add custom config"; +const builder = (yargs) => { + yargs.option("force", { + alias: "f", + default: false, + description: "Overwrite existing configuration", + type: "boolean" + }); +}; +const handler = async (options) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "setup webpack", + force: options.force + }); + const { handler: handler2 } = await import("./webpackHandler.js"); + return handler2(options); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/setup/webpack/webpackHandler.js b/packages/cli/dist/commands/setup/webpack/webpackHandler.js new file mode 100644 index 0000000000..2763371e4a --- /dev/null +++ b/packages/cli/dist/commands/setup/webpack/webpackHandler.js @@ -0,0 +1,89 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var webpackHandler_exports = {}; +__export(webpackHandler_exports, { + handler: () => handler +}); +module.exports = __toCommonJS(webpackHandler_exports); +var import_path = __toESM(require("path")); +var import_chalk = __toESM(require("chalk")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_listr2 = require("listr2"); +var import_telemetry = require("@redwoodjs/telemetry"); +var import_lib = require("../../../lib"); +var import_colors = __toESM(require("../../../lib/colors")); +const handler = async ({ force }) => { + const tasks = new import_listr2.Listr( + [ + { + title: "Adding webpack file to your web folder...", + task: () => { + const webpackConfigFile = `${(0, import_lib.getPaths)().web.config}/webpack.config.js`; + return (0, import_lib.writeFile)( + webpackConfigFile, + import_fs_extra.default.readFileSync( + import_path.default.resolve( + __dirname, + "templates", + "webpack.config.js.template" + ) + ).toString(), + { overwriteExisting: force } + ); + } + }, + { + title: "One more thing...", + task: (_ctx, task) => { + task.title = `One more thing... + + ${import_colors.default.green( + "Quick link to the docs on configuring custom webpack config:" + )} + ${import_chalk.default.hex("#e8e8e8")( + "https://redwoodjs.com/docs/webpack-configuration#configuring-webpack" + )} + `; + } + } + ], + { rendererOptions: { collapseSubtasks: false } } + ); + try { + await tasks.run(); + } catch (e) { + (0, import_telemetry.errorTelemetry)(process.argv, e.message); + console.error(import_colors.default.error(e.message)); + process.exit(e?.exitCode || 1); + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + handler +}); diff --git a/packages/cli/dist/commands/studio.js b/packages/cli/dist/commands/studio.js new file mode 100644 index 0000000000..6ab501d09c --- /dev/null +++ b/packages/cli/dist/commands/studio.js @@ -0,0 +1,60 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var studio_exports = {}; +__export(studio_exports, { + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(studio_exports); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +const command = "studio"; +const description = "Run the Redwood development studio"; +function builder(yargs) { + yargs.option("open", { + default: true, + description: "Open the studio in your browser" + }); +} +async function handler(options) { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "studio", + open: options.open + }); + const { handler: handler2 } = await import("./studioHandler.js"); + return handler2(options); +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/studioHandler.js b/packages/cli/dist/commands/studioHandler.js new file mode 100644 index 0000000000..a99725a1b9 --- /dev/null +++ b/packages/cli/dist/commands/studioHandler.js @@ -0,0 +1,96 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var studioHandler_exports = {}; +__export(studioHandler_exports, { + assertRedwoodVersion: () => assertRedwoodVersion, + handler: () => handler +}); +module.exports = __toCommonJS(studioHandler_exports); +var import_node_path = __toESM(require("node:path")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_semver = __toESM(require("semver")); +var import_project_config = require("@redwoodjs/project-config"); +var import_packages = require("../lib/packages"); +const handler = async (options) => { + try { + if (!(0, import_packages.isModuleInstalled)("@redwoodjs/studio")) { + const minVersions = ["7.0.0-canary.889", "7.x", "8.0.0-0"]; + assertRedwoodVersion(minVersions); + console.log( + "The studio package is not installed, installing it for you, this may take a moment..." + ); + await (0, import_packages.installModule)("@redwoodjs/studio", "11"); + console.log("Studio package installed successfully."); + const installedRealtime = await (0, import_packages.installModule)("@redwoodjs/realtime"); + if (installedRealtime) { + console.log( + "Added @redwoodjs/realtime to your project, as it's used by Studio" + ); + } + const installedApiServer = await (0, import_packages.installModule)("@redwoodjs/api-server"); + if (installedApiServer) { + console.log( + "Added @redwoodjs/api-server to your project, as it's used by Studio" + ); + } + } + const { serve } = await import("@redwoodjs/studio"); + await serve({ open: options.open, enableWeb: true }); + } catch (e) { + console.log("Cannot start the development studio"); + console.log(e); + process.exit(1); + } +}; +function assertRedwoodVersion(minVersions) { + const rwVersion = getProjectRedwoodVersion(); + const coercedRwVersion = import_semver.default.coerce(rwVersion); + if (minVersions.some((minVersion) => { + const v = import_semver.default.valid(minVersion) || import_semver.default.coerce(minVersion); + const coercedMin = import_semver.default.coerce(minVersion); + return import_semver.default.gte(rwVersion, v) && (coercedRwVersion.major === coercedMin.major ? import_semver.default.prerelease(rwVersion)?.[0] === import_semver.default.prerelease(v)?.[0] : true); + })) { + return; + } + console.error( + `The studio command requires Redwood version ${minVersions[0]} or greater, you are using ${rwVersion}.` + ); + process.exit(1); +} +function getProjectRedwoodVersion() { + const { devDependencies } = import_fs_extra.default.readJSONSync( + import_node_path.default.join((0, import_project_config.getPaths)().base, "package.json") + ); + return devDependencies["@redwoodjs/core"]; +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + assertRedwoodVersion, + handler +}); diff --git a/packages/cli/dist/commands/test.js b/packages/cli/dist/commands/test.js new file mode 100644 index 0000000000..4f158fe23d --- /dev/null +++ b/packages/cli/dist/commands/test.js @@ -0,0 +1,81 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var test_exports = {}; +__export(test_exports, { + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(test_exports); +var import_terminal_link = __toESM(require("terminal-link")); +var import_colors = __toESM(require("../lib/colors")); +var import_project = require("../lib/project"); +const command = "test [filter..]"; +const description = "Run Jest tests. Defaults to watch mode"; +const builder = (yargs) => { + yargs.strict(false).positional("filter", { + default: (0, import_project.sides)(), + description: "Which side(s) to test, and/or a regular expression to match against your test files to filter by", + type: "array" + }).option("watch", { + describe: "Run tests related to changed files based on hg/git. Specify the name or path to a file to focus on a specific set of tests", + type: "boolean", + default: true + }).option("collect-coverage", { + describe: "Show test coverage summary and output info to coverage directory", + type: "boolean", + default: false + }).option("db-push", { + describe: "Syncs the test database with your Prisma schema without requiring a migration. It creates a test database if it doesn't already exist.", + type: "boolean", + default: true + }).epilogue( + `For all available flags, run jest cli directly ${import_colors.default.green( + "yarn jest --help" + )} + +Also see the ${(0, import_terminal_link.default)( + "Redwood CLI Reference", + "https://redwoodjs.com/docs/cli-commands#test" + )} +` + ); +}; +const handler = async (options) => { + const { handler: handler2 } = await import("./testHandler.js"); + return handler2(options); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/testHandler.js b/packages/cli/dist/commands/testHandler.js new file mode 100644 index 0000000000..2fc2351336 --- /dev/null +++ b/packages/cli/dist/commands/testHandler.js @@ -0,0 +1,169 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var testHandler_exports = {}; +__export(testHandler_exports, { + handler: () => handler +}); +module.exports = __toCommonJS(testHandler_exports); +var import_path = __toESM(require("path")); +var import_execa = __toESM(require("execa")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_project_config = require("@redwoodjs/project-config"); +var import_telemetry = require("@redwoodjs/telemetry"); +var import_lib = require("../lib"); +var import_colors = __toESM(require("../lib/colors")); +var project = __toESM(require("../lib/project")); +function isInGitRepository() { + try { + import_execa.default.commandSync("git rev-parse --is-inside-work-tree"); + return true; + } catch (e) { + return false; + } +} +function isInMercurialRepository() { + try { + import_execa.default.commandSync("hg --cwd . root"); + return true; + } catch (e) { + return false; + } +} +function isJestConfigFile(sides) { + for (let side of sides) { + try { + if (sides.includes(side)) { + const jestConfigExists = import_fs_extra.default.existsSync(import_path.default.join(side, "jest.config.js")) || import_fs_extra.default.existsSync(import_path.default.join(side, "jest.config.ts")); + if (!jestConfigExists) { + console.error( + import_colors.default.error( + ` +Error: Missing Jest config file ${side}/jest.config.js +To add this file, run \`npx @redwoodjs/codemods update-jest-config\` +` + ) + ); + throw new Error(`Error: Jest config file not found in ${side} side`); + } + } + } catch (e) { + (0, import_telemetry.errorTelemetry)(process.argv, e.message); + process.exit(e?.exitCode || 1); + } + } +} +const handler = async ({ + filter: filterParams = [], + watch = true, + collectCoverage = false, + dbPush = true, + ...others +}) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "test", + watch, + collectCoverage, + dbPush + }); + const rwjsPaths = (0, import_lib.getPaths)(); + const forwardJestFlags = Object.keys(others).flatMap((flagName) => { + if (["watch", "collect-coverage", "db-push", "$0", "_"].includes(flagName)) { + return []; + } else { + const flagValue = others[flagName]; + if (Array.isArray(flagValue)) { + return flagValue.flatMap((val) => { + return [flagName.length > 1 ? `--${flagName}` : `-${flagName}`, val]; + }); + } else { + return [ + flagName.length > 1 ? `--${flagName}` : `-${flagName}`, + flagValue + ]; + } + } + }); + const sides = filterParams.filter( + (filterString) => project.sides().includes(filterString) + ); + const jestFilterArgs = [ + ...filterParams.filter( + (filterString) => !project.sides().includes(filterString) + ) + ]; + const jestArgs = [ + ...jestFilterArgs, + ...forwardJestFlags, + collectCoverage ? "--collectCoverage" : null, + "--passWithNoTests" + ].filter((flagOrValue) => flagOrValue !== null); + if (watch && !process.env.CI && !collectCoverage) { + const hasSourceControl = isInGitRepository() || isInMercurialRepository(); + jestArgs.push(hasSourceControl ? "--watch" : "--watchAll"); + } + if (!sides.length) { + project.sides().forEach((side) => sides.push(side)); + } + if (sides.length > 0) { + jestArgs.push("--projects", ...sides); + } + isJestConfigFile(sides); + try { + const cacheDirDb = `file:${(0, import_project_config.ensurePosixPath)( + rwjsPaths.generated.base + )}/test.db`; + const DATABASE_URL = process.env.TEST_DATABASE_URL || cacheDirDb; + if (sides.includes("api") && !dbPush) { + process.env.SKIP_DB_PUSH = "1"; + } + const runCommand = async () => { + await (0, import_execa.default)("yarn jest", jestArgs, { + cwd: rwjsPaths.base, + shell: true, + stdio: "inherit", + env: { DATABASE_URL } + }); + }; + if (watch) { + await runCommand(); + } else { + await (0, import_telemetry.timedTelemetry)(process.argv, { type: "test" }, async () => { + await runCommand(); + }); + } + } catch (e) { + (0, import_telemetry.errorTelemetry)(process.argv, e.message); + process.exit(e?.exitCode || 1); + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + handler +}); diff --git a/packages/cli/dist/commands/ts-to-js.js b/packages/cli/dist/commands/ts-to-js.js new file mode 100644 index 0000000000..6145c7d5a6 --- /dev/null +++ b/packages/cli/dist/commands/ts-to-js.js @@ -0,0 +1,43 @@ +"use strict"; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var ts_to_js_exports = {}; +__export(ts_to_js_exports, { + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(ts_to_js_exports); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_ts2js = require("@redwoodjs/internal/dist/ts2js"); +var import_project_config = require("@redwoodjs/project-config"); +const command = "ts-to-js"; +const description = "[DEPRECATED]\nConvert a TypeScript project to JavaScript"; +const handler = () => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "ts-to-js" + }); + (0, import_ts2js.convertTsProjectToJs)((0, import_project_config.getPaths)().base); + (0, import_ts2js.convertTsScriptsToJs)((0, import_project_config.getPaths)().base); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/type-check.js b/packages/cli/dist/commands/type-check.js new file mode 100644 index 0000000000..278aad1fe8 --- /dev/null +++ b/packages/cli/dist/commands/type-check.js @@ -0,0 +1,79 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var type_check_exports = {}; +__export(type_check_exports, { + aliases: () => aliases, + builder: () => builder, + command: () => command, + description: () => description, + handler: () => handler +}); +module.exports = __toCommonJS(type_check_exports); +var import_terminal_link = __toESM(require("terminal-link")); +var import_project = require("../lib/project"); +const command = "type-check [sides..]"; +const aliases = ["tsc", "tc"]; +const description = "Run a TypeScript compiler check on your project"; +const builder = (yargs) => { + yargs.strict(false).positional("sides", { + default: (0, import_project.sides)(), + description: "Which side(s) to run a typecheck on", + type: "array" + }).option("prisma", { + type: "boolean", + default: true, + description: "Generate the Prisma client" + }).option("generate", { + type: "boolean", + default: true, + description: "Regenerate types within the project" + }).option("verbose", { + alias: "v", + default: false, + description: "Print more", + type: "boolean" + }).epilogue( + `Also see the ${(0, import_terminal_link.default)( + "Redwood CLI Reference", + "https://redwoodjs.com/docs/cli-commands#type-check" + )}` + ); +}; +const handler = async (options) => { + const { handler: handler2 } = await import("./type-checkHandler.js"); + return handler2(options); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + aliases, + builder, + command, + description, + handler +}); diff --git a/packages/cli/dist/commands/type-checkHandler.js b/packages/cli/dist/commands/type-checkHandler.js new file mode 100644 index 0000000000..56a1210d6f --- /dev/null +++ b/packages/cli/dist/commands/type-checkHandler.js @@ -0,0 +1,105 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var type_checkHandler_exports = {}; +__export(type_checkHandler_exports, { + handler: () => handler +}); +module.exports = __toCommonJS(type_checkHandler_exports); +var import_path = __toESM(require("path")); +var import_concurrently = __toESM(require("concurrently")); +var import_execa = __toESM(require("execa")); +var import_listr2 = require("listr2"); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_upgrade = require("../commands/upgrade"); +var import_lib = require("../lib"); +var import_generatePrismaClient = require("../lib/generatePrismaClient"); +const handler = async ({ sides, verbose, prisma, generate }) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "type-check", + sides: JSON.stringify(sides), + verbose, + prisma, + generate + }); + const typeCheck = async () => { + let conclusiveExitCode = 0; + const yarnVersion = await (0, import_upgrade.getCmdMajorVersion)("yarn"); + const tscForAllSides = sides.map((side) => { + const projectDir = import_path.default.join((0, import_lib.getPaths)().base, side); + return { + cwd: projectDir, + command: `yarn ${yarnVersion > 1 ? "" : "-s"} tsc --noEmit --skipLibCheck` + }; + }); + const { result } = (0, import_concurrently.default)(tscForAllSides, { + group: true, + raw: true + }); + try { + await result; + } catch (err) { + if (err.length) { + const exitCodes = err.map((e) => e?.exitCode).filter(Boolean); + conclusiveExitCode = Math.max(...exitCodes); + } + } + return conclusiveExitCode; + }; + if (generate && prisma) { + await (0, import_generatePrismaClient.generatePrismaClient)({ + verbose, + schema: (0, import_lib.getPaths)().api.dbSchema + }); + } + if (generate) { + await new import_listr2.Listr( + [ + { + title: "Generating types", + task: () => (0, import_execa.default)("yarn rw-gen", { + shell: true, + stdio: verbose ? "inherit" : "ignore" + }) + } + ], + { + renderer: verbose && "verbose", + rendererOptions: { collapseSubtasks: false } + } + ).run(); + } + const exitCode = await typeCheck(); + if (exitCode > 0) { + process.exitCode = exitCode; + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + handler +}); diff --git a/packages/cli/dist/commands/upgrade.js b/packages/cli/dist/commands/upgrade.js new file mode 100644 index 0000000000..3cf6e6d9e0 --- /dev/null +++ b/packages/cli/dist/commands/upgrade.js @@ -0,0 +1,410 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var upgrade_exports = {}; +__export(upgrade_exports, { + builder: () => builder, + command: () => command, + description: () => description, + getCmdMajorVersion: () => getCmdMajorVersion, + handler: () => handler, + validateTag: () => validateTag +}); +module.exports = __toCommonJS(upgrade_exports); +var import_path = __toESM(require("path")); +var import_execa = __toESM(require("execa")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_latest_version = __toESM(require("latest-version")); +var import_listr2 = require("listr2"); +var import_terminal_link = __toESM(require("terminal-link")); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_project_config = require("@redwoodjs/project-config"); +var import_lib = require("../lib"); +var import_colors = __toESM(require("../lib/colors")); +var import_generatePrismaClient = require("../lib/generatePrismaClient"); +const command = "upgrade"; +const description = "Upgrade all @redwoodjs packages via interactive CLI"; +const builder = (yargs) => { + yargs.example( + "rw upgrade -t 0.20.1-canary.5", + "Specify a version. URL for Version History:\nhttps://www.npmjs.com/package/@redwoodjs/core" + ).option("dry-run", { + alias: "d", + description: "Check for outdated packages without upgrading", + type: "boolean" + }).option("tag", { + alias: "t", + description: '[choices: "latest", "rc", "next", "canary", "experimental", or a specific-version (see example below)] WARNING: "canary", "rc" and "experimental" are unstable releases! And "canary" releases include breaking changes often requiring codemods if upgrading a project.', + requiresArg: true, + type: "string", + coerce: validateTag + }).option("verbose", { + alias: "v", + description: "Print verbose logs", + type: "boolean", + default: false + }).option("dedupe", { + description: "Skip dedupe check with --no-dedupe", + type: "boolean", + default: true + }).epilogue( + `Also see the ${(0, import_terminal_link.default)( + "Redwood CLI Reference for the upgrade command", + "https://redwoodjs.com/docs/cli-commands#upgrade" + )}. +And the ${(0, import_terminal_link.default)( + "GitHub releases page", + "https://github.com/redwoodjs/redwood/releases" + )} for more information on the current release.` + ); +}; +const SEMVER_REGEX = /(?<=^v?|\sv?)(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-(?:0|[1-9]\d*|[\da-z-]*[a-z-][\da-z-]*)(?:\.(?:0|[1-9]\d*|[\da-z-]*[a-z-][\da-z-]*))*)?(?:\+[\da-z-]+(?:\.[\da-z-]+)*)?(?=$|\s)/i; +const isValidSemver = (string) => { + return SEMVER_REGEX.test(string); +}; +const isValidRedwoodJSTag = (tag) => { + return ["rc", "canary", "latest", "next", "experimental"].includes(tag); +}; +const validateTag = (tag) => { + const isTagValid = isValidSemver(tag) || isValidRedwoodJSTag(tag); + if (!isTagValid) { + throw new Error( + import_colors.default.error( + "Invalid tag supplied. Supported values: 'rc', 'canary', 'latest', 'next', 'experimental', or a valid semver version\n" + ) + ); + } + return tag; +}; +const handler = async ({ dryRun, tag, verbose, dedupe }) => { + (0, import_cli_helpers.recordTelemetryAttributes)({ + command: "upgrade", + dryRun, + tag, + verbose, + dedupe + }); + const tasks = new import_listr2.Listr( + [ + { + title: "Checking latest version", + task: async (ctx) => setLatestVersionToContext(ctx, tag) + }, + { + title: "Updating your Redwood version", + task: (ctx) => updateRedwoodDepsForAllSides(ctx, { dryRun, verbose }), + enabled: (ctx) => !!ctx.versionToUpgradeTo + }, + { + title: "Updating other packages in your package.json(s)", + task: (ctx) => updatePackageVersionsFromTemplate(ctx, { dryRun, verbose }), + enabled: (ctx) => ctx.versionToUpgradeTo?.includes("canary") + }, + { + title: "Running yarn install", + task: (ctx) => yarnInstall(ctx, { dryRun, verbose }), + skip: () => dryRun + }, + { + title: "Refreshing the Prisma client", + task: (_ctx, task) => refreshPrismaClient(task, { verbose }), + skip: () => dryRun + }, + { + title: "De-duplicating dependencies", + skip: () => dryRun || !dedupe, + task: (_ctx, task) => dedupeDeps(task, { verbose }) + }, + { + title: "One more thing..", + task: (ctx, task) => { + const version = ctx.versionToUpgradeTo; + const messageSections = [ + `One more thing... + + ${import_colors.default.warning( + `\u{1F389} Your project has been upgraded to RedwoodJS ${version}!` + )} + +` + ]; + if ([void 0, "latest", "rc"].includes(tag)) { + messageSections.push( + ` Please review the release notes for any manual steps: + \u2756 ${(0, import_terminal_link.default)( + `Redwood community discussion`, + `https://community.redwoodjs.com/search?q=${version}%23announcements` + )} + \u2756 ${(0, import_terminal_link.default)( + `GitHub Release notes`, + `https://github.com/redwoodjs/redwood/releases` + // intentionally not linking to specific version + )} + +` + ); + } + if (tag) { + const additionalMessages = []; + if (!(0, import_project_config.getConfig)().notifications.versionUpdates.includes(tag) && isValidRedwoodJSTag(tag)) { + additionalMessages.push( + ` \u2756 You may want to update your redwood.toml config so that \`notifications.versionUpdates\` includes "${tag}" +` + ); + } + if (additionalMessages.length > 0) { + messageSections.push( + ` \u{1F4E2} ${import_colors.default.warning(`We'd also like to remind you that:`)} +`, + ...additionalMessages + ); + } + } + task.title = messageSections.join("").trimEnd(); + } + } + ], + { + renderer: verbose && "verbose", + rendererOptions: { collapseSubtasks: false } + } + ); + await tasks.run(); +}; +async function yarnInstall({ verbose }) { + const yarnVersion = await getCmdMajorVersion("yarn"); + try { + await (0, import_execa.default)( + "yarn install", + yarnVersion > 1 ? [] : ["--force", "--non-interactive"], + { + shell: true, + stdio: verbose ? "inherit" : "pipe", + cwd: (0, import_lib.getPaths)().base + } + ); + } catch (e) { + throw new Error( + "Could not finish installation. Please run `yarn install` and then `yarn dedupe`, before continuing" + ); + } +} +async function setLatestVersionToContext(ctx, tag) { + try { + const foundVersion = await (0, import_latest_version.default)( + "@redwoodjs/core", + tag ? { version: tag } : {} + ); + ctx.versionToUpgradeTo = foundVersion; + return foundVersion; + } catch (e) { + throw new Error("Could not find the latest version"); + } +} +function updatePackageJsonVersion(pkgPath, version, { dryRun, verbose }) { + const pkg = JSON.parse( + import_fs_extra.default.readFileSync(import_path.default.join(pkgPath, "package.json"), "utf-8") + ); + if (pkg.dependencies) { + for (const depName of Object.keys(pkg.dependencies).filter( + (x) => x.startsWith("@redwoodjs/") && x !== "@redwoodjs/studio" + )) { + if (verbose || dryRun) { + console.log(` - ${depName}: ${pkg.dependencies[depName]} => ${version}`); + } + pkg.dependencies[depName] = `${version}`; + } + } + if (pkg.devDependencies) { + for (const depName of Object.keys(pkg.devDependencies).filter( + (x) => x.startsWith("@redwoodjs/") && x !== "@redwoodjs/studio" + )) { + if (verbose || dryRun) { + console.log( + ` - ${depName}: ${pkg.devDependencies[depName]} => ${version}` + ); + } + pkg.devDependencies[depName] = `${version}`; + } + } + if (!dryRun) { + import_fs_extra.default.writeFileSync( + import_path.default.join(pkgPath, "package.json"), + JSON.stringify(pkg, void 0, 2) + ); + } +} +function updateRedwoodDepsForAllSides(ctx, options) { + if (!ctx.versionToUpgradeTo) { + throw new Error("Failed to upgrade"); + } + const updatePaths = [ + (0, import_lib.getPaths)().base, + (0, import_lib.getPaths)().api.base, + (0, import_lib.getPaths)().web.base + ]; + return new import_listr2.Listr( + updatePaths.map((basePath) => { + const pkgJsonPath = import_path.default.join(basePath, "package.json"); + return { + title: `Updating ${pkgJsonPath}`, + task: () => updatePackageJsonVersion(basePath, ctx.versionToUpgradeTo, options), + skip: () => !import_fs_extra.default.existsSync(pkgJsonPath) + }; + }) + ); +} +async function updatePackageVersionsFromTemplate(ctx, { dryRun, verbose }) { + if (!ctx.versionToUpgradeTo) { + throw new Error("Failed to upgrade"); + } + const packageJsons = [ + { + basePath: (0, import_lib.getPaths)().base, + url: "https://raw.githubusercontent.com/redwoodjs/redwood/main/packages/create-redwood-app/templates/ts/package.json" + }, + { + basePath: (0, import_lib.getPaths)().api.base, + url: "https://raw.githubusercontent.com/redwoodjs/redwood/main/packages/create-redwood-app/templates/ts/api/package.json" + }, + { + basePath: (0, import_lib.getPaths)().web.base, + url: "https://raw.githubusercontent.com/redwoodjs/redwood/main/packages/create-redwood-app/templates/ts/web/package.json" + } + ]; + return new import_listr2.Listr( + packageJsons.map(({ basePath, url }) => { + const pkgJsonPath = import_path.default.join(basePath, "package.json"); + return { + title: `Updating ${pkgJsonPath}`, + task: async () => { + const res = await fetch(url); + const text = await res.text(); + const templatePackageJson = JSON.parse(text); + const localPackageJsonText = import_fs_extra.default.readFileSync(pkgJsonPath, "utf-8"); + const localPackageJson = JSON.parse(localPackageJsonText); + Object.entries(templatePackageJson.dependencies || {}).forEach( + ([depName, depVersion]) => { + if (!depName.startsWith("@redwoodjs/")) { + if (verbose || dryRun) { + console.log( + ` - ${depName}: ${localPackageJson.dependencies[depName]} => ${depVersion}` + ); + } + localPackageJson.dependencies[depName] = depVersion; + } + } + ); + Object.entries(templatePackageJson.devDependencies || {}).forEach( + ([depName, depVersion]) => { + if (!depName.startsWith("@redwoodjs/")) { + if (verbose || dryRun) { + console.log( + ` - ${depName}: ${localPackageJson.devDependencies[depName]} => ${depVersion}` + ); + } + localPackageJson.devDependencies[depName] = depVersion; + } + } + ); + if (!dryRun) { + import_fs_extra.default.writeFileSync( + pkgJsonPath, + JSON.stringify(localPackageJson, null, 2) + ); + } + }, + skip: () => !import_fs_extra.default.existsSync(pkgJsonPath) + }; + }) + ); +} +async function refreshPrismaClient(task, { verbose }) { + try { + await (0, import_generatePrismaClient.generatePrismaClient)({ + verbose, + force: false, + schema: (0, import_lib.getPaths)().api.dbSchema + }); + } catch (e) { + task.skip("Refreshing the Prisma client caused an Error."); + console.log( + "You may need to update your prisma client manually: $ yarn rw prisma generate" + ); + console.log(import_colors.default.error(e.message)); + } +} +const getCmdMajorVersion = async (command2) => { + const { stdout } = await (0, import_execa.default)(command2, ["--version"], { + cwd: (0, import_lib.getPaths)().base + }); + if (!SEMVER_REGEX.test(stdout)) { + throw new Error(`Unable to verify ${command2} version.`); + } + const version = stdout.match(SEMVER_REGEX)[0]; + return parseInt(version.split(".")[0]); +}; +const dedupeDeps = async (task, { verbose }) => { + try { + const yarnVersion = await getCmdMajorVersion("yarn"); + const npxVersion = await getCmdMajorVersion("npx"); + let npxArgs = []; + if (npxVersion > 6) { + npxArgs = ["--yes"]; + } + const baseExecaArgsForDedupe = { + shell: true, + stdio: verbose ? "inherit" : "pipe", + cwd: (0, import_lib.getPaths)().base + }; + if (yarnVersion > 1) { + await (0, import_execa.default)("yarn", ["dedupe"], baseExecaArgsForDedupe); + } else { + await (0, import_execa.default)( + "npx", + [...npxArgs, "yarn-deduplicate"], + baseExecaArgsForDedupe + ); + } + } catch (e) { + console.log(import_colors.default.error(e.message)); + throw new Error( + "Could not finish de-duplication. For yarn 1.x, please run `npx yarn-deduplicate`, or for yarn 3 run `yarn dedupe` before continuing" + ); + } + await yarnInstall({ verbose }); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + builder, + command, + description, + getCmdMajorVersion, + handler, + validateTag +}); diff --git a/packages/cli/dist/index.d.js b/packages/cli/dist/index.d.js new file mode 100644 index 0000000000..3918c74e44 --- /dev/null +++ b/packages/cli/dist/index.d.js @@ -0,0 +1 @@ +"use strict"; diff --git a/packages/cli/dist/index.js b/packages/cli/dist/index.js new file mode 100755 index 0000000000..3ae1bd9970 --- /dev/null +++ b/packages/cli/dist/index.js @@ -0,0 +1,164 @@ +#!/usr/bin/env node +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var import_path = __toESM(require("path")); +var import_api = require("@opentelemetry/api"); +var import_fs_extra = __toESM(require("fs-extra")); +var import_helpers = require("yargs/helpers"); +var import_yargs = __toESM(require("yargs/yargs")); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_telemetry = require("@redwoodjs/telemetry"); +var buildCommand = __toESM(require("./commands/build")); +var checkCommand = __toESM(require("./commands/check")); +var consoleCommand = __toESM(require("./commands/console")); +var deployCommand = __toESM(require("./commands/deploy")); +var destroyCommand = __toESM(require("./commands/destroy")); +var devCommand = __toESM(require("./commands/dev")); +var execCommand = __toESM(require("./commands/exec")); +var experimentalCommand = __toESM(require("./commands/experimental")); +var generateCommand = __toESM(require("./commands/generate")); +var infoCommand = __toESM(require("./commands/info")); +var lintCommand = __toESM(require("./commands/lint")); +var prerenderCommand = __toESM(require("./commands/prerender")); +var prismaCommand = __toESM(require("./commands/prisma")); +var recordCommand = __toESM(require("./commands/record")); +var serveCommand = __toESM(require("./commands/serve")); +var setupCommand = __toESM(require("./commands/setup")); +var studioCommand = __toESM(require("./commands/studio")); +var testCommand = __toESM(require("./commands/test")); +var tstojsCommand = __toESM(require("./commands/ts-to-js")); +var typeCheckCommand = __toESM(require("./commands/type-check")); +var upgradeCommand = __toESM(require("./commands/upgrade")); +var import_lib = require("./lib"); +var import_exit = require("./lib/exit"); +var import_loadEnvFiles = require("./lib/loadEnvFiles"); +var updateCheck = __toESM(require("./lib/updateCheck")); +var import_plugin = require("./plugin"); +var import_telemetry2 = require("./telemetry/index"); +let { cwd, telemetry, help, version } = (0, import_helpers.Parser)((0, import_helpers.hideBin)(process.argv), { + // Telemetry is enabled by default, but can be disabled in two ways + // - by passing a `--telemetry false` option + // - by setting a `REDWOOD_DISABLE_TELEMETRY` env var + boolean: ["telemetry"], + default: { + telemetry: process.env.REDWOOD_DISABLE_TELEMETRY === void 0 || process.env.REDWOOD_DISABLE_TELEMETRY === "" + } +}); +cwd ??= process.env.RWJS_CWD; +try { + if (cwd) { + if (!import_fs_extra.default.existsSync(import_path.default.join(cwd, "redwood.toml"))) { + throw new Error(`Couldn't find a "redwood.toml" file in ${cwd}`); + } + } else { + const redwoodTOMLPath = (0, import_lib.findUp)("redwood.toml"); + if (!redwoodTOMLPath) { + throw new Error( + `Couldn't find up a "redwood.toml" file from ${process.cwd()}` + ); + } + cwd = import_path.default.dirname(redwoodTOMLPath); + } +} catch (error) { + console.error(error.message); + process.exit(1); +} +process.env.RWJS_CWD = cwd; +(0, import_loadEnvFiles.loadEnvFiles)(); +async function main() { + if (telemetry) { + (0, import_telemetry2.startTelemetry)(); + } + const tracer = import_api.trace.getTracer("redwoodjs"); + await tracer.startActiveSpan("cli", async (span) => { + const telemetryTimeoutTimer = setTimeout(() => { + (0, import_telemetry2.shutdownTelemetry)(); + }, 5 * 6e4); + if (version) { + (0, import_cli_helpers.recordTelemetryAttributes)({ command: "--version" }); + } + if (help) { + (0, import_cli_helpers.recordTelemetryAttributes)({ command: "--help" }); + } + try { + await runYargs(); + } catch (error) { + (0, import_exit.exitWithError)(error); + } + if (span?.isRecording()) { + span?.setStatus({ code: import_api.SpanStatusCode.OK }); + span?.end(); + } + clearTimeout(telemetryTimeoutTimer); + }); + if (telemetry) { + (0, import_telemetry2.shutdownTelemetry)(); + } +} +async function runYargs() { + const yarg = (0, import_yargs.default)((0, import_helpers.hideBin)(process.argv)).scriptName("rw").middleware( + [ + // We've already handled `cwd` above, but it may still be in `argv`. + // We don't need it anymore so let's get rid of it. + // Likewise for `telemetry`. + (argv) => { + delete argv.cwd; + delete argv.addEnvFiles; + delete argv["load-env-files"]; + delete argv.telemetry; + }, + telemetry && import_telemetry.telemetryMiddleware, + updateCheck.isEnabled() && updateCheck.updateCheckMiddleware + ].filter(Boolean) + ).option("cwd", { + describe: "Working directory to use (where `redwood.toml` is located)" + }).option("load-env-files", { + describe: "Load additional .env files. Values defined in files specified later override earlier ones.", + array: true + }).example( + "yarn rw exec migrateUsers --load-env-files stripe nakama", + "Run a script, also loading env vars from '.env.stripe' and '.env.nakama'" + ).option("telemetry", { + describe: "Whether to send anonymous usage telemetry to RedwoodJS", + boolean: true + // hidden: true, + }).example( + "yarn rw g page home /", + "Create a page component named 'Home' at path '/'" + ).demandCommand().strict().exitProcess(false).alias("h", "help").command(buildCommand).command(checkCommand).command(consoleCommand).command(deployCommand).command(destroyCommand).command(devCommand).command(execCommand).command(experimentalCommand).command(generateCommand).command(infoCommand).command(lintCommand).command(prerenderCommand).command(prismaCommand).command(recordCommand).command(serveCommand).command(setupCommand).command(studioCommand).command(testCommand).command(tstojsCommand).command(typeCheckCommand).command(upgradeCommand); + await (0, import_plugin.loadPlugins)(yarg); + await yarg.parse(process.argv.slice(2), {}, (err, _argv, output) => { + if (err) { + process.exitCode = 1; + } + if (output) { + if (err) { + console.error(output); + } else { + console.log(output); + } + } + }); +} +main(); diff --git a/packages/cli/dist/lib/background.js b/packages/cli/dist/lib/background.js new file mode 100644 index 0000000000..ae5251caba --- /dev/null +++ b/packages/cli/dist/lib/background.js @@ -0,0 +1,79 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var background_exports = {}; +__export(background_exports, { + spawnBackgroundProcess: () => spawnBackgroundProcess +}); +module.exports = __toCommonJS(background_exports); +var import_child_process = require("child_process"); +var import_os = __toESM(require("os")); +var import_path = __toESM(require("path")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_project_config = require("@redwoodjs/project-config"); +function spawnBackgroundProcess(name, cmd, args) { + const logDirectory = import_path.default.join((0, import_project_config.getPaths)().generated.base, "logs"); + import_fs_extra.default.ensureDirSync(logDirectory); + const safeName = name.replace(/[^a-z0-9]/gi, "_").toLowerCase(); + const logHeader = [ + `Starting log:`, + ` - Time: ${(/* @__PURE__ */ new Date()).toISOString()}`, + ` - Name: ${name} (${safeName})`, + ` - Command: ${cmd}`, + ` - Arguments: ${args.join(" ")}`, + "", + "" + ].join("\n"); + const stdout = import_fs_extra.default.openSync( + import_path.default.join(logDirectory, `${safeName}.out.log`), + "w" + ); + import_fs_extra.default.writeSync(stdout, logHeader); + const stderr = import_fs_extra.default.openSync( + import_path.default.join(logDirectory, `${safeName}.err.log`), + "w" + ); + import_fs_extra.default.writeSync(stderr, logHeader); + const spawnOptions = import_os.default.type() === "Windows_NT" ? { + // The following options run the process in the background without a console window, even though they don't look like they would. + // See https://github.com/nodejs/node/issues/21825#issuecomment-503766781 for information + detached: false, + windowsHide: false, + shell: true, + stdio: ["ignore", stdout, stderr] + } : { + detached: true, + stdio: ["ignore", stdout, stderr] + }; + const child = (0, import_child_process.spawn)(cmd, args, spawnOptions); + child.unref(); +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + spawnBackgroundProcess +}); diff --git a/packages/cli/dist/lib/colors.js b/packages/cli/dist/lib/colors.js new file mode 100644 index 0000000000..7d3c25ea20 --- /dev/null +++ b/packages/cli/dist/lib/colors.js @@ -0,0 +1,42 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var colors_exports = {}; +__export(colors_exports, { + default: () => colors_default +}); +module.exports = __toCommonJS(colors_exports); +var import_chalk = __toESM(require("chalk")); +var colors_default = { + error: import_chalk.default.bold.red, + warning: import_chalk.default.keyword("orange"), + green: import_chalk.default.green, + info: import_chalk.default.grey, + bold: import_chalk.default.bold, + underline: import_chalk.default.underline +}; diff --git a/packages/cli/dist/lib/configureStorybook.js b/packages/cli/dist/lib/configureStorybook.js new file mode 100644 index 0000000000..1990f60a64 --- /dev/null +++ b/packages/cli/dist/lib/configureStorybook.js @@ -0,0 +1,70 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var configureStorybook_exports = {}; +__export(configureStorybook_exports, { + default: () => extendStorybookConfiguration +}); +module.exports = __toCommonJS(configureStorybook_exports); +var import_path = __toESM(require("path")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_prettier = __toESM(require("prettier")); +var import_merge = require("./merge"); +var import_strategy = require("./merge/strategy"); +var import_project = require("./project"); +var import__ = require("."); +async function extendStorybookConfiguration(newConfigPath = void 0) { + const webPaths = (0, import__.getPaths)().web; + const ts = (0, import_project.isTypeScriptProject)(); + const sbPreviewConfigPath = webPaths.storybookPreviewConfig ?? `${webPaths.config}/storybook.preview.${ts ? "tsx" : "js"}`; + const read = (path2) => import_fs_extra.default.readFileSync(path2, { encoding: "utf-8" }); + if (!import_fs_extra.default.existsSync(sbPreviewConfigPath)) { + const templateContent = read( + import_path.default.resolve(__dirname, "templates", "storybook.preview.tsx.template") + ); + const storybookPreviewContent2 = ts ? templateContent : (0, import__.transformTSToJS)(sbPreviewConfigPath, templateContent); + await (0, import__.writeFile)(sbPreviewConfigPath, storybookPreviewContent2); + } + const storybookPreviewContent = read(sbPreviewConfigPath); + if (newConfigPath) { + const newConfigTemplate = read(newConfigPath); + const newConfigContent = ts ? newConfigTemplate : (0, import__.transformTSToJS)(newConfigPath, newConfigTemplate); + const merged = (0, import_merge.merge)(storybookPreviewContent, newConfigContent, { + ImportDeclaration: import_strategy.interleave, + ArrayExpression: import_strategy.concatUnique, + ObjectExpression: import_strategy.concatUnique, + ArrowFunctionExpression: import_strategy.keepBothStatementParents, + FunctionDeclaration: import_strategy.keepBoth + }); + const formatted = import_prettier.default.format(merged, { + parser: "babel", + ...await import_prettier.default.resolveConfig(sbPreviewConfigPath) + }); + (0, import__.writeFile)(sbPreviewConfigPath, formatted, { overwriteExisting: true }); + } +} diff --git a/packages/cli/dist/lib/exec.js b/packages/cli/dist/lib/exec.js new file mode 100644 index 0000000000..61b520fdd1 --- /dev/null +++ b/packages/cli/dist/lib/exec.js @@ -0,0 +1,115 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var exec_exports = {}; +__export(exec_exports, { + configureBabel: () => configureBabel, + runScriptFunction: () => runScriptFunction +}); +module.exports = __toCommonJS(exec_exports); +var import_path = __toESM(require("path")); +var import_babel_config = require("@redwoodjs/babel-config"); +var import_project_config = require("@redwoodjs/project-config"); +async function runScriptFunction({ + path: scriptPath, + functionName, + args +}) { + const script = require(scriptPath); + const returnValue = await script[functionName](args); + try { + const { db } = require(import_path.default.join((0, import_project_config.getPaths)().api.lib, "db")); + db.$disconnect(); + } catch (e) { + } + return returnValue; +} +async function configureBabel() { + const { + overrides: _overrides, + plugins: webPlugins, + ...otherWebConfig + } = (0, import_babel_config.getWebSideDefaultBabelConfig)(); + (0, import_babel_config.registerApiSideBabelHook)({ + plugins: [ + [ + "babel-plugin-module-resolver", + { + alias: { + $api: (0, import_project_config.getPaths)().api.base, + $web: (0, import_project_config.getPaths)().web.base, + api: (0, import_project_config.getPaths)().api.base, + web: (0, import_project_config.getPaths)().web.base + }, + loglevel: "silent" + // to silence the unnecessary warnings + }, + "exec-$side-module-resolver" + ] + ], + overrides: [ + { + test: ["./api/"], + plugins: [ + [ + "babel-plugin-module-resolver", + { + alias: { + src: (0, import_project_config.getPaths)().api.src + }, + loglevel: "silent" + }, + "exec-api-src-module-resolver" + ] + ] + }, + { + test: ["./web/"], + plugins: [ + ...webPlugins, + [ + "babel-plugin-module-resolver", + { + alias: { + src: (0, import_project_config.getPaths)().web.src + }, + loglevel: "silent" + }, + "exec-web-src-module-resolver" + ] + ], + ...otherWebConfig + } + ] + }); +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + configureBabel, + runScriptFunction +}); diff --git a/packages/cli/dist/lib/exit.js b/packages/cli/dist/lib/exit.js new file mode 100644 index 0000000000..48ef8d7e1a --- /dev/null +++ b/packages/cli/dist/lib/exit.js @@ -0,0 +1,75 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var exit_exports = {}; +__export(exit_exports, { + exitWithError: () => exitWithError +}); +module.exports = __toCommonJS(exit_exports); +var import_chalk = __toESM(require("chalk")); +var import_terminal_link = __toESM(require("terminal-link")); +var import_uuid = require("uuid"); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +const DEFAULT_ERROR_EPILOGUE = [ + "Need help?", + ` - Not sure about something or need advice? Reach out on our ${(0, import_terminal_link.default)( + "Forum", + "https://community.redwoodjs.com/" + )}`, + ` - Think you've found a bug? Open an issue on our ${(0, import_terminal_link.default)( + "GitHub", + "https://github.com/redwoodjs/redwood" + )}` +].join("\n"); +function exitWithError(error, { exitCode, message, epilogue, includeEpilogue, includeReferenceCode } = {}) { + exitCode ??= error?.exitCode ?? 1; + epilogue ??= DEFAULT_ERROR_EPILOGUE; + includeEpilogue ??= true; + includeReferenceCode ??= true; + message ??= error.stack ?? (error.toString() || "Unknown error"); + const errorReferenceCode = (0, import_uuid.v4)(); + const line = import_chalk.default.red("-".repeat(process.stderr.columns)); + const content = !includeEpilogue ? message : [ + "", + line, + message, + ` +${line}`, + epilogue, + includeReferenceCode && ` - Here's your unique error reference to quote: '${errorReferenceCode}'`, + line + ].filter(Boolean).join("\n"); + console.error(content); + (0, import_cli_helpers.recordTelemetryError)(error ?? new Error(message)); + (0, import_cli_helpers.recordTelemetryAttributes)({ errorReferenceCode }); + process.exit(exitCode); +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + exitWithError +}); diff --git a/packages/cli/dist/lib/extendFile.js b/packages/cli/dist/lib/extendFile.js new file mode 100644 index 0000000000..9d00eb1739 --- /dev/null +++ b/packages/cli/dist/lib/extendFile.js @@ -0,0 +1,150 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var extendFile_exports = {}; +__export(extendFile_exports, { + extendJSXFile: () => extendJSXFile, + fileIncludes: () => fileIncludes, + objectToComponentProps: () => objectToComponentProps +}); +module.exports = __toCommonJS(extendFile_exports); +var import_fs_extra = __toESM(require("fs-extra")); +function fileIncludes(path, str) { + return import_fs_extra.default.existsSync(path) && import_fs_extra.default.readFileSync(path).toString().includes(str); +} +function extendJSXFile(path, { + insertComponent: { + name = void 0, + props = void 0, + around = void 0, + within = void 0, + insertBefore = void 0, + insertAfter = void 0 + }, + imports = [], + moduleScopeLines = [] +}) { + const content = import_fs_extra.default.readFileSync(path).toString().split("\n"); + if (moduleScopeLines?.length) { + content.splice( + content.findLastIndex((l) => l.trimStart().startsWith("import")) + 1, + 0, + "", + ...moduleScopeLines + ); + } + if (imports?.length) { + content.splice( + content.findLastIndex((l) => l.includes("@redwoodjs")) + 1, + 0, + "", + ...imports + ); + } + if (name) { + insertComponent(content, { + component: name, + props, + around, + within, + insertBefore, + insertAfter + }); + } + import_fs_extra.default.writeFileSync(path, content.filter((e) => e !== void 0).join("\n")); +} +function insertComponent(content, { component, props, around, within, insertBefore, insertAfter }) { + if (around && within || !(around || within)) { + throw new Error( + "Exactly one of (around | within) must be defined. Choose one." + ); + } + const target = around ?? within; + const findTagIndex = (regex) => content.findIndex((line) => regex.test(line)); + let open = findTagIndex(new RegExp(`([^\\S\r +]*)<${target}\\s*(.*)\\s*>`)); + let close = findTagIndex(new RegExp(`([^\\S\r +]*)`)) + 1; + if (open === -1 || close === -1) { + throw new Error(`Could not find tags for ${target}`); + } + if (within) { + open++; + close--; + } + const [, componentDepth] = content[open].match(/([^\S\r\n]*).*/); + content.splice( + open, + close - open, + // "Delete" the wrapped component contents. We put it back below. + insertBefore && componentDepth + insertBefore, + componentDepth + buildOpeningTag(component, props), + ...content.slice(open, close).map((line) => " " + line), + componentDepth + ``, + insertAfter && componentDepth + insertAfter + ); +} +function buildOpeningTag(componentName, props) { + const propsString = (() => { + switch (typeof props) { + case "undefined": + return ""; + case "object": + return objectToComponentProps(props, { raw: true }).join(" "); + case "string": + return props; + default: + throw new Error( + `Illegal argument passed for 'props'. Required: {Object | string | undefined}, got ${typeof props}` + ); + } + })(); + const possibleSpace = propsString.length ? " " : ""; + return `<${componentName}${possibleSpace}${propsString}>`; +} +function objectToComponentProps(obj, options = { exclude: [], raw: false }) { + const props = []; + const doRaw = (key) => options.raw === true || Array.isArray(options.raw) && options.raw.includes(key); + for (const [key, value] of Object.entries(obj)) { + if (options.exclude && options.exclude.includes(key)) { + continue; + } + if (doRaw(key)) { + props.push(`${key}={${value}}`); + } else { + props.push(`${key}="${value}"`); + } + } + return props; +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + extendJSXFile, + fileIncludes, + objectToComponentProps +}); diff --git a/packages/cli/dist/lib/generatePrismaClient.js b/packages/cli/dist/lib/generatePrismaClient.js new file mode 100644 index 0000000000..b936c26db6 --- /dev/null +++ b/packages/cli/dist/lib/generatePrismaClient.js @@ -0,0 +1,88 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var generatePrismaClient_exports = {}; +__export(generatePrismaClient_exports, { + generatePrismaClient: () => generatePrismaClient, + generatePrismaCommand: () => generatePrismaCommand +}); +module.exports = __toCommonJS(generatePrismaClient_exports); +var import_path = __toESM(require("path")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_lib = require("../lib"); +const skipTask = (schema = (0, import_lib.getPaths)().api.dbSchema) => { + if (!import_fs_extra.default.existsSync(schema)) { + console.log( + `Skipping database and Prisma client generation, no \`schema.prisma\` file found: \`${schema}\`` + ); + return true; + } + return false; +}; +const generatePrismaCommand = (schema) => { + if (skipTask(schema)) { + return {}; + } + return { + cmd: `node "${require.resolve("prisma/build/index.js")}"`, + args: ["generate", schema && `--schema="${schema}"`] + }; +}; +const generatePrismaClient = async ({ + verbose = true, + force = true, + schema = (0, import_lib.getPaths)().api.dbSchema +}) => { + if (skipTask(schema)) { + return; + } + if (!force) { + try { + const { PrismaClient } = require(import_path.default.join((0, import_lib.getPaths)().base, "node_modules/.prisma/client")); + new PrismaClient(); + return; + } catch (e) { + } + } + return await (0, import_lib.runCommandTask)( + [ + { + title: "Generating the Prisma client...", + ...generatePrismaCommand(schema) + } + ], + { + verbose + } + ); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + generatePrismaClient, + generatePrismaCommand +}); diff --git a/packages/cli/dist/lib/index.js b/packages/cli/dist/lib/index.js new file mode 100644 index 0000000000..42acb36371 --- /dev/null +++ b/packages/cli/dist/lib/index.js @@ -0,0 +1,537 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var lib_exports = {}; +__export(lib_exports, { + _getPaths: () => _getPaths, + addPackagesTask: () => addPackagesTask, + addRoutesToRouterTask: () => addRoutesToRouterTask, + addScaffoldImport: () => addScaffoldImport, + bytes: () => bytes, + cleanupEmptyDirsTask: () => cleanupEmptyDirsTask, + deleteFile: () => deleteFile, + deleteFilesTask: () => deleteFilesTask, + existsAnyExtensionSync: () => existsAnyExtensionSync, + findUp: () => import_project_config.findUp, + generateTemplate: () => generateTemplate, + getConfig: () => getConfig, + getDefaultArgs: () => getDefaultArgs, + getGraphqlPath: () => getGraphqlPath, + getInstalledRedwoodVersion: () => getInstalledRedwoodVersion, + getPaths: () => getPaths, + getPrettierOptions: () => getPrettierOptions, + graphFunctionDoesExist: () => graphFunctionDoesExist, + nameVariants: () => nameVariants, + prettify: () => prettify, + printSetupNotes: () => printSetupNotes, + readFile: () => readFile, + removeRoutesFromRouterTask: () => removeRoutesFromRouterTask, + resolveFile: () => resolveFile, + runCommandTask: () => runCommandTask, + saveRemoteFileToDisk: () => saveRemoteFileToDisk, + transformTSToJS: () => transformTSToJS, + usingVSCode: () => usingVSCode, + writeFile: () => writeFile, + writeFilesTask: () => writeFilesTask +}); +module.exports = __toCommonJS(lib_exports); +var import_child_process = require("child_process"); +var import_https = __toESM(require("https")); +var import_path = __toESM(require("path")); +var babel = __toESM(require("@babel/core")); +var import_boxen = __toESM(require("boxen")); +var import_camelcase = __toESM(require("camelcase")); +var import_decamelize = __toESM(require("decamelize")); +var import_execa = __toESM(require("execa")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_listr2 = require("listr2"); +var import_lodash = require("lodash"); +var import_param_case = require("param-case"); +var import_pascalcase = __toESM(require("pascalcase")); +var import_prettier = require("prettier"); +var import_project_config = require("@redwoodjs/project-config"); +var import_colors = __toESM(require("./colors")); +var import_rollback = require("./rollback"); +var import_rwPluralize = require("./rwPluralize"); +const nameVariants = (name) => { + const normalizedName = (0, import_pascalcase.default)((0, import_param_case.paramCase)((0, import_rwPluralize.singularize)(name))); + return { + pascalName: (0, import_pascalcase.default)((0, import_param_case.paramCase)(name)), + camelName: (0, import_camelcase.default)(name), + singularPascalName: normalizedName, + pluralPascalName: (0, import_rwPluralize.pluralize)(normalizedName), + singularCamelName: (0, import_camelcase.default)(normalizedName), + pluralCamelName: (0, import_camelcase.default)((0, import_rwPluralize.pluralize)(normalizedName)), + singularParamName: (0, import_param_case.paramCase)(normalizedName), + pluralParamName: (0, import_param_case.paramCase)((0, import_rwPluralize.pluralize)(normalizedName)), + singularConstantName: (0, import_decamelize.default)(normalizedName).toUpperCase(), + pluralConstantName: (0, import_decamelize.default)((0, import_rwPluralize.pluralize)(normalizedName)).toUpperCase() + }; +}; +const generateTemplate = (templateFilename, { name, ...rest }) => { + try { + const templateFn = (0, import_lodash.template)(readFile(templateFilename).toString()); + const renderedTemplate = templateFn({ + name, + ...nameVariants(name), + ...rest + }); + return prettify(templateFilename, renderedTemplate); + } catch (error) { + error.message = `Error applying template at ${templateFilename} for ${name}: ${error.message}`; + throw error; + } +}; +const prettify = async (templateFilename, renderedTemplate) => { + const parser = { + ".css": "css", + ".js": "babel", + ".jsx": "babel", + ".ts": "babel-ts", + ".tsx": "babel-ts" + }[import_path.default.extname(templateFilename.replace(".template", ""))]; + if (typeof parser === "undefined") { + return renderedTemplate; + } + const prettierOptions = await getPrettierOptions(); + return (0, import_prettier.format)(renderedTemplate, { + ...prettierOptions, + parser + }); +}; +const readFile = (target) => import_fs_extra.default.readFileSync(target, { encoding: "utf8" }); +const SUPPORTED_EXTENSIONS = [".js", ".jsx", ".ts", ".tsx"]; +const deleteFile = (file) => { + const extension = import_path.default.extname(file); + if (SUPPORTED_EXTENSIONS.includes(extension)) { + const baseFile = getBaseFile(file); + SUPPORTED_EXTENSIONS.forEach((ext) => { + const f = baseFile + ext; + if (import_fs_extra.default.existsSync(f)) { + import_fs_extra.default.unlinkSync(f); + } + }); + } else { + import_fs_extra.default.unlinkSync(file); + } +}; +const getBaseFile = (file) => file.replace(/\.\w*$/, ""); +const existsAnyExtensionSync = (file) => { + const extension = import_path.default.extname(file); + if (SUPPORTED_EXTENSIONS.includes(extension)) { + const baseFile = getBaseFile(file); + return SUPPORTED_EXTENSIONS.some((ext) => import_fs_extra.default.existsSync(baseFile + ext)); + } + return import_fs_extra.default.existsSync(file); +}; +const writeFile = (target, contents, { overwriteExisting = false } = {}, task = {}) => { + const { base } = getPaths(); + task.title = `Writing \`./${import_path.default.relative(base, target)}\``; + if (!overwriteExisting && import_fs_extra.default.existsSync(target)) { + throw new Error(`${target} already exists.`); + } + (0, import_rollback.addFileToRollback)(target); + const filename = import_path.default.basename(target); + const targetDir = target.replace(filename, ""); + import_fs_extra.default.mkdirSync(targetDir, { recursive: true }); + import_fs_extra.default.writeFileSync(target, contents); + task.title = `Successfully wrote file \`./${import_path.default.relative(base, target)}\``; +}; +const saveRemoteFileToDisk = (url, localPath, { overwriteExisting = false } = {}) => { + if (!overwriteExisting && import_fs_extra.default.existsSync(localPath)) { + throw new Error(`${localPath} already exists.`); + } + const downloadPromise = new Promise( + (resolve, reject) => import_https.default.get(url, (response) => { + if (response.statusCode === 200) { + response.pipe(import_fs_extra.default.createWriteStream(localPath)); + resolve(); + } else { + reject( + new Error(`${url} responded with status code ${response.statusCode}`) + ); + } + }) + ); + return downloadPromise; +}; +const getInstalledRedwoodVersion = () => { + try { + const packageJson = require("../../package.json"); + return packageJson.version; + } catch (e) { + console.error(import_colors.default.error("Could not find installed redwood version")); + process.exit(1); + } +}; +const bytes = (contents) => Buffer.byteLength(contents, "utf8"); +const _getPaths = () => { + try { + return (0, import_project_config.getPaths)(); + } catch (e) { + console.error(import_colors.default.error(e.message)); + process.exit(1); + } +}; +const getPaths = (0, import_lodash.memoize)(_getPaths); +const resolveFile = import_project_config.resolveFile; +const getGraphqlPath = () => resolveFile(import_path.default.join(getPaths().api.functions, "graphql")); +const graphFunctionDoesExist = () => { + return import_fs_extra.default.existsSync(getGraphqlPath()); +}; +const getConfig = () => { + try { + return (0, import_project_config.getConfig)(); + } catch (e) { + console.error(import_colors.default.error(e.message)); + process.exit(1); + } +}; +const getPrettierOptions = async () => { + try { + const { default: prettierOptions } = await import(`file://${import_path.default.join(getPaths().base, "prettier.config.js")}`); + return prettierOptions; + } catch (e) { + if (process.env.VITEST_POOL_ID !== void 0) { + return { + trailingComma: "es5", + bracketSpacing: true, + tabWidth: 2, + semi: false, + singleQuote: true, + arrowParens: "always", + overrides: [ + { + files: "Routes.*", + options: { + printWidth: 999 + } + } + ] + }; + } + return void 0; + } +}; +const transformTSToJS = (filename, content) => { + const { code } = babel.transform(content, { + filename, + // If you ran `yarn rw generate` in `./web` transformSync would import the `.babelrc.js` file, + // in `./web`? despite us setting `configFile: false`. + cwd: process.env.NODE_ENV === "test" ? void 0 : getPaths().base, + configFile: false, + plugins: [ + [ + "@babel/plugin-transform-typescript", + { + isTSX: true, + allExtensions: true + } + ] + ], + retainLines: true + }); + return prettify(filename.replace(/\.ts(x)?$/, ".js$1"), code); +}; +const writeFilesTask = (files, options) => { + const { base } = getPaths(); + return new import_listr2.Listr( + Object.keys(files).map((file) => { + const contents = files[file]; + return { + title: `...waiting to write file \`./${import_path.default.relative(base, file)}\`...`, + task: (ctx, task) => writeFile(file, contents, options, task) + }; + }) + ); +}; +const deleteFilesTask = (files) => { + const { base } = getPaths(); + return new import_listr2.Listr([ + ...Object.keys(files).map((file) => { + return { + title: `Destroying \`./${import_path.default.relative(base, getBaseFile(file))}\`...`, + skip: () => !existsAnyExtensionSync(file) && `File doesn't exist`, + task: () => deleteFile(file) + }; + }), + { + title: "Cleaning up empty directories...", + task: () => cleanupEmptyDirsTask(files) + } + ]); +}; +const cleanupEmptyDirsTask = (files) => { + const { base } = getPaths(); + const endDirs = Object.keys(files).map((file) => import_path.default.dirname(file)); + const uniqueEndDirs = [...new Set(endDirs)]; + const pathDirs = []; + uniqueEndDirs.forEach((dir) => { + const relDir = import_path.default.relative(base, dir); + const splitDir = relDir.split(import_path.default.sep); + splitDir.pop(); + while (splitDir.length > 3) { + const subDir = import_path.default.join(base, splitDir.join("/")); + pathDirs.push(subDir); + splitDir.pop(); + } + }); + const uniqueDirs = uniqueEndDirs.concat([...new Set(pathDirs)]); + return new import_listr2.Listr( + uniqueDirs.map((dir) => { + return { + title: `Removing empty \`./${import_path.default.relative(base, dir)}\`...`, + task: () => import_fs_extra.default.rmdirSync(dir), + skip: () => { + if (!import_fs_extra.default.existsSync(dir)) { + return `Doesn't exist`; + } + if (import_fs_extra.default.readdirSync(dir).length > 0) { + return "Not empty"; + } + return false; + } + }; + }) + ); +}; +const wrapWithSet = (routesContent, layout, routes, newLineAndIndent, props = {}) => { + const [_, indentOne, indentTwo] = routesContent.match( + /([ \t]*)[^<]*[\r\n]+([ \t]+)/ + ) || ["", 0, 2]; + const oneLevelIndent = indentTwo.slice(0, indentTwo.length - indentOne.length); + const newRoutesWithExtraIndent = routes.map((route) => oneLevelIndent + route); + const propsString = Object.entries(props).map((values) => `${values[0]}="${values[1]}"`).join(" "); + return [ + ``, + ...newRoutesWithExtraIndent, + `` + ].join(newLineAndIndent); +}; +const addRoutesToRouterTask = (routes, layout, setProps = {}) => { + const redwoodPaths = getPaths(); + const routesContent = readFile(redwoodPaths.web.routes).toString(); + let newRoutes = routes.filter((route) => !routesContent.match(route)); + if (newRoutes.length) { + const [routerStart, routerParams, newLineAndIndent] = routesContent.match( + /\s*(\s*)/s + ); + if (/trailingSlashes={?(["'])always\1}?/.test(routerParams)) { + newRoutes = newRoutes.map( + (route) => route.replace(/ path="(.+?)" /, ' path="$1/" ') + ); + } + const routesBatch = layout ? wrapWithSet( + routesContent, + layout, + newRoutes, + newLineAndIndent, + setProps + ) : newRoutes.join(newLineAndIndent); + const newRoutesContent = routesContent.replace( + routerStart, + `${routerStart + routesBatch + newLineAndIndent}` + ); + writeFile(redwoodPaths.web.routes, newRoutesContent, { + overwriteExisting: true + }); + } +}; +const addScaffoldImport = () => { + const appJsPath = getPaths().web.app; + let appJsContents = readFile(appJsPath).toString(); + if (appJsContents.match("./scaffold.css")) { + return "Skipping scaffold style include"; + } + appJsContents = appJsContents.replace( + "import Routes from 'src/Routes'\n", + "import Routes from 'src/Routes'\n\nimport './scaffold.css'" + ); + writeFile(appJsPath, appJsContents, { overwriteExisting: true }); + return "Added scaffold import to App.{jsx,tsx}"; +}; +const removeEmtpySet = (routesContent, layout) => { + const setWithLayoutReg = new RegExp( + `\\s*]*wrap={${layout}}[^<]*>([^<]*)` + ); + const [matchedSet, childContent] = routesContent.match(setWithLayoutReg) || []; + if (!matchedSet) { + return routesContent; + } + const child = childContent.replace(/\s/g, ""); + if (child.length > 0) { + return routesContent; + } + return routesContent.replace(setWithLayoutReg, ""); +}; +const removeRoutesFromRouterTask = (routes, layout) => { + const redwoodPaths = getPaths(); + const routesContent = readFile(redwoodPaths.web.routes).toString(); + const newRoutesContent = routes.reduce((content, route) => { + const matchRouteByName = new RegExp(`\\s*]*name="${route}"[^>]*/>`); + return content.replace(matchRouteByName, ""); + }, routesContent); + const routesWithoutEmptySet = layout ? removeEmtpySet(newRoutesContent, layout) : newRoutesContent; + writeFile(redwoodPaths.web.routes, routesWithoutEmptySet, { + overwriteExisting: true + }); +}; +const addPackagesTask = ({ + packages, + side = "project", + devDependency = false +}) => { + const packagesWithSameRWVersion = packages.map((pkg) => { + if (pkg.includes("@redwoodjs")) { + return `${pkg}@${getInstalledRedwoodVersion()}`; + } else { + return pkg; + } + }); + let installCommand; + if (side !== "project") { + installCommand = [ + "yarn", + [ + "workspace", + side, + "add", + devDependency && "--dev", + ...packagesWithSameRWVersion + ].filter(Boolean) + ]; + } else { + const stdout = (0, import_child_process.execSync)("yarn --version"); + const yarnVersion = stdout.toString().trim(); + installCommand = [ + "yarn", + [ + yarnVersion.startsWith("1") && "-W", + "add", + devDependency && "--dev", + ...packagesWithSameRWVersion + ].filter(Boolean) + ]; + } + return { + title: `Adding dependencies to ${side}`, + task: async () => { + await (0, import_execa.default)(...installCommand); + } + }; +}; +const runCommandTask = async (commands, { verbose }) => { + const tasks = new import_listr2.Listr( + commands.map(({ title, cmd, args, opts = {}, cwd = getPaths().base }) => ({ + title, + task: async () => { + return (0, import_execa.default)(cmd, args, { + shell: true, + cwd, + stdio: verbose ? "inherit" : "pipe", + extendEnv: true, + cleanup: true, + ...opts + }); + } + })), + { + renderer: verbose && "verbose", + rendererOptions: { collapseSubtasks: false, dateFormat: false } + } + ); + try { + await tasks.run(); + return true; + } catch (e) { + console.log(import_colors.default.error(e.message)); + return false; + } +}; +const getDefaultArgs = (builder) => { + return Object.entries(builder).reduce( + (options, [optionName, optionConfig]) => { + options[optionName] = optionConfig.default; + return options; + }, + {} + ); +}; +const usingVSCode = () => { + const redwoodPaths = getPaths(); + const VS_CODE_PATH = import_path.default.join(redwoodPaths.base, ".vscode"); + return import_fs_extra.default.existsSync(VS_CODE_PATH); +}; +const printSetupNotes = (notes) => { + return { + title: "One more thing...", + task: (_ctx, task) => { + task.title = `One more thing... + + ${(0, import_boxen.default)(notes.join("\n"), { + padding: { top: 1, bottom: 1, right: 1, left: 1 }, + margin: 1, + borderColour: "gray" + })} +`; + } + }; +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + _getPaths, + addPackagesTask, + addRoutesToRouterTask, + addScaffoldImport, + bytes, + cleanupEmptyDirsTask, + deleteFile, + deleteFilesTask, + existsAnyExtensionSync, + findUp, + generateTemplate, + getConfig, + getDefaultArgs, + getGraphqlPath, + getInstalledRedwoodVersion, + getPaths, + getPrettierOptions, + graphFunctionDoesExist, + nameVariants, + prettify, + printSetupNotes, + readFile, + removeRoutesFromRouterTask, + resolveFile, + runCommandTask, + saveRemoteFileToDisk, + transformTSToJS, + usingVSCode, + writeFile, + writeFilesTask +}); diff --git a/packages/cli/dist/lib/loadEnvFiles.js b/packages/cli/dist/lib/loadEnvFiles.js new file mode 100644 index 0000000000..7b6b1c49a1 --- /dev/null +++ b/packages/cli/dist/lib/loadEnvFiles.js @@ -0,0 +1,98 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var loadEnvFiles_exports = {}; +__export(loadEnvFiles_exports, { + loadDefaultEnvFiles: () => loadDefaultEnvFiles, + loadEnvFiles: () => loadEnvFiles, + loadNodeEnvDerivedEnvFile: () => loadNodeEnvDerivedEnvFile, + loadUserSpecifiedEnvFiles: () => loadUserSpecifiedEnvFiles +}); +module.exports = __toCommonJS(loadEnvFiles_exports); +var import_path = __toESM(require("path")); +var import_dotenv = require("dotenv"); +var import_dotenv_defaults = require("dotenv-defaults"); +var import_fs_extra = __toESM(require("fs-extra")); +var import_helpers = require("yargs/helpers"); +var import_project_config = require("@redwoodjs/project-config"); +function loadEnvFiles() { + if (process.env.REDWOOD_ENV_FILES_LOADED) { + return; + } + const { base } = (0, import_project_config.getPaths)(); + loadDefaultEnvFiles(base); + loadNodeEnvDerivedEnvFile(base); + const { loadEnvFiles: loadEnvFiles2 } = (0, import_helpers.Parser)((0, import_helpers.hideBin)(process.argv), { + array: ["load-env-files"], + default: { + loadEnvFiles: [] + } + }); + if (loadEnvFiles2.length > 0) { + loadUserSpecifiedEnvFiles(base, loadEnvFiles2); + } + process.env.REDWOOD_ENV_FILES_LOADED = "true"; +} +function loadDefaultEnvFiles(cwd) { + (0, import_dotenv_defaults.config)({ + path: import_path.default.join(cwd, ".env"), + defaults: import_path.default.join(cwd, ".env.defaults"), + multiline: true + }); +} +function loadNodeEnvDerivedEnvFile(cwd) { + if (!process.env.NODE_ENV) { + return; + } + const nodeEnvDerivedEnvFilePath = import_path.default.join( + cwd, + `.env.${process.env.NODE_ENV}` + ); + if (!import_fs_extra.default.existsSync(nodeEnvDerivedEnvFilePath)) { + return; + } + (0, import_dotenv.config)({ path: nodeEnvDerivedEnvFilePath, override: true }); +} +function loadUserSpecifiedEnvFiles(cwd, loadEnvFiles2) { + for (const suffix of loadEnvFiles2) { + const envPath = import_path.default.join(cwd, `.env.${suffix}`); + if (!import_fs_extra.default.pathExistsSync(envPath)) { + throw new Error( + `Couldn't find an .env file at '${envPath}' as specified by '--load-env-files'` + ); + } + (0, import_dotenv.config)({ path: envPath, override: true }); + } +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + loadDefaultEnvFiles, + loadEnvFiles, + loadNodeEnvDerivedEnvFile, + loadUserSpecifiedEnvFiles +}); diff --git a/packages/cli/dist/lib/locking.js b/packages/cli/dist/lib/locking.js new file mode 100644 index 0000000000..46738bd56f --- /dev/null +++ b/packages/cli/dist/lib/locking.js @@ -0,0 +1,97 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var locking_exports = {}; +__export(locking_exports, { + clearLocks: () => clearLocks, + isLockSet: () => isLockSet, + setLock: () => setLock, + unsetLock: () => unsetLock +}); +module.exports = __toCommonJS(locking_exports); +var import_path = __toESM(require("path")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_index = require("./index"); +function ensureLockDirectoryExists() { + const locksPath = import_path.default.join((0, import_index.getPaths)().generated.base, "locks"); + if (!import_fs_extra.default.existsSync(locksPath)) { + import_fs_extra.default.mkdirSync(locksPath, { recursive: true }); + } +} +function setLock(identifier) { + ensureLockDirectoryExists(); + if (isLockSet(identifier)) { + throw new Error(`Lock "${identifier}" is already set`); + } + import_fs_extra.default.writeFileSync( + import_path.default.join((0, import_index.getPaths)().generated.base, "locks", identifier), + "" + ); +} +function unsetLock(identifier) { + try { + import_fs_extra.default.rmSync(import_path.default.join((0, import_index.getPaths)().generated.base, "locks", identifier)); + } catch (error) { + if (error.code !== "ENOENT") { + throw error; + } + } +} +function isLockSet(identifier) { + const lockfilePath = import_path.default.join((0, import_index.getPaths)().generated.base, "locks", identifier); + const exists = import_fs_extra.default.existsSync(lockfilePath); + if (!exists) { + return false; + } + const createdAt = import_fs_extra.default.statSync(lockfilePath).birthtimeMs; + if (Date.now() - createdAt > 36e5) { + unsetLock(identifier); + return false; + } + return true; +} +function clearLocks(identifiers = []) { + ensureLockDirectoryExists(); + if (identifiers.length > 0) { + for (const id of identifiers) { + unsetLock(id); + } + } else { + const locks = import_fs_extra.default.readdirSync(import_path.default.join((0, import_index.getPaths)().generated.base, "locks")); + for (const lock of locks) { + unsetLock(lock); + } + } +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + clearLocks, + isLockSet, + setLock, + unsetLock +}); diff --git a/packages/cli/dist/lib/merge/algorithms.js b/packages/cli/dist/lib/merge/algorithms.js new file mode 100644 index 0000000000..35ca6d99dd --- /dev/null +++ b/packages/cli/dist/lib/merge/algorithms.js @@ -0,0 +1,57 @@ +"use strict"; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var algorithms_exports = {}; +__export(algorithms_exports, { + forEachFunctionOn: () => forEachFunctionOn, + nodeIs: () => nodeIs, + sieve: () => sieve +}); +module.exports = __toCommonJS(algorithms_exports); +var import_lodash = require("lodash"); +const nodeIs = (type) => (node) => node.type === type; +function sieve(...listRulePairs) { + const result = [[]]; + for (const [list, rule] of listRulePairs) { + elementLoop: + for (const element of list) { + for (const arr of result) { + const position = rule(arr); + if (position !== -1) { + arr.splice(position, 0, element); + continue elementLoop; + } + } + result.push([element]); + } + } + return result; +} +function forEachFunctionOn(object, callback) { + (0, import_lodash.forOwn)(object, (value, key) => { + if (typeof value === "function") { + callback(key, value); + } + }); +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + forEachFunctionOn, + nodeIs, + sieve +}); diff --git a/packages/cli/dist/lib/merge/index.js b/packages/cli/dist/lib/merge/index.js new file mode 100644 index 0000000000..c9a416f0e2 --- /dev/null +++ b/packages/cli/dist/lib/merge/index.js @@ -0,0 +1,195 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var merge_exports = {}; +__export(merge_exports, { + merge: () => merge +}); +module.exports = __toCommonJS(merge_exports); +var import_core = require("@babel/core"); +var import_generator = __toESM(require("@babel/generator")); +var import_types = require("@babel/types"); +var import_lodash = require("lodash"); +var import_prettier = __toESM(require("prettier")); +var import_algorithms = require("./algorithms"); +var import_semanticIdentity = require("./semanticIdentity"); +var import_strategy = require("./strategy"); +function extractProperty(property, fromObject) { + if (property === void 0) { + return void 0; + } + const tmp = fromObject[property]; + delete fromObject[property]; + return tmp; +} +function getProgramPath(ast) { + let programPath; + (0, import_core.traverse)(ast, { + Program(path) { + programPath = path; + return; + } + }); + if (programPath === void 0) { + throw new Error("Unable to find Program node in AST"); + } + return programPath; +} +function skipChildren(path) { + for (const key of import_types.VISITOR_KEYS[path.type]) { + path.skipKey(key); + } +} +function makeProxy(path) { + return new Proxy(path, { + get(target, property) { + if (property === "path") { + return target; + } else { + return target.node[property]; + } + }, + set(target, property, value) { + if (property === "path") { + throw new Error("You can't set a path on a proxy!"); + } else { + target.node[property] = value; + return true; + } + }, + has(target, property) { + return property in target.node; + } + }); +} +function expressionUses(exp, ...ids) { + let result = false; + exp.traverse({ + Identifier(path) { + if (!path.parentPath.isNodeType("VariableDeclarator") && ids.includes(path.node.name)) { + result = true; + return; + } + } + }); + return result; +} +function insertBeforeFirstUsage(expression, program) { + const body = program.get("body"); + const pos = body.findIndex( + (exp) => expressionUses(exp, ...Object.keys(expression.getBindingIdentifiers())) + ); + return pos !== -1 ? body[pos].insertBefore(expression.node) : program.pushContainer("body", expression.node); +} +function insertAfterLastImport(expression, program) { + const body = program.get("body"); + return body[body.findLastIndex((bodyExpr) => bodyExpr.isNodeType("ImportDeclaration"))].insertAfter(expression.node); +} +function prune(path) { + switch (path.parentPath.type) { + case "ObjectProperty": + case "VariableDeclarator": + return path.parentPath.remove(); + default: + console.log(`Warning: default prune strategy for ${path.parentPath.type}`); + case "Program": + case "ArrayExpression": + return path.remove(); + } +} +function stripTrailingCommentsStrategy() { + return { + enter(path) { + path.node.trailingComments = []; + } + }; +} +function mergeAST(baseAST, extAST, strategy = {}) { + const identity = extractProperty("identity", strategy) ?? import_semanticIdentity.semanticIdentity; + const identities = {}; + const baseVisitor = { ...stripTrailingCommentsStrategy() }; + const extVisitor = { ...stripTrailingCommentsStrategy() }; + (0, import_algorithms.forEachFunctionOn)(strategy, (typename, strat) => { + extVisitor[typename] = { + enter(path) { + const id = identity(path); + id && (identities[id] ||= []).push(path); + } + }; + baseVisitor[typename] = { + enter(path) { + if ((0, import_strategy.isOpaque)(strat)) { + skipChildren(path); + } + }, + exit(path) { + const exts = extractProperty(identity(path), identities); + if (exts) { + const proxyPath = makeProxy(path); + exts.map(makeProxy).forEach((ext) => { + strat(proxyPath, ext); + prune(ext.path); + }); + } + } + }; + }); + (0, import_core.traverse)(extAST, extVisitor); + (0, import_core.traverse)(baseAST, baseVisitor); + const baseProgram = getProgramPath(baseAST); + const [imports, others] = (0, import_lodash.partition)( + getProgramPath(extAST).get("body"), + (0, import_algorithms.nodeIs)("ImportDeclaration") + ); + imports.forEach((exp) => insertAfterLastImport(exp, baseProgram)); + (0, import_lodash.forEachRight)(others, (exp) => insertBeforeFirstUsage(exp, baseProgram)); +} +function merge(base, extension, strategy) { + function parseReact(code2) { + return (0, import_core.parse)(code2, { + filename: "merged.tsx", + // required to prevent babel error. The .tsx is relevant + presets: ["@babel/preset-typescript"] + }); + } + const baseAST = parseReact(base); + const extAST = parseReact(extension); + mergeAST(baseAST, extAST, strategy); + const { code } = (0, import_generator.default)(baseAST); + return process.env.VITEST_POOL_ID ? import_prettier.default.format(code, { + parser: "babel-ts", + bracketSpacing: true, + tabWidth: 2, + semi: false, + singleQuote: true + }) : code; +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + merge +}); diff --git a/packages/cli/dist/lib/merge/semanticIdentity.js b/packages/cli/dist/lib/merge/semanticIdentity.js new file mode 100644 index 0000000000..720b797b98 --- /dev/null +++ b/packages/cli/dist/lib/merge/semanticIdentity.js @@ -0,0 +1,42 @@ +"use strict"; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var semanticIdentity_exports = {}; +__export(semanticIdentity_exports, { + semanticIdentity: () => semanticIdentity +}); +module.exports = __toCommonJS(semanticIdentity_exports); +function semanticIdentity(path) { + const identity = { + get(path2) { + return path2.type in this ? this[path2.type](path2) : [path2.type]; + }, + ObjectProperty: (path2) => [path2.node.key.name], + VariableDeclarator: (path2) => [path2.node.id.name], + ImportDeclaration: (path2) => [ + "ImportDeclaration", + "source", + path2.node.source.value + ] + }; + return path.getAncestry().reduce((acc, i) => [...identity.get(i), ...acc], []).join("."); +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + semanticIdentity +}); diff --git a/packages/cli/dist/lib/merge/strategy.js b/packages/cli/dist/lib/merge/strategy.js new file mode 100644 index 0000000000..83bcafb907 --- /dev/null +++ b/packages/cli/dist/lib/merge/strategy.js @@ -0,0 +1,178 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var strategy_exports = {}; +__export(strategy_exports, { + concat: () => concat, + concatUnique: () => concatUnique, + interleave: () => interleave, + isOpaque: () => isOpaque, + keepBase: () => keepBase, + keepBoth: () => keepBoth, + keepBothStatementParents: () => keepBothStatementParents, + keepExtension: () => keepExtension, + opaquely: () => opaquely +}); +module.exports = __toCommonJS(strategy_exports); +var t = __toESM(require("@babel/types")); +var import_lodash = __toESM(require("lodash")); +var import_algorithms = require("./algorithms"); +const OPAQUE_UID_TAG = "RW_MERGE_OPAQUE_UID_Q2xldmVyIHlvdSEgSGF2ZSBhIGNvb2tpZS4="; +function requireSameType(base, ext) { + if (base.path.type !== ext.path.type) { + throw new Error( + "Attempting to merge nodes with different types. This is not yet supported." + ); + } +} +function requireStrategyExists(base, _ext, strategy, strategyName) { + if (!(base.path.type in strategy)) { + throw new Error( + `Attempting to ${strategyName} nodes that do not have an ${strategyName} strategy.` + ); + } +} +const strictEquality = (lhs, rhs) => lhs === rhs; +const byName = (lhs, rhs) => lhs.name === rhs.name; +const byKeyName = (lhs, rhs) => lhs.key.name === rhs.key.name; +const byValue = (lhs, rhs) => lhs.value === rhs.value; +function defaultEquality(baseContainer, extContainer) { + const sample = baseContainer.length && baseContainer[0] || extContainer.length && extContainer[0]; + const defaults = { + BigIntLiteral: byValue, + BooleanLiteral: byValue, + Identifier: byName, + NumericLiteral: byValue, + ObjectProperty: byKeyName, + StringLiteral: byValue + }; + return sample && sample.type in defaults ? defaults[sample.type] : strictEquality; +} +function opaquely(strategy) { + strategy[OPAQUE_UID_TAG] = true; + return strategy; +} +function isOpaque(strategy) { + return strategy[OPAQUE_UID_TAG] === true; +} +const keepBase = opaquely(() => { +}); +const keepBoth = opaquely((base, ext) => { + base.path.insertAfter(ext.path.node); +}); +const keepExtension = opaquely((base, ext) => { + base.path.replaceWith(ext.path); +}); +const keepBothStatementParents = opaquely((base, ext) => { + base.path.getStatementParent().insertAfter(ext.path.getStatementParent().node); +}); +const interleaveStrategy = { + ImportDeclaration(baseImport, extImport) { + const baseSpecs = baseImport.specifiers; + const extSpecs = extImport.specifiers; + const importSpecifierEquality = (lhs, rhs) => lhs.type === rhs.type && lhs.imported?.name === rhs.imported?.name && lhs.local?.name == rhs.local?.name; + const uniqueSpecifiersOfType = (type) => import_lodash.default.uniqWith( + [...baseSpecs, ...extSpecs].filter((0, import_algorithms.nodeIs)(type)), + importSpecifierEquality + ); + if (!baseSpecs.length !== !extSpecs.length) { + return keepBothStatementParents(baseImport, extImport); + } + const defaultPosition = (specs) => specs.some((0, import_algorithms.nodeIs)("ImportDefaultSpecifier")) ? -1 : 0; + const namespacePosition = (specs) => specs.some((0, import_algorithms.nodeIs)("ImportNamespaceSpecifier")) || specs.some((0, import_algorithms.nodeIs)("ImportSpecifier")) ? -1 : specs.length; + const importPosition = (specs) => specs.some((0, import_algorithms.nodeIs)("ImportNamespaceIdentifier")) ? -1 : specs.length; + const [firstSpecifierList, ...rest] = (0, import_algorithms.sieve)( + [uniqueSpecifiersOfType("ImportDefaultSpecifier"), defaultPosition], + [uniqueSpecifiersOfType("ImportNamespaceSpecifier"), namespacePosition], + [uniqueSpecifiersOfType("ImportSpecifier"), importPosition] + ); + baseImport.specifiers = firstSpecifierList; + if (rest.length) { + baseImport.path.insertAfter( + rest.map((specs) => t.importDeclaration(specs, baseImport.source)) + ); + } + } +}; +function interleave(base, ext) { + requireSameType(base, ext); + requireStrategyExists(base, ext, interleaveStrategy, "interleave"); + return interleaveStrategy[base.path.type](base, ext); +} +const concatStrategy = { + ArrayExpression(base, ext) { + base.elements = [...base.elements, ...ext.elements]; + }, + ObjectExpression(base, ext) { + base.properties = [...base.properties, ...ext.properties]; + }, + StringLiteral(base, ext) { + base.value = base.value.concat(ext.value); + } +}; +function concat(base, ext) { + requireSameType(base, ext); + requireStrategyExists(base, ext, concatStrategy, "concat"); + return concatStrategy[base.path.type](base, ext); +} +const concatUniqueStrategy = { + ArrayExpression(base, ext, eq) { + eq ||= defaultEquality(base.elements, ext.elements); + base.elements = import_lodash.default.uniqWith([...base.elements, ...ext.elements], eq); + }, + ObjectExpression(base, ext, eq) { + eq ||= defaultEquality(base.properties, ext.properties); + base.properties = import_lodash.default.uniqWith([...base.properties, ...ext.properties], eq); + } +}; +function concatUnique(baseOrEq, ext) { + if (arguments.length === 1) { + return (base, ext2) => { + requireSameType(base, ext2); + requireStrategyExists(base, ext2, concatUniqueStrategy, "concatUnique"); + return concatUniqueStrategy[base.path.type](base, ext2, baseOrEq); + }; + } + if (arguments.length === 2) { + requireSameType(baseOrEq, ext); + requireStrategyExists(baseOrEq, ext, concatUniqueStrategy, "concatUnique"); + return concatUniqueStrategy[baseOrEq.path.type](baseOrEq, ext); + } +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + concat, + concatUnique, + interleave, + isOpaque, + keepBase, + keepBoth, + keepBothStatementParents, + keepExtension, + opaquely +}); diff --git a/packages/cli/dist/lib/mockTelemetry.js b/packages/cli/dist/lib/mockTelemetry.js new file mode 100644 index 0000000000..0e922c5279 --- /dev/null +++ b/packages/cli/dist/lib/mockTelemetry.js @@ -0,0 +1,8 @@ +"use strict"; +var import_vitest = require("vitest"); +import_vitest.vi.mock("@redwoodjs/telemetry", () => { + return { + errorTelemetry: () => import_vitest.vi.fn(), + timedTelemetry: () => import_vitest.vi.fn() + }; +}); diff --git a/packages/cli/dist/lib/packages.js b/packages/cli/dist/lib/packages.js new file mode 100644 index 0000000000..a6ae45fb99 --- /dev/null +++ b/packages/cli/dist/lib/packages.js @@ -0,0 +1,111 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var packages_exports = {}; +__export(packages_exports, { + installModule: () => installModule, + installRedwoodModule: () => installRedwoodModule, + isModuleInstalled: () => isModuleInstalled +}); +module.exports = __toCommonJS(packages_exports); +var import_path = __toESM(require("path")); +var import_execa = __toESM(require("execa")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_index = require("./index"); +async function installModule(name, version = void 0) { + if (isModuleInstalled(name)) { + return false; + } + if (version === void 0) { + return installRedwoodModule(name); + } else { + await import_execa.default.command(`yarn add -D ${name}@${version}`, { + stdio: "inherit", + cwd: (0, import_index.getPaths)().base + }); + } + return true; +} +async function installRedwoodModule(module2) { + const packageJsonPath = require.resolve("@redwoodjs/cli/package.json"); + let { version } = import_fs_extra.default.readJSONSync(packageJsonPath); + if (!isModuleInstalled(module2)) { + if (version.includes("+")) { + version = version.split("+")[0]; + } + let packument; + try { + const packumentResponse = await fetch( + `https://registry.npmjs.org/${module2}` + ); + packument = await packumentResponse.json(); + if (packument.error) { + throw new Error(packument.error); + } + } catch (error) { + throw new Error( + `Couldn't fetch packument for ${module2}: ${error.message}` + ); + } + const versionIsPublished = Object.keys(packument.versions).includes(version); + if (!versionIsPublished) { + version = "canary"; + } + await import_execa.default.command(`yarn add -D ${module2}@${version}`, { + stdio: "inherit", + cwd: (0, import_index.getPaths)().base + }); + await import_execa.default.command(`yarn dedupe`, { + stdio: "inherit", + cwd: (0, import_index.getPaths)().base + }); + return true; + } + return false; +} +function isModuleInstalled(module2) { + const { dependencies, devDependencies } = import_fs_extra.default.readJSONSync( + import_path.default.join((0, import_index.getPaths)().base, "package.json") + ); + const deps = { + ...dependencies, + ...devDependencies + }; + if (deps[module2]) { + return true; + } + return require.resolve.paths(`${module2}/package.json`).some((requireResolvePath) => { + return import_fs_extra.default.existsSync(import_path.default.join(requireResolvePath, module2)); + }); +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + installModule, + installRedwoodModule, + isModuleInstalled +}); diff --git a/packages/cli/dist/lib/plugin.js b/packages/cli/dist/lib/plugin.js new file mode 100644 index 0000000000..8289329591 --- /dev/null +++ b/packages/cli/dist/lib/plugin.js @@ -0,0 +1,243 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var plugin_exports = {}; +__export(plugin_exports, { + PLUGIN_CACHE_BUILTIN: () => PLUGIN_CACHE_BUILTIN, + PLUGIN_CACHE_DEFAULT: () => PLUGIN_CACHE_DEFAULT, + checkPluginListAndWarn: () => checkPluginListAndWarn, + loadCommandCache: () => loadCommandCache, + loadPluginPackage: () => loadPluginPackage, + saveCommandCache: () => saveCommandCache +}); +module.exports = __toCommonJS(plugin_exports); +var import_path = __toESM(require("path")); +var import_chalk = __toESM(require("chalk")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_cli_helpers = require("@redwoodjs/cli-helpers"); +var import_packages = require("./packages"); +var import_index = require("./index"); +const { Select } = require("enquirer"); +const PLUGIN_CACHE_FILENAME = "commandCache.json"; +const PLUGIN_CACHE_DEFAULT = { + "@redwoodjs/cli-storybook": { + storybook: { + aliases: ["sb"], + description: "Launch Storybook: a tool for building UI components and pages in isolation" + } + }, + "@redwoodjs/cli-data-migrate": { + "data-migrate ": { + aliases: ["dataMigrate", "dm"], + description: "Migrate the data in your database" + } + } +}; +const PLUGIN_CACHE_BUILTIN = [ + "build", + "check", + "diagnostics", + "console", + "c", + "deploy", + "destroy", + "d", + "dev", + "exec", + "experimental", + "exp", + "generate", + "g", + "info", + "lint", + "prerender", + "render", + "prisma", + "record", + "serve", + "setup", + "test", + "ts-to-js", + "type-check", + "tsc", + "tc", + "upgrade" +]; +function loadCommandCache() { + let pluginCommandCache = PLUGIN_CACHE_DEFAULT; + const commandCachePath = import_path.default.join( + (0, import_index.getPaths)().generated.base, + PLUGIN_CACHE_FILENAME + ); + try { + const localCommandCache = JSON.parse(import_fs_extra.default.readFileSync(commandCachePath)); + let valid = true; + for (const [key, value] of Object.entries(localCommandCache)) { + if (key === "_builtin") { + continue; + } + valid &&= !Array.isArray(value); + } + if (valid) { + pluginCommandCache = { + ...localCommandCache, + ...PLUGIN_CACHE_DEFAULT + }; + } + } catch (error) { + if (error.code !== "ENOENT") { + console.error(`Error loading plugin command cache at ${commandCachePath}`); + console.error(error); + } + } + pluginCommandCache._builtin = PLUGIN_CACHE_BUILTIN; + return pluginCommandCache; +} +function saveCommandCache(pluginCommandCache) { + const commandCachePath = import_path.default.join( + (0, import_index.getPaths)().generated.base, + PLUGIN_CACHE_FILENAME + ); + try { + import_fs_extra.default.writeFileSync( + commandCachePath, + JSON.stringify(pluginCommandCache, void 0, 2) + ); + } catch (error) { + console.error(`Error saving plugin command cache at ${commandCachePath}`); + console.error(error); + } +} +function checkPluginListAndWarn(plugins) { + for (const plugin of plugins) { + if (!plugin.package) { + console.warn( + import_chalk.default.yellow(`\u26A0\uFE0F A plugin is missing a package, it cannot be loaded.`) + ); + } + } + const pluginPackages = plugins.map((p) => p.package).filter((p) => p !== void 0); + if (pluginPackages.length !== new Set(pluginPackages).size) { + console.warn( + import_chalk.default.yellow( + "\u26A0\uFE0F Duplicate plugin packages found in redwood.toml, duplicates will be ignored." + ) + ); + } + const namespaces = plugins.map((p) => p.package?.split("/")[0]); + namespaces.forEach((ns) => { + if (ns !== void 0 && !ns.startsWith("@")) { + console.warn( + import_chalk.default.yellow( + `\u26A0\uFE0F Plugin "${ns}" is missing a scope/namespace, it will not be loaded.` + ) + ); + } + }); +} +async function loadPluginPackage(packageName, packageVersion, autoInstall) { + if ((0, import_packages.isModuleInstalled)(packageName)) { + return await import(packageName); + } + if (!autoInstall) { + console.warn( + import_chalk.default.yellow( + `\u26A0\uFE0F Plugin "${packageName}" cannot be loaded because it is not installed and "autoInstall" is disabled.` + ) + ); + return null; + } + console.log(import_chalk.default.green(`Installing plugin "${packageName}"...`)); + const installed = await installPluginPackage(packageName, packageVersion); + if (installed) { + return await import(packageName); + } + return null; +} +async function installPluginPackage(packageName, packageVersion) { + let versionToInstall = packageVersion; + const isRedwoodPackage = packageName.startsWith("@redwoodjs/"); + if (!isRedwoodPackage && versionToInstall === void 0) { + versionToInstall = "latest"; + try { + const compatibilityData = await (0, import_cli_helpers.getCompatibilityData)( + packageName, + versionToInstall + ); + versionToInstall = compatibilityData.compatible.version; + console.log( + import_chalk.default.green( + `Installing the latest compatible version: ${versionToInstall}` + ) + ); + } catch (error) { + console.log( + "The following error occurred while checking plugin compatibility for automatic installation:" + ); + const errorMessage = error.message ?? error; + console.log(errorMessage); + if (errorMessage.includes("does not have a tag") || errorMessage.includes("does not have a version")) { + process.exit(1); + } + const prompt = new Select({ + name: "versionDecision", + message: "What would you like to do?", + choices: [ + { + name: "cancel", + message: "Cancel" + }, + { + name: "continue", + message: "Continue and install the 'latest' version" + } + ] + }); + const decision = await prompt.run(); + if (decision === "cancel") { + process.exit(1); + } + } + } + try { + await (0, import_packages.installModule)(packageName, versionToInstall); + return true; + } catch (error) { + console.error(error); + return false; + } +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + PLUGIN_CACHE_BUILTIN, + PLUGIN_CACHE_DEFAULT, + checkPluginListAndWarn, + loadCommandCache, + loadPluginPackage, + saveCommandCache +}); diff --git a/packages/cli/dist/lib/pluralHelpers.js b/packages/cli/dist/lib/pluralHelpers.js new file mode 100644 index 0000000000..38f5a9ff78 --- /dev/null +++ b/packages/cli/dist/lib/pluralHelpers.js @@ -0,0 +1,86 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var pluralHelpers_exports = {}; +__export(pluralHelpers_exports, { + ensureUniquePlural: () => ensureUniquePlural, + isWordPluralizable: () => isWordPluralizable, + validatePlural: () => validatePlural +}); +module.exports = __toCommonJS(pluralHelpers_exports); +var import_prompts = __toESM(require("prompts")); +var import_rwPluralize = require("./rwPluralize"); +const isWordPluralizable = (word) => { + return (0, import_rwPluralize.isPlural)(word) !== (0, import_rwPluralize.isSingular)(word); +}; +const validatePlural = (plural, singular) => { + const trimmedPlural = plural.trim(); + if (trimmedPlural === singular) { + return "Plural can not be same as singular."; + } + if (trimmedPlural.match(/[\n\r\s]+/)) { + return "Only one word please!"; + } + if (trimmedPlural.match(/^[\n\r\s\u0017]*$/)) { + return "Plural can not be empty."; + } + return true; +}; +const ensureUniquePlural = async ({ + model, + isDestroyer = false, + forcePrompt = false +}) => { + if (!forcePrompt && isWordPluralizable(model)) { + return; + } + const generateMessage = `Cannot determine the plural of "${model}". +To continue, the generator requires a unique plural form:`; + const destroyMessage = `Cannot determine the plural of "${model}" originally used to generate the files. +To continue, the destroy command requires the plural form:`; + const promptMessage = isDestroyer ? destroyMessage : generateMessage; + const initialPlural = model.slice(-1) === "s" ? `${model}es` : `${model}s`; + const promptResult = await (0, import_prompts.default)({ + type: "text", + name: "plural", + message: promptMessage, + initial: initialPlural, + validate: (pluralInput) => validatePlural(pluralInput, model) + }); + const pluralToUse = promptResult.plural?.trim().replace(/\u0017/g, ""); + if (!pluralToUse) { + throw Error("Plural name must not be empty"); + } + (0, import_rwPluralize.addSingularPlural)(model, pluralToUse); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + ensureUniquePlural, + isWordPluralizable, + validatePlural +}); diff --git a/packages/cli/dist/lib/ports.js b/packages/cli/dist/lib/ports.js new file mode 100644 index 0000000000..1b92d10c8c --- /dev/null +++ b/packages/cli/dist/lib/ports.js @@ -0,0 +1,51 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var ports_exports = {}; +__export(ports_exports, { + getFreePort: () => getFreePort +}); +module.exports = __toCommonJS(ports_exports); +var import_portfinder = __toESM(require("portfinder")); +async function getFreePort(requestedPort, excludePorts = []) { + try { + let freePort = await import_portfinder.default.getPortPromise({ + port: requestedPort + }); + if (excludePorts.includes(freePort)) { + freePort = await getFreePort(freePort + 1, excludePorts); + } + return freePort; + } catch (error) { + return -1; + } +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + getFreePort +}); diff --git a/packages/cli/dist/lib/project.js b/packages/cli/dist/lib/project.js new file mode 100644 index 0000000000..da24d75114 --- /dev/null +++ b/packages/cli/dist/lib/project.js @@ -0,0 +1,66 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var project_exports = {}; +__export(project_exports, { + isTypeScriptProject: () => isTypeScriptProject, + serverFileExists: () => serverFileExists, + sides: () => sides +}); +module.exports = __toCommonJS(project_exports); +var import_path = __toESM(require("path")); +var import_fs_extra = __toESM(require("fs-extra")); +var import__ = require("."); +const isTypeScriptProject = () => { + const paths = (0, import__.getPaths)(); + return import_fs_extra.default.existsSync(import_path.default.join(paths.web.base, "tsconfig.json")) || import_fs_extra.default.existsSync(import_path.default.join(paths.api.base, "tsconfig.json")); +}; +const sides = () => { + const paths = (0, import__.getPaths)(); + let sides2 = []; + if (import_fs_extra.default.existsSync(import_path.default.join(paths.web.base, "package.json"))) { + sides2 = [...sides2, "web"]; + } + if (import_fs_extra.default.existsSync(import_path.default.join(paths.api.base, "package.json"))) { + sides2 = [...sides2, "api"]; + } + return sides2; +}; +const serverFileExists = () => { + const serverFilePath = import_path.default.join( + (0, import__.getPaths)().api.src, + `server.${isTypeScriptProject() ? "ts" : "js"}` + ); + return import_fs_extra.default.existsSync(serverFilePath); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + isTypeScriptProject, + serverFileExists, + sides +}); diff --git a/packages/cli/dist/lib/rollback.js b/packages/cli/dist/lib/rollback.js new file mode 100644 index 0000000000..1da4e339e2 --- /dev/null +++ b/packages/cli/dist/lib/rollback.js @@ -0,0 +1,110 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var rollback_exports = {}; +__export(rollback_exports, { + addFileToRollback: () => addFileToRollback, + addFunctionToRollback: () => addFunctionToRollback, + executeRollback: () => executeRollback, + prepareForRollback: () => prepareForRollback, + resetRollback: () => resetRollback +}); +module.exports = __toCommonJS(rollback_exports); +var import_path = __toESM(require("path")); +var import_fs_extra = __toESM(require("fs-extra")); +let rollback = []; +function addFunctionToRollback(func, atEnd = false) { + const step = { type: "func", func }; + if (atEnd) { + rollback.unshift(step); + } else { + rollback.push(step); + } +} +function addFileToRollback(path2, atEnd = false) { + const step = { + type: "file", + path: path2, + content: import_fs_extra.default.existsSync(path2) ? import_fs_extra.default.readFileSync(path2) : null + }; + if (atEnd) { + rollback.unshift(step); + } else { + rollback.push(step); + } +} +async function executeRollback(_ = null, task = null) { + if (task) { + task.title = "Reverting generator actions..."; + } + while (rollback.length > 0) { + const step = rollback.pop(); + switch (step.type) { + case "func": + await step.func(); + break; + case "file": + if (step.content === null) { + import_fs_extra.default.unlinkSync(step.path); + let parent = import_path.default.dirname(step.path); + if (parent !== "." && import_fs_extra.default.readdirSync(parent).length === 0) { + import_fs_extra.default.rmdirSync(parent); + } + parent = import_path.default.dirname(parent); + if (parent !== "." && import_fs_extra.default.readdirSync(parent).length === 0) { + import_fs_extra.default.rmdirSync(parent); + } + } else { + import_fs_extra.default.writeFileSync(step.path, step.content); + } + break; + default: + break; + } + } + if (task) { + task.title = `Reverted because: ${task.task.message.error}`; + } +} +function resetRollback() { + rollback.length = 0; +} +function prepareForRollback(tasks) { + resetRollback(); + tasks.tasks?.forEach((task) => { + task.task.rollback = executeRollback; + }); +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + addFileToRollback, + addFunctionToRollback, + executeRollback, + prepareForRollback, + resetRollback +}); diff --git a/packages/cli/dist/lib/rwPluralize.js b/packages/cli/dist/lib/rwPluralize.js new file mode 100644 index 0000000000..e363ea3cac --- /dev/null +++ b/packages/cli/dist/lib/rwPluralize.js @@ -0,0 +1,94 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var rwPluralize_exports = {}; +__export(rwPluralize_exports, { + addSingularPlural: () => addSingularPlural, + isPlural: () => isPlural, + isSingular: () => isSingular, + pluralize: () => pluralize, + singularize: () => singularize +}); +module.exports = __toCommonJS(rwPluralize_exports); +var plurals = __toESM(require("pluralize")); +const mappings = { + toSingular: {}, + toPlural: {} +}; +function lastWord(str) { + const capitals = str.match(/[A-Z]/g); + const lastIndex = str.lastIndexOf(capitals?.slice(-1)[0]); + return lastIndex >= 0 ? str.slice(lastIndex) : str; +} +function pluralize(word) { + if (mappings.toPlural[word]) { + return mappings.toPlural[word]; + } + const singular = lastWord(word); + const base = word.slice(0, word.length - singular.length); + if (mappings.toPlural[singular]) { + return base + mappings.toPlural[singular]; + } + return base + plurals.plural(singular); +} +function singularize(word) { + if (mappings.toSingular[word]) { + return mappings.toSingular[word]; + } + const plural = lastWord(word); + const base = word.slice(0, word.length - plural.length); + if (mappings.toSingular[plural]) { + return base + mappings.toSingular[plural]; + } + return base + plurals.singular(plural); +} +function isPlural(word) { + return plurals.isPlural(lastWord(word)); +} +function isSingular(word) { + return plurals.isSingular(lastWord(word)); +} +function addSingularPlural(singular, plural) { + const existingPlural = Object.keys(mappings.toSingular).find( + (key) => mappings.toSingular[key] === singular + ); + delete mappings.toSingular[existingPlural]; + delete mappings.toPlural[existingPlural]; + mappings.toPlural[singular] = plural; + mappings.toPlural[plural] = plural; + mappings.toSingular[plural] = singular; + mappings.toSingular[singular] = singular; +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + addSingularPlural, + isPlural, + isSingular, + pluralize, + singularize +}); diff --git a/packages/cli/dist/lib/schemaHelpers.js b/packages/cli/dist/lib/schemaHelpers.js new file mode 100644 index 0000000000..87d7a66d84 --- /dev/null +++ b/packages/cli/dist/lib/schemaHelpers.js @@ -0,0 +1,137 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var schemaHelpers_exports = {}; +__export(schemaHelpers_exports, { + getEnum: () => getEnum, + getSchema: () => getSchema, + getSchemaConfig: () => getSchemaConfig, + getSchemaDefinitions: () => getSchemaDefinitions, + verifyModelName: () => verifyModelName +}); +module.exports = __toCommonJS(schemaHelpers_exports); +var import_internals = require("@prisma/internals"); +var import_fs_extra = __toESM(require("fs-extra")); +var import_pluralHelpers = require("./pluralHelpers"); +var import_rwPluralize = require("./rwPluralize"); +var import__ = require("./"); +const schemaMemo = {}; +const getExistingModelName = async (name) => { + if (!name) { + return void 0; + } + const modelName = name.replace(/[_-]/g, "").toLowerCase(); + for (let model of Object.values(schemaMemo)) { + if (model.name.toLowerCase() === modelName) { + return model.name; + } + } + const schema = await getSchemaDefinitions(); + for (let model of schema.datamodel.models) { + if (model.name.toLowerCase() === modelName) { + return model.name; + } + } + return void 0; +}; +const getSchema = async (name) => { + if (name) { + const modelName = await getExistingModelName(name); + if (!modelName) { + throw new Error( + `No schema definition found for \`${name}\` in schema.prisma file` + ); + } + if (!schemaMemo[modelName]) { + const schema = await getSchemaDefinitions(); + const model = schema.datamodel.models.find((model2) => { + return model2.name === modelName; + }); + if (model) { + model.fields.forEach((field) => { + const fieldEnum = schema.datamodel.enums.find((e) => { + return field.type === e.name; + }); + if (fieldEnum) { + field.enumValues = fieldEnum.values; + } + }); + schemaMemo[modelName] = model; + } + } + return schemaMemo[modelName]; + } else { + return (await getSchemaDefinitions()).datamodel; + } +}; +const getEnum = async (name) => { + const schema = await getSchemaDefinitions(); + if (name) { + const model = schema.datamodel.enums.find((model2) => { + return model2.name === name; + }); + if (model) { + return model; + } else { + throw new Error( + `No enum schema definition found for \`${name}\` in schema.prisma file` + ); + } + } + return schema.metadata.datamodel.enums; +}; +const getSchemaDefinitions = () => { + return (0, import_internals.getDMMF)({ datamodelPath: (0, import__.getPaths)().api.dbSchema }); +}; +const getSchemaConfig = () => { + return (0, import_internals.getConfig)({ + datamodel: import_fs_extra.default.readFileSync((0, import__.getPaths)().api.dbSchema).toString() + }); +}; +async function verifyModelName(options) { + const modelName = await getExistingModelName(options.name) || await getExistingModelName((0, import_rwPluralize.singularize)(options.name)); + if (modelName === void 0) { + throw new Error( + `"${options.name}" model not found, check if it exists in "./api/db/schema.prisma"` + ); + } + await (0, import_pluralHelpers.ensureUniquePlural)({ + model: modelName, + isDestroyer: options.isDestroyer, + forcePrompt: (0, import_rwPluralize.isPlural)(modelName) + }); + return { ...options, name: modelName }; +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + getEnum, + getSchema, + getSchemaConfig, + getSchemaDefinitions, + verifyModelName +}); diff --git a/packages/cli/dist/lib/templates/storybook.preview.tsx.template b/packages/cli/dist/lib/templates/storybook.preview.tsx.template new file mode 100644 index 0000000000..042497ff61 --- /dev/null +++ b/packages/cli/dist/lib/templates/storybook.preview.tsx.template @@ -0,0 +1,18 @@ +import * as React from 'react' + +import type { GlobalTypes } from '@storybook/csf' +import type { StoryFn, StoryContext } from '@storybook/react' + +/** @type { import("@storybook/csf").GlobalTypes } */ +export const globalTypes: GlobalTypes = {} + +/** + * An example, no-op storybook decorator. Use a function like this to create decorators. + * @param { import("@storybook/react").StoryFn} StoryFn + * @param { import("@storybook/react").StoryContext} context +*/ +const _exampleDecorator = (StoryFn: StoryFn, _context: StoryContext) => { + return +} + +export const decorators = [] diff --git a/packages/cli/dist/lib/test.js b/packages/cli/dist/lib/test.js new file mode 100644 index 0000000000..c8e7b2e68f --- /dev/null +++ b/packages/cli/dist/lib/test.js @@ -0,0 +1,133 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var test_exports = {}; +__export(test_exports, { + generatorsRootPath: () => generatorsRootPath, + loadFixture: () => loadFixture, + loadGeneratorFixture: () => loadGeneratorFixture +}); +module.exports = __toCommonJS(test_exports); +var import_path = __toESM(require("path")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_vitest = require("vitest"); +var import_mockTelemetry = require("./mockTelemetry"); +import_vitest.vi.mock("@redwoodjs/internal/dist/generate/generate", () => { + return { + generate: () => { + return { errors: [] }; + } + }; +}); +import_vitest.vi.mock("@redwoodjs/project-config", async (importOriginal) => { + const path2 = require("path"); + const originalProjectConfig = await importOriginal(); + return { + ...originalProjectConfig, + getPaths: () => { + const BASE_PATH = "/path/to/project"; + return { + base: BASE_PATH, + api: { + dataMigrations: path2.join(BASE_PATH, "./api/prisma/dataMigrations"), + db: path2.join(globalThis.__dirname, "fixtures"), + // this folder + dbSchema: path2.join( + globalThis.__dirname, + "fixtures", + "schema.prisma" + ), + // this folder + generators: path2.join(BASE_PATH, "./api/generators"), + src: path2.join(BASE_PATH, "./api/src"), + services: path2.join(BASE_PATH, "./api/src/services"), + directives: path2.join(BASE_PATH, "./api/src/directives"), + graphql: path2.join(BASE_PATH, "./api/src/graphql"), + functions: path2.join(BASE_PATH, "./api/src/functions") + }, + web: { + config: path2.join(BASE_PATH, "./web/config"), + src: path2.join(BASE_PATH, "./web/src"), + generators: path2.join(BASE_PATH, "./web/generators"), + routes: path2.join(BASE_PATH, "web/src/Routes.js"), + components: path2.join(BASE_PATH, "/web/src/components"), + layouts: path2.join(BASE_PATH, "/web/src/layouts"), + pages: path2.join(BASE_PATH, "/web/src/pages"), + app: path2.join(BASE_PATH, "/web/src/App.js") + }, + scripts: path2.join(BASE_PATH, "scripts"), + generated: { + base: path2.join(BASE_PATH, ".redwood"), + schema: path2.join(BASE_PATH, ".redwood/schema.graphql"), + types: { + includes: path2.join(BASE_PATH, ".redwood/types/includes"), + mirror: path2.join(BASE_PATH, ".redwood/types/mirror") + } + } + }; + } + }; +}); +import_vitest.vi.mock("./project", () => ({ + isTypeScriptProject: () => false, + sides: () => ["web", "api"] +})); +globalThis.__prettierPath = import_path.default.resolve( + __dirname, + "./__tests__/fixtures/prettier.config.js" +); +import_vitest.vi.spyOn(Math, "random").mockReturnValue(0.123456789); +const generatorsRootPath = import_path.default.join( + __dirname, + "..", + "commands", + "generate" +); +const loadGeneratorFixture = (generator, name) => { + return loadFixture( + import_path.default.join( + __dirname, + "..", + "commands", + "generate", + generator, + "__tests__", + "fixtures", + name + ) + ); +}; +const loadFixture = (filepath) => { + return import_fs_extra.default.readFileSync(filepath).toString(); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + generatorsRootPath, + loadFixture, + loadGeneratorFixture +}); diff --git a/packages/cli/dist/lib/updateCheck.js b/packages/cli/dist/lib/updateCheck.js new file mode 100644 index 0000000000..4b79372625 --- /dev/null +++ b/packages/cli/dist/lib/updateCheck.js @@ -0,0 +1,246 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var updateCheck_exports = {}; +__export(updateCheck_exports, { + CHECK_LOCK_IDENTIFIER: () => CHECK_LOCK_IDENTIFIER, + DEFAULT_DATETIME_MS: () => DEFAULT_DATETIME_MS, + EXCLUDED_COMMANDS: () => EXCLUDED_COMMANDS, + SHOW_LOCK_IDENTIFIER: () => SHOW_LOCK_IDENTIFIER, + check: () => check, + isEnabled: () => isEnabled, + readUpdateDataFile: () => readUpdateDataFile, + shouldCheck: () => shouldCheck, + shouldShow: () => shouldShow, + showUpdateMessage: () => showUpdateMessage, + updateCheckMiddleware: () => updateCheckMiddleware +}); +module.exports = __toCommonJS(updateCheck_exports); +var import_path = __toESM(require("path")); +var import_boxen = __toESM(require("boxen")); +var import_chalk = __toESM(require("chalk")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_latest_version = __toESM(require("latest-version")); +var import_semver = __toESM(require("semver")); +var import_project_config = require("@redwoodjs/project-config"); +var import_background = require("./background"); +var import_locking = require("./locking"); +var import_index = require("./index"); +const CHECK_PERIOD = 24 * 60 * 6e4; +const SHOW_PERIOD = 24 * 60 * 6e4; +const DEFAULT_DATETIME_MS = 9466848e5; +const CHECK_LOCK_IDENTIFIER = "UPDATE_CHECK"; +const SHOW_LOCK_IDENTIFIER = "UPDATE_CHECK_SHOW"; +const EXCLUDED_COMMANDS = ["upgrade", "ts-to-js"]; +let persistenceDirectory; +function getPersistenceDirectory() { + if (persistenceDirectory) { + return persistenceDirectory; + } + persistenceDirectory = import_path.default.join((0, import_index.getPaths)().generated.base, "updateCheck"); + return persistenceDirectory; +} +async function check() { + try { + console.time("Update Check"); + const packageJson = JSON.parse( + import_fs_extra.default.readFileSync(import_path.default.join((0, import_index.getPaths)().base, "package.json")) + ); + let localVersion = packageJson.devDependencies["@redwoodjs/core"]; + while (!/\d/.test(localVersion.charAt(0))) { + localVersion = localVersion.substring(1); + } + console.log(`Detected the current version of RedwoodJS: '${localVersion}'`); + const remoteVersions = /* @__PURE__ */ new Map(); + for (const tag of (0, import_project_config.getConfig)().notifications.versionUpdates) { + console.log(`Checking for new versions for npm tag: '${tag}'`); + try { + remoteVersions.set( + tag, + await (0, import_latest_version.default)("@redwoodjs/core", { version: tag }) + ); + } catch (error) { + console.error(`Couldn't find a version for tag: '${tag}'`); + console.error(error); + } + } + console.log(`Detected the latest versions of RedwoodJS as:`); + console.log(JSON.stringify([...remoteVersions.entries()], void 0, 2)); + console.log("Saving updated version information for future checks..."); + updateUpdateDataFile({ + localVersion, + remoteVersions, + checkedAt: (/* @__PURE__ */ new Date()).getTime() + }); + } finally { + (0, import_locking.unsetLock)(CHECK_LOCK_IDENTIFIER); + console.timeEnd("Update Check"); + } +} +function isEnabled() { + return (0, import_project_config.getConfig)().notifications.versionUpdates.length > 0; +} +function shouldCheck() { + if ((0, import_locking.isLockSet)(CHECK_LOCK_IDENTIFIER)) { + return false; + } + const data = readUpdateDataFile(); + return data.checkedAt < (/* @__PURE__ */ new Date()).getTime() - CHECK_PERIOD; +} +function shouldShow() { + if ((0, import_locking.isLockSet)(SHOW_LOCK_IDENTIFIER)) { + return false; + } + const data = readUpdateDataFile(); + let newerVersion = false; + data.remoteVersions.forEach((version) => { + newerVersion ||= import_semver.default.gt(version, data.localVersion); + }); + return data.shownAt < (/* @__PURE__ */ new Date()).getTime() - SHOW_PERIOD && newerVersion; +} +function showUpdateMessage() { + console.log(getUpdateMessage()); + updateUpdateDataFile({ shownAt: (/* @__PURE__ */ new Date()).getTime() }); +} +function getUpdateMessage() { + const data = readUpdateDataFile(); + const localTag = extractTagFromVersion(data.localVersion) || "latest"; + let updateCount = 0; + let message = " New updates to Redwood are available via `yarn rw upgrade#REPLACEME#` "; + data.remoteVersions.forEach((version, tag) => { + if (import_semver.default.gt(version, data.localVersion)) { + updateCount += 1; + if (tag === localTag) { + message += ` + + \u2756 ${import_chalk.default.underline(import_chalk.default.bold(tag))}: + v${data.localVersion} -> v${version} `; + } else { + message += ` + + \u2756 ${tag}: + v${version} `; + } + } + }); + message += "\n\n See release notes at: https://github.com/redwoodjs/redwood/releases "; + message = message.replace("#REPLACEME#", updateCount > 1 ? " -t [tag]" : ""); + return (0, import_boxen.default)(message, { + padding: 0, + margin: 1, + title: `Redwood Update${updateCount > 1 ? "s " : " "}available \u{1F389}`, + borderColor: "#0b8379", + // The RedwoodJS colour + borderStyle: "round" + }); +} +function readUpdateDataFile() { + try { + if (!import_fs_extra.default.existsSync(getPersistenceDirectory())) { + import_fs_extra.default.mkdirSync(getPersistenceDirectory(), { recursive: true }); + } + const persistedData = JSON.parse( + import_fs_extra.default.readFileSync(import_path.default.join(getPersistenceDirectory(), "data.json")) + ); + persistedData.remoteVersions = new Map( + Object.entries(persistedData.remoteVersions) + ); + return persistedData; + } catch (error) { + if (error.code === "ENOENT") { + return { + localVersion: "0.0.0", + remoteVersions: /* @__PURE__ */ new Map(), + checkedAt: DEFAULT_DATETIME_MS, + shownAt: DEFAULT_DATETIME_MS + }; + } + throw error; + } +} +function updateUpdateDataFile({ + localVersion, + remoteVersions, + checkedAt, + shownAt +} = {}) { + const existingData = readUpdateDataFile(); + const updatedData = { + localVersion: localVersion ?? existingData.localVersion, + remoteVersions: Object.fromEntries( + remoteVersions ?? existingData.remoteVersions + ), + checkedAt: checkedAt ?? existingData.checkedAt, + shownAt: shownAt ?? existingData.shownAt + }; + import_fs_extra.default.writeFileSync( + import_path.default.join(getPersistenceDirectory(), "data.json"), + JSON.stringify(updatedData, null, 2) + ); +} +function extractTagFromVersion(version) { + const tagIndex = version.indexOf("-"); + if (tagIndex === -1) { + return ""; + } + const tag = version.substring(tagIndex + 1).trim(); + return tag.includes(".") ? tag.split(".")[0] : tag; +} +function updateCheckMiddleware(argv) { + if (EXCLUDED_COMMANDS.includes(argv._[0])) { + return; + } + if (shouldShow()) { + (0, import_locking.setLock)(SHOW_LOCK_IDENTIFIER); + process.on("exit", () => { + showUpdateMessage(); + (0, import_locking.unsetLock)(SHOW_LOCK_IDENTIFIER); + }); + } + if (shouldCheck()) { + (0, import_locking.setLock)(CHECK_LOCK_IDENTIFIER); + (0, import_background.spawnBackgroundProcess)("updateCheck", "yarn", [ + "node", + import_path.default.join(__dirname, "updateCheckExecute.js") + ]); + } +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + CHECK_LOCK_IDENTIFIER, + DEFAULT_DATETIME_MS, + EXCLUDED_COMMANDS, + SHOW_LOCK_IDENTIFIER, + check, + isEnabled, + readUpdateDataFile, + shouldCheck, + shouldShow, + showUpdateMessage, + updateCheckMiddleware +}); diff --git a/packages/cli/dist/lib/updateCheckExecute.js b/packages/cli/dist/lib/updateCheckExecute.js new file mode 100644 index 0000000000..451bee3963 --- /dev/null +++ b/packages/cli/dist/lib/updateCheckExecute.js @@ -0,0 +1,3 @@ +"use strict"; +var import_updateCheck = require("./updateCheck"); +(0, import_updateCheck.check)(); diff --git a/packages/cli/dist/middleware/checkNodeVersion.js b/packages/cli/dist/middleware/checkNodeVersion.js new file mode 100644 index 0000000000..2e3f27b12e --- /dev/null +++ b/packages/cli/dist/middleware/checkNodeVersion.js @@ -0,0 +1,57 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var checkNodeVersion_exports = {}; +__export(checkNodeVersion_exports, { + checkNodeVersion: () => checkNodeVersion +}); +module.exports = __toCommonJS(checkNodeVersion_exports); +var import_semver = __toESM(require("semver")); +var import_colors = __toESM(require("../lib/colors")); +function checkNodeVersion() { + const checks = { ok: true }; + const pVersion = process.version; + const pVersionC = import_semver.default.clean(pVersion); + const LOWER_BOUND = "v20.0.0"; + const LOWER_BOUND_C = import_semver.default.clean(LOWER_BOUND); + if (import_semver.default.gt(pVersionC, LOWER_BOUND_C)) { + return checks; + } + checks.ok = false; + checks.message = [ + `Your Node.js version is ${import_colors.default.warning( + pVersion + )}, but Redwood requires ${import_colors.default.green(`>=${LOWER_BOUND}`)}.`, + "Upgrade your Node.js version using `nvm` or a similar tool. See https://redwoodjs.com/docs/how-to/using-nvm." + ].join("\n"); + return checks; +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + checkNodeVersion +}); diff --git a/packages/cli/dist/middleware/detectProjectRwVersion.js b/packages/cli/dist/middleware/detectProjectRwVersion.js new file mode 100644 index 0000000000..74d447a86a --- /dev/null +++ b/packages/cli/dist/middleware/detectProjectRwVersion.js @@ -0,0 +1,33 @@ +"use strict"; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var detectProjectRwVersion_exports = {}; +__export(detectProjectRwVersion_exports, { + default: () => detectProjectRwVersion_default +}); +module.exports = __toCommonJS(detectProjectRwVersion_exports); +var import_lib = require("../lib"); +const detectRwVersion = (argv) => { + if (!argv.rwVersion) { + return { + rwVersion: (0, import_lib.getInstalledRedwoodVersion)() + }; + } + return {}; +}; +var detectProjectRwVersion_default = detectRwVersion; diff --git a/packages/cli/dist/plugin.js b/packages/cli/dist/plugin.js new file mode 100644 index 0000000000..5baae86767 --- /dev/null +++ b/packages/cli/dist/plugin.js @@ -0,0 +1,242 @@ +"use strict"; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var plugin_exports = {}; +__export(plugin_exports, { + loadPlugins: () => loadPlugins +}); +module.exports = __toCommonJS(plugin_exports); +var import_lib = require("./lib"); +var import_plugin = require("./lib/plugin"); +async function loadPlugins(yargs) { + const namespaceIsExplicit = process.argv[2]?.startsWith("@"); + const namespaceInUse = (namespaceIsExplicit ? process.argv[2] : "@redwoodjs") ?? "@redwoodjs"; + const commandString = namespaceIsExplicit ? process.argv.slice(3).join(" ") : process.argv.slice(2).join(" "); + const commandFirstWord = commandString.split(" ")[0]; + if (commandFirstWord === "--version" && namespaceInUse === "@redwoodjs") { + return yargs; + } + const pluginCommandCache = (0, import_plugin.loadCommandCache)(); + if (pluginCommandCache._builtin.includes(commandFirstWord) && namespaceInUse === "@redwoodjs") { + return yargs; + } + const { plugins, autoInstall } = (0, import_lib.getConfig)().experimental.cli; + const enabledPlugins = plugins.filter( + (p) => p.package !== void 0 && (p.enabled ?? true) + ); + (0, import_plugin.checkPluginListAndWarn)(enabledPlugins); + const redwoodPackages = /* @__PURE__ */ new Set(); + const thirdPartyPackages = /* @__PURE__ */ new Set(); + for (const plugin of enabledPlugins) { + if (!plugin.package) { + continue; + } + if (!plugin.package.startsWith("@")) { + continue; + } + if (plugin.package.startsWith("@redwoodjs/")) { + redwoodPackages.add(plugin.package); + } else { + thirdPartyPackages.add(plugin.package); + } + } + const namespaces = Array.from(thirdPartyPackages).map((p) => p.split("/")[0]).sort(); + if (redwoodPackages.size > 0) { + namespaces.unshift("@redwoodjs"); + } + const showingHelpAtRootLevel = !namespaceIsExplicit && (commandFirstWord === "--help" || commandFirstWord === "-h" || commandFirstWord === ""); + const namespaceIsUnknown = !namespaces.includes(namespaceInUse); + if (showingHelpAtRootLevel || namespaceIsUnknown) { + for (const namespace of namespaces) { + if (namespace === "@redwoodjs") { + for (const redwoodPluginPackage of redwoodPackages) { + const commands = await loadCommandsFromCacheOrPackage( + redwoodPluginPackage, + pluginCommandCache, + autoInstall, + true + ); + yargs.command(commands); + } + } else { + yargs.command({ + command: `${namespace} `, + describe: `Commands from ${namespace}`, + builder: () => { + }, + handler: () => { + } + }); + } + } + (0, import_plugin.saveCommandCache)(pluginCommandCache); + return yargs; + } + const showingHelpAtNamespaceLevel = namespaceIsExplicit && (commandFirstWord === "--help" || commandFirstWord === "-h" || commandFirstWord === ""); + if (showingHelpAtNamespaceLevel) { + if (namespaceInUse === "@redwoodjs") { + for (const redwoodPluginPackage of redwoodPackages) { + const commands = await loadCommandsFromCacheOrPackage( + redwoodPluginPackage, + pluginCommandCache, + autoInstall, + true + ); + yargs.command(commands); + } + } else { + const packagesForNamespace = Array.from(thirdPartyPackages).filter( + (p) => p.startsWith(namespaceInUse) + ); + for (const packageForNamespace of packagesForNamespace) { + const commands = await loadCommandsFromCacheOrPackage( + packageForNamespace, + pluginCommandCache, + autoInstall, + true + ); + yargs.command({ + command: `${namespaceInUse} `, + describe: `Commands from ${namespaceInUse}`, + builder: (yargs2) => { + yargs2.command(commands).demandCommand(); + }, + handler: () => { + } + }); + } + } + (0, import_plugin.saveCommandCache)(pluginCommandCache); + return yargs; + } + const packagesToLoad = /* @__PURE__ */ new Set(); + for (const [packageName, cacheEntry] of Object.entries(pluginCommandCache)) { + if (packageName === "_builtin") { + continue; + } + const commandFirstWords = []; + for (const [command, info] of Object.entries(cacheEntry)) { + commandFirstWords.push(command.split(" ")[0]); + commandFirstWords.push( + ...info.aliases?.map((a) => a.split(" ")[0]) ?? [] + ); + } + if (commandFirstWords.includes(commandFirstWord) && packageName.startsWith(namespaceInUse)) { + packagesToLoad.add(packageName); + break; + } + } + const foundMatchingPackage = packagesToLoad.size > 0; + if (!foundMatchingPackage) { + for (const plugin of enabledPlugins) { + if (plugin.package.startsWith(namespaceInUse)) { + packagesToLoad.add(plugin.package); + } + } + } + const commandsToRegister = []; + if (foundMatchingPackage) { + const packageToLoad = packagesToLoad.values().next().value; + const commands = await loadCommandsFromCacheOrPackage( + packageToLoad, + pluginCommandCache, + autoInstall, + false + ); + commandsToRegister.push(...commands); + } else { + for (const packageToLoad of packagesToLoad) { + const commands = await loadCommandsFromCacheOrPackage( + packageToLoad, + pluginCommandCache, + autoInstall, + true + ); + commandsToRegister.push(...commands); + } + } + if (namespaceInUse === "@redwoodjs") { + yargs.command(commandsToRegister); + } else { + yargs.command({ + command: `${namespaceInUse} `, + describe: `Commands from ${namespaceInUse}`, + builder: (yargs2) => { + yargs2.command(commandsToRegister).demandCommand(); + }, + handler: () => { + } + }); + } + if (!namespaceIsExplicit) { + for (const namespace of namespaces) { + if (namespace === "@redwoodjs") { + continue; + } + yargs.command({ + command: `${namespace} `, + describe: `Commands from ${namespace}`, + builder: () => { + }, + handler: () => { + } + }); + } + } + (0, import_plugin.saveCommandCache)(pluginCommandCache); + return yargs; +} +async function loadCommandsFromCacheOrPackage(packageName, cache, autoInstall, readFromCache) { + let cacheEntry = void 0; + if (readFromCache) { + cacheEntry = cache !== void 0 ? cache[packageName] : void 0; + } + if (cacheEntry !== void 0) { + const commands = Object.entries(cacheEntry).map(([command, info]) => { + return { + command, + describe: info.description, + aliases: info.aliases + }; + }); + return commands; + } + const plugin = await (0, import_plugin.loadPluginPackage)(packageName, void 0, autoInstall); + if (plugin) { + const commands = plugin.commands ?? []; + const cacheUpdate = {}; + for (const command of commands) { + const info = { + aliases: command.aliases, + description: command.description + }; + if (Object.values(info).some((value) => value !== void 0)) { + cacheUpdate[command.command] = info; + } + } + if (Object.keys(cacheUpdate).length > 0) { + cache[packageName] = cacheUpdate; + } + return commands; + } + return []; +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + loadPlugins +}); diff --git a/packages/cli/dist/rwfw.js b/packages/cli/dist/rwfw.js new file mode 100755 index 0000000000..607985aeb4 --- /dev/null +++ b/packages/cli/dist/rwfw.js @@ -0,0 +1,70 @@ +#!/usr/bin/env node +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var import_path = __toESM(require("path")); +var import_configstore = __toESM(require("configstore/index")); +var import_execa = __toESM(require("execa")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_terminal_link = __toESM(require("terminal-link")); +var import_project_config = require("@redwoodjs/project-config"); +const config = new import_configstore.default("@redwoodjs/cli"); +const RWFW_PATH = process.env.RWFW_PATH || process.env.RW_PATH || config.get("RWFW_PATH"); +if (!RWFW_PATH) { + console.error("Error: You must specify the path to Redwood Framework"); + console.error("Usage: `RWFW_PATH=~/gh/redwoodjs/redwood yarn rwfw "); + process.exit(1); +} +if (!import_fs_extra.default.existsSync(RWFW_PATH)) { + console.error( + `Error: The specified path to Redwood Framework (${RWFW_PATH}) does not exist.` + ); + console.error("Usage: `RWFW_PATH=~/gh/redwoodjs/redwood yarn rwfw "); + process.exit(1); +} +const absRwFwPath = import_path.default.resolve(process.cwd(), RWFW_PATH); +config.set("RWFW_PATH", absRwFwPath); +const projectPath = import_path.default.dirname( + (0, import_project_config.getConfigPath)(process.env.RWJS_CWD ?? process.cwd()) +); +console.log( + "Redwood Framework Tools Path:", + (0, import_terminal_link.default)(absRwFwPath, absRwFwPath) +); +let command = process.argv.slice(2); +const helpCommands = ["help", "--help"]; +if (!command.length || command.some((cmd) => helpCommands.includes(cmd))) { + command = ["run"]; +} +try { + import_execa.default.sync("yarn", [...command], { + stdio: "inherit", + shell: true, + cwd: absRwFwPath, + env: { + RWJS_CWD: projectPath + } + }); +} catch (e) { + console.log(); +} diff --git a/packages/cli/dist/telemetry/exporter.js b/packages/cli/dist/telemetry/exporter.js new file mode 100644 index 0000000000..dfcb71047c --- /dev/null +++ b/packages/cli/dist/telemetry/exporter.js @@ -0,0 +1,97 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var exporter_exports = {}; +__export(exporter_exports, { + CustomFileExporter: () => CustomFileExporter +}); +module.exports = __toCommonJS(exporter_exports); +var import_path = __toESM(require("path")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_project_config = require("@redwoodjs/project-config"); +class CustomFileExporter { + /** + * @type string + * @private + */ + #storageFileName; + /** + * @type string + * @private + */ + #storageFilePath; + /** + * @type boolean + * @private + */ + #isShutdown = false; + constructor() { + this.#storageFileName = `${Date.now()}.json`; + this.#storageFilePath = import_path.default.join( + (0, import_project_config.getPaths)().generated.base, + "telemetry", + this.#storageFileName + ); + import_fs_extra.default.ensureDirSync(import_path.default.dirname(this.#storageFilePath)); + import_fs_extra.default.writeFileSync(this.#storageFilePath, "["); + } + /** + * Called to export sampled {@link ReadableSpan}s. + * @param spans the list of sampled Spans to be exported. + */ + export(spans, resultCallback) { + for (let i = 0; i < spans.length; i++) { + const span = spans[i]; + delete span["_spanProcessor"]; + import_fs_extra.default.appendFileSync( + this.#storageFilePath, + JSON.stringify(span, void 0, 2) + ); + import_fs_extra.default.appendFileSync(this.#storageFilePath, ","); + } + resultCallback({ code: 0 }); + } + /** Stops the exporter. */ + shutdown() { + if (!this.#isShutdown) { + import_fs_extra.default.truncateSync( + this.#storageFilePath, + import_fs_extra.default.statSync(this.#storageFilePath).size - 1 + ); + import_fs_extra.default.appendFileSync(this.#storageFilePath, "]"); + this.#isShutdown = true; + } + } + /** Immediately export all spans */ + forceFlush() { + } +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + CustomFileExporter +}); diff --git a/packages/cli/dist/telemetry/index.js b/packages/cli/dist/telemetry/index.js new file mode 100644 index 0000000000..b738585db1 --- /dev/null +++ b/packages/cli/dist/telemetry/index.js @@ -0,0 +1,116 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var telemetry_exports = {}; +__export(telemetry_exports, { + shutdownTelemetry: () => shutdownTelemetry, + startTelemetry: () => startTelemetry +}); +module.exports = __toCommonJS(telemetry_exports); +var import_path = __toESM(require("path")); +var import_api = require("@opentelemetry/api"); +var import_api2 = __toESM(require("@opentelemetry/api")); +var import_sdk_trace_node = require("@opentelemetry/sdk-trace-node"); +var import_helpers = require("yargs/helpers"); +var import_background = require("../lib/background"); +var import_exporter = require("./exporter"); +let traceProvider; +let traceProcessor; +let traceExporter; +let isStarted = false; +let isShutdown = false; +async function startTelemetry() { + if (isStarted) { + return; + } + isStarted = true; + try { + import_api.diag.setLogger(new import_api.DiagConsoleLogger(), import_api.DiagLogLevel.ERROR); + traceProvider = new import_sdk_trace_node.NodeTracerProvider({ + sampler: { + shouldSample: () => { + return { + decision: isShutdown ? import_sdk_trace_node.SamplingDecision.NOT_RECORD : import_sdk_trace_node.SamplingDecision.RECORD_AND_SAMPLED + }; + }, + toString: () => { + return "AlwaysSampleWhenNotShutdown"; + } + } + }); + traceExporter = new import_exporter.CustomFileExporter(); + traceProcessor = new import_sdk_trace_node.SimpleSpanProcessor(traceExporter); + traceProvider.addSpanProcessor(traceProcessor); + traceProvider.register(); + const cleanArgv = (0, import_helpers.hideBin)(process.argv); + if (!cleanArgv.includes("sb") && !cleanArgv.includes("storybook")) { + for (const signal of ["SIGTERM", "SIGINT", "SIGHUP"]) { + process.on(signal, () => { + if (process.listenerCount(signal) === 1) { + console.log(`Received ${signal} signal, exiting...`); + process.exit(); + } + }); + } + } else { + process.on("shutdown-telemetry", () => { + shutdownTelemetry(); + }); + } + process.on("exit", () => { + shutdownTelemetry(); + }); + } catch (error) { + console.error("Telemetry error"); + console.error(error); + } +} +function shutdownTelemetry() { + if (isShutdown || !isStarted) { + return; + } + isShutdown = true; + try { + while (import_api2.default.trace.getActiveSpan()?.isRecording()) { + import_api2.default.trace.getActiveSpan()?.end(); + } + traceExporter?.shutdown(); + (0, import_background.spawnBackgroundProcess)("telemetry", "yarn", [ + "node", + import_path.default.join(__dirname, "send.js") + ]); + } catch (error) { + console.error("Telemetry error"); + console.error(error); + } +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + shutdownTelemetry, + startTelemetry +}); diff --git a/packages/cli/dist/telemetry/resource.js b/packages/cli/dist/telemetry/resource.js new file mode 100644 index 0000000000..72bce746c8 --- /dev/null +++ b/packages/cli/dist/telemetry/resource.js @@ -0,0 +1,132 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var resource_exports = {}; +__export(resource_exports, { + getResources: () => getResources +}); +module.exports = __toCommonJS(resource_exports); +var import_path = __toESM(require("path")); +var import_semantic_conventions = require("@opentelemetry/semantic-conventions"); +var import_ci_info = __toESM(require("ci-info")); +var import_envinfo = __toESM(require("envinfo")); +var import_fs_extra = __toESM(require("fs-extra")); +var import_systeminformation = __toESM(require("systeminformation")); +var import_uuid = require("uuid"); +var import_project_config = require("@redwoodjs/project-config"); +var import_hosts = require("@redwoodjs/structure/dist/hosts"); +var import_RWProject = require("@redwoodjs/structure/dist/model/RWProject"); +var import_package = require("../../package"); +async function getResources() { + let UID = (0, import_uuid.v4)(); + try { + const telemetryFile = import_path.default.join((0, import_project_config.getPaths)().generated.base, "telemetry.txt"); + if (!import_fs_extra.default.existsSync(telemetryFile)) { + import_fs_extra.default.ensureFileSync(telemetryFile); + } + if (import_fs_extra.default.statSync(telemetryFile).mtimeMs < Date.now() - 864e5) { + import_fs_extra.default.writeFileSync(telemetryFile, UID); + } else { + const storedUID = import_fs_extra.default.readFileSync(telemetryFile, { encoding: "utf8" }); + if (storedUID && (0, import_uuid.validate)(storedUID)) { + UID = storedUID; + } else { + import_fs_extra.default.writeFileSync(telemetryFile, UID); + } + } + } catch (_error) { + } + const info = JSON.parse( + await import_envinfo.default.run( + { + System: ["OS", "Shell"], + Binaries: ["Node", "Yarn", "npm"], + npmPackages: "@redwoodjs/*", + IDEs: ["VSCode"] + }, + { json: true } + ) + ); + const shell = info.System?.Shell; + if (shell?.path?.match("/")) { + info.System.Shell.name = info.System.Shell.path.split("/").pop(); + } else if (shell?.path.match("\\")) { + info.System.Shell.name = info.System.Shell.path.split("\\").pop(); + } + const cpu = await import_systeminformation.default.cpu(); + const mem = await import_systeminformation.default.mem(); + let developmentEnvironment = void 0; + if (Object.keys(process.env).some((key) => key.startsWith("GITPOD_"))) { + developmentEnvironment = "gitpod"; + } + const webBundler = (0, import_project_config.getConfig)().web.bundler; + const experiments = Object.keys((0, import_project_config.getRawConfig)()["experimental"] || {}); + if (webBundler !== "webpack") { + experiments.push(webBundler); + } + const project = new import_RWProject.RWProject({ + host: new import_hosts.DefaultHost(), + projectRoot: (0, import_project_config.getPaths)().base + }); + const routes = project.getRouter().routes; + const prerenderedRoutes = routes.filter((route) => route.hasPrerender); + const complexity = [ + routes.length, + prerenderedRoutes.length, + project.services.length, + project.cells.length, + project.pages.length + ].join("."); + const sides = project.sides.join(","); + return { + [import_semantic_conventions.SemanticResourceAttributes.SERVICE_NAME]: import_package.name, + [import_semantic_conventions.SemanticResourceAttributes.SERVICE_VERSION]: import_package.version, + [import_semantic_conventions.SemanticResourceAttributes.OS_TYPE]: info.System?.OS?.split(" ")[0], + [import_semantic_conventions.SemanticResourceAttributes.OS_VERSION]: info.System?.OS?.split(" ")[1], + "shell.name": info.System?.Shell?.name, + "node.version": info.Binaries?.Node?.version, + "yarn.version": info.Binaries?.Yarn?.version, + "npm.version": info.Binaries?.npm?.version, + "vscode.version": info.IDEs?.VSCode?.version, + "cpu.count": cpu.physicalCores, + "memory.gb": Math.round(mem.total / 1073741824), + "env.node_env": process.env.NODE_ENV || null, + "ci.redwood": !!process.env.REDWOOD_CI, + "ci.isci": import_ci_info.default.isCI, + "dev.environment": developmentEnvironment, + complexity, + sides, + experiments: JSON.stringify(experiments), + webBundler, + uid: UID + }; +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + getResources +}); diff --git a/packages/cli/dist/telemetry/send.js b/packages/cli/dist/telemetry/send.js new file mode 100644 index 0000000000..814b761304 --- /dev/null +++ b/packages/cli/dist/telemetry/send.js @@ -0,0 +1,103 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var import_path = __toESM(require("path")); +var import_exporter_trace_otlp_http = require("@opentelemetry/exporter-trace-otlp-http"); +var import_resources = require("@opentelemetry/resources"); +var import_fs_extra = __toESM(require("fs-extra")); +var import_project_config = require("@redwoodjs/project-config"); +var import_resource = require("./resource"); +async function main() { + console.log( + "You can disable telemetry by:\n - setting the 'REDWOOD_DISABLE_TELEMETRY' environment variable\n - passing the '--no-telemetry' flag when using the CLI" + ); + console.log( + "Information about Redwood telemetry can be found at:\n - https://telemetry.redwoodjs.com\n" + ); + const telemetryDir = import_path.default.join((0, import_project_config.getPaths)().generated.base, "telemetry"); + import_fs_extra.default.ensureDirSync(telemetryDir); + const telemetryFiles = import_fs_extra.default.readdirSync( + import_path.default.join((0, import_project_config.getPaths)().generated.base, "telemetry") + ); + console.time("Computed resource information"); + const customResourceData = await (0, import_resource.getResources)(); + console.timeEnd("Computed resource information"); + const resource = import_resources.Resource.default().merge(new import_resources.Resource(customResourceData)); + const url = process.env.REDWOOD_REDIRECT_TELEMETRY || "https://quark.quantumparticle.io/v1/traces"; + const traceExporter = new import_exporter_trace_otlp_http.OTLPTraceExporter({ + url + }); + console.log(`Sending telemetry data to '${url}'`); + for (const [index, file] of telemetryFiles.entries()) { + if (file.startsWith("_")) { + continue; + } + console.log(`Sending data from telemetry file '${file}'`); + let spans = []; + try { + spans = import_fs_extra.default.readJSONSync(import_path.default.join(telemetryDir, file)); + } catch (error) { + console.error(`Error reading telemetry file '${file}'`); + console.error(error); + console.error("Deleting this file to prevent further errors"); + import_fs_extra.default.unlinkSync(import_path.default.join(telemetryDir, file)); + continue; + } + if (!Array.isArray(spans)) { + console.error( + `Telemetry file '${file}' does not contain an array of spans. Deleting this file to prevent further errors.` + ); + import_fs_extra.default.unlinkSync(import_path.default.join(telemetryDir, file)); + continue; + } + for (const span of spans) { + span.resource = resource; + span.attributes ??= span._attributes ?? {}; + span.spanContext = () => span._spanContext; + span.events = []; + } + traceExporter.export(spans, ({ code, error }) => { + if (code !== 0) { + console.error("Encountered:"); + console.error(error); + console.error("while exporting the following spans:"); + console.error(spans); + } + }); + import_fs_extra.default.writeJSONSync(import_path.default.join(telemetryDir, `_${file}`), spans, { spaces: 2 }); + import_fs_extra.default.unlinkSync(import_path.default.join(telemetryDir, file)); + telemetryFiles[index] = `_${file}`; + } + traceExporter.shutdown(); + console.log( + "Keeping the lastest 8 telemetry files for visibility/transparency." + ); + const sortedTelemetryFiles = telemetryFiles.sort((a, b) => { + return parseInt(b.split(".")[0].replace("_", "")) - parseInt(a.split(".")[0].replace("_", "")); + }); + for (let i = 8; i < sortedTelemetryFiles.length; i++) { + console.log(`Removing telemetry file '${sortedTelemetryFiles[i]}'`); + import_fs_extra.default.unlinkSync(import_path.default.join(telemetryDir, sortedTelemetryFiles[i])); + } +} +main(); diff --git a/packages/cli/dist/testLib/cells.js b/packages/cli/dist/testLib/cells.js new file mode 100644 index 0000000000..6c287e64bb --- /dev/null +++ b/packages/cli/dist/testLib/cells.js @@ -0,0 +1,230 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var cells_exports = {}; +__export(cells_exports, { + fileToAst: () => fileToAst, + findCells: () => findCells, + getCellGqlQuery: () => getCellGqlQuery, + getNamedExports: () => getNamedExports, + hasDefaultExport: () => hasDefaultExport, + isCellFile: () => isCellFile, + isFileInsideFolder: () => isFileInsideFolder, + parseDocumentAST: () => parseDocumentAST, + parseGqlQueryToAst: () => parseGqlQueryToAst +}); +module.exports = __toCommonJS(cells_exports); +var import_fs = __toESM(require("fs")); +var import_path = __toESM(require("path")); +var import_core = require("@babel/core"); +var import_parser = require("@babel/parser"); +var import_traverse = __toESM(require("@babel/traverse")); +var import_fast_glob = __toESM(require("fast-glob")); +var import_graphql = require("graphql"); +var import_project_config = require("@redwoodjs/project-config"); +const findCells = (cwd = (0, import_project_config.getPaths)().web.src) => { + const modules = import_fast_glob.default.sync("**/*Cell.{js,jsx,ts,tsx}", { + cwd, + absolute: true, + ignore: ["node_modules"] + }); + return modules.filter(isCellFile); +}; +const isCellFile = (p) => { + const { dir, name } = import_path.default.parse(p); + if (!isFileInsideFolder(p, (0, import_project_config.getPaths)().web.src)) { + return false; + } + if (!dir.endsWith(name)) { + return false; + } + const ast = fileToAst(p); + if (hasDefaultExport(ast)) { + return false; + } + const exports2 = getNamedExports(ast); + const exportedQUERY = exports2.findIndex((v) => v.name === "QUERY") !== -1; + const exportedSuccess = exports2.findIndex((v) => v.name === "Success") !== -1; + if (!exportedQUERY && !exportedSuccess) { + return false; + } + return true; +}; +const isFileInsideFolder = (filePath, folderPath) => { + const { dir } = import_path.default.parse(filePath); + const relativePathFromFolder = import_path.default.relative(folderPath, dir); + if (!relativePathFromFolder || relativePathFromFolder.startsWith("..") || import_path.default.isAbsolute(relativePathFromFolder)) { + return false; + } else { + return true; + } +}; +const hasDefaultExport = (ast) => { + let exported = false; + (0, import_traverse.default)(ast, { + ExportDefaultDeclaration() { + exported = true; + return; + } + }); + return exported; +}; +const getNamedExports = (ast) => { + const namedExports = []; + (0, import_traverse.default)(ast, { + ExportNamedDeclaration(path2) { + const specifiers = path2.node?.specifiers; + if (specifiers.length) { + for (const s of specifiers) { + const id = s.exported; + namedExports.push({ + name: id.name, + type: "re-export" + }); + } + return; + } + const declaration = path2.node.declaration; + if (!declaration) { + return; + } + if (declaration.type === "VariableDeclaration") { + const id = declaration.declarations[0].id; + namedExports.push({ + name: id.name, + type: "variable" + }); + } else if (declaration.type === "FunctionDeclaration") { + namedExports.push({ + name: declaration?.id?.name, + type: "function" + }); + } else if (declaration.type === "ClassDeclaration") { + namedExports.push({ + name: declaration?.id?.name, + type: "class" + }); + } + } + }); + return namedExports; +}; +const fileToAst = (filePath) => { + const code = import_fs.default.readFileSync(filePath, "utf-8"); + const isJsxFile = import_path.default.extname(filePath).match(/[jt]sx$/) || isFileInsideFolder(filePath, (0, import_project_config.getPaths)().web.base); + const plugins = [ + "typescript", + "nullishCoalescingOperator", + "objectRestSpread", + isJsxFile && "jsx" + ].filter(Boolean); + try { + return (0, import_parser.parse)(code, { + sourceType: "module", + plugins + }); + } catch (e) { + console.error(e); + throw new Error(e?.message); + } +}; +const getCellGqlQuery = (ast) => { + let cellQuery = void 0; + (0, import_traverse.default)(ast, { + ExportNamedDeclaration({ node }) { + if (node.exportKind === "value" && import_core.types.isVariableDeclaration(node.declaration)) { + const exportedQueryNode = node.declaration.declarations.find((d) => { + return import_core.types.isIdentifier(d.id) && d.id.name === "QUERY" && import_core.types.isTaggedTemplateExpression(d.init); + }); + if (exportedQueryNode) { + const templateExpression = exportedQueryNode.init; + cellQuery = templateExpression.quasi.quasis[0].value.raw; + } + } + return; + } + }); + return cellQuery; +}; +const parseGqlQueryToAst = (gqlQuery) => { + const ast = (0, import_graphql.parse)(gqlQuery); + return parseDocumentAST(ast); +}; +const parseDocumentAST = (document) => { + const operations = []; + (0, import_graphql.visit)(document, { + OperationDefinition(node) { + const fields = []; + node.selectionSet.selections.forEach((field) => { + fields.push(getFields(field)); + }); + operations.push({ + operation: node.operation, + name: node.name?.value, + fields + }); + } + }); + return operations; +}; +const getFields = (field) => { + if (!field.selectionSet) { + return field.name.value; + } else { + const obj = { + [field.name.value]: [] + }; + const lookAtFieldNode = (node) => { + node.selectionSet?.selections.forEach((subField) => { + switch (subField.kind) { + case "Field": + obj[field.name.value].push(getFields(subField)); + break; + case "FragmentSpread": + break; + case "InlineFragment": + lookAtFieldNode(subField); + } + }); + }; + lookAtFieldNode(field); + return obj; + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + fileToAst, + findCells, + getCellGqlQuery, + getNamedExports, + hasDefaultExport, + isCellFile, + isFileInsideFolder, + parseDocumentAST, + parseGqlQueryToAst +}); diff --git a/packages/cli/dist/testLib/fetchFileFromTemplate.js b/packages/cli/dist/testLib/fetchFileFromTemplate.js new file mode 100644 index 0000000000..f44627b372 --- /dev/null +++ b/packages/cli/dist/testLib/fetchFileFromTemplate.js @@ -0,0 +1,29 @@ +"use strict"; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var fetchFileFromTemplate_exports = {}; +__export(fetchFileFromTemplate_exports, { + default: () => fetchFileFromTemplate +}); +module.exports = __toCommonJS(fetchFileFromTemplate_exports); +var import_fetch = require("@whatwg-node/fetch"); +async function fetchFileFromTemplate(tag, file) { + const URL = `https://raw.githubusercontent.com/redwoodjs/redwood/${tag}/packages/create-redwood-app/template/${file}`; + const res = await (0, import_fetch.fetch)(URL); + return res.text(); +} diff --git a/packages/cli/dist/testLib/getFilesWithPattern.js b/packages/cli/dist/testLib/getFilesWithPattern.js new file mode 100644 index 0000000000..af2c432f17 --- /dev/null +++ b/packages/cli/dist/testLib/getFilesWithPattern.js @@ -0,0 +1,51 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var getFilesWithPattern_exports = {}; +__export(getFilesWithPattern_exports, { + default: () => getFilesWithPattern_default +}); +module.exports = __toCommonJS(getFilesWithPattern_exports); +var import_ripgrep = require("@vscode/ripgrep"); +var import_execa = __toESM(require("execa")); +const getFilesWithPattern = ({ + pattern, + filesToSearch +}) => { + try { + const { stdout } = import_execa.default.sync(import_ripgrep.rgPath, [ + "--files-with-matches", + pattern, + ...filesToSearch + ]); + return stdout.toString().split("\n"); + } catch (e) { + return []; + } +}; +var getFilesWithPattern_default = getFilesWithPattern; diff --git a/packages/cli/dist/testLib/getRootPackageJSON.js b/packages/cli/dist/testLib/getRootPackageJSON.js new file mode 100644 index 0000000000..9ba73e7c00 --- /dev/null +++ b/packages/cli/dist/testLib/getRootPackageJSON.js @@ -0,0 +1,44 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var getRootPackageJSON_exports = {}; +__export(getRootPackageJSON_exports, { + default: () => getRootPackageJSON_default +}); +module.exports = __toCommonJS(getRootPackageJSON_exports); +var import_fs = __toESM(require("fs")); +var import_path = __toESM(require("path")); +var import_project_config = require("@redwoodjs/project-config"); +const getRootPackageJSON = () => { + const rootPackageJSONPath = import_path.default.join((0, import_project_config.getPaths)().base, "package.json"); + const rootPackageJSON = JSON.parse( + import_fs.default.readFileSync(rootPackageJSONPath, "utf8") + ); + return [rootPackageJSON, rootPackageJSONPath]; +}; +var getRootPackageJSON_default = getRootPackageJSON; diff --git a/packages/cli/dist/testLib/isTSProject.js b/packages/cli/dist/testLib/isTSProject.js new file mode 100644 index 0000000000..aee3ab77a4 --- /dev/null +++ b/packages/cli/dist/testLib/isTSProject.js @@ -0,0 +1,39 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var isTSProject_exports = {}; +__export(isTSProject_exports, { + default: () => isTSProject_default +}); +module.exports = __toCommonJS(isTSProject_exports); +var import_fast_glob = __toESM(require("fast-glob")); +var import_project_config = require("@redwoodjs/project-config"); +const isTSProject = import_fast_glob.default.sync(`${(0, import_project_config.getPaths)().base}/**/tsconfig.json`, { + ignore: ["**/node_modules/**"] +}).length > 0; +var isTSProject_default = isTSProject; diff --git a/packages/cli/dist/testLib/prettify.js b/packages/cli/dist/testLib/prettify.js new file mode 100644 index 0000000000..c75f51f49b --- /dev/null +++ b/packages/cli/dist/testLib/prettify.js @@ -0,0 +1,51 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var prettify_exports = {}; +__export(prettify_exports, { + default: () => prettify_default +}); +module.exports = __toCommonJS(prettify_exports); +var import_path = __toESM(require("path")); +var import_prettier = require("prettier"); +var import_project_config = require("@redwoodjs/project-config"); +const getPrettierConfig = () => { + try { + return require(import_path.default.join((0, import_project_config.getPaths)().base, "prettier.config.js")); + } catch (e) { + return void 0; + } +}; +const prettify = (code, options = {}) => (0, import_prettier.format)(code, { + singleQuote: true, + semi: false, + ...getPrettierConfig(), + parser: "babel", + ...options +}); +var prettify_default = prettify; diff --git a/packages/cli/dist/testLib/runTransform.js b/packages/cli/dist/testLib/runTransform.js new file mode 100644 index 0000000000..29fd075950 --- /dev/null +++ b/packages/cli/dist/testLib/runTransform.js @@ -0,0 +1,77 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var runTransform_exports = {}; +__export(runTransform_exports, { + default: () => runTransform_default, + runTransform: () => runTransform +}); +module.exports = __toCommonJS(runTransform_exports); +var jscodeshift = __toESM(require("jscodeshift/src/Runner")); +const defaultJscodeshiftOpts = { + verbose: 0, + dry: false, + print: false, + babel: true, + extensions: "js", + ignorePattern: "**/node_modules/**", + ignoreConfig: [], + runInBand: false, + silent: false, + parser: "babel", + parserConfig: {}, + failOnError: false, + stdin: false +}; +const runTransform = async ({ + transformPath, + targetPaths, + parser = "tsx", + options = {} +}) => { + try { + if (process.env.NODE_ENV === "test" && process.env.RWJS_CWD) { + process.chdir(process.env.RWJS_CWD); + } + await jscodeshift.run(transformPath, targetPaths, { + ...defaultJscodeshiftOpts, + parser, + babel: process.env.NODE_ENV === "test", + ...options + // Putting options here lets them override all the defaults. + }); + } catch (e) { + console.error("Transform Error", e.message); + throw new Error("Failed to invoke transform"); + } +}; +var runTransform_default = runTransform; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + runTransform +}); diff --git a/packages/cli/dist/testLib/ts2js.js b/packages/cli/dist/testLib/ts2js.js new file mode 100644 index 0000000000..56bcf042a2 --- /dev/null +++ b/packages/cli/dist/testLib/ts2js.js @@ -0,0 +1,57 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var ts2js_exports = {}; +__export(ts2js_exports, { + default: () => ts2js_default +}); +module.exports = __toCommonJS(ts2js_exports); +var import_core = require("@babel/core"); +var import_project_config = require("@redwoodjs/project-config"); +var import_prettify = __toESM(require("./prettify")); +const ts2js = (file) => { + const result = (0, import_core.transform)(file, { + cwd: (0, import_project_config.getPaths)().base, + configFile: false, + plugins: [ + [ + "@babel/plugin-transform-typescript", + { + isTSX: true, + allExtensions: true + } + ] + ], + retainLines: true + }); + if (result?.code) { + return (0, import_prettify.default)(result.code); + } + return null; +}; +var ts2js_default = ts2js;