@inlang/paraglide-js 1.0.0-prerelease.2 → 1.0.0-prerelease.4
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 +10 -2
- package/dist/cli/commands/compile.d.ts.map +1 -1
- package/dist/cli/commands/compile.js +16 -19
- package/dist/cli/commands/compile.test.d.ts +2 -0
- package/dist/cli/commands/compile.test.d.ts.map +1 -0
- package/dist/cli/commands/compile.test.js +77 -0
- package/dist/cli/commands/init.d.ts +26 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +277 -0
- package/dist/cli/commands/init.test.d.ts +2 -0
- package/dist/cli/commands/init.test.d.ts.map +1 -0
- package/dist/cli/commands/init.test.js +472 -0
- package/dist/cli/main.d.ts +0 -8
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +12 -21
- package/dist/cli/main.test.d.ts +2 -0
- package/dist/cli/main.test.d.ts.map +1 -0
- package/dist/cli/main.test.js +37 -0
- package/dist/cli/state.d.ts +14 -0
- package/dist/cli/state.d.ts.map +1 -0
- package/dist/cli/state.js +28 -0
- package/dist/compiled-output/website/messages.d.ts +73 -0
- package/dist/compiled-output/website/messages.js +1543 -0
- package/dist/compiled-output/{sveltekit-example → website}/runtime.d.ts +1 -1
- package/dist/compiled-output/{react-example → website}/runtime.js +1 -1
- package/package.json +4 -1
- package/dist/compiled-output/example-javascript/messages.d.ts +0 -7
- package/dist/compiled-output/example-javascript/messages.js +0 -42
- package/dist/compiled-output/example-javascript/runtime.d.ts +0 -38
- package/dist/compiled-output/example-javascript/runtime.js +0 -112
- package/dist/compiled-output/react-example/messages.d.ts +0 -7
- package/dist/compiled-output/react-example/messages.js +0 -42
- package/dist/compiled-output/react-example/runtime.d.ts +0 -38
- package/dist/compiled-output/sveltekit-example/messages.d.ts +0 -7
- package/dist/compiled-output/sveltekit-example/messages.js +0 -42
- package/dist/compiled-output/sveltekit-example/runtime.js +0 -112
- package/dist/compiler/jsdocFromParams.d.ts +0 -12
- package/dist/compiler/jsdocFromParams.d.ts.map +0 -1
- package/dist/compiler/jsdocFromParams.js +0 -17
- package/dist/compiler/jsdocFromParams.test.d.ts +0 -2
- package/dist/compiler/jsdocFromParams.test.d.ts.map +0 -1
- package/dist/compiler/jsdocFromParams.test.js +0 -6
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
import { test, expect, vi, beforeAll, beforeEach } from "vitest";
|
|
2
|
+
import { addCompileStepToPackageJSON, addParaglideJsToDependencies, adjustTsConfigIfNecessary, checkIfPackageJsonExists, checkIfUncommittedChanges, createNewProjectFlow, existingProjectFlow, findExistingInlangProjectPath, initializeInlangProject, newProjectTemplate, promptForNamespace, } from "./init.js";
|
|
3
|
+
import consola from "consola";
|
|
4
|
+
import { describe } from "node:test";
|
|
5
|
+
import fsSync from "node:fs";
|
|
6
|
+
import fs from "node:fs/promises";
|
|
7
|
+
import childProcess from "node:child_process";
|
|
8
|
+
import memfs from "memfs";
|
|
9
|
+
import { version } from "../state.js";
|
|
10
|
+
beforeAll(() => {
|
|
11
|
+
// spy on commonly used functions to prevent console output
|
|
12
|
+
// and allow expecations
|
|
13
|
+
vi.spyOn(consola, "log").mockImplementation(() => undefined);
|
|
14
|
+
vi.spyOn(consola, "info").mockImplementation(() => undefined);
|
|
15
|
+
vi.spyOn(consola, "success").mockImplementation(() => undefined);
|
|
16
|
+
vi.spyOn(consola, "error").mockImplementation(() => undefined);
|
|
17
|
+
vi.spyOn(consola, "warn").mockImplementation(() => undefined);
|
|
18
|
+
});
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
vi.resetAllMocks();
|
|
21
|
+
// Re-mock consola before each test call to remove calls from before
|
|
22
|
+
consola.mockTypes(() => vi.fn());
|
|
23
|
+
// set the current working directory to some mock value to prevent
|
|
24
|
+
// the tests from failing when running in a different environment
|
|
25
|
+
process.cwd = () => "/";
|
|
26
|
+
});
|
|
27
|
+
describe("end to end tests", () => {
|
|
28
|
+
test("it should exit if the user presses CTRL+C", async () => {
|
|
29
|
+
mockFiles({});
|
|
30
|
+
mockUserInput([
|
|
31
|
+
// the first user input is CTRL+C
|
|
32
|
+
() => process.emit("SIGINT", "SIGINT"),
|
|
33
|
+
]);
|
|
34
|
+
// simulating a throw to exit the command early without
|
|
35
|
+
// killing the process the test runs in
|
|
36
|
+
vi.spyOn(process, "exit").mockImplementation(() => {
|
|
37
|
+
throw "process.exit";
|
|
38
|
+
});
|
|
39
|
+
try {
|
|
40
|
+
await checkIfPackageJsonExists();
|
|
41
|
+
}
|
|
42
|
+
catch (e) {
|
|
43
|
+
expect(e).toBe("process.exit");
|
|
44
|
+
}
|
|
45
|
+
expect(process.exit).toHaveBeenCalledOnce();
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
describe("initializeInlangProject()", () => {
|
|
49
|
+
test("it should execute existingProjectFlow() if a project has been found", async () => {
|
|
50
|
+
mockFiles({
|
|
51
|
+
"/folder/project.inlang.json": JSON.stringify(newProjectTemplate),
|
|
52
|
+
"/folder/subfolder": {},
|
|
53
|
+
});
|
|
54
|
+
process.cwd = () => "/folder/subfolder";
|
|
55
|
+
mockUserInput(["useExistingProject"]);
|
|
56
|
+
const path = await initializeInlangProject();
|
|
57
|
+
expect(path).toBe("../project.inlang.json");
|
|
58
|
+
}, {
|
|
59
|
+
// i am on a plane with bad internet
|
|
60
|
+
timeout: 20000,
|
|
61
|
+
});
|
|
62
|
+
test("it should execute newProjectFlow() if not project has been found", async () => {
|
|
63
|
+
const { existsSync } = mockFiles({});
|
|
64
|
+
mockUserInput(["newProject"]);
|
|
65
|
+
const path = await initializeInlangProject();
|
|
66
|
+
expect(path).toBe("./project.inlang.json");
|
|
67
|
+
expect(existsSync("./project.inlang.json")).toBe(true);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
describe("addParaglideJsToDependencies()", () => {
|
|
71
|
+
test("it should add paraglide-js to the dependencies with the latest version", async () => {
|
|
72
|
+
mockFiles({
|
|
73
|
+
"/package.json": "{}",
|
|
74
|
+
});
|
|
75
|
+
await addParaglideJsToDependencies();
|
|
76
|
+
expect(fs.writeFile).toHaveBeenCalledOnce();
|
|
77
|
+
expect(consola.success).toHaveBeenCalledOnce();
|
|
78
|
+
const packageJson = JSON.parse((await fs.readFile("/package.json", { encoding: "utf-8" })));
|
|
79
|
+
expect(packageJson.dependencies["@inlang/paraglide-js"]).toBe(version);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
describe("addCompileStepToPackageJSON()", () => {
|
|
83
|
+
test("if no scripts exist, it should add scripts.build", async () => {
|
|
84
|
+
mockFiles({
|
|
85
|
+
"/package.json": "{}",
|
|
86
|
+
});
|
|
87
|
+
await addCompileStepToPackageJSON({
|
|
88
|
+
projectPath: "./project.inlang.json",
|
|
89
|
+
namespace: "frontend",
|
|
90
|
+
});
|
|
91
|
+
expect(fs.writeFile).toHaveBeenCalledOnce();
|
|
92
|
+
expect(consola.success).toHaveBeenCalledOnce();
|
|
93
|
+
const packageJson = JSON.parse((await fs.readFile("/package.json", { encoding: "utf-8" })));
|
|
94
|
+
expect(packageJson.scripts.build).toBe(`paraglide-js compile --project ./project.inlang.json --namespace frontend`);
|
|
95
|
+
});
|
|
96
|
+
test("if an existing build step exists, it should be preceeded by the paraglide-js compile command", async () => {
|
|
97
|
+
mockFiles({
|
|
98
|
+
"/package.json": JSON.stringify({
|
|
99
|
+
scripts: {
|
|
100
|
+
build: "some build step",
|
|
101
|
+
},
|
|
102
|
+
}),
|
|
103
|
+
});
|
|
104
|
+
await addCompileStepToPackageJSON({
|
|
105
|
+
projectPath: "./project.inlang.json",
|
|
106
|
+
namespace: "frontend",
|
|
107
|
+
});
|
|
108
|
+
expect(fs.writeFile).toHaveBeenCalledOnce();
|
|
109
|
+
expect(consola.success).toHaveBeenCalledOnce();
|
|
110
|
+
const packageJson = JSON.parse((await fs.readFile("/package.json", { encoding: "utf-8" })));
|
|
111
|
+
expect(packageJson.scripts.build).toBe(`paraglide-js compile --project ./project.inlang.json --namespace frontend && some build step`);
|
|
112
|
+
});
|
|
113
|
+
test("if a paraglide-js compile step already exists, the user should be prompted to update it manually and exit if the user rejects", async () => {
|
|
114
|
+
mockFiles({
|
|
115
|
+
"/package.json": JSON.stringify({
|
|
116
|
+
scripts: {
|
|
117
|
+
build: "paraglide-js compile --project ./project.inlang.json",
|
|
118
|
+
},
|
|
119
|
+
}),
|
|
120
|
+
});
|
|
121
|
+
mockUserInput([
|
|
122
|
+
// user does not want to update the build step
|
|
123
|
+
false,
|
|
124
|
+
]);
|
|
125
|
+
await addCompileStepToPackageJSON({
|
|
126
|
+
projectPath: "./project.inlang.json",
|
|
127
|
+
namespace: "frontend",
|
|
128
|
+
});
|
|
129
|
+
expect(fs.writeFile).not.toHaveBeenCalled();
|
|
130
|
+
expect(consola.success).not.toHaveBeenCalled();
|
|
131
|
+
expect(consola.warn).toHaveBeenCalledOnce();
|
|
132
|
+
expect(process.exit).toHaveBeenCalled();
|
|
133
|
+
});
|
|
134
|
+
test("if a paraglide-js compile step already exists, the user should be prompted to update it manually and return if they did so", async () => {
|
|
135
|
+
mockFiles({
|
|
136
|
+
"/package.json": JSON.stringify({
|
|
137
|
+
scripts: {
|
|
138
|
+
build: "paraglide-js compile --project ./project.inlang.json",
|
|
139
|
+
},
|
|
140
|
+
}),
|
|
141
|
+
});
|
|
142
|
+
mockUserInput([
|
|
143
|
+
// user does not want to update the build step
|
|
144
|
+
true,
|
|
145
|
+
]);
|
|
146
|
+
await addCompileStepToPackageJSON({
|
|
147
|
+
projectPath: "./project.inlang.json",
|
|
148
|
+
namespace: "frontend",
|
|
149
|
+
});
|
|
150
|
+
expect(fs.writeFile).not.toHaveBeenCalled();
|
|
151
|
+
expect(consola.success).not.toHaveBeenCalled();
|
|
152
|
+
expect(consola.warn).toHaveBeenCalledOnce();
|
|
153
|
+
expect(process.exit).not.toHaveBeenCalled();
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
describe("promptForNamespace()", async () => {
|
|
157
|
+
test("it should trim the input from whitespace", async () => {
|
|
158
|
+
mockUserInput([" frontend "]);
|
|
159
|
+
const namespace = await promptForNamespace();
|
|
160
|
+
expect(namespace).toBe("frontend");
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
describe("existingProjectFlow()", () => {
|
|
164
|
+
test("if the user selects to proceed with the existing project and the project has no errors, the function should return", async () => {
|
|
165
|
+
mockFiles({
|
|
166
|
+
"/project.inlang.json": JSON.stringify(newProjectTemplate),
|
|
167
|
+
});
|
|
168
|
+
mockUserInput(["useExistingProject"]);
|
|
169
|
+
expect(existingProjectFlow({ existingProjectPath: "/project.inlang.json" })).resolves.toBeUndefined();
|
|
170
|
+
});
|
|
171
|
+
test("if the user selects new project, the newProjectFlow() should be executed", async () => {
|
|
172
|
+
const { existsSync } = mockFiles({
|
|
173
|
+
"/folder/project.inlang.json": JSON.stringify(newProjectTemplate),
|
|
174
|
+
});
|
|
175
|
+
mockUserInput(["newProject"]);
|
|
176
|
+
await existingProjectFlow({ existingProjectPath: "/folder/project.inlang.json" });
|
|
177
|
+
// info that a new project is created
|
|
178
|
+
expect(consola.info).toHaveBeenCalledOnce();
|
|
179
|
+
// the newly created project file should exist
|
|
180
|
+
expect(existsSync("/project.inlang.json")).toBe(true);
|
|
181
|
+
});
|
|
182
|
+
test("it should exit if the existing project contains errors", async () => {
|
|
183
|
+
mockFiles({
|
|
184
|
+
"/project.inlang.json": `BROKEN PROJECT FILE`,
|
|
185
|
+
});
|
|
186
|
+
mockUserInput(["useExistingProject"]);
|
|
187
|
+
await existingProjectFlow({ existingProjectPath: "/project.inlang.json" });
|
|
188
|
+
expect(consola.error).toHaveBeenCalled();
|
|
189
|
+
expect(process.exit).toHaveBeenCalled();
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
describe("createNewProjectFlow()", async () => {
|
|
193
|
+
test("it should succeed in creating a new project", async () => {
|
|
194
|
+
const { existsSync } = mockFiles({});
|
|
195
|
+
await createNewProjectFlow();
|
|
196
|
+
// user is informed that a new project is created
|
|
197
|
+
expect(consola.info).toHaveBeenCalledOnce();
|
|
198
|
+
// the project shouldn't have errors
|
|
199
|
+
expect(consola.error).not.toHaveBeenCalled();
|
|
200
|
+
// user is informed that the project has successfully been created
|
|
201
|
+
expect(consola.success).toHaveBeenCalledOnce();
|
|
202
|
+
// the project file should exist
|
|
203
|
+
expect(existsSync("/project.inlang.json")).toBe(true);
|
|
204
|
+
}, {
|
|
205
|
+
// i am testing this while i am on an airplane with slow internet
|
|
206
|
+
timeout: 20000,
|
|
207
|
+
});
|
|
208
|
+
test("it should exit if the project has errors", async () => {
|
|
209
|
+
mockFiles({});
|
|
210
|
+
// invalid project settings file
|
|
211
|
+
vi.spyOn(JSON, "stringify").mockReturnValue(`{}`);
|
|
212
|
+
await createNewProjectFlow();
|
|
213
|
+
// user is informed that a new project is created
|
|
214
|
+
expect(consola.info).toHaveBeenCalledOnce();
|
|
215
|
+
// the project has errors
|
|
216
|
+
expect(consola.error).toHaveBeenCalled();
|
|
217
|
+
// the commands exits
|
|
218
|
+
expect(process.exit).toHaveBeenCalled();
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
describe("checkIfUncommittedChanges()", () => {
|
|
222
|
+
test("it should not fail if the git cli is not installed", async () => {
|
|
223
|
+
vi.spyOn(childProcess, "execSync").mockImplementation(() => {
|
|
224
|
+
throw Error("Command failed: git status");
|
|
225
|
+
});
|
|
226
|
+
expect(checkIfUncommittedChanges()).resolves.toBeUndefined();
|
|
227
|
+
});
|
|
228
|
+
test("it should continue if no uncomitted changes exist", async () => {
|
|
229
|
+
vi.spyOn(childProcess, "execSync").mockImplementation(() => {
|
|
230
|
+
return Buffer.from("");
|
|
231
|
+
});
|
|
232
|
+
expect(checkIfUncommittedChanges()).resolves.toBeUndefined();
|
|
233
|
+
});
|
|
234
|
+
test("it should prompt the user if there are uncommitted changes and exit if the user doesn't want to continue", async () => {
|
|
235
|
+
vi.spyOn(childProcess, "execSync").mockImplementation(() => {
|
|
236
|
+
return Buffer.from("M package.json");
|
|
237
|
+
});
|
|
238
|
+
const processExit = vi.spyOn(process, "exit").mockImplementation(() => undefined);
|
|
239
|
+
mockUserInput([
|
|
240
|
+
// user does not want to continue
|
|
241
|
+
false,
|
|
242
|
+
]);
|
|
243
|
+
await checkIfUncommittedChanges();
|
|
244
|
+
expect(consola.info).toHaveBeenCalledOnce();
|
|
245
|
+
expect(consola.prompt).toHaveBeenCalledOnce();
|
|
246
|
+
expect(processExit).toHaveBeenCalledOnce();
|
|
247
|
+
});
|
|
248
|
+
test("it should prompt the user if there are uncommitted changes and return if the user wants to continue", async () => {
|
|
249
|
+
vi.spyOn(childProcess, "execSync").mockImplementation(() => {
|
|
250
|
+
return Buffer.from("M package.json");
|
|
251
|
+
});
|
|
252
|
+
const processExit = vi.spyOn(process, "exit").mockImplementation(() => undefined);
|
|
253
|
+
mockUserInput([
|
|
254
|
+
// user does want to continue
|
|
255
|
+
true,
|
|
256
|
+
]);
|
|
257
|
+
await checkIfUncommittedChanges();
|
|
258
|
+
expect(consola.info).toHaveBeenCalledOnce();
|
|
259
|
+
expect(consola.prompt).toHaveBeenCalledOnce();
|
|
260
|
+
expect(processExit).not.toHaveBeenCalledOnce();
|
|
261
|
+
});
|
|
262
|
+
test("it should not prompt the user if no uncommitted changes exist", async () => {
|
|
263
|
+
await checkIfUncommittedChanges();
|
|
264
|
+
expect(consola.prompt).not.toHaveBeenCalled();
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
describe("checkIfPackageJsonExists()", () => {
|
|
268
|
+
test("it should exit if no package.json has been found", async () => {
|
|
269
|
+
mockFiles({});
|
|
270
|
+
await checkIfPackageJsonExists();
|
|
271
|
+
expect(consola.warn).toHaveBeenCalledOnce();
|
|
272
|
+
expect(process.exit).toHaveBeenCalledOnce();
|
|
273
|
+
});
|
|
274
|
+
test("it should not exit if a package.json exists in the current working directory", async () => {
|
|
275
|
+
mockFiles({ "package.json": "" });
|
|
276
|
+
await checkIfPackageJsonExists();
|
|
277
|
+
expect(consola.warn).not.toHaveBeenCalled();
|
|
278
|
+
expect(process.exit).not.toHaveBeenCalled();
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
describe("findExistingInlangProjectPath()", () => {
|
|
282
|
+
test("it should return undefined if no project.inlang.json has been found", async () => {
|
|
283
|
+
mockFiles({});
|
|
284
|
+
const path = await findExistingInlangProjectPath();
|
|
285
|
+
expect(path).toBeUndefined();
|
|
286
|
+
});
|
|
287
|
+
test("it find a project in the current working directory", async () => {
|
|
288
|
+
process.cwd = () => "/";
|
|
289
|
+
mockFiles({ "project.inlang.json": "{}" });
|
|
290
|
+
const path = await findExistingInlangProjectPath();
|
|
291
|
+
expect(path).toBe("./project.inlang.json");
|
|
292
|
+
});
|
|
293
|
+
test("it should find a project in a parent directory", async () => {
|
|
294
|
+
process.cwd = () => "/nested/";
|
|
295
|
+
mockFiles({
|
|
296
|
+
"/project.inlang.json": "{}",
|
|
297
|
+
"/nested/": {},
|
|
298
|
+
});
|
|
299
|
+
const path = await findExistingInlangProjectPath();
|
|
300
|
+
expect(path).toBe("../project.inlang.json");
|
|
301
|
+
});
|
|
302
|
+
test("it should find a project in a parent parent directory", async () => {
|
|
303
|
+
process.cwd = () => "/nested/nested/";
|
|
304
|
+
mockFiles({
|
|
305
|
+
"/project.inlang.json": "{}",
|
|
306
|
+
"/nested/nested/": {},
|
|
307
|
+
});
|
|
308
|
+
const path = await findExistingInlangProjectPath();
|
|
309
|
+
expect(path).toBe("../../project.inlang.json");
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
describe("adjustTsConfigIfNecessary()", () => {
|
|
313
|
+
test("it should return if no tsconfig.json exists", async () => {
|
|
314
|
+
mockFiles({});
|
|
315
|
+
const result = await adjustTsConfigIfNecessary();
|
|
316
|
+
// no tsconfig exists, immediately return
|
|
317
|
+
expect(result).toBeUndefined();
|
|
318
|
+
// the tsconfig should not have been read
|
|
319
|
+
expect(fs.readFile).not.toHaveBeenCalled();
|
|
320
|
+
// no info that the moduleResolution needs to be adapted should be logged
|
|
321
|
+
expect(consola.info).not.toHaveBeenCalled();
|
|
322
|
+
});
|
|
323
|
+
test("it should warn if the extended from tsconfig can't be read", async () => {
|
|
324
|
+
mockFiles({
|
|
325
|
+
"/tsconfig.json": `{
|
|
326
|
+
"extends": "./non-existend.json",
|
|
327
|
+
"compilerOptions": {
|
|
328
|
+
"moduleResolution": "Bundler"
|
|
329
|
+
}
|
|
330
|
+
}`,
|
|
331
|
+
});
|
|
332
|
+
await adjustTsConfigIfNecessary();
|
|
333
|
+
// no info that the moduleResolution needs to be adapted should be logged
|
|
334
|
+
expect(consola.warn).toHaveBeenCalledOnce();
|
|
335
|
+
});
|
|
336
|
+
test("it should detect if the extended from tsconfig already set the moduleResolution to bundler", async () => {
|
|
337
|
+
mockFiles({
|
|
338
|
+
"/tsconfig.base.json": `{
|
|
339
|
+
"compilerOptions": {
|
|
340
|
+
"moduleResolution": "Bundler"
|
|
341
|
+
}
|
|
342
|
+
}`,
|
|
343
|
+
"/tsconfig.json": `{
|
|
344
|
+
"extends": "tsconfig.base.json",
|
|
345
|
+
}`,
|
|
346
|
+
});
|
|
347
|
+
await adjustTsConfigIfNecessary();
|
|
348
|
+
// no info that the moduleResolution needs to be adapted should be logged
|
|
349
|
+
expect(consola.info).not.toHaveBeenCalled();
|
|
350
|
+
});
|
|
351
|
+
test("it should prompt the user to set the moduleResolution to bundler", async () => {
|
|
352
|
+
mockFiles({
|
|
353
|
+
"/tsconfig.json": `{}`,
|
|
354
|
+
});
|
|
355
|
+
const userAdjustsTsConfigSpy = vi.fn();
|
|
356
|
+
mockUserInput([
|
|
357
|
+
// user confirms that the moduleResolution has been set to bundler
|
|
358
|
+
() => {
|
|
359
|
+
userAdjustsTsConfigSpy();
|
|
360
|
+
fs.writeFile("/tsconfig.json", `{
|
|
361
|
+
"compilerOptions": {
|
|
362
|
+
"moduleResolution": "Bundler"
|
|
363
|
+
}
|
|
364
|
+
}`);
|
|
365
|
+
return true;
|
|
366
|
+
},
|
|
367
|
+
]);
|
|
368
|
+
await adjustTsConfigIfNecessary();
|
|
369
|
+
// info that the moduleResolution needs to be adapted
|
|
370
|
+
expect(consola.info).toHaveBeenCalledOnce();
|
|
371
|
+
// prompt the user to set the moduleResolution to bundler
|
|
372
|
+
expect(consola.prompt).toHaveBeenCalledOnce();
|
|
373
|
+
// user has set the moduleResolution to bundler
|
|
374
|
+
expect(userAdjustsTsConfigSpy).toHaveBeenCalledOnce();
|
|
375
|
+
});
|
|
376
|
+
test("it should keep prompting the user to set the moduleResolution to bundler if the moduleResolution has not been set", async () => {
|
|
377
|
+
mockFiles({
|
|
378
|
+
"/tsconfig.json": `{}`,
|
|
379
|
+
});
|
|
380
|
+
mockUserInput([
|
|
381
|
+
// user confirms that the moduleResolution has been set to bundler
|
|
382
|
+
// while the moduleResolution is still not set
|
|
383
|
+
// -> should prompt again
|
|
384
|
+
true,
|
|
385
|
+
// user wants to exit
|
|
386
|
+
// -> should warn that type errors might occur and continue with init
|
|
387
|
+
false,
|
|
388
|
+
]);
|
|
389
|
+
await adjustTsConfigIfNecessary();
|
|
390
|
+
// info that the moduleResolution needs to be adapted
|
|
391
|
+
expect(consola.warn).toHaveBeenCalledOnce();
|
|
392
|
+
// 1. prompt the user to set the moduleResolution to bundler
|
|
393
|
+
// 2. prompt again because the moduleResolution is still not set
|
|
394
|
+
expect(consola.prompt).toHaveBeenCalledTimes(2);
|
|
395
|
+
// the user has not set the moduleResolution to bundler
|
|
396
|
+
// after the first prompt eventhough the user said it did
|
|
397
|
+
expect(consola.error).toHaveBeenCalledOnce();
|
|
398
|
+
// the user exists without setting the moduleResolution to bundler
|
|
399
|
+
// warn about type errors
|
|
400
|
+
expect(consola.warn).toHaveBeenCalledOnce();
|
|
401
|
+
});
|
|
402
|
+
});
|
|
403
|
+
// test("the paraglide plugin for vscode should be installed", () => {
|
|
404
|
+
// throw new Error("Not implemented")
|
|
405
|
+
// })
|
|
406
|
+
// test("it sets the tsconfig compiler option 'moduleResolution' to 'bundler'", () => {
|
|
407
|
+
// throw new Error("Not implemented")
|
|
408
|
+
// })
|
|
409
|
+
// test("it should create a project if it doesn't exist", () => {
|
|
410
|
+
// test("the inlang message format should be the default selection", () => {
|
|
411
|
+
// throw new Error("Not implemented")
|
|
412
|
+
// })
|
|
413
|
+
// })
|
|
414
|
+
// test("the vscode extension should be added to the workspace recommendations", () => {
|
|
415
|
+
// test("automatically if the .vscode folder exists", () => {
|
|
416
|
+
// throw new Error("Not implemented")
|
|
417
|
+
// })
|
|
418
|
+
// test("else, the user should be prompted if the vscode extension should be added", () => {
|
|
419
|
+
// throw new Error("Not implemented")
|
|
420
|
+
// })
|
|
421
|
+
// })
|
|
422
|
+
// describe("it should prompt the user if the cli should be added for linting and machine translations", () => {
|
|
423
|
+
// test("the cli should be added to the devDependencies", () => {
|
|
424
|
+
// throw new Error("Not implemented")
|
|
425
|
+
// })
|
|
426
|
+
// test("if a lint script exists, inlang lint should be added to the lint script", () => {
|
|
427
|
+
// throw new Error("Not implemented")
|
|
428
|
+
// })
|
|
429
|
+
// test("if no lint script exists, the lint script should be created in the package.json", () => {
|
|
430
|
+
// throw new Error("Not implemented")
|
|
431
|
+
// })
|
|
432
|
+
// })
|
|
433
|
+
// test("the user should be prompted for the framework and forwarded to the corresponding guide", () => {
|
|
434
|
+
// throw new Error("Not implemented")
|
|
435
|
+
// })
|
|
436
|
+
/**
|
|
437
|
+
* Mock user input.
|
|
438
|
+
*
|
|
439
|
+
* @example
|
|
440
|
+
* mockUserInput([
|
|
441
|
+
* "some input",
|
|
442
|
+
* () => {
|
|
443
|
+
* fs.writeFileSync("some-file", "some content")
|
|
444
|
+
* return true
|
|
445
|
+
* }
|
|
446
|
+
* ]),
|
|
447
|
+
*/
|
|
448
|
+
const mockUserInput = (testUserInput) => {
|
|
449
|
+
vi.spyOn(consola, "prompt").mockImplementation(() => {
|
|
450
|
+
const value = testUserInput.shift();
|
|
451
|
+
if (value === undefined) {
|
|
452
|
+
throw new Error("End of test input");
|
|
453
|
+
}
|
|
454
|
+
else if (typeof value === "function") {
|
|
455
|
+
return value();
|
|
456
|
+
}
|
|
457
|
+
return value;
|
|
458
|
+
});
|
|
459
|
+
};
|
|
460
|
+
const mockFiles = (files) => {
|
|
461
|
+
const _memfs = memfs.createFsFromVolume(memfs.Volume.fromNestedJSON(files));
|
|
462
|
+
vi.spyOn(fsSync, "existsSync").mockImplementation(_memfs.existsSync);
|
|
463
|
+
for (const prop in fs) {
|
|
464
|
+
// @ts-ignore - memfs has the same interface as node:fs/promises
|
|
465
|
+
if (typeof fs[prop] !== "function")
|
|
466
|
+
continue;
|
|
467
|
+
// @ts-ignore - memfs has the same interface as node:fs/promises
|
|
468
|
+
vi.spyOn(fs, prop).mockImplementation(_memfs.promises[prop]);
|
|
469
|
+
}
|
|
470
|
+
return { existsSync: _memfs.existsSync };
|
|
471
|
+
};
|
|
472
|
+
test("a tsconfig with comments should be loaded correctly", () => { });
|
package/dist/cli/main.d.ts
CHANGED
|
@@ -1,11 +1,3 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
-
/**
|
|
3
|
-
* The absolute path to the paraglide directory.
|
|
4
|
-
*
|
|
5
|
-
* slices a path
|
|
6
|
-
* from '/Users/samuel/example/repository/node_modules/paraglide-js/dist/cli/main.js'
|
|
7
|
-
* to '/Users/samuel/example/repository/node_modules/paraglide-js'
|
|
8
|
-
*/
|
|
9
|
-
export declare const paraglideDirectory: string;
|
|
10
2
|
export declare const cli: Command;
|
|
11
3
|
//# sourceMappingURL=main.d.ts.map
|
package/dist/cli/main.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAKnC,eAAO,MAAM,GAAG,SA0Bb,CAAA"}
|
package/dist/cli/main.js
CHANGED
|
@@ -1,40 +1,31 @@
|
|
|
1
1
|
import consola from "consola";
|
|
2
2
|
import dedent from "dedent";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
4
3
|
import { Command } from "commander";
|
|
5
4
|
import { compileCommand } from "./commands/compile.js";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
*
|
|
9
|
-
* Usually something like '/Users/samuel/example/repository/node_modules/paraglide-js/dist/cli/main.js'
|
|
10
|
-
*/
|
|
11
|
-
const metaUrlPath = fileURLToPath(import.meta.url);
|
|
12
|
-
/**
|
|
13
|
-
* The absolute path to the paraglide directory.
|
|
14
|
-
*
|
|
15
|
-
* slices a path
|
|
16
|
-
* from '/Users/samuel/example/repository/node_modules/paraglide-js/dist/cli/main.js'
|
|
17
|
-
* to '/Users/samuel/example/repository/node_modules/paraglide-js'
|
|
18
|
-
*/
|
|
19
|
-
export const paraglideDirectory = metaUrlPath.slice(0, metaUrlPath.indexOf("/dist/"));
|
|
5
|
+
import { paraglideDirectory, version } from "./state.js";
|
|
6
|
+
import { initCommand } from "./commands/init.js";
|
|
20
7
|
export const cli = new Command()
|
|
21
8
|
.name("paraglide-js")
|
|
22
9
|
.addCommand(compileCommand)
|
|
23
|
-
.
|
|
10
|
+
.addCommand(initCommand)
|
|
11
|
+
.showHelpAfterError()
|
|
12
|
+
.version(version)
|
|
13
|
+
.action(() => {
|
|
24
14
|
// ------------------- VALIDATE IF RUNNING FROM CORRECT FOLDER -------------------
|
|
25
15
|
// the CLI expects to be running from the dist folder of the specific paraglide package
|
|
26
16
|
// to compile the output in the correct directory
|
|
27
|
-
if (
|
|
17
|
+
if (paraglideDirectory.endsWith(`paraglide-js`) === false) {
|
|
28
18
|
consola.error(dedent `
|
|
29
|
-
The CLI is not running from
|
|
19
|
+
The CLI is not running from a paraglide-js directory.
|
|
30
20
|
|
|
31
21
|
This is likely an internal bug. Please file an issue at https://github.com/inlang/monorepo/issues.
|
|
32
22
|
|
|
33
23
|
Debug information:
|
|
34
24
|
|
|
35
|
-
-
|
|
25
|
+
- paraglideDirectory: ${paraglideDirectory}
|
|
36
26
|
`);
|
|
37
|
-
process.exit(1);
|
|
27
|
+
return process.exit(1);
|
|
38
28
|
}
|
|
29
|
+
// show the help because no command is specified
|
|
30
|
+
return cli.help();
|
|
39
31
|
});
|
|
40
|
-
cli.showHelpAfterError(true);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.test.d.ts","sourceRoot":"","sources":["../../src/cli/main.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { describe, test, expect, vi } from "vitest";
|
|
2
|
+
import { _setStateForTest } from "./state.js";
|
|
3
|
+
import { cli } from "./main.js";
|
|
4
|
+
import consola from "consola";
|
|
5
|
+
// surpress output for clean test output
|
|
6
|
+
cli.configureOutput({ writeErr: () => { }, writeOut: () => { } });
|
|
7
|
+
describe("it should exit if the CLI is not running from the dist folder", () => {
|
|
8
|
+
const shouldSucceed = [
|
|
9
|
+
"/Users/example/repository/node_modules/paraglide-js",
|
|
10
|
+
"/node_modules/paraglide-js",
|
|
11
|
+
// windows... https://github.com/inlang/monorepo/issues/1478
|
|
12
|
+
"C:\\Users\\Projects\\svelte-one\\node_modules\\@inlang\\paraglide-js",
|
|
13
|
+
];
|
|
14
|
+
const shouldFail = ["/Users/samuel/example/repository"];
|
|
15
|
+
const exitSpy = vi.spyOn(process, "exit").mockImplementation(() => undefined);
|
|
16
|
+
// remove error message to have clean test output
|
|
17
|
+
vi.spyOn(consola, "error").mockImplementation(() => undefined);
|
|
18
|
+
for (const path of shouldSucceed) {
|
|
19
|
+
test("suceed with " + path, async () => {
|
|
20
|
+
// surpress output to have clean test output
|
|
21
|
+
_setStateForTest({
|
|
22
|
+
paraglideDirectory: path,
|
|
23
|
+
});
|
|
24
|
+
await cli.parseAsync([]);
|
|
25
|
+
expect(exitSpy).toHaveBeenLastCalledWith(0);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
for (const path of shouldFail) {
|
|
29
|
+
test("fail with " + path, async () => {
|
|
30
|
+
_setStateForTest({
|
|
31
|
+
paraglideDirectory: path,
|
|
32
|
+
});
|
|
33
|
+
await cli.parseAsync([]);
|
|
34
|
+
expect(exitSpy).toHaveBeenLastCalledWith(1);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The absolute path to the paraglide directory.
|
|
3
|
+
*
|
|
4
|
+
* slices a path
|
|
5
|
+
* from '/Users/samuel/example/repository/node_modules/paraglide-js/dist/cli/main.js'
|
|
6
|
+
* to '/Users/samuel/example/repository/node_modules/paraglide-js'
|
|
7
|
+
*/
|
|
8
|
+
export declare let paraglideDirectory: string;
|
|
9
|
+
export declare const _setStateForTest: (values: Record<"paraglideDirectory", any>) => void;
|
|
10
|
+
/**
|
|
11
|
+
* The version of the paraglide-js package.
|
|
12
|
+
*/
|
|
13
|
+
export declare const version: any;
|
|
14
|
+
//# sourceMappingURL=state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/cli/state.ts"],"names":[],"mappings":"AAaA;;;;;;GAMG;AACH,eAAO,IAAI,kBAAkB,EAAE,MAG9B,CAAA;AAID,eAAO,MAAM,gBAAgB,WAAY,OAAO,oBAAoB,EAAE,GAAG,CAAC,SAEzE,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,OAAO,KAEX,CAAA"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
/**
|
|
5
|
+
* The posix path this file is executed from.
|
|
6
|
+
*
|
|
7
|
+
* Usually something like '/Users/samuel/example/repository/node_modules/paraglide-js/dist/cli/main.js'
|
|
8
|
+
*/
|
|
9
|
+
const currentFilePath = fileURLToPath(import.meta.url)
|
|
10
|
+
.split(path.sep)
|
|
11
|
+
.join(path.posix.sep);
|
|
12
|
+
/**
|
|
13
|
+
* The absolute path to the paraglide directory.
|
|
14
|
+
*
|
|
15
|
+
* slices a path
|
|
16
|
+
* from '/Users/samuel/example/repository/node_modules/paraglide-js/dist/cli/main.js'
|
|
17
|
+
* to '/Users/samuel/example/repository/node_modules/paraglide-js'
|
|
18
|
+
*/
|
|
19
|
+
export let paraglideDirectory = currentFilePath.slice(0, currentFilePath.indexOf("/paraglide-js/") + "/paraglide-js".length);
|
|
20
|
+
// this is only used for testing
|
|
21
|
+
// use cmd + f to find all usages
|
|
22
|
+
export const _setStateForTest = (values) => {
|
|
23
|
+
paraglideDirectory = values.paraglideDirectory;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* The version of the paraglide-js package.
|
|
27
|
+
*/
|
|
28
|
+
export const version = JSON.parse(fs.readFileSync(`${paraglideDirectory}/package.json`, "utf-8")).version;
|