create-m5kdev 0.21.5 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/_virtual/_rolldown/runtime.cjs +23 -0
  2. package/dist/src/constants.cjs +13 -0
  3. package/dist/src/{constants.mjs.map → constants.cjs.map} +1 -1
  4. package/dist/src/{constants.d.mts → constants.d.cts} +1 -1
  5. package/dist/src/create.cjs +36 -0
  6. package/dist/src/create.cjs.map +1 -0
  7. package/dist/src/{create.d.mts → create.d.cts} +2 -2
  8. package/dist/src/fs.cjs +56 -0
  9. package/dist/src/fs.cjs.map +1 -0
  10. package/dist/src/{fs.d.mts → fs.d.cts} +2 -2
  11. package/dist/src/{index.mjs → index.cjs} +2 -4
  12. package/dist/src/index.cjs.map +1 -0
  13. package/dist/src/paths.cjs +17 -0
  14. package/dist/src/paths.cjs.map +1 -0
  15. package/dist/src/{paths.d.mts → paths.d.cts} +1 -1
  16. package/dist/src/prompts.cjs +48 -0
  17. package/dist/src/prompts.cjs.map +1 -0
  18. package/dist/src/{prompts.d.mts → prompts.d.cts} +2 -2
  19. package/dist/src/{runCli.mjs → runCli.cjs} +6 -4
  20. package/dist/src/runCli.cjs.map +1 -0
  21. package/dist/src/{runCli.d.mts → runCli.d.cts} +1 -1
  22. package/dist/src/{strings.mjs → strings.cjs} +10 -4
  23. package/dist/src/{strings.mjs.map → strings.cjs.map} +1 -1
  24. package/dist/src/{strings.d.mts → strings.d.cts} +2 -2
  25. package/dist/src/types.cjs +0 -0
  26. package/dist/src/{types.d.mts → types.d.cts} +1 -1
  27. package/package.json +1 -1
  28. package/templates/minimal-app/apps/email/package.json.tpl +8 -1
  29. package/templates/minimal-app/apps/email/src/emails/accountDeletionEmail.tsx.tpl +25 -21
  30. package/templates/minimal-app/apps/email/src/emails/organizationInviteEmail.tsx.tpl +24 -22
  31. package/templates/minimal-app/apps/email/src/emails/passwordResetEmail.tsx.tpl +25 -16
  32. package/templates/minimal-app/apps/email/src/emails/verificationEmail.tsx.tpl +27 -17
  33. package/templates/minimal-app/apps/email/src/index.ts.tpl +8 -8
  34. package/templates/minimal-app/apps/email/src/resources.ts.tpl +36 -0
  35. package/templates/minimal-app/apps/server/src/app.ts.tpl +10 -1
  36. package/templates/minimal-app/apps/shared/src/modules/app/locale.constants.ts.tpl +7 -0
  37. package/templates/minimal-app/apps/webapp/src/Router.tsx.tpl +3 -0
  38. package/dist/src/constants.mjs +0 -10
  39. package/dist/src/create.mjs +0 -33
  40. package/dist/src/create.mjs.map +0 -1
  41. package/dist/src/fs.mjs +0 -50
  42. package/dist/src/fs.mjs.map +0 -1
  43. package/dist/src/index.mjs.map +0 -1
  44. package/dist/src/paths.mjs +0 -13
  45. package/dist/src/paths.mjs.map +0 -1
  46. package/dist/src/prompts.mjs +0 -45
  47. package/dist/src/prompts.mjs.map +0 -1
  48. package/dist/src/runCli.mjs.map +0 -1
  49. package/dist/src/types.mjs +0 -1
  50. package/templates/minimal-app/apps/email/src/components/BaseEmail.tsx.tpl +0 -117
  51. /package/dist/src/{index.d.mts → index.d.cts} +0 -0
@@ -0,0 +1,23 @@
1
+ //#region \0rolldown/runtime.js
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
+ key = keys[i];
11
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
+ get: ((k) => from[k]).bind(null, key),
13
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
+ });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
+ value: mod,
20
+ enumerable: true
21
+ }) : target, mod));
22
+ //#endregion
23
+ exports.__toESM = __toESM;
@@ -0,0 +1,13 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region src/constants.ts
3
+ const TEMPLATE_NAME = "minimal-app";
4
+ const DEFAULT_APP_NAME = "M5 Starter";
5
+ function getDefaultDescription(appName) {
6
+ return `${appName} is a blog publishing platform scaffolded with the m5kdev stack.`;
7
+ }
8
+ //#endregion
9
+ exports.DEFAULT_APP_NAME = DEFAULT_APP_NAME;
10
+ exports.TEMPLATE_NAME = TEMPLATE_NAME;
11
+ exports.getDefaultDescription = getDefaultDescription;
12
+
13
+ //# sourceMappingURL=constants.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.mjs","names":[],"sources":["../../src/constants.ts"],"sourcesContent":["export const TEMPLATE_NAME = \"minimal-app\";\r\n\r\nexport const DEFAULT_APP_NAME = \"M5 Starter\";\r\n\r\nexport function getDefaultDescription(appName: string): string {\r\n return `${appName} is a blog publishing platform scaffolded with the m5kdev stack.`;\r\n}\r\n"],"mappings":";AAAA,MAAa,gBAAgB;AAE7B,MAAa,mBAAmB;AAEhC,SAAgB,sBAAsB,SAAyB;AAC7D,QAAO,GAAG,QAAQ"}
1
+ {"version":3,"file":"constants.cjs","names":[],"sources":["../../src/constants.ts"],"sourcesContent":["export const TEMPLATE_NAME = \"minimal-app\";\r\n\r\nexport const DEFAULT_APP_NAME = \"M5 Starter\";\r\n\r\nexport function getDefaultDescription(appName: string): string {\r\n return `${appName} is a blog publishing platform scaffolded with the m5kdev stack.`;\r\n}\r\n"],"mappings":";;AAAA,MAAa,gBAAgB;AAE7B,MAAa,mBAAmB;AAEhC,SAAgB,sBAAsB,SAAyB;AAC7D,QAAO,GAAG,QAAQ"}
@@ -4,4 +4,4 @@ declare const DEFAULT_APP_NAME = "M5 Starter";
4
4
  declare function getDefaultDescription(appName: string): string;
5
5
  //#endregion
6
6
  export { DEFAULT_APP_NAME, TEMPLATE_NAME, getDefaultDescription };
7
- //# sourceMappingURL=constants.d.mts.map
7
+ //# sourceMappingURL=constants.d.cts.map
@@ -0,0 +1,36 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
3
+ const require_src_constants = require("./constants.cjs");
4
+ const require_src_strings = require("./strings.cjs");
5
+ const require_src_fs = require("./fs.cjs");
6
+ const require_src_paths = require("./paths.cjs");
7
+ const require_src_prompts = require("./prompts.cjs");
8
+ let node_path = require("node:path");
9
+ node_path = require_runtime.__toESM(node_path);
10
+ //#region src/create.ts
11
+ async function scaffoldProject(initialOptions) {
12
+ const options = await require_src_prompts.resolveCreateCommandOptions(initialOptions);
13
+ const appSlug = require_src_strings.slugifyAppName(options.appName);
14
+ const targetDirectory = node_path.default.resolve(process.cwd(), options.targetDirectory);
15
+ const templateDirectory = require_src_paths.getTemplateRoot();
16
+ const context = {
17
+ appName: options.appName,
18
+ appDescription: options.appDescription,
19
+ appSlug,
20
+ packageScope: require_src_strings.derivePackageScope(appSlug),
21
+ betterAuthSecret: require_src_strings.createBetterAuthSecret()
22
+ };
23
+ await require_src_fs.ensureDirectoryState(targetDirectory, options.force);
24
+ await require_src_fs.copyTemplateDirectory(templateDirectory, targetDirectory, context);
25
+ if (!options.skipGit) await require_src_fs.runGitInit(targetDirectory);
26
+ if (!options.skipInstall) await require_src_fs.runInstall(targetDirectory);
27
+ return {
28
+ context,
29
+ targetDirectory,
30
+ templateName: require_src_constants.TEMPLATE_NAME
31
+ };
32
+ }
33
+ //#endregion
34
+ exports.scaffoldProject = scaffoldProject;
35
+
36
+ //# sourceMappingURL=create.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.cjs","names":["resolveCreateCommandOptions","slugifyAppName","path","getTemplateRoot","derivePackageScope","createBetterAuthSecret","ensureDirectoryState","copyTemplateDirectory","runGitInit","runInstall","TEMPLATE_NAME"],"sources":["../../src/create.ts"],"sourcesContent":["import path from \"node:path\";\r\nimport { TEMPLATE_NAME } from \"./constants\";\r\nimport { copyTemplateDirectory, ensureDirectoryState, runGitInit, runInstall } from \"./fs\";\r\nimport { getTemplateRoot } from \"./paths\";\r\nimport { resolveCreateCommandOptions } from \"./prompts\";\r\nimport { createBetterAuthSecret, derivePackageScope, slugifyAppName } from \"./strings\";\r\nimport type { CreateCommandOptions, TemplateContext } from \"./types\";\r\n\r\nexport interface ScaffoldResult {\r\n context: TemplateContext;\r\n targetDirectory: string;\r\n templateName: string;\r\n}\r\n\r\nexport async function scaffoldProject(initialOptions: CreateCommandOptions): Promise<ScaffoldResult> {\r\n const options = await resolveCreateCommandOptions(initialOptions);\r\n const appSlug = slugifyAppName(options.appName!);\r\n const targetDirectory = path.resolve(process.cwd(), options.targetDirectory!);\r\n const templateDirectory = getTemplateRoot();\r\n\r\n const context: TemplateContext = {\r\n appName: options.appName!,\r\n appDescription: options.appDescription!,\r\n appSlug,\r\n packageScope: derivePackageScope(appSlug),\r\n betterAuthSecret: createBetterAuthSecret(),\r\n };\r\n\r\n await ensureDirectoryState(targetDirectory, options.force);\r\n await copyTemplateDirectory(templateDirectory, targetDirectory, context);\r\n\r\n if (!options.skipGit) {\r\n await runGitInit(targetDirectory);\r\n }\r\n\r\n if (!options.skipInstall) {\r\n await runInstall(targetDirectory);\r\n }\r\n\r\n return {\r\n context,\r\n targetDirectory,\r\n templateName: TEMPLATE_NAME,\r\n };\r\n}\r\n"],"mappings":";;;;;;;;;;AAcA,eAAsB,gBAAgB,gBAA+D;CACnG,MAAM,UAAU,MAAMA,oBAAAA,4BAA4B,eAAe;CACjE,MAAM,UAAUC,oBAAAA,eAAe,QAAQ,QAAS;CAChD,MAAM,kBAAkBC,UAAAA,QAAK,QAAQ,QAAQ,KAAK,EAAE,QAAQ,gBAAiB;CAC7E,MAAM,oBAAoBC,kBAAAA,iBAAiB;CAE3C,MAAM,UAA2B;EAC/B,SAAS,QAAQ;EACjB,gBAAgB,QAAQ;EACxB;EACA,cAAcC,oBAAAA,mBAAmB,QAAQ;EACzC,kBAAkBC,oBAAAA,wBAAwB;EAC3C;AAED,OAAMC,eAAAA,qBAAqB,iBAAiB,QAAQ,MAAM;AAC1D,OAAMC,eAAAA,sBAAsB,mBAAmB,iBAAiB,QAAQ;AAExE,KAAI,CAAC,QAAQ,QACX,OAAMC,eAAAA,WAAW,gBAAgB;AAGnC,KAAI,CAAC,QAAQ,YACX,OAAMC,eAAAA,WAAW,gBAAgB;AAGnC,QAAO;EACL;EACA;EACA,cAAcC,sBAAAA;EACf"}
@@ -1,4 +1,4 @@
1
- import { CreateCommandOptions, TemplateContext } from "./types.mjs";
1
+ import { CreateCommandOptions, TemplateContext } from "./types.cjs";
2
2
 
3
3
  //#region src/create.d.ts
4
4
  interface ScaffoldResult {
@@ -9,4 +9,4 @@ interface ScaffoldResult {
9
9
  declare function scaffoldProject(initialOptions: CreateCommandOptions): Promise<ScaffoldResult>;
10
10
  //#endregion
11
11
  export { ScaffoldResult, scaffoldProject };
12
- //# sourceMappingURL=create.d.mts.map
12
+ //# sourceMappingURL=create.d.cts.map
@@ -0,0 +1,56 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
3
+ const require_src_strings = require("./strings.cjs");
4
+ let node_path = require("node:path");
5
+ node_path = require_runtime.__toESM(node_path);
6
+ let node_child_process = require("node:child_process");
7
+ let node_fs_promises = require("node:fs/promises");
8
+ node_fs_promises = require_runtime.__toESM(node_fs_promises);
9
+ //#region src/fs.ts
10
+ const execFileAsync = (0, require("node:util").promisify)(node_child_process.execFile);
11
+ const TEMPLATE_LINE_ENDING = "\r\n";
12
+ async function ensureDirectoryState(targetDirectory, force) {
13
+ const stat = await node_fs_promises.default.stat(targetDirectory).catch(() => null);
14
+ if (!stat) {
15
+ await node_fs_promises.default.mkdir(targetDirectory, { recursive: true });
16
+ return;
17
+ }
18
+ if (!stat.isDirectory()) throw new Error(`Target path is not a directory: ${targetDirectory}`);
19
+ if ((await node_fs_promises.default.readdir(targetDirectory)).length > 0 && !force) throw new Error(`Target directory is not empty: ${targetDirectory}. Re-run with --force to overwrite.`);
20
+ }
21
+ async function copyTemplateDirectory(templateDirectory, targetDirectory, context) {
22
+ const entries = await node_fs_promises.default.readdir(templateDirectory, { withFileTypes: true });
23
+ for (const entry of entries) {
24
+ const sourcePath = node_path.default.join(templateDirectory, entry.name);
25
+ const targetName = entry.name.endsWith(".tpl") ? entry.name.slice(0, -4) : entry.name;
26
+ const targetPath = node_path.default.join(targetDirectory, targetName);
27
+ if (entry.isDirectory()) {
28
+ await node_fs_promises.default.mkdir(targetPath, { recursive: true });
29
+ await copyTemplateDirectory(sourcePath, targetPath, context);
30
+ continue;
31
+ }
32
+ if (!entry.isFile()) continue;
33
+ const rendered = require_src_strings.renderTemplate(await node_fs_promises.default.readFile(sourcePath, "utf8"), context).replace(/\r?\n/g, TEMPLATE_LINE_ENDING);
34
+ await node_fs_promises.default.mkdir(node_path.default.dirname(targetPath), { recursive: true });
35
+ await node_fs_promises.default.writeFile(targetPath, rendered, "utf8");
36
+ }
37
+ }
38
+ async function runInstall(targetDirectory) {
39
+ await execFileAsync("pnpm", ["install"], {
40
+ cwd: targetDirectory,
41
+ env: process.env
42
+ });
43
+ }
44
+ async function runGitInit(targetDirectory) {
45
+ await execFileAsync("git", ["init"], {
46
+ cwd: targetDirectory,
47
+ env: process.env
48
+ });
49
+ }
50
+ //#endregion
51
+ exports.copyTemplateDirectory = copyTemplateDirectory;
52
+ exports.ensureDirectoryState = ensureDirectoryState;
53
+ exports.runGitInit = runGitInit;
54
+ exports.runInstall = runInstall;
55
+
56
+ //# sourceMappingURL=fs.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fs.cjs","names":["execFile","fs","path","renderTemplate"],"sources":["../../src/fs.ts"],"sourcesContent":["import { execFile } from \"node:child_process\";\r\nimport fs from \"node:fs/promises\";\r\nimport path from \"node:path\";\r\nimport { promisify } from \"node:util\";\r\nimport type { TemplateContext } from \"./types\";\r\nimport { renderTemplate } from \"./strings\";\r\n\r\nconst execFileAsync = promisify(execFile);\r\nconst TEMPLATE_LINE_ENDING = \"\\r\\n\";\r\n\r\nexport async function ensureDirectoryState(\r\n targetDirectory: string,\r\n force: boolean\r\n): Promise<void> {\r\n const stat = await fs.stat(targetDirectory).catch(() => null);\r\n\r\n if (!stat) {\r\n await fs.mkdir(targetDirectory, { recursive: true });\r\n return;\r\n }\r\n\r\n if (!stat.isDirectory()) {\r\n throw new Error(`Target path is not a directory: ${targetDirectory}`);\r\n }\r\n\r\n const entries = await fs.readdir(targetDirectory);\r\n if (entries.length > 0 && !force) {\r\n throw new Error(\r\n `Target directory is not empty: ${targetDirectory}. Re-run with --force to overwrite.`\r\n );\r\n }\r\n}\r\n\r\nexport async function copyTemplateDirectory(\r\n templateDirectory: string,\r\n targetDirectory: string,\r\n context: TemplateContext\r\n): Promise<void> {\r\n const entries = await fs.readdir(templateDirectory, { withFileTypes: true });\r\n\r\n for (const entry of entries) {\r\n const sourcePath = path.join(templateDirectory, entry.name);\r\n const targetName = entry.name.endsWith(\".tpl\") ? entry.name.slice(0, -4) : entry.name;\r\n const targetPath = path.join(targetDirectory, targetName);\r\n\r\n if (entry.isDirectory()) {\r\n await fs.mkdir(targetPath, { recursive: true });\r\n await copyTemplateDirectory(sourcePath, targetPath, context);\r\n continue;\r\n }\r\n\r\n if (!entry.isFile()) {\r\n continue;\r\n }\r\n\r\n const content = await fs.readFile(sourcePath, \"utf8\");\r\n const rendered = renderTemplate(content, context).replace(/\\r?\\n/g, TEMPLATE_LINE_ENDING);\r\n await fs.mkdir(path.dirname(targetPath), { recursive: true });\r\n await fs.writeFile(targetPath, rendered, \"utf8\");\r\n }\r\n}\r\n\r\nexport async function runInstall(targetDirectory: string): Promise<void> {\r\n await execFileAsync(\"pnpm\", [\"install\"], {\r\n cwd: targetDirectory,\r\n env: process.env,\r\n });\r\n}\r\n\r\nexport async function runGitInit(targetDirectory: string): Promise<void> {\r\n await execFileAsync(\"git\", [\"init\"], {\r\n cwd: targetDirectory,\r\n env: process.env,\r\n });\r\n}\r\n"],"mappings":";;;;;;;;;AAOA,MAAM,iBAAA,wBAAA,WAA0BA,mBAAAA,SAAS;AACzC,MAAM,uBAAuB;AAE7B,eAAsB,qBACpB,iBACA,OACe;CACf,MAAM,OAAO,MAAMC,iBAAAA,QAAG,KAAK,gBAAgB,CAAC,YAAY,KAAK;AAE7D,KAAI,CAAC,MAAM;AACT,QAAMA,iBAAAA,QAAG,MAAM,iBAAiB,EAAE,WAAW,MAAM,CAAC;AACpD;;AAGF,KAAI,CAAC,KAAK,aAAa,CACrB,OAAM,IAAI,MAAM,mCAAmC,kBAAkB;AAIvE,MADgB,MAAMA,iBAAAA,QAAG,QAAQ,gBAAgB,EACrC,SAAS,KAAK,CAAC,MACzB,OAAM,IAAI,MACR,kCAAkC,gBAAgB,qCACnD;;AAIL,eAAsB,sBACpB,mBACA,iBACA,SACe;CACf,MAAM,UAAU,MAAMA,iBAAAA,QAAG,QAAQ,mBAAmB,EAAE,eAAe,MAAM,CAAC;AAE5E,MAAK,MAAM,SAAS,SAAS;EAC3B,MAAM,aAAaC,UAAAA,QAAK,KAAK,mBAAmB,MAAM,KAAK;EAC3D,MAAM,aAAa,MAAM,KAAK,SAAS,OAAO,GAAG,MAAM,KAAK,MAAM,GAAG,GAAG,GAAG,MAAM;EACjF,MAAM,aAAaA,UAAAA,QAAK,KAAK,iBAAiB,WAAW;AAEzD,MAAI,MAAM,aAAa,EAAE;AACvB,SAAMD,iBAAAA,QAAG,MAAM,YAAY,EAAE,WAAW,MAAM,CAAC;AAC/C,SAAM,sBAAsB,YAAY,YAAY,QAAQ;AAC5D;;AAGF,MAAI,CAAC,MAAM,QAAQ,CACjB;EAIF,MAAM,WAAWE,oBAAAA,eADD,MAAMF,iBAAAA,QAAG,SAAS,YAAY,OAAO,EACZ,QAAQ,CAAC,QAAQ,UAAU,qBAAqB;AACzF,QAAMA,iBAAAA,QAAG,MAAMC,UAAAA,QAAK,QAAQ,WAAW,EAAE,EAAE,WAAW,MAAM,CAAC;AAC7D,QAAMD,iBAAAA,QAAG,UAAU,YAAY,UAAU,OAAO;;;AAIpD,eAAsB,WAAW,iBAAwC;AACvE,OAAM,cAAc,QAAQ,CAAC,UAAU,EAAE;EACvC,KAAK;EACL,KAAK,QAAQ;EACd,CAAC;;AAGJ,eAAsB,WAAW,iBAAwC;AACvE,OAAM,cAAc,OAAO,CAAC,OAAO,EAAE;EACnC,KAAK;EACL,KAAK,QAAQ;EACd,CAAC"}
@@ -1,4 +1,4 @@
1
- import { TemplateContext } from "./types.mjs";
1
+ import { TemplateContext } from "./types.cjs";
2
2
 
3
3
  //#region src/fs.d.ts
4
4
  declare function ensureDirectoryState(targetDirectory: string, force: boolean): Promise<void>;
@@ -7,4 +7,4 @@ declare function runInstall(targetDirectory: string): Promise<void>;
7
7
  declare function runGitInit(targetDirectory: string): Promise<void>;
8
8
  //#endregion
9
9
  export { copyTemplateDirectory, ensureDirectoryState, runGitInit, runInstall };
10
- //# sourceMappingURL=fs.d.mts.map
10
+ //# sourceMappingURL=fs.d.cts.map
@@ -1,12 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import { runCli } from "./runCli.mjs";
3
2
  //#region src/index.ts
4
- runCli(process.argv.slice(2)).catch((error) => {
3
+ require("./runCli.cjs").runCli(process.argv.slice(2)).catch((error) => {
5
4
  const message = error instanceof Error ? error.message : String(error);
6
5
  console.error(message);
7
6
  process.exitCode = 1;
8
7
  });
9
8
  //#endregion
10
- export {};
11
9
 
12
- //# sourceMappingURL=index.mjs.map
10
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["runCli"],"sources":["../../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\r\n\r\nimport { runCli } from \"./runCli\";\r\n\r\nrunCli(process.argv.slice(2)).catch((error: unknown) => {\r\n const message = error instanceof Error ? error.message : String(error);\r\n console.error(message);\r\n process.exitCode = 1;\r\n});\r\n"],"mappings":";;wBAIAA,OAAO,QAAQ,KAAK,MAAM,EAAE,CAAC,CAAC,OAAO,UAAmB;CACtD,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;AACtE,SAAQ,MAAM,QAAQ;AACtB,SAAQ,WAAW;EACnB"}
@@ -0,0 +1,17 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
3
+ const require_src_constants = require("./constants.cjs");
4
+ let node_path = require("node:path");
5
+ node_path = require_runtime.__toESM(node_path);
6
+ let node_fs = require("node:fs");
7
+ node_fs = require_runtime.__toESM(node_fs);
8
+ //#region src/paths.ts
9
+ function getTemplateRoot() {
10
+ const sourcePath = node_path.default.resolve(__dirname, "../templates", require_src_constants.TEMPLATE_NAME);
11
+ if (node_fs.default.existsSync(sourcePath)) return sourcePath;
12
+ return node_path.default.resolve(__dirname, "../../templates", require_src_constants.TEMPLATE_NAME);
13
+ }
14
+ //#endregion
15
+ exports.getTemplateRoot = getTemplateRoot;
16
+
17
+ //# sourceMappingURL=paths.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.cjs","names":["path","TEMPLATE_NAME","fs"],"sources":["../../src/paths.ts"],"sourcesContent":["import fs from \"node:fs\";\r\nimport path from \"node:path\";\r\nimport { TEMPLATE_NAME } from \"./constants\";\r\n\r\nexport function getTemplateRoot(): string {\r\n const sourcePath = path.resolve(__dirname, \"../templates\", TEMPLATE_NAME);\r\n if (fs.existsSync(sourcePath)) {\r\n return sourcePath;\r\n }\r\n\r\n return path.resolve(__dirname, \"../../templates\", TEMPLATE_NAME);\r\n}\r\n"],"mappings":";;;;;;;;AAIA,SAAgB,kBAA0B;CACxC,MAAM,aAAaA,UAAAA,QAAK,QAAQ,WAAW,gBAAgBC,sBAAAA,cAAc;AACzE,KAAIC,QAAAA,QAAG,WAAW,WAAW,CAC3B,QAAO;AAGT,QAAOF,UAAAA,QAAK,QAAQ,WAAW,mBAAmBC,sBAAAA,cAAc"}
@@ -2,4 +2,4 @@
2
2
  declare function getTemplateRoot(): string;
3
3
  //#endregion
4
4
  export { getTemplateRoot };
5
- //# sourceMappingURL=paths.d.mts.map
5
+ //# sourceMappingURL=paths.d.cts.map
@@ -0,0 +1,48 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
3
+ const require_src_constants = require("./constants.cjs");
4
+ const require_src_strings = require("./strings.cjs");
5
+ let node_readline_promises = require("node:readline/promises");
6
+ node_readline_promises = require_runtime.__toESM(node_readline_promises);
7
+ let node_process = require("node:process");
8
+ //#region src/prompts.ts
9
+ function requireInteractive(yes) {
10
+ if (!yes && !process.stdin.isTTY) throw new Error("Missing required values in a non-interactive shell. Pass --yes or provide flags.");
11
+ }
12
+ async function promptValue(question, fallback) {
13
+ const rl = node_readline_promises.default.createInterface({
14
+ input: node_process.stdin,
15
+ output: node_process.stdout
16
+ });
17
+ try {
18
+ const suffix = fallback ? ` (${fallback})` : "";
19
+ return (await rl.question(`${question}${suffix}: `)).trim() || fallback || "";
20
+ } finally {
21
+ rl.close();
22
+ }
23
+ }
24
+ async function resolveCreateCommandOptions(options) {
25
+ const resolved = { ...options };
26
+ if (!resolved.appName) {
27
+ requireInteractive(resolved.yes);
28
+ resolved.appName = resolved.yes ? resolved.targetDirectory ? require_src_strings.toDisplayName(pathBaseName(resolved.targetDirectory)) : require_src_constants.DEFAULT_APP_NAME : await promptValue("App name", resolved.targetDirectory ? pathBaseName(resolved.targetDirectory) : require_src_constants.DEFAULT_APP_NAME);
29
+ }
30
+ resolved.appName = require_src_strings.toDisplayName(resolved.appName);
31
+ if (!resolved.targetDirectory) {
32
+ requireInteractive(resolved.yes);
33
+ resolved.targetDirectory = resolved.yes ? require_src_strings.slugifyAppName(resolved.appName) : await promptValue("Target directory", require_src_strings.slugifyAppName(resolved.appName));
34
+ }
35
+ if (!resolved.appDescription) {
36
+ requireInteractive(resolved.yes);
37
+ const fallback = require_src_constants.getDefaultDescription(resolved.appName);
38
+ resolved.appDescription = resolved.yes ? fallback : await promptValue("App description", fallback);
39
+ }
40
+ return resolved;
41
+ }
42
+ function pathBaseName(value) {
43
+ return value.replace(/[\\/]+$/g, "").split(/[\\/]/).filter(Boolean).at(-1) || "M5 Starter";
44
+ }
45
+ //#endregion
46
+ exports.resolveCreateCommandOptions = resolveCreateCommandOptions;
47
+
48
+ //# sourceMappingURL=prompts.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompts.cjs","names":["readline","toDisplayName","DEFAULT_APP_NAME","slugifyAppName","getDefaultDescription"],"sources":["../../src/prompts.ts"],"sourcesContent":["import readline from \"node:readline/promises\";\r\nimport { stdin as input, stdout as output } from \"node:process\";\r\nimport { DEFAULT_APP_NAME, getDefaultDescription } from \"./constants\";\r\nimport { slugifyAppName, toDisplayName } from \"./strings\";\r\nimport type { CreateCommandOptions } from \"./types\";\r\n\r\nfunction requireInteractive(yes: boolean): void {\r\n if (!yes && !process.stdin.isTTY) {\r\n throw new Error(\"Missing required values in a non-interactive shell. Pass --yes or provide flags.\");\r\n }\r\n}\r\n\r\nasync function promptValue(question: string, fallback?: string): Promise<string> {\r\n const rl = readline.createInterface({ input, output });\r\n\r\n try {\r\n const suffix = fallback ? ` (${fallback})` : \"\";\r\n const response = await rl.question(`${question}${suffix}: `);\r\n return response.trim() || fallback || \"\";\r\n } finally {\r\n rl.close();\r\n }\r\n}\r\n\r\nexport async function resolveCreateCommandOptions(\r\n options: CreateCommandOptions\r\n): Promise<CreateCommandOptions> {\r\n const resolved = { ...options };\r\n\r\n if (!resolved.appName) {\r\n requireInteractive(resolved.yes);\r\n resolved.appName = resolved.yes\r\n ? resolved.targetDirectory\r\n ? toDisplayName(pathBaseName(resolved.targetDirectory))\r\n : DEFAULT_APP_NAME\r\n : await promptValue(\"App name\", resolved.targetDirectory ? pathBaseName(resolved.targetDirectory) : DEFAULT_APP_NAME);\r\n }\r\n\r\n resolved.appName = toDisplayName(resolved.appName);\r\n\r\n if (!resolved.targetDirectory) {\r\n requireInteractive(resolved.yes);\r\n resolved.targetDirectory = resolved.yes\r\n ? slugifyAppName(resolved.appName)\r\n : await promptValue(\"Target directory\", slugifyAppName(resolved.appName));\r\n }\r\n\r\n if (!resolved.appDescription) {\r\n requireInteractive(resolved.yes);\r\n const fallback = getDefaultDescription(resolved.appName);\r\n resolved.appDescription = resolved.yes\r\n ? fallback\r\n : await promptValue(\"App description\", fallback);\r\n }\r\n\r\n return resolved;\r\n}\r\n\r\nfunction pathBaseName(value: string): string {\r\n const normalized = value.replace(/[\\\\/]+$/g, \"\");\r\n const segments = normalized.split(/[\\\\/]/).filter(Boolean);\r\n return segments.at(-1) || DEFAULT_APP_NAME;\r\n}\r\n"],"mappings":";;;;;;;;AAMA,SAAS,mBAAmB,KAAoB;AAC9C,KAAI,CAAC,OAAO,CAAC,QAAQ,MAAM,MACzB,OAAM,IAAI,MAAM,mFAAmF;;AAIvG,eAAe,YAAY,UAAkB,UAAoC;CAC/E,MAAM,KAAKA,uBAAAA,QAAS,gBAAgB;EAAE,OAAA,aAAA;EAAO,QAAA,aAAA;EAAQ,CAAC;AAEtD,KAAI;EACF,MAAM,SAAS,WAAW,KAAK,SAAS,KAAK;AAE7C,UADiB,MAAM,GAAG,SAAS,GAAG,WAAW,OAAO,IAAI,EAC5C,MAAM,IAAI,YAAY;WAC9B;AACR,KAAG,OAAO;;;AAId,eAAsB,4BACpB,SAC+B;CAC/B,MAAM,WAAW,EAAE,GAAG,SAAS;AAE/B,KAAI,CAAC,SAAS,SAAS;AACrB,qBAAmB,SAAS,IAAI;AAChC,WAAS,UAAU,SAAS,MACxB,SAAS,kBACPC,oBAAAA,cAAc,aAAa,SAAS,gBAAgB,CAAC,GACrDC,sBAAAA,mBACF,MAAM,YAAY,YAAY,SAAS,kBAAkB,aAAa,SAAS,gBAAgB,GAAGA,sBAAAA,iBAAiB;;AAGzH,UAAS,UAAUD,oBAAAA,cAAc,SAAS,QAAQ;AAElD,KAAI,CAAC,SAAS,iBAAiB;AAC7B,qBAAmB,SAAS,IAAI;AAChC,WAAS,kBAAkB,SAAS,MAChCE,oBAAAA,eAAe,SAAS,QAAQ,GAChC,MAAM,YAAY,oBAAoBA,oBAAAA,eAAe,SAAS,QAAQ,CAAC;;AAG7E,KAAI,CAAC,SAAS,gBAAgB;AAC5B,qBAAmB,SAAS,IAAI;EAChC,MAAM,WAAWC,sBAAAA,sBAAsB,SAAS,QAAQ;AACxD,WAAS,iBAAiB,SAAS,MAC/B,WACA,MAAM,YAAY,mBAAmB,SAAS;;AAGpD,QAAO;;AAGT,SAAS,aAAa,OAAuB;AAG3C,QAFmB,MAAM,QAAQ,YAAY,GAAG,CACpB,MAAM,QAAQ,CAAC,OAAO,QAAQ,CAC1C,GAAG,GAAG,IAAA"}
@@ -1,7 +1,7 @@
1
- import { CreateCommandOptions } from "./types.mjs";
1
+ import { CreateCommandOptions } from "./types.cjs";
2
2
 
3
3
  //#region src/prompts.d.ts
4
4
  declare function resolveCreateCommandOptions(options: CreateCommandOptions): Promise<CreateCommandOptions>;
5
5
  //#endregion
6
6
  export { resolveCreateCommandOptions };
7
- //# sourceMappingURL=prompts.d.mts.map
7
+ //# sourceMappingURL=prompts.d.cts.map
@@ -1,4 +1,5 @@
1
- import { scaffoldProject } from "./create.mjs";
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_src_create = require("./create.cjs");
2
3
  //#region src/runCli.ts
3
4
  async function runCli(argv) {
4
5
  const parsed = parseCli(argv);
@@ -8,7 +9,7 @@ async function runCli(argv) {
8
9
  }
9
10
  if (parsed.command !== "create") throw new Error(`Unknown command: ${parsed.command}`);
10
11
  const options = toCreateCommandOptions(parsed);
11
- const result = await scaffoldProject(options);
12
+ const result = await require_src_create.scaffoldProject(options);
12
13
  console.log("");
13
14
  console.log(`Scaffolded ${result.context.appName} in ${result.targetDirectory}`);
14
15
  console.log("");
@@ -96,6 +97,7 @@ function printHelp() {
96
97
  console.log(" --skip-git Skip git init");
97
98
  }
98
99
  //#endregion
99
- export { parseCli, runCli };
100
+ exports.parseCli = parseCli;
101
+ exports.runCli = runCli;
100
102
 
101
- //# sourceMappingURL=runCli.mjs.map
103
+ //# sourceMappingURL=runCli.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runCli.cjs","names":["scaffoldProject"],"sources":["../../src/runCli.ts"],"sourcesContent":["import { scaffoldProject } from \"./create\";\r\nimport type { CreateCommandOptions } from \"./types\";\r\n\r\ninterface ParsedCli {\r\n command?: string;\r\n directory?: string;\r\n help?: boolean;\r\n options: Record<string, string | boolean>;\r\n}\r\n\r\nexport async function runCli(argv: readonly string[]): Promise<void> {\r\n const parsed = parseCli(argv);\r\n\r\n if (\r\n !parsed.command ||\r\n parsed.help ||\r\n parsed.command === \"--help\" ||\r\n parsed.command === \"-h\" ||\r\n parsed.command === \"help\"\r\n ) {\r\n printHelp();\r\n return;\r\n }\r\n\r\n if (parsed.command !== \"create\") {\r\n throw new Error(`Unknown command: ${parsed.command}`);\r\n }\r\n\r\n const options = toCreateCommandOptions(parsed);\r\n const result = await scaffoldProject(options);\r\n\r\n console.log(\"\");\r\n console.log(`Scaffolded ${result.context.appName} in ${result.targetDirectory}`);\r\n console.log(\"\");\r\n console.log(\"Next steps:\");\r\n console.log(` cd ${result.targetDirectory}`);\r\n if (options.skipInstall) {\r\n console.log(\" pnpm install\");\r\n }\r\n console.log(\" pnpm --filter ./apps/server sync\");\r\n console.log(\" pnpm dev\");\r\n}\r\n\r\nexport function parseCli(argv: readonly string[]): ParsedCli {\r\n const normalizedArgv = normalizeCliArgv(argv);\r\n const [command, ...rest] = normalizedArgv;\r\n const options: Record<string, string | boolean> = {};\r\n let directory: string | undefined;\r\n let help = false;\r\n\r\n for (let index = 0; index < rest.length; index += 1) {\r\n const token = rest[index];\r\n\r\n if (token === \"--help\" || token === \"-h\") {\r\n help = true;\r\n continue;\r\n }\r\n\r\n if (!token.startsWith(\"--\")) {\r\n directory ??= token;\r\n continue;\r\n }\r\n\r\n const [rawKey, inlineValue] = token.slice(2).split(\"=\", 2);\r\n\r\n if (isBooleanFlag(rawKey)) {\r\n options[rawKey] = true;\r\n continue;\r\n }\r\n\r\n const nextValue = inlineValue ?? rest[index + 1];\r\n if (!nextValue || nextValue.startsWith(\"--\")) {\r\n throw new Error(`Missing value for --${rawKey}`);\r\n }\r\n\r\n options[rawKey] = nextValue;\r\n if (inlineValue === undefined) {\r\n index += 1;\r\n }\r\n }\r\n\r\n return { command, directory, help, options };\r\n}\r\n\r\nfunction normalizeCliArgv(argv: readonly string[]): readonly string[] {\r\n const [firstToken] = argv;\r\n\r\n if (!firstToken || firstToken === \"create\" || firstToken === \"--help\" || firstToken === \"-h\") {\r\n return argv;\r\n }\r\n\r\n if (firstToken === \"help\") {\r\n return [\"--help\"];\r\n }\r\n\r\n return [\"create\", ...argv];\r\n}\r\n\r\nfunction isBooleanFlag(value: string): boolean {\r\n return [\"yes\", \"force\", \"skip-install\", \"skip-git\"].includes(value);\r\n}\r\n\r\nfunction toCreateCommandOptions(parsed: ParsedCli): CreateCommandOptions {\r\n return {\r\n targetDirectory: parsed.directory,\r\n appName: getStringOption(parsed.options, \"name\"),\r\n appDescription: getStringOption(parsed.options, \"description\"),\r\n yes: Boolean(parsed.options.yes),\r\n force: Boolean(parsed.options.force),\r\n skipInstall: Boolean(parsed.options[\"skip-install\"]),\r\n skipGit: Boolean(parsed.options[\"skip-git\"]),\r\n };\r\n}\r\n\r\nfunction getStringOption(\r\n options: Record<string, string | boolean>,\r\n key: string\r\n): string | undefined {\r\n const value = options[key];\r\n return typeof value === \"string\" ? value : undefined;\r\n}\r\n\r\nfunction printHelp(): void {\r\n console.log(\"m5kdev\");\r\n console.log(\"\");\r\n console.log(\"Usage:\");\r\n console.log(\" pnpm create m5kdev [directory] [options]\");\r\n console.log(\" m5kdev [directory] [options]\");\r\n console.log(\" m5kdev create [directory] [options]\");\r\n console.log(\"\");\r\n console.log(\"Options:\");\r\n console.log(\" --name <value> Set the app name\");\r\n console.log(\" --description <value> Set the app description\");\r\n console.log(\" --yes Accept defaults for missing prompts\");\r\n console.log(\" --force Allow writing into a non-empty directory\");\r\n console.log(\" --skip-install Skip pnpm install\");\r\n console.log(\" --skip-git Skip git init\");\r\n}\r\n"],"mappings":";;;AAUA,eAAsB,OAAO,MAAwC;CACnE,MAAM,SAAS,SAAS,KAAK;AAE7B,KACE,CAAC,OAAO,WACR,OAAO,QACP,OAAO,YAAY,YACnB,OAAO,YAAY,QACnB,OAAO,YAAY,QACnB;AACA,aAAW;AACX;;AAGF,KAAI,OAAO,YAAY,SACrB,OAAM,IAAI,MAAM,oBAAoB,OAAO,UAAU;CAGvD,MAAM,UAAU,uBAAuB,OAAO;CAC9C,MAAM,SAAS,MAAMA,mBAAAA,gBAAgB,QAAQ;AAE7C,SAAQ,IAAI,GAAG;AACf,SAAQ,IAAI,cAAc,OAAO,QAAQ,QAAQ,MAAM,OAAO,kBAAkB;AAChF,SAAQ,IAAI,GAAG;AACf,SAAQ,IAAI,cAAc;AAC1B,SAAQ,IAAI,QAAQ,OAAO,kBAAkB;AAC7C,KAAI,QAAQ,YACV,SAAQ,IAAI,iBAAiB;AAE/B,SAAQ,IAAI,qCAAqC;AACjD,SAAQ,IAAI,aAAa;;AAG3B,SAAgB,SAAS,MAAoC;CAE3D,MAAM,CAAC,SAAS,GAAG,QADI,iBAAiB,KAAK;CAE7C,MAAM,UAA4C,EAAE;CACpD,IAAI;CACJ,IAAI,OAAO;AAEX,MAAK,IAAI,QAAQ,GAAG,QAAQ,KAAK,QAAQ,SAAS,GAAG;EACnD,MAAM,QAAQ,KAAK;AAEnB,MAAI,UAAU,YAAY,UAAU,MAAM;AACxC,UAAO;AACP;;AAGF,MAAI,CAAC,MAAM,WAAW,KAAK,EAAE;AAC3B,iBAAc;AACd;;EAGF,MAAM,CAAC,QAAQ,eAAe,MAAM,MAAM,EAAE,CAAC,MAAM,KAAK,EAAE;AAE1D,MAAI,cAAc,OAAO,EAAE;AACzB,WAAQ,UAAU;AAClB;;EAGF,MAAM,YAAY,eAAe,KAAK,QAAQ;AAC9C,MAAI,CAAC,aAAa,UAAU,WAAW,KAAK,CAC1C,OAAM,IAAI,MAAM,uBAAuB,SAAS;AAGlD,UAAQ,UAAU;AAClB,MAAI,gBAAgB,KAAA,EAClB,UAAS;;AAIb,QAAO;EAAE;EAAS;EAAW;EAAM;EAAS;;AAG9C,SAAS,iBAAiB,MAA4C;CACpE,MAAM,CAAC,cAAc;AAErB,KAAI,CAAC,cAAc,eAAe,YAAY,eAAe,YAAY,eAAe,KACtF,QAAO;AAGT,KAAI,eAAe,OACjB,QAAO,CAAC,SAAS;AAGnB,QAAO,CAAC,UAAU,GAAG,KAAK;;AAG5B,SAAS,cAAc,OAAwB;AAC7C,QAAO;EAAC;EAAO;EAAS;EAAgB;EAAW,CAAC,SAAS,MAAM;;AAGrE,SAAS,uBAAuB,QAAyC;AACvE,QAAO;EACL,iBAAiB,OAAO;EACxB,SAAS,gBAAgB,OAAO,SAAS,OAAO;EAChD,gBAAgB,gBAAgB,OAAO,SAAS,cAAc;EAC9D,KAAK,QAAQ,OAAO,QAAQ,IAAI;EAChC,OAAO,QAAQ,OAAO,QAAQ,MAAM;EACpC,aAAa,QAAQ,OAAO,QAAQ,gBAAgB;EACpD,SAAS,QAAQ,OAAO,QAAQ,YAAY;EAC7C;;AAGH,SAAS,gBACP,SACA,KACoB;CACpB,MAAM,QAAQ,QAAQ;AACtB,QAAO,OAAO,UAAU,WAAW,QAAQ,KAAA;;AAG7C,SAAS,YAAkB;AACzB,SAAQ,IAAI,SAAS;AACrB,SAAQ,IAAI,GAAG;AACf,SAAQ,IAAI,SAAS;AACrB,SAAQ,IAAI,6CAA6C;AACzD,SAAQ,IAAI,iCAAiC;AAC7C,SAAQ,IAAI,wCAAwC;AACpD,SAAQ,IAAI,GAAG;AACf,SAAQ,IAAI,WAAW;AACvB,SAAQ,IAAI,8CAA8C;AAC1D,SAAQ,IAAI,qDAAqD;AACjE,SAAQ,IAAI,iEAAiE;AAC7E,SAAQ,IAAI,sEAAsE;AAClF,SAAQ,IAAI,+CAA+C;AAC3D,SAAQ,IAAI,2CAA2C"}
@@ -9,4 +9,4 @@ declare function runCli(argv: readonly string[]): Promise<void>;
9
9
  declare function parseCli(argv: readonly string[]): ParsedCli;
10
10
  //#endregion
11
11
  export { parseCli, runCli };
12
- //# sourceMappingURL=runCli.d.mts.map
12
+ //# sourceMappingURL=runCli.d.cts.map
@@ -1,4 +1,6 @@
1
- import { randomBytes } from "node:crypto";
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ require("../_virtual/_rolldown/runtime.cjs");
3
+ let node_crypto = require("node:crypto");
2
4
  //#region src/strings.ts
3
5
  function slugifyAppName(value) {
4
6
  const slug = value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+/g, "").replace(/-+$/g, "").replace(/-{2,}/g, "-");
@@ -14,7 +16,7 @@ function toDisplayName(value) {
14
16
  return normalized;
15
17
  }
16
18
  function createBetterAuthSecret() {
17
- return randomBytes(24).toString("base64url");
19
+ return (0, node_crypto.randomBytes)(24).toString("base64url");
18
20
  }
19
21
  function renderTemplate(content, context) {
20
22
  const replacements = {
@@ -29,6 +31,10 @@ function renderTemplate(content, context) {
29
31
  });
30
32
  }
31
33
  //#endregion
32
- export { createBetterAuthSecret, derivePackageScope, renderTemplate, slugifyAppName, toDisplayName };
34
+ exports.createBetterAuthSecret = createBetterAuthSecret;
35
+ exports.derivePackageScope = derivePackageScope;
36
+ exports.renderTemplate = renderTemplate;
37
+ exports.slugifyAppName = slugifyAppName;
38
+ exports.toDisplayName = toDisplayName;
33
39
 
34
- //# sourceMappingURL=strings.mjs.map
40
+ //# sourceMappingURL=strings.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"strings.mjs","names":[],"sources":["../../src/strings.ts"],"sourcesContent":["import { randomBytes } from \"node:crypto\";\r\nimport type { TemplateContext } from \"./types\";\r\n\r\nexport function slugifyAppName(value: string): string {\r\n const slug = value\r\n .trim()\r\n .toLowerCase()\r\n .replace(/[^a-z0-9]+/g, \"-\")\r\n .replace(/^-+/g, \"\")\r\n .replace(/-+$/g, \"\")\r\n .replace(/-{2,}/g, \"-\");\r\n\r\n if (!slug) {\r\n throw new Error(\"Unable to derive an app slug from the provided name.\");\r\n }\r\n\r\n return slug;\r\n}\r\n\r\nexport function derivePackageScope(appSlug: string): string {\r\n return `@${appSlug}`;\r\n}\r\n\r\nexport function toDisplayName(value: string): string {\r\n const normalized = value.trim();\r\n if (!normalized) {\r\n throw new Error(\"App name cannot be empty.\");\r\n }\r\n\r\n return normalized;\r\n}\r\n\r\nexport function createBetterAuthSecret(): string {\r\n return randomBytes(24).toString(\"base64url\");\r\n}\r\n\r\nexport function renderTemplate(content: string, context: TemplateContext): string {\r\n const replacements: Record<string, string> = {\r\n APP_NAME: context.appName,\r\n APP_DESCRIPTION: context.appDescription,\r\n APP_SLUG: context.appSlug,\r\n PACKAGE_SCOPE: context.packageScope,\r\n BETTER_AUTH_SECRET: context.betterAuthSecret,\r\n };\r\n\r\n return content.replace(/\\{\\{([A-Z_]+)\\}\\}/g, (match, token: string) => {\r\n const replacement = replacements[token];\r\n return replacement ?? match;\r\n });\r\n}\r\n"],"mappings":";;AAGA,SAAgB,eAAe,OAAuB;CACpD,MAAM,OAAO,MACV,MAAM,CACN,aAAa,CACb,QAAQ,eAAe,IAAI,CAC3B,QAAQ,QAAQ,GAAG,CACnB,QAAQ,QAAQ,GAAG,CACnB,QAAQ,UAAU,IAAI;AAEzB,KAAI,CAAC,KACH,OAAM,IAAI,MAAM,uDAAuD;AAGzE,QAAO;;AAGT,SAAgB,mBAAmB,SAAyB;AAC1D,QAAO,IAAI;;AAGb,SAAgB,cAAc,OAAuB;CACnD,MAAM,aAAa,MAAM,MAAM;AAC/B,KAAI,CAAC,WACH,OAAM,IAAI,MAAM,4BAA4B;AAG9C,QAAO;;AAGT,SAAgB,yBAAiC;AAC/C,QAAO,YAAY,GAAG,CAAC,SAAS,YAAY;;AAG9C,SAAgB,eAAe,SAAiB,SAAkC;CAChF,MAAM,eAAuC;EAC3C,UAAU,QAAQ;EAClB,iBAAiB,QAAQ;EACzB,UAAU,QAAQ;EAClB,eAAe,QAAQ;EACvB,oBAAoB,QAAQ;EAC7B;AAED,QAAO,QAAQ,QAAQ,uBAAuB,OAAO,UAAkB;AAErE,SADoB,aAAa,UACX;GACtB"}
1
+ {"version":3,"file":"strings.cjs","names":[],"sources":["../../src/strings.ts"],"sourcesContent":["import { randomBytes } from \"node:crypto\";\r\nimport type { TemplateContext } from \"./types\";\r\n\r\nexport function slugifyAppName(value: string): string {\r\n const slug = value\r\n .trim()\r\n .toLowerCase()\r\n .replace(/[^a-z0-9]+/g, \"-\")\r\n .replace(/^-+/g, \"\")\r\n .replace(/-+$/g, \"\")\r\n .replace(/-{2,}/g, \"-\");\r\n\r\n if (!slug) {\r\n throw new Error(\"Unable to derive an app slug from the provided name.\");\r\n }\r\n\r\n return slug;\r\n}\r\n\r\nexport function derivePackageScope(appSlug: string): string {\r\n return `@${appSlug}`;\r\n}\r\n\r\nexport function toDisplayName(value: string): string {\r\n const normalized = value.trim();\r\n if (!normalized) {\r\n throw new Error(\"App name cannot be empty.\");\r\n }\r\n\r\n return normalized;\r\n}\r\n\r\nexport function createBetterAuthSecret(): string {\r\n return randomBytes(24).toString(\"base64url\");\r\n}\r\n\r\nexport function renderTemplate(content: string, context: TemplateContext): string {\r\n const replacements: Record<string, string> = {\r\n APP_NAME: context.appName,\r\n APP_DESCRIPTION: context.appDescription,\r\n APP_SLUG: context.appSlug,\r\n PACKAGE_SCOPE: context.packageScope,\r\n BETTER_AUTH_SECRET: context.betterAuthSecret,\r\n };\r\n\r\n return content.replace(/\\{\\{([A-Z_]+)\\}\\}/g, (match, token: string) => {\r\n const replacement = replacements[token];\r\n return replacement ?? match;\r\n });\r\n}\r\n"],"mappings":";;;;AAGA,SAAgB,eAAe,OAAuB;CACpD,MAAM,OAAO,MACV,MAAM,CACN,aAAa,CACb,QAAQ,eAAe,IAAI,CAC3B,QAAQ,QAAQ,GAAG,CACnB,QAAQ,QAAQ,GAAG,CACnB,QAAQ,UAAU,IAAI;AAEzB,KAAI,CAAC,KACH,OAAM,IAAI,MAAM,uDAAuD;AAGzE,QAAO;;AAGT,SAAgB,mBAAmB,SAAyB;AAC1D,QAAO,IAAI;;AAGb,SAAgB,cAAc,OAAuB;CACnD,MAAM,aAAa,MAAM,MAAM;AAC/B,KAAI,CAAC,WACH,OAAM,IAAI,MAAM,4BAA4B;AAG9C,QAAO;;AAGT,SAAgB,yBAAiC;AAC/C,SAAA,GAAA,YAAA,aAAmB,GAAG,CAAC,SAAS,YAAY;;AAG9C,SAAgB,eAAe,SAAiB,SAAkC;CAChF,MAAM,eAAuC;EAC3C,UAAU,QAAQ;EAClB,iBAAiB,QAAQ;EACzB,UAAU,QAAQ;EAClB,eAAe,QAAQ;EACvB,oBAAoB,QAAQ;EAC7B;AAED,QAAO,QAAQ,QAAQ,uBAAuB,OAAO,UAAkB;AAErE,SADoB,aAAa,UACX;GACtB"}
@@ -1,4 +1,4 @@
1
- import { TemplateContext } from "./types.mjs";
1
+ import { TemplateContext } from "./types.cjs";
2
2
 
3
3
  //#region src/strings.d.ts
4
4
  declare function slugifyAppName(value: string): string;
@@ -8,4 +8,4 @@ declare function createBetterAuthSecret(): string;
8
8
  declare function renderTemplate(content: string, context: TemplateContext): string;
9
9
  //#endregion
10
10
  export { createBetterAuthSecret, derivePackageScope, renderTemplate, slugifyAppName, toDisplayName };
11
- //# sourceMappingURL=strings.d.mts.map
11
+ //# sourceMappingURL=strings.d.cts.map
File without changes
@@ -19,4 +19,4 @@ interface TemplateContext {
19
19
  }
20
20
  //#endregion
21
21
  export { CreateCommandOptions, PromptValues, TemplateContext };
22
- //# sourceMappingURL=types.d.mts.map
22
+ //# sourceMappingURL=types.d.cts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-m5kdev",
3
- "version": "0.21.5",
3
+ "version": "0.23.0",
4
4
  "license": "GPL-3.0-only",
5
5
  "repository": {
6
6
  "type": "git",
@@ -10,11 +10,14 @@
10
10
  "email:dev": "react-email dev --dir src/emails"
11
11
  },
12
12
  "dependencies": {
13
+ "@m5kdev/commons": "workspace:*",
14
+ "@m5kdev/email": "workspace:*",
13
15
  "@react-email/components": "catalog:",
14
16
  "react": "catalog:",
15
17
  "react-dom": "catalog:"
16
18
  },
17
19
  "devDependencies": {
20
+ "@m5kdev/backend": "workspace:*",
18
21
  "@m5kdev/config": "catalog:",
19
22
  "@types/node": "catalog:",
20
23
  "@types/react": "catalog:",
@@ -23,5 +26,9 @@
23
26
  "tslib": "catalog:",
24
27
  "typescript": "catalog:"
25
28
  },
26
- "main": "./src/index.ts"
29
+ "main": "./src/index.ts",
30
+ "exports": {
31
+ ".": "./src/index.ts",
32
+ "./resources": "./src/resources.ts"
33
+ }
27
34
  }
@@ -1,30 +1,34 @@
1
- import { Text } from "@react-email/components";
2
- import { BaseEmail } from "../components/BaseEmail";
1
+ import { Heading, Text } from "@react-email/components";
2
+ import { CtaButton } from "@m5kdev/email/components/CtaButton";
3
+ import { EmailLayout } from "@m5kdev/email/components/EmailLayout";
4
+ import type { Brand, EmailTranslateFn } from "@m5kdev/email/types";
5
+
6
+ function resolveT(t?: EmailTranslateFn): EmailTranslateFn {
7
+ return t ?? ((key: string) => key);
8
+ }
3
9
 
4
10
  export default function AccountDeletionEmail({
5
- previewText = "Confirm your account deletion",
11
+ previewText,
6
12
  url,
13
+ brand,
14
+ t,
15
+ htmlLang,
7
16
  }: {
8
- previewText?: string;
17
+ previewText: string;
9
18
  url: string;
19
+ brand: Brand;
20
+ t?: EmailTranslateFn;
21
+ htmlLang?: string;
10
22
  }) {
23
+ const translate = resolveT(t);
24
+
11
25
  return (
12
- <BaseEmail
13
- previewText={previewText}
14
- eyebrow="Account Safety"
15
- title="Confirm account deletion"
16
- ctaLabel="Review deletion"
17
- ctaUrl={url}
18
- body={
19
- <>
20
- <Text style={{ margin: "0" }}>
21
- We received a request to delete your {{APP_NAME}} account.
22
- </Text>
23
- <Text style={{ margin: "0" }}>
24
- If that was you, use the link below to confirm the action.
25
- </Text>
26
- </>
27
- }
28
- />
26
+ <EmailLayout previewText={previewText} brand={brand} htmlLang={htmlLang}>
27
+ <Heading className="mb-4 text-2xl font-bold text-black">
28
+ {translate("accountDeletion.title")}
29
+ </Heading>
30
+ <Text className="mb-6 text-base text-gray-700">{translate("accountDeletion.body")}</Text>
31
+ <CtaButton href={url}>{translate("accountDeletion.action")}</CtaButton>
32
+ </EmailLayout>
29
33
  );
30
34
  }
@@ -1,31 +1,33 @@
1
- import { Text } from "@react-email/components";
2
- import { BaseEmail } from "../components/BaseEmail";
1
+ import { Heading, Text } from "@react-email/components";
2
+ import { CtaButton } from "@m5kdev/email/components/CtaButton";
3
+ import { EmailLayout } from "@m5kdev/email/components/EmailLayout";
4
+ import type { EmailTranslateFn, OrganizationInviteTemplateProps } from "@m5kdev/email/types";
5
+
6
+ function resolveT(t?: EmailTranslateFn): EmailTranslateFn {
7
+ return t ?? ((key: string) => key);
8
+ }
3
9
 
4
10
  export default function OrganizationInviteEmail({
5
- previewText = "You have been invited to join an organization",
11
+ previewText,
6
12
  inviterName,
7
13
  organizationName,
8
14
  role,
9
15
  url,
10
- }: {
11
- previewText?: string;
12
- inviterName: string;
13
- organizationName: string;
14
- role: string;
15
- url: string;
16
- }) {
16
+ brand,
17
+ t,
18
+ htmlLang,
19
+ }: OrganizationInviteTemplateProps) {
20
+ const translate = resolveT(t);
21
+
17
22
  return (
18
- <BaseEmail
19
- previewText={previewText}
20
- eyebrow="Collaboration"
21
- title={`Join ${organizationName}`}
22
- ctaLabel="Accept invitation"
23
- ctaUrl={url}
24
- body={
25
- <Text style={{ margin: "0" }}>
26
- {inviterName} invited you to join {organizationName} as {role}.
27
- </Text>
28
- }
29
- />
23
+ <EmailLayout previewText={previewText} brand={brand} htmlLang={htmlLang}>
24
+ <Heading className="mb-4 text-2xl font-bold text-black">
25
+ {translate("organizationInvite.title", { organizationName })}
26
+ </Heading>
27
+ <Text className="mb-6 text-base text-gray-700">
28
+ {translate("organizationInvite.body", { inviterName, organizationName, role })}
29
+ </Text>
30
+ <CtaButton href={url}>{translate("organizationInvite.action")}</CtaButton>
31
+ </EmailLayout>
30
32
  );
31
33
  }
@@ -1,25 +1,34 @@
1
- import { Text } from "@react-email/components";
2
- import { BaseEmail } from "../components/BaseEmail";
1
+ import { Heading, Text } from "@react-email/components";
2
+ import { CtaButton } from "@m5kdev/email/components/CtaButton";
3
+ import { EmailLayout } from "@m5kdev/email/components/EmailLayout";
4
+ import type { Brand, EmailTranslateFn } from "@m5kdev/email/types";
5
+
6
+ function resolveT(t?: EmailTranslateFn): EmailTranslateFn {
7
+ return t ?? ((key: string) => key);
8
+ }
3
9
 
4
10
  export default function PasswordResetEmail({
5
- previewText = "Reset your password",
11
+ previewText,
6
12
  url,
13
+ brand,
14
+ t,
15
+ htmlLang,
7
16
  }: {
8
- previewText?: string;
17
+ previewText: string;
9
18
  url: string;
19
+ brand: Brand;
20
+ t?: EmailTranslateFn;
21
+ htmlLang?: string;
10
22
  }) {
23
+ const translate = resolveT(t);
24
+
11
25
  return (
12
- <BaseEmail
13
- previewText={previewText}
14
- eyebrow="Access"
15
- title="Reset your password"
16
- ctaLabel="Choose a new password"
17
- ctaUrl={url}
18
- body={
19
- <Text style={{ margin: "0" }}>
20
- Use the secure link below to choose a new password for your {{APP_NAME}} account.
21
- </Text>
22
- }
23
- />
26
+ <EmailLayout previewText={previewText} brand={brand} htmlLang={htmlLang}>
27
+ <Heading className="mb-4 text-2xl font-bold text-black">
28
+ {translate("passwordReset.title")}
29
+ </Heading>
30
+ <Text className="mb-6 text-base text-gray-700">{translate("passwordReset.body")}</Text>
31
+ <CtaButton href={url}>{translate("passwordReset.action")}</CtaButton>
32
+ </EmailLayout>
24
33
  );
25
34
  }
@@ -1,26 +1,36 @@
1
- import { Text } from "@react-email/components";
2
- import { BaseEmail } from "../components/BaseEmail";
1
+ import { Heading, Text } from "@react-email/components";
2
+ import { CtaButton } from "@m5kdev/email/components/CtaButton";
3
+ import { EmailLayout } from "@m5kdev/email/components/EmailLayout";
4
+ import type { Brand, EmailTranslateFn } from "@m5kdev/email/types";
5
+
6
+ function resolveT(t?: EmailTranslateFn): EmailTranslateFn {
7
+ return t ?? ((key: string) => key);
8
+ }
3
9
 
4
10
  export default function VerificationEmail({
5
- previewText = "Verify your email",
11
+ previewText,
6
12
  url,
13
+ brand,
14
+ t,
15
+ htmlLang,
7
16
  }: {
8
- previewText?: string;
17
+ previewText: string;
9
18
  url: string;
19
+ brand: Brand;
20
+ t?: EmailTranslateFn;
21
+ htmlLang?: string;
10
22
  }) {
23
+ const translate = resolveT(t);
24
+
11
25
  return (
12
- <BaseEmail
13
- previewText={previewText}
14
- eyebrow="Welcome"
15
- title="Verify your email"
16
- ctaLabel="Verify account"
17
- ctaUrl={url}
18
- body={
19
- <Text style={{ margin: "0" }}>
20
- Confirm your email address to finish setting up {{APP_NAME}} and access your editorial
21
- workspace.
22
- </Text>
23
- }
24
- />
26
+ <EmailLayout previewText={previewText} brand={brand} htmlLang={htmlLang}>
27
+ <Heading className="mb-4 text-2xl font-bold text-black">
28
+ {translate("verification.title")}
29
+ </Heading>
30
+ <Text className="mb-6 text-base text-gray-700">
31
+ {translate("verification.body", { appName: "{{APP_NAME}}" })}
32
+ </Text>
33
+ <CtaButton href={url}>{translate("verification.action")}</CtaButton>
34
+ </EmailLayout>
25
35
  );
26
36
  }
@@ -7,26 +7,26 @@ import VerificationEmail from "./emails/verificationEmail";
7
7
  export const templates = {
8
8
  accountDeletion: {
9
9
  id: "account-deletion",
10
- subject: "Delete your account",
11
- previewText: "Confirm your account deletion",
10
+ subject: "accountDeletion.subject",
11
+ previewText: "accountDeletion.previewText",
12
12
  react: AccountDeletionEmail as FunctionComponent<Record<string, unknown>>,
13
13
  },
14
14
  passwordReset: {
15
15
  id: "password-reset",
16
- subject: "Reset your password",
17
- previewText: "Reset your password request",
16
+ subject: "passwordReset.subject",
17
+ previewText: "passwordReset.previewText",
18
18
  react: PasswordResetEmail as FunctionComponent<Record<string, unknown>>,
19
19
  },
20
20
  verification: {
21
21
  id: "verification",
22
- subject: "Verify your email",
23
- previewText: "Verify your email address",
22
+ subject: "verification.subject",
23
+ previewText: "verification.previewText",
24
24
  react: VerificationEmail as FunctionComponent<Record<string, unknown>>,
25
25
  },
26
26
  organizationInvite: {
27
27
  id: "organization-invite",
28
- subject: "Join the organization",
29
- previewText: "You have been invited to join an organization",
28
+ subject: "organizationInvite.subject",
29
+ previewText: "organizationInvite.previewText",
30
30
  react: OrganizationInviteEmail as FunctionComponent<Record<string, unknown>>,
31
31
  },
32
32
  };
@@ -0,0 +1,36 @@
1
+ import type { BackendAppI18nResources } from "@m5kdev/backend/i18n/app-i18n";
2
+
3
+ export const emailResources = {
4
+ en: {
5
+ translation: {
6
+ verification: {
7
+ subject: "Verify your email",
8
+ previewText: "Verify your email address",
9
+ title: "Verify your email",
10
+ body: "Confirm your email address to finish setting up {{APP_NAME}} and access your editorial workspace.",
11
+ action: "Verify account",
12
+ },
13
+ passwordReset: {
14
+ subject: "Reset your password",
15
+ previewText: "Reset your password request",
16
+ title: "Reset your password",
17
+ body: "Use this link to choose a new password for your account.",
18
+ action: "Reset password",
19
+ },
20
+ accountDeletion: {
21
+ subject: "Delete your account",
22
+ previewText: "Confirm your account deletion",
23
+ title: "Confirm account deletion",
24
+ body: "Use this link to confirm deleting your account.",
25
+ action: "Confirm deletion",
26
+ },
27
+ organizationInvite: {
28
+ subject: "Join the organization",
29
+ previewText: "You have been invited to join an organization",
30
+ title: "Join {{organizationName}}",
31
+ body: "{{inviterName}} invited you to join {{organizationName}} as {{role}}.",
32
+ action: "Accept invitation",
33
+ },
34
+ },
35
+ },
36
+ } satisfies BackendAppI18nResources;
@@ -5,7 +5,10 @@ import { EmailModule } from "@m5kdev/backend/modules/email/email.module";
5
5
  import { NotificationModule } from "@m5kdev/backend/modules/notification/notification.module";
6
6
  import { WorkflowModule } from "@m5kdev/backend/modules/workflow/workflow.module";
7
7
  import { templates } from "{{PACKAGE_SCOPE}}/email";
8
+ import { emailResources } from "{{PACKAGE_SCOPE}}/email/resources";
8
9
  import { APP_NAME } from "{{PACKAGE_SCOPE}}/shared/modules/app/app.constants";
10
+ import { AUTH_LOCALE_CONFIG } from "{{PACKAGE_SCOPE}}/shared/modules/app/locale.constants";
11
+ import { USER_LOCALE_HEADER } from "@m5kdev/commons/modules/auth/auth.constants";
9
12
  import cors from "cors";
10
13
  import express from "express";
11
14
  import { schema } from "./generated/schema";
@@ -46,6 +49,7 @@ app.use(
46
49
  "Waitlist-Invitation-Code",
47
50
  "Organization-Invitation-Code",
48
51
  "Admin-Create-Verified-User",
52
+ USER_LOCALE_HEADER,
49
53
  ],
50
54
  })
51
55
  );
@@ -61,6 +65,10 @@ export const builtBackendApp = createBackendApp(
61
65
  web: appUrl,
62
66
  api: serverUrl,
63
67
  },
68
+ locales: AUTH_LOCALE_CONFIG,
69
+ },
70
+ i18n: {
71
+ resources: emailResources,
64
72
  },
65
73
  redis: {
66
74
  url: redisUrl,
@@ -74,7 +82,7 @@ export const builtBackendApp = createBackendApp(
74
82
  outputDirectory: ".emails",
75
83
  },
76
84
  auth: {
77
- factory({ db, services, appConfig }) {
85
+ factory({ db, services, appConfig, i18n }) {
78
86
  return createBetterAuth({
79
87
  orm: db.orm as never,
80
88
  schema: db.schema as never,
@@ -82,6 +90,7 @@ export const builtBackendApp = createBackendApp(
82
90
  email: services.email.email,
83
91
  },
84
92
  app: appConfig,
93
+ i18n,
85
94
  config: {
86
95
  waitlist: enableWaitlist,
87
96
  provisionedAccountEmailDomain: "provisioned.{{APP_SLUG}}.local",
@@ -0,0 +1,7 @@
1
+ export const AUTH_LOCALE_CONFIG = {
2
+ defaultLocale: "en",
3
+ locales: [
4
+ { code: "en", displayName: "English" },
5
+ { code: "en_GB", displayName: "English (UK)" },
6
+ ],
7
+ } as const;
@@ -5,6 +5,7 @@ import { AuthOrganizationChildOrganizationsRoute } from "@m5kdev/web-ui/modules/
5
5
  import { AuthOrganizationMembersRoute } from "@m5kdev/web-ui/modules/auth/components/AuthOrganizationMembersRoute";
6
6
  import { AuthOrganizationPreferences } from "@m5kdev/web-ui/modules/auth/components/AuthOrganizationPreferences";
7
7
  import { AuthPublicRouter } from "@m5kdev/web-ui/modules/auth/components/AuthPublicRouter";
8
+ import { syncI18nLocale } from "@m5kdev/frontend/modules/app/utils/locale";
8
9
  import { AuthUserRouter } from "@m5kdev/web-ui/modules/auth/components/AuthUserRouter";
9
10
  import { APP_NAME } from "{{PACKAGE_SCOPE}}/shared/modules/app/app.constants";
10
11
  import type { ReactNode } from "react";
@@ -65,6 +66,7 @@ export function Router() {
65
66
  {AuthPublicRouter({
66
67
  header: <AuthHeader />,
67
68
  waitlist: isWaitlist,
69
+ onLocaleChange: syncI18nLocale,
68
70
  })}
69
71
 
70
72
  <Route
@@ -97,6 +99,7 @@ export function Router() {
97
99
  {AuthUserRouter({
98
100
  schema: preferenceSchema,
99
101
  controls: preferenceControls,
102
+ onLocaleChange: syncI18nLocale,
100
103
  })}
101
104
  {AuthAdminRouter({
102
105
  enableWaitlist: true,
@@ -1,10 +0,0 @@
1
- //#region src/constants.ts
2
- const TEMPLATE_NAME = "minimal-app";
3
- const DEFAULT_APP_NAME = "M5 Starter";
4
- function getDefaultDescription(appName) {
5
- return `${appName} is a blog publishing platform scaffolded with the m5kdev stack.`;
6
- }
7
- //#endregion
8
- export { DEFAULT_APP_NAME, TEMPLATE_NAME, getDefaultDescription };
9
-
10
- //# sourceMappingURL=constants.mjs.map
@@ -1,33 +0,0 @@
1
- import { TEMPLATE_NAME } from "./constants.mjs";
2
- import { createBetterAuthSecret, derivePackageScope, slugifyAppName } from "./strings.mjs";
3
- import { copyTemplateDirectory, ensureDirectoryState, runGitInit, runInstall } from "./fs.mjs";
4
- import { getTemplateRoot } from "./paths.mjs";
5
- import { resolveCreateCommandOptions } from "./prompts.mjs";
6
- import path from "node:path";
7
- //#region src/create.ts
8
- async function scaffoldProject(initialOptions) {
9
- const options = await resolveCreateCommandOptions(initialOptions);
10
- const appSlug = slugifyAppName(options.appName);
11
- const targetDirectory = path.resolve(process.cwd(), options.targetDirectory);
12
- const templateDirectory = getTemplateRoot();
13
- const context = {
14
- appName: options.appName,
15
- appDescription: options.appDescription,
16
- appSlug,
17
- packageScope: derivePackageScope(appSlug),
18
- betterAuthSecret: createBetterAuthSecret()
19
- };
20
- await ensureDirectoryState(targetDirectory, options.force);
21
- await copyTemplateDirectory(templateDirectory, targetDirectory, context);
22
- if (!options.skipGit) await runGitInit(targetDirectory);
23
- if (!options.skipInstall) await runInstall(targetDirectory);
24
- return {
25
- context,
26
- targetDirectory,
27
- templateName: TEMPLATE_NAME
28
- };
29
- }
30
- //#endregion
31
- export { scaffoldProject };
32
-
33
- //# sourceMappingURL=create.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"create.mjs","names":[],"sources":["../../src/create.ts"],"sourcesContent":["import path from \"node:path\";\r\nimport { TEMPLATE_NAME } from \"./constants\";\r\nimport { copyTemplateDirectory, ensureDirectoryState, runGitInit, runInstall } from \"./fs\";\r\nimport { getTemplateRoot } from \"./paths\";\r\nimport { resolveCreateCommandOptions } from \"./prompts\";\r\nimport { createBetterAuthSecret, derivePackageScope, slugifyAppName } from \"./strings\";\r\nimport type { CreateCommandOptions, TemplateContext } from \"./types\";\r\n\r\nexport interface ScaffoldResult {\r\n context: TemplateContext;\r\n targetDirectory: string;\r\n templateName: string;\r\n}\r\n\r\nexport async function scaffoldProject(initialOptions: CreateCommandOptions): Promise<ScaffoldResult> {\r\n const options = await resolveCreateCommandOptions(initialOptions);\r\n const appSlug = slugifyAppName(options.appName!);\r\n const targetDirectory = path.resolve(process.cwd(), options.targetDirectory!);\r\n const templateDirectory = getTemplateRoot();\r\n\r\n const context: TemplateContext = {\r\n appName: options.appName!,\r\n appDescription: options.appDescription!,\r\n appSlug,\r\n packageScope: derivePackageScope(appSlug),\r\n betterAuthSecret: createBetterAuthSecret(),\r\n };\r\n\r\n await ensureDirectoryState(targetDirectory, options.force);\r\n await copyTemplateDirectory(templateDirectory, targetDirectory, context);\r\n\r\n if (!options.skipGit) {\r\n await runGitInit(targetDirectory);\r\n }\r\n\r\n if (!options.skipInstall) {\r\n await runInstall(targetDirectory);\r\n }\r\n\r\n return {\r\n context,\r\n targetDirectory,\r\n templateName: TEMPLATE_NAME,\r\n };\r\n}\r\n"],"mappings":";;;;;;;AAcA,eAAsB,gBAAgB,gBAA+D;CACnG,MAAM,UAAU,MAAM,4BAA4B,eAAe;CACjE,MAAM,UAAU,eAAe,QAAQ,QAAS;CAChD,MAAM,kBAAkB,KAAK,QAAQ,QAAQ,KAAK,EAAE,QAAQ,gBAAiB;CAC7E,MAAM,oBAAoB,iBAAiB;CAE3C,MAAM,UAA2B;EAC/B,SAAS,QAAQ;EACjB,gBAAgB,QAAQ;EACxB;EACA,cAAc,mBAAmB,QAAQ;EACzC,kBAAkB,wBAAwB;EAC3C;AAED,OAAM,qBAAqB,iBAAiB,QAAQ,MAAM;AAC1D,OAAM,sBAAsB,mBAAmB,iBAAiB,QAAQ;AAExE,KAAI,CAAC,QAAQ,QACX,OAAM,WAAW,gBAAgB;AAGnC,KAAI,CAAC,QAAQ,YACX,OAAM,WAAW,gBAAgB;AAGnC,QAAO;EACL;EACA;EACA,cAAc;EACf"}
package/dist/src/fs.mjs DELETED
@@ -1,50 +0,0 @@
1
- import { renderTemplate } from "./strings.mjs";
2
- import path from "node:path";
3
- import { execFile } from "node:child_process";
4
- import fs from "node:fs/promises";
5
- import { promisify } from "node:util";
6
- //#region src/fs.ts
7
- const execFileAsync = promisify(execFile);
8
- const TEMPLATE_LINE_ENDING = "\r\n";
9
- async function ensureDirectoryState(targetDirectory, force) {
10
- const stat = await fs.stat(targetDirectory).catch(() => null);
11
- if (!stat) {
12
- await fs.mkdir(targetDirectory, { recursive: true });
13
- return;
14
- }
15
- if (!stat.isDirectory()) throw new Error(`Target path is not a directory: ${targetDirectory}`);
16
- if ((await fs.readdir(targetDirectory)).length > 0 && !force) throw new Error(`Target directory is not empty: ${targetDirectory}. Re-run with --force to overwrite.`);
17
- }
18
- async function copyTemplateDirectory(templateDirectory, targetDirectory, context) {
19
- const entries = await fs.readdir(templateDirectory, { withFileTypes: true });
20
- for (const entry of entries) {
21
- const sourcePath = path.join(templateDirectory, entry.name);
22
- const targetName = entry.name.endsWith(".tpl") ? entry.name.slice(0, -4) : entry.name;
23
- const targetPath = path.join(targetDirectory, targetName);
24
- if (entry.isDirectory()) {
25
- await fs.mkdir(targetPath, { recursive: true });
26
- await copyTemplateDirectory(sourcePath, targetPath, context);
27
- continue;
28
- }
29
- if (!entry.isFile()) continue;
30
- const rendered = renderTemplate(await fs.readFile(sourcePath, "utf8"), context).replace(/\r?\n/g, TEMPLATE_LINE_ENDING);
31
- await fs.mkdir(path.dirname(targetPath), { recursive: true });
32
- await fs.writeFile(targetPath, rendered, "utf8");
33
- }
34
- }
35
- async function runInstall(targetDirectory) {
36
- await execFileAsync("pnpm", ["install"], {
37
- cwd: targetDirectory,
38
- env: process.env
39
- });
40
- }
41
- async function runGitInit(targetDirectory) {
42
- await execFileAsync("git", ["init"], {
43
- cwd: targetDirectory,
44
- env: process.env
45
- });
46
- }
47
- //#endregion
48
- export { copyTemplateDirectory, ensureDirectoryState, runGitInit, runInstall };
49
-
50
- //# sourceMappingURL=fs.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"fs.mjs","names":[],"sources":["../../src/fs.ts"],"sourcesContent":["import { execFile } from \"node:child_process\";\r\nimport fs from \"node:fs/promises\";\r\nimport path from \"node:path\";\r\nimport { promisify } from \"node:util\";\r\nimport type { TemplateContext } from \"./types\";\r\nimport { renderTemplate } from \"./strings\";\r\n\r\nconst execFileAsync = promisify(execFile);\r\nconst TEMPLATE_LINE_ENDING = \"\\r\\n\";\r\n\r\nexport async function ensureDirectoryState(\r\n targetDirectory: string,\r\n force: boolean\r\n): Promise<void> {\r\n const stat = await fs.stat(targetDirectory).catch(() => null);\r\n\r\n if (!stat) {\r\n await fs.mkdir(targetDirectory, { recursive: true });\r\n return;\r\n }\r\n\r\n if (!stat.isDirectory()) {\r\n throw new Error(`Target path is not a directory: ${targetDirectory}`);\r\n }\r\n\r\n const entries = await fs.readdir(targetDirectory);\r\n if (entries.length > 0 && !force) {\r\n throw new Error(\r\n `Target directory is not empty: ${targetDirectory}. Re-run with --force to overwrite.`\r\n );\r\n }\r\n}\r\n\r\nexport async function copyTemplateDirectory(\r\n templateDirectory: string,\r\n targetDirectory: string,\r\n context: TemplateContext\r\n): Promise<void> {\r\n const entries = await fs.readdir(templateDirectory, { withFileTypes: true });\r\n\r\n for (const entry of entries) {\r\n const sourcePath = path.join(templateDirectory, entry.name);\r\n const targetName = entry.name.endsWith(\".tpl\") ? entry.name.slice(0, -4) : entry.name;\r\n const targetPath = path.join(targetDirectory, targetName);\r\n\r\n if (entry.isDirectory()) {\r\n await fs.mkdir(targetPath, { recursive: true });\r\n await copyTemplateDirectory(sourcePath, targetPath, context);\r\n continue;\r\n }\r\n\r\n if (!entry.isFile()) {\r\n continue;\r\n }\r\n\r\n const content = await fs.readFile(sourcePath, \"utf8\");\r\n const rendered = renderTemplate(content, context).replace(/\\r?\\n/g, TEMPLATE_LINE_ENDING);\r\n await fs.mkdir(path.dirname(targetPath), { recursive: true });\r\n await fs.writeFile(targetPath, rendered, \"utf8\");\r\n }\r\n}\r\n\r\nexport async function runInstall(targetDirectory: string): Promise<void> {\r\n await execFileAsync(\"pnpm\", [\"install\"], {\r\n cwd: targetDirectory,\r\n env: process.env,\r\n });\r\n}\r\n\r\nexport async function runGitInit(targetDirectory: string): Promise<void> {\r\n await execFileAsync(\"git\", [\"init\"], {\r\n cwd: targetDirectory,\r\n env: process.env,\r\n });\r\n}\r\n"],"mappings":";;;;;;AAOA,MAAM,gBAAgB,UAAU,SAAS;AACzC,MAAM,uBAAuB;AAE7B,eAAsB,qBACpB,iBACA,OACe;CACf,MAAM,OAAO,MAAM,GAAG,KAAK,gBAAgB,CAAC,YAAY,KAAK;AAE7D,KAAI,CAAC,MAAM;AACT,QAAM,GAAG,MAAM,iBAAiB,EAAE,WAAW,MAAM,CAAC;AACpD;;AAGF,KAAI,CAAC,KAAK,aAAa,CACrB,OAAM,IAAI,MAAM,mCAAmC,kBAAkB;AAIvE,MADgB,MAAM,GAAG,QAAQ,gBAAgB,EACrC,SAAS,KAAK,CAAC,MACzB,OAAM,IAAI,MACR,kCAAkC,gBAAgB,qCACnD;;AAIL,eAAsB,sBACpB,mBACA,iBACA,SACe;CACf,MAAM,UAAU,MAAM,GAAG,QAAQ,mBAAmB,EAAE,eAAe,MAAM,CAAC;AAE5E,MAAK,MAAM,SAAS,SAAS;EAC3B,MAAM,aAAa,KAAK,KAAK,mBAAmB,MAAM,KAAK;EAC3D,MAAM,aAAa,MAAM,KAAK,SAAS,OAAO,GAAG,MAAM,KAAK,MAAM,GAAG,GAAG,GAAG,MAAM;EACjF,MAAM,aAAa,KAAK,KAAK,iBAAiB,WAAW;AAEzD,MAAI,MAAM,aAAa,EAAE;AACvB,SAAM,GAAG,MAAM,YAAY,EAAE,WAAW,MAAM,CAAC;AAC/C,SAAM,sBAAsB,YAAY,YAAY,QAAQ;AAC5D;;AAGF,MAAI,CAAC,MAAM,QAAQ,CACjB;EAIF,MAAM,WAAW,eADD,MAAM,GAAG,SAAS,YAAY,OAAO,EACZ,QAAQ,CAAC,QAAQ,UAAU,qBAAqB;AACzF,QAAM,GAAG,MAAM,KAAK,QAAQ,WAAW,EAAE,EAAE,WAAW,MAAM,CAAC;AAC7D,QAAM,GAAG,UAAU,YAAY,UAAU,OAAO;;;AAIpD,eAAsB,WAAW,iBAAwC;AACvE,OAAM,cAAc,QAAQ,CAAC,UAAU,EAAE;EACvC,KAAK;EACL,KAAK,QAAQ;EACd,CAAC;;AAGJ,eAAsB,WAAW,iBAAwC;AACvE,OAAM,cAAc,OAAO,CAAC,OAAO,EAAE;EACnC,KAAK;EACL,KAAK,QAAQ;EACd,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\r\n\r\nimport { runCli } from \"./runCli\";\r\n\r\nrunCli(process.argv.slice(2)).catch((error: unknown) => {\r\n const message = error instanceof Error ? error.message : String(error);\r\n console.error(message);\r\n process.exitCode = 1;\r\n});\r\n"],"mappings":";;;AAIA,OAAO,QAAQ,KAAK,MAAM,EAAE,CAAC,CAAC,OAAO,UAAmB;CACtD,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;AACtE,SAAQ,MAAM,QAAQ;AACtB,SAAQ,WAAW;EACnB"}
@@ -1,13 +0,0 @@
1
- import { TEMPLATE_NAME } from "./constants.mjs";
2
- import path from "node:path";
3
- import fs from "node:fs";
4
- //#region src/paths.ts
5
- function getTemplateRoot() {
6
- const sourcePath = path.resolve(__dirname, "../templates", TEMPLATE_NAME);
7
- if (fs.existsSync(sourcePath)) return sourcePath;
8
- return path.resolve(__dirname, "../../templates", TEMPLATE_NAME);
9
- }
10
- //#endregion
11
- export { getTemplateRoot };
12
-
13
- //# sourceMappingURL=paths.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"paths.mjs","names":[],"sources":["../../src/paths.ts"],"sourcesContent":["import fs from \"node:fs\";\r\nimport path from \"node:path\";\r\nimport { TEMPLATE_NAME } from \"./constants\";\r\n\r\nexport function getTemplateRoot(): string {\r\n const sourcePath = path.resolve(__dirname, \"../templates\", TEMPLATE_NAME);\r\n if (fs.existsSync(sourcePath)) {\r\n return sourcePath;\r\n }\r\n\r\n return path.resolve(__dirname, \"../../templates\", TEMPLATE_NAME);\r\n}\r\n"],"mappings":";;;;AAIA,SAAgB,kBAA0B;CACxC,MAAM,aAAa,KAAK,QAAQ,WAAW,gBAAgB,cAAc;AACzE,KAAI,GAAG,WAAW,WAAW,CAC3B,QAAO;AAGT,QAAO,KAAK,QAAQ,WAAW,mBAAmB,cAAc"}
@@ -1,45 +0,0 @@
1
- import { DEFAULT_APP_NAME, getDefaultDescription } from "./constants.mjs";
2
- import { slugifyAppName, toDisplayName } from "./strings.mjs";
3
- import readline from "node:readline/promises";
4
- import { stdin, stdout } from "node:process";
5
- //#region src/prompts.ts
6
- function requireInteractive(yes) {
7
- if (!yes && !process.stdin.isTTY) throw new Error("Missing required values in a non-interactive shell. Pass --yes or provide flags.");
8
- }
9
- async function promptValue(question, fallback) {
10
- const rl = readline.createInterface({
11
- input: stdin,
12
- output: stdout
13
- });
14
- try {
15
- const suffix = fallback ? ` (${fallback})` : "";
16
- return (await rl.question(`${question}${suffix}: `)).trim() || fallback || "";
17
- } finally {
18
- rl.close();
19
- }
20
- }
21
- async function resolveCreateCommandOptions(options) {
22
- const resolved = { ...options };
23
- if (!resolved.appName) {
24
- requireInteractive(resolved.yes);
25
- resolved.appName = resolved.yes ? resolved.targetDirectory ? toDisplayName(pathBaseName(resolved.targetDirectory)) : DEFAULT_APP_NAME : await promptValue("App name", resolved.targetDirectory ? pathBaseName(resolved.targetDirectory) : DEFAULT_APP_NAME);
26
- }
27
- resolved.appName = toDisplayName(resolved.appName);
28
- if (!resolved.targetDirectory) {
29
- requireInteractive(resolved.yes);
30
- resolved.targetDirectory = resolved.yes ? slugifyAppName(resolved.appName) : await promptValue("Target directory", slugifyAppName(resolved.appName));
31
- }
32
- if (!resolved.appDescription) {
33
- requireInteractive(resolved.yes);
34
- const fallback = getDefaultDescription(resolved.appName);
35
- resolved.appDescription = resolved.yes ? fallback : await promptValue("App description", fallback);
36
- }
37
- return resolved;
38
- }
39
- function pathBaseName(value) {
40
- return value.replace(/[\\/]+$/g, "").split(/[\\/]/).filter(Boolean).at(-1) || "M5 Starter";
41
- }
42
- //#endregion
43
- export { resolveCreateCommandOptions };
44
-
45
- //# sourceMappingURL=prompts.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"prompts.mjs","names":[],"sources":["../../src/prompts.ts"],"sourcesContent":["import readline from \"node:readline/promises\";\r\nimport { stdin as input, stdout as output } from \"node:process\";\r\nimport { DEFAULT_APP_NAME, getDefaultDescription } from \"./constants\";\r\nimport { slugifyAppName, toDisplayName } from \"./strings\";\r\nimport type { CreateCommandOptions } from \"./types\";\r\n\r\nfunction requireInteractive(yes: boolean): void {\r\n if (!yes && !process.stdin.isTTY) {\r\n throw new Error(\"Missing required values in a non-interactive shell. Pass --yes or provide flags.\");\r\n }\r\n}\r\n\r\nasync function promptValue(question: string, fallback?: string): Promise<string> {\r\n const rl = readline.createInterface({ input, output });\r\n\r\n try {\r\n const suffix = fallback ? ` (${fallback})` : \"\";\r\n const response = await rl.question(`${question}${suffix}: `);\r\n return response.trim() || fallback || \"\";\r\n } finally {\r\n rl.close();\r\n }\r\n}\r\n\r\nexport async function resolveCreateCommandOptions(\r\n options: CreateCommandOptions\r\n): Promise<CreateCommandOptions> {\r\n const resolved = { ...options };\r\n\r\n if (!resolved.appName) {\r\n requireInteractive(resolved.yes);\r\n resolved.appName = resolved.yes\r\n ? resolved.targetDirectory\r\n ? toDisplayName(pathBaseName(resolved.targetDirectory))\r\n : DEFAULT_APP_NAME\r\n : await promptValue(\"App name\", resolved.targetDirectory ? pathBaseName(resolved.targetDirectory) : DEFAULT_APP_NAME);\r\n }\r\n\r\n resolved.appName = toDisplayName(resolved.appName);\r\n\r\n if (!resolved.targetDirectory) {\r\n requireInteractive(resolved.yes);\r\n resolved.targetDirectory = resolved.yes\r\n ? slugifyAppName(resolved.appName)\r\n : await promptValue(\"Target directory\", slugifyAppName(resolved.appName));\r\n }\r\n\r\n if (!resolved.appDescription) {\r\n requireInteractive(resolved.yes);\r\n const fallback = getDefaultDescription(resolved.appName);\r\n resolved.appDescription = resolved.yes\r\n ? fallback\r\n : await promptValue(\"App description\", fallback);\r\n }\r\n\r\n return resolved;\r\n}\r\n\r\nfunction pathBaseName(value: string): string {\r\n const normalized = value.replace(/[\\\\/]+$/g, \"\");\r\n const segments = normalized.split(/[\\\\/]/).filter(Boolean);\r\n return segments.at(-1) || DEFAULT_APP_NAME;\r\n}\r\n"],"mappings":";;;;;AAMA,SAAS,mBAAmB,KAAoB;AAC9C,KAAI,CAAC,OAAO,CAAC,QAAQ,MAAM,MACzB,OAAM,IAAI,MAAM,mFAAmF;;AAIvG,eAAe,YAAY,UAAkB,UAAoC;CAC/E,MAAM,KAAK,SAAS,gBAAgB;EAAE,OAAA;EAAO,QAAA;EAAQ,CAAC;AAEtD,KAAI;EACF,MAAM,SAAS,WAAW,KAAK,SAAS,KAAK;AAE7C,UADiB,MAAM,GAAG,SAAS,GAAG,WAAW,OAAO,IAAI,EAC5C,MAAM,IAAI,YAAY;WAC9B;AACR,KAAG,OAAO;;;AAId,eAAsB,4BACpB,SAC+B;CAC/B,MAAM,WAAW,EAAE,GAAG,SAAS;AAE/B,KAAI,CAAC,SAAS,SAAS;AACrB,qBAAmB,SAAS,IAAI;AAChC,WAAS,UAAU,SAAS,MACxB,SAAS,kBACP,cAAc,aAAa,SAAS,gBAAgB,CAAC,GACrD,mBACF,MAAM,YAAY,YAAY,SAAS,kBAAkB,aAAa,SAAS,gBAAgB,GAAG,iBAAiB;;AAGzH,UAAS,UAAU,cAAc,SAAS,QAAQ;AAElD,KAAI,CAAC,SAAS,iBAAiB;AAC7B,qBAAmB,SAAS,IAAI;AAChC,WAAS,kBAAkB,SAAS,MAChC,eAAe,SAAS,QAAQ,GAChC,MAAM,YAAY,oBAAoB,eAAe,SAAS,QAAQ,CAAC;;AAG7E,KAAI,CAAC,SAAS,gBAAgB;AAC5B,qBAAmB,SAAS,IAAI;EAChC,MAAM,WAAW,sBAAsB,SAAS,QAAQ;AACxD,WAAS,iBAAiB,SAAS,MAC/B,WACA,MAAM,YAAY,mBAAmB,SAAS;;AAGpD,QAAO;;AAGT,SAAS,aAAa,OAAuB;AAG3C,QAFmB,MAAM,QAAQ,YAAY,GAAG,CACpB,MAAM,QAAQ,CAAC,OAAO,QAAQ,CAC1C,GAAG,GAAG,IAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"runCli.mjs","names":[],"sources":["../../src/runCli.ts"],"sourcesContent":["import { scaffoldProject } from \"./create\";\r\nimport type { CreateCommandOptions } from \"./types\";\r\n\r\ninterface ParsedCli {\r\n command?: string;\r\n directory?: string;\r\n help?: boolean;\r\n options: Record<string, string | boolean>;\r\n}\r\n\r\nexport async function runCli(argv: readonly string[]): Promise<void> {\r\n const parsed = parseCli(argv);\r\n\r\n if (\r\n !parsed.command ||\r\n parsed.help ||\r\n parsed.command === \"--help\" ||\r\n parsed.command === \"-h\" ||\r\n parsed.command === \"help\"\r\n ) {\r\n printHelp();\r\n return;\r\n }\r\n\r\n if (parsed.command !== \"create\") {\r\n throw new Error(`Unknown command: ${parsed.command}`);\r\n }\r\n\r\n const options = toCreateCommandOptions(parsed);\r\n const result = await scaffoldProject(options);\r\n\r\n console.log(\"\");\r\n console.log(`Scaffolded ${result.context.appName} in ${result.targetDirectory}`);\r\n console.log(\"\");\r\n console.log(\"Next steps:\");\r\n console.log(` cd ${result.targetDirectory}`);\r\n if (options.skipInstall) {\r\n console.log(\" pnpm install\");\r\n }\r\n console.log(\" pnpm --filter ./apps/server sync\");\r\n console.log(\" pnpm dev\");\r\n}\r\n\r\nexport function parseCli(argv: readonly string[]): ParsedCli {\r\n const normalizedArgv = normalizeCliArgv(argv);\r\n const [command, ...rest] = normalizedArgv;\r\n const options: Record<string, string | boolean> = {};\r\n let directory: string | undefined;\r\n let help = false;\r\n\r\n for (let index = 0; index < rest.length; index += 1) {\r\n const token = rest[index];\r\n\r\n if (token === \"--help\" || token === \"-h\") {\r\n help = true;\r\n continue;\r\n }\r\n\r\n if (!token.startsWith(\"--\")) {\r\n directory ??= token;\r\n continue;\r\n }\r\n\r\n const [rawKey, inlineValue] = token.slice(2).split(\"=\", 2);\r\n\r\n if (isBooleanFlag(rawKey)) {\r\n options[rawKey] = true;\r\n continue;\r\n }\r\n\r\n const nextValue = inlineValue ?? rest[index + 1];\r\n if (!nextValue || nextValue.startsWith(\"--\")) {\r\n throw new Error(`Missing value for --${rawKey}`);\r\n }\r\n\r\n options[rawKey] = nextValue;\r\n if (inlineValue === undefined) {\r\n index += 1;\r\n }\r\n }\r\n\r\n return { command, directory, help, options };\r\n}\r\n\r\nfunction normalizeCliArgv(argv: readonly string[]): readonly string[] {\r\n const [firstToken] = argv;\r\n\r\n if (!firstToken || firstToken === \"create\" || firstToken === \"--help\" || firstToken === \"-h\") {\r\n return argv;\r\n }\r\n\r\n if (firstToken === \"help\") {\r\n return [\"--help\"];\r\n }\r\n\r\n return [\"create\", ...argv];\r\n}\r\n\r\nfunction isBooleanFlag(value: string): boolean {\r\n return [\"yes\", \"force\", \"skip-install\", \"skip-git\"].includes(value);\r\n}\r\n\r\nfunction toCreateCommandOptions(parsed: ParsedCli): CreateCommandOptions {\r\n return {\r\n targetDirectory: parsed.directory,\r\n appName: getStringOption(parsed.options, \"name\"),\r\n appDescription: getStringOption(parsed.options, \"description\"),\r\n yes: Boolean(parsed.options.yes),\r\n force: Boolean(parsed.options.force),\r\n skipInstall: Boolean(parsed.options[\"skip-install\"]),\r\n skipGit: Boolean(parsed.options[\"skip-git\"]),\r\n };\r\n}\r\n\r\nfunction getStringOption(\r\n options: Record<string, string | boolean>,\r\n key: string\r\n): string | undefined {\r\n const value = options[key];\r\n return typeof value === \"string\" ? value : undefined;\r\n}\r\n\r\nfunction printHelp(): void {\r\n console.log(\"m5kdev\");\r\n console.log(\"\");\r\n console.log(\"Usage:\");\r\n console.log(\" pnpm create m5kdev [directory] [options]\");\r\n console.log(\" m5kdev [directory] [options]\");\r\n console.log(\" m5kdev create [directory] [options]\");\r\n console.log(\"\");\r\n console.log(\"Options:\");\r\n console.log(\" --name <value> Set the app name\");\r\n console.log(\" --description <value> Set the app description\");\r\n console.log(\" --yes Accept defaults for missing prompts\");\r\n console.log(\" --force Allow writing into a non-empty directory\");\r\n console.log(\" --skip-install Skip pnpm install\");\r\n console.log(\" --skip-git Skip git init\");\r\n}\r\n"],"mappings":";;AAUA,eAAsB,OAAO,MAAwC;CACnE,MAAM,SAAS,SAAS,KAAK;AAE7B,KACE,CAAC,OAAO,WACR,OAAO,QACP,OAAO,YAAY,YACnB,OAAO,YAAY,QACnB,OAAO,YAAY,QACnB;AACA,aAAW;AACX;;AAGF,KAAI,OAAO,YAAY,SACrB,OAAM,IAAI,MAAM,oBAAoB,OAAO,UAAU;CAGvD,MAAM,UAAU,uBAAuB,OAAO;CAC9C,MAAM,SAAS,MAAM,gBAAgB,QAAQ;AAE7C,SAAQ,IAAI,GAAG;AACf,SAAQ,IAAI,cAAc,OAAO,QAAQ,QAAQ,MAAM,OAAO,kBAAkB;AAChF,SAAQ,IAAI,GAAG;AACf,SAAQ,IAAI,cAAc;AAC1B,SAAQ,IAAI,QAAQ,OAAO,kBAAkB;AAC7C,KAAI,QAAQ,YACV,SAAQ,IAAI,iBAAiB;AAE/B,SAAQ,IAAI,qCAAqC;AACjD,SAAQ,IAAI,aAAa;;AAG3B,SAAgB,SAAS,MAAoC;CAE3D,MAAM,CAAC,SAAS,GAAG,QADI,iBAAiB,KAAK;CAE7C,MAAM,UAA4C,EAAE;CACpD,IAAI;CACJ,IAAI,OAAO;AAEX,MAAK,IAAI,QAAQ,GAAG,QAAQ,KAAK,QAAQ,SAAS,GAAG;EACnD,MAAM,QAAQ,KAAK;AAEnB,MAAI,UAAU,YAAY,UAAU,MAAM;AACxC,UAAO;AACP;;AAGF,MAAI,CAAC,MAAM,WAAW,KAAK,EAAE;AAC3B,iBAAc;AACd;;EAGF,MAAM,CAAC,QAAQ,eAAe,MAAM,MAAM,EAAE,CAAC,MAAM,KAAK,EAAE;AAE1D,MAAI,cAAc,OAAO,EAAE;AACzB,WAAQ,UAAU;AAClB;;EAGF,MAAM,YAAY,eAAe,KAAK,QAAQ;AAC9C,MAAI,CAAC,aAAa,UAAU,WAAW,KAAK,CAC1C,OAAM,IAAI,MAAM,uBAAuB,SAAS;AAGlD,UAAQ,UAAU;AAClB,MAAI,gBAAgB,KAAA,EAClB,UAAS;;AAIb,QAAO;EAAE;EAAS;EAAW;EAAM;EAAS;;AAG9C,SAAS,iBAAiB,MAA4C;CACpE,MAAM,CAAC,cAAc;AAErB,KAAI,CAAC,cAAc,eAAe,YAAY,eAAe,YAAY,eAAe,KACtF,QAAO;AAGT,KAAI,eAAe,OACjB,QAAO,CAAC,SAAS;AAGnB,QAAO,CAAC,UAAU,GAAG,KAAK;;AAG5B,SAAS,cAAc,OAAwB;AAC7C,QAAO;EAAC;EAAO;EAAS;EAAgB;EAAW,CAAC,SAAS,MAAM;;AAGrE,SAAS,uBAAuB,QAAyC;AACvE,QAAO;EACL,iBAAiB,OAAO;EACxB,SAAS,gBAAgB,OAAO,SAAS,OAAO;EAChD,gBAAgB,gBAAgB,OAAO,SAAS,cAAc;EAC9D,KAAK,QAAQ,OAAO,QAAQ,IAAI;EAChC,OAAO,QAAQ,OAAO,QAAQ,MAAM;EACpC,aAAa,QAAQ,OAAO,QAAQ,gBAAgB;EACpD,SAAS,QAAQ,OAAO,QAAQ,YAAY;EAC7C;;AAGH,SAAS,gBACP,SACA,KACoB;CACpB,MAAM,QAAQ,QAAQ;AACtB,QAAO,OAAO,UAAU,WAAW,QAAQ,KAAA;;AAG7C,SAAS,YAAkB;AACzB,SAAQ,IAAI,SAAS;AACrB,SAAQ,IAAI,GAAG;AACf,SAAQ,IAAI,SAAS;AACrB,SAAQ,IAAI,6CAA6C;AACzD,SAAQ,IAAI,iCAAiC;AAC7C,SAAQ,IAAI,wCAAwC;AACpD,SAAQ,IAAI,GAAG;AACf,SAAQ,IAAI,WAAW;AACvB,SAAQ,IAAI,8CAA8C;AAC1D,SAAQ,IAAI,qDAAqD;AACjE,SAAQ,IAAI,iEAAiE;AAC7E,SAAQ,IAAI,sEAAsE;AAClF,SAAQ,IAAI,+CAA+C;AAC3D,SAAQ,IAAI,2CAA2C"}
@@ -1 +0,0 @@
1
- export {};
@@ -1,117 +0,0 @@
1
- import {
2
- Body,
3
- Button,
4
- Container,
5
- Head,
6
- Heading,
7
- Hr,
8
- Html,
9
- Preview,
10
- Section,
11
- Text,
12
- } from "@react-email/components";
13
- import type { ReactNode } from "react";
14
-
15
- interface BaseEmailProps {
16
- previewText: string;
17
- title: string;
18
- eyebrow: string;
19
- body: ReactNode;
20
- ctaLabel?: string;
21
- ctaUrl?: string;
22
- }
23
-
24
- export function BaseEmail({ previewText, title, eyebrow, body, ctaLabel, ctaUrl }: BaseEmailProps) {
25
- return (
26
- <Html>
27
- <Head />
28
- <Preview>{previewText}</Preview>
29
- <Body
30
- style={{
31
- backgroundColor: "#f3efe6",
32
- fontFamily: "'Manrope', 'Helvetica Neue', Arial, sans-serif",
33
- margin: "0",
34
- padding: "32px 0",
35
- }}
36
- >
37
- <Container
38
- style={{
39
- backgroundColor: "#fffaf1",
40
- border: "1px solid #d6c7ab",
41
- borderRadius: "24px",
42
- margin: "0 auto",
43
- maxWidth: "620px",
44
- padding: "40px",
45
- }}
46
- >
47
- <Text
48
- style={{
49
- color: "#8b5e34",
50
- fontSize: "12px",
51
- fontWeight: "700",
52
- letterSpacing: "0.18em",
53
- margin: "0 0 16px",
54
- textTransform: "uppercase",
55
- }}
56
- >
57
- {eyebrow}
58
- </Text>
59
- <Heading
60
- style={{
61
- color: "#171314",
62
- fontFamily: "'Cormorant Garamond', Georgia, serif",
63
- fontSize: "34px",
64
- lineHeight: "1.1",
65
- margin: "0 0 20px",
66
- }}
67
- >
68
- {title}
69
- </Heading>
70
- <Section
71
- style={{
72
- color: "#463630",
73
- fontSize: "16px",
74
- lineHeight: "1.7",
75
- }}
76
- >
77
- {body}
78
- </Section>
79
- {ctaLabel && ctaUrl ? (
80
- <Section style={{ marginTop: "28px" }}>
81
- <Button
82
- href={ctaUrl}
83
- style={{
84
- backgroundColor: "#1f4f46",
85
- borderRadius: "999px",
86
- color: "#fdf9f1",
87
- fontSize: "15px",
88
- fontWeight: "700",
89
- padding: "14px 24px",
90
- textDecoration: "none",
91
- }}
92
- >
93
- {ctaLabel}
94
- </Button>
95
- </Section>
96
- ) : null}
97
- <Hr
98
- style={{
99
- borderColor: "#e5dccd",
100
- margin: "28px 0",
101
- }}
102
- />
103
- <Text
104
- style={{
105
- color: "#7f6b5d",
106
- fontSize: "13px",
107
- lineHeight: "1.6",
108
- margin: "0",
109
- }}
110
- >
111
- Sent by {{APP_NAME}}.
112
- </Text>
113
- </Container>
114
- </Body>
115
- </Html>
116
- );
117
- }
File without changes