@x18n/builtins 0.0.13 → 0.0.15
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/angular-templates-DqIOGfzT.mjs +8 -0
- package/dist/checks.d.mts +1 -0
- package/dist/checks.mjs +26 -1
- package/dist/extractors.mjs +7 -3
- package/dist/finders.d.mts +4 -1
- package/dist/finders.mjs +1 -1
- package/dist/formats/ecmascript.d.mts +5 -0
- package/dist/formats/ecmascript.mjs +232 -0
- package/dist/formats/ftl.d.mts +5 -0
- package/dist/formats/ftl.mjs +234 -0
- package/dist/formats/ini.d.mts +5 -0
- package/dist/formats/ini.mjs +158 -0
- package/dist/formats/json.d.mts +6 -0
- package/dist/formats/json.mjs +10 -0
- package/dist/formats/json5.d.mts +5 -0
- package/dist/formats/json5.mjs +143 -0
- package/dist/formats/jsonc.d.mts +6 -0
- package/dist/formats/jsonc.mjs +10 -0
- package/dist/formats/php.d.mts +5 -0
- package/dist/formats/php.mjs +198 -0
- package/dist/formats/po.d.mts +5 -0
- package/dist/formats/po.mjs +177 -0
- package/dist/formats/properties.d.mts +5 -0
- package/dist/formats/properties.mjs +123 -0
- package/dist/formats/resx.d.mts +6 -0
- package/dist/formats/resx.mjs +202 -0
- package/dist/formats/yaml.d.mts +5 -0
- package/dist/formats/yaml.mjs +140 -0
- package/dist/formats.d.mts +10 -34
- package/dist/formats.mjs +61 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +30 -22
- package/dist/{vue-CF4pe-Yr.mjs → js-CV-ECNJG.mjs} +67 -222
- package/dist/json-CGNSGS97.mjs +109 -0
- package/dist/json-CyjI8bjp.d.mts +8 -0
- package/dist/jsx-templates-B1J0no3i.mjs +17 -0
- package/dist/literal-gIdB1VGy.mjs +125 -0
- package/dist/locale-root-C_rch3IQ.mjs +15 -0
- package/dist/manifests.d.mts +8 -0
- package/dist/manifests.mjs +66 -2
- package/dist/placeholders.d.mts +11 -0
- package/dist/placeholders.mjs +28 -1
- package/dist/plugin-Cb1b0axD.mjs +83 -0
- package/dist/plural-forms-B7_QX4nG.mjs +12 -0
- package/dist/presets/cargo-i18n.d.mts +6 -0
- package/dist/presets/cargo-i18n.mjs +28 -0
- package/dist/presets/chrome-extension.d.mts +6 -0
- package/dist/presets/chrome-extension.mjs +52 -0
- package/dist/presets/dotnet-resx.d.mts +6 -0
- package/dist/presets/dotnet-resx.mjs +64 -0
- package/dist/presets/easy-localization.d.mts +6 -0
- package/dist/presets/easy-localization.mjs +36 -0
- package/dist/presets/ember-intl.d.mts +5 -0
- package/dist/presets/ember-intl.mjs +31 -0
- package/dist/presets/fluent-react.d.mts +5 -0
- package/dist/presets/fluent-react.mjs +46 -0
- package/dist/presets/fluent-vue.d.mts +5 -0
- package/dist/presets/fluent-vue.mjs +51 -0
- package/dist/presets/flutter-translate.d.mts +6 -0
- package/dist/presets/flutter-translate.mjs +30 -0
- package/dist/presets/flutter.d.mts +6 -0
- package/dist/presets/flutter.mjs +21 -0
- package/dist/presets/general.d.mts +6 -0
- package/dist/presets/general.mjs +17 -0
- package/dist/presets/globalize.d.mts +5 -0
- package/dist/presets/globalize.mjs +25 -0
- package/dist/presets/gravity-ui-i18n.d.mts +6 -0
- package/dist/presets/gravity-ui-i18n.mjs +41 -0
- package/dist/presets/i18n-tag.d.mts +5 -0
- package/dist/presets/i18n-tag.mjs +21 -0
- package/dist/presets/i18next-shopify.d.mts +6 -0
- package/dist/presets/i18next-shopify.mjs +23 -0
- package/dist/presets/i18next.d.mts +5 -0
- package/dist/presets/i18next.mjs +10 -0
- package/dist/presets/i18nice.d.mts +6 -0
- package/dist/presets/i18nice.mjs +29 -0
- package/dist/presets/jekyll.d.mts +5 -0
- package/dist/presets/jekyll.mjs +23 -0
- package/dist/presets/laravel-vue-i18n.d.mts +6 -0
- package/dist/presets/laravel-vue-i18n.mjs +54 -0
- package/dist/presets/laravel.d.mts +6 -0
- package/dist/presets/laravel.mjs +23 -0
- package/dist/presets/lingui.d.mts +5 -0
- package/dist/presets/lingui.mjs +61 -0
- package/dist/presets/next-international.d.mts +5 -0
- package/dist/presets/next-international.mjs +27 -0
- package/dist/presets/next-intl.d.mts +5 -0
- package/dist/presets/next-intl.mjs +55 -0
- package/dist/presets/next-translate.d.mts +5 -0
- package/dist/presets/next-translate.mjs +53 -0
- package/dist/presets/ngx-translate.d.mts +5 -0
- package/dist/presets/ngx-translate.mjs +41 -0
- package/dist/presets/nuxt-i18n-micro.d.mts +5 -0
- package/dist/presets/nuxt-i18n-micro.mjs +81 -0
- package/dist/presets/nuxt-i18n.d.mts +5 -0
- package/dist/presets/nuxt-i18n.mjs +23 -0
- package/dist/presets/paraglide.d.mts +5 -0
- package/dist/presets/paraglide.mjs +38 -0
- package/dist/presets/php-gettext.d.mts +6 -0
- package/dist/presets/php-gettext.mjs +27 -0
- package/dist/presets/php-joomla.d.mts +6 -0
- package/dist/presets/php-joomla.mjs +22 -0
- package/dist/presets/polyglot.d.mts +5 -0
- package/dist/presets/polyglot.mjs +23 -0
- package/dist/presets/python-gettext.d.mts +6 -0
- package/dist/presets/python-gettext.mjs +44 -0
- package/dist/presets/react-i18next.d.mts +5 -0
- package/dist/presets/react-i18next.mjs +67 -0
- package/dist/presets/react-intl.d.mts +5 -0
- package/dist/presets/react-intl.mjs +47 -0
- package/dist/presets/ruby-i18n-js.d.mts +5 -0
- package/dist/presets/ruby-i18n-js.mjs +19 -0
- package/dist/presets/ruby-rails.d.mts +7 -0
- package/dist/presets/ruby-rails.mjs +44 -0
- package/dist/presets/solid-primitives-i18n.d.mts +5 -0
- package/dist/presets/solid-primitives-i18n.mjs +29 -0
- package/dist/presets/svelte-i18n.d.mts +5 -0
- package/dist/presets/svelte-i18n.mjs +33 -0
- package/dist/presets/svelte-i18next.d.mts +5 -0
- package/dist/presets/svelte-i18next.mjs +27 -0
- package/dist/presets/transloco.d.mts +5 -0
- package/dist/presets/transloco.mjs +44 -0
- package/dist/presets/typesafe-i18n.d.mts +6 -0
- package/dist/presets/typesafe-i18n.mjs +61 -0
- package/dist/presets/ui5.d.mts +5 -0
- package/dist/presets/ui5.mjs +35 -0
- package/dist/presets/vscode-extension.d.mts +5 -0
- package/dist/presets/vscode-extension.mjs +53 -0
- package/dist/presets/vue-i18n.d.mts +5 -0
- package/dist/presets/vue-i18n.mjs +59 -0
- package/dist/presets.d.mts +7 -54
- package/dist/presets.mjs +346 -1
- package/dist/reporters/github.d.mts +6 -0
- package/dist/reporters/github.mjs +27 -0
- package/dist/reporters/json.d.mts +6 -0
- package/dist/reporters/json.mjs +24 -0
- package/dist/reporters/markdown.d.mts +6 -0
- package/dist/reporters/markdown.mjs +37 -0
- package/dist/reporters/pretty.d.mts +6 -0
- package/dist/reporters/pretty.mjs +64 -0
- package/dist/reporters/sarif.d.mts +6 -0
- package/dist/reporters/sarif.mjs +48 -0
- package/dist/reporters.d.mts +5 -12
- package/dist/reporters.mjs +8 -218
- package/dist/reviews.d.mts +6 -0
- package/dist/reviews.mjs +35 -0
- package/dist/scanners/astro.d.mts +6 -0
- package/dist/scanners/astro.mjs +256 -0
- package/dist/scanners/js.d.mts +6 -0
- package/dist/scanners/js.mjs +33 -0
- package/dist/scanners/regex-scopes.d.mts +14 -0
- package/dist/scanners/regex-scopes.mjs +41 -0
- package/dist/scanners/regex.d.mts +9 -0
- package/dist/{regex-AYVhuSb6.mjs → scanners/regex.mjs} +2 -40
- package/dist/scanners/svelte.d.mts +6 -0
- package/dist/{scanners-DhnnVp7s.mjs → scanners/svelte.mjs} +3 -3
- package/dist/scanners/vue.d.mts +6 -0
- package/dist/scanners/vue.mjs +172 -0
- package/dist/scanners.d.mts +9 -50
- package/dist/scanners.mjs +36 -4
- package/dist/sfc-blocks-DaGKDZsb.mjs +213 -0
- package/dist/shared-C7ceptwi.mjs +55 -0
- package/dist/shared-D-tbsYmc.mjs +30 -0
- package/dist/shared-D4NJBKEP.mjs +17 -0
- package/dist/shared-Tq06G0gp.mjs +6 -0
- package/dist/shared-meYeVckh.mjs +11 -0
- package/dist/vue-compiler-0sfLIiiL.mjs +8 -0
- package/package.json +68 -3
- package/dist/formats-CFkM5co0.mjs +0 -1370
- package/dist/presets-Co5SlnKr.mjs +0 -1187
- package/dist/sfc-blocks-BqQ8m9rX.mjs +0 -161
- package/dist/yaml-plugin.d.mts +0 -8
- package/dist/yaml-plugin.mjs +0 -68
package/dist/formats.d.mts
CHANGED
|
@@ -1,34 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
//#region src/formats/
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
//#endregion
|
|
11
|
-
//#region src/formats/json.d.ts
|
|
12
|
-
interface JsonOptions {
|
|
13
|
-
/** Spaces per level for new files. Existing files keep their indent. */
|
|
14
|
-
readonly indent?: number;
|
|
15
|
-
}
|
|
16
|
-
export declare function json(options?: JsonOptions): Plugin;
|
|
17
|
-
export declare function jsonc(options?: JsonOptions): Plugin;
|
|
18
|
-
//#endregion
|
|
19
|
-
//#region src/formats/json5.d.ts
|
|
20
|
-
export declare function json5(): Plugin;
|
|
21
|
-
//#endregion
|
|
22
|
-
//#region src/formats/php.d.ts
|
|
23
|
-
export declare function php(): Plugin;
|
|
24
|
-
//#endregion
|
|
25
|
-
//#region src/formats/po.d.ts
|
|
26
|
-
export declare function po(): Plugin;
|
|
27
|
-
//#endregion
|
|
28
|
-
//#region src/formats/properties.d.ts
|
|
29
|
-
export declare function properties(): Plugin;
|
|
30
|
-
//#endregion
|
|
31
|
-
//#region src/formats/yaml.d.ts
|
|
32
|
-
export declare function yaml(): Plugin;
|
|
33
|
-
//#endregion
|
|
34
|
-
export type { JsonOptions };
|
|
1
|
+
import { FormatEntry } from "@x18n/core";
|
|
2
|
+
//#region src/formats/index.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Built-in formats by id, in claim order. A format is a folder: `<name>/entry.ts` holds its extensions and a
|
|
5
|
+
* `load` for `<name>/index.ts`, the format itself, which is imported when a file of that format is first read.
|
|
6
|
+
*/
|
|
7
|
+
export declare const formats: {
|
|
8
|
+
readonly [id: string]: FormatEntry;
|
|
9
|
+
};
|
|
10
|
+
//#endregion
|
package/dist/formats.mjs
CHANGED
|
@@ -1 +1,61 @@
|
|
|
1
|
-
|
|
1
|
+
//#endregion
|
|
2
|
+
//#region src/formats/index.ts
|
|
3
|
+
/**
|
|
4
|
+
* Built-in formats by id, in claim order. A format is a folder: `<name>/entry.ts` holds its extensions and a
|
|
5
|
+
* `load` for `<name>/index.ts`, the format itself, which is imported when a file of that format is first read.
|
|
6
|
+
*/
|
|
7
|
+
const formats = {
|
|
8
|
+
json: {
|
|
9
|
+
extensions: ["json"],
|
|
10
|
+
load: () => import("./formats/json.mjs")
|
|
11
|
+
},
|
|
12
|
+
jsonc: {
|
|
13
|
+
extensions: ["jsonc"],
|
|
14
|
+
load: () => import("./formats/jsonc.mjs")
|
|
15
|
+
},
|
|
16
|
+
json5: {
|
|
17
|
+
extensions: ["json5"],
|
|
18
|
+
load: () => import("./formats/json5.mjs")
|
|
19
|
+
},
|
|
20
|
+
yaml: {
|
|
21
|
+
extensions: ["yml", "yaml"],
|
|
22
|
+
load: () => import("./formats/yaml.mjs")
|
|
23
|
+
},
|
|
24
|
+
po: {
|
|
25
|
+
extensions: ["po", "pot"],
|
|
26
|
+
load: () => import("./formats/po.mjs")
|
|
27
|
+
},
|
|
28
|
+
properties: {
|
|
29
|
+
extensions: ["properties"],
|
|
30
|
+
load: () => import("./formats/properties.mjs")
|
|
31
|
+
},
|
|
32
|
+
resx: {
|
|
33
|
+
extensions: ["resx"],
|
|
34
|
+
load: () => import("./formats/resx.mjs")
|
|
35
|
+
},
|
|
36
|
+
ftl: {
|
|
37
|
+
extensions: ["ftl"],
|
|
38
|
+
load: () => import("./formats/ftl.mjs")
|
|
39
|
+
},
|
|
40
|
+
ini: {
|
|
41
|
+
extensions: ["ini"],
|
|
42
|
+
load: () => import("./formats/ini.mjs")
|
|
43
|
+
},
|
|
44
|
+
php: {
|
|
45
|
+
extensions: ["php"],
|
|
46
|
+
load: () => import("./formats/php.mjs")
|
|
47
|
+
},
|
|
48
|
+
js: {
|
|
49
|
+
extensions: [
|
|
50
|
+
"js",
|
|
51
|
+
"mjs",
|
|
52
|
+
"cjs",
|
|
53
|
+
"ts",
|
|
54
|
+
"mts",
|
|
55
|
+
"cts"
|
|
56
|
+
],
|
|
57
|
+
load: () => import("./formats/ecmascript.mjs")
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
//#endregion
|
|
61
|
+
export { formats };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Builtins } from "@x18n/core";
|
|
2
2
|
//#region src/index.d.ts
|
|
3
|
-
/** Plugins that are always on, and the presets that `'auto'` detects. */
|
|
4
|
-
export declare function createBuiltins(): Builtins
|
|
3
|
+
/** Plugins that are always on, and the presets that `'auto'` detects. Formats, scanners and presets load on demand; `regex` and the reporters are always loaded. */
|
|
4
|
+
export declare function createBuiltins(): Promise<Builtins>;
|
|
5
5
|
//#endregion
|
package/dist/index.mjs
CHANGED
|
@@ -1,35 +1,43 @@
|
|
|
1
1
|
import { recommended } from "./checks.mjs";
|
|
2
2
|
import { i as pattern } from "./finders-CqTGh4OJ.mjs";
|
|
3
|
-
import { a as json5, c as ini, i as php, l as ftl, n as properties, o as json, r as po, s as jsonc, t as yaml, u as ecmascript } from "./formats-CFkM5co0.mjs";
|
|
4
|
-
import { a as js, n as vue } from "./vue-CF4pe-Yr.mjs";
|
|
5
3
|
import { manifests } from "./manifests.mjs";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
4
|
+
import { reviews } from "./reviews.mjs";
|
|
5
|
+
import { formats } from "@x18n/builtins/formats";
|
|
6
|
+
import { presets } from "@x18n/builtins/presets";
|
|
7
|
+
import { reporters } from "@x18n/builtins/reporters";
|
|
8
|
+
import { scanners } from "@x18n/builtins/scanners";
|
|
9
|
+
import regex from "@x18n/builtins/scanners/regex";
|
|
10
10
|
//#region src/index.ts
|
|
11
|
-
/** Plugins that are always on, and the presets that `'auto'` detects. */
|
|
12
|
-
function createBuiltins() {
|
|
11
|
+
/** Plugins that are always on, and the presets that `'auto'` detects. Formats, scanners and presets load on demand; `regex` and the reporters are always loaded. */
|
|
12
|
+
async function createBuiltins() {
|
|
13
|
+
const formatPlugins = Object.entries(formats).map(([id, entry]) => ({
|
|
14
|
+
name: `x18n:format-${id}`,
|
|
15
|
+
lazyFormats: [{
|
|
16
|
+
id,
|
|
17
|
+
...entry
|
|
18
|
+
}]
|
|
19
|
+
}));
|
|
20
|
+
const scannerPlugins = Object.entries(scanners).map(([id, entry]) => ({
|
|
21
|
+
name: `x18n:scanner-${id}`,
|
|
22
|
+
lazyScanners: [{
|
|
23
|
+
id,
|
|
24
|
+
...entry
|
|
25
|
+
}]
|
|
26
|
+
}));
|
|
27
|
+
const reporterPlugin = {
|
|
28
|
+
name: "x18n:reporters",
|
|
29
|
+
reporters: await Promise.all(Object.values(reporters).map(async (load) => (await load()).default))
|
|
30
|
+
};
|
|
13
31
|
return {
|
|
14
32
|
plugins: [
|
|
15
33
|
manifests(),
|
|
16
|
-
|
|
17
|
-
jsonc(),
|
|
18
|
-
json5(),
|
|
19
|
-
yaml(),
|
|
20
|
-
po(),
|
|
21
|
-
properties(),
|
|
22
|
-
ftl(),
|
|
23
|
-
ini(),
|
|
24
|
-
php(),
|
|
25
|
-
ecmascript(),
|
|
34
|
+
...formatPlugins,
|
|
26
35
|
pattern(),
|
|
27
|
-
|
|
28
|
-
vue(),
|
|
29
|
-
svelte(),
|
|
36
|
+
...scannerPlugins,
|
|
30
37
|
regex(),
|
|
31
38
|
recommended(),
|
|
32
|
-
|
|
39
|
+
reviews(),
|
|
40
|
+
reporterPlugin
|
|
33
41
|
],
|
|
34
42
|
presets
|
|
35
43
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region src/scanners/js.ts
|
|
1
|
+
//#region src/scanners/shared/js.ts
|
|
2
2
|
const textAttributeWords = /* @__PURE__ */ new Set([
|
|
3
3
|
"title",
|
|
4
4
|
"subtitle",
|
|
@@ -83,6 +83,8 @@ var FileScan = class {
|
|
|
83
83
|
declared = /* @__PURE__ */ new Map();
|
|
84
84
|
factories = /* @__PURE__ */ new Map();
|
|
85
85
|
typed = /* @__PURE__ */ new Map();
|
|
86
|
+
/** Members of the file's top-level interfaces and object type aliases, for typed props (`({ t }: Props)`). */
|
|
87
|
+
typeMembers = /* @__PURE__ */ new Map();
|
|
86
88
|
consumed = /* @__PURE__ */ new Set();
|
|
87
89
|
/** Render-prop functions of factory components (`<Translation>{t => ...}</Translation>`), by start offset. */
|
|
88
90
|
renderProps = /* @__PURE__ */ new Map();
|
|
@@ -145,6 +147,11 @@ var FileScan = class {
|
|
|
145
147
|
return constant && isStringLiteral(constant) ? constant.value : void 0;
|
|
146
148
|
}
|
|
147
149
|
}
|
|
150
|
+
/** A fixed namespace, or the one an argument holds. */
|
|
151
|
+
namespaceArg(args, source, fallback) {
|
|
152
|
+
if (typeof source === "string") return source;
|
|
153
|
+
return source ? this.stringArg(this.argValues(args, source, fallback)[0]) : void 0;
|
|
154
|
+
}
|
|
148
155
|
argValues(args, source, fallback) {
|
|
149
156
|
const arg = args[typeof source === "number" ? source : source?.arg ?? fallback];
|
|
150
157
|
const property = typeof source === "object" ? source.property : void 0;
|
|
@@ -165,13 +172,9 @@ var FileScan = class {
|
|
|
165
172
|
}
|
|
166
173
|
/** ---- Usages ---- */
|
|
167
174
|
/** `expression` is the call holding `value`, kept only when the key is the call's own literal. */
|
|
168
|
-
addKey(value,
|
|
175
|
+
addKey(value, base, expression) {
|
|
169
176
|
if ("props" in value) return;
|
|
170
177
|
const node = unwrap(value);
|
|
171
|
-
const base = {
|
|
172
|
-
namespace: namespace ?? binding.namespace,
|
|
173
|
-
keyPrefix: binding.keyPrefix
|
|
174
|
-
};
|
|
175
178
|
const call = expression ? { expression: this.range(expression) } : {};
|
|
176
179
|
if (isStringLiteral(node)) {
|
|
177
180
|
this.consumed.add(node.start);
|
|
@@ -187,7 +190,7 @@ var FileScan = class {
|
|
|
187
190
|
return;
|
|
188
191
|
}
|
|
189
192
|
if (node.type === "JSXExpressionContainer") {
|
|
190
|
-
if (node.expression.type !== "JSXEmptyExpression") this.addKey(node.expression,
|
|
193
|
+
if (node.expression.type !== "JSXEmptyExpression") this.addKey(node.expression, base);
|
|
191
194
|
return;
|
|
192
195
|
}
|
|
193
196
|
if (node.type === "TemplateLiteral") {
|
|
@@ -209,8 +212,8 @@ var FileScan = class {
|
|
|
209
212
|
return;
|
|
210
213
|
}
|
|
211
214
|
if (node.type === "ConditionalExpression") {
|
|
212
|
-
this.addKey(node.consequent,
|
|
213
|
-
this.addKey(node.alternate,
|
|
215
|
+
this.addKey(node.consequent, base);
|
|
216
|
+
this.addKey(node.alternate, base);
|
|
214
217
|
return;
|
|
215
218
|
}
|
|
216
219
|
if (node.type === "ArrowFunctionExpression" && node.params[0]?.type === "Identifier") return this.addSelector(node.params[0].name, node.body, base);
|
|
@@ -225,6 +228,10 @@ var FileScan = class {
|
|
|
225
228
|
});
|
|
226
229
|
return;
|
|
227
230
|
}
|
|
231
|
+
this.addUnknown(node, base);
|
|
232
|
+
}
|
|
233
|
+
addUnknown(node, base) {
|
|
234
|
+
if (isStringLiteral(node)) this.consumed.add(node.start);
|
|
228
235
|
this.usages.push({
|
|
229
236
|
...base,
|
|
230
237
|
raw: this.text.slice(node.start, node.end),
|
|
@@ -267,10 +274,17 @@ var FileScan = class {
|
|
|
267
274
|
handleCall(name, args, expression) {
|
|
268
275
|
const bindings = this.lookupAll(name).filter((binding) => !binding.spec.factory);
|
|
269
276
|
for (const binding of bindings) {
|
|
270
|
-
const namespace =
|
|
277
|
+
const namespace = this.namespaceArg(args, binding.spec.namespace, 1);
|
|
278
|
+
const keyPrefix = binding.spec.keyPrefix ? this.stringArg(this.argValues(args, binding.spec.keyPrefix, 1)[0]) : void 0;
|
|
279
|
+
const base = {
|
|
280
|
+
namespace: namespace ?? binding.namespace,
|
|
281
|
+
keyPrefix: keyPrefix ?? binding.keyPrefix
|
|
282
|
+
};
|
|
271
283
|
for (const value of this.argValues(args, binding.spec.key, 0)) {
|
|
272
|
-
if (
|
|
273
|
-
|
|
284
|
+
if ("props" in value) continue;
|
|
285
|
+
if (bindings.length > 1 && unwrap(value).type === "ObjectExpression") continue;
|
|
286
|
+
if (binding.spec.keyPrefix && keyPrefix === void 0) this.addUnknown(unwrap(value), base);
|
|
287
|
+
else this.addKey(value, base, expression);
|
|
274
288
|
}
|
|
275
289
|
}
|
|
276
290
|
return bindings.length > 0;
|
|
@@ -321,7 +335,7 @@ var FileScan = class {
|
|
|
321
335
|
/** The first factory spec whose namespace and prefix sources all resolve, else the first spec. */
|
|
322
336
|
pickFactory(args, specs) {
|
|
323
337
|
const resolved = specs.map((spec) => {
|
|
324
|
-
const namespace =
|
|
338
|
+
const namespace = this.namespaceArg(args, spec.namespace, 0);
|
|
325
339
|
const keyPrefix = spec.keyPrefix ? this.stringArg(this.argValues(args, spec.keyPrefix, 0)[0]) : void 0;
|
|
326
340
|
const complete = (!spec.namespace || namespace !== void 0) && (!spec.keyPrefix || keyPrefix !== void 0);
|
|
327
341
|
return {
|
|
@@ -354,17 +368,49 @@ var FileScan = class {
|
|
|
354
368
|
if (key && local.type === "Identifier" && this.declared.has(`${key}.t`)) this.bind(`${local.name}.t`, binding);
|
|
355
369
|
}
|
|
356
370
|
}
|
|
371
|
+
/** Parameters typed as a translate function (`t: TFunction<'ns'>`), also as destructured props. */
|
|
357
372
|
params(params) {
|
|
358
373
|
for (const param of params) {
|
|
359
374
|
const target = param.type === "TSParameterProperty" ? param.parameter : param;
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
375
|
+
const annotation = target.type === "Identifier" || target.type === "ObjectPattern" ? target.typeAnnotation?.typeAnnotation : void 0;
|
|
376
|
+
if (!annotation) continue;
|
|
377
|
+
if (target.type === "Identifier") this.bindTyped(target.name, annotation);
|
|
378
|
+
else if (target.type === "ObjectPattern") this.props(target, annotation);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
/** `({ t }: { t: TFunction<'ns'> })`, or with an interface or type of the file. */
|
|
382
|
+
props(pattern, annotation) {
|
|
383
|
+
const members = annotation.type === "TSTypeLiteral" ? annotation.members : annotation.type === "TSTypeReference" && annotation.typeName.type === "Identifier" ? this.typeMembers.get(annotation.typeName.name) ?? [] : [];
|
|
384
|
+
for (const property of pattern.properties) {
|
|
385
|
+
if (property.type !== "Property") continue;
|
|
386
|
+
const local = unwrap(property.value);
|
|
387
|
+
const name = propertyName(property.key);
|
|
388
|
+
const member = members.find((m) => m.type === "TSPropertySignature" && !m.computed && propertyName(m.key) === name);
|
|
389
|
+
const type = member?.type === "TSPropertySignature" ? member.typeAnnotation?.typeAnnotation : void 0;
|
|
390
|
+
if (local.type === "Identifier" && type) this.bindTyped(local.name, type);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
/** The namespace is the first type argument: `TFunction<'shared'>` or `TFunction<['shared', 'common']>`. */
|
|
394
|
+
bindTyped(name, annotation) {
|
|
395
|
+
if (annotation.type !== "TSTypeReference" || annotation.typeName.type !== "Identifier") return;
|
|
396
|
+
const spec = this.typed.get(annotation.typeName.name);
|
|
397
|
+
if (!spec) return;
|
|
398
|
+
const arg = annotation.typeArguments?.params[0];
|
|
399
|
+
const first = arg?.type === "TSTupleType" ? arg.elementTypes[0] : arg;
|
|
400
|
+
const namespace = first?.type === "TSLiteralType" && isStringLiteral(first.literal) ? first.literal.value : void 0;
|
|
401
|
+
this.bind(name, {
|
|
402
|
+
spec: {
|
|
365
403
|
...spec,
|
|
366
404
|
types: void 0
|
|
367
|
-
}
|
|
405
|
+
},
|
|
406
|
+
namespace
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
types(program) {
|
|
410
|
+
for (const statement of program.body) {
|
|
411
|
+
const node = statement.type === "ExportNamedDeclaration" && statement.declaration ? statement.declaration : statement;
|
|
412
|
+
if (node.type === "TSInterfaceDeclaration") this.typeMembers.set(node.id.name, node.body.body);
|
|
413
|
+
else if (node.type === "TSTypeAliasDeclaration" && node.typeAnnotation.type === "TSTypeLiteral") this.typeMembers.set(node.id.name, node.typeAnnotation.members);
|
|
368
414
|
}
|
|
369
415
|
}
|
|
370
416
|
importSpecifier(node) {
|
|
@@ -532,211 +578,10 @@ function scanSource(parse, text, options) {
|
|
|
532
578
|
range: false
|
|
533
579
|
});
|
|
534
580
|
const scan = new FileScan(source, (options.offset ?? 0) + shift, options.calls, options.hardStrings !== false, new Set(options.ignoreCallees), options.textAttributes);
|
|
581
|
+
scan.types(result.program);
|
|
535
582
|
scan.walk(result.program, void 0, "");
|
|
536
583
|
scan.markers(result.program, result.comments);
|
|
537
584
|
return scan.result();
|
|
538
585
|
}
|
|
539
|
-
/** The AST scanner for JS and TS, driven by call specs (`usage.calls`). */
|
|
540
|
-
function js() {
|
|
541
|
-
return {
|
|
542
|
-
name: "x18n:scanner-js",
|
|
543
|
-
scanners: [{
|
|
544
|
-
id: "js",
|
|
545
|
-
extensions: [
|
|
546
|
-
"js",
|
|
547
|
-
"jsx",
|
|
548
|
-
"mjs",
|
|
549
|
-
"cjs",
|
|
550
|
-
"ts",
|
|
551
|
-
"tsx",
|
|
552
|
-
"mts",
|
|
553
|
-
"cts"
|
|
554
|
-
],
|
|
555
|
-
async scan(file, ctx) {
|
|
556
|
-
return scanSource(await loadParser(), file.text, {
|
|
557
|
-
calls: ctx.calls,
|
|
558
|
-
filename: file.path,
|
|
559
|
-
hardStrings: true,
|
|
560
|
-
ignoreCallees: ctx.config.extract.ignoreCallees,
|
|
561
|
-
textAttributes: ctx.config.extract.attributes
|
|
562
|
-
});
|
|
563
|
-
}
|
|
564
|
-
}]
|
|
565
|
-
};
|
|
566
|
-
}
|
|
567
|
-
//#endregion
|
|
568
|
-
//#region src/scanners/vue.ts
|
|
569
|
-
let compiler;
|
|
570
|
-
function loadVueCompiler() {
|
|
571
|
-
compiler ??= import("@vue/compiler-sfc");
|
|
572
|
-
return compiler;
|
|
573
|
-
}
|
|
574
|
-
const ELEMENT = 1;
|
|
575
|
-
const TEXT = 2;
|
|
576
|
-
const SIMPLE_EXPRESSION = 4;
|
|
577
|
-
const INTERPOLATION = 5;
|
|
578
|
-
const ATTRIBUTE = 6;
|
|
579
|
-
/** `keypath` is the vue-i18n prop for a key, on `<i18n-t>` and on the app's own wrappers of it; `path` on the legacy components. */
|
|
580
|
-
function isKeyAttribute(tag, name) {
|
|
581
|
-
return name === "keypath" || name === "path" && (tag === "i18n" || tag === "i18n-d");
|
|
582
|
-
}
|
|
583
|
-
function merge(into, result) {
|
|
584
|
-
into.usages.push(...result.usages);
|
|
585
|
-
into.keySlots.push(...result.keySlots ?? []);
|
|
586
|
-
into.hardStrings.push(...result.hardStrings ?? []);
|
|
587
|
-
}
|
|
588
|
-
var TemplateScan = class {
|
|
589
|
-
parse;
|
|
590
|
-
calls;
|
|
591
|
-
textAttributes;
|
|
592
|
-
out = {
|
|
593
|
-
usages: [],
|
|
594
|
-
keySlots: [],
|
|
595
|
-
hardStrings: []
|
|
596
|
-
};
|
|
597
|
-
constructor(parse, calls, textAttributes) {
|
|
598
|
-
this.parse = parse;
|
|
599
|
-
this.calls = calls;
|
|
600
|
-
this.textAttributes = textAttributes;
|
|
601
|
-
}
|
|
602
|
-
expression(content, offset) {
|
|
603
|
-
if (!content.trim()) return;
|
|
604
|
-
try {
|
|
605
|
-
merge(this.out, scanSource(this.parse, content, {
|
|
606
|
-
calls: this.calls,
|
|
607
|
-
offset,
|
|
608
|
-
filename: "template.ts",
|
|
609
|
-
expression: true,
|
|
610
|
-
hardStrings: false
|
|
611
|
-
}));
|
|
612
|
-
} catch {}
|
|
613
|
-
}
|
|
614
|
-
/** `:keypath="cond ? 'a' : 'b'"`: the expression is read as the key argument of a translate call. */
|
|
615
|
-
keyExpression(content, offset) {
|
|
616
|
-
const callee = "__x18nKey(";
|
|
617
|
-
try {
|
|
618
|
-
const result = scanSource(this.parse, `${callee}${content})`, {
|
|
619
|
-
calls: [{ callee: "__x18nKey" }],
|
|
620
|
-
offset: offset - 10,
|
|
621
|
-
filename: "template.ts",
|
|
622
|
-
hardStrings: false
|
|
623
|
-
});
|
|
624
|
-
merge(this.out, {
|
|
625
|
-
...result,
|
|
626
|
-
usages: result.usages.map(({ expression: _, ...usage }) => usage)
|
|
627
|
-
});
|
|
628
|
-
} catch {}
|
|
629
|
-
}
|
|
630
|
-
/** `v-t="'key'"` and `v-t="{ path: 'key' }"`. */
|
|
631
|
-
vt(dir) {
|
|
632
|
-
const exp = dir.exp;
|
|
633
|
-
if (!exp || !("content" in exp)) return;
|
|
634
|
-
const offset = exp.loc.start.offset;
|
|
635
|
-
const literal = /^\s*(['"`])(.*)\1\s*$/.exec(exp.content) ?? /path\s*:\s*(['"`])(.*?)\1/.exec(exp.content);
|
|
636
|
-
if (!literal) return;
|
|
637
|
-
const start = offset + exp.content.indexOf(literal[2], literal.index);
|
|
638
|
-
const range = {
|
|
639
|
-
start,
|
|
640
|
-
end: start + literal[2].length
|
|
641
|
-
};
|
|
642
|
-
this.out.usages.push({
|
|
643
|
-
raw: literal[2],
|
|
644
|
-
range,
|
|
645
|
-
kind: "call",
|
|
646
|
-
dynamic: false
|
|
647
|
-
});
|
|
648
|
-
this.out.keySlots.push(range);
|
|
649
|
-
}
|
|
650
|
-
attribute(element, prop) {
|
|
651
|
-
if (prop.type === ATTRIBUTE) {
|
|
652
|
-
const value = prop.value;
|
|
653
|
-
if (!value) return;
|
|
654
|
-
const range = {
|
|
655
|
-
start: value.loc.start.offset + 1,
|
|
656
|
-
end: value.loc.end.offset - 1
|
|
657
|
-
};
|
|
658
|
-
if (isKeyAttribute(element.tag, prop.name)) {
|
|
659
|
-
this.out.usages.push({
|
|
660
|
-
raw: value.content,
|
|
661
|
-
range,
|
|
662
|
-
kind: "call",
|
|
663
|
-
dynamic: false
|
|
664
|
-
});
|
|
665
|
-
this.out.keySlots.push(range);
|
|
666
|
-
} else if (isTextAttribute(prop.name, this.textAttributes) && /\p{L}/u.test(value.content)) this.out.hardStrings.push({
|
|
667
|
-
text: value.content,
|
|
668
|
-
range: {
|
|
669
|
-
start: prop.loc.start.offset,
|
|
670
|
-
end: prop.loc.end.offset
|
|
671
|
-
},
|
|
672
|
-
kind: "vue-attribute",
|
|
673
|
-
attribute: prop.name
|
|
674
|
-
});
|
|
675
|
-
return;
|
|
676
|
-
}
|
|
677
|
-
if (prop.name === "t") {
|
|
678
|
-
this.vt(prop);
|
|
679
|
-
return;
|
|
680
|
-
}
|
|
681
|
-
const exp = prop.exp;
|
|
682
|
-
if (exp && "content" in exp) {
|
|
683
|
-
if (prop.name === "bind" && prop.arg && "content" in prop.arg && isKeyAttribute(element.tag, prop.arg.content)) this.keyExpression(exp.content, exp.loc.start.offset);
|
|
684
|
-
else this.expression(exp.content, exp.loc.start.offset);
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
node(node) {
|
|
688
|
-
if (node.type === INTERPOLATION && "content" in node.content) this.expression(node.content.content, node.content.loc.start.offset);
|
|
689
|
-
if (node.type === TEXT) {
|
|
690
|
-
const text = node.content.trim();
|
|
691
|
-
if (text && /\p{L}/u.test(text)) {
|
|
692
|
-
const start = node.loc.start.offset + node.content.indexOf(text);
|
|
693
|
-
this.out.hardStrings.push({
|
|
694
|
-
text,
|
|
695
|
-
range: {
|
|
696
|
-
start,
|
|
697
|
-
end: start + text.length
|
|
698
|
-
},
|
|
699
|
-
kind: "vue-template-text"
|
|
700
|
-
});
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
if (node.type === ELEMENT) for (const prop of node.props) this.attribute(node, prop);
|
|
704
|
-
if ("children" in node) {
|
|
705
|
-
for (const child of node.children) if (typeof child === "object" && child.type !== SIMPLE_EXPRESSION) this.node(child);
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
};
|
|
709
|
-
/** Vue SFCs: `<script>` through the `js` scanner, template expressions, `v-t`, `<i18n-t keypath>`. */
|
|
710
|
-
function vue() {
|
|
711
|
-
return {
|
|
712
|
-
name: "x18n:scanner-vue",
|
|
713
|
-
scanners: [{
|
|
714
|
-
id: "vue",
|
|
715
|
-
extensions: ["vue"],
|
|
716
|
-
async scan(file, ctx) {
|
|
717
|
-
const [{ parse }, parseJs] = await Promise.all([loadVueCompiler(), loadParser()]);
|
|
718
|
-
const { descriptor } = parse(file.text, {
|
|
719
|
-
filename: file.path,
|
|
720
|
-
sourceMap: false,
|
|
721
|
-
ignoreEmpty: false
|
|
722
|
-
});
|
|
723
|
-
const template = new TemplateScan(parseJs, ctx.calls, ctx.config.extract.attributes);
|
|
724
|
-
for (const script of [descriptor.script, descriptor.scriptSetup]) {
|
|
725
|
-
if (!script) continue;
|
|
726
|
-
const lang = script.lang ?? "js";
|
|
727
|
-
merge(template.out, scanSource(parseJs, script.content, {
|
|
728
|
-
calls: ctx.calls,
|
|
729
|
-
ignoreCallees: ctx.config.extract.ignoreCallees,
|
|
730
|
-
textAttributes: ctx.config.extract.attributes,
|
|
731
|
-
offset: script.loc.start.offset,
|
|
732
|
-
filename: `script.${lang === "tsx" || lang === "jsx" ? lang : lang === "ts" ? "ts" : "js"}`
|
|
733
|
-
}));
|
|
734
|
-
}
|
|
735
|
-
if (descriptor.template?.ast) template.node(descriptor.template.ast);
|
|
736
|
-
return template.out;
|
|
737
|
-
}
|
|
738
|
-
}]
|
|
739
|
-
};
|
|
740
|
-
}
|
|
741
586
|
//#endregion
|
|
742
|
-
export {
|
|
587
|
+
export { loadParser as n, scanSource as r, isTextAttribute as t };
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { r as indentUnit } from "./shared-C7ceptwi.mjs";
|
|
2
|
+
import { t as editLiteral } from "./literal-gIdB1VGy.mjs";
|
|
3
|
+
import { diffText, errors, pathId } from "@x18n/core";
|
|
4
|
+
import { parseTree, printParseErrorCode } from "jsonc-parser";
|
|
5
|
+
//#region src/formats/shared/json.ts
|
|
6
|
+
function readTree(node, path, ranges) {
|
|
7
|
+
const out = {};
|
|
8
|
+
for (const property of node.children ?? []) {
|
|
9
|
+
const [keyNode, valueNode] = property.children ?? [];
|
|
10
|
+
if (!keyNode || !valueNode || typeof keyNode.value !== "string") continue;
|
|
11
|
+
const key = keyNode.value;
|
|
12
|
+
const childPath = [...path, key];
|
|
13
|
+
ranges.set(pathId(childPath), {
|
|
14
|
+
key: {
|
|
15
|
+
start: keyNode.offset + 1,
|
|
16
|
+
end: keyNode.offset + keyNode.length - 1
|
|
17
|
+
},
|
|
18
|
+
value: {
|
|
19
|
+
start: valueNode.offset,
|
|
20
|
+
end: valueNode.offset + valueNode.length
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
if (valueNode.type === "object") out[key] = readTree(valueNode, childPath, ranges);
|
|
24
|
+
else if (valueNode.type === "string" || valueNode.type === "number" || valueNode.type === "boolean" || valueNode.type === "null") out[key] = valueNode.value;
|
|
25
|
+
}
|
|
26
|
+
return out;
|
|
27
|
+
}
|
|
28
|
+
function objectOf(node) {
|
|
29
|
+
const items = [];
|
|
30
|
+
for (const property of node.children ?? []) {
|
|
31
|
+
const [keyNode, valueNode] = property.children ?? [];
|
|
32
|
+
if (!keyNode || !valueNode || typeof keyNode.value !== "string") continue;
|
|
33
|
+
items.push({
|
|
34
|
+
key: keyNode.value,
|
|
35
|
+
range: {
|
|
36
|
+
start: property.offset,
|
|
37
|
+
end: property.offset + property.length
|
|
38
|
+
},
|
|
39
|
+
value: valueNode.type === "object" ? objectOf(valueNode) : {
|
|
40
|
+
kind: "scalar",
|
|
41
|
+
range: {
|
|
42
|
+
start: valueNode.offset,
|
|
43
|
+
end: valueNode.offset + valueNode.length
|
|
44
|
+
},
|
|
45
|
+
quote: void 0
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
kind: "object",
|
|
51
|
+
range: {
|
|
52
|
+
start: node.offset,
|
|
53
|
+
end: node.offset + node.length
|
|
54
|
+
},
|
|
55
|
+
items
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/** The root object, or undefined for an empty file. */
|
|
59
|
+
function parseRoot(text, path, allowComments) {
|
|
60
|
+
const problems = [];
|
|
61
|
+
const root = parseTree(text, problems, {
|
|
62
|
+
allowTrailingComma: true,
|
|
63
|
+
disallowComments: !allowComments
|
|
64
|
+
});
|
|
65
|
+
if (problems.length > 0) {
|
|
66
|
+
const first = problems[0];
|
|
67
|
+
throw errors.X18N2002({
|
|
68
|
+
path,
|
|
69
|
+
reason: `${printParseErrorCode(first.error)} at offset ${first.offset}`
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
if (root?.type === "object") return root;
|
|
73
|
+
if (text.trim() === "") return void 0;
|
|
74
|
+
throw errors.X18N2003({ path });
|
|
75
|
+
}
|
|
76
|
+
function createJsonFormat(id, extensions, allowComments, defaultIndent) {
|
|
77
|
+
return {
|
|
78
|
+
id,
|
|
79
|
+
extensions,
|
|
80
|
+
parse(text, ctx) {
|
|
81
|
+
const root = parseRoot(text, ctx.path, allowComments);
|
|
82
|
+
const ranges = /* @__PURE__ */ new Map();
|
|
83
|
+
return {
|
|
84
|
+
tree: root ? readTree(root, [], ranges) : {},
|
|
85
|
+
ranges
|
|
86
|
+
};
|
|
87
|
+
},
|
|
88
|
+
edit(text, ops, ctx) {
|
|
89
|
+
const syntax = {
|
|
90
|
+
open: "{",
|
|
91
|
+
close: "}",
|
|
92
|
+
separator: ": ",
|
|
93
|
+
key: (key) => JSON.stringify(key),
|
|
94
|
+
leaf: (value) => JSON.stringify(value),
|
|
95
|
+
unit: indentUnit(text, ctx.indent ?? defaultIndent, " "),
|
|
96
|
+
trailingComma: false
|
|
97
|
+
};
|
|
98
|
+
let next = text.trim() === "" ? "{}\n" : text;
|
|
99
|
+
for (const op of ops) next = editLiteral(next, objectOf(parseRoot(next, ctx.path, allowComments)), op, syntax);
|
|
100
|
+
return diffText(text, next);
|
|
101
|
+
},
|
|
102
|
+
serialize(tree, ctx) {
|
|
103
|
+
const indent = ctx.indent ?? defaultIndent ?? 2;
|
|
104
|
+
return `${JSON.stringify(tree, null, indent === "tab" ? " " : indent)}\n`;
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
//#endregion
|
|
109
|
+
export { createJsonFormat as t };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//#region src/presets/shared/jsx-templates.ts
|
|
2
|
+
const jsxTemplates = [
|
|
3
|
+
{
|
|
4
|
+
template: "{t('{key}'{args})}",
|
|
5
|
+
kinds: ["jsx-text"]
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
template: "{attr}={t('{key}'{args})}",
|
|
9
|
+
kinds: ["jsx-attribute"]
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
template: "t('{key}'{args})",
|
|
13
|
+
kinds: ["js-string", "js-template"]
|
|
14
|
+
}
|
|
15
|
+
];
|
|
16
|
+
//#endregion
|
|
17
|
+
export { jsxTemplates as t };
|