@inlang/paraglide-js 2.27.0 → 2.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. package/README.md +9 -0
  2. package/dist/bundler-plugins/per-locale-build/index.js +14 -7
  3. package/dist/bundler-plugins/per-locale-build/index.js.map +1 -1
  4. package/dist/bundler-plugins/per-locale-build/vite-environments.test.js +235 -2
  5. package/dist/bundler-plugins/per-locale-build/vite-environments.test.js.map +1 -1
  6. package/dist/bundler-plugins/unplugin.d.ts +4 -1
  7. package/dist/bundler-plugins/unplugin.js +134 -22
  8. package/dist/bundler-plugins/unplugin.js.map +1 -1
  9. package/dist/bundler-plugins/unplugin.test.js +490 -1
  10. package/dist/bundler-plugins/unplugin.test.js.map +1 -1
  11. package/dist/bundler-plugins/vite-error-response.test.d.ts +1 -0
  12. package/dist/bundler-plugins/vite-error-response.test.js +171 -0
  13. package/dist/bundler-plugins/vite-error-response.test.js.map +1 -0
  14. package/dist/bundler-plugins/vite.d.ts +8 -0
  15. package/dist/bundler-plugins/vite.js +14 -4
  16. package/dist/bundler-plugins/vite.js.map +1 -1
  17. package/dist/bundler-plugins/vite.test.js +1 -1
  18. package/dist/bundler-plugins/vite.test.js.map +1 -1
  19. package/dist/cli/commands/compile/command.js +13 -0
  20. package/dist/cli/commands/compile/command.js.map +1 -1
  21. package/dist/cli/commands/compile/command.test.js +87 -3
  22. package/dist/cli/commands/compile/command.test.js.map +1 -1
  23. package/dist/cli/commands/init/command.d.ts +5 -0
  24. package/dist/cli/commands/init/command.js +213 -59
  25. package/dist/cli/commands/init/command.js.map +1 -1
  26. package/dist/cli/commands/init/command.test.d.ts +1 -0
  27. package/dist/cli/commands/init/command.test.js +123 -0
  28. package/dist/cli/commands/init/command.test.js.map +1 -0
  29. package/dist/cli/init-options.d.ts +11 -0
  30. package/dist/cli/init-options.js +2 -0
  31. package/dist/cli/init-options.js.map +1 -0
  32. package/dist/cli/steps/add-inlang-check.js +2 -2
  33. package/dist/cli/steps/add-inlang-check.js.map +1 -1
  34. package/dist/cli/steps/add-message-lint.d.ts +34 -0
  35. package/dist/cli/steps/add-message-lint.js +105 -0
  36. package/dist/cli/steps/add-message-lint.js.map +1 -0
  37. package/dist/cli/steps/add-message-lint.test.d.ts +1 -0
  38. package/dist/cli/steps/add-message-lint.test.js +74 -0
  39. package/dist/cli/steps/add-message-lint.test.js.map +1 -0
  40. package/dist/cli/steps/add-vite-plugin.d.ts +18 -0
  41. package/dist/cli/steps/add-vite-plugin.js +34 -6
  42. package/dist/cli/steps/add-vite-plugin.js.map +1 -1
  43. package/dist/cli/steps/add-vite-plugin.test.js +25 -9
  44. package/dist/cli/steps/add-vite-plugin.test.js.map +1 -1
  45. package/dist/cli/steps/check-for-uncomitted-changes.d.ts +3 -0
  46. package/dist/cli/steps/check-for-uncomitted-changes.js +15 -3
  47. package/dist/cli/steps/check-for-uncomitted-changes.js.map +1 -1
  48. package/dist/cli/steps/detect-bundler.d.ts +1 -0
  49. package/dist/cli/steps/detect-bundler.js +7 -1
  50. package/dist/cli/steps/detect-bundler.js.map +1 -1
  51. package/dist/cli/steps/initialize-inlang-project.d.ts +4 -3
  52. package/dist/cli/steps/initialize-inlang-project.js +60 -157
  53. package/dist/cli/steps/initialize-inlang-project.js.map +1 -1
  54. package/dist/cli/steps/maybe-add-machine-translation.d.ts +4 -0
  55. package/dist/cli/steps/maybe-add-machine-translation.js +11 -6
  56. package/dist/cli/steps/maybe-add-machine-translation.js.map +1 -1
  57. package/dist/cli/steps/maybe-add-sherlock.d.ts +2 -0
  58. package/dist/cli/steps/maybe-add-sherlock.js +12 -6
  59. package/dist/cli/steps/maybe-add-sherlock.js.map +1 -1
  60. package/dist/cli/steps/maybe-add-sherlock.test.d.ts +1 -0
  61. package/dist/cli/steps/maybe-add-sherlock.test.js +44 -0
  62. package/dist/cli/steps/maybe-add-sherlock.test.js.map +1 -0
  63. package/dist/cli/steps/prompt-for-outdir.d.ts +2 -0
  64. package/dist/cli/steps/prompt-for-outdir.js +4 -0
  65. package/dist/cli/steps/prompt-for-outdir.js.map +1 -1
  66. package/dist/cli/steps/update-package-json.js +8 -13
  67. package/dist/cli/steps/update-package-json.js.map +1 -1
  68. package/dist/cli/steps/update-ts-config.d.ts +6 -0
  69. package/dist/cli/steps/update-ts-config.js +27 -4
  70. package/dist/cli/steps/update-ts-config.js.map +1 -1
  71. package/dist/cli/utils.d.ts +2 -0
  72. package/dist/cli/utils.js +6 -0
  73. package/dist/cli/utils.js.map +1 -1
  74. package/dist/cli/utils.test.d.ts +1 -0
  75. package/dist/cli/utils.test.js +16 -0
  76. package/dist/cli/utils.test.js.map +1 -0
  77. package/dist/compiler/compile-annotation.js +6 -1
  78. package/dist/compiler/compile-annotation.js.map +1 -1
  79. package/dist/compiler/compile-bundle.d.ts +4 -0
  80. package/dist/compiler/compile-bundle.js +67 -2
  81. package/dist/compiler/compile-bundle.js.map +1 -1
  82. package/dist/compiler/compile-bundle.test.js +214 -1
  83. package/dist/compiler/compile-bundle.test.js.map +1 -1
  84. package/dist/compiler/compile-lifecycle.test.d.ts +1 -0
  85. package/dist/compiler/compile-lifecycle.test.js +69 -0
  86. package/dist/compiler/compile-lifecycle.test.js.map +1 -0
  87. package/dist/compiler/compile-local-variable.test.js +14 -14
  88. package/dist/compiler/compile-local-variable.test.js.map +1 -1
  89. package/dist/compiler/compile-message.exact-number.test.js +1 -1
  90. package/dist/compiler/compile-message.exact-number.test.js.map +1 -1
  91. package/dist/compiler/compile-message.locals.test.js +1 -1
  92. package/dist/compiler/compile-message.locals.test.js.map +1 -1
  93. package/dist/compiler/compile-message.test.js +2 -2
  94. package/dist/compiler/compile-message.test.js.map +1 -1
  95. package/dist/compiler/compile-pattern.test.js +5 -5
  96. package/dist/compiler/compile-pattern.test.js.map +1 -1
  97. package/dist/compiler/compile-project.js +1 -0
  98. package/dist/compiler/compile-project.js.map +1 -1
  99. package/dist/compiler/compile-project.test.js +94 -1
  100. package/dist/compiler/compile-project.test.js.map +1 -1
  101. package/dist/compiler/compile.js +36 -17
  102. package/dist/compiler/compile.js.map +1 -1
  103. package/dist/compiler/compile.test.js +70 -0
  104. package/dist/compiler/compile.test.js.map +1 -1
  105. package/dist/compiler/compiler-options.d.ts +41 -0
  106. package/dist/compiler/compiler-options.js.map +1 -1
  107. package/dist/compiler/output-structure/message-modules.js +1 -1
  108. package/dist/compiler/output-structure/message-modules.js.map +1 -1
  109. package/dist/compiler/registry.js +71 -13
  110. package/dist/compiler/registry.js.map +1 -1
  111. package/dist/compiler/registry.test.d.ts +1 -0
  112. package/dist/compiler/registry.test.js +291 -0
  113. package/dist/compiler/registry.test.js.map +1 -0
  114. package/dist/compiler/runtime/create-runtime.d.ts +2 -0
  115. package/dist/compiler/runtime/create-runtime.js +9 -0
  116. package/dist/compiler/runtime/create-runtime.js.map +1 -1
  117. package/dist/compiler/runtime/extract-locale-from-cookie.js +8 -1
  118. package/dist/compiler/runtime/extract-locale-from-cookie.js.map +1 -1
  119. package/dist/compiler/runtime/extract-locale-from-cookie.test.js +26 -0
  120. package/dist/compiler/runtime/extract-locale-from-cookie.test.js.map +1 -1
  121. package/dist/compiler/runtime/extract-locale-from-header.js +47 -5
  122. package/dist/compiler/runtime/extract-locale-from-header.js.map +1 -1
  123. package/dist/compiler/runtime/extract-locale-from-header.test.js +58 -0
  124. package/dist/compiler/runtime/extract-locale-from-header.test.js.map +1 -1
  125. package/dist/compiler/runtime/get-locale.js +7 -1
  126. package/dist/compiler/runtime/get-locale.js.map +1 -1
  127. package/dist/compiler/runtime/get-locale.test.js +95 -0
  128. package/dist/compiler/runtime/get-locale.test.js.map +1 -1
  129. package/dist/compiler/runtime/localize-href.d.ts +8 -0
  130. package/dist/compiler/runtime/localize-href.js +60 -0
  131. package/dist/compiler/runtime/localize-href.js.map +1 -1
  132. package/dist/compiler/runtime/localize-href.test.js +227 -1
  133. package/dist/compiler/runtime/localize-href.test.js.map +1 -1
  134. package/dist/compiler/runtime/localize-url.d.ts +8 -4
  135. package/dist/compiler/runtime/localize-url.js +12 -8
  136. package/dist/compiler/runtime/localize-url.js.map +1 -1
  137. package/dist/compiler/runtime/localize-url.test.js +45 -0
  138. package/dist/compiler/runtime/localize-url.test.js.map +1 -1
  139. package/dist/compiler/runtime/set-locale.js +24 -6
  140. package/dist/compiler/runtime/set-locale.js.map +1 -1
  141. package/dist/compiler/runtime/set-locale.test.js +182 -0
  142. package/dist/compiler/runtime/set-locale.test.js.map +1 -1
  143. package/dist/compiler/runtime/type.d.ts +2 -0
  144. package/dist/compiler/runtime/type.js.map +1 -1
  145. package/dist/compiler/runtime/type.test.js +75 -0
  146. package/dist/compiler/runtime/type.test.js.map +1 -1
  147. package/dist/compiler/runtime/variables.d.ts +2 -0
  148. package/dist/compiler/runtime/variables.js +7 -0
  149. package/dist/compiler/runtime/variables.js.map +1 -1
  150. package/dist/compiler/server/middleware.d.ts +8 -2
  151. package/dist/compiler/server/middleware.js +175 -7
  152. package/dist/compiler/server/middleware.js.map +1 -1
  153. package/dist/compiler/server/middleware.test.js +750 -3
  154. package/dist/compiler/server/middleware.test.js.map +1 -1
  155. package/dist/eslint/index.d.ts +4 -0
  156. package/dist/eslint/index.js +8 -0
  157. package/dist/eslint/index.js.map +1 -0
  158. package/dist/eslint/no-dynamic-message-access.d.ts +3 -0
  159. package/dist/eslint/no-dynamic-message-access.js +150 -0
  160. package/dist/eslint/no-dynamic-message-access.js.map +1 -0
  161. package/dist/eslint/no-dynamic-message-access.test.d.ts +1 -0
  162. package/dist/eslint/no-dynamic-message-access.test.js +126 -0
  163. package/dist/eslint/no-dynamic-message-access.test.js.map +1 -0
  164. package/dist/eslint/oxlint.test.d.ts +1 -0
  165. package/dist/eslint/oxlint.test.js +64 -0
  166. package/dist/eslint/oxlint.test.js.map +1 -0
  167. package/dist/services/config/config-schema.d.ts +2 -0
  168. package/dist/services/config/config-schema.js +2 -0
  169. package/dist/services/config/config-schema.js.map +1 -1
  170. package/dist/services/config/load-paraglide-config.test.js +12 -0
  171. package/dist/services/config/load-paraglide-config.test.js.map +1 -1
  172. package/dist/services/env-variables/index.js +1 -1
  173. package/dist/services/env-variables/index.js.map +1 -1
  174. package/dist/services/file-handling/write-output.bench.js +51 -11
  175. package/dist/services/file-handling/write-output.bench.js.map +1 -1
  176. package/dist/services/file-handling/write-output.js +60 -20
  177. package/dist/services/file-handling/write-output.js.map +1 -1
  178. package/dist/services/file-handling/write-output.test.js +175 -0
  179. package/dist/services/file-handling/write-output.test.js.map +1 -1
  180. package/package.json +14 -4
@@ -0,0 +1,123 @@
1
+ import { test, expect, vi, beforeEach, afterEach } from "vitest";
2
+ import { mkdtemp, writeFile, readFile, readdir, rm } from "node:fs/promises";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { execFile } from "node:child_process";
6
+ import { promisify } from "node:util";
7
+ import { Logger } from "../../../services/logger/index.js";
8
+ import { checkForUncommittedChanges } from "../../steps/check-for-uncomitted-changes.js";
9
+ import { fileURLToPath } from "node:url";
10
+ import * as defaults from "../../defaults.js";
11
+ import * as utils from "../../utils.js";
12
+ import { createInitCommand, runInit } from "./command.js";
13
+ let root;
14
+ beforeEach(async () => {
15
+ root = await mkdtemp(join(tmpdir(), "paraglide-init-test-"));
16
+ vi.spyOn(process, "cwd").mockReturnValue(root);
17
+ vi.spyOn(utils, "prompt").mockImplementation(async () => {
18
+ throw new Error("Unexpected prompt in headless init");
19
+ });
20
+ vi.spyOn(utils, "promptSelection").mockImplementation(async () => {
21
+ throw new Error("Unexpected select in headless init");
22
+ });
23
+ const original = defaults.getNewProjectTemplate;
24
+ vi.spyOn(defaults, "getNewProjectTemplate").mockImplementation(() => ({
25
+ ...original(),
26
+ modules: [
27
+ fileURLToPath(new URL("../../../../node_modules/@inlang/plugin-message-format/dist/index.js", import.meta.url)),
28
+ ],
29
+ }));
30
+ await writeFile(join(root, "package.json"), JSON.stringify({
31
+ name: "fixture",
32
+ version: "1.0.0",
33
+ scripts: { build: "vite build" },
34
+ }));
35
+ });
36
+ afterEach(async () => {
37
+ vi.restoreAllMocks();
38
+ process.exitCode = 0;
39
+ await rm(root, { recursive: true, force: true });
40
+ });
41
+ test("headless setup and rerun preserve project/messages/comments and skip optional tools", async () => {
42
+ await writeFile(join(root, "tsconfig.json"), '// keep this comment\n{ "compilerOptions": { "strict": true, }, }\n');
43
+ await runInit({ yes: true, compile: false });
44
+ const packageFirst = await readFile(join(root, "package.json"), "utf8");
45
+ const settingsFirst = await readFile(join(root, "project.inlang/settings.json"), "utf8");
46
+ const messagesFirst = await readFile(join(root, "messages/en.json"), "utf8");
47
+ await runInit({ nonInteractive: true, compile: false });
48
+ expect(await readFile(join(root, "package.json"), "utf8")).toBe(packageFirst);
49
+ expect(await readFile(join(root, "project.inlang/settings.json"), "utf8")).toBe(settingsFirst);
50
+ expect(await readFile(join(root, "messages/en.json"), "utf8")).toBe(messagesFirst);
51
+ const tsconfig = await readFile(join(root, "tsconfig.json"), "utf8");
52
+ expect(tsconfig).toContain("keep this comment");
53
+ expect(tsconfig).toContain('"allowJs": true');
54
+ expect(tsconfig).toContain('"strict": true');
55
+ const pkg = JSON.parse(packageFirst);
56
+ expect(pkg.scripts["machine-translate"]).toBeUndefined();
57
+ expect(pkg.scripts["lint:i18n"]).toBeUndefined();
58
+ expect(pkg.devDependencies.eslint).toBeUndefined();
59
+ expect(utils.prompt).not.toHaveBeenCalled();
60
+ expect(utils.promptSelection).not.toHaveBeenCalled();
61
+ });
62
+ test("explicit paths and ESLint setup work without prompts", async () => {
63
+ await writeFile(join(root, "eslint.config.mjs"), "// preserve existing config");
64
+ await runInit({
65
+ yes: true,
66
+ project: "./config/project.inlang",
67
+ outdir: "./src/i18n",
68
+ lint: "eslint",
69
+ check: false,
70
+ compile: false,
71
+ });
72
+ const pkg = JSON.parse(await readFile(join(root, "package.json"), "utf8"));
73
+ expect(pkg.scripts["check:i18n"]).toBeUndefined();
74
+ expect(pkg.scripts["lint:i18n"]).toContain("eslint.paraglide.config.mjs");
75
+ expect(await readFile(join(root, "eslint.config.mjs"), "utf8")).toBe("// preserve existing config");
76
+ const settings = JSON.parse(await readFile(join(root, "config/project.inlang/settings.json"), "utf8"));
77
+ expect(settings["plugin.inlang.messageFormat"].pathPattern).toBe("../messages/{locale}.json");
78
+ expect(pkg.scripts.build).toContain("./config/project.inlang");
79
+ expect(pkg.scripts.build).toContain("./src/i18n");
80
+ });
81
+ test("closed input without an unattended flag fails before writes", async () => {
82
+ await expect(runInit({})).rejects.toThrow("Use --yes");
83
+ expect(await readdir(root)).toEqual(["package.json"]);
84
+ });
85
+ test("unwritable integration and invalid paths fail before scaffolding", async () => {
86
+ await writeFile(join(root, "vite.config.ts"), "export default () => ({plugins:getPlugins()});");
87
+ await expect(runInit({ yes: true })).rejects.toThrow("Vite configuration cannot");
88
+ expect((await readdir(root)).sort()).toEqual([
89
+ "package.json",
90
+ "vite.config.ts",
91
+ ]);
92
+ await expect(runInit({ yes: true, outdir: "." })).rejects.toThrow("distinct");
93
+ });
94
+ test("CLI help exposes unattended mode and optional lint choices", () => {
95
+ const help = createInitCommand().helpInformation();
96
+ for (const flag of [
97
+ "--yes",
98
+ "--non-interactive",
99
+ "--outdir",
100
+ "--project",
101
+ "--lint",
102
+ "--allow-dirty",
103
+ "--no-compile",
104
+ ])
105
+ expect(help).toContain(flag);
106
+ });
107
+ test("dirty git trees require explicit permission without prompting", async () => {
108
+ await promisify(execFile)("git", ["init", root]);
109
+ const ctx = {
110
+ logger: new Logger({ silent: true, prefix: false }),
111
+ root,
112
+ interactive: false,
113
+ };
114
+ await expect(checkForUncommittedChanges(ctx)).rejects.toThrow("--allow-dirty");
115
+ await expect(checkForUncommittedChanges({ ...ctx, allowDirty: true })).resolves.toBeDefined();
116
+ expect(utils.prompt).not.toHaveBeenCalled();
117
+ });
118
+ test("invalid lint choices fail before creating files", async () => {
119
+ const command = createInitCommand().exitOverride();
120
+ await expect(command.parseAsync(["--yes", "--lint", "invalid"], { from: "user" })).rejects.toThrow("Allowed choices");
121
+ expect(await readdir(root)).toEqual(["package.json"]);
122
+ });
123
+ //# sourceMappingURL=command.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command.test.js","sourceRoot":"","sources":["../../../../src/cli/commands/init/command.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { test, expect, vi, beforeEach, afterEach } from \"vitest\";\nimport { mkdtemp, writeFile, readFile, readdir, rm } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { Logger } from \"../../../services/logger/index.js\";\nimport { checkForUncommittedChanges } from \"../../steps/check-for-uncomitted-changes.js\";\nimport { fileURLToPath } from \"node:url\";\nimport * as defaults from \"../../defaults.js\";\nimport * as utils from \"../../utils.js\";\nimport { createInitCommand, runInit } from \"./command.js\";\n\nlet root: string;\nbeforeEach(async () => {\n\troot = await mkdtemp(join(tmpdir(), \"paraglide-init-test-\"));\n\tvi.spyOn(process, \"cwd\").mockReturnValue(root);\n\tvi.spyOn(utils, \"prompt\").mockImplementation(async () => {\n\t\tthrow new Error(\"Unexpected prompt in headless init\");\n\t});\n\tvi.spyOn(utils, \"promptSelection\").mockImplementation(async () => {\n\t\tthrow new Error(\"Unexpected select in headless init\");\n\t});\n\tconst original = defaults.getNewProjectTemplate;\n\tvi.spyOn(defaults, \"getNewProjectTemplate\").mockImplementation(() => ({\n\t\t...original(),\n\t\tmodules: [\n\t\t\tfileURLToPath(\n\t\t\t\tnew URL(\n\t\t\t\t\t\"../../../../node_modules/@inlang/plugin-message-format/dist/index.js\",\n\t\t\t\t\timport.meta.url\n\t\t\t\t)\n\t\t\t),\n\t\t],\n\t}));\n\tawait writeFile(\n\t\tjoin(root, \"package.json\"),\n\t\tJSON.stringify({\n\t\t\tname: \"fixture\",\n\t\t\tversion: \"1.0.0\",\n\t\t\tscripts: { build: \"vite build\" },\n\t\t})\n\t);\n});\nafterEach(async () => {\n\tvi.restoreAllMocks();\n\tprocess.exitCode = 0;\n\tawait rm(root, { recursive: true, force: true });\n});\n\ntest(\"headless setup and rerun preserve project/messages/comments and skip optional tools\", async () => {\n\tawait writeFile(\n\t\tjoin(root, \"tsconfig.json\"),\n\t\t'// keep this comment\\n{ \"compilerOptions\": { \"strict\": true, }, }\\n'\n\t);\n\tawait runInit({ yes: true, compile: false });\n\tconst packageFirst = await readFile(join(root, \"package.json\"), \"utf8\");\n\tconst settingsFirst = await readFile(\n\t\tjoin(root, \"project.inlang/settings.json\"),\n\t\t\"utf8\"\n\t);\n\tconst messagesFirst = await readFile(join(root, \"messages/en.json\"), \"utf8\");\n\tawait runInit({ nonInteractive: true, compile: false });\n\texpect(await readFile(join(root, \"package.json\"), \"utf8\")).toBe(packageFirst);\n\texpect(\n\t\tawait readFile(join(root, \"project.inlang/settings.json\"), \"utf8\")\n\t).toBe(settingsFirst);\n\texpect(await readFile(join(root, \"messages/en.json\"), \"utf8\")).toBe(\n\t\tmessagesFirst\n\t);\n\tconst tsconfig = await readFile(join(root, \"tsconfig.json\"), \"utf8\");\n\texpect(tsconfig).toContain(\"keep this comment\");\n\texpect(tsconfig).toContain('\"allowJs\": true');\n\texpect(tsconfig).toContain('\"strict\": true');\n\tconst pkg = JSON.parse(packageFirst);\n\texpect(pkg.scripts[\"machine-translate\"]).toBeUndefined();\n\texpect(pkg.scripts[\"lint:i18n\"]).toBeUndefined();\n\texpect(pkg.devDependencies.eslint).toBeUndefined();\n\texpect(utils.prompt).not.toHaveBeenCalled();\n\texpect(utils.promptSelection).not.toHaveBeenCalled();\n});\n\ntest(\"explicit paths and ESLint setup work without prompts\", async () => {\n\tawait writeFile(\n\t\tjoin(root, \"eslint.config.mjs\"),\n\t\t\"// preserve existing config\"\n\t);\n\tawait runInit({\n\t\tyes: true,\n\t\tproject: \"./config/project.inlang\",\n\t\toutdir: \"./src/i18n\",\n\t\tlint: \"eslint\",\n\t\tcheck: false,\n\t\tcompile: false,\n\t});\n\tconst pkg = JSON.parse(await readFile(join(root, \"package.json\"), \"utf8\"));\n\texpect(pkg.scripts[\"check:i18n\"]).toBeUndefined();\n\texpect(pkg.scripts[\"lint:i18n\"]).toContain(\"eslint.paraglide.config.mjs\");\n\texpect(await readFile(join(root, \"eslint.config.mjs\"), \"utf8\")).toBe(\n\t\t\"// preserve existing config\"\n\t);\n\tconst settings = JSON.parse(\n\t\tawait readFile(join(root, \"config/project.inlang/settings.json\"), \"utf8\")\n\t);\n\texpect(settings[\"plugin.inlang.messageFormat\"].pathPattern).toBe(\n\t\t\"../messages/{locale}.json\"\n\t);\n\texpect(pkg.scripts.build).toContain(\"./config/project.inlang\");\n\texpect(pkg.scripts.build).toContain(\"./src/i18n\");\n});\n\ntest(\"closed input without an unattended flag fails before writes\", async () => {\n\tawait expect(runInit({})).rejects.toThrow(\"Use --yes\");\n\texpect(await readdir(root)).toEqual([\"package.json\"]);\n});\n\ntest(\"unwritable integration and invalid paths fail before scaffolding\", async () => {\n\tawait writeFile(\n\t\tjoin(root, \"vite.config.ts\"),\n\t\t\"export default () => ({plugins:getPlugins()});\"\n\t);\n\tawait expect(runInit({ yes: true })).rejects.toThrow(\n\t\t\"Vite configuration cannot\"\n\t);\n\texpect((await readdir(root)).sort()).toEqual([\n\t\t\"package.json\",\n\t\t\"vite.config.ts\",\n\t]);\n\tawait expect(runInit({ yes: true, outdir: \".\" })).rejects.toThrow(\"distinct\");\n});\n\ntest(\"CLI help exposes unattended mode and optional lint choices\", () => {\n\tconst help = createInitCommand().helpInformation();\n\tfor (const flag of [\n\t\t\"--yes\",\n\t\t\"--non-interactive\",\n\t\t\"--outdir\",\n\t\t\"--project\",\n\t\t\"--lint\",\n\t\t\"--allow-dirty\",\n\t\t\"--no-compile\",\n\t])\n\t\texpect(help).toContain(flag);\n});\n\ntest(\"dirty git trees require explicit permission without prompting\", async () => {\n\tawait promisify(execFile)(\"git\", [\"init\", root]);\n\tconst ctx = {\n\t\tlogger: new Logger({ silent: true, prefix: false }),\n\t\troot,\n\t\tinteractive: false,\n\t};\n\tawait expect(checkForUncommittedChanges(ctx)).rejects.toThrow(\n\t\t\"--allow-dirty\"\n\t);\n\tawait expect(\n\t\tcheckForUncommittedChanges({ ...ctx, allowDirty: true })\n\t).resolves.toBeDefined();\n\texpect(utils.prompt).not.toHaveBeenCalled();\n});\n\ntest(\"invalid lint choices fail before creating files\", async () => {\n\tconst command = createInitCommand().exitOverride();\n\tawait expect(\n\t\tcommand.parseAsync([\"--yes\", \"--lint\", \"invalid\"], { from: \"user\" })\n\t).rejects.toThrow(\"Allowed choices\");\n\texpect(await readdir(root)).toEqual([\"package.json\"]);\n});\n"]}
@@ -0,0 +1,11 @@
1
+ export type InitOptions = {
2
+ yes?: boolean;
3
+ nonInteractive?: boolean;
4
+ allowDirty?: boolean;
5
+ project?: string;
6
+ outdir?: string;
7
+ lint?: "none" | "eslint" | "oxlint";
8
+ check?: boolean;
9
+ sherlock?: boolean;
10
+ machineTranslation?: boolean;
11
+ };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=init-options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init-options.js","sourceRoot":"","sources":["../../src/cli/init-options.ts"],"names":[],"mappings":"","sourcesContent":["export type InitOptions = {\n\tyes?: boolean;\n\tnonInteractive?: boolean;\n\tallowDirty?: boolean;\n\tproject?: string;\n\toutdir?: string;\n\tlint?: \"none\" | \"eslint\" | \"oxlint\";\n\tcheck?: boolean;\n\tsherlock?: boolean;\n\tmachineTranslation?: boolean;\n};\n"]}
@@ -1,4 +1,4 @@
1
- import { toRelativeDisplayPath } from "../utils.js";
1
+ import { toRelativeDisplayPath, shellArgument, } from "../utils.js";
2
2
  import { updatePackageJson } from "./update-package-json.js";
3
3
  /**
4
4
  * The `@inlang/cli` range `init` adds. 3.4.0 introduced `inlang check`.
@@ -29,7 +29,7 @@ export const addInlangCheck = async (ctx) => {
29
29
  addedScript = true;
30
30
  return {
31
31
  ...scripts,
32
- [CHECK_SCRIPT_NAME]: `inlang check --project ./${projectPath}`,
32
+ [CHECK_SCRIPT_NAME]: `inlang check --project ${shellArgument("./" + projectPath)}`,
33
33
  };
34
34
  },
35
35
  })(ctx);
@@ -1 +1 @@
1
- {"version":3,"file":"add-inlang-check.js","sourceRoot":"","sources":["../../../src/cli/steps/add-inlang-check.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAgB,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE7D;;GAEG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,QAAQ,CAAC;AAEjD,MAAM,CAAC,MAAM,iBAAiB,GAAG,YAAY,CAAC;AAE9C;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GASvB,KAAK,EAAE,GAAG,EAAE,EAAE;IACjB,MAAM,WAAW,GAAG,qBAAqB,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC;IACrE,IAAI,WAA+B,CAAC;IACpC,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,IAAI,WAAW,GAAG,KAAK,CAAC;IAExB,MAAM,iBAAiB,CAAC;QACvB,eAAe,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;YAClC,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;YACrC,IAAI,WAAW,KAAK,SAAS;gBAAE,OAAO,OAAO,CAAC;YAC9C,QAAQ,GAAG,IAAI,CAAC;YAChB,OAAO,EAAE,GAAG,OAAO,EAAE,aAAa,EAAE,wBAAwB,EAAE,CAAC;QAChE,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;YAC1B,IAAI,OAAO,CAAC,iBAAiB,CAAC,KAAK,SAAS;gBAAE,OAAO,OAAO,CAAC;YAC7D,WAAW,GAAG,IAAI,CAAC;YACnB,OAAO;gBACN,GAAG,OAAO;gBACV,CAAC,iBAAiB,CAAC,EAAE,4BAA4B,WAAW,EAAE;aAC9D,CAAC;QACH,CAAC;KACD,CAAC,CAAC,GAAG,CAAC,CAAC;IAER,IAAI,QAAQ,EAAE,CAAC;QACd,GAAG,CAAC,MAAM,CAAC,OAAO,CACjB,qBAAqB,wBAAwB,0CAA0C,CACvF,CAAC;IACH,CAAC;IACD,IAAI,WAAW,EAAE,CAAC;QACjB,GAAG,CAAC,MAAM,CAAC,OAAO,CACjB,WAAW,iBAAiB,uDAAuD,CACnF,CAAC;IACH,CAAC;IACD,IAAI,WAAW,KAAK,SAAS,IAAI,iBAAiB,CAAC,WAAW,CAAC,EAAE,CAAC;QACjE,GAAG,CAAC,MAAM,CAAC,IAAI,CACd,6EAA6E,WAAW,oBAAoB,wBAAwB,IAAI,CACxI,CAAC;IACH,CAAC;IAED,OAAO,GAAG,CAAC;AACZ,CAAC,CAAC;AAEF;;;;GAIG;AACH,SAAS,iBAAiB,CAAC,KAAa;IACvC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IAC1C,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACzB,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/B,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/B,OAAO,KAAK,GAAG,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC;AAChD,CAAC","sourcesContent":["import type { Logger } from \"../../services/logger/index.js\";\nimport { toRelativeDisplayPath, type CliStep } from \"../utils.js\";\nimport { updatePackageJson } from \"./update-package-json.js\";\n\n/**\n * The `@inlang/cli` range `init` adds. 3.4.0 introduced `inlang check`.\n */\nexport const INLANG_CLI_VERSION_RANGE = \"^3.4.0\";\n\nexport const CHECK_SCRIPT_NAME = \"check:i18n\";\n\n/**\n * Adds `@inlang/cli` to the devDependencies (if not present) and a\n * `check:i18n` script that runs `inlang check` (if no script of that name\n * exists).\n */\nexport const addInlangCheck: CliStep<\n\t{\n\t\tfs: typeof import(\"node:fs/promises\");\n\t\tlogger: Logger;\n\t\tprojectPath: string;\n\t\troot: string;\n\t\tpackageJsonPath: string;\n\t},\n\tunknown\n> = async (ctx) => {\n\tconst projectPath = toRelativeDisplayPath(ctx.root, ctx.projectPath);\n\tlet existingCli: string | undefined;\n\tlet addedCli = false;\n\tlet addedScript = false;\n\n\tawait updatePackageJson({\n\t\tdevDependencies: async (devDeps) => {\n\t\t\texistingCli = devDeps[\"@inlang/cli\"];\n\t\t\tif (existingCli !== undefined) return devDeps;\n\t\t\taddedCli = true;\n\t\t\treturn { ...devDeps, \"@inlang/cli\": INLANG_CLI_VERSION_RANGE };\n\t\t},\n\t\tscripts: async (scripts) => {\n\t\t\tif (scripts[CHECK_SCRIPT_NAME] !== undefined) return scripts;\n\t\t\taddedScript = true;\n\t\t\treturn {\n\t\t\t\t...scripts,\n\t\t\t\t[CHECK_SCRIPT_NAME]: `inlang check --project ./${projectPath}`,\n\t\t\t};\n\t\t},\n\t})(ctx);\n\n\tif (addedCli) {\n\t\tctx.logger.success(\n\t\t\t`Added @inlang/cli ${INLANG_CLI_VERSION_RANGE} to the devDependencies in package.json.`\n\t\t);\n\t}\n\tif (addedScript) {\n\t\tctx.logger.success(\n\t\t\t`Added a ${CHECK_SCRIPT_NAME} script that checks the translations to package.json.`\n\t\t);\n\t}\n\tif (existingCli !== undefined && mayBeOlderThan3_4(existingCli)) {\n\t\tctx.logger.warn(\n\t\t\t`\\`inlang check\\` needs @inlang/cli 3.4.0 or later. Your package.json has \"${existingCli}\". Update it to \"${INLANG_CLI_VERSION_RANGE}\".`\n\t\t);\n\t}\n\n\treturn ctx;\n};\n\n/**\n * Whether a version range may resolve to an `@inlang/cli` before 3.4.0,\n * judged by the first version it names (`^3.0.0`, `3.2.0`, `~2.1.0`).\n * Ranges without a version (`latest`, `*`, `workspace:*`) are trusted.\n */\nfunction mayBeOlderThan3_4(range: string): boolean {\n\tconst match = range.match(/(\\d+)\\.(\\d+)/);\n\tif (!match) return false;\n\tconst major = Number(match[1]);\n\tconst minor = Number(match[2]);\n\treturn major < 3 || (major === 3 && minor < 4);\n}\n"]}
1
+ {"version":3,"file":"add-inlang-check.js","sourceRoot":"","sources":["../../../src/cli/steps/add-inlang-check.ts"],"names":[],"mappings":"AACA,OAAO,EACN,qBAAqB,EACrB,aAAa,GAEb,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE7D;;GAEG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,QAAQ,CAAC;AAEjD,MAAM,CAAC,MAAM,iBAAiB,GAAG,YAAY,CAAC;AAE9C;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GASvB,KAAK,EAAE,GAAG,EAAE,EAAE;IACjB,MAAM,WAAW,GAAG,qBAAqB,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC;IACrE,IAAI,WAA+B,CAAC;IACpC,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,IAAI,WAAW,GAAG,KAAK,CAAC;IAExB,MAAM,iBAAiB,CAAC;QACvB,eAAe,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;YAClC,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;YACrC,IAAI,WAAW,KAAK,SAAS;gBAAE,OAAO,OAAO,CAAC;YAC9C,QAAQ,GAAG,IAAI,CAAC;YAChB,OAAO,EAAE,GAAG,OAAO,EAAE,aAAa,EAAE,wBAAwB,EAAE,CAAC;QAChE,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;YAC1B,IAAI,OAAO,CAAC,iBAAiB,CAAC,KAAK,SAAS;gBAAE,OAAO,OAAO,CAAC;YAC7D,WAAW,GAAG,IAAI,CAAC;YACnB,OAAO;gBACN,GAAG,OAAO;gBACV,CAAC,iBAAiB,CAAC,EAAE,0BAA0B,aAAa,CAAC,IAAI,GAAG,WAAW,CAAC,EAAE;aAClF,CAAC;QACH,CAAC;KACD,CAAC,CAAC,GAAG,CAAC,CAAC;IAER,IAAI,QAAQ,EAAE,CAAC;QACd,GAAG,CAAC,MAAM,CAAC,OAAO,CACjB,qBAAqB,wBAAwB,0CAA0C,CACvF,CAAC;IACH,CAAC;IACD,IAAI,WAAW,EAAE,CAAC;QACjB,GAAG,CAAC,MAAM,CAAC,OAAO,CACjB,WAAW,iBAAiB,uDAAuD,CACnF,CAAC;IACH,CAAC;IACD,IAAI,WAAW,KAAK,SAAS,IAAI,iBAAiB,CAAC,WAAW,CAAC,EAAE,CAAC;QACjE,GAAG,CAAC,MAAM,CAAC,IAAI,CACd,6EAA6E,WAAW,oBAAoB,wBAAwB,IAAI,CACxI,CAAC;IACH,CAAC;IAED,OAAO,GAAG,CAAC;AACZ,CAAC,CAAC;AAEF;;;;GAIG;AACH,SAAS,iBAAiB,CAAC,KAAa;IACvC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IAC1C,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACzB,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/B,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/B,OAAO,KAAK,GAAG,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC;AAChD,CAAC","sourcesContent":["import type { Logger } from \"../../services/logger/index.js\";\nimport {\n\ttoRelativeDisplayPath,\n\tshellArgument,\n\ttype CliStep,\n} from \"../utils.js\";\nimport { updatePackageJson } from \"./update-package-json.js\";\n\n/**\n * The `@inlang/cli` range `init` adds. 3.4.0 introduced `inlang check`.\n */\nexport const INLANG_CLI_VERSION_RANGE = \"^3.4.0\";\n\nexport const CHECK_SCRIPT_NAME = \"check:i18n\";\n\n/**\n * Adds `@inlang/cli` to the devDependencies (if not present) and a\n * `check:i18n` script that runs `inlang check` (if no script of that name\n * exists).\n */\nexport const addInlangCheck: CliStep<\n\t{\n\t\tfs: typeof import(\"node:fs/promises\");\n\t\tlogger: Logger;\n\t\tprojectPath: string;\n\t\troot: string;\n\t\tpackageJsonPath: string;\n\t},\n\tunknown\n> = async (ctx) => {\n\tconst projectPath = toRelativeDisplayPath(ctx.root, ctx.projectPath);\n\tlet existingCli: string | undefined;\n\tlet addedCli = false;\n\tlet addedScript = false;\n\n\tawait updatePackageJson({\n\t\tdevDependencies: async (devDeps) => {\n\t\t\texistingCli = devDeps[\"@inlang/cli\"];\n\t\t\tif (existingCli !== undefined) return devDeps;\n\t\t\taddedCli = true;\n\t\t\treturn { ...devDeps, \"@inlang/cli\": INLANG_CLI_VERSION_RANGE };\n\t\t},\n\t\tscripts: async (scripts) => {\n\t\t\tif (scripts[CHECK_SCRIPT_NAME] !== undefined) return scripts;\n\t\t\taddedScript = true;\n\t\t\treturn {\n\t\t\t\t...scripts,\n\t\t\t\t[CHECK_SCRIPT_NAME]: `inlang check --project ${shellArgument(\"./\" + projectPath)}`,\n\t\t\t};\n\t\t},\n\t})(ctx);\n\n\tif (addedCli) {\n\t\tctx.logger.success(\n\t\t\t`Added @inlang/cli ${INLANG_CLI_VERSION_RANGE} to the devDependencies in package.json.`\n\t\t);\n\t}\n\tif (addedScript) {\n\t\tctx.logger.success(\n\t\t\t`Added a ${CHECK_SCRIPT_NAME} script that checks the translations to package.json.`\n\t\t);\n\t}\n\tif (existingCli !== undefined && mayBeOlderThan3_4(existingCli)) {\n\t\tctx.logger.warn(\n\t\t\t`\\`inlang check\\` needs @inlang/cli 3.4.0 or later. Your package.json has \"${existingCli}\". Update it to \"${INLANG_CLI_VERSION_RANGE}\".`\n\t\t);\n\t}\n\n\treturn ctx;\n};\n\n/**\n * Whether a version range may resolve to an `@inlang/cli` before 3.4.0,\n * judged by the first version it names (`^3.0.0`, `3.2.0`, `~2.1.0`).\n * Ranges without a version (`latest`, `*`, `workspace:*`) are trusted.\n */\nfunction mayBeOlderThan3_4(range: string): boolean {\n\tconst match = range.match(/(\\d+)\\.(\\d+)/);\n\tif (!match) return false;\n\tconst major = Number(match[1]);\n\tconst minor = Number(match[2]);\n\treturn major < 3 || (major === 3 && minor < 4);\n}\n"]}
@@ -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
- return {
38
- ok: false,
39
- reason: "Already present",
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: '${args.projectPath}', outdir: '${args.outdir}' }),` +
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":"AAGA,MAAM,CAAC,MAAM,aAAa,GAStB,KAAK,EAAE,GAAG,EAAE,EAAE;IACjB,IAAI,CAAC;QACJ,MAAM,WAAW,GAAG,MAAM,GAAG,CAAC,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,EAAE;YACzD,QAAQ,EAAE,OAAO;SACjB,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,gBAAgB,CAAC;YAC/B,MAAM,EAAE,WAAW;YACnB,WAAW,EAAE,GAAG,CAAC,WAAW;YAC5B,MAAM,EAAE,GAAG,CAAC,MAAM;SAClB,CAAC,CAAC;QACH,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;YACf,MAAM,GAAG,CAAC,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;YACvD,GAAG,CAAC,MAAM,EAAE,OAAO,CAClB,wDAAwD,CACxD,CAAC;QACH,CAAC;aAAM,CAAC;YACP,MAAM,GAAG,GAAG;gBACX,+DAA+D;gBAC/D,UAAU,GAAG,MAAM,CAAC,MAAM;gBAC1B,EAAE;gBACF,iCAAiC;aACjC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAEb,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;YACtB,OAAO,GAAG,CAAC;QACZ,CAAC;IACF,CAAC;IAAC,MAAM,CAAC;QACR,GAAG,CAAC,MAAM,EAAE,KAAK,CAChB,iDAAiD,GAAG,CAAC,UAAU,GAAG,CAClE,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;IAED,OAAO,GAAG,CAAC;AACZ,CAAC,CAAC;AAcF;;GAEG;AACH,SAAS,gBAAgB,CAAC,IAIzB;IACA,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,sBAAsB,CAAC,EAAE,CAAC;QAClD,OAAO;YACN,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,iBAAiB;SACzB,CAAC;IACH,CAAC;IAED,MAAM,aAAa,GAAG,mBAAmB,CAAC;IAC1C,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAE9C,IAAI,CAAC,KAAK,EAAE,CAAC;QACZ,OAAO;YACN,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,kCAAkC;SAC1C,CAAC;IACH,CAAC;IAED,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;IAC9C,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAE1C,MAAM,aAAa,GAClB,8DAA8D;QAC9D,MAAM;QACN,mCAAmC,IAAI,CAAC,WAAW,eAAe,IAAI,CAAC,MAAM,OAAO;QACpF,KAAK,CAAC;IAEP,OAAO;QACN,EAAE,EAAE,IAAI;QACR,OAAO,EAAE,aAAa;KACtB,CAAC;AACH,CAAC","sourcesContent":["import 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.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 */\nfunction updateViteConfig(args: {\n\tconfig: string;\n\tprojectPath: string;\n\toutdir: string;\n}): UpdateResult {\n\tif (args.config.includes(\"@inlang/paraglide-js\")) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\treason: \"Already present\",\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: '${args.projectPath}', outdir: '${args.outdir}' }),` +\n\t\tafter;\n\n\treturn {\n\t\tok: true,\n\t\tupdated: updatedConfig,\n\t};\n}\n"]}
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"]}