create-next-pro-cli 0.1.26 → 0.1.27
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.
- package/README.md +205 -292
- package/create-next-pro-completion.sh +8 -38
- package/create-next-pro-completion.zsh +13 -0
- package/dist/bin.bun.js +1127 -628
- package/dist/bin.node.js +1170 -617
- package/dist/bin.node.js.map +1 -1
- package/package.json +49 -27
- package/templates/Projects/default/.env.example +17 -0
- package/templates/Projects/default/.github/workflows/quality.yml +24 -0
- package/templates/Projects/default/.gitignore.template +47 -0
- package/templates/Projects/default/.prettierignore +3 -0
- package/templates/Projects/default/README.md +52 -108
- package/templates/Projects/default/bun.lock +1152 -0
- package/templates/Projects/default/eslint.config.mjs +27 -11
- package/templates/Projects/default/messages/en/_global_ui.json +23 -10
- package/templates/Projects/default/messages/en.ts +17 -2
- package/templates/Projects/default/messages/fr/_global_ui.json +23 -10
- package/templates/Projects/default/messages/fr.ts +17 -2
- package/templates/Projects/default/next.config.ts +43 -3
- package/templates/Projects/default/package.json +42 -24
- package/templates/Projects/default/playwright.config.ts +26 -0
- package/templates/Projects/default/pnpm-workspace.yaml +5 -0
- package/templates/Projects/default/public/{cnp-logo.svg → logo.svg} +1 -1
- package/templates/Projects/default/src/app/[locale]/(public)/layout.tsx +8 -1
- package/templates/Projects/default/src/app/[locale]/(public)/login/page.tsx +8 -0
- package/templates/Projects/default/src/app/[locale]/(public)/register/page.tsx +8 -0
- package/templates/Projects/default/src/app/[locale]/(user)/dashboard/error.tsx +25 -0
- package/templates/Projects/default/src/app/[locale]/(user)/dashboard/loading.tsx +5 -0
- package/templates/Projects/default/src/app/[locale]/(user)/{Dashboard → dashboard}/page.tsx +1 -1
- package/templates/Projects/default/src/app/[locale]/(user)/layout.tsx +24 -2
- package/templates/Projects/default/src/app/[locale]/(user)/settings/loading.tsx +5 -0
- package/templates/Projects/default/src/app/[locale]/(user)/{Settings → settings}/page.tsx +1 -2
- package/templates/Projects/default/src/app/[locale]/(user)/userInfo/loading.tsx +5 -0
- package/templates/Projects/default/src/app/[locale]/(user)/{UserInfo → userInfo}/page.tsx +1 -2
- package/templates/Projects/default/src/app/[locale]/layout.tsx +34 -10
- package/templates/Projects/default/src/app/[locale]/loading.tsx +0 -9
- package/templates/Projects/default/src/app/[locale]/not-found.tsx +6 -15
- package/templates/Projects/default/src/app/[locale]/page.tsx +10 -1
- package/templates/Projects/default/src/app/api/auth/[...nextauth]/route.ts +8 -60
- package/templates/Projects/default/src/app/not-found.tsx +1 -1
- package/templates/Projects/default/src/app/sitemap.ts +2 -2
- package/templates/Projects/default/src/app/styles/globals.css +166 -113
- package/templates/Projects/default/src/auth.ts +20 -0
- package/templates/Projects/default/src/config.ts +3 -3
- package/templates/Projects/default/src/env.ts +65 -0
- package/templates/Projects/default/src/lib/i18n/messages.ts +8 -0
- package/templates/Projects/default/src/lib/i18n/request.ts +2 -16
- package/templates/Projects/default/src/lib/security/csp.ts +16 -0
- package/templates/Projects/default/src/lib/utils.ts +2 -1
- package/templates/Projects/default/src/proxy.ts +13 -0
- package/templates/Projects/default/src/ui/_global/BackButton.tsx +4 -2
- package/templates/Projects/default/src/ui/_global/Button.tsx +3 -8
- package/templates/Projects/default/src/ui/_global/GlobalHeader.tsx +10 -28
- package/templates/Projects/default/src/ui/_global/GlobalMain.tsx +1 -1
- package/templates/Projects/default/src/ui/_global/LocaleSwitcher.tsx +9 -10
- package/templates/Projects/default/src/ui/_global/PublicNav.tsx +51 -17
- package/templates/Projects/default/src/ui/_global/ThemeToggle.tsx +45 -39
- package/templates/Projects/default/src/ui/_global/UserNav.tsx +6 -6
- package/templates/Projects/default/src/ui/_home/page-ui.tsx +5 -7
- package/templates/Projects/default/src/ui/{Dashboard → dashboard}/LogoutButton.tsx +9 -7
- package/templates/Projects/default/src/ui/{Dashboard → dashboard}/StatsCard.tsx +4 -4
- package/templates/Projects/default/src/ui/{Dashboard → dashboard}/page-ui.tsx +7 -8
- package/templates/Projects/default/src/ui/login/page-ui.tsx +36 -0
- package/templates/Projects/default/src/ui/register/page-ui.tsx +38 -0
- package/templates/Projects/default/src/ui/settings/page-ui.tsx +15 -0
- package/templates/Projects/default/src/ui/userInfo/page-ui.tsx +15 -0
- package/templates/Projects/default/tailwind.config.ts +81 -1
- package/templates/Projects/default/tests/consumer/validate-template.ts +66 -0
- package/templates/Projects/default/tests/e2e/template-remediation.playwright.ts +106 -0
- package/templates/Projects/default/tests/rendering/verify-rendering.ts +56 -0
- package/templates/Projects/default/tests/unit/csp.test.ts +19 -0
- package/templates/Projects/default/tests/unit/env.test.ts +76 -0
- package/templates/Projects/default/tsconfig.json +6 -6
- package/templates/Projects/default/example.env +0 -8
- package/templates/Projects/default/messages/getMergedMessages.ts +0 -31
- package/templates/Projects/default/middleware.ts +0 -11
- package/templates/Projects/default/src/app/[locale]/(public)/Login/page.tsx +0 -6
- package/templates/Projects/default/src/app/[locale]/(public)/Register/page.tsx +0 -6
- package/templates/Projects/default/src/app/[locale]/(user)/Dashboard/error.tsx +0 -38
- package/templates/Projects/default/src/app/[locale]/(user)/Dashboard/loading.tsx +0 -10
- package/templates/Projects/default/src/app/[locale]/(user)/Settings/loading.tsx +0 -17
- package/templates/Projects/default/src/app/[locale]/(user)/UserInfo/loading.tsx +0 -17
- package/templates/Projects/default/src/app/api/auth/post-login/route.ts +0 -26
- package/templates/Projects/default/src/app/api/hello/route.ts +0 -5
- package/templates/Projects/default/src/app/layout.tsx +0 -11
- package/templates/Projects/default/src/app/page.tsx +0 -6
- package/templates/Projects/default/src/auth.config.ts +0 -0
- package/templates/Projects/default/src/lib/auth/disconnect.ts +0 -11
- package/templates/Projects/default/src/lib/auth/isConnected.ts +0 -18
- package/templates/Projects/default/src/lib/sample/example.ts +0 -3
- package/templates/Projects/default/src/lib/sample/index.ts +0 -3
- package/templates/Projects/default/src/ui/Login/page-ui.tsx +0 -22
- package/templates/Projects/default/src/ui/Register/page-ui.tsx +0 -26
- package/templates/Projects/default/src/ui/Settings/page-ui.tsx +0 -17
- package/templates/Projects/default/src/ui/UserInfo/page-ui.tsx +0 -17
- /package/templates/Projects/default/messages/en/{Dashboard.json → dashboard.json} +0 -0
- /package/templates/Projects/default/messages/en/{Login.json → login.json} +0 -0
- /package/templates/Projects/default/messages/en/{Register.json → register.json} +0 -0
- /package/templates/Projects/default/messages/en/{Settings.json → settings.json} +0 -0
- /package/templates/Projects/default/messages/en/{UserInfo.json → userInfo.json} +0 -0
- /package/templates/Projects/default/messages/fr/{Dashboard.json → dashboard.json} +0 -0
- /package/templates/Projects/default/messages/fr/{Login.json → login.json} +0 -0
- /package/templates/Projects/default/messages/fr/{Register.json → register.json} +0 -0
- /package/templates/Projects/default/messages/fr/{Settings.json → settings.json} +0 -0
- /package/templates/Projects/default/messages/fr/{UserInfo.json → userInfo.json} +0 -0
- /package/templates/Projects/default/public/{cnp-logo.png → logo.png} +0 -0
- /package/templates/Projects/default/src/ui/{Dashboard → dashboard}/WelcomeCard.tsx +0 -0
package/dist/bin.bun.js
CHANGED
|
@@ -5,21 +5,35 @@ var __getProtoOf = Object.getPrototypeOf;
|
|
|
5
5
|
var __defProp = Object.defineProperty;
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
function __accessProp(key) {
|
|
9
|
+
return this[key];
|
|
10
|
+
}
|
|
11
|
+
var __toESMCache_node;
|
|
12
|
+
var __toESMCache_esm;
|
|
8
13
|
var __toESM = (mod, isNodeMode, target) => {
|
|
14
|
+
var canCache = mod != null && typeof mod === "object";
|
|
15
|
+
if (canCache) {
|
|
16
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
17
|
+
var cached = cache.get(mod);
|
|
18
|
+
if (cached)
|
|
19
|
+
return cached;
|
|
20
|
+
}
|
|
9
21
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
10
22
|
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
11
23
|
for (let key of __getOwnPropNames(mod))
|
|
12
24
|
if (!__hasOwnProp.call(to, key))
|
|
13
25
|
__defProp(to, key, {
|
|
14
|
-
get: (
|
|
26
|
+
get: __accessProp.bind(mod, key),
|
|
15
27
|
enumerable: true
|
|
16
28
|
});
|
|
29
|
+
if (canCache)
|
|
30
|
+
cache.set(mod, to);
|
|
17
31
|
return to;
|
|
18
32
|
};
|
|
19
33
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
20
34
|
var __require = import.meta.require;
|
|
21
35
|
|
|
22
|
-
// node_modules/
|
|
36
|
+
// node_modules/kleur/index.js
|
|
23
37
|
var require_kleur = __commonJS((exports, module) => {
|
|
24
38
|
var { FORCE_COLOR, NODE_DISABLE_COLORS, TERM } = process.env;
|
|
25
39
|
var $ = {
|
|
@@ -243,7 +257,7 @@ var require_clear = __commonJS((exports, module) => {
|
|
|
243
257
|
if (it)
|
|
244
258
|
o = it;
|
|
245
259
|
var i = 0;
|
|
246
|
-
var F = function
|
|
260
|
+
var F = function F2() {};
|
|
247
261
|
return { s: F, n: function n() {
|
|
248
262
|
if (i >= o.length)
|
|
249
263
|
return { done: true };
|
|
@@ -2575,7 +2589,7 @@ var require_dist = __commonJS((exports, module) => {
|
|
|
2575
2589
|
if (it)
|
|
2576
2590
|
o = it;
|
|
2577
2591
|
var i = 0;
|
|
2578
|
-
var F = function
|
|
2592
|
+
var F = function F2() {};
|
|
2579
2593
|
return { s: F, n: function n() {
|
|
2580
2594
|
if (i >= o.length)
|
|
2581
2595
|
return { done: true };
|
|
@@ -2678,7 +2692,7 @@ In order to be iterable, non-array objects must have a [Symbol.iterator]() metho
|
|
|
2678
2692
|
}
|
|
2679
2693
|
return question2.format ? yield question2.format(answer2, answers) : answer2;
|
|
2680
2694
|
});
|
|
2681
|
-
return function
|
|
2695
|
+
return function getFormattedAnswer2(_x, _x2) {
|
|
2682
2696
|
return _ref.apply(this, arguments);
|
|
2683
2697
|
};
|
|
2684
2698
|
}();
|
|
@@ -4937,17 +4951,211 @@ var require_prompts3 = __commonJS((exports, module) => {
|
|
|
4937
4951
|
});
|
|
4938
4952
|
|
|
4939
4953
|
// src/index.ts
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4954
|
+
import path8 from "path";
|
|
4955
|
+
|
|
4956
|
+
// src/cli/onboarding.ts
|
|
4943
4957
|
import path from "path";
|
|
4944
|
-
|
|
4945
|
-
|
|
4958
|
+
function configDirectory(context) {
|
|
4959
|
+
return context.env.XDG_CONFIG_HOME ? path.join(context.env.XDG_CONFIG_HOME, "create-next-pro") : path.join(context.homeDir, ".config", "create-next-pro");
|
|
4960
|
+
}
|
|
4961
|
+
function configFile(context) {
|
|
4962
|
+
return path.join(configDirectory(context), "config.json");
|
|
4963
|
+
}
|
|
4964
|
+
async function readConfig(context) {
|
|
4965
|
+
try {
|
|
4966
|
+
return JSON.parse(await context.fs.readText(configFile(context)));
|
|
4967
|
+
} catch {
|
|
4968
|
+
return null;
|
|
4969
|
+
}
|
|
4970
|
+
}
|
|
4971
|
+
async function ensureLineInRc(context, target, line) {
|
|
4972
|
+
try {
|
|
4973
|
+
const current = context.fs.exists(target) ? await context.fs.readText(target) : "";
|
|
4974
|
+
if (!current.includes(line))
|
|
4975
|
+
await context.fs.appendText(target, `
|
|
4976
|
+
${line}
|
|
4977
|
+
`);
|
|
4978
|
+
} catch {}
|
|
4979
|
+
}
|
|
4980
|
+
async function installCompletion(context, shell) {
|
|
4981
|
+
const directory = configDirectory(context);
|
|
4982
|
+
const source = path.join(context.packageRoot, shell === "zsh" ? "create-next-pro-completion.zsh" : "create-next-pro-completion.sh");
|
|
4983
|
+
const target = path.join(directory, `completion.${shell === "zsh" ? "zsh" : "sh"}`);
|
|
4984
|
+
await context.fs.mkdir(directory);
|
|
4985
|
+
await context.fs.copyFile(source, target);
|
|
4986
|
+
const rcFile = path.join(context.homeDir, shell === "zsh" ? ".zshrc" : ".bashrc");
|
|
4987
|
+
await ensureLineInRc(context, rcFile, `source "${target}"`);
|
|
4988
|
+
}
|
|
4989
|
+
async function onboarding(context, version) {
|
|
4990
|
+
context.terminal.log(`\uD83D\uDE80 Welcome to create-next-pro v${version}
|
|
4991
|
+
`);
|
|
4992
|
+
const response = await context.prompt([
|
|
4993
|
+
{
|
|
4994
|
+
type: "select",
|
|
4995
|
+
name: "shell",
|
|
4996
|
+
message: "Which shell do you use?",
|
|
4997
|
+
choices: [
|
|
4998
|
+
{ title: "zsh", value: "zsh" },
|
|
4999
|
+
{ title: "bash", value: "bash" }
|
|
5000
|
+
],
|
|
5001
|
+
initial: context.env.SHELL?.includes("zsh") ? 0 : 1
|
|
5002
|
+
},
|
|
5003
|
+
{
|
|
5004
|
+
type: "toggle",
|
|
5005
|
+
name: "completion",
|
|
5006
|
+
message: "Install autocompletion?",
|
|
5007
|
+
initial: true,
|
|
5008
|
+
active: "Yes",
|
|
5009
|
+
inactive: "No"
|
|
5010
|
+
}
|
|
5011
|
+
], { onCancel: () => false });
|
|
5012
|
+
if (response.shell !== "bash" && response.shell !== "zsh") {
|
|
5013
|
+
throw new Error("Configuration cancelled.");
|
|
5014
|
+
}
|
|
5015
|
+
const now = new Date().toISOString();
|
|
5016
|
+
const config = {
|
|
5017
|
+
version: 1,
|
|
5018
|
+
shell: response.shell,
|
|
5019
|
+
completionInstalled: Boolean(response.completion),
|
|
5020
|
+
createdAt: now,
|
|
5021
|
+
updatedAt: now
|
|
5022
|
+
};
|
|
5023
|
+
if (config.completionInstalled)
|
|
5024
|
+
await installCompletion(context, config.shell);
|
|
5025
|
+
await context.fs.mkdir(configDirectory(context));
|
|
5026
|
+
await context.fs.writeText(configFile(context), JSON.stringify(config, null, 2));
|
|
5027
|
+
context.terminal.log(`
|
|
5028
|
+
\u2705 Configuration saved.`);
|
|
5029
|
+
context.terminal.log("you can now use the CLI ! ex : ");
|
|
5030
|
+
context.terminal.log(" Without prompt (will change in future) :");
|
|
5031
|
+
context.terminal.log(" create-next-pro my-next-project");
|
|
5032
|
+
context.terminal.log(" With prompt :");
|
|
5033
|
+
context.terminal.log(" create-next-pro");
|
|
5034
|
+
context.terminal.log("For more information, visit: https://github.com/Rising-Corporation/create-next-pro-cli");
|
|
5035
|
+
context.terminal.log("Happy coding! \uD83C\uDF89");
|
|
5036
|
+
return config;
|
|
5037
|
+
}
|
|
4946
5038
|
|
|
4947
|
-
// src/
|
|
4948
|
-
|
|
5039
|
+
// src/cli/completion.ts
|
|
5040
|
+
import { readdir as readdir2 } from "fs/promises";
|
|
5041
|
+
import path3 from "path";
|
|
5042
|
+
|
|
5043
|
+
// src/core/page-catalog.ts
|
|
5044
|
+
import { readdir } from "fs/promises";
|
|
5045
|
+
import path2 from "path";
|
|
5046
|
+
function isRouteGroup(segment) {
|
|
5047
|
+
return segment.startsWith("(") && segment.endsWith(")");
|
|
5048
|
+
}
|
|
5049
|
+
async function discoverPages(projectRoot) {
|
|
5050
|
+
const appRoot = path2.join(projectRoot, "src", "app", "[locale]");
|
|
5051
|
+
const candidates = [];
|
|
5052
|
+
async function visit(directory, relative = []) {
|
|
5053
|
+
let entries;
|
|
5054
|
+
try {
|
|
5055
|
+
entries = await readdir(directory, { withFileTypes: true });
|
|
5056
|
+
} catch {
|
|
5057
|
+
return;
|
|
5058
|
+
}
|
|
5059
|
+
if (entries.some((entry) => entry.isFile() && entry.name === "page.tsx")) {
|
|
5060
|
+
const routeSegments = relative.filter((segment) => !isRouteGroup(segment));
|
|
5061
|
+
if (routeSegments.length > 0 && !routeSegments.some((segment) => segment.startsWith("_") || segment.startsWith("["))) {
|
|
5062
|
+
const logicalName = routeSegments.join(".");
|
|
5063
|
+
candidates.push({
|
|
5064
|
+
logicalName,
|
|
5065
|
+
routeSegments,
|
|
5066
|
+
routeDirectory: directory,
|
|
5067
|
+
uiDirectory: path2.join(projectRoot, "src", "ui", ...routeSegments),
|
|
5068
|
+
messageFile: path2.join(projectRoot, "messages", "{locale}", `${routeSegments[0]}.json`),
|
|
5069
|
+
messageKey: routeSegments.length > 1 ? routeSegments.at(-1) : undefined
|
|
5070
|
+
});
|
|
5071
|
+
}
|
|
5072
|
+
}
|
|
5073
|
+
for (const entry of entries) {
|
|
5074
|
+
if (entry.isDirectory() && !entry.name.startsWith(".")) {
|
|
5075
|
+
await visit(path2.join(directory, entry.name), [
|
|
5076
|
+
...relative,
|
|
5077
|
+
entry.name
|
|
5078
|
+
]);
|
|
5079
|
+
}
|
|
5080
|
+
}
|
|
5081
|
+
}
|
|
5082
|
+
await visit(appRoot);
|
|
5083
|
+
return candidates.sort((left, right) => left.logicalName.localeCompare(right.logicalName));
|
|
5084
|
+
}
|
|
5085
|
+
|
|
5086
|
+
// src/cli/completion.ts
|
|
5087
|
+
var PUBLIC_COMMANDS = [
|
|
5088
|
+
"addpage",
|
|
5089
|
+
"addcomponent",
|
|
5090
|
+
"addlib",
|
|
5091
|
+
"addapi",
|
|
5092
|
+
"addlanguage",
|
|
5093
|
+
"addtext",
|
|
5094
|
+
"rmpage",
|
|
5095
|
+
"--help",
|
|
5096
|
+
"--version",
|
|
5097
|
+
"--reconfigure"
|
|
5098
|
+
];
|
|
5099
|
+
var OPTIONS = {
|
|
5100
|
+
addpage: [
|
|
5101
|
+
"--layout",
|
|
5102
|
+
"--page",
|
|
5103
|
+
"--loading",
|
|
5104
|
+
"--not-found",
|
|
5105
|
+
"--error",
|
|
5106
|
+
"--global-error",
|
|
5107
|
+
"--route",
|
|
5108
|
+
"--template",
|
|
5109
|
+
"--default"
|
|
5110
|
+
],
|
|
5111
|
+
addcomponent: ["--page", "-P"]
|
|
5112
|
+
};
|
|
5113
|
+
async function directories(root) {
|
|
5114
|
+
try {
|
|
5115
|
+
return (await readdir2(root, { withFileTypes: true })).filter((entry) => entry.isDirectory() && !entry.name.startsWith("_")).map((entry) => entry.name).sort();
|
|
5116
|
+
} catch {
|
|
5117
|
+
return [];
|
|
5118
|
+
}
|
|
5119
|
+
}
|
|
5120
|
+
async function completionCandidates(command, context) {
|
|
5121
|
+
if (!command)
|
|
5122
|
+
return [...PUBLIC_COMMANDS];
|
|
5123
|
+
if (command === "rmpage") {
|
|
5124
|
+
return (await discoverPages(context.cwd)).map((candidate) => candidate.logicalName);
|
|
5125
|
+
}
|
|
5126
|
+
if (command === "addcomponent" || command === "addpage") {
|
|
5127
|
+
return [
|
|
5128
|
+
...OPTIONS[command] ?? [],
|
|
5129
|
+
...await directories(path3.join(context.cwd, "src", "ui"))
|
|
5130
|
+
];
|
|
5131
|
+
}
|
|
5132
|
+
if (command === "addlanguage")
|
|
5133
|
+
return ["de", "en", "es", "fr", "it", "ja", "pt"];
|
|
5134
|
+
return OPTIONS[command] ?? [];
|
|
5135
|
+
}
|
|
5136
|
+
async function printCompletions(args, context) {
|
|
5137
|
+
for (const candidate of await completionCandidates(args[1], context)) {
|
|
5138
|
+
context.terminal.log(candidate);
|
|
5139
|
+
}
|
|
5140
|
+
}
|
|
5141
|
+
|
|
5142
|
+
// src/core/contracts.ts
|
|
5143
|
+
var success = () => ({ exitCode: 0 });
|
|
5144
|
+
|
|
5145
|
+
class CliError extends Error {
|
|
5146
|
+
exitCode;
|
|
5147
|
+
constructor(message, exitCode = 1) {
|
|
5148
|
+
super(message);
|
|
5149
|
+
this.exitCode = exitCode;
|
|
5150
|
+
this.name = "CliError";
|
|
5151
|
+
}
|
|
5152
|
+
}
|
|
5153
|
+
|
|
5154
|
+
// src/lib/addApi.ts
|
|
5155
|
+
var import_prompts2 = __toESM(require_prompts3(), 1);
|
|
4949
5156
|
import { join as join2 } from "path";
|
|
4950
|
-
import { mkdir, readFile as
|
|
5157
|
+
import { mkdir as mkdir2, readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
|
|
5158
|
+
import { existsSync as existsSync3 } from "fs";
|
|
4951
5159
|
|
|
4952
5160
|
// src/lib/utils.ts
|
|
4953
5161
|
import { readFile } from "fs/promises";
|
|
@@ -4956,8 +5164,12 @@ import { join } from "path";
|
|
|
4956
5164
|
function capitalize(str) {
|
|
4957
5165
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
4958
5166
|
}
|
|
4959
|
-
|
|
4960
|
-
const
|
|
5167
|
+
function configuredAliasPrefix(config) {
|
|
5168
|
+
const alias = config.importAlias ?? "@/*";
|
|
5169
|
+
return alias.endsWith("/*") ? alias.slice(0, -2) : "@";
|
|
5170
|
+
}
|
|
5171
|
+
async function loadConfig(cwd = process.cwd()) {
|
|
5172
|
+
const configPath = join(cwd, "cnp.config.json");
|
|
4961
5173
|
if (!existsSync(configPath))
|
|
4962
5174
|
return null;
|
|
4963
5175
|
try {
|
|
@@ -4992,21 +5204,193 @@ function toFileName(key) {
|
|
|
4992
5204
|
}
|
|
4993
5205
|
}
|
|
4994
5206
|
|
|
5207
|
+
// src/runtime/node-context.ts
|
|
5208
|
+
var import_prompts = __toESM(require_prompts3(), 1);
|
|
5209
|
+
import { existsSync as existsSync2 } from "fs";
|
|
5210
|
+
import {
|
|
5211
|
+
appendFile,
|
|
5212
|
+
copyFile,
|
|
5213
|
+
mkdir,
|
|
5214
|
+
readFile as readFile2,
|
|
5215
|
+
writeFile
|
|
5216
|
+
} from "fs/promises";
|
|
5217
|
+
import os from "os";
|
|
5218
|
+
import path4 from "path";
|
|
5219
|
+
import { fileURLToPath } from "url";
|
|
5220
|
+
function findPackageRoot(start) {
|
|
5221
|
+
let current = start;
|
|
5222
|
+
while (true) {
|
|
5223
|
+
const packagePath = path4.join(current, "package.json");
|
|
5224
|
+
if (existsSync2(packagePath))
|
|
5225
|
+
return current;
|
|
5226
|
+
const parent = path4.dirname(current);
|
|
5227
|
+
if (parent === current) {
|
|
5228
|
+
throw new Error(`Unable to locate package.json from ${start}`);
|
|
5229
|
+
}
|
|
5230
|
+
current = parent;
|
|
5231
|
+
}
|
|
5232
|
+
}
|
|
5233
|
+
function resolvePackageRoot(metaUrl = import.meta.url) {
|
|
5234
|
+
return findPackageRoot(path4.dirname(fileURLToPath(metaUrl)));
|
|
5235
|
+
}
|
|
5236
|
+
function createNodeContext(overrides = {}) {
|
|
5237
|
+
return {
|
|
5238
|
+
argv: process.argv.slice(2),
|
|
5239
|
+
cwd: process.cwd(),
|
|
5240
|
+
env: process.env,
|
|
5241
|
+
homeDir: os.homedir(),
|
|
5242
|
+
packageRoot: resolvePackageRoot(),
|
|
5243
|
+
terminal: console,
|
|
5244
|
+
prompt: import_prompts.default,
|
|
5245
|
+
fs: {
|
|
5246
|
+
exists: existsSync2,
|
|
5247
|
+
readText: (target) => readFile2(target, "utf8"),
|
|
5248
|
+
writeText: async (target, content) => {
|
|
5249
|
+
await writeFile(target, content);
|
|
5250
|
+
},
|
|
5251
|
+
mkdir: async (target) => {
|
|
5252
|
+
await mkdir(target, { recursive: true });
|
|
5253
|
+
},
|
|
5254
|
+
copyFile: async (source, target) => {
|
|
5255
|
+
await copyFile(source, target);
|
|
5256
|
+
},
|
|
5257
|
+
appendText: async (target, content) => {
|
|
5258
|
+
await appendFile(target, content);
|
|
5259
|
+
}
|
|
5260
|
+
},
|
|
5261
|
+
...overrides
|
|
5262
|
+
};
|
|
5263
|
+
}
|
|
5264
|
+
|
|
5265
|
+
// src/core/project-paths.ts
|
|
5266
|
+
import path5 from "path";
|
|
5267
|
+
import { lstat } from "fs/promises";
|
|
5268
|
+
var SAFE_SEGMENT = /^[A-Za-z][A-Za-z0-9_-]*$/;
|
|
5269
|
+
function hasControlCharacters(value) {
|
|
5270
|
+
return [...value].some((character) => {
|
|
5271
|
+
const code = character.charCodeAt(0);
|
|
5272
|
+
return code <= 31 || code === 127;
|
|
5273
|
+
});
|
|
5274
|
+
}
|
|
5275
|
+
function parseLogicalName(value, label = "name") {
|
|
5276
|
+
if (!value || hasControlCharacters(value)) {
|
|
5277
|
+
throw new CliError(`Invalid ${label}: a non-empty printable value is required.`);
|
|
5278
|
+
}
|
|
5279
|
+
if (path5.isAbsolute(value) || value.includes("/") || value.includes("\\")) {
|
|
5280
|
+
throw new CliError(`Invalid ${label}: paths and separators are not allowed.`);
|
|
5281
|
+
}
|
|
5282
|
+
const segments = value.split(".");
|
|
5283
|
+
if (segments.some((segment) => !SAFE_SEGMENT.test(segment))) {
|
|
5284
|
+
throw new CliError(`Invalid ${label}: use dot-separated alphanumeric segments beginning with a letter.`);
|
|
5285
|
+
}
|
|
5286
|
+
return segments;
|
|
5287
|
+
}
|
|
5288
|
+
function validateProjectName(value) {
|
|
5289
|
+
if (!value || hasControlCharacters(value) || path5.isAbsolute(value) || value === "." || value === ".." || value.includes("/") || value.includes("\\") || !/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(value)) {
|
|
5290
|
+
throw new CliError("Invalid project name: use letters, numbers, dots, dashes or underscores without path separators.");
|
|
5291
|
+
}
|
|
5292
|
+
return value;
|
|
5293
|
+
}
|
|
5294
|
+
function resolveInside(root, ...segments) {
|
|
5295
|
+
const absoluteRoot = path5.resolve(root);
|
|
5296
|
+
const target = path5.resolve(absoluteRoot, ...segments);
|
|
5297
|
+
if (target !== absoluteRoot && !target.startsWith(`${absoluteRoot}${path5.sep}`)) {
|
|
5298
|
+
throw new CliError(`Refusing to access a path outside the project: ${target}`);
|
|
5299
|
+
}
|
|
5300
|
+
return target;
|
|
5301
|
+
}
|
|
5302
|
+
async function assertSafeTarget(root, target) {
|
|
5303
|
+
const safeTarget = resolveInside(root, path5.relative(root, target));
|
|
5304
|
+
const relativeSegments = path5.relative(path5.resolve(root), safeTarget).split(path5.sep);
|
|
5305
|
+
let current = path5.resolve(root);
|
|
5306
|
+
for (const segment of relativeSegments) {
|
|
5307
|
+
if (!segment)
|
|
5308
|
+
continue;
|
|
5309
|
+
current = path5.join(current, segment);
|
|
5310
|
+
try {
|
|
5311
|
+
if ((await lstat(current)).isSymbolicLink()) {
|
|
5312
|
+
throw new CliError(`Symbolic links are forbidden in project paths: ${current}`);
|
|
5313
|
+
}
|
|
5314
|
+
} catch (error) {
|
|
5315
|
+
if (error instanceof CliError)
|
|
5316
|
+
throw error;
|
|
5317
|
+
if (error.code !== "ENOENT")
|
|
5318
|
+
throw error;
|
|
5319
|
+
}
|
|
5320
|
+
}
|
|
5321
|
+
return safeTarget;
|
|
5322
|
+
}
|
|
5323
|
+
function normalizeImportAlias(value) {
|
|
5324
|
+
if (!/^[A-Za-z@~][A-Za-z0-9@~_-]*\/\*$/.test(value)) {
|
|
5325
|
+
throw new CliError('Invalid import alias: expected a prefix followed by "/*" (for example "@/*" or "@core/*").');
|
|
5326
|
+
}
|
|
5327
|
+
return value;
|
|
5328
|
+
}
|
|
5329
|
+
|
|
5330
|
+
// src/lib/addApi.ts
|
|
5331
|
+
async function addApi(args, cwd = process.cwd()) {
|
|
5332
|
+
let apiName = args[1];
|
|
5333
|
+
if (!apiName || apiName.startsWith("-")) {
|
|
5334
|
+
const response = await import_prompts2.default.prompt({
|
|
5335
|
+
type: "text",
|
|
5336
|
+
name: "apiName",
|
|
5337
|
+
message: "\uD83D\uDD0C API route name to add:",
|
|
5338
|
+
validate: (name) => name ? true : "API route name is required"
|
|
5339
|
+
});
|
|
5340
|
+
apiName = response.apiName;
|
|
5341
|
+
}
|
|
5342
|
+
const apiSegments = parseLogicalName(apiName, "API route name");
|
|
5343
|
+
const config = await loadConfig(cwd);
|
|
5344
|
+
if (!config) {
|
|
5345
|
+
console.error("\u274C Configuration file cnp.config.json not found. Run this command from the project root.");
|
|
5346
|
+
return;
|
|
5347
|
+
}
|
|
5348
|
+
const apiDir = join2(cwd, "src", "app", "api", ...apiSegments);
|
|
5349
|
+
await assertSafeTarget(cwd, apiDir);
|
|
5350
|
+
if (!existsSync3(apiDir)) {
|
|
5351
|
+
await mkdir2(apiDir, { recursive: true });
|
|
5352
|
+
}
|
|
5353
|
+
const templateDir = join2(resolvePackageRoot(import.meta.url), "templates", "Api");
|
|
5354
|
+
const routeTemplate = join2(templateDir, "route.ts");
|
|
5355
|
+
const routePath = join2(apiDir, "route.ts");
|
|
5356
|
+
if (!existsSync3(routePath)) {
|
|
5357
|
+
if (existsSync3(routeTemplate)) {
|
|
5358
|
+
let content = await readFile3(routeTemplate, "utf-8");
|
|
5359
|
+
content = content.replace(/template/g, apiName);
|
|
5360
|
+
await writeFile2(routePath, content);
|
|
5361
|
+
} else {
|
|
5362
|
+
await writeFile2(routePath, `import { NextResponse } from "next/server";
|
|
5363
|
+
|
|
5364
|
+
export async function GET() {
|
|
5365
|
+
return NextResponse.json({ message: "Hello from ${apiName}" });
|
|
5366
|
+
}
|
|
5367
|
+
`);
|
|
5368
|
+
}
|
|
5369
|
+
console.log(`\uD83D\uDCC4 File created: ${routePath}`);
|
|
5370
|
+
} else {
|
|
5371
|
+
console.log(`\u2139\uFE0F File already exists: ${routePath}`);
|
|
5372
|
+
}
|
|
5373
|
+
console.log(`\u2705 API route "${apiName}" added.`);
|
|
5374
|
+
}
|
|
5375
|
+
|
|
4995
5376
|
// src/lib/addComponent.ts
|
|
4996
|
-
|
|
4997
|
-
|
|
5377
|
+
var import_prompts3 = __toESM(require_prompts3(), 1);
|
|
5378
|
+
import { join as join3 } from "path";
|
|
5379
|
+
import { mkdir as mkdir3, readFile as readFile4, writeFile as writeFile3, readdir as readdir3 } from "fs/promises";
|
|
5380
|
+
import { existsSync as existsSync4, statSync } from "fs";
|
|
5381
|
+
async function addComponent(args, cwd = process.cwd()) {
|
|
4998
5382
|
let componentName = args[1];
|
|
4999
5383
|
let pageScope = null;
|
|
5000
|
-
|
|
5384
|
+
const pageIndex = args.findIndex((arg) => arg === "-P" || arg === "--page");
|
|
5001
5385
|
if (pageIndex !== -1 && args[pageIndex + 1]) {
|
|
5002
5386
|
pageScope = args[pageIndex + 1];
|
|
5003
5387
|
}
|
|
5004
5388
|
let nestedPath = null;
|
|
5005
5389
|
if (pageScope && pageScope.includes(".")) {
|
|
5006
|
-
nestedPath =
|
|
5390
|
+
nestedPath = join3(...pageScope.split("."));
|
|
5007
5391
|
}
|
|
5008
5392
|
if (!componentName || componentName.startsWith("-")) {
|
|
5009
|
-
const response = await
|
|
5393
|
+
const response = await import_prompts3.default.prompt({
|
|
5010
5394
|
type: "text",
|
|
5011
5395
|
name: "componentName",
|
|
5012
5396
|
message: "\uD83E\uDDE9 Component name to add:",
|
|
@@ -5014,18 +5398,21 @@ async function addComponent(args) {
|
|
|
5014
5398
|
});
|
|
5015
5399
|
componentName = response.componentName;
|
|
5016
5400
|
}
|
|
5017
|
-
|
|
5401
|
+
parseLogicalName(componentName, "component name");
|
|
5402
|
+
if (pageScope)
|
|
5403
|
+
parseLogicalName(pageScope, "page name");
|
|
5404
|
+
const config = await loadConfig(cwd);
|
|
5018
5405
|
if (!config) {
|
|
5019
5406
|
console.error("\u274C Configuration file cnp.config.json not found. Run this command from the project root.");
|
|
5020
5407
|
return;
|
|
5021
5408
|
}
|
|
5022
5409
|
const useI18n = !!config.useI18n;
|
|
5023
5410
|
const componentNameUpper = capitalize(componentName);
|
|
5024
|
-
const templatePath =
|
|
5411
|
+
const templatePath = join3(resolvePackageRoot(import.meta.url), "templates", "Component");
|
|
5025
5412
|
let messagesPath = null;
|
|
5026
5413
|
if (useI18n) {
|
|
5027
|
-
messagesPath =
|
|
5028
|
-
if (!
|
|
5414
|
+
messagesPath = join3(cwd, "messages");
|
|
5415
|
+
if (!existsSync4(messagesPath)) {
|
|
5029
5416
|
console.error("\u274C Messages directory missing. Ensure i18n was configured.");
|
|
5030
5417
|
return;
|
|
5031
5418
|
}
|
|
@@ -5034,56 +5421,65 @@ async function addComponent(args) {
|
|
|
5034
5421
|
let translationKey;
|
|
5035
5422
|
if (pageScope) {
|
|
5036
5423
|
if (nestedPath) {
|
|
5037
|
-
componentTargetPath =
|
|
5424
|
+
componentTargetPath = join3(cwd, "src", "ui", nestedPath);
|
|
5038
5425
|
translationKey = pageScope;
|
|
5039
5426
|
} else {
|
|
5040
|
-
componentTargetPath =
|
|
5427
|
+
componentTargetPath = join3(cwd, "src", "ui", pageScope);
|
|
5041
5428
|
translationKey = pageScope;
|
|
5042
5429
|
}
|
|
5043
5430
|
} else {
|
|
5044
|
-
componentTargetPath =
|
|
5431
|
+
componentTargetPath = join3(cwd, "src", "ui", "_global");
|
|
5045
5432
|
translationKey = "_global_ui";
|
|
5046
5433
|
}
|
|
5047
|
-
|
|
5048
|
-
|
|
5434
|
+
await assertSafeTarget(cwd, componentTargetPath);
|
|
5435
|
+
if (!existsSync4(componentTargetPath)) {
|
|
5436
|
+
await mkdir3(componentTargetPath, { recursive: true });
|
|
5049
5437
|
}
|
|
5050
|
-
const componentFile =
|
|
5051
|
-
const templateComponentPath =
|
|
5052
|
-
if (
|
|
5053
|
-
let content = await
|
|
5438
|
+
const componentFile = join3(componentTargetPath, `${componentNameUpper}.tsx`);
|
|
5439
|
+
const templateComponentPath = join3(templatePath, "Component.tsx");
|
|
5440
|
+
if (existsSync4(templateComponentPath)) {
|
|
5441
|
+
let content = await readFile4(templateComponentPath, "utf-8");
|
|
5054
5442
|
content = content.replace(/Component/g, componentNameUpper).replace(/componentPage/g, translationKey);
|
|
5055
|
-
await
|
|
5443
|
+
await writeFile3(componentFile, content);
|
|
5056
5444
|
console.log(`\uD83D\uDCC4 File created: ${componentFile}`);
|
|
5057
5445
|
} else {
|
|
5058
5446
|
console.error("\u274C Template Component.tsx introuvable :", templateComponentPath);
|
|
5059
5447
|
}
|
|
5060
5448
|
if (useI18n && messagesPath) {
|
|
5061
|
-
const entries = await
|
|
5449
|
+
const entries = await readdir3(messagesPath, { withFileTypes: true });
|
|
5062
5450
|
const langDirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
5063
|
-
const jsonTemplate =
|
|
5064
|
-
if (!
|
|
5451
|
+
const jsonTemplate = join3(templatePath, "component.json");
|
|
5452
|
+
if (!existsSync4(jsonTemplate)) {
|
|
5065
5453
|
console.error("\u274C Template component.json not found:", jsonTemplate);
|
|
5066
5454
|
return;
|
|
5067
5455
|
}
|
|
5068
|
-
const jsonContent = await
|
|
5456
|
+
const jsonContent = await readFile4(jsonTemplate, "utf-8");
|
|
5069
5457
|
const parsed = JSON.parse(jsonContent);
|
|
5070
5458
|
for (const locale of langDirs) {
|
|
5071
|
-
const localeDir =
|
|
5072
|
-
if (!
|
|
5459
|
+
const localeDir = join3(messagesPath, locale);
|
|
5460
|
+
if (!existsSync4(localeDir) || !statSync(localeDir).isDirectory())
|
|
5073
5461
|
continue;
|
|
5074
5462
|
let jsonTarget;
|
|
5075
5463
|
if (pageScope) {
|
|
5076
|
-
|
|
5464
|
+
const [messageFile] = pageScope.split(".");
|
|
5465
|
+
jsonTarget = join3(messagesPath, locale, `${messageFile}.json`);
|
|
5077
5466
|
} else {
|
|
5078
|
-
jsonTarget =
|
|
5467
|
+
jsonTarget = join3(messagesPath, locale, `_global_ui.json`);
|
|
5079
5468
|
}
|
|
5080
5469
|
let current = {};
|
|
5081
|
-
if (
|
|
5082
|
-
const jsonFile = await
|
|
5470
|
+
if (existsSync4(jsonTarget)) {
|
|
5471
|
+
const jsonFile = await readFile4(jsonTarget, "utf-8");
|
|
5083
5472
|
current = JSON.parse(jsonFile);
|
|
5084
5473
|
}
|
|
5085
|
-
|
|
5086
|
-
|
|
5474
|
+
if (pageScope?.includes(".")) {
|
|
5475
|
+
const child = pageScope.split(".")[1];
|
|
5476
|
+
const childMessages = current[child] && typeof current[child] === "object" ? current[child] : {};
|
|
5477
|
+
childMessages[componentNameUpper] = parsed;
|
|
5478
|
+
current[child] = childMessages;
|
|
5479
|
+
} else {
|
|
5480
|
+
current[componentNameUpper] = parsed;
|
|
5481
|
+
}
|
|
5482
|
+
await writeFile3(jsonTarget, JSON.stringify(current, null, 2));
|
|
5087
5483
|
console.log(`\uD83D\uDCC4 File updated: ${jsonTarget}`);
|
|
5088
5484
|
}
|
|
5089
5485
|
} else {
|
|
@@ -5092,230 +5488,129 @@ async function addComponent(args) {
|
|
|
5092
5488
|
console.log(`\u2705 Component "${componentNameUpper}" added ${pageScope ? `to page ${pageScope}` : "globally"}${useI18n ? " with localized messages" : ""}.`);
|
|
5093
5489
|
}
|
|
5094
5490
|
|
|
5095
|
-
// src/lib/
|
|
5096
|
-
var
|
|
5097
|
-
import { join as
|
|
5098
|
-
import {
|
|
5099
|
-
import {
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
let childName = null;
|
|
5113
|
-
if (pageName.includes(".")) {
|
|
5114
|
-
[parentName, childName] = pageName.split(".");
|
|
5115
|
-
}
|
|
5116
|
-
let shortFlags = args.find((arg) => /^-[A-Za-z]+$/.test(arg));
|
|
5117
|
-
let longFlags = new Set(args.filter((a) => a.startsWith("--")));
|
|
5118
|
-
const flags = new Set;
|
|
5119
|
-
if (!shortFlags && Array.from(longFlags).length === 0) {
|
|
5120
|
-
shortFlags = "-LPl";
|
|
5121
|
-
}
|
|
5122
|
-
if (shortFlags) {
|
|
5123
|
-
for (const char of shortFlags.slice(1)) {
|
|
5124
|
-
switch (char) {
|
|
5125
|
-
case "L":
|
|
5126
|
-
flags.add("layout");
|
|
5127
|
-
break;
|
|
5128
|
-
case "P":
|
|
5129
|
-
flags.add("page");
|
|
5130
|
-
break;
|
|
5131
|
-
case "l":
|
|
5132
|
-
flags.add("loading");
|
|
5133
|
-
break;
|
|
5134
|
-
case "n":
|
|
5135
|
-
flags.add("not-found");
|
|
5136
|
-
break;
|
|
5137
|
-
case "e":
|
|
5138
|
-
flags.add("error");
|
|
5139
|
-
break;
|
|
5140
|
-
case "g":
|
|
5141
|
-
flags.add("global-error");
|
|
5142
|
-
break;
|
|
5143
|
-
case "r":
|
|
5144
|
-
flags.add("route");
|
|
5145
|
-
break;
|
|
5146
|
-
case "t":
|
|
5147
|
-
flags.add("template");
|
|
5148
|
-
break;
|
|
5149
|
-
case "d":
|
|
5150
|
-
flags.add("default");
|
|
5151
|
-
break;
|
|
5152
|
-
}
|
|
5491
|
+
// src/lib/addLanguage.ts
|
|
5492
|
+
var import_prompts4 = __toESM(require_prompts3(), 1);
|
|
5493
|
+
import { join as join4 } from "path";
|
|
5494
|
+
import { existsSync as existsSync5 } from "fs";
|
|
5495
|
+
import { cp, readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
|
|
5496
|
+
function generateLocales() {
|
|
5497
|
+
const dn = new Intl.DisplayNames(["en"], { type: "language" });
|
|
5498
|
+
const locales = [];
|
|
5499
|
+
for (let i = 0;i < 26; i++) {
|
|
5500
|
+
for (let j = 0;j < 26; j++) {
|
|
5501
|
+
const code = String.fromCharCode(97 + i) + String.fromCharCode(97 + j);
|
|
5502
|
+
try {
|
|
5503
|
+
const name = dn.of(code);
|
|
5504
|
+
if (name && name.toLowerCase() !== code) {
|
|
5505
|
+
locales.push(code);
|
|
5506
|
+
}
|
|
5507
|
+
} catch {}
|
|
5153
5508
|
}
|
|
5154
5509
|
}
|
|
5155
|
-
|
|
5156
|
-
|
|
5157
|
-
|
|
5158
|
-
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
"global-error",
|
|
5162
|
-
"route",
|
|
5163
|
-
"template",
|
|
5164
|
-
"default"
|
|
5165
|
-
]) {
|
|
5166
|
-
if (longFlags.has("--" + flag))
|
|
5167
|
-
flags.add(flag);
|
|
5168
|
-
}
|
|
5169
|
-
const config = await loadConfig();
|
|
5170
|
-
if (!config) {
|
|
5171
|
-
console.error("\u274C Configuration file cnp.config.json not found. Run this command from the project root.");
|
|
5510
|
+
return locales.sort();
|
|
5511
|
+
}
|
|
5512
|
+
async function addLanguage(args, cwd = process.cwd()) {
|
|
5513
|
+
const config = await loadConfig(cwd);
|
|
5514
|
+
if (!config?.useI18n) {
|
|
5515
|
+
console.error("\u274C i18n is not enabled in this project.");
|
|
5172
5516
|
return;
|
|
5173
5517
|
}
|
|
5174
|
-
const
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
srcSegments.push("[locale]");
|
|
5178
|
-
const srcPath = join3(process.cwd(), ...srcSegments);
|
|
5179
|
-
if (!existsSync3(srcPath)) {
|
|
5180
|
-
console.error(`\u274C Expected directory not found: ${srcPath}`);
|
|
5518
|
+
const messagesPath = join4(cwd, "messages");
|
|
5519
|
+
if (!existsSync5(messagesPath)) {
|
|
5520
|
+
console.error("\u274C Messages directory missing. Ensure i18n was configured.");
|
|
5181
5521
|
return;
|
|
5182
5522
|
}
|
|
5183
|
-
|
|
5184
|
-
let
|
|
5185
|
-
if (
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5523
|
+
const available = generateLocales();
|
|
5524
|
+
let locale = args[1];
|
|
5525
|
+
if (!locale || !available.includes(locale)) {
|
|
5526
|
+
const response = await import_prompts4.default({
|
|
5527
|
+
type: "autocomplete",
|
|
5528
|
+
name: "locale",
|
|
5529
|
+
message: "\uD83C\uDF10 Locale to add:",
|
|
5530
|
+
choices: available.map((l) => ({ title: l, value: l }))
|
|
5531
|
+
});
|
|
5532
|
+
locale = response.locale;
|
|
5193
5533
|
}
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
} else {
|
|
5201
|
-
uiPageDir = join3(process.cwd(), "src", "ui", pageName);
|
|
5202
|
-
localePagePath = join3(srcPath, pageName);
|
|
5203
|
-
jsonFileName = pageName;
|
|
5534
|
+
if (!locale)
|
|
5535
|
+
return;
|
|
5536
|
+
parseLogicalName(locale, "locale");
|
|
5537
|
+
await assertSafeTarget(cwd, messagesPath);
|
|
5538
|
+
if (existsSync5(join4(messagesPath, locale))) {
|
|
5539
|
+
throw new Error(`Locale ${locale} already exists.`);
|
|
5204
5540
|
}
|
|
5205
|
-
|
|
5206
|
-
|
|
5541
|
+
const routingFile = join4(cwd, "src", "lib", "i18n", "routing.ts");
|
|
5542
|
+
const messagesRegistryFile = join4(cwd, "src", "lib", "i18n", "messages.ts");
|
|
5543
|
+
if (!existsSync5(routingFile)) {
|
|
5544
|
+
throw new Error("routing.ts not found. Are you in project root?");
|
|
5207
5545
|
}
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
if (existsSync3(uiPageTemplate)) {
|
|
5211
|
-
let uiContent = await readFile3(uiPageTemplate, "utf-8");
|
|
5212
|
-
uiContent = uiContent.replace(/template/g, childName || pageName).replace(/Template/g, capitalize(childName || pageName));
|
|
5213
|
-
await writeFile2(uiPageFile, uiContent);
|
|
5214
|
-
console.log(`\uD83D\uDCC4 File created: ${uiPageFile}`);
|
|
5215
|
-
} else {
|
|
5216
|
-
console.warn("\u26A0\uFE0F Missing template file: page-ui.tsx at path:", uiPageTemplate);
|
|
5546
|
+
if (!existsSync5(messagesRegistryFile)) {
|
|
5547
|
+
throw new Error("messages.ts not found. Are you in project root?");
|
|
5217
5548
|
}
|
|
5218
|
-
|
|
5219
|
-
|
|
5549
|
+
const routingContent = await readFile5(routingFile, "utf-8");
|
|
5550
|
+
const defaultMatch = routingContent.match(/defaultLocale:\s*"([^"]+)"/);
|
|
5551
|
+
const defaultLocale = defaultMatch ? defaultMatch[1] : null;
|
|
5552
|
+
if (!defaultLocale || !existsSync5(join4(messagesPath, defaultLocale))) {
|
|
5553
|
+
throw new Error("Default locale not found.");
|
|
5220
5554
|
}
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
|
|
5224
|
-
const dst = join3(localePagePath, filename);
|
|
5225
|
-
if (!existsSync3(src)) {
|
|
5226
|
-
console.warn(`\u26A0\uFE0F Missing template file: ${filename} at path: ${src}`);
|
|
5227
|
-
continue;
|
|
5228
|
-
}
|
|
5229
|
-
const content = await readFile3(src, "utf-8");
|
|
5230
|
-
const replaced = content.replace(/template/g, childName || pageName).replace(/Template/g, capitalize(childName || pageName));
|
|
5231
|
-
await writeFile2(dst, replaced);
|
|
5232
|
-
console.log(`\uD83D\uDCC4 File created: ${dst}`);
|
|
5555
|
+
const defaultAggregatorFile = join4(messagesPath, `${defaultLocale}.ts`);
|
|
5556
|
+
if (!existsSync5(defaultAggregatorFile)) {
|
|
5557
|
+
throw new Error(`Default locale aggregator not found: ${defaultLocale}.ts`);
|
|
5233
5558
|
}
|
|
5234
|
-
|
|
5235
|
-
|
|
5236
|
-
|
|
5237
|
-
|
|
5238
|
-
|
|
5239
|
-
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
|
|
5254
|
-
|
|
5255
|
-
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
}
|
|
5265
|
-
console.log("\u2139\uFE0F Skipping translation templates; next-intl not enabled.");
|
|
5266
|
-
}
|
|
5267
|
-
console.log(`\u2705 Page "${pageName}" with templates added${useI18n ? " for each locale" : ""}.`);
|
|
5268
|
-
}
|
|
5559
|
+
const localesMatch = routingContent.match(/locales:\s*\[([^\]]*)\]/);
|
|
5560
|
+
if (!localesMatch) {
|
|
5561
|
+
throw new Error("Unable to locate routing locales.");
|
|
5562
|
+
}
|
|
5563
|
+
const localesArr = localesMatch[1].split(",").map((s) => s.trim().replace(/["']/g, "")).filter(Boolean);
|
|
5564
|
+
if (localesArr.includes(locale)) {
|
|
5565
|
+
throw new Error(`Locale ${locale} already exists in routing.`);
|
|
5566
|
+
}
|
|
5567
|
+
const newLocales = `locales: [${[...localesArr, locale].map((item) => `"${item}"`).join(", ")}]`;
|
|
5568
|
+
const nextRoutingContent = routingContent.replace(/locales:\s*\[[^\]]*\]/, newLocales);
|
|
5569
|
+
const defaultAggregatorContent = await readFile5(defaultAggregatorFile, "utf-8");
|
|
5570
|
+
const defaultImportPrefix = `./${defaultLocale}/`;
|
|
5571
|
+
if (!defaultAggregatorContent.includes(defaultImportPrefix)) {
|
|
5572
|
+
throw new Error(`Default locale aggregator does not import from ${defaultImportPrefix}`);
|
|
5573
|
+
}
|
|
5574
|
+
const nextAggregatorContent = defaultAggregatorContent.replaceAll(defaultImportPrefix, `./${locale}/`);
|
|
5575
|
+
const messagesRegistryContent = await readFile5(messagesRegistryFile, "utf-8");
|
|
5576
|
+
const registryMatch = messagesRegistryContent.match(/const messages = \{([^}]*)\} as const;/);
|
|
5577
|
+
if (!registryMatch) {
|
|
5578
|
+
throw new Error("Unable to locate the typed messages registry.");
|
|
5579
|
+
}
|
|
5580
|
+
const registeredLocales = registryMatch[1].split(",").map((item) => item.trim()).filter(Boolean);
|
|
5581
|
+
if (registeredLocales.includes(locale)) {
|
|
5582
|
+
throw new Error(`Locale ${locale} already exists in messages registry.`);
|
|
5583
|
+
}
|
|
5584
|
+
const registryDeclaration = `const messages = { ${[
|
|
5585
|
+
...registeredLocales,
|
|
5586
|
+
locale
|
|
5587
|
+
].join(", ")} } as const;`;
|
|
5588
|
+
const declarationIndex = messagesRegistryContent.indexOf("const messages =");
|
|
5589
|
+
const nextMessagesRegistryContent = messagesRegistryContent.slice(0, declarationIndex) + `import ${locale} from "../../../messages/${locale}";
|
|
5269
5590
|
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
5277
|
-
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
|
|
5281
|
-
|
|
5282
|
-
validate: (name) => name ? true : "Page name is required"
|
|
5283
|
-
});
|
|
5284
|
-
pageName = response.pageName;
|
|
5285
|
-
}
|
|
5286
|
-
const messagesPath = join4(process.cwd(), "messages");
|
|
5287
|
-
const entries = await readdir3(messagesPath, { withFileTypes: true });
|
|
5288
|
-
const langDirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
5289
|
-
for (const locale of langDirs) {
|
|
5290
|
-
const jsonTarget = join4(messagesPath, locale, `${pageName}.json`);
|
|
5291
|
-
if (existsSync4(jsonTarget)) {
|
|
5292
|
-
await writeFile3(jsonTarget, "");
|
|
5293
|
-
await import("child_process").then((cp) => cp.execSync(`rm -f '${jsonTarget}'`));
|
|
5294
|
-
console.log(`\uD83D\uDDD1\uFE0F Deleted: ${jsonTarget}`);
|
|
5295
|
-
}
|
|
5296
|
-
}
|
|
5297
|
-
const uiPageDir = join4(process.cwd(), "src", "ui", pageName);
|
|
5298
|
-
if (existsSync4(uiPageDir)) {
|
|
5299
|
-
await import("child_process").then((cp) => cp.execSync(`rm -rf '${uiPageDir}'`));
|
|
5300
|
-
console.log(`\uD83D\uDDD1\uFE0F Deleted: ${uiPageDir}`);
|
|
5301
|
-
}
|
|
5302
|
-
const appLocaleDir = join4(process.cwd(), "src", "app", "[locale]", pageName);
|
|
5303
|
-
if (existsSync4(appLocaleDir)) {
|
|
5304
|
-
await import("child_process").then((cp) => cp.execSync(`rm -rf '${appLocaleDir}'`));
|
|
5305
|
-
console.log(`\uD83D\uDDD1\uFE0F Deleted: ${appLocaleDir}`);
|
|
5306
|
-
}
|
|
5307
|
-
console.log(`\u2705 Page "${pageName}" deleted.`);
|
|
5591
|
+
` + messagesRegistryContent.slice(declarationIndex).replace(/const messages = \{[^}]*\} as const;/, registryDeclaration);
|
|
5592
|
+
await cp(join4(messagesPath, defaultLocale), join4(messagesPath, locale), {
|
|
5593
|
+
recursive: true
|
|
5594
|
+
});
|
|
5595
|
+
console.log(`\uD83D\uDCC4 Directory created: ${join4(messagesPath, locale)}`);
|
|
5596
|
+
await writeFile4(join4(messagesPath, `${locale}.ts`), nextAggregatorContent);
|
|
5597
|
+
console.log(`\uD83D\uDCC4 File created: ${join4(messagesPath, `${locale}.ts`)}`);
|
|
5598
|
+
await writeFile4(messagesRegistryFile, nextMessagesRegistryContent);
|
|
5599
|
+
console.log(`\uD83D\uDCC4 File updated: ${messagesRegistryFile}`);
|
|
5600
|
+
await writeFile4(routingFile, nextRoutingContent);
|
|
5601
|
+
console.log(`\uD83D\uDCC4 File updated: ${routingFile}`);
|
|
5602
|
+
console.log(`\u2705 Locale "${locale}" added and copied from default locale "${defaultLocale}".`);
|
|
5308
5603
|
}
|
|
5309
5604
|
|
|
5310
5605
|
// src/lib/addLib.ts
|
|
5311
|
-
var
|
|
5606
|
+
var import_prompts5 = __toESM(require_prompts3(), 1);
|
|
5312
5607
|
import { join as join5 } from "path";
|
|
5313
|
-
import { mkdir as
|
|
5314
|
-
import { existsSync as
|
|
5315
|
-
async function addLib(args) {
|
|
5608
|
+
import { mkdir as mkdir4, readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
|
|
5609
|
+
import { existsSync as existsSync6 } from "fs";
|
|
5610
|
+
async function addLib(args, cwd = process.cwd()) {
|
|
5316
5611
|
let libArg = args[1];
|
|
5317
5612
|
if (!libArg || libArg.startsWith("-")) {
|
|
5318
|
-
const response = await
|
|
5613
|
+
const response = await import_prompts5.default.prompt({
|
|
5319
5614
|
type: "text",
|
|
5320
5615
|
name: "libArg",
|
|
5321
5616
|
message: "\uD83D\uDCE6 Lib name to add:",
|
|
@@ -5323,50 +5618,54 @@ async function addLib(args) {
|
|
|
5323
5618
|
});
|
|
5324
5619
|
libArg = response.libArg;
|
|
5325
5620
|
}
|
|
5621
|
+
const libSegments = parseLogicalName(libArg, "library name");
|
|
5326
5622
|
let libName = libArg;
|
|
5327
5623
|
let fileName = null;
|
|
5328
|
-
if (
|
|
5329
|
-
[libName, fileName] =
|
|
5624
|
+
if (libSegments.length === 2) {
|
|
5625
|
+
[libName, fileName] = libSegments;
|
|
5626
|
+
} else if (libSegments.length > 2) {
|
|
5627
|
+
throw new Error("Libraries currently support exactly library.module.");
|
|
5330
5628
|
}
|
|
5331
|
-
const config = await loadConfig();
|
|
5629
|
+
const config = await loadConfig(cwd);
|
|
5332
5630
|
if (!config) {
|
|
5333
5631
|
console.error("\u274C Configuration file cnp.config.json not found. Run this command from the project root.");
|
|
5334
5632
|
return;
|
|
5335
5633
|
}
|
|
5336
|
-
const libDir = join5(
|
|
5337
|
-
|
|
5338
|
-
|
|
5634
|
+
const libDir = join5(cwd, "src", "lib", libName);
|
|
5635
|
+
await assertSafeTarget(cwd, libDir);
|
|
5636
|
+
if (!existsSync6(libDir)) {
|
|
5637
|
+
await mkdir4(libDir, { recursive: true });
|
|
5339
5638
|
}
|
|
5340
|
-
const templateDir = join5(
|
|
5639
|
+
const templateDir = join5(resolvePackageRoot(import.meta.url), "templates", "Lib");
|
|
5341
5640
|
const indexTemplate = join5(templateDir, "index.ts");
|
|
5342
5641
|
const fileTemplate = join5(templateDir, "item.ts");
|
|
5343
5642
|
const indexPath = join5(libDir, "index.ts");
|
|
5344
|
-
if (!
|
|
5345
|
-
if (
|
|
5346
|
-
const content = await
|
|
5347
|
-
await
|
|
5643
|
+
if (!existsSync6(indexPath)) {
|
|
5644
|
+
if (existsSync6(indexTemplate)) {
|
|
5645
|
+
const content = await readFile6(indexTemplate, "utf-8");
|
|
5646
|
+
await writeFile5(indexPath, content);
|
|
5348
5647
|
} else {
|
|
5349
|
-
await
|
|
5648
|
+
await writeFile5(indexPath, `export {}
|
|
5350
5649
|
`);
|
|
5351
5650
|
}
|
|
5352
5651
|
console.log(`\uD83D\uDCC4 File created: ${indexPath}`);
|
|
5353
5652
|
}
|
|
5354
5653
|
if (fileName) {
|
|
5355
5654
|
const filePath = join5(libDir, `${fileName}.ts`);
|
|
5356
|
-
if (!
|
|
5357
|
-
if (
|
|
5358
|
-
let content = await
|
|
5655
|
+
if (!existsSync6(filePath)) {
|
|
5656
|
+
if (existsSync6(fileTemplate)) {
|
|
5657
|
+
let content = await readFile6(fileTemplate, "utf-8");
|
|
5359
5658
|
content = content.replace(/template/g, fileName).replace(/Template/g, capitalize(fileName));
|
|
5360
|
-
await
|
|
5659
|
+
await writeFile5(filePath, content);
|
|
5361
5660
|
} else {
|
|
5362
|
-
await
|
|
5661
|
+
await writeFile5(filePath, `export function ${fileName}() {
|
|
5363
5662
|
// TODO: implement
|
|
5364
5663
|
}
|
|
5365
5664
|
`);
|
|
5366
5665
|
}
|
|
5367
5666
|
console.log(`\uD83D\uDCC4 File created: ${filePath}`);
|
|
5368
5667
|
}
|
|
5369
|
-
let indexContent = await
|
|
5668
|
+
let indexContent = await readFile6(indexPath, "utf-8");
|
|
5370
5669
|
const importLine = `import { ${fileName} } from "./${fileName}";`;
|
|
5371
5670
|
const importRegex = new RegExp(`import\\s*{\\s*${fileName}\\s*}\\s*from\\s*"\\./${fileName}";`);
|
|
5372
5671
|
const exportRegex = /export\s*{([^}]*)}/m;
|
|
@@ -5394,65 +5693,382 @@ async function addLib(args) {
|
|
|
5394
5693
|
|
|
5395
5694
|
export { ` + exportsSet.join(", ") + ` };
|
|
5396
5695
|
`;
|
|
5397
|
-
await
|
|
5696
|
+
await writeFile5(indexPath, indexContent);
|
|
5398
5697
|
console.log(`\u270F\uFE0F Updated index: ${indexPath}`);
|
|
5399
5698
|
}
|
|
5400
5699
|
console.log(`\u2705 Lib "${libName}"${fileName ? ` with module ${fileName}` : ""} added.`);
|
|
5401
5700
|
}
|
|
5402
5701
|
|
|
5403
|
-
// src/lib/
|
|
5404
|
-
var
|
|
5702
|
+
// src/lib/addPage.ts
|
|
5703
|
+
var import_prompts6 = __toESM(require_prompts3(), 1);
|
|
5405
5704
|
import { join as join6 } from "path";
|
|
5406
|
-
import { mkdir as
|
|
5407
|
-
import { existsSync as
|
|
5408
|
-
|
|
5409
|
-
|
|
5410
|
-
if (
|
|
5411
|
-
|
|
5705
|
+
import { mkdir as mkdir5, readFile as readFile7, writeFile as writeFile6, readdir as readdir4 } from "fs/promises";
|
|
5706
|
+
import { existsSync as existsSync7, statSync as statSync2 } from "fs";
|
|
5707
|
+
function registerMessagesFile(content, locale, fileName) {
|
|
5708
|
+
const importPath = `./${locale}/${fileName}.json`;
|
|
5709
|
+
if (content.includes(importPath))
|
|
5710
|
+
return content;
|
|
5711
|
+
const declarationIndex = content.indexOf("const messages =");
|
|
5712
|
+
const registryMatch = content.match(/const messages = \{([\s\S]*?)\n\};/);
|
|
5713
|
+
if (declarationIndex === -1 || !registryMatch) {
|
|
5714
|
+
throw new Error(`Unable to register ${fileName}.json in messages/${locale}.ts`);
|
|
5715
|
+
}
|
|
5716
|
+
const importStatement = `import ${fileName} from "${importPath}";
|
|
5717
|
+
`;
|
|
5718
|
+
const nextRegistry = registryMatch[0].replace(/\n\};$/, `
|
|
5719
|
+
${fileName},
|
|
5720
|
+
};`);
|
|
5721
|
+
return content.slice(0, declarationIndex) + importStatement + content.slice(declarationIndex).replace(registryMatch[0], nextRegistry);
|
|
5722
|
+
}
|
|
5723
|
+
async function addPage(args, cwd = process.cwd()) {
|
|
5724
|
+
let pageName = args[1];
|
|
5725
|
+
if (!pageName || pageName.startsWith("-")) {
|
|
5726
|
+
const response = await import_prompts6.default.prompt({
|
|
5412
5727
|
type: "text",
|
|
5413
|
-
name: "
|
|
5414
|
-
message: "\uD83D\
|
|
5415
|
-
validate: (name) => name ? true : "
|
|
5728
|
+
name: "pageName",
|
|
5729
|
+
message: "\uD83D\uDCDD Page name to add:",
|
|
5730
|
+
validate: (name) => name ? true : "Page name is required"
|
|
5416
5731
|
});
|
|
5417
|
-
|
|
5732
|
+
pageName = response.pageName;
|
|
5733
|
+
}
|
|
5734
|
+
const pageSegments = parseLogicalName(pageName, "page name");
|
|
5735
|
+
let parentName = null;
|
|
5736
|
+
let childName = null;
|
|
5737
|
+
if (pageSegments.length === 2) {
|
|
5738
|
+
[parentName, childName] = pageSegments;
|
|
5739
|
+
} else if (pageSegments.length > 2) {
|
|
5740
|
+
throw new Error("Nested pages currently support exactly Parent.Child.");
|
|
5741
|
+
}
|
|
5742
|
+
let shortFlags = args.find((arg) => /^-[A-Za-z]+$/.test(arg));
|
|
5743
|
+
const longFlags = new Set(args.filter((a) => a.startsWith("--")));
|
|
5744
|
+
const flags = new Set;
|
|
5745
|
+
if (!shortFlags && Array.from(longFlags).length === 0) {
|
|
5746
|
+
shortFlags = "-LPl";
|
|
5418
5747
|
}
|
|
5419
|
-
|
|
5748
|
+
if (shortFlags) {
|
|
5749
|
+
for (const char of shortFlags.slice(1)) {
|
|
5750
|
+
switch (char) {
|
|
5751
|
+
case "L":
|
|
5752
|
+
flags.add("layout");
|
|
5753
|
+
break;
|
|
5754
|
+
case "P":
|
|
5755
|
+
flags.add("page");
|
|
5756
|
+
break;
|
|
5757
|
+
case "l":
|
|
5758
|
+
flags.add("loading");
|
|
5759
|
+
break;
|
|
5760
|
+
case "n":
|
|
5761
|
+
flags.add("not-found");
|
|
5762
|
+
break;
|
|
5763
|
+
case "e":
|
|
5764
|
+
flags.add("error");
|
|
5765
|
+
break;
|
|
5766
|
+
case "g":
|
|
5767
|
+
flags.add("global-error");
|
|
5768
|
+
break;
|
|
5769
|
+
case "r":
|
|
5770
|
+
flags.add("route");
|
|
5771
|
+
break;
|
|
5772
|
+
case "t":
|
|
5773
|
+
flags.add("template");
|
|
5774
|
+
break;
|
|
5775
|
+
case "d":
|
|
5776
|
+
flags.add("default");
|
|
5777
|
+
break;
|
|
5778
|
+
}
|
|
5779
|
+
}
|
|
5780
|
+
}
|
|
5781
|
+
for (const flag of [
|
|
5782
|
+
"layout",
|
|
5783
|
+
"page",
|
|
5784
|
+
"loading",
|
|
5785
|
+
"not-found",
|
|
5786
|
+
"error",
|
|
5787
|
+
"global-error",
|
|
5788
|
+
"route",
|
|
5789
|
+
"template",
|
|
5790
|
+
"default"
|
|
5791
|
+
]) {
|
|
5792
|
+
if (longFlags.has("--" + flag))
|
|
5793
|
+
flags.add(flag);
|
|
5794
|
+
}
|
|
5795
|
+
const config = await loadConfig(cwd);
|
|
5420
5796
|
if (!config) {
|
|
5421
5797
|
console.error("\u274C Configuration file cnp.config.json not found. Run this command from the project root.");
|
|
5422
5798
|
return;
|
|
5423
5799
|
}
|
|
5424
|
-
const
|
|
5425
|
-
|
|
5426
|
-
|
|
5800
|
+
const useI18n = !!config.useI18n;
|
|
5801
|
+
const aliasPrefix = configuredAliasPrefix(config);
|
|
5802
|
+
const srcSegments = ["src", "app"];
|
|
5803
|
+
if (useI18n)
|
|
5804
|
+
srcSegments.push("[locale]");
|
|
5805
|
+
const srcPath = join6(cwd, ...srcSegments);
|
|
5806
|
+
if (!existsSync7(srcPath)) {
|
|
5807
|
+
console.error(`\u274C Expected directory not found: ${srcPath}`);
|
|
5808
|
+
return;
|
|
5809
|
+
}
|
|
5810
|
+
let messagesPath = null;
|
|
5811
|
+
let locales = [];
|
|
5812
|
+
if (useI18n) {
|
|
5813
|
+
messagesPath = join6(cwd, "messages");
|
|
5814
|
+
if (!existsSync7(messagesPath)) {
|
|
5815
|
+
console.error("\u274C Messages directory missing. Ensure i18n was configured.");
|
|
5816
|
+
return;
|
|
5817
|
+
}
|
|
5818
|
+
const entries = await readdir4(messagesPath, { withFileTypes: true });
|
|
5819
|
+
locales = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
5427
5820
|
}
|
|
5428
|
-
const
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5821
|
+
const templatePath = join6(resolvePackageRoot(import.meta.url), "templates", "Page");
|
|
5822
|
+
let uiPageDir, localePagePath, jsonFileName;
|
|
5823
|
+
if (parentName && childName) {
|
|
5824
|
+
uiPageDir = join6(cwd, "src", "ui", parentName, childName);
|
|
5825
|
+
localePagePath = join6(srcPath, parentName, childName);
|
|
5826
|
+
jsonFileName = parentName;
|
|
5827
|
+
} else {
|
|
5828
|
+
uiPageDir = join6(cwd, "src", "ui", pageName);
|
|
5829
|
+
localePagePath = join6(srcPath, pageName);
|
|
5830
|
+
jsonFileName = pageName;
|
|
5831
|
+
}
|
|
5832
|
+
await assertSafeTarget(cwd, uiPageDir);
|
|
5833
|
+
await assertSafeTarget(cwd, localePagePath);
|
|
5834
|
+
if (!existsSync7(uiPageDir)) {
|
|
5835
|
+
await mkdir5(uiPageDir, { recursive: true });
|
|
5836
|
+
}
|
|
5837
|
+
const uiPageFile = join6(uiPageDir, "page-ui.tsx");
|
|
5838
|
+
const uiPageTemplate = join6(templatePath, "page-ui.tsx");
|
|
5839
|
+
if (existsSync7(uiPageTemplate)) {
|
|
5840
|
+
let uiContent = await readFile7(uiPageTemplate, "utf-8");
|
|
5841
|
+
const translationNamespace = parentName && childName ? `${parentName}.${childName}` : pageName;
|
|
5842
|
+
uiContent = uiContent.replace('useTranslations("template")', `useTranslations("${translationNamespace}")`);
|
|
5843
|
+
uiContent = uiContent.replaceAll('from "@/', `from "${aliasPrefix}/`).replace(/template/g, childName || pageName).replace(/Template/g, capitalize(childName || pageName));
|
|
5844
|
+
await writeFile6(uiPageFile, uiContent);
|
|
5845
|
+
console.log(`\uD83D\uDCC4 File created: ${uiPageFile}`);
|
|
5846
|
+
} else {
|
|
5847
|
+
console.warn("\u26A0\uFE0F Missing template file: page-ui.tsx at path:", uiPageTemplate);
|
|
5848
|
+
}
|
|
5849
|
+
if (!existsSync7(localePagePath)) {
|
|
5850
|
+
await mkdir5(localePagePath, { recursive: true });
|
|
5851
|
+
}
|
|
5852
|
+
for (const flag of flags) {
|
|
5853
|
+
const filename = toFileName(flag);
|
|
5854
|
+
const src = join6(templatePath, filename);
|
|
5855
|
+
const dst = join6(localePagePath, filename);
|
|
5856
|
+
if (!existsSync7(src)) {
|
|
5857
|
+
console.warn(`\u26A0\uFE0F Missing template file: ${filename} at path: ${src}`);
|
|
5858
|
+
continue;
|
|
5859
|
+
}
|
|
5860
|
+
const content = await readFile7(src, "utf-8");
|
|
5861
|
+
const uiImportPath = parentName && childName ? `${parentName}/${childName}` : pageName;
|
|
5862
|
+
const replaced = content.replace("@/ui/template/", `@/ui/${uiImportPath}/`).replaceAll('from "@/', `from "${aliasPrefix}/`).replace(/template/g, childName || pageName).replace(/Template/g, capitalize(childName || pageName));
|
|
5863
|
+
await writeFile6(dst, replaced);
|
|
5864
|
+
console.log(`\uD83D\uDCC4 File created: ${dst}`);
|
|
5865
|
+
}
|
|
5866
|
+
if (useI18n && messagesPath) {
|
|
5867
|
+
const jsonTemplate = join6(templatePath, "page.json");
|
|
5868
|
+
if (!existsSync7(jsonTemplate)) {
|
|
5869
|
+
console.warn("\u26A0\uFE0F Missing template: page.json at path:", jsonTemplate);
|
|
5436
5870
|
} else {
|
|
5437
|
-
await
|
|
5871
|
+
const content = await readFile7(jsonTemplate, "utf-8");
|
|
5872
|
+
const replaced = content.replace(/template/g, childName || pageName).replace(/Template/g, capitalize(childName || pageName));
|
|
5873
|
+
for (const locale of locales) {
|
|
5874
|
+
const localeDir = join6(messagesPath, locale);
|
|
5875
|
+
if (!existsSync7(localeDir) || !statSync2(localeDir).isDirectory())
|
|
5876
|
+
continue;
|
|
5877
|
+
const jsonTarget = join6(messagesPath, locale, `${jsonFileName}.json`);
|
|
5878
|
+
let current = {};
|
|
5879
|
+
if (existsSync7(jsonTarget)) {
|
|
5880
|
+
const jsonFile = await readFile7(jsonTarget, "utf-8");
|
|
5881
|
+
try {
|
|
5882
|
+
current = JSON.parse(jsonFile);
|
|
5883
|
+
} catch {
|
|
5884
|
+
current = {};
|
|
5885
|
+
}
|
|
5886
|
+
}
|
|
5887
|
+
if (parentName && childName) {
|
|
5888
|
+
current[childName] = JSON.parse(replaced);
|
|
5889
|
+
} else {
|
|
5890
|
+
current = JSON.parse(replaced);
|
|
5891
|
+
}
|
|
5892
|
+
await writeFile6(jsonTarget, `${JSON.stringify(current, null, 2)}
|
|
5893
|
+
`);
|
|
5894
|
+
console.log(`\uD83D\uDCC4 File created: ${jsonTarget}`);
|
|
5895
|
+
const localeAggregator = join6(messagesPath, `${locale}.ts`);
|
|
5896
|
+
if (!existsSync7(localeAggregator)) {
|
|
5897
|
+
throw new Error(`Locale aggregator not found: messages/${locale}.ts`);
|
|
5898
|
+
}
|
|
5899
|
+
const aggregatorContent = await readFile7(localeAggregator, "utf-8");
|
|
5900
|
+
const nextAggregatorContent = registerMessagesFile(aggregatorContent, locale, jsonFileName);
|
|
5901
|
+
if (nextAggregatorContent !== aggregatorContent) {
|
|
5902
|
+
await writeFile6(localeAggregator, nextAggregatorContent);
|
|
5903
|
+
console.log(`\uD83D\uDCC4 File updated: ${localeAggregator}`);
|
|
5904
|
+
}
|
|
5905
|
+
}
|
|
5906
|
+
}
|
|
5907
|
+
} else {
|
|
5908
|
+
console.log("\u2139\uFE0F Skipping translation templates; next-intl not enabled.");
|
|
5909
|
+
}
|
|
5910
|
+
console.log(`\u2705 Page "${pageName}" with templates added${useI18n ? " for each locale" : ""}.`);
|
|
5911
|
+
}
|
|
5438
5912
|
|
|
5439
|
-
|
|
5440
|
-
|
|
5913
|
+
// src/lib/addText.ts
|
|
5914
|
+
import { join as join7 } from "path";
|
|
5915
|
+
import { existsSync as existsSync8 } from "fs";
|
|
5916
|
+
import { readFile as readFile8, writeFile as writeFile7, readdir as readdir5 } from "fs/promises";
|
|
5917
|
+
function defaultText(key) {
|
|
5918
|
+
return key.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
5919
|
+
}
|
|
5920
|
+
async function addText(args, cwd = process.cwd()) {
|
|
5921
|
+
const pathArg = args[1];
|
|
5922
|
+
if (!pathArg) {
|
|
5923
|
+
console.error("\u274C Dot path parameter is required.");
|
|
5924
|
+
return;
|
|
5925
|
+
}
|
|
5926
|
+
const providedText = args.slice(2).join(" ");
|
|
5927
|
+
parseLogicalName(pathArg, "translation path");
|
|
5928
|
+
const config = await loadConfig(cwd);
|
|
5929
|
+
if (!config?.useI18n) {
|
|
5930
|
+
console.error("\u274C i18n is not enabled in this project.");
|
|
5931
|
+
return;
|
|
5932
|
+
}
|
|
5933
|
+
const messagesPath = join7(cwd, "messages");
|
|
5934
|
+
if (!existsSync8(messagesPath)) {
|
|
5935
|
+
console.error("\u274C Messages directory missing. Ensure i18n was configured.");
|
|
5936
|
+
return;
|
|
5937
|
+
}
|
|
5938
|
+
const entries = await readdir5(messagesPath, { withFileTypes: true });
|
|
5939
|
+
const locales = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
5940
|
+
const [fileName, ...segments] = pathArg.split(".");
|
|
5941
|
+
if (!fileName || segments.length === 0) {
|
|
5942
|
+
console.error("\u274C Invalid dot path provided.");
|
|
5943
|
+
return;
|
|
5944
|
+
}
|
|
5945
|
+
const finalKey = segments[segments.length - 1];
|
|
5946
|
+
const text = providedText || defaultText(finalKey);
|
|
5947
|
+
for (const locale of locales) {
|
|
5948
|
+
const filePath = join7(messagesPath, locale, `${fileName}.json`);
|
|
5949
|
+
await assertSafeTarget(cwd, filePath);
|
|
5950
|
+
let data = {};
|
|
5951
|
+
if (existsSync8(filePath)) {
|
|
5952
|
+
const raw = await readFile8(filePath, "utf-8");
|
|
5953
|
+
try {
|
|
5954
|
+
data = JSON.parse(raw);
|
|
5955
|
+
} catch {}
|
|
5956
|
+
}
|
|
5957
|
+
let cursor = data;
|
|
5958
|
+
for (let i = 0;i < segments.length - 1; i++) {
|
|
5959
|
+
const seg = segments[i];
|
|
5960
|
+
if (!cursor[seg])
|
|
5961
|
+
cursor[seg] = {};
|
|
5962
|
+
cursor = cursor[seg];
|
|
5963
|
+
}
|
|
5964
|
+
cursor[finalKey] = text;
|
|
5965
|
+
await writeFile7(filePath, JSON.stringify(data, null, 2));
|
|
5966
|
+
console.log(`\uD83D\uDCC4 File updated: ${filePath}`);
|
|
5967
|
+
}
|
|
5968
|
+
console.log(`\u2705 Text added at path "${pathArg}" with value "${text}".`);
|
|
5441
5969
|
}
|
|
5970
|
+
|
|
5971
|
+
// src/lib/rmPage.ts
|
|
5972
|
+
import { existsSync as existsSync9 } from "fs";
|
|
5973
|
+
import { readFile as readFile9, readdir as readdir6, rm, writeFile as writeFile8 } from "fs/promises";
|
|
5974
|
+
import path6 from "path";
|
|
5975
|
+
async function removeMessages(projectRoot, candidate, terminal) {
|
|
5976
|
+
const messagesRoot = resolveInside(projectRoot, "messages");
|
|
5977
|
+
let locales;
|
|
5978
|
+
try {
|
|
5979
|
+
locales = (await readdir6(messagesRoot, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
5980
|
+
} catch {
|
|
5981
|
+
return;
|
|
5982
|
+
}
|
|
5983
|
+
for (const locale of locales) {
|
|
5984
|
+
const target = resolveInside(projectRoot, "messages", locale, `${candidate.routeSegments[0]}.json`);
|
|
5985
|
+
if (!existsSync9(target))
|
|
5986
|
+
continue;
|
|
5987
|
+
if (candidate.messageKey) {
|
|
5988
|
+
const data = JSON.parse(await readFile9(target, "utf8"));
|
|
5989
|
+
delete data[candidate.messageKey];
|
|
5990
|
+
await writeFile8(target, `${JSON.stringify(data, null, 2)}
|
|
5442
5991
|
`);
|
|
5992
|
+
} else {
|
|
5993
|
+
await rm(target);
|
|
5443
5994
|
}
|
|
5444
|
-
|
|
5445
|
-
} else {
|
|
5446
|
-
console.log(`\u2139\uFE0F File already exists: ${routePath}`);
|
|
5995
|
+
terminal.log(`\uD83D\uDDD1\uFE0F Deleted messages for ${candidate.logicalName}: ${target}`);
|
|
5447
5996
|
}
|
|
5448
|
-
|
|
5997
|
+
}
|
|
5998
|
+
async function rmPage(args, cwd = process.cwd(), prompt, terminal = console) {
|
|
5999
|
+
const candidates = await discoverPages(cwd);
|
|
6000
|
+
let logicalName = args[1];
|
|
6001
|
+
if (!logicalName || logicalName.startsWith("-")) {
|
|
6002
|
+
if (!prompt)
|
|
6003
|
+
throw new CliError("A page name is required in non-interactive mode.");
|
|
6004
|
+
const selected = await prompt([
|
|
6005
|
+
{
|
|
6006
|
+
type: "autocomplete",
|
|
6007
|
+
name: "page",
|
|
6008
|
+
message: "\uD83D\uDDD1\uFE0F Page to remove:",
|
|
6009
|
+
choices: candidates.map((candidate2) => ({
|
|
6010
|
+
title: candidate2.logicalName.replaceAll(".", " \u203A "),
|
|
6011
|
+
value: candidate2.logicalName
|
|
6012
|
+
}))
|
|
6013
|
+
},
|
|
6014
|
+
{
|
|
6015
|
+
type: (value) => value ? "confirm" : null,
|
|
6016
|
+
name: "confirm",
|
|
6017
|
+
message: "Confirm page deletion?",
|
|
6018
|
+
initial: false
|
|
6019
|
+
}
|
|
6020
|
+
]);
|
|
6021
|
+
if (!selected.confirm) {
|
|
6022
|
+
terminal.log("Page deletion cancelled.");
|
|
6023
|
+
return;
|
|
6024
|
+
}
|
|
6025
|
+
logicalName = String(selected.page ?? "");
|
|
6026
|
+
}
|
|
6027
|
+
parseLogicalName(logicalName, "page name");
|
|
6028
|
+
const candidate = candidates.find((entry) => entry.logicalName === logicalName);
|
|
6029
|
+
if (!candidate)
|
|
6030
|
+
throw new CliError(`Page not found: ${logicalName}`);
|
|
6031
|
+
await removeMessages(cwd, candidate, terminal);
|
|
6032
|
+
for (const target of [candidate.uiDirectory, candidate.routeDirectory]) {
|
|
6033
|
+
const safeTarget = resolveInside(cwd, path6.relative(cwd, target));
|
|
6034
|
+
if (existsSync9(safeTarget)) {
|
|
6035
|
+
await rm(safeTarget, { recursive: true, force: false });
|
|
6036
|
+
terminal.log(`\uD83D\uDDD1\uFE0F Deleted: ${safeTarget}`);
|
|
6037
|
+
}
|
|
6038
|
+
}
|
|
6039
|
+
terminal.log(`\u2705 Page "${logicalName}" deleted.`);
|
|
6040
|
+
}
|
|
6041
|
+
|
|
6042
|
+
// src/cli/registry.ts
|
|
6043
|
+
function legacyHandler(command) {
|
|
6044
|
+
return async (args, context) => {
|
|
6045
|
+
await command(args, context.cwd);
|
|
6046
|
+
return success();
|
|
6047
|
+
};
|
|
6048
|
+
}
|
|
6049
|
+
function createCommandRegistry() {
|
|
6050
|
+
return new Map([
|
|
6051
|
+
["addcomponent", legacyHandler(addComponent)],
|
|
6052
|
+
["addpage", legacyHandler(addPage)],
|
|
6053
|
+
["addlib", legacyHandler(addLib)],
|
|
6054
|
+
["addapi", legacyHandler(addApi)],
|
|
6055
|
+
["addlanguage", legacyHandler(addLanguage)],
|
|
6056
|
+
["addtext", legacyHandler(addText)],
|
|
6057
|
+
[
|
|
6058
|
+
"rmpage",
|
|
6059
|
+
async (args, context) => {
|
|
6060
|
+
await rmPage(args, context.cwd, context.prompt, context.terminal);
|
|
6061
|
+
return success();
|
|
6062
|
+
}
|
|
6063
|
+
]
|
|
6064
|
+
]);
|
|
5449
6065
|
}
|
|
5450
6066
|
|
|
5451
6067
|
// src/scaffold.ts
|
|
5452
|
-
import {
|
|
5453
|
-
import { join as
|
|
5454
|
-
import { existsSync as
|
|
5455
|
-
import { fileURLToPath } from "url";
|
|
6068
|
+
import { mkdir as mkdir7, rm as rm2, writeFile as writeFile10 } from "fs/promises";
|
|
6069
|
+
import { join as join8, resolve } from "path";
|
|
6070
|
+
import { existsSync as existsSync10 } from "fs";
|
|
6071
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
5456
6072
|
|
|
5457
6073
|
// src/lib/helper/consoleColor.ts
|
|
5458
6074
|
var RED = "\x1B[31m";
|
|
@@ -5469,56 +6085,159 @@ function cyan(text) {
|
|
|
5469
6085
|
return CYAN + text + RESET;
|
|
5470
6086
|
}
|
|
5471
6087
|
|
|
6088
|
+
// src/core/template-manifest.ts
|
|
6089
|
+
import {
|
|
6090
|
+
lstat as lstat2,
|
|
6091
|
+
mkdir as mkdir6,
|
|
6092
|
+
readdir as readdir7,
|
|
6093
|
+
readFile as readFile10,
|
|
6094
|
+
writeFile as writeFile9,
|
|
6095
|
+
copyFile as copyFile2
|
|
6096
|
+
} from "fs/promises";
|
|
6097
|
+
import path7 from "path";
|
|
6098
|
+
var TEMPLATE_DENY_NAMES = new Set([
|
|
6099
|
+
".env",
|
|
6100
|
+
".git",
|
|
6101
|
+
".agent",
|
|
6102
|
+
".cursor",
|
|
6103
|
+
".next",
|
|
6104
|
+
"node_modules",
|
|
6105
|
+
"artifacts",
|
|
6106
|
+
"coverage",
|
|
6107
|
+
"playwright-report",
|
|
6108
|
+
"test-results"
|
|
6109
|
+
]);
|
|
6110
|
+
function isDistributableTemplatePath(relativePath) {
|
|
6111
|
+
const segments = relativePath.split(path7.sep);
|
|
6112
|
+
return !segments.some((segment) => TEMPLATE_DENY_NAMES.has(segment) || segment.endsWith(".tsbuildinfo") || segment === ".DS_Store");
|
|
6113
|
+
}
|
|
6114
|
+
async function templateManifest(root) {
|
|
6115
|
+
const files = [];
|
|
6116
|
+
async function visit(directory, relativeDirectory = "") {
|
|
6117
|
+
const entries = await readdir7(directory, { withFileTypes: true });
|
|
6118
|
+
entries.sort((left, right) => left.name.localeCompare(right.name));
|
|
6119
|
+
for (const entry of entries) {
|
|
6120
|
+
const relative = path7.join(relativeDirectory, entry.name);
|
|
6121
|
+
if (!isDistributableTemplatePath(relative))
|
|
6122
|
+
continue;
|
|
6123
|
+
const source = path7.join(directory, entry.name);
|
|
6124
|
+
const stats = await lstat2(source);
|
|
6125
|
+
if (stats.isSymbolicLink()) {
|
|
6126
|
+
throw new CliError(`Symbolic links are forbidden in templates: ${relative}`);
|
|
6127
|
+
}
|
|
6128
|
+
if (stats.isDirectory())
|
|
6129
|
+
await visit(source, relative);
|
|
6130
|
+
else if (stats.isFile())
|
|
6131
|
+
files.push(relative);
|
|
6132
|
+
else
|
|
6133
|
+
throw new CliError(`Unsupported template entry: ${relative}`);
|
|
6134
|
+
}
|
|
6135
|
+
}
|
|
6136
|
+
await visit(root);
|
|
6137
|
+
return files;
|
|
6138
|
+
}
|
|
6139
|
+
async function copyTemplate(root, target) {
|
|
6140
|
+
const manifest = await templateManifest(root);
|
|
6141
|
+
await mkdir6(target, { recursive: true });
|
|
6142
|
+
for (const relative of manifest) {
|
|
6143
|
+
const destination = path7.join(target, relative === ".gitignore.template" ? ".gitignore" : relative);
|
|
6144
|
+
await mkdir6(path7.dirname(destination), { recursive: true });
|
|
6145
|
+
await copyFile2(path7.join(root, relative), destination);
|
|
6146
|
+
}
|
|
6147
|
+
return manifest;
|
|
6148
|
+
}
|
|
6149
|
+
async function customizeGeneratedProject(target, projectName, importAlias) {
|
|
6150
|
+
const packagePath = path7.join(target, "package.json");
|
|
6151
|
+
const packageJson = JSON.parse(await readFile10(packagePath, "utf8"));
|
|
6152
|
+
packageJson.name = projectName.toLowerCase();
|
|
6153
|
+
delete packageJson.packageManager;
|
|
6154
|
+
await writeFile9(packagePath, `${JSON.stringify(packageJson, null, 2)}
|
|
6155
|
+
`);
|
|
6156
|
+
const tsconfigPath = path7.join(target, "tsconfig.json");
|
|
6157
|
+
const tsconfigContent = await readFile10(tsconfigPath, "utf8");
|
|
6158
|
+
const tsconfig = JSON.parse(tsconfigContent);
|
|
6159
|
+
if (!tsconfig.compilerOptions?.paths?.["@/*"]) {
|
|
6160
|
+
throw new CliError('The template tsconfig must define the default "@/*" alias.');
|
|
6161
|
+
}
|
|
6162
|
+
await writeFile9(tsconfigPath, tsconfigContent.replace('"@/*"', `"${importAlias}"`));
|
|
6163
|
+
if (importAlias !== "@/*") {
|
|
6164
|
+
const prefix = importAlias.slice(0, -2);
|
|
6165
|
+
for (const relative of await templateManifest(target)) {
|
|
6166
|
+
if (!/\.[cm]?[jt]sx?$/.test(relative))
|
|
6167
|
+
continue;
|
|
6168
|
+
const file = path7.join(target, relative);
|
|
6169
|
+
const content = await readFile10(file, "utf8");
|
|
6170
|
+
const next = content.replaceAll('from "@/', `from "${prefix}/`).replaceAll("from '@/", `from '${prefix}/`);
|
|
6171
|
+
if (next !== content)
|
|
6172
|
+
await writeFile9(file, next);
|
|
6173
|
+
}
|
|
6174
|
+
}
|
|
6175
|
+
}
|
|
6176
|
+
|
|
5472
6177
|
// src/scaffold.ts
|
|
5473
|
-
async function scaffoldProject(options) {
|
|
5474
|
-
const
|
|
6178
|
+
async function scaffoldProject(options, runtime = {}) {
|
|
6179
|
+
const requiredFeatures = [
|
|
6180
|
+
"useTypescript",
|
|
6181
|
+
"useEslint",
|
|
6182
|
+
"useTailwind",
|
|
6183
|
+
"useSrcDir",
|
|
6184
|
+
"useTurbopack",
|
|
6185
|
+
"useI18n"
|
|
6186
|
+
];
|
|
6187
|
+
const unsupported = requiredFeatures.filter((feature) => options[feature] !== true);
|
|
6188
|
+
if (unsupported.length > 0) {
|
|
6189
|
+
throw new CliError(`The default Next.js 16 template requires: ${unsupported.join(", ")}.`);
|
|
6190
|
+
}
|
|
6191
|
+
const cwd = runtime.cwd ?? process.cwd();
|
|
6192
|
+
const terminal = runtime.terminal ?? console;
|
|
6193
|
+
const projectName = validateProjectName(options.projectName);
|
|
6194
|
+
const importAlias = normalizeImportAlias(options.customAlias === false ? "@/*" : options.importAlias || "@/*");
|
|
6195
|
+
const targetPath = join8(cwd, projectName);
|
|
5475
6196
|
const __dirname2 = new URL(".", import.meta.url);
|
|
5476
|
-
const templatePath =
|
|
5477
|
-
|
|
6197
|
+
const templatePath = runtime.templatePath ?? join8(fileURLToPath2(__dirname2), "..", "templates", "Projects", "default");
|
|
6198
|
+
const resolvedCwd = resolve(cwd);
|
|
6199
|
+
const resolvedTarget = resolve(targetPath);
|
|
6200
|
+
if (!resolvedTarget.startsWith(`${resolvedCwd}/`)) {
|
|
6201
|
+
throw new CliError("The project destination must be a child of the current directory.");
|
|
6202
|
+
}
|
|
6203
|
+
if (existsSync10(targetPath)) {
|
|
5478
6204
|
if (options.force) {
|
|
5479
|
-
|
|
5480
|
-
await
|
|
6205
|
+
terminal.warn("\u26A0\uFE0F Target directory already exists, removing...");
|
|
6206
|
+
await rm2(targetPath, { recursive: true, force: true });
|
|
5481
6207
|
} else {
|
|
5482
|
-
|
|
5483
|
-
|
|
6208
|
+
terminal.error(red("[X] Target directory already exists. Use --force to overwrite."));
|
|
6209
|
+
throw new CliError("[X] Target directory already exists. Use --force to overwrite.");
|
|
5484
6210
|
}
|
|
5485
6211
|
}
|
|
5486
6212
|
try {
|
|
5487
|
-
|
|
5488
|
-
await
|
|
5489
|
-
|
|
5490
|
-
await
|
|
5491
|
-
|
|
5492
|
-
|
|
5493
|
-
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
|
|
5499
|
-
|
|
5500
|
-
|
|
5501
|
-
|
|
5502
|
-
|
|
5503
|
-
|
|
5504
|
-
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
console.log(" " + green(`bun install && bun dev`));
|
|
5508
|
-
console.log(" " + green(`npm install && npm run dev`));
|
|
5509
|
-
console.log(" " + green(`pnpm install && pnpm run dev`));
|
|
5510
|
-
console.log("");
|
|
5511
|
-
console.log("Documentation and examples can be found at:");
|
|
5512
|
-
console.log(" " + cyan("https://github.com/Rising-Corporation/create-next-pro-cli"));
|
|
5513
|
-
console.log("_-`'-_-'`_`'-_-'`_`'-_-'`_`'-_-'`_`'-_-'`_`'-_-'`_`'-_-'`_`'-_-'`-_");
|
|
6213
|
+
terminal.log("Creating project directory...");
|
|
6214
|
+
await mkdir7(targetPath, { recursive: true });
|
|
6215
|
+
terminal.log("Copying files from template...");
|
|
6216
|
+
await copyTemplate(templatePath, targetPath);
|
|
6217
|
+
await customizeGeneratedProject(targetPath, projectName, importAlias);
|
|
6218
|
+
await writeFile10(join8(targetPath, "cnp.config.json"), `${JSON.stringify({ ...options, projectName, importAlias }, null, 2)}
|
|
6219
|
+
`);
|
|
6220
|
+
terminal.log("Project setup complete!");
|
|
6221
|
+
terminal.log("");
|
|
6222
|
+
terminal.log("To get started:");
|
|
6223
|
+
terminal.log(" " + green(`cd ${options.projectName}`));
|
|
6224
|
+
terminal.log("");
|
|
6225
|
+
terminal.log("Then install dependencies and launch the dev server with your preferred tool:");
|
|
6226
|
+
terminal.log(" " + green(`bun install && bun dev`));
|
|
6227
|
+
terminal.log(" " + green(`npm install && npm run dev`));
|
|
6228
|
+
terminal.log(" " + green(`pnpm install && pnpm run dev`));
|
|
6229
|
+
terminal.log("");
|
|
6230
|
+
terminal.log("Documentation and examples can be found at:");
|
|
6231
|
+
terminal.log(" " + cyan("https://github.com/Rising-Corporation/create-next-pro-cli"));
|
|
6232
|
+
terminal.log("_-`'-_-'`_`'-_-'`_`'-_-'`_`'-_-'`_`'-_-'`_`'-_-'`_`'-_-'`_`'-_-'`-_");
|
|
5514
6233
|
} catch (err) {
|
|
5515
|
-
|
|
5516
|
-
|
|
6234
|
+
terminal.error(red("[X] Error during project creation:"), err);
|
|
6235
|
+
throw new CliError("[X] Error during project creation:");
|
|
5517
6236
|
}
|
|
5518
6237
|
}
|
|
5519
6238
|
|
|
5520
6239
|
// src/lib/createProject.ts
|
|
5521
|
-
async function createProject(nameArg, force) {
|
|
6240
|
+
async function createProject(nameArg, force, context) {
|
|
5522
6241
|
const response = {
|
|
5523
6242
|
projectName: nameArg,
|
|
5524
6243
|
useTypescript: true,
|
|
@@ -5531,14 +6250,17 @@ async function createProject(nameArg, force) {
|
|
|
5531
6250
|
importAlias: "@/*",
|
|
5532
6251
|
force
|
|
5533
6252
|
};
|
|
5534
|
-
|
|
5535
|
-
|
|
6253
|
+
const terminal = context?.terminal ?? console;
|
|
6254
|
+
terminal.log(`Creating project "${response.projectName}"...`);
|
|
6255
|
+
await scaffoldProject(response, {
|
|
6256
|
+
cwd: context?.cwd ?? process.cwd(),
|
|
6257
|
+
terminal
|
|
6258
|
+
});
|
|
5536
6259
|
}
|
|
5537
6260
|
|
|
5538
6261
|
// src/lib/createProjectWithPrompt.ts
|
|
5539
|
-
|
|
5540
|
-
|
|
5541
|
-
const response = await import_prompts6.default.prompt([
|
|
6262
|
+
async function createProjectWithPrompt(context) {
|
|
6263
|
+
const response = await context.prompt([
|
|
5542
6264
|
{
|
|
5543
6265
|
type: "text",
|
|
5544
6266
|
name: "projectName",
|
|
@@ -5608,232 +6330,18 @@ async function createProjectWithPrompt() {
|
|
|
5608
6330
|
initial: "@core/*"
|
|
5609
6331
|
}
|
|
5610
6332
|
]);
|
|
5611
|
-
|
|
6333
|
+
const options = response;
|
|
6334
|
+
context.terminal.log(`
|
|
5612
6335
|
Your choices:`);
|
|
5613
|
-
|
|
5614
|
-
await scaffoldProject(
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
// src/lib/addLanguage.ts
|
|
5618
|
-
var import_prompts7 = __toESM(require_prompts3(), 1);
|
|
5619
|
-
import { join as join8 } from "path";
|
|
5620
|
-
import { existsSync as existsSync8 } from "fs";
|
|
5621
|
-
import { cp as cp2, readFile as readFile7, writeFile as writeFile7 } from "fs/promises";
|
|
5622
|
-
function generateLocales() {
|
|
5623
|
-
const dn = new Intl.DisplayNames(["en"], { type: "language" });
|
|
5624
|
-
const locales = [];
|
|
5625
|
-
for (let i = 0;i < 26; i++) {
|
|
5626
|
-
for (let j = 0;j < 26; j++) {
|
|
5627
|
-
const code = String.fromCharCode(97 + i) + String.fromCharCode(97 + j);
|
|
5628
|
-
try {
|
|
5629
|
-
const name = dn.of(code);
|
|
5630
|
-
if (name && name.toLowerCase() !== code) {
|
|
5631
|
-
locales.push(code);
|
|
5632
|
-
}
|
|
5633
|
-
} catch {}
|
|
5634
|
-
}
|
|
5635
|
-
}
|
|
5636
|
-
return locales.sort();
|
|
5637
|
-
}
|
|
5638
|
-
async function addLanguage(args) {
|
|
5639
|
-
const config = await loadConfig();
|
|
5640
|
-
if (!config?.useI18n) {
|
|
5641
|
-
console.error("\u274C i18n is not enabled in this project.");
|
|
5642
|
-
return;
|
|
5643
|
-
}
|
|
5644
|
-
const messagesPath = join8(process.cwd(), "messages");
|
|
5645
|
-
if (!existsSync8(messagesPath)) {
|
|
5646
|
-
console.error("\u274C Messages directory missing. Ensure i18n was configured.");
|
|
5647
|
-
return;
|
|
5648
|
-
}
|
|
5649
|
-
const available = generateLocales();
|
|
5650
|
-
let locale = args[1];
|
|
5651
|
-
if (!locale || !available.includes(locale)) {
|
|
5652
|
-
const response = await import_prompts7.default({
|
|
5653
|
-
type: "autocomplete",
|
|
5654
|
-
name: "locale",
|
|
5655
|
-
message: "\uD83C\uDF10 Locale to add:",
|
|
5656
|
-
choices: available.map((l) => ({ title: l, value: l }))
|
|
5657
|
-
});
|
|
5658
|
-
locale = response.locale;
|
|
5659
|
-
}
|
|
5660
|
-
if (!locale)
|
|
5661
|
-
return;
|
|
5662
|
-
if (existsSync8(join8(messagesPath, locale))) {
|
|
5663
|
-
console.error(`\u274C Locale ${locale} already exists.`);
|
|
5664
|
-
return;
|
|
5665
|
-
}
|
|
5666
|
-
const routingFile = join8(process.cwd(), "src", "lib", "i18n", "routing.ts");
|
|
5667
|
-
if (!existsSync8(routingFile)) {
|
|
5668
|
-
console.error("\u274C routing.ts not found. Are you in project root?");
|
|
5669
|
-
return;
|
|
5670
|
-
}
|
|
5671
|
-
const routingContent = await readFile7(routingFile, "utf-8");
|
|
5672
|
-
const defaultMatch = routingContent.match(/defaultLocale:\s*"([^"]+)"/);
|
|
5673
|
-
const defaultLocale = defaultMatch ? defaultMatch[1] : null;
|
|
5674
|
-
if (!defaultLocale || !existsSync8(join8(messagesPath, defaultLocale))) {
|
|
5675
|
-
console.error("\u274C Default locale not found.");
|
|
5676
|
-
return;
|
|
5677
|
-
}
|
|
5678
|
-
await cp2(join8(messagesPath, defaultLocale), join8(messagesPath, locale), {
|
|
5679
|
-
recursive: true
|
|
6336
|
+
context.terminal.log(options);
|
|
6337
|
+
await scaffoldProject(options, {
|
|
6338
|
+
cwd: context.cwd,
|
|
6339
|
+
terminal: context.terminal
|
|
5680
6340
|
});
|
|
5681
|
-
console.log(`\uD83D\uDCC4 Directory created: ${join8(messagesPath, locale)}`);
|
|
5682
|
-
const localesMatch = routingContent.match(/locales:\s*\[([^\]]*)\]/);
|
|
5683
|
-
if (localesMatch) {
|
|
5684
|
-
const localesArr = localesMatch[1].split(",").map((s) => s.trim().replace(/["']/g, "")).filter(Boolean);
|
|
5685
|
-
if (!localesArr.includes(locale)) {
|
|
5686
|
-
localesArr.push(locale);
|
|
5687
|
-
const newLocales = `locales: [${localesArr.map((l) => `"${l}"`).join(", ")}]`;
|
|
5688
|
-
const newContent = routingContent.replace(/locales:\s*\[[^\]]*\]/, newLocales);
|
|
5689
|
-
await writeFile7(routingFile, newContent);
|
|
5690
|
-
console.log(`\uD83D\uDCC4 File updated: ${routingFile}`);
|
|
5691
|
-
}
|
|
5692
|
-
}
|
|
5693
|
-
console.log(`\u2705 Locale "${locale}" added and copied from default locale "${defaultLocale}".`);
|
|
5694
|
-
}
|
|
5695
|
-
|
|
5696
|
-
// src/lib/addText.ts
|
|
5697
|
-
import { join as join9 } from "path";
|
|
5698
|
-
import { existsSync as existsSync9 } from "fs";
|
|
5699
|
-
import { readFile as readFile8, writeFile as writeFile8, readdir as readdir4 } from "fs/promises";
|
|
5700
|
-
function defaultText(key) {
|
|
5701
|
-
return key.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
5702
|
-
}
|
|
5703
|
-
async function addText(args) {
|
|
5704
|
-
const pathArg = args[1];
|
|
5705
|
-
if (!pathArg) {
|
|
5706
|
-
console.error("\u274C Dot path parameter is required.");
|
|
5707
|
-
return;
|
|
5708
|
-
}
|
|
5709
|
-
const providedText = args.slice(2).join(" ");
|
|
5710
|
-
const config = await loadConfig();
|
|
5711
|
-
if (!config?.useI18n) {
|
|
5712
|
-
console.error("\u274C i18n is not enabled in this project.");
|
|
5713
|
-
return;
|
|
5714
|
-
}
|
|
5715
|
-
const messagesPath = join9(process.cwd(), "messages");
|
|
5716
|
-
if (!existsSync9(messagesPath)) {
|
|
5717
|
-
console.error("\u274C Messages directory missing. Ensure i18n was configured.");
|
|
5718
|
-
return;
|
|
5719
|
-
}
|
|
5720
|
-
const entries = await readdir4(messagesPath, { withFileTypes: true });
|
|
5721
|
-
const locales = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
5722
|
-
const [fileName, ...segments] = pathArg.split(".");
|
|
5723
|
-
if (!fileName || segments.length === 0) {
|
|
5724
|
-
console.error("\u274C Invalid dot path provided.");
|
|
5725
|
-
return;
|
|
5726
|
-
}
|
|
5727
|
-
const finalKey = segments[segments.length - 1];
|
|
5728
|
-
const text = providedText || defaultText(finalKey);
|
|
5729
|
-
for (const locale of locales) {
|
|
5730
|
-
const filePath = join9(messagesPath, locale, `${fileName}.json`);
|
|
5731
|
-
let data = {};
|
|
5732
|
-
if (existsSync9(filePath)) {
|
|
5733
|
-
const raw = await readFile8(filePath, "utf-8");
|
|
5734
|
-
try {
|
|
5735
|
-
data = JSON.parse(raw);
|
|
5736
|
-
} catch {}
|
|
5737
|
-
}
|
|
5738
|
-
let cursor = data;
|
|
5739
|
-
for (let i = 0;i < segments.length - 1; i++) {
|
|
5740
|
-
const seg = segments[i];
|
|
5741
|
-
if (!cursor[seg])
|
|
5742
|
-
cursor[seg] = {};
|
|
5743
|
-
cursor = cursor[seg];
|
|
5744
|
-
}
|
|
5745
|
-
cursor[finalKey] = text;
|
|
5746
|
-
await writeFile8(filePath, JSON.stringify(data, null, 2));
|
|
5747
|
-
console.log(`\uD83D\uDCC4 File updated: ${filePath}`);
|
|
5748
|
-
}
|
|
5749
|
-
console.log(`\u2705 Text added at path "${pathArg}" with value "${text}".`);
|
|
5750
6341
|
}
|
|
5751
6342
|
|
|
5752
6343
|
// src/index.ts
|
|
5753
|
-
var
|
|
5754
|
-
var CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
|
|
5755
|
-
function readCfg() {
|
|
5756
|
-
try {
|
|
5757
|
-
return JSON.parse(fs.readFileSync(CONFIG_FILE, "utf8"));
|
|
5758
|
-
} catch {
|
|
5759
|
-
return null;
|
|
5760
|
-
}
|
|
5761
|
-
}
|
|
5762
|
-
function writeCfg(cfg) {
|
|
5763
|
-
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
5764
|
-
fs.writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2));
|
|
5765
|
-
}
|
|
5766
|
-
function rcFile(shell) {
|
|
5767
|
-
return path.join(os.homedir(), shell === "zsh" ? ".zshrc" : ".bashrc");
|
|
5768
|
-
}
|
|
5769
|
-
function ensureLineInRc(file, line) {
|
|
5770
|
-
try {
|
|
5771
|
-
const cur = fs.existsSync(file) ? fs.readFileSync(file, "utf8") : "";
|
|
5772
|
-
if (!cur.includes(line))
|
|
5773
|
-
fs.appendFileSync(file, `
|
|
5774
|
-
${line}
|
|
5775
|
-
`);
|
|
5776
|
-
} catch {}
|
|
5777
|
-
}
|
|
5778
|
-
async function installCompletion(shell) {
|
|
5779
|
-
const __dirname2 = path.dirname(fileURLToPath2(import.meta.url));
|
|
5780
|
-
const completionSrc = path.resolve(__dirname2, "../create-next-pro-completion.sh");
|
|
5781
|
-
const completionDst = path.join(CONFIG_DIR, "completion.sh");
|
|
5782
|
-
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
5783
|
-
fs.copyFileSync(completionSrc, completionDst);
|
|
5784
|
-
ensureLineInRc(rcFile(shell), `source "${completionDst}"`);
|
|
5785
|
-
}
|
|
5786
|
-
var __filename2 = fileURLToPath2(import.meta.url);
|
|
5787
|
-
var __dirname2 = dirname(__filename2);
|
|
5788
|
-
var packageJsonPath = resolve(__dirname2, "../package.json");
|
|
5789
|
-
var packageJson = fs.readFileSync(packageJsonPath, "utf8");
|
|
5790
|
-
async function onboarding() {
|
|
5791
|
-
const pkg = JSON.parse(packageJson);
|
|
5792
|
-
console.log(`\uD83D\uDE80 Welcome to create-next-pro v${pkg.version}
|
|
5793
|
-
`);
|
|
5794
|
-
const res = await import_prompts8.default([
|
|
5795
|
-
{
|
|
5796
|
-
type: "select",
|
|
5797
|
-
name: "shell",
|
|
5798
|
-
message: "Which shell do you use?",
|
|
5799
|
-
choices: [
|
|
5800
|
-
{ title: "zsh", value: "zsh" },
|
|
5801
|
-
{ title: "bash", value: "bash" }
|
|
5802
|
-
],
|
|
5803
|
-
initial: (os.userInfo().shell || "").includes("zsh") ? 0 : 1
|
|
5804
|
-
},
|
|
5805
|
-
{
|
|
5806
|
-
type: "toggle",
|
|
5807
|
-
name: "completion",
|
|
5808
|
-
message: "Install autocompletion?",
|
|
5809
|
-
initial: true,
|
|
5810
|
-
active: "Yes",
|
|
5811
|
-
inactive: "No"
|
|
5812
|
-
}
|
|
5813
|
-
], { onCancel: () => process.exit(1) });
|
|
5814
|
-
const cfg = {
|
|
5815
|
-
version: 1,
|
|
5816
|
-
shell: res.shell,
|
|
5817
|
-
completionInstalled: !!res.completion,
|
|
5818
|
-
createdAt: new Date().toISOString(),
|
|
5819
|
-
updatedAt: new Date().toISOString()
|
|
5820
|
-
};
|
|
5821
|
-
if (cfg.completionInstalled)
|
|
5822
|
-
await installCompletion(cfg.shell);
|
|
5823
|
-
writeCfg(cfg);
|
|
5824
|
-
console.log(`
|
|
5825
|
-
\u2705 Configuration saved.`);
|
|
5826
|
-
console.log("you can now use the CLI ! ex : ");
|
|
5827
|
-
console.log(" Without prompt (will change in future) :");
|
|
5828
|
-
console.log(" create-next-pro my-next-project");
|
|
5829
|
-
console.log(" With prompt :");
|
|
5830
|
-
console.log(" create-next-pro");
|
|
5831
|
-
console.log("For more information, visit: https://github.com/Rising-Corporation/create-next-pro-cli");
|
|
5832
|
-
console.log("Happy coding! \uD83C\uDF89");
|
|
5833
|
-
return cfg;
|
|
5834
|
-
}
|
|
5835
|
-
function showHelp() {
|
|
5836
|
-
console.log(`create-next-pro
|
|
6344
|
+
var HELP_TEXT = `create-next-pro
|
|
5837
6345
|
|
|
5838
6346
|
Usage:
|
|
5839
6347
|
create-next-pro <project-name> [--force]
|
|
@@ -5847,69 +6355,60 @@ Usage:
|
|
|
5847
6355
|
|
|
5848
6356
|
Options:
|
|
5849
6357
|
--help Show this help message
|
|
6358
|
+
--version Show the CLI version
|
|
5850
6359
|
--reconfigure Run the configuration assistant again
|
|
5851
|
-
|
|
6360
|
+
`;
|
|
6361
|
+
function parseOptions(args) {
|
|
6362
|
+
return {
|
|
6363
|
+
force: args.includes("--force"),
|
|
6364
|
+
help: args.includes("--help"),
|
|
6365
|
+
version: args.includes("--version") || args.includes("-v"),
|
|
6366
|
+
reconfigure: args.includes("--reconfigure")
|
|
6367
|
+
};
|
|
5852
6368
|
}
|
|
5853
|
-
function
|
|
5854
|
-
const
|
|
5855
|
-
|
|
6369
|
+
async function packageVersion(context) {
|
|
6370
|
+
const packageJson = JSON.parse(await context.fs.readText(path8.join(context.packageRoot, "package.json")));
|
|
6371
|
+
return packageJson.version;
|
|
5856
6372
|
}
|
|
5857
|
-
async function main() {
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
|
|
5862
|
-
args
|
|
5863
|
-
|
|
5864
|
-
|
|
5865
|
-
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
|
|
5878
|
-
|
|
5879
|
-
|
|
5880
|
-
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
|
|
5890
|
-
|
|
5891
|
-
|
|
5892
|
-
|
|
5893
|
-
|
|
5894
|
-
if (args[0] === "addlanguage") {
|
|
5895
|
-
await addLanguage(args);
|
|
5896
|
-
return;
|
|
5897
|
-
}
|
|
5898
|
-
if (args[0] === "addtext") {
|
|
5899
|
-
await addText(args);
|
|
5900
|
-
return;
|
|
5901
|
-
}
|
|
5902
|
-
if (args[0] === "rmpage") {
|
|
5903
|
-
rmPage(args);
|
|
5904
|
-
return;
|
|
5905
|
-
}
|
|
5906
|
-
const nameArg = args.find((arg) => !arg.startsWith("--"));
|
|
5907
|
-
if (nameArg) {
|
|
5908
|
-
createProject(nameArg, force);
|
|
5909
|
-
return;
|
|
6373
|
+
async function main(context = createNodeContext()) {
|
|
6374
|
+
try {
|
|
6375
|
+
const args = [...context.argv];
|
|
6376
|
+
const options = parseOptions(args);
|
|
6377
|
+
const version = await packageVersion(context);
|
|
6378
|
+
if (args[0] === "__complete") {
|
|
6379
|
+
await printCompletions(args, context);
|
|
6380
|
+
return 0;
|
|
6381
|
+
}
|
|
6382
|
+
if (options.help) {
|
|
6383
|
+
context.terminal.log(HELP_TEXT);
|
|
6384
|
+
return 0;
|
|
6385
|
+
}
|
|
6386
|
+
if (options.version) {
|
|
6387
|
+
context.terminal.log(`v${version}`);
|
|
6388
|
+
return 0;
|
|
6389
|
+
}
|
|
6390
|
+
if (options.reconfigure || !await readConfig(context)) {
|
|
6391
|
+
await onboarding(context, version);
|
|
6392
|
+
return 0;
|
|
6393
|
+
}
|
|
6394
|
+
if (args[0] === "addpage" && args.length === 1)
|
|
6395
|
+
args.push("-LPl");
|
|
6396
|
+
const handler = args[0] ? createCommandRegistry().get(args[0]) : undefined;
|
|
6397
|
+
if (handler)
|
|
6398
|
+
return (await handler(args, context)).exitCode;
|
|
6399
|
+
const nameArg = args.find((arg) => !arg.startsWith("--"));
|
|
6400
|
+
if (nameArg) {
|
|
6401
|
+
await createProject(nameArg, options.force, context);
|
|
6402
|
+
return 0;
|
|
6403
|
+
}
|
|
6404
|
+
await createProjectWithPrompt(context);
|
|
6405
|
+
return 0;
|
|
6406
|
+
} catch (error) {
|
|
6407
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
6408
|
+
context.terminal.error(message);
|
|
6409
|
+
return error instanceof CliError ? error.exitCode : 1;
|
|
5910
6410
|
}
|
|
5911
|
-
await createProjectWithPrompt();
|
|
5912
6411
|
}
|
|
5913
6412
|
|
|
5914
6413
|
// bin.bun.ts
|
|
5915
|
-
main();
|
|
6414
|
+
process.exitCode = await main();
|