@x18n/builtins 0.0.2 → 0.0.5

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.
@@ -0,0 +1,34 @@
1
+ import { Plugin } from "@x18n/core";
2
+ //#region src/formats/ecmascript.d.ts
3
+ export declare function ecmascript(): Plugin;
4
+ //#endregion
5
+ //#region src/formats/ftl.d.ts
6
+ export declare function ftl(): Plugin;
7
+ //#endregion
8
+ //#region src/formats/ini.d.ts
9
+ export declare function ini(): Plugin;
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 };
@@ -0,0 +1 @@
1
+ export { u as ecmascript, l as ftl, c as ini, o as json, a as json5, s as jsonc, i as php, r as po, n as properties, t as yaml } from "./formats-BAogSrvw.mjs";
@@ -0,0 +1,5 @@
1
+ import { Builtins } from "@x18n/core";
2
+ //#region src/index.d.ts
3
+ /** Plugins that are always on, and the presets that `'auto'` detects. */
4
+ export declare function createBuiltins(): Builtins;
5
+ //#endregion
package/dist/index.mjs ADDED
@@ -0,0 +1,38 @@
1
+ import { recommended } from "./checks.mjs";
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-BAogSrvw.mjs";
4
+ import { i as js, n as vue } from "./vue-5GFR5hft.mjs";
5
+ import { manifests } from "./manifests.mjs";
6
+ import { n as regex } from "./regex-B9TBGgXC.mjs";
7
+ import { t as presets } from "./presets-DNnE_2MQ.mjs";
8
+ import { reporters } from "./reporters.mjs";
9
+ import { t as svelte } from "./scanners-B27IEH4_.mjs";
10
+ //#region src/index.ts
11
+ /** Plugins that are always on, and the presets that `'auto'` detects. */
12
+ function createBuiltins() {
13
+ return {
14
+ plugins: [
15
+ manifests(),
16
+ json(),
17
+ jsonc(),
18
+ json5(),
19
+ yaml(),
20
+ po(),
21
+ properties(),
22
+ ftl(),
23
+ ini(),
24
+ php(),
25
+ ecmascript(),
26
+ pattern(),
27
+ js(),
28
+ vue(),
29
+ svelte(),
30
+ regex(),
31
+ recommended(),
32
+ reporters()
33
+ ],
34
+ presets
35
+ };
36
+ }
37
+ //#endregion
38
+ export { createBuiltins };
@@ -0,0 +1,10 @@
1
+ import { ManifestReader, Plugin } from "@x18n/core";
2
+ //#region src/manifests/index.d.ts
3
+ /** Adapted from i18n-ally (MIT), src/packagesParsers/ */
4
+ export declare const packageJson: ManifestReader;
5
+ export declare const composerJson: ManifestReader;
6
+ export declare const pubspecYaml: ManifestReader;
7
+ /** Every `gem 'name'` line, not just `rails-i18n` as in i18n-ally. */
8
+ export declare const gemfile: ManifestReader;
9
+ export declare function manifests(): Plugin;
10
+ //#endregion
@@ -0,0 +1,49 @@
1
+ import { parse } from "yaml";
2
+ //#region src/manifests/index.ts
3
+ function keysOf(value) {
4
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? Object.keys(value) : [];
5
+ }
6
+ function fieldsOf(data, fields) {
7
+ if (typeof data !== "object" || data === null) return [];
8
+ const entries = new Map(Object.entries(data));
9
+ return fields.flatMap((field) => keysOf(entries.get(field)));
10
+ }
11
+ /** Adapted from i18n-ally (MIT), src/packagesParsers/ */
12
+ const packageJson = {
13
+ id: "package.json",
14
+ file: "package.json",
15
+ read: (text) => fieldsOf(JSON.parse(text), [
16
+ "dependencies",
17
+ "devDependencies",
18
+ "peerDependencies"
19
+ ])
20
+ };
21
+ const composerJson = {
22
+ id: "composer.json",
23
+ file: "composer.json",
24
+ read: (text) => fieldsOf(JSON.parse(text), ["require", "require-dev"])
25
+ };
26
+ const pubspecYaml = {
27
+ id: "pubspec.yaml",
28
+ file: "pubspec.yaml",
29
+ read: (text) => fieldsOf(parse(text), ["dependencies", "dev_dependencies"])
30
+ };
31
+ /** Every `gem 'name'` line, not just `rails-i18n` as in i18n-ally. */
32
+ const gemfile = {
33
+ id: "Gemfile",
34
+ file: "Gemfile",
35
+ read: (text) => [...text.matchAll(/^\s*gem\s+["']([^"']+)["']/gm)].map((match) => match[1])
36
+ };
37
+ function manifests() {
38
+ return {
39
+ name: "x18n:manifests",
40
+ manifests: [
41
+ packageJson,
42
+ composerJson,
43
+ pubspecYaml,
44
+ gemfile
45
+ ]
46
+ };
47
+ }
48
+ //#endregion
49
+ export { composerJson, gemfile, manifests, packageJson, pubspecYaml };
@@ -0,0 +1,27 @@
1
+ import { PlaceholderSyntax, Plugin } from "@x18n/core";
2
+ //#region src/placeholders/index.d.ts
3
+ /** `{name}`: vue-i18n named interpolation and similar. */
4
+ export declare const braces: PlaceholderSyntax;
5
+ /** `{{name}}`: i18next interpolation, including `{{name, format}}` and `{{- raw}}`. */
6
+ export declare const doubleBraces: PlaceholderSyntax;
7
+ /** `%{name}`: Rails, Polyglot. */
8
+ export declare const percentBraces: PlaceholderSyntax;
9
+ /** `%s`, `%d`, `%1$s`: printf. */
10
+ export declare const printf: PlaceholderSyntax;
11
+ /** `@:key` and `@.modifier:key`: vue-i18n linked messages. */
12
+ export declare const linked: PlaceholderSyntax;
13
+ /** `{ $name }`: Fluent variables. */
14
+ export declare const fluent: PlaceholderSyntax;
15
+ /** `:name`: Laravel. */
16
+ export declare const colon: PlaceholderSyntax;
17
+ /** `$name$`: Chrome extension messages. */
18
+ export declare const dollars: PlaceholderSyntax;
19
+ /** `$t(key)` nesting in i18next. */
20
+ export declare const nesting: PlaceholderSyntax;
21
+ /** ICU MessageFormat arguments: `{name}`, `{count, plural, ...}`. Nested sub-messages are scanned too. */
22
+ export declare const icu: PlaceholderSyntax;
23
+ /** Registers placeholder syntaxes. Presets call it with their library's syntaxes. */
24
+ export declare function placeholders(...syntaxes: PlaceholderSyntax[]): Plugin;
25
+ /** A syntax from a YAML shape such as `{name}`, `{{name}}` or `%{name}`, where `name` marks the name. */
26
+ export declare function placeholderFromShape(shape: string): PlaceholderSyntax;
27
+ //#endregion
@@ -0,0 +1,131 @@
1
+ //#region src/placeholders/index.ts
2
+ function byRegex(id, regex, example) {
3
+ return {
4
+ id,
5
+ example,
6
+ find(value) {
7
+ const out = [];
8
+ for (const match of value.matchAll(regex)) out.push({
9
+ syntax: id,
10
+ text: match[0],
11
+ name: match[1] ?? match[0],
12
+ range: {
13
+ start: match.index,
14
+ end: match.index + match[0].length
15
+ }
16
+ });
17
+ return out;
18
+ }
19
+ };
20
+ }
21
+ /** `{name}`: vue-i18n named interpolation and similar. */
22
+ const braces = byRegex("braces", /(?<![{$])\{\s*([\w.-]+)\s*\}(?!\})/g, "{name}");
23
+ /** `{{name}}`: i18next interpolation, including `{{name, format}}` and `{{- raw}}`. */
24
+ const doubleBraces = byRegex("double-braces", /\{\{-?\s*([\w.]+)\s*(?:,[^}]*)?\}\}/g, "{{name}}");
25
+ /** `%{name}`: Rails, Polyglot. */
26
+ const percentBraces = byRegex("percent-braces", /%\{(\w+)\}/g, "%{name}");
27
+ /** `%s`, `%d`, `%1$s`: printf. */
28
+ const printf = byRegex("printf", /%(?:(\d+)\$)?[-+ 0#]*(?:[1-9]\d*)?(?:\.\d+)?[sdifoxXeEgGc@]/g, "%s");
29
+ /** `@:key` and `@.modifier:key`: vue-i18n linked messages. */
30
+ const linked = {
31
+ ...byRegex("linked", /@(?:\.\w+)?:\(?([\w.-]+)\)?/g, "@:key"),
32
+ reference: true
33
+ };
34
+ /** `{ $name }`: Fluent variables. */
35
+ const fluent = byRegex("fluent", /\{\s*\$([\w-]+)(?:\s[^}]*)?\}/g, "{ $name }");
36
+ /** `:name`: Laravel. */
37
+ const colon = byRegex("colon", /(?<![\w:]):([a-z_]\w*)/gi, ":name");
38
+ /** `$name$`: Chrome extension messages. */
39
+ const dollars = byRegex("dollars", /\$(\w+)\$/g, "$name$");
40
+ /** `$t(key)` nesting in i18next. */
41
+ const nesting = {
42
+ ...byRegex("nesting", /\$t\(([\w.:-]+)(?:,[^)]*)?\)/g, "$t(key)"),
43
+ reference: true
44
+ };
45
+ /** Offset after the `}` that closes the `{` at `start`, or -1. */
46
+ function closingBrace(value, start, to) {
47
+ let depth = 1;
48
+ let j = start + 1;
49
+ while (j < to && depth > 0) {
50
+ if (value[j] === "{") depth++;
51
+ else if (value[j] === "}") depth--;
52
+ j++;
53
+ }
54
+ return depth > 0 ? -1 : j;
55
+ }
56
+ /** ICU MessageFormat arguments: `{name}`, `{count, plural, ...}`. Nested sub-messages are scanned too. */
57
+ const icu = {
58
+ id: "icu",
59
+ example: "{name}",
60
+ find(value) {
61
+ const out = [];
62
+ const scan = (from, to) => {
63
+ let i = from;
64
+ while (i < to) {
65
+ const char = value[i];
66
+ if (char === "'") {
67
+ const close = value.indexOf("'", i + 1);
68
+ if (value[i + 1] === "{" || value[i + 1] === "}") i = close === -1 ? to : close + 1;
69
+ else i++;
70
+ continue;
71
+ }
72
+ if (char !== "{") {
73
+ i++;
74
+ continue;
75
+ }
76
+ const j = closingBrace(value, i, to);
77
+ if (j === -1) return;
78
+ const body = value.slice(i + 1, j - 1);
79
+ const name = /^\s*([\w.-]+)/.exec(body)?.[1];
80
+ if (name) out.push({
81
+ syntax: "icu",
82
+ text: value.slice(i, j),
83
+ name,
84
+ range: {
85
+ start: i,
86
+ end: j
87
+ }
88
+ });
89
+ const firstSub = body.indexOf("{");
90
+ if (firstSub !== -1) scan(i + 1 + firstSub, j - 1);
91
+ i = j;
92
+ }
93
+ };
94
+ scan(0, value.length);
95
+ return out;
96
+ },
97
+ validate(value) {
98
+ let depth = 0;
99
+ let quoted = false;
100
+ for (let i = 0; i < value.length; i++) {
101
+ const char = value[i];
102
+ if (char === "'" && (value[i + 1] === "{" || value[i + 1] === "}" || quoted)) {
103
+ quoted = !quoted;
104
+ continue;
105
+ }
106
+ if (quoted) continue;
107
+ if (char === "{") depth++;
108
+ if (char === "}") depth--;
109
+ if (depth < 0) return [`Unexpected "}" at ${i}`];
110
+ }
111
+ if (depth > 0) return ["Unclosed \"{\""];
112
+ const errors = [];
113
+ for (const match of value.matchAll(/\{\s*([\w.-]+)\s*,\s*(plural|select|selectordinal)\s*,([\s\S]*)\}/g)) if (!/\bother\s*\{/.test(match[3] ?? "")) errors.push(`"${match[1]}" ${match[2]} has no "other" case`);
114
+ return errors;
115
+ }
116
+ };
117
+ /** Registers placeholder syntaxes. Presets call it with their library's syntaxes. */
118
+ function placeholders(...syntaxes) {
119
+ return {
120
+ name: `x18n:placeholders-${syntaxes.map((s) => s.id).join("-")}`,
121
+ placeholders: syntaxes
122
+ };
123
+ }
124
+ /** A syntax from a YAML shape such as `{name}`, `{{name}}` or `%{name}`, where `name` marks the name. */
125
+ function placeholderFromShape(shape) {
126
+ const [before, after] = shape.split("name");
127
+ const escape = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
128
+ return byRegex(`shape:${shape}`, new RegExp(`${escape(before ?? "")}\\s*([\\w.-]+)\\s*${escape(after ?? "")}`, "g"), shape);
129
+ }
130
+ //#endregion
131
+ export { braces, colon, dollars, doubleBraces, fluent, icu, linked, nesting, percentBraces, placeholderFromShape, placeholders, printf };