@inlang/paraglide-js 2.24.1 → 2.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundler-plugins/esbuild.d.ts +1 -1
- package/dist/bundler-plugins/per-locale-build/index.d.ts +3 -3
- package/dist/bundler-plugins/per-locale-build/index.js +59 -15
- package/dist/bundler-plugins/per-locale-build/index.js.map +1 -1
- package/dist/bundler-plugins/rolldown.d.ts +1 -1
- package/dist/bundler-plugins/rollup.d.ts +1 -1
- package/dist/bundler-plugins/rspack.d.ts +1 -1
- package/dist/bundler-plugins/unplugin.d.ts +35 -1
- package/dist/bundler-plugins/unplugin.js +404 -123
- package/dist/bundler-plugins/unplugin.js.map +1 -1
- package/dist/bundler-plugins/unplugin.test.js +357 -202
- package/dist/bundler-plugins/unplugin.test.js.map +1 -1
- package/dist/bundler-plugins/vite.d.ts +2 -2
- package/dist/bundler-plugins/vite.js.map +1 -1
- package/dist/bundler-plugins/vite.test.js +2 -1
- package/dist/bundler-plugins/vite.test.js.map +1 -1
- package/dist/bundler-plugins/webpack.d.ts +1 -1
- package/dist/cli/commands/compile/command.js +156 -36
- package/dist/cli/commands/compile/command.js.map +1 -1
- package/dist/cli/commands/compile/command.test.js +311 -7
- package/dist/cli/commands/compile/command.test.js.map +1 -1
- package/dist/cli/defaults.d.ts +0 -2
- package/dist/cli/defaults.js +0 -2
- package/dist/cli/defaults.js.map +1 -1
- package/dist/cli/steps/initialize-inlang-project.js +2 -1
- package/dist/cli/steps/initialize-inlang-project.js.map +1 -1
- package/dist/cli/steps/prompt-for-outdir.js +1 -1
- package/dist/cli/steps/prompt-for-outdir.js.map +1 -1
- package/dist/compiler/compile-annotation.d.ts +2 -2
- package/dist/compiler/compile-annotation.js +9 -9
- package/dist/compiler/compile-annotation.js.map +1 -1
- package/dist/compiler/compile-local-variable.d.ts +2 -1
- package/dist/compiler/compile-local-variable.js +4 -4
- package/dist/compiler/compile-local-variable.js.map +1 -1
- package/dist/compiler/compile-local-variable.test.js +82 -0
- package/dist/compiler/compile-local-variable.test.js.map +1 -1
- package/dist/compiler/compile-message.js +10 -2
- package/dist/compiler/compile-message.js.map +1 -1
- package/dist/compiler/compile-message.test.js +132 -2
- package/dist/compiler/compile-message.test.js.map +1 -1
- package/dist/compiler/compile-pattern.js +4 -2
- package/dist/compiler/compile-pattern.js.map +1 -1
- package/dist/compiler/compile-pattern.test.js +31 -0
- package/dist/compiler/compile-pattern.test.js.map +1 -1
- package/dist/compiler/compiler-options.d.ts +1 -1
- package/dist/compiler/compiler-options.js.map +1 -1
- package/dist/compiler/server/create-server-file.js +18 -4
- package/dist/compiler/server/create-server-file.js.map +1 -1
- package/dist/compiler/server/create-server-file.test.js +76 -0
- package/dist/compiler/server/create-server-file.test.js.map +1 -1
- package/dist/compiler/variable-access.d.ts +2 -0
- package/dist/compiler/variable-access.js +6 -0
- package/dist/compiler/variable-access.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/services/config/config-schema.d.ts +69 -0
- package/dist/services/config/config-schema.js +78 -0
- package/dist/services/config/config-schema.js.map +1 -0
- package/dist/services/config/defaults.d.ts +11 -0
- package/dist/services/config/defaults.js +12 -0
- package/dist/services/config/defaults.js.map +1 -0
- package/dist/services/config/discover-config-file.d.ts +19 -0
- package/dist/services/config/discover-config-file.js +39 -0
- package/dist/services/config/discover-config-file.js.map +1 -0
- package/dist/services/config/index.d.ts +24 -0
- package/dist/services/config/index.js +25 -0
- package/dist/services/config/index.js.map +1 -0
- package/dist/services/config/load-paraglide-config.d.ts +44 -0
- package/dist/services/config/load-paraglide-config.js +109 -0
- package/dist/services/config/load-paraglide-config.js.map +1 -0
- package/dist/services/config/load-paraglide-config.test.d.ts +1 -0
- package/dist/services/config/load-paraglide-config.test.js +201 -0
- package/dist/services/config/load-paraglide-config.test.js.map +1 -0
- package/dist/services/config/resolve-compiler-options.d.ts +24 -0
- package/dist/services/config/resolve-compiler-options.js +51 -0
- package/dist/services/config/resolve-compiler-options.js.map +1 -0
- package/dist/services/config/resolve-compiler-options.test.d.ts +1 -0
- package/dist/services/config/resolve-compiler-options.test.js +60 -0
- package/dist/services/config/resolve-compiler-options.test.js.map +1 -0
- package/dist/services/env-variables/index.js +1 -1
- package/dist/services/env-variables/index.js.map +1 -1
- package/dist/services/logger/index.js +2 -0
- package/dist/services/logger/index.js.map +1 -1
- package/package.json +4 -2
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { test, expect, beforeEach, afterEach, vi } from "vitest";
|
|
2
2
|
import consola from "consola";
|
|
3
3
|
import { memfs } from "memfs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import realFs from "node:fs";
|
|
4
6
|
import { loadProjectInMemory, newProject, saveProjectToDirectory, } from "@inlang/sdk";
|
|
5
7
|
let originalNodeEnv;
|
|
8
|
+
let testDirectories = [];
|
|
6
9
|
beforeEach(() => {
|
|
7
10
|
originalNodeEnv = process.env.NODE_ENV;
|
|
8
11
|
// Reset module state between tests so the module-scoped
|
|
@@ -11,72 +14,102 @@ beforeEach(() => {
|
|
|
11
14
|
// Mock logging methods to suppress error messages in tests
|
|
12
15
|
consola.mockTypes(() => vi.fn());
|
|
13
16
|
});
|
|
14
|
-
afterEach(() => {
|
|
17
|
+
afterEach(async () => {
|
|
15
18
|
if (originalNodeEnv !== undefined) {
|
|
16
19
|
process.env.NODE_ENV = originalNodeEnv;
|
|
17
20
|
}
|
|
18
21
|
else {
|
|
19
22
|
delete process.env.NODE_ENV;
|
|
20
23
|
}
|
|
24
|
+
vi.restoreAllMocks();
|
|
25
|
+
for (const directory of testDirectories.splice(0)) {
|
|
26
|
+
await realFs.promises.rm(directory, { recursive: true, force: true });
|
|
27
|
+
}
|
|
21
28
|
});
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Creates a real on-disk fixture: `<workspace>/project.inlang` with a
|
|
31
|
+
* saved inlang project. The paraglide config lives inside the project
|
|
32
|
+
* directory and is loaded through jiti, so it must exist on the real
|
|
33
|
+
* filesystem.
|
|
34
|
+
*/
|
|
35
|
+
async function createProjectFixture(options = {}) {
|
|
36
|
+
const { mkdtemp, mkdir, writeFile } = await import("node:fs/promises");
|
|
37
|
+
const { tmpdir } = await import("node:os");
|
|
38
|
+
const workspace = await mkdtemp(path.join(tmpdir(), "paraglide-plugin-"));
|
|
39
|
+
testDirectories.push(workspace);
|
|
40
|
+
const projectDir = path.join(workspace, "project.inlang");
|
|
41
|
+
await mkdir(projectDir, { recursive: true });
|
|
27
42
|
const project = await loadProjectInMemory({
|
|
28
43
|
blob: await newProject({
|
|
29
44
|
settings: {
|
|
30
45
|
baseLocale: "en",
|
|
31
|
-
locales: ["en", "de"
|
|
46
|
+
locales: options.locales ?? ["en", "de"],
|
|
32
47
|
},
|
|
33
48
|
}),
|
|
34
49
|
});
|
|
35
|
-
const fs = memfs().fs;
|
|
36
50
|
await saveProjectToDirectory({
|
|
37
51
|
project,
|
|
38
|
-
path:
|
|
39
|
-
fs:
|
|
52
|
+
path: projectDir,
|
|
53
|
+
fs: realFs.promises,
|
|
40
54
|
});
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
55
|
+
const outdir = path.join(workspace, options.outdirName ?? "test-output");
|
|
56
|
+
if (options.config !== false) {
|
|
57
|
+
const content = options.config?.content ??
|
|
58
|
+
["export default {", ` outdir: ${JSON.stringify(outdir)},`, "}"].join("\n");
|
|
59
|
+
await writeFile(path.join(projectDir, options.config?.fileName ?? "paraglide.config.js"), content);
|
|
60
|
+
}
|
|
61
|
+
return { workspace, projectDir, outdir };
|
|
62
|
+
}
|
|
63
|
+
/** Fires Vite's configResolved so the plugin learns its root. */
|
|
64
|
+
function setViteRoot(plugin, root) {
|
|
65
|
+
const configResolved = plugin.vite?.configResolved ??
|
|
66
|
+
plugin.configResolved;
|
|
67
|
+
configResolved?.handler?.({ root });
|
|
68
|
+
}
|
|
69
|
+
const mockContext = { addWatchFile: () => { } };
|
|
70
|
+
test("watch mode follows config precedence flips", async () => {
|
|
71
|
+
process.env.NODE_ENV = "development";
|
|
72
|
+
const { writeFile, rm } = await import("node:fs/promises");
|
|
73
|
+
const { workspace, projectDir, outdir } = await createProjectFixture({
|
|
74
|
+
config: { fileName: "paraglide.config.ts" },
|
|
45
75
|
});
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
};
|
|
49
|
-
|
|
76
|
+
const jsOutdir = path.join(workspace, "js-output");
|
|
77
|
+
const { paraglideVitePlugin: vitePlugin } = await import("../bundler-plugins/vite.js");
|
|
78
|
+
const plugin = vitePlugin({ project: projectDir });
|
|
79
|
+
setViteRoot(plugin, workspace);
|
|
80
|
+
await plugin.buildStart?.call(mockContext);
|
|
81
|
+
expect(await realFs.promises.readdir(outdir)).not.toHaveLength(0);
|
|
82
|
+
// Delete the winning .ts config and create a .js one.
|
|
83
|
+
await rm(path.join(projectDir, "paraglide.config.ts"));
|
|
84
|
+
await writeFile(path.join(projectDir, "paraglide.config.js"), ["export default {", ` outdir: ${JSON.stringify(jsOutdir)},`, "}"].join("\n"));
|
|
85
|
+
await expect(plugin.watchChange?.call(mockContext, path.join(projectDir, "paraglide.config.js"))).resolves.toBeUndefined();
|
|
86
|
+
expect(await realFs.promises.readdir(jsOutdir)).not.toHaveLength(0);
|
|
50
87
|
});
|
|
51
88
|
test("vite plugin does not throw on compilation errors in development", async () => {
|
|
52
89
|
const { paraglideVitePlugin } = await import("../bundler-plugins/vite.js");
|
|
53
90
|
process.env.NODE_ENV = "development";
|
|
54
|
-
|
|
55
|
-
const
|
|
91
|
+
const { mkdtemp } = await import("node:fs/promises");
|
|
92
|
+
const { tmpdir } = await import("node:os");
|
|
93
|
+
const workspace = await mkdtemp(path.join(tmpdir(), "paraglide-plugin-"));
|
|
94
|
+
testDirectories.push(workspace);
|
|
56
95
|
const plugin = paraglideVitePlugin({
|
|
57
|
-
project: "
|
|
58
|
-
outdir: "
|
|
59
|
-
fs: fs,
|
|
96
|
+
project: path.join(workspace, "does-not-exist.inlang"),
|
|
97
|
+
outdir: path.join(workspace, "out"),
|
|
60
98
|
});
|
|
61
|
-
const mockContext = {
|
|
62
|
-
addWatchFile: () => { },
|
|
63
|
-
};
|
|
64
99
|
// In development mode - should catch errors and NOT throw
|
|
65
100
|
await expect(plugin.buildStart?.call(mockContext)).resolves.toBeUndefined();
|
|
66
101
|
});
|
|
67
102
|
test("vite plugin throws on compilation errors at build time", async () => {
|
|
68
103
|
const { paraglideVitePlugin } = await import("../bundler-plugins/vite.js");
|
|
69
104
|
process.env.NODE_ENV = "production";
|
|
70
|
-
|
|
71
|
-
const
|
|
105
|
+
const { mkdtemp } = await import("node:fs/promises");
|
|
106
|
+
const { tmpdir } = await import("node:os");
|
|
107
|
+
const workspace = await mkdtemp(path.join(tmpdir(), "paraglide-plugin-"));
|
|
108
|
+
testDirectories.push(workspace);
|
|
72
109
|
const plugin = paraglideVitePlugin({
|
|
73
|
-
project: "
|
|
74
|
-
outdir: "
|
|
75
|
-
fs: fs,
|
|
110
|
+
project: path.join(workspace, "does-not-exist.inlang"),
|
|
111
|
+
outdir: path.join(workspace, "out"),
|
|
76
112
|
});
|
|
77
|
-
const mockContext = {
|
|
78
|
-
addWatchFile: () => { },
|
|
79
|
-
};
|
|
80
113
|
// In production mode - should throw the error
|
|
81
114
|
await expect(plugin.buildStart?.call(mockContext)).rejects.toThrow();
|
|
82
115
|
});
|
|
@@ -86,78 +119,42 @@ test("vite plugin throws on compilation errors at build time", async () => {
|
|
|
86
119
|
// config-watcher reload) and producing ENOENT/MISSING_EXPORT/ERR_LOAD_URL.
|
|
87
120
|
test("vite plugin does not wipe outdir during first buildStart (#659)", async () => {
|
|
88
121
|
const { paraglideVitePlugin: vitePlugin } = await import("../bundler-plugins/vite.js");
|
|
89
|
-
const
|
|
90
|
-
blob: await newProject({
|
|
91
|
-
settings: {
|
|
92
|
-
baseLocale: "en",
|
|
93
|
-
locales: ["en", "de", "fr"],
|
|
94
|
-
},
|
|
95
|
-
}),
|
|
96
|
-
});
|
|
97
|
-
const fs = memfs().fs;
|
|
98
|
-
await saveProjectToDirectory({
|
|
99
|
-
project,
|
|
100
|
-
path: "/project.inlang",
|
|
101
|
-
fs: fs.promises,
|
|
102
|
-
});
|
|
122
|
+
const { projectDir, outdir } = await createProjectFixture();
|
|
103
123
|
// Pre-seed outdir with a sentinel file mimicking a prior compilation
|
|
104
124
|
// still on disk from an earlier process.
|
|
105
|
-
await
|
|
106
|
-
await
|
|
107
|
-
const rmSpy = vi.spyOn(
|
|
125
|
+
await realFs.promises.mkdir(outdir, { recursive: true });
|
|
126
|
+
await realFs.promises.writeFile(path.join(outdir, "__sentinel__.txt"), "kept");
|
|
127
|
+
const rmSpy = vi.spyOn(realFs.promises, "rm");
|
|
108
128
|
const plugin = vitePlugin({
|
|
109
|
-
project:
|
|
110
|
-
outdir
|
|
129
|
+
project: projectDir,
|
|
130
|
+
outdir,
|
|
111
131
|
cleanOutdir: true,
|
|
112
|
-
fs: fs,
|
|
113
132
|
});
|
|
114
|
-
const mockContext = { addWatchFile: () => { } };
|
|
115
133
|
await plugin.buildStart?.call(mockContext);
|
|
116
134
|
// The plugin must not recursively remove the outdir during the first
|
|
117
135
|
// compile, even though previousCompilation is undefined. Concurrent
|
|
118
136
|
// readers may be holding file handles into outdir.
|
|
119
137
|
for (const call of rmSpy.mock.calls) {
|
|
120
|
-
expect(call[0]).not.toBe(
|
|
138
|
+
expect(String(call[0])).not.toBe(outdir);
|
|
121
139
|
}
|
|
122
140
|
});
|
|
123
141
|
test("vite plugin warm-restart writes nothing when inputs unchanged (#659)", async () => {
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
settings: {
|
|
127
|
-
baseLocale: "en",
|
|
128
|
-
locales: ["en", "de", "fr"],
|
|
129
|
-
},
|
|
130
|
-
}),
|
|
131
|
-
});
|
|
132
|
-
const fs = memfs().fs;
|
|
133
|
-
await saveProjectToDirectory({
|
|
134
|
-
project,
|
|
135
|
-
path: "/project.inlang",
|
|
136
|
-
fs: fs.promises,
|
|
137
|
-
});
|
|
138
|
-
const mockContext = { addWatchFile: () => { } };
|
|
142
|
+
const { projectDir, outdir } = await createProjectFixture();
|
|
143
|
+
const mockCtx = { addWatchFile: () => { } };
|
|
139
144
|
// First process: compile and persist files to outdir.
|
|
140
145
|
{
|
|
141
146
|
const { paraglideVitePlugin: vitePlugin } = await import("../bundler-plugins/vite.js");
|
|
142
|
-
const plugin = vitePlugin({
|
|
143
|
-
|
|
144
|
-
outdir: "/test-output",
|
|
145
|
-
fs: fs,
|
|
146
|
-
});
|
|
147
|
-
await plugin.buildStart?.call(mockContext);
|
|
147
|
+
const plugin = vitePlugin({ project: projectDir, outdir });
|
|
148
|
+
await plugin.buildStart?.call(mockCtx);
|
|
148
149
|
}
|
|
149
150
|
// Second process: simulate a fresh node process by resetting modules so
|
|
150
151
|
// the module-scoped `previousCompilation` is undefined again. The plugin
|
|
151
152
|
// should seed from on-disk hashes and write zero files (no race window).
|
|
152
153
|
vi.resetModules();
|
|
153
154
|
const { paraglideVitePlugin: vitePluginFresh } = await import("../bundler-plugins/vite.js");
|
|
154
|
-
const plugin2 = vitePluginFresh({
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
fs: fs,
|
|
158
|
-
});
|
|
159
|
-
const writeFileSpy = vi.spyOn(fs.promises, "writeFile");
|
|
160
|
-
await plugin2.buildStart?.call(mockContext);
|
|
155
|
+
const plugin2 = vitePluginFresh({ project: projectDir, outdir });
|
|
156
|
+
const writeFileSpy = vi.spyOn(realFs.promises, "writeFile");
|
|
157
|
+
await plugin2.buildStart?.call(mockCtx);
|
|
161
158
|
expect(writeFileSpy).not.toHaveBeenCalled();
|
|
162
159
|
});
|
|
163
160
|
// Regression test for https://github.com/opral/paraglide-js/issues/741:
|
|
@@ -171,28 +168,21 @@ test("vite plugin skips compile on an unchanged fresh process (#741)", async ()
|
|
|
171
168
|
compile: compileSpy,
|
|
172
169
|
}));
|
|
173
170
|
try {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}),
|
|
171
|
+
let emitReadme = true;
|
|
172
|
+
const optionsGetter = () => ({
|
|
173
|
+
emitReadme,
|
|
178
174
|
});
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
project,
|
|
182
|
-
path: "/project.inlang",
|
|
183
|
-
fs: fs.promises,
|
|
175
|
+
const { projectDir, outdir } = await createProjectFixture({
|
|
176
|
+
locales: ["en", "de"],
|
|
184
177
|
});
|
|
185
|
-
const options = {
|
|
186
|
-
project: "/project.inlang",
|
|
187
|
-
outdir: "/test-output",
|
|
188
|
-
emitReadme: true,
|
|
189
|
-
fs,
|
|
190
|
-
};
|
|
191
|
-
const mockContext = { addWatchFile: () => { } };
|
|
192
178
|
const startFreshProcess = async () => {
|
|
193
179
|
vi.resetModules();
|
|
194
180
|
const { paraglideVitePlugin } = await import("../bundler-plugins/vite.js");
|
|
195
|
-
const plugin = paraglideVitePlugin(
|
|
181
|
+
const plugin = paraglideVitePlugin({
|
|
182
|
+
project: projectDir,
|
|
183
|
+
outdir,
|
|
184
|
+
...optionsGetter(),
|
|
185
|
+
});
|
|
196
186
|
await plugin.buildStart?.call(mockContext);
|
|
197
187
|
};
|
|
198
188
|
await startFreshProcess();
|
|
@@ -201,34 +191,34 @@ test("vite plugin skips compile on an unchanged fresh process (#741)", async ()
|
|
|
201
191
|
await startFreshProcess();
|
|
202
192
|
expect(compileSpy).toHaveBeenCalledTimes(1);
|
|
203
193
|
// A file added beside a tracked project input invalidates the cache.
|
|
204
|
-
await
|
|
194
|
+
await realFs.promises.writeFile(path.join(projectDir, "new-project-input.txt"), "new");
|
|
205
195
|
await startFreshProcess();
|
|
206
196
|
expect(compileSpy).toHaveBeenCalledTimes(2);
|
|
207
197
|
// Compiler options are part of the cache key.
|
|
208
|
-
|
|
198
|
+
emitReadme = false;
|
|
209
199
|
await startFreshProcess();
|
|
210
200
|
expect(compileSpy).toHaveBeenCalledTimes(3);
|
|
211
201
|
// Generated output is validated too, so edited output self-repairs.
|
|
212
|
-
await
|
|
202
|
+
await realFs.promises.writeFile(path.join(outdir, "runtime.js"), "tampered");
|
|
213
203
|
await startFreshProcess();
|
|
214
204
|
expect(compileSpy).toHaveBeenCalledTimes(4);
|
|
215
|
-
expect(await
|
|
205
|
+
expect(await realFs.promises.readFile(path.join(outdir, "runtime.js"), "utf8")).not.toBe("tampered");
|
|
216
206
|
// A compiler version change invalidates the persisted cache.
|
|
217
|
-
const cacheDirectory = "
|
|
218
|
-
for (const cacheFile of await
|
|
219
|
-
const cachePath =
|
|
220
|
-
const cache = JSON.parse(await
|
|
207
|
+
const cacheDirectory = path.join(projectDir, "cache", "paraglide-js");
|
|
208
|
+
for (const cacheFile of await realFs.promises.readdir(cacheDirectory)) {
|
|
209
|
+
const cachePath = path.join(cacheDirectory, cacheFile);
|
|
210
|
+
const cache = JSON.parse(await realFs.promises.readFile(cachePath, "utf8"));
|
|
221
211
|
cache.compilerVersion = "0.0.0-stale";
|
|
222
|
-
await
|
|
212
|
+
await realFs.promises.writeFile(cachePath, JSON.stringify(cache));
|
|
223
213
|
}
|
|
224
214
|
await startFreshProcess();
|
|
225
215
|
expect(compileSpy).toHaveBeenCalledTimes(5);
|
|
226
216
|
// A syntactically valid but malformed cache is treated as a miss.
|
|
227
|
-
for (const cacheFile of await
|
|
228
|
-
const cachePath =
|
|
229
|
-
const cache = JSON.parse(await
|
|
217
|
+
for (const cacheFile of await realFs.promises.readdir(cacheDirectory)) {
|
|
218
|
+
const cachePath = path.join(cacheDirectory, cacheFile);
|
|
219
|
+
const cache = JSON.parse(await realFs.promises.readFile(cachePath, "utf8"));
|
|
230
220
|
cache.outputHashes = null;
|
|
231
|
-
await
|
|
221
|
+
await realFs.promises.writeFile(cachePath, JSON.stringify(cache));
|
|
232
222
|
}
|
|
233
223
|
await startFreshProcess();
|
|
234
224
|
expect(compileSpy).toHaveBeenCalledTimes(6);
|
|
@@ -239,8 +229,7 @@ test("vite plugin skips compile on an unchanged fresh process (#741)", async ()
|
|
|
239
229
|
});
|
|
240
230
|
// Regression test for https://github.com/opral/paraglide-js/issues/693:
|
|
241
231
|
// `vite build` fires buildStart once per environment (client, ssr) and each
|
|
242
|
-
// run used to do a full compile()
|
|
243
|
-
// even though the inputs hadn't changed.
|
|
232
|
+
// run used to do a full compile() even though the inputs hadn't changed.
|
|
244
233
|
test("vite plugin skips compile() when buildStart fires again with unchanged inputs (#693)", async () => {
|
|
245
234
|
const actualCompileModule = await vi.importActual("../compiler/compile.js");
|
|
246
235
|
const compileSpy = vi.fn(actualCompileModule.compile);
|
|
@@ -250,26 +239,10 @@ test("vite plugin skips compile() when buildStart fires again with unchanged inp
|
|
|
250
239
|
}));
|
|
251
240
|
try {
|
|
252
241
|
const { paraglideVitePlugin: vitePlugin } = await import("../bundler-plugins/vite.js");
|
|
253
|
-
const
|
|
254
|
-
|
|
255
|
-
settings: {
|
|
256
|
-
baseLocale: "en",
|
|
257
|
-
locales: ["en", "de", "fr"],
|
|
258
|
-
},
|
|
259
|
-
}),
|
|
260
|
-
});
|
|
261
|
-
const fs = memfs().fs;
|
|
262
|
-
await saveProjectToDirectory({
|
|
263
|
-
project,
|
|
264
|
-
path: "/project.inlang",
|
|
265
|
-
fs: fs.promises,
|
|
242
|
+
const { projectDir, outdir } = await createProjectFixture({
|
|
243
|
+
locales: ["en", "de", "fr"],
|
|
266
244
|
});
|
|
267
|
-
const plugin = vitePlugin({
|
|
268
|
-
project: "/project.inlang",
|
|
269
|
-
outdir: "/test-output",
|
|
270
|
-
fs: fs,
|
|
271
|
-
});
|
|
272
|
-
const mockContext = { addWatchFile: () => { } };
|
|
245
|
+
const plugin = vitePlugin({ project: projectDir, outdir });
|
|
273
246
|
// First environment (e.g. client) compiles.
|
|
274
247
|
await plugin.buildStart?.call(mockContext);
|
|
275
248
|
expect(compileSpy).toHaveBeenCalledTimes(1);
|
|
@@ -277,10 +250,10 @@ test("vite plugin skips compile() when buildStart fires again with unchanged inp
|
|
|
277
250
|
await plugin.buildStart?.call(mockContext);
|
|
278
251
|
expect(compileSpy).toHaveBeenCalledTimes(1);
|
|
279
252
|
// Changing an input file invalidates the digest and recompiles.
|
|
280
|
-
const settingsPath = "
|
|
281
|
-
const settings = JSON.parse(await
|
|
253
|
+
const settingsPath = path.join(projectDir, "settings.json");
|
|
254
|
+
const settings = JSON.parse(await realFs.promises.readFile(settingsPath, "utf-8"));
|
|
282
255
|
settings.locales = ["en", "de", "fr", "es"];
|
|
283
|
-
await
|
|
256
|
+
await realFs.promises.writeFile(settingsPath, JSON.stringify(settings));
|
|
284
257
|
await plugin.buildStart?.call(mockContext);
|
|
285
258
|
expect(compileSpy).toHaveBeenCalledTimes(2);
|
|
286
259
|
}
|
|
@@ -299,28 +272,13 @@ test("vite plugin recompiles when a file is added to a tracked directory (#693)"
|
|
|
299
272
|
}));
|
|
300
273
|
try {
|
|
301
274
|
const { paraglideVitePlugin: vitePlugin } = await import("../bundler-plugins/vite.js");
|
|
302
|
-
const
|
|
303
|
-
|
|
304
|
-
settings: { baseLocale: "en", locales: ["en", "de"] },
|
|
305
|
-
}),
|
|
306
|
-
});
|
|
307
|
-
const fs = memfs().fs;
|
|
308
|
-
await saveProjectToDirectory({
|
|
309
|
-
project,
|
|
310
|
-
path: "/project.inlang",
|
|
311
|
-
fs: fs.promises,
|
|
312
|
-
});
|
|
313
|
-
const plugin = vitePlugin({
|
|
314
|
-
project: "/project.inlang",
|
|
315
|
-
outdir: "/test-output",
|
|
316
|
-
fs: fs,
|
|
317
|
-
});
|
|
318
|
-
const mockContext = { addWatchFile: () => { } };
|
|
275
|
+
const { projectDir, outdir } = await createProjectFixture();
|
|
276
|
+
const plugin = vitePlugin({ project: projectDir, outdir });
|
|
319
277
|
await plugin.buildStart?.call(mockContext);
|
|
320
278
|
await plugin.buildStart?.call(mockContext);
|
|
321
279
|
expect(compileSpy).toHaveBeenCalledTimes(1);
|
|
322
280
|
// No tracked file changed, but the listing of a tracked directory did.
|
|
323
|
-
await
|
|
281
|
+
await realFs.promises.writeFile(path.join(projectDir, "added-later.txt"), "new");
|
|
324
282
|
await plugin.buildStart?.call(mockContext);
|
|
325
283
|
expect(compileSpy).toHaveBeenCalledTimes(2);
|
|
326
284
|
}
|
|
@@ -329,8 +287,7 @@ test("vite plugin recompiles when a file is added to a tracked directory (#693)"
|
|
|
329
287
|
}
|
|
330
288
|
});
|
|
331
289
|
// After a failed compile the stored digest must not be reused: restoring an
|
|
332
|
-
// input to its previous content has to trigger a real recompile
|
|
333
|
-
// failed attempt may have left no (or partial) output.
|
|
290
|
+
// input to its previous content has to trigger a real recompile.
|
|
334
291
|
test("vite plugin does not reuse a digest from before a failed compile (#693)", async () => {
|
|
335
292
|
process.env.NODE_ENV = "development";
|
|
336
293
|
const actualCompileModule = await vi.importActual("../compiler/compile.js");
|
|
@@ -341,36 +298,20 @@ test("vite plugin does not reuse a digest from before a failed compile (#693)",
|
|
|
341
298
|
}));
|
|
342
299
|
try {
|
|
343
300
|
const { paraglideVitePlugin: vitePlugin } = await import("../bundler-plugins/vite.js");
|
|
344
|
-
const
|
|
345
|
-
|
|
346
|
-
settings: { baseLocale: "en", locales: ["en", "de"] },
|
|
347
|
-
}),
|
|
348
|
-
});
|
|
349
|
-
const fs = memfs().fs;
|
|
350
|
-
await saveProjectToDirectory({
|
|
351
|
-
project,
|
|
352
|
-
path: "/project.inlang",
|
|
353
|
-
fs: fs.promises,
|
|
354
|
-
});
|
|
355
|
-
const plugin = vitePlugin({
|
|
356
|
-
project: "/project.inlang",
|
|
357
|
-
outdir: "/test-output",
|
|
358
|
-
fs: fs,
|
|
359
|
-
});
|
|
360
|
-
const mockContext = { addWatchFile: () => { } };
|
|
301
|
+
const { projectDir, outdir } = await createProjectFixture();
|
|
302
|
+
const plugin = vitePlugin({ project: projectDir, outdir });
|
|
361
303
|
await plugin.buildStart?.call(mockContext);
|
|
362
304
|
expect(compileSpy).toHaveBeenCalledTimes(1);
|
|
363
|
-
const settingsPath = "
|
|
364
|
-
const originalSettings = await
|
|
305
|
+
const settingsPath = path.join(projectDir, "settings.json");
|
|
306
|
+
const originalSettings = await realFs.promises.readFile(settingsPath, "utf-8");
|
|
365
307
|
// Break the settings file — the compile attempt fails (dev mode
|
|
366
308
|
// swallows the error) and must clear the stored digest.
|
|
367
|
-
await
|
|
309
|
+
await realFs.promises.writeFile(settingsPath, "{ not json");
|
|
368
310
|
await plugin.buildStart?.call(mockContext);
|
|
369
311
|
expect(compileSpy).toHaveBeenCalledTimes(2);
|
|
370
|
-
// Restore the exact original content.
|
|
371
|
-
//
|
|
372
|
-
|
|
373
|
-
await fs.promises.writeFile(settingsPath, originalSettings);
|
|
312
|
+
// Restore the exact original content. It must compile again instead
|
|
313
|
+
// of skipping.
|
|
314
|
+
await realFs.promises.writeFile(settingsPath, originalSettings);
|
|
374
315
|
await plugin.buildStart?.call(mockContext);
|
|
375
316
|
expect(compileSpy).toHaveBeenCalledTimes(3);
|
|
376
317
|
}
|
|
@@ -380,40 +321,254 @@ test("vite plugin does not reuse a digest from before a failed compile (#693)",
|
|
|
380
321
|
});
|
|
381
322
|
// Two plugin instances with different filesystems must not share the tracked
|
|
382
323
|
// fs wrapper or cached compilation state — each compiles into its own fs.
|
|
324
|
+
// The project and config live on the real filesystem (the config loader
|
|
325
|
+
// always uses the native fs); only compiler I/O is virtualized here.
|
|
383
326
|
test("vite plugin instances with different fs compile against their own fs (#693)", async () => {
|
|
384
327
|
const { paraglideVitePlugin: vitePlugin } = await import("../bundler-plugins/vite.js");
|
|
385
|
-
const
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
328
|
+
const { projectDir, outdir } = await createProjectFixture({
|
|
329
|
+
locales: ["en"],
|
|
330
|
+
});
|
|
331
|
+
const fsA = memfs().fs;
|
|
332
|
+
const fsB = memfs().fs;
|
|
333
|
+
// The compiler reads the project through its own fs, so seed a copy of
|
|
334
|
+
// the real project into each instance's filesystem at /project.inlang.
|
|
335
|
+
async function seedIntoFs(target) {
|
|
336
|
+
async function walk(dir, virtualDir) {
|
|
337
|
+
for (const entry of await realFs.promises.readdir(dir, {
|
|
338
|
+
withFileTypes: true,
|
|
339
|
+
})) {
|
|
340
|
+
const source = path.join(dir, entry.name);
|
|
341
|
+
const destination = path.join(virtualDir, entry.name);
|
|
342
|
+
if (entry.isDirectory()) {
|
|
343
|
+
await target.promises.mkdir(destination, { recursive: true });
|
|
344
|
+
await walk(source, destination);
|
|
345
|
+
}
|
|
346
|
+
else {
|
|
347
|
+
await target.promises.writeFile(destination, await realFs.promises.readFile(source));
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
await target.promises.mkdir("/project.inlang", { recursive: true });
|
|
352
|
+
await walk(projectDir, "/project.inlang");
|
|
353
|
+
}
|
|
354
|
+
await seedIntoFs(fsA);
|
|
355
|
+
await seedIntoFs(fsB);
|
|
402
356
|
const pluginA = vitePlugin({
|
|
403
357
|
project: "/project.inlang",
|
|
404
|
-
outdir
|
|
358
|
+
outdir,
|
|
405
359
|
fs: fsA,
|
|
406
360
|
});
|
|
407
361
|
await pluginA.buildStart?.call(mockContext);
|
|
408
362
|
const pluginB = vitePlugin({
|
|
409
363
|
project: "/project.inlang",
|
|
410
|
-
outdir
|
|
364
|
+
outdir,
|
|
411
365
|
fs: fsB,
|
|
412
366
|
});
|
|
413
367
|
await pluginB.buildStart?.call(mockContext);
|
|
414
368
|
// B's output must exist in B's filesystem — with a shared tracked fs it
|
|
415
369
|
// would have been read from and written into A's filesystem instead.
|
|
416
|
-
const outputB = await fsB.promises.readdir(
|
|
370
|
+
const outputB = await fsB.promises.readdir(outdir);
|
|
417
371
|
expect(outputB.length).toBeGreaterThan(0);
|
|
418
372
|
});
|
|
373
|
+
// The plugins must pick up options from a paraglide.config file inside the
|
|
374
|
+
// project directory when they are not passed explicitly, and explicit
|
|
375
|
+
// options must win over the file.
|
|
376
|
+
test("vite plugin reads options from a paraglide config file", async () => {
|
|
377
|
+
const { writeFile } = await import("node:fs/promises");
|
|
378
|
+
const { workspace, projectDir, outdir } = await createProjectFixture({
|
|
379
|
+
outdirName: "from-config",
|
|
380
|
+
config: false,
|
|
381
|
+
});
|
|
382
|
+
const flagOutdir = path.join(workspace, "from-flag");
|
|
383
|
+
// No config yet: the plugin falls back to the default outdir.
|
|
384
|
+
const { paraglideVitePlugin: vitePlugin } = await import("../bundler-plugins/vite.js");
|
|
385
|
+
const pluginWithoutConfig = vitePlugin({
|
|
386
|
+
project: projectDir,
|
|
387
|
+
outdir,
|
|
388
|
+
});
|
|
389
|
+
setViteRoot(pluginWithoutConfig, workspace);
|
|
390
|
+
await pluginWithoutConfig.buildStart?.call(mockContext);
|
|
391
|
+
expect(await realFs.promises.readdir(outdir)).not.toHaveLength(0);
|
|
392
|
+
// A config inside the project directory wins over the defaults...
|
|
393
|
+
await writeFile(path.join(projectDir, "paraglide.config.js"), [
|
|
394
|
+
"export default {",
|
|
395
|
+
` outdir: ${JSON.stringify(path.join(workspace, "from-config-file"))},`,
|
|
396
|
+
"}",
|
|
397
|
+
].join("\n"));
|
|
398
|
+
const pluginWithConfig = vitePlugin({ project: projectDir });
|
|
399
|
+
setViteRoot(pluginWithConfig, workspace);
|
|
400
|
+
await pluginWithConfig.buildStart?.call(mockContext);
|
|
401
|
+
expect(await realFs.promises.readdir(path.join(workspace, "from-config-file"))).not.toHaveLength(0);
|
|
402
|
+
// ...and an explicit option wins over the config file.
|
|
403
|
+
const pluginWithOverride = vitePlugin({
|
|
404
|
+
project: projectDir,
|
|
405
|
+
outdir: flagOutdir,
|
|
406
|
+
});
|
|
407
|
+
setViteRoot(pluginWithOverride, workspace);
|
|
408
|
+
await pluginWithOverride.buildStart?.call(mockContext);
|
|
409
|
+
expect(await realFs.promises.readdir(flagOutdir)).not.toHaveLength(0);
|
|
410
|
+
});
|
|
411
|
+
// A relative `project` option is resolved against the tool root.
|
|
412
|
+
test("resolves a relative project option against the vite root", async () => {
|
|
413
|
+
const { workspace, projectDir, outdir } = await createProjectFixture();
|
|
414
|
+
const { paraglideVitePlugin: vitePlugin } = await import("../bundler-plugins/vite.js");
|
|
415
|
+
const plugin = vitePlugin({
|
|
416
|
+
project: "./project.inlang", // relative — resolved against the root
|
|
417
|
+
outdir,
|
|
418
|
+
});
|
|
419
|
+
setViteRoot(plugin, workspace);
|
|
420
|
+
expect(projectDir).toBe(path.join(workspace, "project.inlang"));
|
|
421
|
+
await plugin.buildStart?.call(mockContext);
|
|
422
|
+
expect(await realFs.promises.readdir(outdir)).not.toHaveLength(0);
|
|
423
|
+
});
|
|
424
|
+
test("watch mode hot-reloads when the config file changes", async () => {
|
|
425
|
+
const { writeFile } = await import("node:fs/promises");
|
|
426
|
+
const { workspace, projectDir, outdir } = await createProjectFixture({
|
|
427
|
+
outdirName: "cold-output",
|
|
428
|
+
});
|
|
429
|
+
const hotOutput = path.join(workspace, "hot-output");
|
|
430
|
+
const { paraglideVitePlugin: vitePlugin } = await import("../bundler-plugins/vite.js");
|
|
431
|
+
const plugin = vitePlugin({ project: projectDir });
|
|
432
|
+
setViteRoot(plugin, workspace);
|
|
433
|
+
const capturingContext = {
|
|
434
|
+
addWatchFile: (p) => {
|
|
435
|
+
addedWatchFiles.push(p);
|
|
436
|
+
},
|
|
437
|
+
};
|
|
438
|
+
const addedWatchFiles = [];
|
|
439
|
+
await plugin.buildStart?.call(capturingContext);
|
|
440
|
+
expect(await realFs.promises.readdir(outdir)).not.toHaveLength(0);
|
|
441
|
+
// Changes to unrelated files do not recompile.
|
|
442
|
+
const outputBefore = await realFs.promises.readdir(outdir);
|
|
443
|
+
await plugin.watchChange?.call(undefined, "/unrelated.ts");
|
|
444
|
+
expect(await realFs.promises.readdir(outdir)).toEqual(outputBefore);
|
|
445
|
+
// A config change recompiles with the new options.
|
|
446
|
+
await writeFile(path.join(projectDir, "paraglide.config.js"), ["export default {", ` outdir: ${JSON.stringify(hotOutput)},`, "}"].join("\n"));
|
|
447
|
+
await plugin.watchChange?.call(undefined, path.join(projectDir, "paraglide.config.js"));
|
|
448
|
+
expect(await realFs.promises.readdir(hotOutput)).not.toHaveLength(0);
|
|
449
|
+
});
|
|
450
|
+
// An invalid edited config must not crash the dev server — the previously
|
|
451
|
+
// compiled output keeps being served.
|
|
452
|
+
test("watchChange keeps the previous output when the edited config is invalid", async () => {
|
|
453
|
+
const { writeFile } = await import("node:fs/promises");
|
|
454
|
+
const { projectDir, outdir } = await createProjectFixture();
|
|
455
|
+
const { paraglideVitePlugin: vitePlugin } = await import("../bundler-plugins/vite.js");
|
|
456
|
+
const plugin = vitePlugin({ project: projectDir, outdir });
|
|
457
|
+
setViteRoot(plugin, path.dirname(projectDir));
|
|
458
|
+
await plugin.buildStart?.call(mockContext);
|
|
459
|
+
expect(await realFs.promises.readdir(outdir)).not.toHaveLength(0);
|
|
460
|
+
// Break the config, then trigger a change on it.
|
|
461
|
+
await writeFile(path.join(projectDir, "paraglide.config.js"), "export default { outdir: 42 }");
|
|
462
|
+
await expect(plugin.watchChange?.call(undefined, path.join(projectDir, "paraglide.config.js"))).resolves.toBeUndefined();
|
|
463
|
+
// The previous compilation is still in place.
|
|
464
|
+
expect(await realFs.promises.readdir(outdir)).not.toHaveLength(0);
|
|
465
|
+
});
|
|
466
|
+
// An invalid config at startup follows the same policy as compilation
|
|
467
|
+
// errors: swallowed in development, thrown in production.
|
|
468
|
+
test("buildStart does not throw when the config file is invalid in development", async () => {
|
|
469
|
+
process.env.NODE_ENV = "development";
|
|
470
|
+
const { writeFile } = await import("node:fs/promises");
|
|
471
|
+
const { projectDir } = await createProjectFixture({ config: false });
|
|
472
|
+
await writeFile(path.join(projectDir, "paraglide.config.ts"), 'export default { outdir: "./o", cookieMaxAge: "forever" }');
|
|
473
|
+
const { paraglideVitePlugin: vitePlugin } = await import("../bundler-plugins/vite.js");
|
|
474
|
+
const plugin = vitePlugin({ project: projectDir });
|
|
475
|
+
setViteRoot(plugin, path.dirname(projectDir));
|
|
476
|
+
await expect(plugin.buildStart?.call(mockContext)).resolves.toBeUndefined();
|
|
477
|
+
});
|
|
478
|
+
// Regression: with no valid cached config (dev server started on an
|
|
479
|
+
// invalid config), an unrelated file change must not reject watchChange —
|
|
480
|
+
// that would crash the bundler's watcher.
|
|
481
|
+
test("watchChange does not throw for unrelated files while the config is invalid", async () => {
|
|
482
|
+
process.env.NODE_ENV = "development";
|
|
483
|
+
const { writeFile } = await import("node:fs/promises");
|
|
484
|
+
const { projectDir } = await createProjectFixture({ config: false });
|
|
485
|
+
await writeFile(path.join(projectDir, "paraglide.config.ts"), 'export default { outdir: "./o", cookieMaxAge: "forever" }');
|
|
486
|
+
const { paraglideVitePlugin: vitePlugin } = await import("../bundler-plugins/vite.js");
|
|
487
|
+
const plugin = vitePlugin({ project: projectDir });
|
|
488
|
+
setViteRoot(plugin, path.dirname(projectDir));
|
|
489
|
+
// Swallowed by buildStart; nothing valid ends up in the cache.
|
|
490
|
+
await plugin.buildStart?.call(mockContext);
|
|
491
|
+
await expect(plugin.watchChange?.call(undefined, "/some/unrelated.ts")).resolves.toBeUndefined();
|
|
492
|
+
});
|
|
493
|
+
// Regression (review): webpack fires one watchChange per modified file and
|
|
494
|
+
// a config event must never be starved by later input events.
|
|
495
|
+
test("queued config events are applied even when input events follow", async () => {
|
|
496
|
+
const { writeFile } = await import("node:fs/promises");
|
|
497
|
+
const { workspace, projectDir } = await createProjectFixture({
|
|
498
|
+
outdirName: "cold-output",
|
|
499
|
+
});
|
|
500
|
+
const hotOutput = path.join(workspace, "hot-output");
|
|
501
|
+
const { paraglideVitePlugin: vitePlugin } = await import("../bundler-plugins/vite.js");
|
|
502
|
+
const plugin = vitePlugin({ project: projectDir });
|
|
503
|
+
setViteRoot(plugin, workspace);
|
|
504
|
+
await plugin.buildStart?.call(mockContext);
|
|
505
|
+
// Volley: input, config, input — fired while the first rebuild is still
|
|
506
|
+
// in flight (no awaiting between calls).
|
|
507
|
+
const first = plugin.watchChange?.call(undefined, path.join(projectDir, "messages", "en.json"));
|
|
508
|
+
await writeFile(path.join(projectDir, "paraglide.config.js"), ["export default {", ` outdir: ${JSON.stringify(hotOutput)},`, "}"].join("\n"));
|
|
509
|
+
const second = plugin.watchChange?.call(undefined, path.join(projectDir, "paraglide.config.js"));
|
|
510
|
+
const third = plugin.watchChange?.call(undefined, path.join(projectDir, "messages", "de.json"));
|
|
511
|
+
await Promise.all([first, second, third]);
|
|
512
|
+
expect(await realFs.promises.readdir(hotOutput)).not.toHaveLength(0);
|
|
513
|
+
});
|
|
514
|
+
// Regression (review): an invalid config in production must fail the build
|
|
515
|
+
// loudly instead of silently compiling with default options.
|
|
516
|
+
test("buildStart throws when the config is invalid in production", async () => {
|
|
517
|
+
process.env.NODE_ENV = "production";
|
|
518
|
+
const { writeFile } = await import("node:fs/promises");
|
|
519
|
+
const { projectDir } = await createProjectFixture();
|
|
520
|
+
await writeFile(path.join(projectDir, "paraglide.config.js"), "export default { outdir: 42 }");
|
|
521
|
+
const { paraglideVitePlugin: vitePlugin } = await import("../bundler-plugins/vite.js");
|
|
522
|
+
const plugin = vitePlugin({ project: projectDir });
|
|
523
|
+
setViteRoot(plugin, path.dirname(projectDir));
|
|
524
|
+
await expect(plugin.buildStart?.call(mockContext)).rejects.toThrow(/paraglide config/i);
|
|
525
|
+
});
|
|
526
|
+
// Regression (review): pinning outputStructure in the shared defaults
|
|
527
|
+
// template killed the development default (#486). Without an explicit
|
|
528
|
+
// structure, plugins must emit locale-modules outside production.
|
|
529
|
+
test("development default output structure is locale-modules", async () => {
|
|
530
|
+
const { writeFile } = await import("node:fs/promises");
|
|
531
|
+
const { projectDir, outdir } = await createProjectFixture();
|
|
532
|
+
// Ensure at least one message exists so the two structures differ.
|
|
533
|
+
await realFs.promises.mkdir(path.join(projectDir, "messages"), {
|
|
534
|
+
recursive: true,
|
|
535
|
+
});
|
|
536
|
+
await writeFile(path.join(projectDir, "messages", "en.json"), JSON.stringify({ hello: "Hello" }));
|
|
537
|
+
await writeFile(path.join(projectDir, "messages", "de.json"), JSON.stringify({ hello: "Hallo" }));
|
|
538
|
+
const { paraglideVitePlugin: vitePlugin } = await import("../bundler-plugins/vite.js");
|
|
539
|
+
const plugin = vitePlugin({ project: projectDir, outdir });
|
|
540
|
+
setViteRoot(plugin, path.dirname(projectDir));
|
|
541
|
+
delete process.env.NODE_ENV;
|
|
542
|
+
await plugin.buildStart?.call(mockContext);
|
|
543
|
+
const messages = await realFs.promises.readdir(path.join(outdir, "messages"));
|
|
544
|
+
expect(messages).toContain("en.js");
|
|
545
|
+
expect(messages).toContain("de.js");
|
|
546
|
+
expect(messages).not.toContain("hello.js");
|
|
547
|
+
});
|
|
548
|
+
// All four candidate names are registered inside the project directory so
|
|
549
|
+
// renames/format switches are reported even by backends whose watchers only
|
|
550
|
+
// report registered files (webpack).
|
|
551
|
+
test("registers all config candidates inside the project directory", async () => {
|
|
552
|
+
process.env.NODE_ENV = "development";
|
|
553
|
+
const { workspace, projectDir } = await createProjectFixture({
|
|
554
|
+
config: false,
|
|
555
|
+
});
|
|
556
|
+
const { paraglideVitePlugin: vitePlugin } = await import("../bundler-plugins/vite.js");
|
|
557
|
+
const plugin = vitePlugin({ project: projectDir });
|
|
558
|
+
setViteRoot(plugin, workspace);
|
|
559
|
+
const addedWatchFiles = [];
|
|
560
|
+
await plugin.buildStart?.call({
|
|
561
|
+
addWatchFile: (p) => {
|
|
562
|
+
addedWatchFiles.push(p);
|
|
563
|
+
},
|
|
564
|
+
});
|
|
565
|
+
for (const name of [
|
|
566
|
+
"paraglide.config.js",
|
|
567
|
+
"paraglide.config.mjs",
|
|
568
|
+
"paraglide.config.ts",
|
|
569
|
+
"paraglide.config.cjs",
|
|
570
|
+
]) {
|
|
571
|
+
expect(addedWatchFiles).toContain(path.join(projectDir, name));
|
|
572
|
+
}
|
|
573
|
+
});
|
|
419
574
|
//# sourceMappingURL=unplugin.test.js.map
|