@inlang/paraglide-js 2.4.0 → 2.6.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/vite.d.ts +1 -1
- package/dist/bundler-plugins/vite.d.ts.map +1 -1
- package/dist/bundler-plugins/webpack.d.ts +1 -1
- package/dist/bundler-plugins/webpack.d.ts.map +1 -1
- package/dist/cli/steps/update-ts-config.d.ts +13 -0
- package/dist/cli/steps/update-ts-config.d.ts.map +1 -1
- package/dist/cli/steps/update-ts-config.js +131 -16
- package/dist/cli/steps/update-ts-config.test.d.ts +2 -0
- package/dist/cli/steps/update-ts-config.test.d.ts.map +1 -0
- package/dist/cli/steps/update-ts-config.test.js +59 -0
- package/dist/compiler/compile-bundle.js +1 -1
- package/dist/compiler/compile-bundle.test.js +27 -25
- package/dist/compiler/compile-local-variable.js +1 -1
- package/dist/compiler/compile-local-variable.test.js +2 -2
- package/dist/compiler/compile-message.js +7 -7
- package/dist/compiler/compile-message.test.js +147 -1
- package/dist/compiler/compile-pattern.d.ts +1 -1
- package/dist/compiler/compile-pattern.js +2 -2
- package/dist/compiler/compile-pattern.test.js +1 -1
- package/dist/compiler/emit-ts-declarations.d.ts +12 -0
- package/dist/compiler/emit-ts-declarations.d.ts.map +1 -0
- package/dist/compiler/emit-ts-declarations.js +99 -0
- package/dist/compiler/index.d.ts +1 -1
- package/dist/compiler/index.d.ts.map +1 -1
- package/dist/compiler/jsdoc-types.js +1 -1
- package/dist/compiler/jsdoc-types.test.js +9 -0
- package/dist/compiler/output-structure/locale-modules.d.ts.map +1 -1
- package/dist/compiler/output-structure/locale-modules.js +8 -1
- package/dist/compiler/output-structure/message-modules.d.ts.map +1 -1
- package/dist/compiler/output-structure/message-modules.js +36 -11
- package/dist/compiler/output-structure/message-modules.test.js +42 -0
- package/dist/compiler/runtime/assert-is-locale.d.ts.map +1 -1
- package/dist/compiler/runtime/assert-is-locale.js +7 -3
- package/dist/compiler/runtime/assert-is-locale.test.js +33 -2
- package/dist/compiler/runtime/create-runtime.d.ts.map +1 -1
- package/dist/compiler/runtime/create-runtime.js +42 -0
- package/dist/compiler/runtime/is-locale.d.ts.map +1 -1
- package/dist/compiler/runtime/is-locale.js +5 -1
- package/dist/compiler/runtime/is-locale.test.d.ts +2 -0
- package/dist/compiler/runtime/is-locale.test.d.ts.map +1 -0
- package/dist/compiler/runtime/is-locale.test.js +31 -0
- package/dist/compiler/types.d.ts +18 -2
- package/dist/compiler/types.d.ts.map +1 -1
- package/dist/services/env-variables/index.js +1 -1
- package/package.json +3 -3
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const paraglideVitePlugin: (options: import("../index.js").CompilerOptions) => import("
|
|
1
|
+
export declare const paraglideVitePlugin: (options: import("../index.js").CompilerOptions) => import("unplugin").VitePlugin<any> | import("unplugin").VitePlugin<any>[];
|
|
2
2
|
//# sourceMappingURL=vite.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vite.d.ts","sourceRoot":"","sources":["../../src/bundler-plugins/vite.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,mBAAmB,+
|
|
1
|
+
{"version":3,"file":"vite.d.ts","sourceRoot":"","sources":["../../src/bundler-plugins/vite.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,mBAAmB,+HAAoC,CAAC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const paraglideWebpackPlugin: (options: import("../index.js").CompilerOptions) =>
|
|
1
|
+
export declare const paraglideWebpackPlugin: (options: import("../index.js").CompilerOptions) => WebpackPluginInstance;
|
|
2
2
|
//# sourceMappingURL=webpack.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webpack.d.ts","sourceRoot":"","sources":["../../src/bundler-plugins/webpack.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,sBAAsB,
|
|
1
|
+
{"version":3,"file":"webpack.d.ts","sourceRoot":"","sources":["../../src/bundler-plugins/webpack.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,sBAAsB,2EAAuC,CAAC"}
|
|
@@ -11,4 +11,17 @@ export declare const maybeUpdateTsConfigAllowJs: CliStep<{
|
|
|
11
11
|
fs: typeof import("node:fs/promises");
|
|
12
12
|
logger: Logger;
|
|
13
13
|
}, unknown>;
|
|
14
|
+
/**
|
|
15
|
+
* Recursively checks whether allowJs is enabled in the provided tsconfig or any
|
|
16
|
+
* referenced configuration files.
|
|
17
|
+
*
|
|
18
|
+
* @param tsconfigPath The path to the tsconfig to inspect.
|
|
19
|
+
* @param fs The file system used to read the configs.
|
|
20
|
+
* @param visited A set of already inspected files to avoid circular lookups.
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* await hasAllowJsEnabled("./tsconfig.json", fs);
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare const hasAllowJsEnabled: (tsconfigPath: string, fs: typeof import("node:fs/promises"), visited?: Set<string>) => Promise<boolean>;
|
|
14
27
|
//# sourceMappingURL=update-ts-config.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update-ts-config.d.ts","sourceRoot":"","sources":["../../../src/cli/steps/update-ts-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"update-ts-config.d.ts","sourceRoot":"","sources":["../../../src/cli/steps/update-ts-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gCAAgC,CAAC;AAS7D,eAAO,MAAM,mBAAmB,EAAE,OAAO,CACxC;IAAE,EAAE,EAAE,cAAc,kBAAkB,CAAC,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EACzD,OAAO,CAKP,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,0BAA0B,EAAE,OAAO,CAC/C;IAAE,EAAE,EAAE,cAAc,kBAAkB,CAAC,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EACzD,OAAO,CA6CP,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,iBAAiB,GAC7B,cAAc,MAAM,EACpB,IAAI,cAAc,kBAAkB,CAAC,EACrC,UAAS,GAAG,CAAC,MAAM,CAAa,KAC9B,OAAO,CAAC,OAAO,CA4CjB,CAAC"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { prompt } from "../utils.js";
|
|
2
2
|
import JSON5 from "json5";
|
|
3
3
|
import { pathExists } from "../../services/file-handling/exists.js";
|
|
4
|
-
|
|
4
|
+
import nodePath from "node:path";
|
|
5
|
+
import { createRequire } from "node:module";
|
|
6
|
+
const require = createRequire(import.meta.url);
|
|
5
7
|
export const maybeUpdateTsConfig = async (ctx) => {
|
|
6
8
|
const ctx1 = await maybeUpdateTsConfigAllowJs(ctx);
|
|
7
9
|
return ctx1;
|
|
@@ -14,10 +16,7 @@ export const maybeUpdateTsConfigAllowJs = async (ctx) => {
|
|
|
14
16
|
if ((await pathExists("./tsconfig.json", ctx.fs)) === false) {
|
|
15
17
|
return ctx;
|
|
16
18
|
}
|
|
17
|
-
|
|
18
|
-
// tsconfig allows comments ... FML
|
|
19
|
-
let tsconfig = JSON5.parse(file);
|
|
20
|
-
if (tsconfig.compilerOptions?.allowJs === true) {
|
|
19
|
+
if (await hasAllowJsEnabled("./tsconfig.json", ctx.fs)) {
|
|
21
20
|
// all clear, allowJs is already set to true
|
|
22
21
|
return ctx;
|
|
23
22
|
}
|
|
@@ -38,17 +37,7 @@ export const maybeUpdateTsConfigAllowJs = async (ctx) => {
|
|
|
38
37
|
ctx.logger.warn("Continuing without adjusting the tsconfig.json. This may lead to type errors.");
|
|
39
38
|
return ctx;
|
|
40
39
|
}
|
|
41
|
-
|
|
42
|
-
// just trust that it's correct.
|
|
43
|
-
if (tsconfig.extends) {
|
|
44
|
-
isValid = true;
|
|
45
|
-
return ctx;
|
|
46
|
-
}
|
|
47
|
-
const file = await ctx.fs.readFile("./tsconfig.json", {
|
|
48
|
-
encoding: "utf-8",
|
|
49
|
-
});
|
|
50
|
-
tsconfig = JSON5.parse(file);
|
|
51
|
-
if (tsconfig?.compilerOptions?.allowJs === true) {
|
|
40
|
+
if (await hasAllowJsEnabled("./tsconfig.json", ctx.fs)) {
|
|
52
41
|
isValid = true;
|
|
53
42
|
return ctx;
|
|
54
43
|
}
|
|
@@ -58,6 +47,132 @@ export const maybeUpdateTsConfigAllowJs = async (ctx) => {
|
|
|
58
47
|
}
|
|
59
48
|
return ctx;
|
|
60
49
|
};
|
|
50
|
+
/**
|
|
51
|
+
* Recursively checks whether allowJs is enabled in the provided tsconfig or any
|
|
52
|
+
* referenced configuration files.
|
|
53
|
+
*
|
|
54
|
+
* @param tsconfigPath The path to the tsconfig to inspect.
|
|
55
|
+
* @param fs The file system used to read the configs.
|
|
56
|
+
* @param visited A set of already inspected files to avoid circular lookups.
|
|
57
|
+
* @example
|
|
58
|
+
* ```ts
|
|
59
|
+
* await hasAllowJsEnabled("./tsconfig.json", fs);
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export const hasAllowJsEnabled = async (tsconfigPath, fs, visited = new Set()) => {
|
|
63
|
+
const normalizedPath = normalizeConfigPath(tsconfigPath);
|
|
64
|
+
if (visited.has(normalizedPath)) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
visited.add(normalizedPath);
|
|
68
|
+
const file = await fs.readFile(normalizedPath, { encoding: "utf-8" });
|
|
69
|
+
const tsconfig = JSON5.parse(file);
|
|
70
|
+
if (tsconfig?.compilerOptions?.allowJs === true) {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
const baseDir = nodePath.dirname(normalizedPath);
|
|
74
|
+
const extendCandidates = Array.isArray(tsconfig?.extends)
|
|
75
|
+
? tsconfig.extends
|
|
76
|
+
: tsconfig?.extends
|
|
77
|
+
? [tsconfig.extends]
|
|
78
|
+
: [];
|
|
79
|
+
for (const candidate of extendCandidates) {
|
|
80
|
+
if (typeof candidate !== "string")
|
|
81
|
+
continue;
|
|
82
|
+
const resolved = await resolveExtendedConfig(candidate, baseDir, fs);
|
|
83
|
+
if (resolved && (await hasAllowJsEnabled(resolved, fs, visited))) {
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (Array.isArray(tsconfig?.references)) {
|
|
88
|
+
for (const reference of tsconfig.references) {
|
|
89
|
+
const referencePath = reference?.path;
|
|
90
|
+
if (typeof referencePath !== "string")
|
|
91
|
+
continue;
|
|
92
|
+
const resolved = await resolveReferenceConfig(referencePath, baseDir, fs);
|
|
93
|
+
if (resolved && (await hasAllowJsEnabled(resolved, fs, visited))) {
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return false;
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* Normalizes a tsconfig path to an absolute path.
|
|
102
|
+
*/
|
|
103
|
+
const normalizeConfigPath = (configPath) => {
|
|
104
|
+
return nodePath.isAbsolute(configPath)
|
|
105
|
+
? configPath
|
|
106
|
+
: nodePath.resolve(process.cwd(), configPath);
|
|
107
|
+
};
|
|
108
|
+
/**
|
|
109
|
+
* Resolves the extended tsconfig path relative to the base config.
|
|
110
|
+
*/
|
|
111
|
+
const resolveExtendedConfig = async (extendsSpecifier, baseDir, fs) => {
|
|
112
|
+
const candidates = new Set();
|
|
113
|
+
const resolvedBase = nodePath.isAbsolute(extendsSpecifier)
|
|
114
|
+
? extendsSpecifier
|
|
115
|
+
: nodePath.resolve(baseDir, extendsSpecifier);
|
|
116
|
+
candidates.add(resolvedBase);
|
|
117
|
+
if (nodePath.extname(resolvedBase) === "") {
|
|
118
|
+
candidates.add(`${resolvedBase}.json`);
|
|
119
|
+
candidates.add(nodePath.join(resolvedBase, "tsconfig.json"));
|
|
120
|
+
}
|
|
121
|
+
for (const candidate of candidates) {
|
|
122
|
+
if (await pathExists(candidate, fs)) {
|
|
123
|
+
return candidate;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
try {
|
|
127
|
+
return require.resolve(extendsSpecifier, { paths: [baseDir] });
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
if (extendsSpecifier.endsWith(".json") === false) {
|
|
131
|
+
try {
|
|
132
|
+
return require.resolve(`${extendsSpecifier}.json`, {
|
|
133
|
+
paths: [baseDir],
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
return undefined;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return undefined;
|
|
142
|
+
};
|
|
143
|
+
/**
|
|
144
|
+
* Resolves the tsconfig referenced through the `references` property.
|
|
145
|
+
*/
|
|
146
|
+
const resolveReferenceConfig = async (referenceSpecifier, baseDir, fs) => {
|
|
147
|
+
const candidates = new Set();
|
|
148
|
+
const resolvedBase = nodePath.isAbsolute(referenceSpecifier)
|
|
149
|
+
? referenceSpecifier
|
|
150
|
+
: nodePath.resolve(baseDir, referenceSpecifier);
|
|
151
|
+
candidates.add(resolvedBase);
|
|
152
|
+
if (nodePath.extname(resolvedBase) === "") {
|
|
153
|
+
candidates.add(`${resolvedBase}.json`);
|
|
154
|
+
candidates.add(nodePath.join(resolvedBase, "tsconfig.json"));
|
|
155
|
+
}
|
|
156
|
+
for (const candidate of candidates) {
|
|
157
|
+
if (await pathExists(candidate, fs)) {
|
|
158
|
+
try {
|
|
159
|
+
const stats = await fs.stat(candidate);
|
|
160
|
+
if (stats.isDirectory()) {
|
|
161
|
+
const directoryConfig = nodePath.join(candidate, "tsconfig.json");
|
|
162
|
+
if (await pathExists(directoryConfig, fs)) {
|
|
163
|
+
return directoryConfig;
|
|
164
|
+
}
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
// ignore, we'll continue checking other candidates
|
|
170
|
+
}
|
|
171
|
+
return candidate;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return undefined;
|
|
175
|
+
};
|
|
61
176
|
// /**
|
|
62
177
|
// * Ensures that the moduleResolution compiler option is set to "bundler" or similar in the tsconfig.json.
|
|
63
178
|
// *
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-ts-config.test.d.ts","sourceRoot":"","sources":["../../../src/cli/steps/update-ts-config.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { expect, test, vi } from "vitest";
|
|
2
|
+
import { memfs } from "memfs";
|
|
3
|
+
import { maybeUpdateTsConfigAllowJs } from "./update-ts-config.js";
|
|
4
|
+
import { Logger } from "../../services/logger/index.js";
|
|
5
|
+
const setCwd = (cwd) => {
|
|
6
|
+
const original = process.cwd;
|
|
7
|
+
process.cwd = (() => cwd);
|
|
8
|
+
return () => {
|
|
9
|
+
process.cwd = original;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
// Regression coverage for https://github.com/opral/inlang-paraglide-js/issues/560
|
|
13
|
+
test("skips prompting when allowJs is set in a referenced tsconfig", async () => {
|
|
14
|
+
const fs = memfs({
|
|
15
|
+
"/tsconfig.json": JSON.stringify({
|
|
16
|
+
references: [{ path: "./tsconfig.app.json" }],
|
|
17
|
+
}),
|
|
18
|
+
"/tsconfig.app.json": JSON.stringify({
|
|
19
|
+
compilerOptions: { allowJs: true },
|
|
20
|
+
}),
|
|
21
|
+
}).fs;
|
|
22
|
+
const restoreCwd = setCwd("/");
|
|
23
|
+
const logger = new Logger({ silent: true, prefix: false });
|
|
24
|
+
const infoSpy = vi.spyOn(logger, "info");
|
|
25
|
+
try {
|
|
26
|
+
await maybeUpdateTsConfigAllowJs({
|
|
27
|
+
fs: fs.promises,
|
|
28
|
+
logger,
|
|
29
|
+
});
|
|
30
|
+
expect(infoSpy).not.toHaveBeenCalled();
|
|
31
|
+
}
|
|
32
|
+
finally {
|
|
33
|
+
restoreCwd();
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
// Regression coverage for https://github.com/opral/inlang-paraglide-js/issues/560
|
|
37
|
+
test("skips prompting when allowJs is provided via extends", async () => {
|
|
38
|
+
const fs = memfs({
|
|
39
|
+
"/tsconfig.json": JSON.stringify({
|
|
40
|
+
extends: "./tsconfig.base.json",
|
|
41
|
+
}),
|
|
42
|
+
"/tsconfig.base.json": JSON.stringify({
|
|
43
|
+
compilerOptions: { allowJs: true },
|
|
44
|
+
}),
|
|
45
|
+
}).fs;
|
|
46
|
+
const restoreCwd = setCwd("/");
|
|
47
|
+
const logger = new Logger({ silent: true, prefix: false });
|
|
48
|
+
const infoSpy = vi.spyOn(logger, "info");
|
|
49
|
+
try {
|
|
50
|
+
await maybeUpdateTsConfigAllowJs({
|
|
51
|
+
fs: fs.promises,
|
|
52
|
+
logger,
|
|
53
|
+
});
|
|
54
|
+
expect(infoSpy).not.toHaveBeenCalled();
|
|
55
|
+
}
|
|
56
|
+
finally {
|
|
57
|
+
restoreCwd();
|
|
58
|
+
}
|
|
59
|
+
});
|
|
@@ -49,7 +49,7 @@ ${isSafeBundleId ? "export " : ""}const ${safeBundleId} = (inputs${hasInputs ? "
|
|
|
49
49
|
trackMessageCall("${safeBundleId}", locale)
|
|
50
50
|
${args.availableLocales
|
|
51
51
|
.map((locale, index) => `${index > 0 ? " " : ""}${!isFullyTranslated || index < args.availableLocales.length - 1 ? `if (locale === "${locale}") ` : ""}return ${args.messageReferenceExpression(locale, args.bundle.id)}(inputs)`)
|
|
52
|
-
.join("\n")}${!isFullyTranslated ? `\n return "${args.bundle.id}"` : ""}
|
|
52
|
+
.join("\n")}${!isFullyTranslated ? `\n return /** @type {LocalizedString} */ ("${args.bundle.id}")` : ""}
|
|
53
53
|
};`;
|
|
54
54
|
if (isSafeBundleId === false) {
|
|
55
55
|
code += `\nexport { ${safeBundleId} as "${escapeForDoubleQuoteString(args.bundle.id)}" }`;
|
|
@@ -49,7 +49,7 @@ test("compiles to jsdoc", async () => {
|
|
|
49
49
|
*
|
|
50
50
|
* @param {{ age: NonNullable<unknown> }} inputs
|
|
51
51
|
* @param {{ locale?: "en" | "en-US" }} options
|
|
52
|
-
* @returns {
|
|
52
|
+
* @returns {LocalizedString}
|
|
53
53
|
*/
|
|
54
54
|
/* @__NO_SIDE_EFFECTS__ */
|
|
55
55
|
export const blue_moon_bottle = (inputs, options = {}) => {
|
|
@@ -100,29 +100,31 @@ test("compiles to jsdoc with missing translation", async () => {
|
|
|
100
100
|
locales: ["en", "en-US", "fr"],
|
|
101
101
|
},
|
|
102
102
|
});
|
|
103
|
-
expect(result.bundle.code).toMatchInlineSnapshot(`
|
|
104
|
-
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
* @param {{
|
|
113
|
-
* @
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}
|
|
103
|
+
expect(result.bundle.code).toMatchInlineSnapshot(`
|
|
104
|
+
"/**
|
|
105
|
+
* This function has been compiled by [Paraglide JS](https://inlang.com/m/gerre34r).
|
|
106
|
+
*
|
|
107
|
+
* - Changing this function will be over-written by the next build.
|
|
108
|
+
*
|
|
109
|
+
* - If you want to change the translations, you can either edit the source files e.g. \`en.json\`, or
|
|
110
|
+
* use another inlang app like [Fink](https://inlang.com/m/tdozzpar) or the [VSCode extension Sherlock](https://inlang.com/m/r7kp499g).
|
|
111
|
+
*
|
|
112
|
+
* @param {{ age: NonNullable<unknown> }} inputs
|
|
113
|
+
* @param {{ locale?: "en" | "en-US" }} options
|
|
114
|
+
* @returns {LocalizedString}
|
|
115
|
+
*/
|
|
116
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
117
|
+
export const blue_moon_bottle = (inputs, options = {}) => {
|
|
118
|
+
if (experimentalMiddlewareLocaleSplitting && isServer === false) {
|
|
119
|
+
return /** @type {any} */ (globalThis).__paraglide_ssr.blue_moon_bottle(inputs)
|
|
120
|
+
}
|
|
121
|
+
const locale = options.locale ?? getLocale()
|
|
122
|
+
trackMessageCall("blue_moon_bottle", locale)
|
|
123
|
+
if (locale === "en") return en.blue_moon_bottle(inputs)
|
|
124
|
+
if (locale === "en-US") return en_us2.blue_moon_bottle(inputs)
|
|
125
|
+
return /** @type {LocalizedString} */ ("blue_moon_bottle")
|
|
126
|
+
};"
|
|
127
|
+
`);
|
|
126
128
|
});
|
|
127
129
|
// https://github.com/opral/inlang-paraglide-js/issues/285
|
|
128
130
|
test("compiles bundles with arbitrary module identifiers", async () => {
|
|
@@ -208,5 +210,5 @@ test("handles message pattern with duplicate variable references", async () => {
|
|
|
208
210
|
// Check that the pattern is compiled correctly
|
|
209
211
|
const enMessage = result.messages?.en;
|
|
210
212
|
expect(enMessage).toBeDefined();
|
|
211
|
-
expect(enMessage?.code).toContain("Last ${i
|
|
213
|
+
expect(enMessage?.code).toContain("Last ${i?.days} days, showing ${i?.days} items");
|
|
212
214
|
});
|
|
@@ -23,7 +23,7 @@ test("compiles a variable reference local variable", () => {
|
|
|
23
23
|
},
|
|
24
24
|
},
|
|
25
25
|
});
|
|
26
|
-
expect(code).toEqual("const myVar = i
|
|
26
|
+
expect(code).toEqual("const myVar = i?.name;");
|
|
27
27
|
});
|
|
28
28
|
test("compiles a local variable with an annotation and empty options", () => {
|
|
29
29
|
const code = compileLocalVariable({
|
|
@@ -67,5 +67,5 @@ test("compiles a local variable with an annotation and options", () => {
|
|
|
67
67
|
},
|
|
68
68
|
},
|
|
69
69
|
});
|
|
70
|
-
expect(code).toEqual('const myVar = registry.myFunction("en", "Hello", { option1: "value1", option2: i
|
|
70
|
+
expect(code).toEqual('const myVar = registry.myFunction("en", "Hello", { option1: "value1", option2: i?.varRef });');
|
|
71
71
|
});
|
|
@@ -33,8 +33,8 @@ function compileMessageWithOneVariant(declarations, message, variants) {
|
|
|
33
33
|
compiledLocalVariables.push(compileLocalVariable({ declaration, locale: message.locale }));
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
|
-
const code = `/** @type {(inputs: ${inputsType(inputs)}) =>
|
|
37
|
-
${compiledLocalVariables.join("\n\t")}return ${compiledPattern.code}
|
|
36
|
+
const code = `/** @type {(inputs: ${inputsType(inputs)}) => LocalizedString} */ (${hasInputs ? "i" : ""}) => {
|
|
37
|
+
${compiledLocalVariables.join("\n\t")}return /** @type {LocalizedString} */ (${compiledPattern.code})
|
|
38
38
|
};`;
|
|
39
39
|
return { code, node: message };
|
|
40
40
|
}
|
|
@@ -54,7 +54,7 @@ function compileMessageWithMultipleVariants(declarations, message, variants) {
|
|
|
54
54
|
});
|
|
55
55
|
const isCatchAll = variant.matches.every((match) => match.type === "catchall-match");
|
|
56
56
|
if (isCatchAll) {
|
|
57
|
-
compiledVariants.push(`return ${compiledPattern.code}`);
|
|
57
|
+
compiledVariants.push(`return /** @type {LocalizedString} */ (${compiledPattern.code})`);
|
|
58
58
|
hasCatchAll = true;
|
|
59
59
|
}
|
|
60
60
|
const conditions = [];
|
|
@@ -65,7 +65,7 @@ function compileMessageWithMultipleVariants(declarations, message, variants) {
|
|
|
65
65
|
}
|
|
66
66
|
const variableType = declarations.find((decl) => decl.name === match.key)?.type;
|
|
67
67
|
if (variableType === "input-variable") {
|
|
68
|
-
conditions.push(`i
|
|
68
|
+
conditions.push(`i?.${match.key} == ${doubleQuote(match.value)}`);
|
|
69
69
|
}
|
|
70
70
|
else if (variableType === "local-variable") {
|
|
71
71
|
conditions.push(`${match.key} == ${doubleQuote(match.value)}`);
|
|
@@ -73,7 +73,7 @@ function compileMessageWithMultipleVariants(declarations, message, variants) {
|
|
|
73
73
|
}
|
|
74
74
|
if (conditions.length === 0)
|
|
75
75
|
continue;
|
|
76
|
-
compiledVariants.push(`if (${conditions.join(" && ")}) return ${compiledPattern.code};`);
|
|
76
|
+
compiledVariants.push(`if (${conditions.join(" && ")}) return /** @type {LocalizedString} */ (${compiledPattern.code});`);
|
|
77
77
|
}
|
|
78
78
|
const compiledLocalVariables = [];
|
|
79
79
|
for (const declaration of declarations) {
|
|
@@ -81,9 +81,9 @@ function compileMessageWithMultipleVariants(declarations, message, variants) {
|
|
|
81
81
|
compiledLocalVariables.push(compileLocalVariable({ declaration, locale: message.locale }));
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
|
-
const code = `/** @type {(inputs: ${inputsType(inputs)}) =>
|
|
84
|
+
const code = `/** @type {(inputs: ${inputsType(inputs)}) => LocalizedString} */ (${hasInputs ? "i" : ""}) => {${compiledLocalVariables.join("\n\t")}
|
|
85
85
|
${compiledVariants.join("\n\t")}
|
|
86
|
-
${hasCatchAll ? "" : `return "${message.bundleId}";`}
|
|
86
|
+
${hasCatchAll ? "" : `return /** @type {LocalizedString} */ ("${message.bundleId}");`}
|
|
87
87
|
};`;
|
|
88
88
|
return { code, node: message };
|
|
89
89
|
}
|
|
@@ -142,7 +142,7 @@ test("only emits input arguments when inputs exist", async () => {
|
|
|
142
142
|
},
|
|
143
143
|
];
|
|
144
144
|
const compiled = compileMessage(declarations, message, variants);
|
|
145
|
-
expect(compiled.code).toBe("/** @type {(inputs: {}) =>
|
|
145
|
+
expect(compiled.code).toBe("/** @type {(inputs: {}) => LocalizedString} */ () => {\n\treturn /** @type {LocalizedString} */ (`Hello`)\n};");
|
|
146
146
|
});
|
|
147
147
|
// https://github.com/opral/inlang-paraglide-js/issues/379
|
|
148
148
|
test("compiles messages that use plural()", async () => {
|
|
@@ -198,6 +198,90 @@ test("compiles messages that use plural()", async () => {
|
|
|
198
198
|
// INTL.plural will match "other" for undefined
|
|
199
199
|
expect(plural_test({ count: undefined })).toBe("There are many cats.");
|
|
200
200
|
});
|
|
201
|
+
test("compiles messages that use plural() with ordinal type", async () => {
|
|
202
|
+
const declarations = [
|
|
203
|
+
{ type: "input-variable", name: "count" },
|
|
204
|
+
{
|
|
205
|
+
type: "local-variable",
|
|
206
|
+
name: "countOrdinal",
|
|
207
|
+
value: {
|
|
208
|
+
arg: { type: "variable-reference", name: "count" },
|
|
209
|
+
annotation: {
|
|
210
|
+
type: "function-reference",
|
|
211
|
+
name: "plural",
|
|
212
|
+
options: [
|
|
213
|
+
{ name: "type", value: { type: "literal", value: "ordinal" } },
|
|
214
|
+
],
|
|
215
|
+
},
|
|
216
|
+
type: "expression",
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
];
|
|
220
|
+
const message = {
|
|
221
|
+
locale: "en",
|
|
222
|
+
bundleId: "ordinal_test",
|
|
223
|
+
id: "message_id",
|
|
224
|
+
selectors: [{ type: "variable-reference", name: "countOrdinal" }],
|
|
225
|
+
};
|
|
226
|
+
const variants = [
|
|
227
|
+
{
|
|
228
|
+
id: "1",
|
|
229
|
+
messageId: "message_id",
|
|
230
|
+
matches: [{ type: "literal-match", value: "one", key: "countOrdinal" }],
|
|
231
|
+
pattern: [
|
|
232
|
+
{
|
|
233
|
+
type: "expression",
|
|
234
|
+
arg: { type: "variable-reference", name: "count" },
|
|
235
|
+
},
|
|
236
|
+
{ type: "text", value: "st place" },
|
|
237
|
+
],
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
id: "2",
|
|
241
|
+
messageId: "message_id",
|
|
242
|
+
matches: [{ type: "literal-match", value: "two", key: "countOrdinal" }],
|
|
243
|
+
pattern: [
|
|
244
|
+
{
|
|
245
|
+
type: "expression",
|
|
246
|
+
arg: { type: "variable-reference", name: "count" },
|
|
247
|
+
},
|
|
248
|
+
{ type: "text", value: "nd place" },
|
|
249
|
+
],
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
id: "3",
|
|
253
|
+
messageId: "message_id",
|
|
254
|
+
matches: [{ type: "literal-match", value: "few", key: "countOrdinal" }],
|
|
255
|
+
pattern: [
|
|
256
|
+
{
|
|
257
|
+
type: "expression",
|
|
258
|
+
arg: { type: "variable-reference", name: "count" },
|
|
259
|
+
},
|
|
260
|
+
{ type: "text", value: "rd place" },
|
|
261
|
+
],
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
id: "4",
|
|
265
|
+
messageId: "message_id",
|
|
266
|
+
matches: [{ type: "literal-match", value: "other", key: "countOrdinal" }],
|
|
267
|
+
pattern: [
|
|
268
|
+
{
|
|
269
|
+
type: "expression",
|
|
270
|
+
arg: { type: "variable-reference", name: "count" },
|
|
271
|
+
},
|
|
272
|
+
{ type: "text", value: "th place" },
|
|
273
|
+
],
|
|
274
|
+
},
|
|
275
|
+
];
|
|
276
|
+
const compiled = compileMessage(declarations, message, variants);
|
|
277
|
+
const { ordinal_test } = await import("data:text/javascript;base64," +
|
|
278
|
+
btoa(createRegistry()) +
|
|
279
|
+
btoa("export const ordinal_test = " + compiled.code.replace("registry.", "")));
|
|
280
|
+
expect(ordinal_test({ count: 1 })).toBe("1st place");
|
|
281
|
+
expect(ordinal_test({ count: 2 })).toBe("2nd place");
|
|
282
|
+
expect(ordinal_test({ count: 3 })).toBe("3rd place");
|
|
283
|
+
expect(ordinal_test({ count: 4 })).toBe("4th place");
|
|
284
|
+
});
|
|
201
285
|
test("compiles messages that use datetime()", async () => {
|
|
202
286
|
const createMessage = async (locale) => {
|
|
203
287
|
const declarations = [
|
|
@@ -306,3 +390,65 @@ test("compiles messages that use datetime a function with options", async () =>
|
|
|
306
390
|
expect(enMessage({ date: "2022-03-31" })).toMatch(/Today is March \d{1,2}\./);
|
|
307
391
|
expect(deMessage({ date: "2022-03-31" })).toMatch(/Today is \d{1,2}\. März\./);
|
|
308
392
|
});
|
|
393
|
+
test("does not throw when input is omitted for a single-variant message", async () => {
|
|
394
|
+
const declarations = [
|
|
395
|
+
{ type: "input-variable", name: "name" },
|
|
396
|
+
];
|
|
397
|
+
const message = {
|
|
398
|
+
locale: "en",
|
|
399
|
+
bundleId: "greeting",
|
|
400
|
+
id: "greeting",
|
|
401
|
+
selectors: [{ type: "variable-reference", name: "name" }],
|
|
402
|
+
};
|
|
403
|
+
const variants = [
|
|
404
|
+
{
|
|
405
|
+
id: "1",
|
|
406
|
+
messageId: "greeting",
|
|
407
|
+
matches: [{ type: "catchall-match", key: "name" }],
|
|
408
|
+
pattern: [
|
|
409
|
+
{ type: "text", value: "Hello " },
|
|
410
|
+
{
|
|
411
|
+
type: "expression",
|
|
412
|
+
arg: { type: "variable-reference", name: "name" },
|
|
413
|
+
},
|
|
414
|
+
{ type: "text", value: "!" },
|
|
415
|
+
],
|
|
416
|
+
},
|
|
417
|
+
];
|
|
418
|
+
const compiled = compileMessage(declarations, message, variants);
|
|
419
|
+
const { greeting } = await import("data:text/javascript;base64," +
|
|
420
|
+
btoa("export const greeting = " + compiled.code));
|
|
421
|
+
expect(() => greeting()).not.toThrow();
|
|
422
|
+
expect(greeting()).toBe("Hello undefined!");
|
|
423
|
+
});
|
|
424
|
+
test("does not throw when input is omitted for multi-variant message", async () => {
|
|
425
|
+
const declarations = [
|
|
426
|
+
{ type: "input-variable", name: "status" },
|
|
427
|
+
];
|
|
428
|
+
const message = {
|
|
429
|
+
locale: "en",
|
|
430
|
+
bundleId: "status_message",
|
|
431
|
+
id: "status_message",
|
|
432
|
+
selectors: [{ type: "variable-reference", name: "status" }],
|
|
433
|
+
};
|
|
434
|
+
const variants = [
|
|
435
|
+
{
|
|
436
|
+
id: "1",
|
|
437
|
+
messageId: "status_message",
|
|
438
|
+
matches: [{ type: "literal-match", key: "status", value: "ready" }],
|
|
439
|
+
pattern: [{ type: "text", value: "Ready to go" }],
|
|
440
|
+
},
|
|
441
|
+
{
|
|
442
|
+
id: "2",
|
|
443
|
+
messageId: "status_message",
|
|
444
|
+
matches: [{ type: "catchall-match", key: "status" }],
|
|
445
|
+
pattern: [{ type: "text", value: "Unknown status" }],
|
|
446
|
+
},
|
|
447
|
+
];
|
|
448
|
+
const compiled = compileMessage(declarations, message, variants);
|
|
449
|
+
const { status_message } = await import("data:text/javascript;base64," +
|
|
450
|
+
btoa("export const status_message = " + compiled.code));
|
|
451
|
+
expect(status_message({ status: "ready" })).toBe("Ready to go");
|
|
452
|
+
expect(() => status_message()).not.toThrow();
|
|
453
|
+
expect(status_message()).toBe("Unknown status");
|
|
454
|
+
});
|
|
@@ -11,7 +11,7 @@ import type { Compiled } from "./types.js";
|
|
|
11
11
|
*
|
|
12
12
|
* const { code } = compilePattern({ pattern, declarations: [{ type: "input-variable", name: "age" }] });
|
|
13
13
|
*
|
|
14
|
-
* // code will be: `Your age is ${i
|
|
14
|
+
* // code will be: `Your age is ${i?.age}`
|
|
15
15
|
*/
|
|
16
16
|
export declare const compilePattern: (args: {
|
|
17
17
|
pattern: Pattern;
|
|
@@ -10,7 +10,7 @@ import { escapeForTemplateLiteral } from "../services/codegen/escape.js";
|
|
|
10
10
|
*
|
|
11
11
|
* const { code } = compilePattern({ pattern, declarations: [{ type: "input-variable", name: "age" }] });
|
|
12
12
|
*
|
|
13
|
-
* // code will be: `Your age is ${i
|
|
13
|
+
* // code will be: `Your age is ${i?.age}`
|
|
14
14
|
*/
|
|
15
15
|
export const compilePattern = (args) => {
|
|
16
16
|
let result = "";
|
|
@@ -22,7 +22,7 @@ export const compilePattern = (args) => {
|
|
|
22
22
|
if (part.arg.type === "variable-reference") {
|
|
23
23
|
const declaration = args.declarations.find((decl) => decl.name === part.arg.name);
|
|
24
24
|
if (declaration?.type === "input-variable") {
|
|
25
|
-
result += `\${i
|
|
25
|
+
result += `\${i?.${part.arg.name}}`;
|
|
26
26
|
}
|
|
27
27
|
else if (declaration?.type === "local-variable") {
|
|
28
28
|
result += `\${${part.arg.name}}`;
|
|
@@ -26,7 +26,7 @@ test("should compile a pattern with multiple VariableReference's", () => {
|
|
|
26
26
|
{ type: "input-variable", name: "count" },
|
|
27
27
|
],
|
|
28
28
|
});
|
|
29
|
-
expect(code).toBe("`Hello ${i
|
|
29
|
+
expect(code).toBe("`Hello ${i?.name}! You have ${i?.count} messages.`");
|
|
30
30
|
});
|
|
31
31
|
test("should escape backticks", () => {
|
|
32
32
|
const pattern = [{ type: "text", value: "`Hello world`" }];
|