@inlang/paraglide-js 2.4.0 → 2.5.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.
@@ -1,2 +1,2 @@
1
- export declare const paraglideVitePlugin: (options: import("../index.js").CompilerOptions) => import("vite").Plugin<any> | import("vite").Plugin<any>[];
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,+GAAoC,CAAC"}
1
+ {"version":3,"file":"vite.d.ts","sourceRoot":"","sources":["../../src/bundler-plugins/vite.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,mBAAmB,+HAAoC,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;AAM7D,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,CA4DP,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
- // import nodePath from "node:path";
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
- const file = await ctx.fs.readFile("./tsconfig.json", { encoding: "utf-8" });
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
- // don't re-ask the question if there is an `extends` present in the tsconfig
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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=update-ts-config.test.d.ts.map
@@ -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
+ });
@@ -0,0 +1,6 @@
1
+ export type OutputFile = {
2
+ filename: string;
3
+ contents: string;
4
+ };
5
+ export type EmitOutputFile = (file: OutputFile) => void | Promise<void>;
6
+ //# sourceMappingURL=output-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output-file.d.ts","sourceRoot":"","sources":["../../src/compiler/output-file.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -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,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,UAE1E;AAED,wBAAgB,cAAc,CAC7B,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,GAC7C,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAkFxB"}
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,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,UAE1E;AAED,wBAAgB,cAAc,CAC7B,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,GAC7C,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA+GxB"}
@@ -31,19 +31,41 @@ export function generateOutput(compiledBundles, settings, fallbackMap) {
31
31
  }
32
32
  // add the fallbacks (needs to be done after the messages to avoid referencing
33
33
  // the message before they are defined)
34
- for (const locale of needsFallback) {
35
- // add fallback
34
+ const needsFallbackSet = new Set(needsFallback);
35
+ const emittedFallbacks = new Set();
36
+ const emittingFallbacks = new Set();
37
+ /**
38
+ * Emits the fallback definition for a locale ensuring that dependent fallbacks
39
+ * are declared beforehand.
40
+ *
41
+ * @example
42
+ * emitFallback("fr-ca");
43
+ */
44
+ const emitFallback = (locale) => {
45
+ if (emittedFallbacks.has(locale))
46
+ return;
47
+ if (emittingFallbacks.has(locale))
48
+ return;
49
+ emittingFallbacks.add(locale);
36
50
  const safeLocale = toSafeModuleId(locale);
37
51
  const fallbackLocale = fallbackMap[locale];
52
+ if (fallbackLocale &&
53
+ needsFallbackSet.has(fallbackLocale) &&
54
+ !compiledBundle.messages[fallbackLocale]) {
55
+ emitFallback(fallbackLocale);
56
+ }
38
57
  if (fallbackLocale) {
39
58
  const safeFallbackLocale = toSafeModuleId(fallbackLocale);
40
- // take the fallback locale
41
59
  messages.push(`/** @type {(inputs: ${inputsType(inputs)}) => string} */\nconst ${safeLocale}_${safeModuleId} = ${safeFallbackLocale}_${safeModuleId};`);
42
60
  }
43
61
  else {
44
- // fallback to just the bundle id
45
62
  messages.push(`/** @type {(inputs: ${inputsType(inputs)}) => string} */\nconst ${safeLocale}_${safeModuleId} = () => '${escapeForSingleQuoteString(bundleId)}'`);
46
63
  }
64
+ emittingFallbacks.delete(locale);
65
+ emittedFallbacks.add(locale);
66
+ };
67
+ for (const locale of needsFallback) {
68
+ emitFallback(locale);
47
69
  }
48
70
  output[filename] = messages.join("\n\n") + "\n\n" + output[filename];
49
71
  // add the imports
@@ -75,3 +75,45 @@ test("handles case senstivity by creating directories and files only in lowercas
75
75
  expect(output).toHaveProperty("messages/happyelephant2.js");
76
76
  expect(output).not.toHaveProperty("messages/HappyElephant.js");
77
77
  });
78
+ // Regression test for https://github.com/opral/inlang-paraglide-js/issues/507
79
+ test("emits fallback definitions after their dependencies", () => {
80
+ const resources = [
81
+ {
82
+ bundle: {
83
+ code: "export const admin_tasks = (inputs) => inputs;",
84
+ node: {
85
+ id: "admin_tasks",
86
+ declarations: [],
87
+ },
88
+ },
89
+ messages: {
90
+ en: {
91
+ code: '/** @type {(inputs: {}) => string} */ () => "admin"',
92
+ node: {},
93
+ },
94
+ },
95
+ },
96
+ ];
97
+ const settings = {
98
+ locales: ["fr-ca", "fr", "en"],
99
+ baseLocale: "en",
100
+ };
101
+ const fallbackMap = {
102
+ "fr-ca": "fr",
103
+ fr: "en",
104
+ en: undefined,
105
+ };
106
+ const output = generateOutput(resources, settings, fallbackMap);
107
+ const file = output["messages/admin_tasks.js"];
108
+ expect(file).toBeDefined();
109
+ if (!file) {
110
+ throw new Error("messages/admin_tasks.js should have been generated");
111
+ }
112
+ expect(file).toContain("const fr_admin_tasks = en_admin_tasks;");
113
+ expect(file).toContain("const fr_ca_admin_tasks = fr_admin_tasks;");
114
+ const frIndex = file.indexOf("const fr_admin_tasks");
115
+ const frCaIndex = file.indexOf("const fr_ca_admin_tasks");
116
+ expect(frIndex).toBeGreaterThan(-1);
117
+ expect(frCaIndex).toBeGreaterThan(-1);
118
+ expect(frIndex).toBeLessThan(frCaIndex);
119
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"assert-is-locale.d.ts","sourceRoot":"","sources":["../../../src/compiler/runtime/assert-is-locale.js"],"names":[],"mappings":"AAGA;;;;;;GAMG;AACH,sCAJW,GAAG,GACD,MAAM,CAUlB"}
1
+ {"version":3,"file":"assert-is-locale.d.ts","sourceRoot":"","sources":["../../../src/compiler/runtime/assert-is-locale.js"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,sCAJW,GAAG,GACD,MAAM,CAiBlB"}
@@ -1,4 +1,3 @@
1
- import { isLocale } from "./is-locale.js";
2
1
  import { locales } from "./variables.js";
3
2
  /**
4
3
  * Asserts that the input is a locale.
@@ -8,8 +7,13 @@ import { locales } from "./variables.js";
8
7
  * @throws {Error} If the input is not a locale.
9
8
  */
10
9
  export function assertIsLocale(input) {
11
- if (isLocale(input) === false) {
10
+ if (typeof input !== "string") {
11
+ throw new Error(`Invalid locale: ${input}. Expected a string.`);
12
+ }
13
+ const lowerInput = input.toLowerCase();
14
+ const matchedLocale = locales.find((item) => item.toLowerCase() === lowerInput);
15
+ if (!matchedLocale) {
12
16
  throw new Error(`Invalid locale: ${input}. Expected one of: ${locales.join(", ")}`);
13
17
  }
14
- return input;
18
+ return matchedLocale;
15
19
  }
@@ -1,6 +1,6 @@
1
- import { test, expect } from "vitest";
2
- import { createParaglide } from "../create-paraglide.js";
3
1
  import { newProject } from "@inlang/sdk";
2
+ import { expect, test } from "vitest";
3
+ import { createParaglide } from "../create-paraglide.js";
4
4
  test("throws if the locale is not available", async () => {
5
5
  const runtime = await createParaglide({
6
6
  blob: await newProject({
@@ -12,6 +12,21 @@ test("throws if the locale is not available", async () => {
12
12
  });
13
13
  expect(() => runtime.assertIsLocale("es")).toThrow();
14
14
  });
15
+ test("throws for non-string inputs", async () => {
16
+ const runtime = await createParaglide({
17
+ blob: await newProject({
18
+ settings: {
19
+ baseLocale: "en",
20
+ locales: ["en", "de"],
21
+ },
22
+ }),
23
+ });
24
+ expect(() => runtime.assertIsLocale(null)).toThrow();
25
+ expect(() => runtime.assertIsLocale(undefined)).toThrow();
26
+ expect(() => runtime.assertIsLocale(123)).toThrow();
27
+ expect(() => runtime.assertIsLocale({})).toThrow();
28
+ expect(() => runtime.assertIsLocale([])).toThrow();
29
+ });
15
30
  test("passes if the locale is available", async () => {
16
31
  const runtime = await createParaglide({
17
32
  blob: await newProject({
@@ -37,3 +52,19 @@ test("the return value is a Locale", async () => {
37
52
  // in the ambient type definition
38
53
  locale;
39
54
  });
55
+ test("is case-insensitive", async () => {
56
+ const runtime = await createParaglide({
57
+ blob: await newProject({
58
+ settings: {
59
+ baseLocale: "en",
60
+ locales: ["en", "pt-BR", "de-ch"],
61
+ },
62
+ }),
63
+ });
64
+ expect(() => runtime.assertIsLocale("EN")).not.toThrow();
65
+ expect(() => runtime.assertIsLocale("pt-br")).not.toThrow();
66
+ expect(() => runtime.assertIsLocale("de-CH")).not.toThrow();
67
+ expect(runtime.assertIsLocale("EN")).toBe("en");
68
+ expect(runtime.assertIsLocale("pT-bR")).toBe("pt-BR");
69
+ expect(runtime.assertIsLocale("de-CH")).toBe("de-ch");
70
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"is-locale.d.ts","sourceRoot":"","sources":["../../../src/compiler/runtime/is-locale.js"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AACH,iCAHW,GAAG,GACD,MAAM,IAAI,MAAM,CAI5B"}
1
+ {"version":3,"file":"is-locale.d.ts","sourceRoot":"","sources":["../../../src/compiler/runtime/is-locale.js"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AACH,iCAHW,GAAG,GACD,MAAM,IAAI,MAAM,CAO5B"}
@@ -13,5 +13,9 @@ import { locales } from "./variables.js";
13
13
  * @returns {locale is Locale}
14
14
  */
15
15
  export function isLocale(locale) {
16
- return !locale ? false : locales.includes(locale);
16
+ if (typeof locale !== "string")
17
+ return false;
18
+ return !locale
19
+ ? false
20
+ : locales.some((item) => item.toLowerCase() === locale.toLowerCase());
17
21
  }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=is-locale.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-locale.test.d.ts","sourceRoot":"","sources":["../../../src/compiler/runtime/is-locale.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,31 @@
1
+ import { newProject } from "@inlang/sdk";
2
+ import { expect, test } from "vitest";
3
+ import { createParaglide } from "../create-paraglide.js";
4
+ const runtime = await createParaglide({
5
+ blob: await newProject({
6
+ settings: {
7
+ baseLocale: "en",
8
+ locales: ["en", "pt-BR", "de-ch"],
9
+ },
10
+ }),
11
+ });
12
+ test("returns true for exact matches", () => {
13
+ expect(runtime.isLocale("pt-BR")).toBe(true);
14
+ });
15
+ test("is case-insensitive", () => {
16
+ expect(runtime.isLocale("EN")).toBe(true);
17
+ expect(runtime.isLocale("pt-br")).toBe(true);
18
+ expect(runtime.isLocale("de-CH")).toBe(true);
19
+ });
20
+ test("returns false for non-existent locales", () => {
21
+ expect(runtime.isLocale("es")).toBe(false);
22
+ expect(runtime.isLocale("xx")).toBe(false);
23
+ expect(runtime.isLocale("")).toBe(false);
24
+ });
25
+ test("returns false for non-string inputs", () => {
26
+ expect(runtime.isLocale(null)).toBe(false);
27
+ expect(runtime.isLocale(undefined)).toBe(false);
28
+ expect(runtime.isLocale(123)).toBe(false);
29
+ expect(runtime.isLocale({})).toBe(false);
30
+ expect(runtime.isLocale([])).toBe(false);
31
+ });
@@ -1,5 +1,5 @@
1
1
  export const ENV_VARIABLES = {
2
2
  PARJS_APP_ID: "library.inlang.paraglideJs",
3
3
  PARJS_POSTHOG_TOKEN: undefined,
4
- PARJS_PACKAGE_VERSION: "2.4.0",
4
+ PARJS_PACKAGE_VERSION: "2.5.0",
5
5
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@inlang/paraglide-js",
3
3
  "type": "module",
4
- "version": "2.4.0",
4
+ "version": "2.5.0",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
7
7
  "access": "public",
@@ -26,13 +26,13 @@
26
26
  "./urlpattern-polyfill": "./dist/urlpattern-polyfill/index.js"
27
27
  },
28
28
  "dependencies": {
29
- "@inlang/sdk": "2.4.9",
30
29
  "commander": "11.1.0",
31
30
  "consola": "3.4.0",
32
31
  "json5": "2.2.3",
33
32
  "unplugin": "^2.1.2",
34
33
  "urlpattern-polyfill": "^10.0.0",
35
- "@inlang/recommend-sherlock": "0.2.1"
34
+ "@inlang/recommend-sherlock": "0.2.1",
35
+ "@inlang/sdk": "2.4.9"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@rollup/plugin-virtual": "3.0.2",
@@ -48,8 +48,8 @@
48
48
  "typedoc-plugin-missing-exports": "4.0.0",
49
49
  "typescript": "5.8.3",
50
50
  "vitest": "3.1.4",
51
- "@inlang/plugin-message-format": "4.0.0",
52
- "@opral/tsconfig": "1.1.0"
51
+ "@opral/tsconfig": "1.1.0",
52
+ "@inlang/plugin-message-format": "4.0.0"
53
53
  },
54
54
  "keywords": [
55
55
  "inlang",