@x18n/builtins 0.0.14 → 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/checks.d.mts +1 -0
- package/dist/checks.mjs +26 -1
- package/dist/formats/ecmascript.mjs +72 -51
- package/dist/formats/ftl.mjs +1 -1
- package/dist/formats/ini.mjs +1 -1
- package/dist/formats/json.mjs +1 -1
- package/dist/formats/json5.mjs +126 -13
- package/dist/formats/jsonc.mjs +1 -1
- package/dist/formats/php.mjs +28 -30
- package/dist/formats/po.mjs +1 -1
- package/dist/formats/properties.mjs +1 -1
- package/dist/formats/resx.mjs +1 -1
- package/dist/formats/yaml.mjs +28 -1
- package/dist/index.mjs +2 -0
- package/dist/json-CGNSGS97.mjs +109 -0
- package/dist/literal-gIdB1VGy.mjs +125 -0
- package/dist/presets/ruby-i18n-js.d.mts +5 -0
- package/dist/presets/{i18n-js.mjs → ruby-i18n-js.mjs} +5 -5
- package/dist/presets.mjs +49 -49
- package/dist/reviews.d.mts +6 -0
- package/dist/reviews.mjs +35 -0
- package/dist/{shared-BWaXUmwA.mjs → shared-C7ceptwi.mjs} +5 -20
- package/package.json +4 -3
- package/dist/json-Db-8qSbE.mjs +0 -73
- package/dist/presets/i18n-js.d.mts +0 -5
package/dist/checks.d.mts
CHANGED
|
@@ -3,6 +3,7 @@ import { Check, Plugin } from "@x18n/core";
|
|
|
3
3
|
export declare const missingKey: Check;
|
|
4
4
|
export declare const missingTranslation: Check;
|
|
5
5
|
export declare const staleTranslation: Check;
|
|
6
|
+
export declare const reviewChangesRequested: Check;
|
|
6
7
|
export declare const emptyTranslation: Check;
|
|
7
8
|
export declare const redundantTranslation: Check;
|
|
8
9
|
export declare const keyConflict: Check;
|
package/dist/checks.mjs
CHANGED
|
@@ -88,6 +88,30 @@ const staleTranslation = {
|
|
|
88
88
|
};
|
|
89
89
|
}
|
|
90
90
|
};
|
|
91
|
+
const reviewChangesRequested = {
|
|
92
|
+
id: "review-changes-requested",
|
|
93
|
+
defaultSeverity: "info",
|
|
94
|
+
description: "A message with an open \"request change\" review.",
|
|
95
|
+
*run(ctx) {
|
|
96
|
+
for (const thread of ctx.reviews({ open: true })) {
|
|
97
|
+
if (thread.state !== "changes-requested") continue;
|
|
98
|
+
const request = thread.comments.findLast((comment) => comment.type === "request-change");
|
|
99
|
+
const by = request.author?.name ? ` by ${request.author.name}` : "";
|
|
100
|
+
yield {
|
|
101
|
+
key: thread.key,
|
|
102
|
+
locale: thread.locale,
|
|
103
|
+
message: `Change requested${by} for "${thread.key}" in ${thread.locale}${request.text ? `: ${request.text}` : "."}`,
|
|
104
|
+
fixes: [{
|
|
105
|
+
title: "Resolve the review",
|
|
106
|
+
ops: [{
|
|
107
|
+
type: "resolve-review",
|
|
108
|
+
id: request.id
|
|
109
|
+
}]
|
|
110
|
+
}]
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
};
|
|
91
115
|
const emptyTranslation = {
|
|
92
116
|
id: "empty-translation",
|
|
93
117
|
defaultSeverity: "warning",
|
|
@@ -361,6 +385,7 @@ const allChecks = [
|
|
|
361
385
|
keyConflict,
|
|
362
386
|
missingTranslation,
|
|
363
387
|
staleTranslation,
|
|
388
|
+
reviewChangesRequested,
|
|
364
389
|
emptyTranslation,
|
|
365
390
|
redundantTranslation,
|
|
366
391
|
unusedKey,
|
|
@@ -379,4 +404,4 @@ function recommended() {
|
|
|
379
404
|
};
|
|
380
405
|
}
|
|
381
406
|
//#endregion
|
|
382
|
-
export { allChecks, dynamicKey, emptyTranslation, glossary, hardString, identicalToSource, invalidSyntax, keyConflict, keyStyle, localeCodeConflict, missingKey, missingTranslation, placeholderMismatch, recommended, redundantTranslation, staleTranslation, unusedKey };
|
|
407
|
+
export { allChecks, dynamicKey, emptyTranslation, glossary, hardString, identicalToSource, invalidSyntax, keyConflict, keyStyle, localeCodeConflict, missingKey, missingTranslation, placeholderMismatch, recommended, redundantTranslation, reviewChangesRequested, staleTranslation, unusedKey };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { r as indentUnit } from "../shared-C7ceptwi.mjs";
|
|
2
|
+
import { n as literalText, t as editLiteral } from "../literal-gIdB1VGy.mjs";
|
|
3
|
+
import { applyMessageOps, diffText, errors, pathId } from "@x18n/core";
|
|
3
4
|
import { parseSync } from "oxc-parser";
|
|
4
5
|
//#region src/formats/ecmascript/index.ts
|
|
5
6
|
/** Type-only wrappers, `defineX({ ... })` helpers that return their argument, and loaders that only return an object (`defineI18nLocale(() => ({ ... }))`). */
|
|
@@ -71,46 +72,71 @@ function readLeaf(node, text) {
|
|
|
71
72
|
quote: void 0
|
|
72
73
|
};
|
|
73
74
|
}
|
|
74
|
-
function readObject(node, path,
|
|
75
|
-
const
|
|
75
|
+
function readObject(node, path, ranges, text, fail) {
|
|
76
|
+
const tree = {};
|
|
77
|
+
const items = [];
|
|
76
78
|
for (const property of node.properties) {
|
|
77
79
|
const { key, range, value } = readProperty(property, fail);
|
|
78
80
|
const childPath = [...path, key];
|
|
79
|
-
const
|
|
81
|
+
const item = {
|
|
82
|
+
key,
|
|
83
|
+
range: {
|
|
84
|
+
start: property.start,
|
|
85
|
+
end: property.end
|
|
86
|
+
}
|
|
87
|
+
};
|
|
80
88
|
if (value.type === "ObjectExpression") {
|
|
81
|
-
|
|
89
|
+
ranges.set(pathId(childPath), {
|
|
82
90
|
key: range,
|
|
83
91
|
value: {
|
|
84
92
|
start: value.start,
|
|
85
93
|
end: value.end
|
|
86
94
|
}
|
|
87
95
|
});
|
|
88
|
-
|
|
96
|
+
const child = readObject(value, childPath, ranges, text, fail);
|
|
97
|
+
tree[key] = child.tree;
|
|
98
|
+
items.push({
|
|
99
|
+
...item,
|
|
100
|
+
value: child.literal
|
|
101
|
+
});
|
|
89
102
|
continue;
|
|
90
103
|
}
|
|
91
104
|
const leaf = readLeaf(value, text);
|
|
92
105
|
if (!leaf) return fail("X18N2004", value, `${value.type} is not a static value`);
|
|
93
|
-
|
|
94
|
-
module.ranges.set(id, {
|
|
106
|
+
ranges.set(pathId(childPath), {
|
|
95
107
|
key: range,
|
|
96
|
-
value:
|
|
97
|
-
start: value.start + 1,
|
|
98
|
-
end: value.end - 1
|
|
99
|
-
} : {
|
|
108
|
+
value: leaf.quote === void 0 ? {
|
|
100
109
|
start: value.start,
|
|
101
110
|
end: value.end
|
|
111
|
+
} : {
|
|
112
|
+
start: value.start + 1,
|
|
113
|
+
end: value.end - 1
|
|
102
114
|
}
|
|
103
115
|
});
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
116
|
+
tree[key] = leaf.value;
|
|
117
|
+
items.push({
|
|
118
|
+
...item,
|
|
119
|
+
value: {
|
|
120
|
+
kind: "scalar",
|
|
121
|
+
range: {
|
|
122
|
+
start: value.start,
|
|
123
|
+
end: value.end
|
|
124
|
+
},
|
|
125
|
+
quote: leaf.quote
|
|
126
|
+
}
|
|
110
127
|
});
|
|
111
|
-
out[key] = leaf.value;
|
|
112
128
|
}
|
|
113
|
-
return
|
|
129
|
+
return {
|
|
130
|
+
tree,
|
|
131
|
+
literal: {
|
|
132
|
+
kind: "object",
|
|
133
|
+
range: {
|
|
134
|
+
start: node.start,
|
|
135
|
+
end: node.end
|
|
136
|
+
},
|
|
137
|
+
items
|
|
138
|
+
}
|
|
139
|
+
};
|
|
114
140
|
}
|
|
115
141
|
function readModule(text, path) {
|
|
116
142
|
const fail = (code, node, reason) => {
|
|
@@ -129,14 +155,15 @@ function readModule(text, path) {
|
|
|
129
155
|
reason: result.errors[0].message
|
|
130
156
|
});
|
|
131
157
|
const root = findRoot(result.program, fail);
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
158
|
+
const ranges = /* @__PURE__ */ new Map();
|
|
159
|
+
if (!root) return {
|
|
160
|
+
tree: {},
|
|
161
|
+
ranges,
|
|
162
|
+
literal: void 0
|
|
135
163
|
};
|
|
136
164
|
return {
|
|
137
|
-
root,
|
|
138
|
-
|
|
139
|
-
...found
|
|
165
|
+
...readObject(root, [], ranges, text, fail),
|
|
166
|
+
ranges
|
|
140
167
|
};
|
|
141
168
|
}
|
|
142
169
|
const singleEncodings = {
|
|
@@ -153,29 +180,23 @@ function literal(value, quote) {
|
|
|
153
180
|
if (quote === "`") return `\`${value.replace(/\\|`|\$\{/g, (match) => `\\${match}`)}\``;
|
|
154
181
|
return `'${value.replace(/[\\'\n\r\u2028\u2029]/g, (char) => singleEncodings[char])}'`;
|
|
155
182
|
}
|
|
156
|
-
function
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
function unitOf(text, indent) {
|
|
167
|
-
if (indent !== void 0) return indent === "tab" ? " " : " ".repeat(indent);
|
|
168
|
-
return /^([ \t]+)\S/m.exec(text)?.[1] ?? " ";
|
|
183
|
+
function syntaxOf(text, indent) {
|
|
184
|
+
return {
|
|
185
|
+
open: "{",
|
|
186
|
+
close: "}",
|
|
187
|
+
separator: ": ",
|
|
188
|
+
key: (key) => /^[a-z_$][\w$]*$/i.test(key) ? key : literal(key, "'"),
|
|
189
|
+
leaf: (value, quote) => literal(value, quote ?? "'"),
|
|
190
|
+
unit: indentUnit(text, indent, " "),
|
|
191
|
+
trailingComma: true
|
|
192
|
+
};
|
|
169
193
|
}
|
|
170
|
-
function moduleText(tree, path,
|
|
171
|
-
return `${path.endsWith(".cjs") ? "module.exports =" : "export default"} ${
|
|
194
|
+
function moduleText(tree, path, syntax) {
|
|
195
|
+
return `${path.endsWith(".cjs") ? "module.exports =" : "export default"} ${literalText(tree, syntax)}\n`;
|
|
172
196
|
}
|
|
173
|
-
function applyOp(text, op, path,
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
if (op.type === "set" && scalar) return splice(text, scalar.range, literal(op.value, scalar.quote));
|
|
177
|
-
const tree = applyMessageOps(module.tree, [op]);
|
|
178
|
-
return module.root ? splice(text, module.root, objectText(tree, unit, 0)) : moduleText(tree, path, unit);
|
|
197
|
+
function applyOp(text, op, path, syntax) {
|
|
198
|
+
const root = readModule(text, path).literal;
|
|
199
|
+
return root ? editLiteral(text, root, op, syntax) : moduleText(applyMessageOps({}, [op]), path, syntax);
|
|
179
200
|
}
|
|
180
201
|
function ecmascript() {
|
|
181
202
|
return {
|
|
@@ -198,12 +219,12 @@ function ecmascript() {
|
|
|
198
219
|
};
|
|
199
220
|
},
|
|
200
221
|
edit(text, ops, ctx) {
|
|
201
|
-
const
|
|
222
|
+
const syntax = syntaxOf(text, ctx.indent);
|
|
202
223
|
let next = text;
|
|
203
|
-
for (const op of ops) next = applyOp(next, op, ctx.path,
|
|
224
|
+
for (const op of ops) next = applyOp(next, op, ctx.path, syntax);
|
|
204
225
|
return diffText(text, next);
|
|
205
226
|
},
|
|
206
|
-
serialize: (tree, ctx) => moduleText(tree, ctx.path,
|
|
227
|
+
serialize: (tree, ctx) => moduleText(tree, ctx.path, syntaxOf("", ctx.indent))
|
|
207
228
|
}]
|
|
208
229
|
};
|
|
209
230
|
}
|
package/dist/formats/ftl.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as splice, c as withBlankLines, i as leafText, n as detectEol, o as splitLines, t as appendLines } from "../shared-
|
|
1
|
+
import { a as splice, c as withBlankLines, i as leafText, n as detectEol, o as splitLines, t as appendLines } from "../shared-C7ceptwi.mjs";
|
|
2
2
|
import { diffText, errors, pathId, walkLeaves } from "@x18n/core";
|
|
3
3
|
//#region src/formats/ftl/index.ts
|
|
4
4
|
const trimmedEnd = (text, line) => line.start + text.slice(line.start, line.end).trimEnd().length;
|
package/dist/formats/ini.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as splice, i as leafText, n as detectEol, o as splitLines, t as appendLines } from "../shared-
|
|
1
|
+
import { a as splice, i as leafText, n as detectEol, o as splitLines, t as appendLines } from "../shared-C7ceptwi.mjs";
|
|
2
2
|
import { buildTree, diffText, errors, isMessageLeaf, pathId } from "@x18n/core";
|
|
3
3
|
//#region src/formats/ini/index.ts
|
|
4
4
|
/** Reads the value after `=`; `start` is its first non-blank offset. */
|
package/dist/formats/json.mjs
CHANGED
package/dist/formats/json5.mjs
CHANGED
|
@@ -1,6 +1,115 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { r as indentUnit } from "../shared-C7ceptwi.mjs";
|
|
2
|
+
import { t as editLiteral } from "../literal-gIdB1VGy.mjs";
|
|
3
|
+
import { diffText, errors, isMessageTree, pathId } from "@x18n/core";
|
|
2
4
|
import JSON5 from "json5";
|
|
3
5
|
//#region src/formats/json5/index.ts
|
|
6
|
+
const stringToken = /^(?:"(?:[^"\\]|\\[\s\S])*"|'(?:[^'\\]|\\[\s\S])*')/;
|
|
7
|
+
function parse(text, path) {
|
|
8
|
+
let data;
|
|
9
|
+
try {
|
|
10
|
+
data = JSON5.parse(text);
|
|
11
|
+
} catch (error) {
|
|
12
|
+
throw errors.X18N2002({
|
|
13
|
+
path,
|
|
14
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
15
|
+
cause: error
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
if (!isMessageTree(data)) throw errors.X18N2003({ path });
|
|
19
|
+
return data;
|
|
20
|
+
}
|
|
21
|
+
/** Where the keys and values of valid JSON5 that holds a message tree are. */
|
|
22
|
+
function scan(text) {
|
|
23
|
+
const ranges = /* @__PURE__ */ new Map();
|
|
24
|
+
let pos = 0;
|
|
25
|
+
const skip = () => {
|
|
26
|
+
pos += /^(?:\s|\/\/.*|\/\*[\s\S]*?\*\/)*/.exec(text.slice(pos))[0].length;
|
|
27
|
+
};
|
|
28
|
+
const token = (pattern) => {
|
|
29
|
+
const start = pos;
|
|
30
|
+
pos += pattern.exec(text.slice(pos))[0].length;
|
|
31
|
+
return {
|
|
32
|
+
start,
|
|
33
|
+
end: pos
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
const inner = (range, quoted) => quoted ? {
|
|
37
|
+
start: range.start + 1,
|
|
38
|
+
end: range.end - 1
|
|
39
|
+
} : range;
|
|
40
|
+
const scalar = () => {
|
|
41
|
+
const quote = text[pos] === "\"" || text[pos] === "'" ? text[pos] : void 0;
|
|
42
|
+
return {
|
|
43
|
+
kind: "scalar",
|
|
44
|
+
range: token(quote ? stringToken : /^[^\s,}/]+/),
|
|
45
|
+
quote
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
const object = (path) => {
|
|
49
|
+
const start = pos++;
|
|
50
|
+
const items = [];
|
|
51
|
+
skip();
|
|
52
|
+
while (text[pos] !== "}") {
|
|
53
|
+
const quoted = text[pos] === "\"" || text[pos] === "'";
|
|
54
|
+
const keyRange = token(quoted ? stringToken : /^[^\s:/]+/);
|
|
55
|
+
const key = Object.keys(JSON5.parse(`{${text.slice(keyRange.start, keyRange.end)}:0}`))[0];
|
|
56
|
+
skip();
|
|
57
|
+
pos++;
|
|
58
|
+
skip();
|
|
59
|
+
const value = text[pos] === "{" ? object([...path, key]) : scalar();
|
|
60
|
+
ranges.set(pathId([...path, key]), {
|
|
61
|
+
key: inner(keyRange, quoted),
|
|
62
|
+
value: inner(value.range, value.kind === "scalar" && value.quote !== void 0)
|
|
63
|
+
});
|
|
64
|
+
items.push({
|
|
65
|
+
key,
|
|
66
|
+
range: {
|
|
67
|
+
start: keyRange.start,
|
|
68
|
+
end: value.range.end
|
|
69
|
+
},
|
|
70
|
+
value
|
|
71
|
+
});
|
|
72
|
+
skip();
|
|
73
|
+
if (text[pos] === ",") pos++;
|
|
74
|
+
skip();
|
|
75
|
+
}
|
|
76
|
+
pos++;
|
|
77
|
+
return {
|
|
78
|
+
kind: "object",
|
|
79
|
+
range: {
|
|
80
|
+
start,
|
|
81
|
+
end: pos
|
|
82
|
+
},
|
|
83
|
+
items
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
skip();
|
|
87
|
+
return {
|
|
88
|
+
root: object([]),
|
|
89
|
+
ranges
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function firstQuote(object) {
|
|
93
|
+
for (const { value } of object.items) {
|
|
94
|
+
const quote = value.kind === "scalar" ? value.quote : firstQuote(value);
|
|
95
|
+
if (quote) return quote;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/** New keys and strings follow the file: quoted keys if its first key is quoted, and the quote of its first string. */
|
|
99
|
+
function syntaxOf(text, root, indent) {
|
|
100
|
+
const first = root.items[0];
|
|
101
|
+
const keyQuote = first && /^["']/.test(text[first.range.start]) ? text[first.range.start] : void 0;
|
|
102
|
+
const quote = firstQuote(root) ?? "\"";
|
|
103
|
+
return {
|
|
104
|
+
open: "{",
|
|
105
|
+
close: "}",
|
|
106
|
+
separator: ": ",
|
|
107
|
+
key: (key) => keyQuote ? JSON5.stringify(key, { quote: keyQuote }) : /^[a-z_$][\w$]*$/i.test(key) ? key : JSON5.stringify(key, { quote }),
|
|
108
|
+
leaf: (value, current) => JSON5.stringify(value, { quote: current ?? quote }),
|
|
109
|
+
unit: indentUnit(text, indent, " "),
|
|
110
|
+
trailingComma: false
|
|
111
|
+
};
|
|
112
|
+
}
|
|
4
113
|
function json5() {
|
|
5
114
|
return {
|
|
6
115
|
name: "x18n:format-json5",
|
|
@@ -8,19 +117,23 @@ function json5() {
|
|
|
8
117
|
id: "json5",
|
|
9
118
|
extensions: ["json5"],
|
|
10
119
|
parse(text, ctx) {
|
|
11
|
-
if (text.trim() === "") return {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
120
|
+
if (text.trim() === "") return {
|
|
121
|
+
tree: {},
|
|
122
|
+
ranges: /* @__PURE__ */ new Map()
|
|
123
|
+
};
|
|
124
|
+
return {
|
|
125
|
+
tree: parse(text, ctx.path),
|
|
126
|
+
ranges: scan(text).ranges
|
|
127
|
+
};
|
|
128
|
+
},
|
|
129
|
+
edit(text, ops, ctx) {
|
|
130
|
+
let next = text.trim() === "" ? "{}\n" : text;
|
|
131
|
+
for (const op of ops) {
|
|
132
|
+
parse(next, ctx.path);
|
|
133
|
+
const { root } = scan(next);
|
|
134
|
+
next = editLiteral(next, root, op, syntaxOf(next, root, ctx.indent));
|
|
21
135
|
}
|
|
22
|
-
|
|
23
|
-
return { tree: data };
|
|
136
|
+
return diffText(text, next);
|
|
24
137
|
},
|
|
25
138
|
serialize: (tree, ctx) => `${JSON5.stringify(tree, null, ctx.indent === "tab" ? " " : ctx.indent ?? 2)}\n`
|
|
26
139
|
}]
|
package/dist/formats/jsonc.mjs
CHANGED
package/dist/formats/php.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { applyMessageOps, diffText, errors,
|
|
1
|
+
import { n as literalText, t as editLiteral } from "../literal-gIdB1VGy.mjs";
|
|
2
|
+
import { applyMessageOps, diffText, errors, pathId } from "@x18n/core";
|
|
3
3
|
//#region src/formats/php/index.ts
|
|
4
4
|
const doubleEscapes = {
|
|
5
5
|
"n": "\n",
|
|
@@ -68,20 +68,25 @@ function parseFile(text, path) {
|
|
|
68
68
|
const key = parseValue();
|
|
69
69
|
if (!eat(/^=>/)) return fail("X18N2003", "array item without a key");
|
|
70
70
|
if (key.kind !== "scalar" || typeof key.value !== "string" && typeof key.value !== "number") return fail("X18N2004", "array keys must be strings or integers");
|
|
71
|
+
const value = parseValue();
|
|
71
72
|
items.push({
|
|
72
73
|
key: String(key.value),
|
|
74
|
+
range: {
|
|
75
|
+
start: key.range.start,
|
|
76
|
+
end: value.range.end
|
|
77
|
+
},
|
|
73
78
|
keyRange: key.quote ? {
|
|
74
79
|
start: key.range.start + 1,
|
|
75
80
|
end: key.range.end - 1
|
|
76
81
|
} : key.range,
|
|
77
|
-
value
|
|
82
|
+
value
|
|
78
83
|
});
|
|
79
84
|
if (eat(/^,/)) continue;
|
|
80
85
|
skip();
|
|
81
86
|
if (text[pos] !== close) return fail("X18N2004", `expected "," or "${close}"; expressions are not static`);
|
|
82
87
|
}
|
|
83
88
|
return {
|
|
84
|
-
kind: "
|
|
89
|
+
kind: "object",
|
|
85
90
|
range: {
|
|
86
91
|
start,
|
|
87
92
|
end: pos
|
|
@@ -114,7 +119,7 @@ function parseFile(text, path) {
|
|
|
114
119
|
eat(/^declare\s*\([^)]*\)\s*;/i);
|
|
115
120
|
if (!eat(/^return\b/i)) fail("X18N2004", "expected `return [ ... ];`");
|
|
116
121
|
const root = parseValue();
|
|
117
|
-
if (root.kind !== "
|
|
122
|
+
if (root.kind !== "object") fail("X18N2003", "the file must return an array");
|
|
118
123
|
if (!eat(/^;/)) fail("X18N2004", "expected \";\" after the array; expressions are not static");
|
|
119
124
|
eat(/^\?>/);
|
|
120
125
|
if (eat(/^$/) === void 0) fail("X18N2002", "unexpected text after the array");
|
|
@@ -122,7 +127,7 @@ function parseFile(text, path) {
|
|
|
122
127
|
}
|
|
123
128
|
function readTree(value, path, ranges) {
|
|
124
129
|
const out = {};
|
|
125
|
-
if (value.kind !== "
|
|
130
|
+
if (value.kind !== "object") return out;
|
|
126
131
|
for (const item of value.items) {
|
|
127
132
|
const childPath = [...path, item.key];
|
|
128
133
|
const inner = item.value.kind === "scalar" && item.value.quote ? {
|
|
@@ -133,7 +138,7 @@ function readTree(value, path, ranges) {
|
|
|
133
138
|
key: item.keyRange,
|
|
134
139
|
value: inner
|
|
135
140
|
});
|
|
136
|
-
out[item.key] = item.value.kind === "
|
|
141
|
+
out[item.key] = item.value.kind === "object" ? readTree(item.value, childPath, ranges) : item.value.value;
|
|
137
142
|
}
|
|
138
143
|
return out;
|
|
139
144
|
}
|
|
@@ -149,28 +154,21 @@ function literal(value, quote) {
|
|
|
149
154
|
if (quote === "\"" || /[\n\r\t\v\f]/.test(value)) return `"${value.replace(/[\\"$\n\t\r\v\f]/g, (char) => doubleEncodings[char] ?? `\\${char}`)}"`;
|
|
150
155
|
return `'${value.replace(/[\\']/g, "\\$&")}'`;
|
|
151
156
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
}
|
|
163
|
-
/** Indent unit: configured, else the file's (first indented item), else 4 spaces. */
|
|
164
|
-
function unitOf(text, indent) {
|
|
165
|
-
if (indent !== void 0) return indent === "tab" ? " " : " ".repeat(indent);
|
|
166
|
-
return /^([ \t]+)['"\]]/m.exec(text)?.[1] ?? " ";
|
|
157
|
+
/** The indent unit is the configured one, else the file's (first indented item), else 4 spaces. */
|
|
158
|
+
function syntaxOf(text, indent) {
|
|
159
|
+
return {
|
|
160
|
+
open: "[",
|
|
161
|
+
close: "]",
|
|
162
|
+
separator: " => ",
|
|
163
|
+
key: (key) => literal(key, "'"),
|
|
164
|
+
leaf: (value, quote) => literal(value, quote),
|
|
165
|
+
unit: indent === void 0 ? /^([ \t]+)['"\]]/m.exec(text)?.[1] ?? " " : indent === "tab" ? " " : " ".repeat(indent),
|
|
166
|
+
trailingComma: true
|
|
167
|
+
};
|
|
167
168
|
}
|
|
168
|
-
function applyOp(text, op, path,
|
|
169
|
+
function applyOp(text, op, path, syntax) {
|
|
169
170
|
const root = parseFile(text, path);
|
|
170
|
-
|
|
171
|
-
if (op.type === "set" && node?.kind === "scalar") return splice(text, node.range, literal(op.value, node.quote));
|
|
172
|
-
const tree = applyMessageOps(root ? readTree(root, [], /* @__PURE__ */ new Map()) : {}, [op]);
|
|
173
|
-
return root ? splice(text, root.range, arrayText(tree, unit, 0)) : `${text.trim() === "" ? "<?php" : text.trimEnd()}\n\nreturn ${arrayText(tree, unit, 0)};\n`;
|
|
171
|
+
return root ? editLiteral(text, root, op, syntax) : `${text.trim() === "" ? "<?php" : text.trimEnd()}\n\nreturn ${literalText(applyMessageOps({}, [op]), syntax)};\n`;
|
|
174
172
|
}
|
|
175
173
|
function php() {
|
|
176
174
|
return {
|
|
@@ -187,12 +185,12 @@ function php() {
|
|
|
187
185
|
};
|
|
188
186
|
},
|
|
189
187
|
edit(text, ops, ctx) {
|
|
190
|
-
const
|
|
188
|
+
const syntax = syntaxOf(text, ctx.indent);
|
|
191
189
|
let next = text;
|
|
192
|
-
for (const op of ops) next = applyOp(next, op, ctx.path,
|
|
190
|
+
for (const op of ops) next = applyOp(next, op, ctx.path, syntax);
|
|
193
191
|
return diffText(text, next);
|
|
194
192
|
},
|
|
195
|
-
serialize: (tree, ctx) => `<?php\n\nreturn ${
|
|
193
|
+
serialize: (tree, ctx) => `<?php\n\nreturn ${literalText(tree, syntaxOf("", ctx.indent))};\n`
|
|
196
194
|
}]
|
|
197
195
|
};
|
|
198
196
|
}
|
package/dist/formats/po.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as splice, c as withBlankLines, i as leafText, n as detectEol, o as splitLines, t as appendLines } from "../shared-
|
|
1
|
+
import { a as splice, c as withBlankLines, i as leafText, n as detectEol, o as splitLines, t as appendLines } from "../shared-C7ceptwi.mjs";
|
|
2
2
|
import { diffText, errors, pathId, walkLeaves } from "@x18n/core";
|
|
3
3
|
//#region src/formats/po/index.ts
|
|
4
4
|
/** gettext joins msgctxt and msgid with EOT. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as splice, i as leafText, o as splitLines, t as appendLines } from "../shared-
|
|
1
|
+
import { a as splice, i as leafText, o as splitLines, t as appendLines } from "../shared-C7ceptwi.mjs";
|
|
2
2
|
import { diffText, errors, pathId, walkLeaves } from "@x18n/core";
|
|
3
3
|
//#region src/formats/properties/index.ts
|
|
4
4
|
const escapes = {
|
package/dist/formats/resx.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as splice, i as leafText, n as detectEol } from "../shared-
|
|
1
|
+
import { a as splice, i as leafText, n as detectEol } from "../shared-C7ceptwi.mjs";
|
|
2
2
|
import { diffText, errors, pathId, walkLeaves } from "@x18n/core";
|
|
3
3
|
//#region src/formats/resx/index.ts
|
|
4
4
|
const entities = {
|
package/dist/formats/yaml.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { s as toLeaf } from "../shared-
|
|
1
|
+
import { a as splice, s as toLeaf } from "../shared-C7ceptwi.mjs";
|
|
2
2
|
import { applyTextEdits, diffText, errors, pathId } from "@x18n/core";
|
|
3
3
|
import { Document, Scalar, isMap, isNode, isScalar, parseDocument, stringify } from "yaml";
|
|
4
4
|
//#region src/formats/yaml/index.ts
|
|
@@ -58,6 +58,26 @@ function scalarText(value, type) {
|
|
|
58
58
|
if (type === Scalar.QUOTE_SINGLE) return `'${value.replace(/'/g, "''")}'`;
|
|
59
59
|
return stringify(value, { lineWidth: 0 }).trimEnd();
|
|
60
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* Deletes a pair of a block map by its lines, so the comments around it stay where they are. The comment at the end of
|
|
63
|
+
* its last line goes with it. Undefined when the Document API must do it.
|
|
64
|
+
*/
|
|
65
|
+
function removePair(text, doc, path) {
|
|
66
|
+
const map = path.length === 1 ? doc.contents : doc.getIn(path.slice(0, -1), true);
|
|
67
|
+
if (!isMap(map)) return text;
|
|
68
|
+
const pair = map.items.findLast((item) => isScalar(item.key) && String(item.key.value) === path.at(-1));
|
|
69
|
+
const key = pair?.key;
|
|
70
|
+
if (!pair || !isScalar(key)) return text;
|
|
71
|
+
if (map.flow || !key.range || map.items.length === 1 && path.length > 1) return void 0;
|
|
72
|
+
const start = text.lastIndexOf("\n", key.range[0] - 1) + 1;
|
|
73
|
+
if (!/^[ \t]*$/.test(text.slice(start, key.range[0]))) return void 0;
|
|
74
|
+
const valueEnd = isNode(pair.value) && pair.value.range ? pair.value.range[1] : key.range[1];
|
|
75
|
+
const end = text[valueEnd - 1] === "\n" ? valueEnd : valueEnd + /^[ \t]*(?:#.*)?(?:\r?\n)?/.exec(text.slice(valueEnd))[0].length;
|
|
76
|
+
return splice(text, {
|
|
77
|
+
start,
|
|
78
|
+
end
|
|
79
|
+
}, "");
|
|
80
|
+
}
|
|
61
81
|
function indentOf(text) {
|
|
62
82
|
return /^( +)\S/m.exec(text)?.[1]?.length ?? 2;
|
|
63
83
|
}
|
|
@@ -95,6 +115,13 @@ function yaml() {
|
|
|
95
115
|
}]);
|
|
96
116
|
continue;
|
|
97
117
|
}
|
|
118
|
+
if (op.type === "delete") {
|
|
119
|
+
const removed = removePair(next, doc, op.path);
|
|
120
|
+
if (removed !== void 0) {
|
|
121
|
+
next = removed;
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
98
125
|
if (doc.contents === null) doc = new Document({});
|
|
99
126
|
if (op.type === "set") doc.setIn(op.path, op.value);
|
|
100
127
|
else doc.deleteIn(op.path);
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { recommended } from "./checks.mjs";
|
|
2
2
|
import { i as pattern } from "./finders-CqTGh4OJ.mjs";
|
|
3
3
|
import { manifests } from "./manifests.mjs";
|
|
4
|
+
import { reviews } from "./reviews.mjs";
|
|
4
5
|
import { formats } from "@x18n/builtins/formats";
|
|
5
6
|
import { presets } from "@x18n/builtins/presets";
|
|
6
7
|
import { reporters } from "@x18n/builtins/reporters";
|
|
@@ -35,6 +36,7 @@ async function createBuiltins() {
|
|
|
35
36
|
...scannerPlugins,
|
|
36
37
|
regex(),
|
|
37
38
|
recommended(),
|
|
39
|
+
reviews(),
|
|
38
40
|
reporterPlugin
|
|
39
41
|
],
|
|
40
42
|
presets
|