@inlang/paraglide-js 2.0.0-beta.20 → 2.0.0-beta.21

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 (53) hide show
  1. package/dist/bundler-plugins/rollup.d.ts +1 -1
  2. package/dist/bundler-plugins/rollup.d.ts.map +1 -1
  3. package/dist/bundler-plugins/unplugin.d.ts.map +1 -1
  4. package/dist/bundler-plugins/unplugin.js +10 -17
  5. package/dist/compiler/compile-bundle.d.ts.map +1 -1
  6. package/dist/compiler/compile-bundle.js +9 -21
  7. package/dist/compiler/compile-bundle.test.js +62 -39
  8. package/dist/compiler/compile-message.d.ts.map +1 -1
  9. package/dist/compiler/compile-message.js +5 -2
  10. package/dist/compiler/compile-message.test.js +23 -0
  11. package/dist/compiler/compile-project.test.js +26 -0
  12. package/dist/compiler/compile.d.ts +6 -3
  13. package/dist/compiler/compile.d.ts.map +1 -1
  14. package/dist/compiler/compile.js +6 -2
  15. package/dist/compiler/compile.test.js +6 -2
  16. package/dist/compiler/compiler-options.d.ts +1 -1
  17. package/dist/compiler/compiler-options.js +1 -1
  18. package/dist/compiler/output-structure/locale-modules.d.ts.map +1 -1
  19. package/dist/compiler/output-structure/locale-modules.js +6 -5
  20. package/dist/compiler/output-structure/message-modules.d.ts.map +1 -1
  21. package/dist/compiler/output-structure/message-modules.js +37 -24
  22. package/dist/compiler/output-structure/message-modules.test.js +48 -0
  23. package/dist/compiler/runtime/create-runtime.d.ts.map +1 -1
  24. package/dist/compiler/runtime/create-runtime.js +6 -3
  25. package/dist/compiler/runtime/extract-locale-from-url.d.ts +2 -2
  26. package/dist/compiler/runtime/extract-locale-from-url.d.ts.map +1 -1
  27. package/dist/compiler/runtime/extract-locale-from-url.js +24 -2
  28. package/dist/compiler/runtime/extract-locale-from-url.test.js +12 -0
  29. package/dist/compiler/runtime/localize-href.d.ts +63 -27
  30. package/dist/compiler/runtime/localize-href.d.ts.map +1 -1
  31. package/dist/compiler/runtime/localize-href.js +64 -38
  32. package/dist/compiler/runtime/localize-url.d.ts +80 -8
  33. package/dist/compiler/runtime/localize-url.d.ts.map +1 -1
  34. package/dist/compiler/runtime/localize-url.js +146 -16
  35. package/dist/compiler/runtime/localize-url.test.js +84 -0
  36. package/dist/compiler/runtime/server-middleware.d.ts +1 -3
  37. package/dist/compiler/runtime/server-middleware.d.ts.map +1 -1
  38. package/dist/compiler/runtime/server-middleware.js +1 -3
  39. package/dist/compiler/runtime/variables.d.ts +1 -0
  40. package/dist/compiler/runtime/variables.d.ts.map +1 -1
  41. package/dist/compiler/runtime/variables.js +1 -0
  42. package/dist/compiler/safe-module-id.d.ts +8 -0
  43. package/dist/compiler/safe-module-id.d.ts.map +1 -0
  44. package/dist/compiler/safe-module-id.js +71 -0
  45. package/dist/compiler/safe-module-id.test.d.ts +2 -0
  46. package/dist/compiler/safe-module-id.test.d.ts.map +1 -0
  47. package/dist/compiler/safe-module-id.test.js +27 -0
  48. package/dist/services/env-variables/index.js +1 -1
  49. package/dist/services/file-handling/write-output.d.ts +1 -1
  50. package/dist/services/file-handling/write-output.d.ts.map +1 -1
  51. package/dist/services/file-handling/write-output.js +1 -1
  52. package/dist/services/file-handling/write-output.test.js +2 -1
  53. package/package.json +3 -3
@@ -1,2 +1,2 @@
1
- export declare const paraglideRollupPlugin: (options: import("../index.js").CompilerOptions) => import("unplugin").RollupPlugin<any> | import("unplugin").RollupPlugin<any>[];
1
+ export declare const paraglideRollupPlugin: (options: import("../index.js").CompilerOptions) => import("rollup").Plugin<any> | import("rollup").Plugin<any>[];
2
2
  //# sourceMappingURL=rollup.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"rollup.d.ts","sourceRoot":"","sources":["../../src/bundler-plugins/rollup.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,qBAAqB,mIAAsC,CAAC"}
1
+ {"version":3,"file":"rollup.d.ts","sourceRoot":"","sources":["../../src/bundler-plugins/rollup.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,qBAAqB,mHAAsC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"unplugin.d.ts","sourceRoot":"","sources":["../../src/bundler-plugins/unplugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAMhD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAcvE,eAAO,MAAM,eAAe,EAAE,eAAe,CAAC,eAAe,CAoF3D,CAAC"}
1
+ {"version":3,"file":"unplugin.d.ts","sourceRoot":"","sources":["../../src/bundler-plugins/unplugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAMhD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAQvE,eAAO,MAAM,eAAe,EAAE,eAAe,CAAC,eAAe,CAiF3D,CAAC"}
@@ -5,23 +5,16 @@ import { nodeNormalizePath } from "../utilities/node-normalize-path.js";
5
5
  import { Logger } from "../services/logger/index.js";
6
6
  const PLUGIN_NAME = "unplugin-paraglide-js";
7
7
  const logger = new Logger();
8
- let compilationResult;
9
- // https://github.com/opral/inlang-paraglide-js/issues/371
10
- //
11
- // has the second benefit that paraglide js only compiles once
12
- // per enviornment build (browser, server, etc.)
13
- let hasInitiallyCompiled = false;
8
+ let previousCompilation;
14
9
  export const unpluginFactory = (args) => ({
15
10
  name: PLUGIN_NAME,
16
11
  enforce: "pre",
17
12
  async buildStart() {
18
- if (hasInitiallyCompiled === true) {
19
- return;
20
- }
21
13
  logger.info("Compiling inlang project...");
22
14
  try {
23
- compilationResult = await compile({
15
+ previousCompilation = await compile({
24
16
  fs: wrappedFs,
17
+ previousCompilation,
25
18
  ...args,
26
19
  });
27
20
  logger.success("Compilation complete");
@@ -35,7 +28,6 @@ export const unpluginFactory = (args) => ({
35
28
  for (const path of Array.from(readFiles)) {
36
29
  this.addWatchFile(path);
37
30
  }
38
- hasInitiallyCompiled = true;
39
31
  }
40
32
  },
41
33
  async watchChange(path) {
@@ -48,10 +40,11 @@ export const unpluginFactory = (args) => ({
48
40
  logger.info(`Re-compiling inlang project... File "${relative(process.cwd(), path)}" has changed.`);
49
41
  // Clear readFiles to track fresh file reads
50
42
  readFiles.clear();
51
- compilationResult = await compile({
43
+ previousCompilation = await compile({
52
44
  fs: wrappedFs,
45
+ previousCompilation,
53
46
  ...args,
54
- }, compilationResult?.outputHashes);
47
+ });
55
48
  logger.success("Compilation complete");
56
49
  // Add any new files to watch
57
50
  for (const filePath of Array.from(readFiles)) {
@@ -61,7 +54,7 @@ export const unpluginFactory = (args) => ({
61
54
  catch (e) {
62
55
  readFiles = previouslyReadFiles;
63
56
  // Reset compilation result on error
64
- compilationResult = undefined;
57
+ previousCompilation = undefined;
65
58
  logger.warn("Failed to re-compile project:", e.message);
66
59
  }
67
60
  },
@@ -74,14 +67,14 @@ export const unpluginFactory = (args) => ({
74
67
  async_hooks: false,
75
68
  },
76
69
  };
77
- //we need the compiler to run before the build so that the message-modules will be present
78
- //In the other bundlers `buildStart` already runs before the build. In webpack it's a race condition
79
70
  compiler.hooks.beforeRun.tapPromise(PLUGIN_NAME, async () => {
80
71
  try {
81
- await compile({
72
+ previousCompilation = await compile({
82
73
  fs: wrappedFs,
74
+ previousCompilation,
83
75
  ...args,
84
76
  });
77
+ logger.success("Compilation complete");
85
78
  }
86
79
  catch (error) {
87
80
  logger.warn("Failed to compile project:", error.message);
@@ -1 +1 @@
1
- {"version":3,"file":"compile-bundle.d.ts","sourceRoot":"","sources":["../../src/compiler/compile-bundle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAKjE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAI3C,MAAM,MAAM,0BAA0B,GAAG;IACxC,kDAAkD;IAClD,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IACzB,gDAAgD;IAChD,QAAQ,EAAE;QACT,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;KACpC,CAAC;CACF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,SAAU;IACnC,MAAM,EAAE,YAAY,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;CAChD,KAAG,0BAmCH,CAAC"}
1
+ {"version":3,"file":"compile-bundle.d.ts","sourceRoot":"","sources":["../../src/compiler/compile-bundle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEjE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAK3C,MAAM,MAAM,0BAA0B,GAAG;IACxC,kDAAkD;IAClD,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IACzB,gDAAgD;IAChD,QAAQ,EAAE;QACT,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;KACpC,CAAC;CACF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,SAAU;IACnC,MAAM,EAAE,YAAY,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;CAChD,KAAG,0BAyBH,CAAC"}
@@ -1,21 +1,12 @@
1
1
  import { compileMessage } from "./compile-message.js";
2
- import { jsIdentifier } from "../services/codegen/identifier.js";
3
- import { isValidJSIdentifier } from "../services/valid-js-identifier/index.js";
4
- import { escapeForDoubleQuoteString } from "../services/codegen/escape.js";
5
2
  import { jsDocBundleFunctionTypes } from "./jsdoc-types.js";
6
- import { KEYWORDS } from "../services/valid-js-identifier/reserved-words.js";
3
+ import { toSafeModuleId } from "./safe-module-id.js";
4
+ import { escapeForDoubleQuoteString } from "../services/codegen/escape.js";
7
5
  /**
8
6
  * Compiles all the messages in the bundle and returns an index-function + each compiled message
9
7
  */
10
8
  export const compileBundle = (args) => {
11
9
  const compiledMessages = {};
12
- if (KEYWORDS.includes(args.bundle.id) || args.bundle.id === "then") {
13
- throw new Error([
14
- `You are using a reserved JS keyword as id "${args.bundle.id}".`,
15
- "Rename the message bundle id to something else.",
16
- "See https://github.com/opral/inlang-paraglide-js/issues/331",
17
- ].join("\n"));
18
- }
19
10
  for (const message of args.bundle.messages) {
20
11
  if (compiledMessages[message.locale]) {
21
12
  throw new Error(`Duplicate locale: ${message.locale}`);
@@ -35,6 +26,8 @@ export const compileBundle = (args) => {
35
26
  const compileBundleFunction = (args) => {
36
27
  const inputs = args.bundle.declarations.filter((decl) => decl.type === "input-variable");
37
28
  const hasInputs = inputs.length > 0;
29
+ const safeBundleId = toSafeModuleId(args.bundle.id);
30
+ const isSafeBundleId = safeBundleId === args.bundle.id;
38
31
  let code = `/**
39
32
  * This function has been compiled by [Paraglide JS](https://inlang.com/m/gerre34r).
40
33
  *
@@ -45,20 +38,15 @@ const compileBundleFunction = (args) => {
45
38
  * ${jsDocBundleFunctionTypes({ inputs, locales: args.availableLocales })}
46
39
  */
47
40
  /* @__NO_SIDE_EFFECTS__ */
48
- const ${jsIdentifier(args.bundle.id)} = (inputs${hasInputs ? "" : "= {}"}, options = {}) => {
41
+ ${isSafeBundleId ? "export" : ""} const ${safeBundleId} = (inputs${hasInputs ? "" : "= {}"}, options = {}) => {
49
42
  const locale = options.locale ?? getLocale()
50
43
  ${args.availableLocales
51
- .map((locale, index) => `${index > 0 ? " " : ""}if (locale === "${locale}") return ${jsIdentifier(locale)}.${args.bundle.id}(inputs)`)
44
+ .map((locale, index) => `${index > 0 ? " " : ""}if (locale === "${locale}") return ${toSafeModuleId(locale)}.${safeBundleId}(inputs)`)
52
45
  .join("\n")}
53
46
  return "${args.bundle.id}"
54
- }
55
- `;
56
- // export the function
57
- if (isValidJSIdentifier(args.bundle.id)) {
58
- code += `\nexport { ${args.bundle.id} }`;
59
- }
60
- else {
61
- code += `\nexport { ${jsIdentifier(args.bundle.id)} as "${escapeForDoubleQuoteString(args.bundle.id)}" }`;
47
+ };`;
48
+ if (isSafeBundleId === false) {
49
+ code += `\nexport { ${safeBundleId} as "${escapeForDoubleQuoteString(args.bundle.id)}" }`;
62
50
  }
63
51
  return {
64
52
  code,
@@ -1,6 +1,33 @@
1
1
  import { test, expect } from "vitest";
2
2
  import { compileBundle } from "./compile-bundle.js";
3
+ import { toSafeModuleId } from "./safe-module-id.js";
3
4
  test("compiles to jsdoc", async () => {
5
+ const mockBundle = {
6
+ id: "blue_moon_bottle",
7
+ declarations: [{ type: "input-variable", name: "age" }],
8
+ messages: [
9
+ {
10
+ id: "message-id",
11
+ bundleId: "blue_moon_bottle",
12
+ locale: "en",
13
+ selectors: [],
14
+ variants: [
15
+ {
16
+ id: "1",
17
+ messageId: "message-id",
18
+ matches: [],
19
+ pattern: [
20
+ { type: "text", value: "Hello" },
21
+ {
22
+ type: "expression",
23
+ arg: { type: "variable-reference", name: "age" },
24
+ },
25
+ ],
26
+ },
27
+ ],
28
+ },
29
+ ],
30
+ };
4
31
  const result = compileBundle({
5
32
  fallbackMap: {
6
33
  en: "en",
@@ -21,48 +48,44 @@ test("compiles to jsdoc", async () => {
21
48
  * @returns {string}
22
49
  */
23
50
  /* @__NO_SIDE_EFFECTS__ */
24
- const blue_moon_bottle = (inputs, options = {}) => {
51
+ export const blue_moon_bottle = (inputs, options = {}) => {
25
52
  const locale = options.locale ?? getLocale()
26
53
  if (locale === "en") return en.blue_moon_bottle(inputs)
27
- if (locale === "en-US") return en_US.blue_moon_bottle(inputs)
54
+ if (locale === "en-US") return en_us.blue_moon_bottle(inputs)
28
55
  return "blue_moon_bottle"
29
- }
30
-
31
- export { blue_moon_bottle }"`);
56
+ };"`);
32
57
  });
33
- const mockBundle = {
34
- id: "blue_moon_bottle",
35
- declarations: [{ type: "input-variable", name: "age" }],
36
- messages: [
37
- {
38
- id: "message-id",
39
- bundleId: "blue_moon_bottle",
40
- locale: "en",
41
- selectors: [],
42
- variants: [
43
- {
44
- id: "1",
45
- messageId: "message-id",
46
- matches: [],
47
- pattern: [
48
- { type: "text", value: "Hello" },
49
- {
50
- type: "expression",
51
- arg: { type: "variable-reference", name: "age" },
52
- },
53
- ],
54
- },
55
- ],
56
- },
57
- ],
58
- };
59
- test("throws if a JS keyword is used as an identifier", async () => {
60
- expect(() => compileBundle({
58
+ // https://github.com/opral/inlang-paraglide-js/issues/285
59
+ test("compiles bundles with arbitrary module identifiers", async () => {
60
+ const mockBundle = {
61
+ id: "$p@44🍌",
62
+ declarations: [{ type: "input-variable", name: "age" }],
63
+ messages: [
64
+ {
65
+ id: "message-id",
66
+ bundleId: "$p@44🍌",
67
+ locale: "en",
68
+ selectors: [],
69
+ variants: [
70
+ {
71
+ id: "1",
72
+ messageId: "message-id",
73
+ matches: [],
74
+ pattern: [
75
+ { type: "text", value: "Hello" },
76
+ {
77
+ type: "expression",
78
+ arg: { type: "variable-reference", name: "age" },
79
+ },
80
+ ],
81
+ },
82
+ ],
83
+ },
84
+ ],
85
+ };
86
+ const result = compileBundle({
61
87
  fallbackMap: {},
62
- bundle: {
63
- id: "then",
64
- declarations: [],
65
- messages: [],
66
- },
67
- })).toThrow();
88
+ bundle: mockBundle,
89
+ });
90
+ expect(result.bundle.code).includes(`export { ${toSafeModuleId("$p@44🍌")} as "$p@44🍌" }`);
68
91
  });
@@ -1 +1 @@
1
- {"version":3,"file":"compile-message.d.ts","sourceRoot":"","sources":["../../src/compiler/compile-message.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEjE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAK3C;;;GAGG;AACH,eAAO,MAAM,cAAc,iBACZ,WAAW,EAAE,WAClB,OAAO,YACN,OAAO,EAAE,KACjB,QAAQ,CAAC,OAAO,CAUlB,CAAC"}
1
+ {"version":3,"file":"compile-message.d.ts","sourceRoot":"","sources":["../../src/compiler/compile-message.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEjE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAM3C;;;GAGG;AACH,eAAO,MAAM,cAAc,iBACZ,WAAW,EAAE,WAClB,OAAO,YACN,OAAO,EAAE,KACjB,QAAQ,CAAC,OAAO,CAUlB,CAAC"}
@@ -2,6 +2,7 @@ import { compilePattern } from "./compile-pattern.js";
2
2
  import { doubleQuote } from "../services/codegen/quotes.js";
3
3
  import { inputsType } from "./jsdoc-types.js";
4
4
  import { compileLocalVariable } from "./compile-local-variable.js";
5
+ import { toSafeModuleId } from "./safe-module-id.js";
5
6
  /**
6
7
  * Returns the compiled message as a string
7
8
  *
@@ -33,8 +34,9 @@ function compileMessageWithOneVariant(declarations, message, variants) {
33
34
  compiledLocalVariables.push(compileLocalVariable({ declaration, locale: message.locale }));
34
35
  }
35
36
  }
37
+ const safeModuleId = toSafeModuleId(message.bundleId);
36
38
  const code = `/** @type {(inputs: ${inputsType(inputs)}) => string} */
37
- export const ${message.bundleId} = (${hasInputs ? "i" : ""}) => {
39
+ export const ${safeModuleId} = (${hasInputs ? "i" : ""}) => {
38
40
  ${compiledLocalVariables.join("\n\t")}return ${compiledPattern.code}
39
41
  };`;
40
42
  return { code, node: message };
@@ -81,8 +83,9 @@ function compileMessageWithMultipleVariants(declarations, message, variants) {
81
83
  compiledLocalVariables.push(compileLocalVariable({ declaration, locale: message.locale }));
82
84
  }
83
85
  }
86
+ const safeModuleId = toSafeModuleId(message.bundleId);
84
87
  const code = `/** @type {(inputs: ${inputsType(inputs)}) => string} */
85
- export const ${message.bundleId} = (${hasInputs ? "i" : ""}) => {
88
+ export const ${safeModuleId} = (${hasInputs ? "i" : ""}) => {
86
89
  ${compiledLocalVariables.join("\n\t")}
87
90
  ${compiledVariants.join("\n\t")}
88
91
  return \`${message.id}\`;
@@ -1,6 +1,7 @@
1
1
  import { test, expect } from "vitest";
2
2
  import { compileMessage } from "./compile-message.js";
3
3
  import { createRegistry } from "./registry.js";
4
+ import { toSafeModuleId } from "./safe-module-id.js";
4
5
  test("compiles a message with a single variant", async () => {
5
6
  const declarations = [];
6
7
  const message = {
@@ -254,3 +255,25 @@ test("compiles messages that use datetime a function with options", async () =>
254
255
  expect(enMessage({ date: "2022-03-31" })).toMatch(/Today is March \d{1,2}\./);
255
256
  expect(deMessage({ date: "2022-03-31" })).toMatch(/Today is \d{1,2}\. März\./);
256
257
  });
258
+ // https://github.com/opral/inlang-paraglide-js/issues/285
259
+ test("compiles messages with arbitrary module identifiers", async () => {
260
+ const declarations = [];
261
+ const message = {
262
+ locale: "en",
263
+ bundleId: "$p@44🍌",
264
+ id: "message-id",
265
+ selectors: [],
266
+ };
267
+ const variants = [
268
+ {
269
+ id: "1",
270
+ messageId: "message-id",
271
+ matches: [],
272
+ pattern: [{ type: "text", value: "Hello" }],
273
+ },
274
+ ];
275
+ const compiled = compileMessage(declarations, message, variants);
276
+ const m = await import("data:text/javascript;base64," +
277
+ Buffer.from(compiled.code).toString("base64"));
278
+ expect(m[toSafeModuleId("$p@44🍌")]()).toBe("Hello");
279
+ });
@@ -328,6 +328,32 @@ describe.each([
328
328
  runtime.setLocale("en-US");
329
329
  expect(m.missingInGerman()).toBe("A simple message.");
330
330
  });
331
+ test("arbitrary module identifiers work", async () => {
332
+ const project = await loadProjectInMemory({
333
+ blob: await newProject({
334
+ settings: { locales: ["en", "de"], baseLocale: "en" },
335
+ }),
336
+ });
337
+ await insertBundleNested(project.db, createBundleNested({
338
+ id: "$502.23-hello_world",
339
+ messages: [
340
+ {
341
+ locale: "en",
342
+ variants: [
343
+ { pattern: [{ type: "text", value: "A simple message." }] },
344
+ ],
345
+ },
346
+ ],
347
+ }));
348
+ const output = await compileProject({
349
+ project,
350
+ compilerOptions,
351
+ });
352
+ const code = await bundleCode(output, `export * as m from "./paraglide/messages.js"
353
+ export * as runtime from "./paraglide/runtime.js"`);
354
+ const { m } = await importCode(code);
355
+ expect(m["$502.23-hello_world"]()).toBe("A simple message.");
356
+ });
331
357
  test("falls back to parent locale if message doesn't exist", async () => {
332
358
  const project = await loadProjectInMemory({
333
359
  blob: await newProject({
@@ -1,4 +1,7 @@
1
1
  import { type CompilerOptions } from "./compiler-options.js";
2
+ export type CompilationResult = {
3
+ outputHashes: Record<string, string> | undefined;
4
+ };
2
5
  /**
3
6
  * Loads, compiles, and writes the output to disk.
4
7
  *
@@ -12,7 +15,7 @@ import { type CompilerOptions } from "./compiler-options.js";
12
15
  * outdir: 'path/to/output',
13
16
  * })
14
17
  */
15
- export declare function compile(options: CompilerOptions, previousOutputHashes?: Record<string, string>): Promise<{
16
- outputHashes: Record<string, string> | undefined;
17
- }>;
18
+ export declare function compile(options: CompilerOptions & {
19
+ previousCompilation?: CompilationResult;
20
+ }): Promise<CompilationResult>;
18
21
  //# sourceMappingURL=compile.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../src/compiler/compile.ts"],"names":[],"mappings":"AASA,OAAO,EAEN,KAAK,eAAe,EACpB,MAAM,uBAAuB,CAAC;AAQ/B;;;;;;;;;;;;GAYG;AACH,wBAAsB,OAAO,CAC5B,OAAO,EAAE,eAAe,EACxB,oBAAoB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC3C,OAAO,CAAC;IAAE,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAA;CAAE,CAAC,CA6D/D"}
1
+ {"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../src/compiler/compile.ts"],"names":[],"mappings":"AASA,OAAO,EAEN,KAAK,eAAe,EACpB,MAAM,uBAAuB,CAAC;AAQ/B,MAAM,MAAM,iBAAiB,GAAG;IAC/B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;CACjD,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,wBAAsB,OAAO,CAC5B,OAAO,EAAE,eAAe,GAAG;IAC1B,mBAAmB,CAAC,EAAE,iBAAiB,CAAC;CACxC,GACC,OAAO,CAAC,iBAAiB,CAAC,CAgE5B"}
@@ -21,7 +21,7 @@ let compilationInProgress = null;
21
21
  * outdir: 'path/to/output',
22
22
  * })
23
23
  */
24
- export async function compile(options, previousOutputHashes) {
24
+ export async function compile(options) {
25
25
  const withDefaultOptions = {
26
26
  ...defaultCompilerOptions,
27
27
  ...options,
@@ -48,7 +48,7 @@ export async function compile(options, previousOutputHashes) {
48
48
  directory: absoluteOutdir,
49
49
  output,
50
50
  fs: fs.promises,
51
- previousOutputHashes,
51
+ previousOutputHashes: options.previousCompilation?.outputHashes,
52
52
  });
53
53
  if (!localAccount) {
54
54
  const activeAccount = await project.lix.db
@@ -60,6 +60,10 @@ export async function compile(options, previousOutputHashes) {
60
60
  await project.close();
61
61
  return { outputHashes };
62
62
  }
63
+ catch (e) {
64
+ console.error(e);
65
+ return { outputHashes: undefined };
66
+ }
63
67
  finally {
64
68
  // release the lock
65
69
  compilationInProgress = null;
@@ -216,11 +216,15 @@ test("includes eslint-disable comment", async () => {
216
216
  const messagesWithoutComment = await fs.promises.readFile("/output/messages.js", "utf8");
217
217
  expect(messagesWithoutComment).not.toContain("// eslint-disable");
218
218
  });
219
- test("default compiler options should include variable and baseLocale to ensure easy try out of paraglide js, working both in server and browser environemnts", () => {
219
+ test("default compiler options should include cookied, variable and baseLocale to ensure easy try out of paraglide js, working both in server and browser environemnts", () => {
220
220
  // someone trying out paraglide js should be able to call `getLocale()` and `setLocale()`
221
221
  // without getting an error slammed in their face saying "define your strategy".
222
222
  //
223
223
  // instead, make the apis work out of the box and once the developer is convinced that
224
224
  // paraglide js is the right tool for them, they can then define their own strategy.
225
- expect(defaultCompilerOptions.strategy).toEqual(expect.arrayContaining(["globalVariable", "baseLocale"]));
225
+ expect(defaultCompilerOptions.strategy).toEqual([
226
+ "cookie",
227
+ "globalVariable",
228
+ "baseLocale",
229
+ ]);
226
230
  });
@@ -4,7 +4,7 @@ export declare const defaultCompilerOptions: {
4
4
  readonly emitGitIgnore: true;
5
5
  readonly includeEslintDisableComment: true;
6
6
  readonly emitPrettierIgnore: true;
7
- readonly strategy: ["url", "cookie", "globalVariable", "baseLocale"];
7
+ readonly strategy: ["cookie", "globalVariable", "baseLocale"];
8
8
  readonly cookieName: "PARAGLIDE_LOCALE";
9
9
  };
10
10
  export type CompilerOptions = {
@@ -3,6 +3,6 @@ export const defaultCompilerOptions = {
3
3
  emitGitIgnore: true,
4
4
  includeEslintDisableComment: true,
5
5
  emitPrettierIgnore: true,
6
- strategy: ["url", "cookie", "globalVariable", "baseLocale"],
6
+ strategy: ["cookie", "globalVariable", "baseLocale"],
7
7
  cookieName: "PARAGLIDE_LOCALE",
8
8
  };
@@ -1 +1 @@
1
- {"version":3,"file":"locale-modules.d.ts","sourceRoot":"","sources":["../../../src/compiler/output-structure/locale-modules.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,sBAAsB,CAAC;AAIvE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE9D,wBAAgB,qBAAqB,CACpC,eAAe,EAAE,0BAA0B,EAAE,EAC7C,QAAQ,EAAE,IAAI,CAAC,eAAe,EAAE,SAAS,GAAG,YAAY,CAAC,EACzD,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EAC/C,eAAe,EAAE;IAChB,QAAQ,EAAE,WAAW,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,CAAC;IACnD,UAAU,EAAE,WAAW,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC,CAAC;CACvD,GACC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAyDxB"}
1
+ {"version":3,"file":"locale-modules.d.ts","sourceRoot":"","sources":["../../../src/compiler/output-structure/locale-modules.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,sBAAsB,CAAC;AAGvE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAG9D,wBAAgB,qBAAqB,CACpC,eAAe,EAAE,0BAA0B,EAAE,EAC7C,QAAQ,EAAE,IAAI,CAAC,eAAe,EAAE,SAAS,GAAG,YAAY,CAAC,EACzD,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EAC/C,eAAe,EAAE;IAChB,QAAQ,EAAE,WAAW,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,CAAC;IACnD,UAAU,EAAE,WAAW,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC,CAAC;CACvD,GACC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA2DxB"}
@@ -1,11 +1,11 @@
1
- import { jsIdentifier } from "../../services/codegen/identifier.js";
2
1
  import { createRuntimeFile } from "../runtime/create-runtime.js";
3
2
  import { createRegistry } from "../registry.js";
3
+ import { toSafeModuleId } from "../safe-module-id.js";
4
4
  export function generateLocaleModules(compiledBundles, settings, fallbackMap, compilerOptions) {
5
5
  const indexFile = [
6
6
  `import { getLocale } from "../runtime.js"`,
7
7
  settings.locales
8
- .map((locale) => `import * as ${jsIdentifier(locale)} from "./${locale}.js"`)
8
+ .map((locale) => `import * as ${toSafeModuleId(locale)} from "./${locale}.js"`)
9
9
  .join("\n"),
10
10
  compiledBundles.map(({ bundle }) => bundle.code).join("\n"),
11
11
  ].join("\n");
@@ -29,16 +29,17 @@ export function generateLocaleModules(compiledBundles, settings, fallbackMap, co
29
29
  let file = "";
30
30
  for (const compiledBundle of compiledBundles) {
31
31
  const compiledMessage = compiledBundle.messages[locale];
32
- const id = jsIdentifier(compiledBundle.bundle.node.id);
32
+ const bundleModuleId = toSafeModuleId(compiledBundle.bundle.node.id);
33
+ const bundleId = compiledBundle.bundle.node.id;
33
34
  if (!compiledMessage) {
34
35
  const fallbackLocale = fallbackMap[locale];
35
36
  if (fallbackLocale) {
36
37
  // use the fall back locale e.g. render the message in English if the German message is missing
37
- file += `\nexport { ${id} } from "./${fallbackLocale}.js"`;
38
+ file += `\nexport { ${bundleModuleId} } from "./${fallbackLocale}.js"`;
38
39
  }
39
40
  else {
40
41
  // no fallback exists, render the bundleId
41
- file += `\nexport const ${id} = () => '${id}'`;
42
+ file += `\n/** @type {(inputs?: Record<string, never>) => string} */\nexport const ${bundleModuleId} = () => '${bundleId}'`;
42
43
  }
43
44
  continue;
44
45
  }
@@ -1 +1 @@
1
- {"version":3,"file":"message-modules.d.ts","sourceRoot":"","sources":["../../../src/compiler/output-structure/message-modules.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,sBAAsB,CAAC;AAKvE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE9D,wBAAgB,sBAAsB,CACrC,eAAe,EAAE,0BAA0B,EAAE,EAC7C,QAAQ,EAAE,IAAI,CAAC,eAAe,EAAE,SAAS,GAAG,YAAY,CAAC,EACzD,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EAC/C,eAAe,EAAE;IAChB,QAAQ,EAAE,WAAW,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,CAAC;IACnD,UAAU,EAAE,WAAW,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC,CAAC;CACvD,GACC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAwExB"}
1
+ {"version":3,"file":"message-modules.d.ts","sourceRoot":"","sources":["../../../src/compiler/output-structure/message-modules.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,sBAAsB,CAAC;AAIvE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAG9D,wBAAgB,sBAAsB,CACrC,eAAe,EAAE,0BAA0B,EAAE,EAC7C,QAAQ,EAAE,IAAI,CAAC,eAAe,EAAE,SAAS,GAAG,YAAY,CAAC,EACzD,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EAC/C,eAAe,EAAE;IAChB,QAAQ,EAAE,WAAW,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,CAAC;IACnD,UAAU,EAAE,WAAW,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC,CAAC;CACvD,GACC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAoFxB"}
@@ -1,7 +1,7 @@
1
1
  import { createRuntimeFile } from "../runtime/create-runtime.js";
2
2
  import { createRegistry } from "../registry.js";
3
- import { jsIdentifier } from "../../services/codegen/identifier.js";
4
3
  import { escapeForSingleQuoteString } from "../../services/codegen/escape.js";
4
+ import { toSafeModuleId } from "../safe-module-id.js";
5
5
  export function generateMessageModules(compiledBundles, settings, fallbackMap, compilerOptions) {
6
6
  const output = {
7
7
  ["runtime.js"]: createRuntimeFile({
@@ -11,33 +11,40 @@ export function generateMessageModules(compiledBundles, settings, fallbackMap, c
11
11
  }),
12
12
  ["registry.js"]: createRegistry(),
13
13
  };
14
- // messages index file
14
+ // all messages index file
15
15
  output["messages/_index.js"] = [
16
- ...compiledBundles.map(({ bundle }) => `export * from './${bundle.node.id}/index.js'`),
16
+ ...compiledBundles.map(({ bundle }) => `export * from './${toSafeModuleId(bundle.node.id)}/index.js'`),
17
17
  ].join("\n");
18
18
  output["messages.js"] = [
19
19
  "export * from './messages/_index.js'",
20
20
  "// enabling auto-import by exposing all messages as m",
21
21
  "export * as m from './messages/_index.js'",
22
22
  ].join("\n");
23
- // Creates a per message index file
24
23
  for (const compiledBundle of compiledBundles) {
25
- const filename = `messages/${compiledBundle.bundle.node.id}/index.js`;
26
- const code = [
27
- settings.locales
28
- .map((locale) => `import * as ${jsIdentifier(locale)} from "./${locale}.js"`)
29
- .join("\n"),
30
- `import { getLocale } from '../../runtime.js'`,
31
- "",
32
- compiledBundle.bundle.code,
33
- ].join("\n");
34
- output[filename] = code;
35
- }
36
- for (const locale of settings.locales) {
37
- for (const compiledBundle of compiledBundles) {
24
+ const bundleFileId = toSafeModuleId(compiledBundle.bundle.node.id);
25
+ // bundle file
26
+ const indexFilename = `messages/${bundleFileId}/index.js`;
27
+ if (output[indexFilename]) {
28
+ // bundle file already exists, need to append to it
29
+ output[indexFilename] += `\n${compiledBundle.bundle.code}`;
30
+ }
31
+ else {
32
+ // create fresh bundle file
33
+ const code = [
34
+ settings.locales
35
+ .map((locale) => `import * as ${toSafeModuleId(locale)} from "./${locale}.js"`)
36
+ .join("\n"),
37
+ `import { getLocale } from '../../runtime.js'`,
38
+ "",
39
+ compiledBundle.bundle.code,
40
+ ].join("\n");
41
+ output[indexFilename] = code;
42
+ }
43
+ // message files
44
+ for (const locale of settings.locales) {
38
45
  let file = "";
39
46
  const compiledMessage = compiledBundle.messages[locale];
40
- const id = jsIdentifier(compiledBundle.bundle.node.id);
47
+ const id = toSafeModuleId(compiledBundle.bundle.node.id);
41
48
  if (!compiledMessage) {
42
49
  // add fallback
43
50
  const fallbackLocale = fallbackMap[locale];
@@ -47,18 +54,24 @@ export function generateMessageModules(compiledBundles, settings, fallbackMap, c
47
54
  }
48
55
  else {
49
56
  // fallback to just the bundle id
50
- file += `\nexport const ${id} = () => '${escapeForSingleQuoteString(compiledBundle.bundle.node.id)}'`;
57
+ file += `\n/** @type {(inputs?: Record<string, never>) => string} */\nexport const ${id} = () => '${escapeForSingleQuoteString(compiledBundle.bundle.node.id)}'`;
51
58
  }
52
59
  }
53
60
  else {
54
61
  file += `\n${compiledMessage.code}`;
55
62
  }
56
- // Add the registry import to the message file
57
- // if registry is used
58
- if (file.includes("registry.")) {
59
- file = `import * as registry from '../../registry.js'\n` + file;
63
+ if (output[`messages/${bundleFileId}/${locale}.js`]) {
64
+ // message file already exists, need to append to it
65
+ output[`messages/${bundleFileId}/${locale}.js`] += file;
66
+ }
67
+ else {
68
+ // Add the registry import to the message file
69
+ // if registry is used
70
+ if (file.includes("registry.")) {
71
+ file = `import * as registry from '../../registry.js'\n` + file;
72
+ }
73
+ output[`messages/${bundleFileId}/${locale}.js`] = file;
60
74
  }
61
- output[`messages/${compiledBundle.bundle.node.id}/${locale}.js`] = file;
62
75
  }
63
76
  }
64
77
  return output;