@inlang/paraglide-js 2.27.0 → 2.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -0
- package/dist/bundler-plugins/per-locale-build/index.js +14 -7
- package/dist/bundler-plugins/per-locale-build/index.js.map +1 -1
- package/dist/bundler-plugins/per-locale-build/vite-environments.test.js +235 -2
- package/dist/bundler-plugins/per-locale-build/vite-environments.test.js.map +1 -1
- package/dist/bundler-plugins/unplugin.d.ts +1 -1
- package/dist/bundler-plugins/unplugin.js +79 -10
- package/dist/bundler-plugins/unplugin.js.map +1 -1
- package/dist/bundler-plugins/unplugin.test.js +334 -1
- package/dist/bundler-plugins/unplugin.test.js.map +1 -1
- package/dist/bundler-plugins/vite.js +2 -1
- package/dist/bundler-plugins/vite.js.map +1 -1
- package/dist/bundler-plugins/vite.test.js +1 -1
- package/dist/bundler-plugins/vite.test.js.map +1 -1
- package/dist/cli/commands/compile/command.js +13 -0
- package/dist/cli/commands/compile/command.js.map +1 -1
- package/dist/cli/commands/compile/command.test.js +87 -3
- package/dist/cli/commands/compile/command.test.js.map +1 -1
- package/dist/cli/commands/init/command.d.ts +5 -0
- package/dist/cli/commands/init/command.js +213 -59
- package/dist/cli/commands/init/command.js.map +1 -1
- package/dist/cli/commands/init/command.test.d.ts +1 -0
- package/dist/cli/commands/init/command.test.js +123 -0
- package/dist/cli/commands/init/command.test.js.map +1 -0
- package/dist/cli/init-options.d.ts +11 -0
- package/dist/cli/init-options.js +2 -0
- package/dist/cli/init-options.js.map +1 -0
- package/dist/cli/steps/add-inlang-check.js +2 -2
- package/dist/cli/steps/add-inlang-check.js.map +1 -1
- package/dist/cli/steps/add-message-lint.d.ts +34 -0
- package/dist/cli/steps/add-message-lint.js +105 -0
- package/dist/cli/steps/add-message-lint.js.map +1 -0
- package/dist/cli/steps/add-message-lint.test.d.ts +1 -0
- package/dist/cli/steps/add-message-lint.test.js +74 -0
- package/dist/cli/steps/add-message-lint.test.js.map +1 -0
- package/dist/cli/steps/add-vite-plugin.d.ts +18 -0
- package/dist/cli/steps/add-vite-plugin.js +34 -6
- package/dist/cli/steps/add-vite-plugin.js.map +1 -1
- package/dist/cli/steps/add-vite-plugin.test.js +25 -9
- package/dist/cli/steps/add-vite-plugin.test.js.map +1 -1
- package/dist/cli/steps/check-for-uncomitted-changes.d.ts +3 -0
- package/dist/cli/steps/check-for-uncomitted-changes.js +15 -3
- package/dist/cli/steps/check-for-uncomitted-changes.js.map +1 -1
- package/dist/cli/steps/detect-bundler.d.ts +1 -0
- package/dist/cli/steps/detect-bundler.js +7 -1
- package/dist/cli/steps/detect-bundler.js.map +1 -1
- package/dist/cli/steps/initialize-inlang-project.d.ts +4 -3
- package/dist/cli/steps/initialize-inlang-project.js +60 -157
- package/dist/cli/steps/initialize-inlang-project.js.map +1 -1
- package/dist/cli/steps/maybe-add-machine-translation.d.ts +4 -0
- package/dist/cli/steps/maybe-add-machine-translation.js +11 -6
- package/dist/cli/steps/maybe-add-machine-translation.js.map +1 -1
- package/dist/cli/steps/maybe-add-sherlock.d.ts +2 -0
- package/dist/cli/steps/maybe-add-sherlock.js +12 -6
- package/dist/cli/steps/maybe-add-sherlock.js.map +1 -1
- package/dist/cli/steps/maybe-add-sherlock.test.d.ts +1 -0
- package/dist/cli/steps/maybe-add-sherlock.test.js +44 -0
- package/dist/cli/steps/maybe-add-sherlock.test.js.map +1 -0
- package/dist/cli/steps/prompt-for-outdir.d.ts +2 -0
- package/dist/cli/steps/prompt-for-outdir.js +4 -0
- package/dist/cli/steps/prompt-for-outdir.js.map +1 -1
- package/dist/cli/steps/update-package-json.js +8 -13
- package/dist/cli/steps/update-package-json.js.map +1 -1
- package/dist/cli/steps/update-ts-config.d.ts +6 -0
- package/dist/cli/steps/update-ts-config.js +27 -4
- package/dist/cli/steps/update-ts-config.js.map +1 -1
- package/dist/cli/utils.d.ts +2 -0
- package/dist/cli/utils.js +6 -0
- package/dist/cli/utils.js.map +1 -1
- package/dist/cli/utils.test.d.ts +1 -0
- package/dist/cli/utils.test.js +16 -0
- package/dist/cli/utils.test.js.map +1 -0
- package/dist/compiler/compile-annotation.js +6 -1
- package/dist/compiler/compile-annotation.js.map +1 -1
- package/dist/compiler/compile-bundle.d.ts +4 -0
- package/dist/compiler/compile-bundle.js +67 -2
- package/dist/compiler/compile-bundle.js.map +1 -1
- package/dist/compiler/compile-bundle.test.js +214 -1
- package/dist/compiler/compile-bundle.test.js.map +1 -1
- package/dist/compiler/compile-lifecycle.test.d.ts +1 -0
- package/dist/compiler/compile-lifecycle.test.js +69 -0
- package/dist/compiler/compile-lifecycle.test.js.map +1 -0
- package/dist/compiler/compile-local-variable.test.js +14 -14
- package/dist/compiler/compile-local-variable.test.js.map +1 -1
- package/dist/compiler/compile-message.exact-number.test.js +1 -1
- package/dist/compiler/compile-message.exact-number.test.js.map +1 -1
- package/dist/compiler/compile-message.locals.test.js +1 -1
- package/dist/compiler/compile-message.locals.test.js.map +1 -1
- package/dist/compiler/compile-message.test.js +2 -2
- package/dist/compiler/compile-message.test.js.map +1 -1
- package/dist/compiler/compile-pattern.test.js +5 -5
- package/dist/compiler/compile-pattern.test.js.map +1 -1
- package/dist/compiler/compile-project.js +1 -0
- package/dist/compiler/compile-project.js.map +1 -1
- package/dist/compiler/compile-project.test.js +94 -1
- package/dist/compiler/compile-project.test.js.map +1 -1
- package/dist/compiler/compile.js +36 -17
- package/dist/compiler/compile.js.map +1 -1
- package/dist/compiler/compile.test.js +70 -0
- package/dist/compiler/compile.test.js.map +1 -1
- package/dist/compiler/compiler-options.d.ts +41 -0
- package/dist/compiler/compiler-options.js.map +1 -1
- package/dist/compiler/output-structure/message-modules.js +1 -1
- package/dist/compiler/output-structure/message-modules.js.map +1 -1
- package/dist/compiler/registry.js +71 -13
- package/dist/compiler/registry.js.map +1 -1
- package/dist/compiler/registry.test.d.ts +1 -0
- package/dist/compiler/registry.test.js +291 -0
- package/dist/compiler/registry.test.js.map +1 -0
- package/dist/compiler/runtime/create-runtime.d.ts +2 -0
- package/dist/compiler/runtime/create-runtime.js +9 -0
- package/dist/compiler/runtime/create-runtime.js.map +1 -1
- package/dist/compiler/runtime/extract-locale-from-cookie.js +8 -1
- package/dist/compiler/runtime/extract-locale-from-cookie.js.map +1 -1
- package/dist/compiler/runtime/extract-locale-from-cookie.test.js +26 -0
- package/dist/compiler/runtime/extract-locale-from-cookie.test.js.map +1 -1
- package/dist/compiler/runtime/extract-locale-from-header.js +47 -5
- package/dist/compiler/runtime/extract-locale-from-header.js.map +1 -1
- package/dist/compiler/runtime/extract-locale-from-header.test.js +58 -0
- package/dist/compiler/runtime/extract-locale-from-header.test.js.map +1 -1
- package/dist/compiler/runtime/get-locale.js +7 -1
- package/dist/compiler/runtime/get-locale.js.map +1 -1
- package/dist/compiler/runtime/get-locale.test.js +95 -0
- package/dist/compiler/runtime/get-locale.test.js.map +1 -1
- package/dist/compiler/runtime/localize-href.d.ts +8 -0
- package/dist/compiler/runtime/localize-href.js +60 -0
- package/dist/compiler/runtime/localize-href.js.map +1 -1
- package/dist/compiler/runtime/localize-href.test.js +227 -1
- package/dist/compiler/runtime/localize-href.test.js.map +1 -1
- package/dist/compiler/runtime/set-locale.js +24 -6
- package/dist/compiler/runtime/set-locale.js.map +1 -1
- package/dist/compiler/runtime/set-locale.test.js +182 -0
- package/dist/compiler/runtime/set-locale.test.js.map +1 -1
- package/dist/compiler/runtime/type.d.ts +2 -0
- package/dist/compiler/runtime/type.js.map +1 -1
- package/dist/compiler/runtime/type.test.js +34 -0
- package/dist/compiler/runtime/type.test.js.map +1 -1
- package/dist/compiler/runtime/variables.d.ts +2 -0
- package/dist/compiler/runtime/variables.js +7 -0
- package/dist/compiler/runtime/variables.js.map +1 -1
- package/dist/compiler/server/middleware.d.ts +8 -2
- package/dist/compiler/server/middleware.js +175 -7
- package/dist/compiler/server/middleware.js.map +1 -1
- package/dist/compiler/server/middleware.test.js +750 -3
- package/dist/compiler/server/middleware.test.js.map +1 -1
- package/dist/eslint/index.d.ts +4 -0
- package/dist/eslint/index.js +8 -0
- package/dist/eslint/index.js.map +1 -0
- package/dist/eslint/no-dynamic-message-access.d.ts +3 -0
- package/dist/eslint/no-dynamic-message-access.js +150 -0
- package/dist/eslint/no-dynamic-message-access.js.map +1 -0
- package/dist/eslint/no-dynamic-message-access.test.d.ts +1 -0
- package/dist/eslint/no-dynamic-message-access.test.js +126 -0
- package/dist/eslint/no-dynamic-message-access.test.js.map +1 -0
- package/dist/eslint/oxlint.test.d.ts +1 -0
- package/dist/eslint/oxlint.test.js +64 -0
- package/dist/eslint/oxlint.test.js.map +1 -0
- package/dist/services/config/config-schema.d.ts +2 -0
- package/dist/services/config/config-schema.js +2 -0
- package/dist/services/config/config-schema.js.map +1 -1
- package/dist/services/config/load-paraglide-config.test.js +12 -0
- package/dist/services/config/load-paraglide-config.test.js.map +1 -1
- package/dist/services/env-variables/index.js +1 -1
- package/dist/services/env-variables/index.js.map +1 -1
- package/dist/services/file-handling/write-output.bench.js +51 -11
- package/dist/services/file-handling/write-output.bench.js.map +1 -1
- package/dist/services/file-handling/write-output.js +60 -20
- package/dist/services/file-handling/write-output.js.map +1 -1
- package/dist/services/file-handling/write-output.test.js +175 -0
- package/dist/services/file-handling/write-output.test.js.map +1 -1
- package/package.json +14 -4
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type fs from "node:fs/promises";
|
|
2
|
+
import type { Logger } from "../../services/logger/index.js";
|
|
3
|
+
export type MessageLint = "none" | "eslint" | "oxlint";
|
|
4
|
+
export declare function chooseMessageLint(ctx: {
|
|
5
|
+
interactive: boolean;
|
|
6
|
+
lint?: MessageLint;
|
|
7
|
+
}): Promise<MessageLint>;
|
|
8
|
+
export declare function messageLintConfig(lint: Exclude<MessageLint, "none">, outdir: string): {
|
|
9
|
+
filename: string;
|
|
10
|
+
source: string;
|
|
11
|
+
command: string;
|
|
12
|
+
};
|
|
13
|
+
export declare function preflightMessageLint(ctx: {
|
|
14
|
+
fs: typeof fs;
|
|
15
|
+
root: string;
|
|
16
|
+
packageJsonPath: string;
|
|
17
|
+
outdir: string;
|
|
18
|
+
lint: MessageLint;
|
|
19
|
+
}): Promise<void>;
|
|
20
|
+
export declare function addMessageLint(ctx: {
|
|
21
|
+
fs: typeof fs;
|
|
22
|
+
root: string;
|
|
23
|
+
packageJsonPath: string;
|
|
24
|
+
outdir: string;
|
|
25
|
+
logger: Logger;
|
|
26
|
+
lint: MessageLint;
|
|
27
|
+
}): Promise<{
|
|
28
|
+
fs: typeof fs;
|
|
29
|
+
root: string;
|
|
30
|
+
packageJsonPath: string;
|
|
31
|
+
outdir: string;
|
|
32
|
+
logger: Logger;
|
|
33
|
+
lint: MessageLint;
|
|
34
|
+
}>;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import nodePath from "node:path";
|
|
2
|
+
import { promptSelection } from "../utils.js";
|
|
3
|
+
import { updatePackageJson } from "./update-package-json.js";
|
|
4
|
+
export async function chooseMessageLint(ctx) {
|
|
5
|
+
if (ctx.lint)
|
|
6
|
+
return ctx.lint;
|
|
7
|
+
if (!ctx.interactive)
|
|
8
|
+
return "none";
|
|
9
|
+
return promptSelection("Set up linting to guard message tree-shaking?", {
|
|
10
|
+
initial: "none",
|
|
11
|
+
options: [
|
|
12
|
+
{ label: "Skip", value: "none" },
|
|
13
|
+
{ label: "ESLint", value: "eslint" },
|
|
14
|
+
{ label: "Oxlint", value: "oxlint" },
|
|
15
|
+
],
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
export function messageLintConfig(lint, outdir) {
|
|
19
|
+
const directory = nodePath.basename(outdir);
|
|
20
|
+
const rule = ["error", { messageModuleDirectories: [directory] }];
|
|
21
|
+
if (lint === "oxlint")
|
|
22
|
+
return {
|
|
23
|
+
filename: ".oxlintrc.paraglide.json",
|
|
24
|
+
source: JSON.stringify({
|
|
25
|
+
plugins: [],
|
|
26
|
+
jsPlugins: [
|
|
27
|
+
{ name: "paraglide", specifier: "@inlang/paraglide-js/eslint" },
|
|
28
|
+
],
|
|
29
|
+
ignorePatterns: [outdir.replace(/^\.\//, "") + "/**"],
|
|
30
|
+
rules: { "paraglide/no-dynamic-message-access": rule },
|
|
31
|
+
}, null, 2) + "\n",
|
|
32
|
+
command: "oxlint --config ./.oxlintrc.paraglide.json .",
|
|
33
|
+
};
|
|
34
|
+
return {
|
|
35
|
+
filename: "eslint.paraglide.config.mjs",
|
|
36
|
+
source: `import paraglide from "@inlang/paraglide-js/eslint";
|
|
37
|
+
import tsParser from "@typescript-eslint/parser";
|
|
38
|
+
|
|
39
|
+
export default [
|
|
40
|
+
{ ignores: [${JSON.stringify(outdir.replace(/^\.\//, "") + "/**")}, "node_modules/**"] },
|
|
41
|
+
{
|
|
42
|
+
files: ["**/*.{js,jsx,mjs,cjs,ts,tsx,mts,cts}"],
|
|
43
|
+
languageOptions: { parser: tsParser, parserOptions: { ecmaFeatures: { jsx: true } } },
|
|
44
|
+
plugins: { paraglide },
|
|
45
|
+
rules: { "paraglide/no-dynamic-message-access": ${JSON.stringify(rule)} },
|
|
46
|
+
},
|
|
47
|
+
];
|
|
48
|
+
`,
|
|
49
|
+
command: "eslint --config ./eslint.paraglide.config.mjs .",
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export async function preflightMessageLint(ctx) {
|
|
53
|
+
if (ctx.lint === "none")
|
|
54
|
+
return;
|
|
55
|
+
const config = messageLintConfig(ctx.lint, ctx.outdir);
|
|
56
|
+
const pkg = JSON.parse(await ctx.fs.readFile(ctx.packageJsonPath, "utf8"));
|
|
57
|
+
if (pkg.scripts?.["lint:i18n"] && pkg.scripts["lint:i18n"] !== config.command)
|
|
58
|
+
throw new Error("An unrelated lint:i18n script exists. Rename it before setting up message linting.");
|
|
59
|
+
const existing = await ctx.fs
|
|
60
|
+
.readFile(nodePath.join(ctx.root, config.filename), "utf8")
|
|
61
|
+
.catch((error) => {
|
|
62
|
+
if (error.code === "ENOENT")
|
|
63
|
+
return undefined;
|
|
64
|
+
throw error;
|
|
65
|
+
});
|
|
66
|
+
if (existing !== undefined && existing !== config.source)
|
|
67
|
+
throw new Error(`${config.filename} already exists with different content; it will not be overwritten.`);
|
|
68
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
69
|
+
const parserMajor = String(deps["@typescript-eslint/parser"] ?? "").match(/^(?:[~^]|>=)?(\d+)/)?.[1];
|
|
70
|
+
if (ctx.lint === "eslint" && parserMajor && Number(parserMajor) < 8)
|
|
71
|
+
throw new Error("Message lint setup requires @typescript-eslint/parser 8 or later. Update the existing dependency first.");
|
|
72
|
+
const major = String(deps.eslint ?? "").match(/^(?:[~^]|>=)?(\d+)/)?.[1];
|
|
73
|
+
if (ctx.lint === "eslint" && major && Number(major) < 9)
|
|
74
|
+
throw new Error("Message linting requires ESLint 9 or 10. Update the existing ESLint dependency first.");
|
|
75
|
+
const oxVersion = String(deps.oxlint ?? "").match(/^(?:[~^]|>=)?(\d+)\.(\d+)/);
|
|
76
|
+
if (ctx.lint === "oxlint" &&
|
|
77
|
+
oxVersion &&
|
|
78
|
+
Number(oxVersion[1]) === 1 &&
|
|
79
|
+
Number(oxVersion[2]) < 88)
|
|
80
|
+
throw new Error("Message lint setup requires Oxlint 1.88 or later. Update the existing dependency first.");
|
|
81
|
+
}
|
|
82
|
+
export async function addMessageLint(ctx) {
|
|
83
|
+
if (ctx.lint === "none")
|
|
84
|
+
return ctx;
|
|
85
|
+
await preflightMessageLint(ctx);
|
|
86
|
+
const config = messageLintConfig(ctx.lint, ctx.outdir);
|
|
87
|
+
await ctx.fs.writeFile(nodePath.join(ctx.root, config.filename), config.source);
|
|
88
|
+
await updatePackageJson({
|
|
89
|
+
devDependencies: async (deps) => {
|
|
90
|
+
const pkg = JSON.parse(await ctx.fs.readFile(ctx.packageJsonPath, "utf8"));
|
|
91
|
+
const known = { ...pkg.dependencies, ...deps };
|
|
92
|
+
const needed = ctx.lint === "eslint"
|
|
93
|
+
? { eslint: "^9.39.2", "@typescript-eslint/parser": "^8.71.2" }
|
|
94
|
+
: { oxlint: "^1.88.0" };
|
|
95
|
+
return {
|
|
96
|
+
...deps,
|
|
97
|
+
...Object.fromEntries(Object.entries(needed).filter(([name]) => !known[name])),
|
|
98
|
+
};
|
|
99
|
+
},
|
|
100
|
+
scripts: async (scripts) => ({ ...scripts, "lint:i18n": config.command }),
|
|
101
|
+
})(ctx);
|
|
102
|
+
ctx.logger.success(`Added ${ctx.lint} message linting. After installing dependencies, run npm run lint:i18n. Existing lint configurations are unchanged.`);
|
|
103
|
+
return ctx;
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=add-message-lint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"add-message-lint.js","sourceRoot":"","sources":["../../../src/cli/steps/add-message-lint.ts"],"names":[],"mappings":"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","sourcesContent":["import type fs from \"node:fs/promises\";\nimport nodePath from \"node:path\";\nimport { promptSelection } from \"../utils.js\";\nimport type { Logger } from \"../../services/logger/index.js\";\nimport { updatePackageJson } from \"./update-package-json.js\";\n\nexport type MessageLint = \"none\" | \"eslint\" | \"oxlint\";\nexport async function chooseMessageLint(ctx: {\n\tinteractive: boolean;\n\tlint?: MessageLint;\n}): Promise<MessageLint> {\n\tif (ctx.lint) return ctx.lint;\n\tif (!ctx.interactive) return \"none\";\n\treturn promptSelection(\"Set up linting to guard message tree-shaking?\", {\n\t\tinitial: \"none\",\n\t\toptions: [\n\t\t\t{ label: \"Skip\", value: \"none\" },\n\t\t\t{ label: \"ESLint\", value: \"eslint\" },\n\t\t\t{ label: \"Oxlint\", value: \"oxlint\" },\n\t\t],\n\t});\n}\n\nexport function messageLintConfig(\n\tlint: Exclude<MessageLint, \"none\">,\n\toutdir: string\n): { filename: string; source: string; command: string } {\n\tconst directory = nodePath.basename(outdir);\n\tconst rule = [\"error\", { messageModuleDirectories: [directory] }];\n\tif (lint === \"oxlint\")\n\t\treturn {\n\t\t\tfilename: \".oxlintrc.paraglide.json\",\n\t\t\tsource:\n\t\t\t\tJSON.stringify(\n\t\t\t\t\t{\n\t\t\t\t\t\tplugins: [],\n\t\t\t\t\t\tjsPlugins: [\n\t\t\t\t\t\t\t{ name: \"paraglide\", specifier: \"@inlang/paraglide-js/eslint\" },\n\t\t\t\t\t\t],\n\t\t\t\t\t\tignorePatterns: [outdir.replace(/^\\.\\//, \"\") + \"/**\"],\n\t\t\t\t\t\trules: { \"paraglide/no-dynamic-message-access\": rule },\n\t\t\t\t\t},\n\t\t\t\t\tnull,\n\t\t\t\t\t2\n\t\t\t\t) + \"\\n\",\n\t\t\tcommand: \"oxlint --config ./.oxlintrc.paraglide.json .\",\n\t\t};\n\treturn {\n\t\tfilename: \"eslint.paraglide.config.mjs\",\n\t\tsource: `import paraglide from \"@inlang/paraglide-js/eslint\";\nimport tsParser from \"@typescript-eslint/parser\";\n\nexport default [\n { ignores: [${JSON.stringify(outdir.replace(/^\\.\\//, \"\") + \"/**\")}, \"node_modules/**\"] },\n {\n files: [\"**/*.{js,jsx,mjs,cjs,ts,tsx,mts,cts}\"],\n languageOptions: { parser: tsParser, parserOptions: { ecmaFeatures: { jsx: true } } },\n plugins: { paraglide },\n rules: { \"paraglide/no-dynamic-message-access\": ${JSON.stringify(rule)} },\n },\n];\n`,\n\t\tcommand: \"eslint --config ./eslint.paraglide.config.mjs .\",\n\t};\n}\n\nexport async function preflightMessageLint(ctx: {\n\tfs: typeof fs;\n\troot: string;\n\tpackageJsonPath: string;\n\toutdir: string;\n\tlint: MessageLint;\n}) {\n\tif (ctx.lint === \"none\") return;\n\tconst config = messageLintConfig(ctx.lint, ctx.outdir);\n\tconst pkg = JSON.parse(await ctx.fs.readFile(ctx.packageJsonPath, \"utf8\"));\n\tif (pkg.scripts?.[\"lint:i18n\"] && pkg.scripts[\"lint:i18n\"] !== config.command)\n\t\tthrow new Error(\n\t\t\t\"An unrelated lint:i18n script exists. Rename it before setting up message linting.\"\n\t\t);\n\tconst existing = await ctx.fs\n\t\t.readFile(nodePath.join(ctx.root, config.filename), \"utf8\")\n\t\t.catch((error: NodeJS.ErrnoException) => {\n\t\t\tif (error.code === \"ENOENT\") return undefined;\n\t\t\tthrow error;\n\t\t});\n\tif (existing !== undefined && existing !== config.source)\n\t\tthrow new Error(\n\t\t\t`${config.filename} already exists with different content; it will not be overwritten.`\n\t\t);\n\tconst deps = { ...pkg.dependencies, ...pkg.devDependencies };\n\tconst parserMajor = String(deps[\"@typescript-eslint/parser\"] ?? \"\").match(\n\t\t/^(?:[~^]|>=)?(\\d+)/\n\t)?.[1];\n\tif (ctx.lint === \"eslint\" && parserMajor && Number(parserMajor) < 8)\n\t\tthrow new Error(\n\t\t\t\"Message lint setup requires @typescript-eslint/parser 8 or later. Update the existing dependency first.\"\n\t\t);\n\tconst major = String(deps.eslint ?? \"\").match(/^(?:[~^]|>=)?(\\d+)/)?.[1];\n\tif (ctx.lint === \"eslint\" && major && Number(major) < 9)\n\t\tthrow new Error(\n\t\t\t\"Message linting requires ESLint 9 or 10. Update the existing ESLint dependency first.\"\n\t\t);\n\tconst oxVersion = String(deps.oxlint ?? \"\").match(\n\t\t/^(?:[~^]|>=)?(\\d+)\\.(\\d+)/\n\t);\n\tif (\n\t\tctx.lint === \"oxlint\" &&\n\t\toxVersion &&\n\t\tNumber(oxVersion[1]) === 1 &&\n\t\tNumber(oxVersion[2]) < 88\n\t)\n\t\tthrow new Error(\n\t\t\t\"Message lint setup requires Oxlint 1.88 or later. Update the existing dependency first.\"\n\t\t);\n}\n\nexport async function addMessageLint(ctx: {\n\tfs: typeof fs;\n\troot: string;\n\tpackageJsonPath: string;\n\toutdir: string;\n\tlogger: Logger;\n\tlint: MessageLint;\n}) {\n\tif (ctx.lint === \"none\") return ctx;\n\tawait preflightMessageLint(ctx);\n\tconst config = messageLintConfig(ctx.lint, ctx.outdir);\n\tawait ctx.fs.writeFile(\n\t\tnodePath.join(ctx.root, config.filename),\n\t\tconfig.source\n\t);\n\tawait updatePackageJson({\n\t\tdevDependencies: async (deps) => {\n\t\t\tconst pkg = JSON.parse(\n\t\t\t\tawait ctx.fs.readFile(ctx.packageJsonPath, \"utf8\")\n\t\t\t);\n\t\t\tconst known = { ...pkg.dependencies, ...deps };\n\t\t\tconst needed =\n\t\t\t\tctx.lint === \"eslint\"\n\t\t\t\t\t? { eslint: \"^9.39.2\", \"@typescript-eslint/parser\": \"^8.71.2\" }\n\t\t\t\t\t: { oxlint: \"^1.88.0\" };\n\t\t\treturn {\n\t\t\t\t...deps,\n\t\t\t\t...Object.fromEntries(\n\t\t\t\t\tObject.entries(needed).filter(([name]) => !known[name])\n\t\t\t\t),\n\t\t\t};\n\t\t},\n\t\tscripts: async (scripts) => ({ ...scripts, \"lint:i18n\": config.command }),\n\t})(ctx);\n\tctx.logger.success(\n\t\t`Added ${ctx.lint} message linting. After installing dependencies, run npm run lint:i18n. Existing lint configurations are unchanged.`\n\t);\n\treturn ctx;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { test, expect } from "vitest";
|
|
2
|
+
import { mkdtemp, writeFile, readFile, rm } from "node:fs/promises";
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import { addMessageLint, preflightMessageLint } from "./add-message-lint.js";
|
|
7
|
+
import { Logger } from "../../services/logger/index.js";
|
|
8
|
+
for (const lint of ["eslint", "oxlint"])
|
|
9
|
+
test(`${lint} setup is optional, additive and repeatable`, async () => {
|
|
10
|
+
const root = await mkdtemp(join(tmpdir(), "paraglide-lint-init-"));
|
|
11
|
+
try {
|
|
12
|
+
const packageJsonPath = join(root, "package.json");
|
|
13
|
+
const configPath = join(root, lint === "eslint" ? "eslint.config.mjs" : ".oxlintrc.json");
|
|
14
|
+
const original = "// existing config sentinel\n";
|
|
15
|
+
await writeFile(configPath, original);
|
|
16
|
+
await writeFile(packageJsonPath, JSON.stringify({
|
|
17
|
+
scripts: { lint: "existing-lint" },
|
|
18
|
+
devDependencies: lint === "eslint" ? { eslint: "^10.0.0" } : { oxlint: "^1.88.0" },
|
|
19
|
+
}));
|
|
20
|
+
const ctx = {
|
|
21
|
+
fs,
|
|
22
|
+
root,
|
|
23
|
+
packageJsonPath,
|
|
24
|
+
outdir: "./src/i18n",
|
|
25
|
+
lint,
|
|
26
|
+
logger: new Logger({ silent: true, prefix: false }),
|
|
27
|
+
};
|
|
28
|
+
await addMessageLint(ctx);
|
|
29
|
+
const first = await readFile(packageJsonPath, "utf8");
|
|
30
|
+
await addMessageLint(ctx);
|
|
31
|
+
expect(await readFile(packageJsonPath, "utf8")).toBe(first);
|
|
32
|
+
expect(await readFile(configPath, "utf8")).toBe(original);
|
|
33
|
+
const pkg = JSON.parse(first);
|
|
34
|
+
expect(pkg.scripts.lint).toBe("existing-lint");
|
|
35
|
+
expect(pkg.scripts["lint:i18n"]).toContain(lint === "eslint"
|
|
36
|
+
? "eslint.paraglide.config.mjs"
|
|
37
|
+
: ".oxlintrc.paraglide.json");
|
|
38
|
+
expect(pkg.devDependencies[lint]).toBe(lint === "eslint" ? "^10.0.0" : "^1.88.0");
|
|
39
|
+
const generated = await readFile(join(root, lint === "eslint"
|
|
40
|
+
? "eslint.paraglide.config.mjs"
|
|
41
|
+
: ".oxlintrc.paraglide.json"), "utf8");
|
|
42
|
+
expect(generated).toContain("messageModuleDirectories");
|
|
43
|
+
expect(generated).toContain("i18n");
|
|
44
|
+
}
|
|
45
|
+
finally {
|
|
46
|
+
await rm(root, { recursive: true, force: true });
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
test("lint conflicts fail before writing dependencies or replacing files", async () => {
|
|
50
|
+
const root = await mkdtemp(join(tmpdir(), "paraglide-lint-conflict-"));
|
|
51
|
+
try {
|
|
52
|
+
const packageJsonPath = join(root, "package.json");
|
|
53
|
+
const before = JSON.stringify({ scripts: { "lint:i18n": "custom-tool" } });
|
|
54
|
+
await writeFile(packageJsonPath, before);
|
|
55
|
+
const ctx = {
|
|
56
|
+
fs,
|
|
57
|
+
root,
|
|
58
|
+
packageJsonPath,
|
|
59
|
+
outdir: "./src/paraglide",
|
|
60
|
+
lint: "eslint",
|
|
61
|
+
logger: new Logger({ silent: true, prefix: false }),
|
|
62
|
+
};
|
|
63
|
+
await expect(preflightMessageLint(ctx)).rejects.toThrow("unrelated lint:i18n");
|
|
64
|
+
expect(await readFile(packageJsonPath, "utf8")).toBe(before);
|
|
65
|
+
await writeFile(packageJsonPath, "{}");
|
|
66
|
+
await writeFile(join(root, "eslint.paraglide.config.mjs"), "custom sentinel");
|
|
67
|
+
await expect(addMessageLint(ctx)).rejects.toThrow("not be overwritten");
|
|
68
|
+
expect(await readFile(join(root, "eslint.paraglide.config.mjs"), "utf8")).toBe("custom sentinel");
|
|
69
|
+
}
|
|
70
|
+
finally {
|
|
71
|
+
await rm(root, { recursive: true, force: true });
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
//# sourceMappingURL=add-message-lint.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"add-message-lint.test.js","sourceRoot":"","sources":["../../../src/cli/steps/add-message-lint.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { test, expect } from \"vitest\";\nimport { mkdtemp, writeFile, readFile, rm } from \"node:fs/promises\";\nimport fs from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { tmpdir } from \"node:os\";\nimport { addMessageLint, preflightMessageLint } from \"./add-message-lint.js\";\nimport { Logger } from \"../../services/logger/index.js\";\n\nfor (const lint of [\"eslint\", \"oxlint\"] as const)\n\ttest(`${lint} setup is optional, additive and repeatable`, async () => {\n\t\tconst root = await mkdtemp(join(tmpdir(), \"paraglide-lint-init-\"));\n\t\ttry {\n\t\t\tconst packageJsonPath = join(root, \"package.json\");\n\t\t\tconst configPath = join(\n\t\t\t\troot,\n\t\t\t\tlint === \"eslint\" ? \"eslint.config.mjs\" : \".oxlintrc.json\"\n\t\t\t);\n\t\t\tconst original = \"// existing config sentinel\\n\";\n\t\t\tawait writeFile(configPath, original);\n\t\t\tawait writeFile(\n\t\t\t\tpackageJsonPath,\n\t\t\t\tJSON.stringify({\n\t\t\t\t\tscripts: { lint: \"existing-lint\" },\n\t\t\t\t\tdevDependencies:\n\t\t\t\t\t\tlint === \"eslint\" ? { eslint: \"^10.0.0\" } : { oxlint: \"^1.88.0\" },\n\t\t\t\t})\n\t\t\t);\n\t\t\tconst ctx = {\n\t\t\t\tfs,\n\t\t\t\troot,\n\t\t\t\tpackageJsonPath,\n\t\t\t\toutdir: \"./src/i18n\",\n\t\t\t\tlint,\n\t\t\t\tlogger: new Logger({ silent: true, prefix: false }),\n\t\t\t};\n\t\t\tawait addMessageLint(ctx);\n\t\t\tconst first = await readFile(packageJsonPath, \"utf8\");\n\t\t\tawait addMessageLint(ctx);\n\t\t\texpect(await readFile(packageJsonPath, \"utf8\")).toBe(first);\n\t\t\texpect(await readFile(configPath, \"utf8\")).toBe(original);\n\t\t\tconst pkg = JSON.parse(first);\n\t\t\texpect(pkg.scripts.lint).toBe(\"existing-lint\");\n\t\t\texpect(pkg.scripts[\"lint:i18n\"]).toContain(\n\t\t\t\tlint === \"eslint\"\n\t\t\t\t\t? \"eslint.paraglide.config.mjs\"\n\t\t\t\t\t: \".oxlintrc.paraglide.json\"\n\t\t\t);\n\t\t\texpect(pkg.devDependencies[lint]).toBe(\n\t\t\t\tlint === \"eslint\" ? \"^10.0.0\" : \"^1.88.0\"\n\t\t\t);\n\t\t\tconst generated = await readFile(\n\t\t\t\tjoin(\n\t\t\t\t\troot,\n\t\t\t\t\tlint === \"eslint\"\n\t\t\t\t\t\t? \"eslint.paraglide.config.mjs\"\n\t\t\t\t\t\t: \".oxlintrc.paraglide.json\"\n\t\t\t\t),\n\t\t\t\t\"utf8\"\n\t\t\t);\n\t\t\texpect(generated).toContain(\"messageModuleDirectories\");\n\t\t\texpect(generated).toContain(\"i18n\");\n\t\t} finally {\n\t\t\tawait rm(root, { recursive: true, force: true });\n\t\t}\n\t});\n\ntest(\"lint conflicts fail before writing dependencies or replacing files\", async () => {\n\tconst root = await mkdtemp(join(tmpdir(), \"paraglide-lint-conflict-\"));\n\ttry {\n\t\tconst packageJsonPath = join(root, \"package.json\");\n\t\tconst before = JSON.stringify({ scripts: { \"lint:i18n\": \"custom-tool\" } });\n\t\tawait writeFile(packageJsonPath, before);\n\t\tconst ctx = {\n\t\t\tfs,\n\t\t\troot,\n\t\t\tpackageJsonPath,\n\t\t\toutdir: \"./src/paraglide\",\n\t\t\tlint: \"eslint\" as const,\n\t\t\tlogger: new Logger({ silent: true, prefix: false }),\n\t\t};\n\t\tawait expect(preflightMessageLint(ctx)).rejects.toThrow(\n\t\t\t\"unrelated lint:i18n\"\n\t\t);\n\t\texpect(await readFile(packageJsonPath, \"utf8\")).toBe(before);\n\t\tawait writeFile(packageJsonPath, \"{}\");\n\t\tawait writeFile(\n\t\t\tjoin(root, \"eslint.paraglide.config.mjs\"),\n\t\t\t\"custom sentinel\"\n\t\t);\n\t\tawait expect(addMessageLint(ctx)).rejects.toThrow(\"not be overwritten\");\n\t\texpect(\n\t\t\tawait readFile(join(root, \"eslint.paraglide.config.mjs\"), \"utf8\")\n\t\t).toBe(\"custom sentinel\");\n\t} finally {\n\t\tawait rm(root, { recursive: true, force: true });\n\t}\n});\n"]}
|
|
@@ -7,3 +7,21 @@ export declare const addVitePlugin: CliStep<{
|
|
|
7
7
|
logger?: Logger;
|
|
8
8
|
configPath: string;
|
|
9
9
|
}, unknown>;
|
|
10
|
+
type UpdateResult = {
|
|
11
|
+
ok: true;
|
|
12
|
+
updated: string;
|
|
13
|
+
reason?: undefined;
|
|
14
|
+
} | {
|
|
15
|
+
ok: false;
|
|
16
|
+
reason: string;
|
|
17
|
+
updated?: undefined;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* @private Only exported for testings
|
|
21
|
+
*/
|
|
22
|
+
export declare function updateViteConfig(args: {
|
|
23
|
+
config: string;
|
|
24
|
+
projectPath: string;
|
|
25
|
+
outdir: string;
|
|
26
|
+
}): UpdateResult;
|
|
27
|
+
export {};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import JSON5 from "json5";
|
|
2
|
+
import path from "node:path";
|
|
1
3
|
export const addVitePlugin = async (ctx) => {
|
|
2
4
|
try {
|
|
3
5
|
const fileContent = await ctx.fs.readFile(ctx.configPath, {
|
|
@@ -8,6 +10,8 @@ export const addVitePlugin = async (ctx) => {
|
|
|
8
10
|
projectPath: ctx.projectPath,
|
|
9
11
|
outdir: ctx.outdir,
|
|
10
12
|
});
|
|
13
|
+
if (result.reason === "Already present")
|
|
14
|
+
return ctx;
|
|
11
15
|
if (result.ok) {
|
|
12
16
|
await ctx.fs.writeFile(ctx.configPath, result.updated);
|
|
13
17
|
ctx.logger?.success("Added the Paraglide JS vite plugin to the vite config.");
|
|
@@ -32,12 +36,36 @@ export const addVitePlugin = async (ctx) => {
|
|
|
32
36
|
/**
|
|
33
37
|
* @private Only exported for testings
|
|
34
38
|
*/
|
|
35
|
-
function updateViteConfig(args) {
|
|
39
|
+
export function updateViteConfig(args) {
|
|
36
40
|
if (args.config.includes("@inlang/paraglide-js")) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
const call = args.config.match(/\bparaglideVitePlugin\s*\(\s*\{([\s\S]*?)\}\s*\)/);
|
|
42
|
+
if (!call)
|
|
43
|
+
return {
|
|
44
|
+
ok: false,
|
|
45
|
+
reason: "Existing Paraglide import needs manual plugin integration",
|
|
46
|
+
};
|
|
47
|
+
try {
|
|
48
|
+
const options = JSON5.parse("{" + call[1] + "}");
|
|
49
|
+
if (typeof options.project !== "string" ||
|
|
50
|
+
typeof options.outdir !== "string")
|
|
51
|
+
return {
|
|
52
|
+
ok: false,
|
|
53
|
+
reason: "Existing plugin paths must be configured explicitly",
|
|
54
|
+
};
|
|
55
|
+
if (path.resolve(options.project) !== path.resolve(args.projectPath) ||
|
|
56
|
+
path.resolve(options.outdir) !== path.resolve(args.outdir))
|
|
57
|
+
return {
|
|
58
|
+
ok: false,
|
|
59
|
+
reason: "Existing Vite plugin paths differ from the requested project/outdir",
|
|
60
|
+
};
|
|
61
|
+
return { ok: false, reason: "Already present" };
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return {
|
|
65
|
+
ok: false,
|
|
66
|
+
reason: "Existing Vite plugin options cannot be verified automatically",
|
|
67
|
+
};
|
|
68
|
+
}
|
|
41
69
|
}
|
|
42
70
|
const PLUGINS_REGEX = /plugins\s*:\s*\[/g;
|
|
43
71
|
const match = PLUGINS_REGEX.exec(args.config);
|
|
@@ -52,7 +80,7 @@ function updateViteConfig(args) {
|
|
|
52
80
|
const after = args.config.slice(endIndex);
|
|
53
81
|
const updatedConfig = "import { paraglideVitePlugin } from '@inlang/paraglide-js'\n" +
|
|
54
82
|
before +
|
|
55
|
-
`paraglideVitePlugin({ project:
|
|
83
|
+
`paraglideVitePlugin({ project: ${JSON.stringify(args.projectPath)}, outdir: ${JSON.stringify(args.outdir)} }),` +
|
|
56
84
|
after;
|
|
57
85
|
return {
|
|
58
86
|
ok: true,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"add-vite-plugin.js","sourceRoot":"","sources":["../../../src/cli/steps/add-vite-plugin.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"add-vite-plugin.js","sourceRoot":"","sources":["../../../src/cli/steps/add-vite-plugin.ts"],"names":[],"mappings":"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","sourcesContent":["import JSON5 from \"json5\";\nimport path from \"node:path\";\nimport type { Logger } from \"../../services/logger/index.js\";\nimport type { CliStep } from \"../utils.js\";\n\nexport const addVitePlugin: CliStep<\n\t{\n\t\tfs: typeof import(\"node:fs/promises\");\n\t\tprojectPath: string;\n\t\toutdir: string;\n\t\tlogger?: Logger;\n\t\tconfigPath: string;\n\t},\n\tunknown\n> = async (ctx) => {\n\ttry {\n\t\tconst fileContent = await ctx.fs.readFile(ctx.configPath, {\n\t\t\tencoding: \"utf-8\",\n\t\t});\n\n\t\tconst result = updateViteConfig({\n\t\t\tconfig: fileContent,\n\t\t\tprojectPath: ctx.projectPath,\n\t\t\toutdir: ctx.outdir,\n\t\t});\n\t\tif (result.reason === \"Already present\") return ctx;\n\t\tif (result.ok) {\n\t\t\tawait ctx.fs.writeFile(ctx.configPath, result.updated);\n\t\t\tctx.logger?.success(\n\t\t\t\t\"Added the Paraglide JS vite plugin to the vite config.\"\n\t\t\t);\n\t\t} else {\n\t\t\tconst msg = [\n\t\t\t\t\"Failed to add the Paraglide JS vite plugin in vite.config.js.\",\n\t\t\t\t\"Reason: \" + result.reason,\n\t\t\t\t\"\",\n\t\t\t\t\"Please add the plugin manually.\",\n\t\t\t].join(\"\\n\");\n\n\t\t\tctx.logger?.warn(msg);\n\t\t\treturn ctx;\n\t\t}\n\t} catch {\n\t\tctx.logger?.error(\n\t\t\t`Failed to add the Paraglide JS vite plugin to ${ctx.configPath}.`\n\t\t);\n\t\tprocess.exit(1);\n\t}\n\n\treturn ctx;\n};\n\ntype UpdateResult =\n\t| {\n\t\t\tok: true;\n\t\t\tupdated: string;\n\t\t\treason?: undefined;\n\t }\n\t| {\n\t\t\tok: false;\n\t\t\treason: string;\n\t\t\tupdated?: undefined;\n\t };\n\n/**\n * @private Only exported for testings\n */\nexport function updateViteConfig(args: {\n\tconfig: string;\n\tprojectPath: string;\n\toutdir: string;\n}): UpdateResult {\n\tif (args.config.includes(\"@inlang/paraglide-js\")) {\n\t\tconst call = args.config.match(\n\t\t\t/\\bparaglideVitePlugin\\s*\\(\\s*\\{([\\s\\S]*?)\\}\\s*\\)/\n\t\t);\n\t\tif (!call)\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\treason: \"Existing Paraglide import needs manual plugin integration\",\n\t\t\t};\n\t\ttry {\n\t\t\tconst options = JSON5.parse(\"{\" + call[1] + \"}\");\n\t\t\tif (\n\t\t\t\ttypeof options.project !== \"string\" ||\n\t\t\t\ttypeof options.outdir !== \"string\"\n\t\t\t)\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\treason: \"Existing plugin paths must be configured explicitly\",\n\t\t\t\t};\n\t\t\tif (\n\t\t\t\tpath.resolve(options.project) !== path.resolve(args.projectPath) ||\n\t\t\t\tpath.resolve(options.outdir) !== path.resolve(args.outdir)\n\t\t\t)\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\treason:\n\t\t\t\t\t\t\"Existing Vite plugin paths differ from the requested project/outdir\",\n\t\t\t\t};\n\t\t\treturn { ok: false, reason: \"Already present\" };\n\t\t} catch {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\treason: \"Existing Vite plugin options cannot be verified automatically\",\n\t\t\t};\n\t\t}\n\t}\n\n\tconst PLUGINS_REGEX = /plugins\\s*:\\s*\\[/g;\n\tconst match = PLUGINS_REGEX.exec(args.config);\n\n\tif (!match) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\treason: \"Could not find the plugins array\",\n\t\t};\n\t}\n\n\tconst endIndex = match.index + match[0].length;\n\tconst before = args.config.slice(0, endIndex);\n\tconst after = args.config.slice(endIndex);\n\n\tconst updatedConfig =\n\t\t\"import { paraglideVitePlugin } from '@inlang/paraglide-js'\\n\" +\n\t\tbefore +\n\t\t`paraglideVitePlugin({ project: ${JSON.stringify(args.projectPath)}, outdir: ${JSON.stringify(args.outdir)} }),` +\n\t\tafter;\n\n\treturn {\n\t\tok: true,\n\t\tupdated: updatedConfig,\n\t};\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { expect, test } from "vitest";
|
|
2
|
-
import { addVitePlugin } from "./add-vite-plugin.js";
|
|
2
|
+
import { addVitePlugin, updateViteConfig } from "./add-vite-plugin.js";
|
|
3
3
|
import { memfs } from "memfs";
|
|
4
4
|
test("updates a vite.config.ts", async () => {
|
|
5
5
|
const config = `import { sveltekit } from "@sveltejs/kit/vite"
|
|
@@ -22,15 +22,15 @@ export default defineConfig({
|
|
|
22
22
|
encoding: "utf-8",
|
|
23
23
|
});
|
|
24
24
|
expect(updatedConfig).toMatchInlineSnapshot(`
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
"import { paraglideVitePlugin } from '@inlang/paraglide-js'
|
|
26
|
+
import { sveltekit } from "@sveltejs/kit/vite"
|
|
27
|
+
import { defineConfig } from "vite"
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
export default defineConfig({
|
|
30
|
+
plugins: [paraglideVitePlugin({ project: "./project.inlang", outdir: "./src/paraglide" }),sveltekit()],
|
|
31
|
+
})
|
|
32
|
+
"
|
|
33
|
+
`);
|
|
34
34
|
});
|
|
35
35
|
test("doesn't update configs where the paraglide plugin is already present", async () => {
|
|
36
36
|
const config = `
|
|
@@ -67,4 +67,20 @@ test("doesn't update configs where the paraglide plugin is already present", asy
|
|
|
67
67
|
});
|
|
68
68
|
expect(updatedConfig).toMatch(config);
|
|
69
69
|
});
|
|
70
|
+
test("existing integration must match requested paths before unattended reuse", () => {
|
|
71
|
+
const config = 'import {paraglideVitePlugin} from "@inlang/paraglide-js"; export default {plugins:[paraglideVitePlugin({project:"./project.inlang",outdir:"./src/paraglide"})]}';
|
|
72
|
+
expect(updateViteConfig({
|
|
73
|
+
config,
|
|
74
|
+
projectPath: "./project.inlang",
|
|
75
|
+
outdir: "./src/different",
|
|
76
|
+
})).toMatchObject({
|
|
77
|
+
ok: false,
|
|
78
|
+
reason: "Existing Vite plugin paths differ from the requested project/outdir",
|
|
79
|
+
});
|
|
80
|
+
expect(updateViteConfig({
|
|
81
|
+
config: 'import {compile} from "@inlang/paraglide-js"; export default {plugins:[]}',
|
|
82
|
+
projectPath: "./project.inlang",
|
|
83
|
+
outdir: "./src/paraglide",
|
|
84
|
+
})).toMatchObject({ ok: false });
|
|
85
|
+
});
|
|
70
86
|
//# sourceMappingURL=add-vite-plugin.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"add-vite-plugin.test.js","sourceRoot":"","sources":["../../../src/cli/steps/add-vite-plugin.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"add-vite-plugin.test.js","sourceRoot":"","sources":["../../../src/cli/steps/add-vite-plugin.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACvE,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAE9B,IAAI,CAAC,0BAA0B,EAAE,KAAK,IAAI,EAAE;IAC3C,MAAM,MAAM,GAAG;;;;;;CAMf,CAAC;IAED,MAAM,EAAE,GAAG,KAAK,CAAC;QAChB,iBAAiB,EAAE,MAAM;KACzB,CAAC,CAAC,EAAyC,CAAC;IAE7C,MAAM,aAAa,CAAC;QACnB,EAAE,EAAE,EAAE,CAAC,QAAQ;QACf,MAAM,EAAE,iBAAiB;QACzB,WAAW,EAAE,kBAAkB;QAC/B,UAAU,EAAE,iBAAiB;KAC7B,CAAC,CAAC;IAEH,MAAM,aAAa,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,iBAAiB,EAAE;QACnE,QAAQ,EAAE,OAAO;KACjB,CAAC,CAAC;IAEH,MAAM,CAAC,aAAa,CAAC,CAAC,qBAAqB,CAAC;;;;;;;;;EAS3C,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,sEAAsE,EAAE,KAAK,IAAI,EAAE;IACvF,MAAM,MAAM,GAAG;;;;;;;;;;;;;;;;;;;CAmBf,CAAC;IACD,MAAM,EAAE,GAAG,KAAK,CAAC;QAChB,iBAAiB,EAAE,MAAM;KACzB,CAAC,CAAC,EAAyC,CAAC;IAC7C,MAAM,aAAa,CAAC;QACnB,EAAE,EAAE,EAAE,CAAC,QAAQ;QACf,MAAM,EAAE,iBAAiB;QACzB,WAAW,EAAE,kBAAkB;QAC/B,UAAU,EAAE,iBAAiB;KAC7B,CAAC,CAAC;IAEH,MAAM,aAAa,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,iBAAiB,EAAE;QACnE,QAAQ,EAAE,OAAO;KACjB,CAAC,CAAC;IAEH,MAAM,CAAC,aAAa,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;AACvC,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,yEAAyE,EAAE,GAAG,EAAE;IACpF,MAAM,MAAM,GACX,iKAAiK,CAAC;IACnK,MAAM,CACL,gBAAgB,CAAC;QAChB,MAAM;QACN,WAAW,EAAE,kBAAkB;QAC/B,MAAM,EAAE,iBAAiB;KACzB,CAAC,CACF,CAAC,aAAa,CAAC;QACf,EAAE,EAAE,KAAK;QACT,MAAM,EACL,qEAAqE;KACtE,CAAC,CAAC;IACH,MAAM,CACL,gBAAgB,CAAC;QAChB,MAAM,EACL,2EAA2E;QAC5E,WAAW,EAAE,kBAAkB;QAC/B,MAAM,EAAE,iBAAiB;KACzB,CAAC,CACF,CAAC,aAAa,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;AAChC,CAAC,CAAC,CAAC","sourcesContent":["import { expect, test } from \"vitest\";\nimport { addVitePlugin, updateViteConfig } from \"./add-vite-plugin.js\";\nimport { memfs } from \"memfs\";\n\ntest(\"updates a vite.config.ts\", async () => {\n\tconst config = `import { sveltekit } from \"@sveltejs/kit/vite\"\nimport { defineConfig } from \"vite\"\n\nexport default defineConfig({\n\tplugins: [sveltekit()],\n})\n`;\n\n\tconst fs = memfs({\n\t\t\"/vite.config.ts\": config,\n\t}).fs as unknown as typeof import(\"node:fs\");\n\n\tawait addVitePlugin({\n\t\tfs: fs.promises,\n\t\toutdir: \"./src/paraglide\",\n\t\tprojectPath: \"./project.inlang\",\n\t\tconfigPath: \"/vite.config.ts\",\n\t});\n\n\tconst updatedConfig = await fs.promises.readFile(\"/vite.config.ts\", {\n\t\tencoding: \"utf-8\",\n\t});\n\n\texpect(updatedConfig).toMatchInlineSnapshot(`\n\t\t\"import { paraglideVitePlugin } from '@inlang/paraglide-js'\n\t\timport { sveltekit } from \"@sveltejs/kit/vite\"\n\t\timport { defineConfig } from \"vite\"\n\n\t\texport default defineConfig({\n\t\t\tplugins: [paraglideVitePlugin({ project: \"./project.inlang\", outdir: \"./src/paraglide\" }),sveltekit()],\n\t\t})\n\t\t\"\n\t`);\n});\n\ntest(\"doesn't update configs where the paraglide plugin is already present\", async () => {\n\tconst config = `\n import { sveltekit } from \"@sveltejs/kit/vite\"\n import { paraglideVitePlugin } from \"@inlang/paraglide-js\"\n import { visualizer } from \"rollup-plugin-visualizer\"\n import { defineConfig } from \"vite\"\n \n export default defineConfig({\n plugins: [\n paraglideVitePlugin({\n project: \"./project.inlang\",\n outdir: \"./src/paraglide\",\n }),\n sveltekit(),\n visualizer({\n filename: \"stats.html\",\n emitFile: true,\n }),\n ],\n }) \n`;\n\tconst fs = memfs({\n\t\t\"/vite.config.ts\": config,\n\t}).fs as unknown as typeof import(\"node:fs\");\n\tawait addVitePlugin({\n\t\tfs: fs.promises,\n\t\toutdir: \"./src/paraglide\",\n\t\tprojectPath: \"./project.inlang\",\n\t\tconfigPath: \"/vite.config.ts\",\n\t});\n\n\tconst updatedConfig = await fs.promises.readFile(\"/vite.config.ts\", {\n\t\tencoding: \"utf-8\",\n\t});\n\n\texpect(updatedConfig).toMatch(config);\n});\n\ntest(\"existing integration must match requested paths before unattended reuse\", () => {\n\tconst config =\n\t\t'import {paraglideVitePlugin} from \"@inlang/paraglide-js\"; export default {plugins:[paraglideVitePlugin({project:\"./project.inlang\",outdir:\"./src/paraglide\"})]}';\n\texpect(\n\t\tupdateViteConfig({\n\t\t\tconfig,\n\t\t\tprojectPath: \"./project.inlang\",\n\t\t\toutdir: \"./src/different\",\n\t\t})\n\t).toMatchObject({\n\t\tok: false,\n\t\treason:\n\t\t\t\"Existing Vite plugin paths differ from the requested project/outdir\",\n\t});\n\texpect(\n\t\tupdateViteConfig({\n\t\t\tconfig:\n\t\t\t\t'import {compile} from \"@inlang/paraglide-js\"; export default {plugins:[]}',\n\t\t\tprojectPath: \"./project.inlang\",\n\t\t\toutdir: \"./src/paraglide\",\n\t\t})\n\t).toMatchObject({ ok: false });\n});\n"]}
|
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
import { prompt } from "../utils.js";
|
|
2
2
|
import childProcess from "node:child_process";
|
|
3
3
|
export const checkForUncommittedChanges = async (ctx) => {
|
|
4
|
+
if (ctx.allowDirty)
|
|
5
|
+
return ctx;
|
|
6
|
+
let status;
|
|
7
|
+
try {
|
|
8
|
+
status = await execAsync("git status --porcelain", ctx.root);
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return ctx;
|
|
12
|
+
}
|
|
13
|
+
if (ctx.interactive === false && status.length > 0) {
|
|
14
|
+
throw new Error("Uncommitted changes found. Commit them or pass --allow-dirty to continue.");
|
|
15
|
+
}
|
|
4
16
|
try {
|
|
5
|
-
if (
|
|
17
|
+
if (status.length === 0) {
|
|
6
18
|
return ctx;
|
|
7
19
|
}
|
|
8
20
|
ctx.logger.info(`You have uncommitted changes.\n\nCommitting outstanding changes ensures that you don't lose any work, and see the changes the paraglide-js init command introduces.`);
|
|
@@ -22,9 +34,9 @@ export const checkForUncommittedChanges = async (ctx) => {
|
|
|
22
34
|
return ctx;
|
|
23
35
|
}
|
|
24
36
|
};
|
|
25
|
-
function execAsync(command) {
|
|
37
|
+
function execAsync(command, cwd) {
|
|
26
38
|
return new Promise((resolve, reject) => {
|
|
27
|
-
childProcess.exec(command, (error, stdout) => {
|
|
39
|
+
childProcess.exec(command, { cwd }, (error, stdout) => {
|
|
28
40
|
if (error) {
|
|
29
41
|
reject(error);
|
|
30
42
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check-for-uncomitted-changes.js","sourceRoot":"","sources":["../../../src/cli/steps/check-for-uncomitted-changes.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,YAAY,MAAM,oBAAoB,CAAC;AAE9C,MAAM,CAAC,MAAM,0BAA0B,
|
|
1
|
+
{"version":3,"file":"check-for-uncomitted-changes.js","sourceRoot":"","sources":["../../../src/cli/steps/check-for-uncomitted-changes.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,YAAY,MAAM,oBAAoB,CAAC;AAE9C,MAAM,CAAC,MAAM,0BAA0B,GAQnC,KAAK,EAAE,GAAG,EAAE,EAAE;IACjB,IAAI,GAAG,CAAC,UAAU;QAAE,OAAO,GAAG,CAAC;IAC/B,IAAI,MAAc,CAAC;IACnB,IAAI,CAAC;QACJ,MAAM,GAAG,MAAM,SAAS,CAAC,wBAAwB,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;IAC9D,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,GAAG,CAAC;IACZ,CAAC;IACD,IAAI,GAAG,CAAC,WAAW,KAAK,KAAK,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,KAAK,CACd,2EAA2E,CAC3E,CAAC;IACH,CAAC;IACD,IAAI,CAAC;QACJ,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO,GAAG,CAAC;QACZ,CAAC;QAED,GAAG,CAAC,MAAM,CAAC,IAAI,CACd,qKAAqK,CACrK,CAAC;QACF,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,8BAA8B,EAAE;YAC7D,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,KAAK;SACd,CAAC,CAAC;QACH,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACvB,OAAO,GAAG,CAAC;QACZ,CAAC;aAAM,CAAC;YACP,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjB,CAAC;IACF,CAAC;IAAC,MAAM,CAAC;QACR,2BAA2B;QAC3B,OAAO,GAAG,CAAC;IACZ,CAAC;AACF,CAAC,CAAC;AAEF,SAAS,SAAS,CAAC,OAAe,EAAE,GAAY;IAC/C,OAAO,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC9C,YAAY,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE;YACrD,IAAI,KAAK,EAAE,CAAC;gBACX,MAAM,CAAC,KAAK,CAAC,CAAC;YACf,CAAC;iBAAM,CAAC;gBACP,OAAO,CAAC,MAAM,CAAC,CAAC;YACjB,CAAC;QACF,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;AACJ,CAAC","sourcesContent":["import type { Logger } from \"../../services/logger/index.js\";\nimport type { CliStep } from \"../utils.js\";\nimport { prompt } from \"../utils.js\";\nimport childProcess from \"node:child_process\";\n\nexport const checkForUncommittedChanges: CliStep<\n\t{\n\t\tlogger: Logger;\n\t\tinteractive?: boolean;\n\t\tallowDirty?: boolean;\n\t\troot?: string;\n\t},\n\tunknown\n> = async (ctx) => {\n\tif (ctx.allowDirty) return ctx;\n\tlet status: string;\n\ttry {\n\t\tstatus = await execAsync(\"git status --porcelain\", ctx.root);\n\t} catch {\n\t\treturn ctx;\n\t}\n\tif (ctx.interactive === false && status.length > 0) {\n\t\tthrow new Error(\n\t\t\t\"Uncommitted changes found. Commit them or pass --allow-dirty to continue.\"\n\t\t);\n\t}\n\ttry {\n\t\tif (status.length === 0) {\n\t\t\treturn ctx;\n\t\t}\n\n\t\tctx.logger.info(\n\t\t\t`You have uncommitted changes.\\n\\nCommitting outstanding changes ensures that you don't lose any work, and see the changes the paraglide-js init command introduces.`\n\t\t);\n\t\tconst response = await prompt(\"Continue without committing?\", {\n\t\t\ttype: \"confirm\",\n\t\t\tinitial: false,\n\t\t});\n\t\tif (response === true) {\n\t\t\treturn ctx;\n\t\t} else {\n\t\t\tprocess.exit(0);\n\t\t}\n\t} catch {\n\t\t// git cli is not installed\n\t\treturn ctx;\n\t}\n};\n\nfunction execAsync(command: string, cwd?: string) {\n\treturn new Promise<string>((resolve, reject) => {\n\t\tchildProcess.exec(command, { cwd }, (error, stdout) => {\n\t\t\tif (error) {\n\t\t\t\treject(error);\n\t\t\t} else {\n\t\t\t\tresolve(stdout);\n\t\t\t}\n\t\t});\n\t});\n}\n"]}
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
+
import path from "node:path";
|
|
1
2
|
export const detectBundler = async (ctx) => {
|
|
2
|
-
const potentialViteConfigPaths = [
|
|
3
|
+
const potentialViteConfigPaths = [
|
|
4
|
+
"./vite.config.js",
|
|
5
|
+
"./vite.config.ts",
|
|
6
|
+
"./vite.config.mjs",
|
|
7
|
+
"./vite.config.mts",
|
|
8
|
+
].map((file) => (ctx.root ? path.resolve(ctx.root, file) : file));
|
|
3
9
|
const viteConfigPath = await Promise.all(potentialViteConfigPaths.map(async (path) => ({
|
|
4
10
|
path,
|
|
5
11
|
exists: await ctx.fs
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detect-bundler.js","sourceRoot":"","sources":["../../../src/cli/steps/detect-bundler.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"detect-bundler.js","sourceRoot":"","sources":["../../../src/cli/steps/detect-bundler.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,MAAM,CAAC,MAAM,aAAa,GAUtB,KAAK,EAAE,GAAG,EAAE,EAAE;IACjB,MAAM,wBAAwB,GAAG;QAChC,kBAAkB;QAClB,kBAAkB;QAClB,mBAAmB;QACnB,mBAAmB;KACnB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAElE,MAAM,cAAc,GAAG,MAAM,OAAO,CAAC,GAAG,CACvC,wBAAwB,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;QAC7C,IAAI;QACJ,MAAM,EAAE,MAAM,GAAG,CAAC,EAAE;aAClB,MAAM,CAAC,IAAI,CAAC;aACZ,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC;aAChB,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC;KACpB,CAAC,CAAC,CACH,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC;IAEnE,IAAI,CAAC,cAAc,EAAE,CAAC;QACrB,OAAO;YACN,GAAG,GAAG;YACN,OAAO,EAAE,SAAS;SAClB,CAAC;IACH,CAAC;IAED,OAAO;QACN,GAAG,GAAG;QACN,OAAO,EAAE,MAAM;QACf,UAAU,EAAE,cAAc;KAC1B,CAAC;AACH,CAAC,CAAC","sourcesContent":["import path from \"node:path\";\nimport type { CliStep } from \"../utils.js\";\n\nexport const detectBundler: CliStep<\n\t{ fs: typeof import(\"node:fs/promises\"); root?: string },\n\t| {\n\t\t\tbundler?: undefined;\n\t\t\tconfigPath?: undefined;\n\t }\n\t| {\n\t\t\tbundler: \"vite\";\n\t\t\tconfigPath: string;\n\t }\n> = async (ctx) => {\n\tconst potentialViteConfigPaths = [\n\t\t\"./vite.config.js\",\n\t\t\"./vite.config.ts\",\n\t\t\"./vite.config.mjs\",\n\t\t\"./vite.config.mts\",\n\t].map((file) => (ctx.root ? path.resolve(ctx.root, file) : file));\n\n\tconst viteConfigPath = await Promise.all(\n\t\tpotentialViteConfigPaths.map(async (path) => ({\n\t\t\tpath,\n\t\t\texists: await ctx.fs\n\t\t\t\t.access(path)\n\t\t\t\t.then(() => true)\n\t\t\t\t.catch(() => false),\n\t\t}))\n\t).then((results) => results.find((result) => result.exists)?.path);\n\n\tif (!viteConfigPath) {\n\t\treturn {\n\t\t\t...ctx,\n\t\t\tbundler: undefined,\n\t\t};\n\t}\n\n\treturn {\n\t\t...ctx,\n\t\tbundler: \"vite\",\n\t\tconfigPath: viteConfigPath,\n\t};\n};\n"]}
|
|
@@ -1,24 +1,25 @@
|
|
|
1
1
|
import { type InlangProject } from "@inlang/sdk";
|
|
2
2
|
import type { Logger } from "../../services/logger/index.js";
|
|
3
3
|
import type { CliStep } from "../utils.js";
|
|
4
|
-
import fs from "node:fs";
|
|
4
|
+
import type fs from "node:fs";
|
|
5
5
|
export declare const initializeInlangProject: CliStep<{
|
|
6
6
|
fs: typeof fs.promises;
|
|
7
7
|
syncFs: typeof fs;
|
|
8
8
|
logger: Logger;
|
|
9
9
|
root: string;
|
|
10
|
+
projectPath?: string;
|
|
10
11
|
}, {
|
|
11
12
|
project: InlangProject;
|
|
12
|
-
/** Relative path to the project */
|
|
13
13
|
projectPath: string;
|
|
14
14
|
}>;
|
|
15
15
|
export declare const createNewProjectFlow: (ctx: {
|
|
16
16
|
fs: typeof fs.promises;
|
|
17
17
|
syncFs: typeof fs;
|
|
18
18
|
logger: Logger;
|
|
19
|
+
root?: string;
|
|
20
|
+
projectPath?: string;
|
|
19
21
|
}) => Promise<{
|
|
20
22
|
project: InlangProject;
|
|
21
|
-
/** An absolute path to the created project */
|
|
22
23
|
projectPath: string;
|
|
23
24
|
}>;
|
|
24
25
|
/**
|