@saykit/config 0.7.0 → 0.8.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,5 +1,33 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_hash = require("../../hash-51ce8YiG.cjs");
|
|
3
|
+
//#region src/features/messages/escape.ts
|
|
4
|
+
/**
|
|
5
|
+
* Quoting for text that has to survive a message format unchanged.
|
|
6
|
+
*
|
|
7
|
+
* This lives beside the converter rather than in a transform because it belongs
|
|
8
|
+
* to the format being written, not to the syntax being read. A literal is the
|
|
9
|
+
* same literal whether it came from a template literal or from JSX, and the
|
|
10
|
+
* moment there is a second target format its reserved characters — and the way
|
|
11
|
+
* it spells an escape — are its own.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Escape literal text so ICU MessageFormat 1 reads it as the text it is.
|
|
15
|
+
*
|
|
16
|
+
* MF1 escapes with an apostrophe: a run wrapped in one is taken literally, and a
|
|
17
|
+
* doubled apostrophe is a single apostrophe. The subtlety is that an apostrophe
|
|
18
|
+
* only opens a quoted run when a character that needs quoting follows it —
|
|
19
|
+
* anything else and the apostrophe is itself literal. So `'#'` is a real escape
|
|
20
|
+
* inside a plural and the three characters `'#'` anywhere else, which is why
|
|
21
|
+
* `hash` has to be told rather than assumed.
|
|
22
|
+
*
|
|
23
|
+
* @param text Literal text as the author wrote it
|
|
24
|
+
* @param hash Whether `#` stands for a number here, i.e. whether some `plural`
|
|
25
|
+
* or `ordinal` encloses this text
|
|
26
|
+
*/
|
|
27
|
+
function escapeIcuLiteral(text, hash = false) {
|
|
28
|
+
return text.replace(/'/gu, "''").replace(hash ? /[{}#]+/gu : /[{}]+/gu, (run) => `'${run}'`);
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
3
31
|
//#region src/features/messages/types.ts
|
|
4
32
|
var Base = class {
|
|
5
33
|
toICUString() {
|
|
@@ -135,9 +163,28 @@ function assignSequenceIdentifiers(message, sequence = { current: 0 }, equivalen
|
|
|
135
163
|
while (reserved.has(identifier)) identifier = `${sequence.current++}`;
|
|
136
164
|
return identifier;
|
|
137
165
|
}
|
|
166
|
+
const numbered = {
|
|
167
|
+
tags: [],
|
|
168
|
+
values: []
|
|
169
|
+
};
|
|
170
|
+
/**
|
|
171
|
+
* The number this expression already has, or a fresh one. An anonymous
|
|
172
|
+
* placeholder is named by its position, so the same value written twice would
|
|
173
|
+
* otherwise arrive as two names — `{0} x {1}` for one length, which asks a
|
|
174
|
+
* translator to keep two holes in step and the caller to supply one value
|
|
175
|
+
* under two props. This is the rule explicit names already follow, where a
|
|
176
|
+
* repeat is allowed precisely when nothing distinguishes it.
|
|
177
|
+
*/
|
|
178
|
+
function number(assigned, expression) {
|
|
179
|
+
const seen = assigned.find(([e]) => equivalent(e, expression));
|
|
180
|
+
if (seen) return seen[1];
|
|
181
|
+
const identifier = next();
|
|
182
|
+
assigned.push([expression, identifier]);
|
|
183
|
+
return identifier;
|
|
184
|
+
}
|
|
138
185
|
function walk(message) {
|
|
139
186
|
if (message instanceof ArgumentMessage || message instanceof ElementMessage || message instanceof ChoiceMessage) {
|
|
140
|
-
if (message.identifier === AUTO_INCREMENT_IDENTIFIER) message.identifier =
|
|
187
|
+
if (message.identifier === AUTO_INCREMENT_IDENTIFIER) message.identifier = number(message instanceof ElementMessage ? numbered.tags : numbered.values, message.expression);
|
|
141
188
|
}
|
|
142
189
|
if (message instanceof CompositeMessage || message instanceof ElementMessage) for (const child of message.children) walk(child);
|
|
143
190
|
if (message instanceof ChoiceMessage) for (const branch of message.branches) {
|
|
@@ -177,10 +224,11 @@ function collectAssignedIdentifiers(message, equivalent) {
|
|
|
177
224
|
//#endregion
|
|
178
225
|
//#region src/features/messages/convert.ts
|
|
179
226
|
function convertMessageToIcu(message) {
|
|
180
|
-
function internalConvertMessageToIcu(message) {
|
|
227
|
+
function internalConvertMessageToIcu(message, hash) {
|
|
181
228
|
switch (true) {
|
|
182
|
-
case message instanceof LiteralMessage: return String(message.text);
|
|
229
|
+
case message instanceof LiteralMessage: return escapeIcuLiteral(String(message.text), hash !== void 0);
|
|
183
230
|
case message instanceof ArgumentMessage: {
|
|
231
|
+
if (!message.format && hash !== void 0 && String(message.identifier) === hash) return "#";
|
|
184
232
|
const parts = [String(message.identifier)];
|
|
185
233
|
if (message.format) parts.push(message.format.type);
|
|
186
234
|
if (message.format?.style) parts.push(message.format.style);
|
|
@@ -188,23 +236,24 @@ function convertMessageToIcu(message) {
|
|
|
188
236
|
}
|
|
189
237
|
case message instanceof ElementMessage: {
|
|
190
238
|
if (message.children.length === 0) return `<${String(message.identifier)}/>`;
|
|
191
|
-
const children = message.children.map((m) => internalConvertMessageToIcu(m)).join("");
|
|
239
|
+
const children = message.children.map((m) => internalConvertMessageToIcu(m, hash)).join("");
|
|
192
240
|
return `<${String(message.identifier)}>${children}</${String(message.identifier)}>`;
|
|
193
241
|
}
|
|
194
242
|
case message instanceof ChoiceMessage: {
|
|
243
|
+
const scope = message.kind === "select" ? hash : String(message.identifier);
|
|
195
244
|
const branches = message.branches.map(({ identifier, value }) => ({
|
|
196
245
|
identifier: getBranchCase(message.kind, identifier),
|
|
197
|
-
value: internalConvertMessageToIcu(value)
|
|
246
|
+
value: internalConvertMessageToIcu(value, scope)
|
|
198
247
|
})).map(({ identifier, value }) => ` ${identifier} {${value}}\n`).join("");
|
|
199
248
|
const format = message.kind === "ordinal" ? "selectordinal" : message.kind;
|
|
200
249
|
const offset = message.offset === void 0 || message.kind === "select" ? "" : ` offset:${message.offset}`;
|
|
201
250
|
return `{${String(message.identifier)}, ${format},${offset}\n${branches}}`;
|
|
202
251
|
}
|
|
203
|
-
case message instanceof CompositeMessage: return Object.entries(message.children).map(([, m]) => internalConvertMessageToIcu(m)).join("");
|
|
252
|
+
case message instanceof CompositeMessage: return Object.entries(message.children).map(([, m]) => internalConvertMessageToIcu(m, hash)).join("");
|
|
204
253
|
default: throw new Error("Unknown message type", { cause: message });
|
|
205
254
|
}
|
|
206
255
|
}
|
|
207
|
-
return internalConvertMessageToIcu(message).trim();
|
|
256
|
+
return internalConvertMessageToIcu(message, void 0).trim();
|
|
208
257
|
}
|
|
209
258
|
//#endregion
|
|
210
259
|
//#region src/features/messages/format.ts
|
|
@@ -282,6 +331,7 @@ exports.ElementMessage = ElementMessage;
|
|
|
282
331
|
exports.LiteralMessage = LiteralMessage;
|
|
283
332
|
exports.assignSequenceIdentifiers = assignSequenceIdentifiers;
|
|
284
333
|
exports.convertMessageToIcu = convertMessageToIcu;
|
|
334
|
+
exports.escapeIcuLiteral = escapeIcuLiteral;
|
|
285
335
|
exports.generateHash = require_hash.generateHash;
|
|
286
336
|
exports.getBranchCase = getBranchCase;
|
|
287
337
|
exports.isArgumentType = isArgumentType;
|
|
@@ -147,7 +147,33 @@ type Message = LiteralMessage | ArgumentMessage | ElementMessage | ChoiceMessage
|
|
|
147
147
|
//#region src/features/messages/convert.d.ts
|
|
148
148
|
declare function convertMessageToIcu(message: Message): string;
|
|
149
149
|
//#endregion
|
|
150
|
+
//#region src/features/messages/escape.d.ts
|
|
151
|
+
/**
|
|
152
|
+
* Quoting for text that has to survive a message format unchanged.
|
|
153
|
+
*
|
|
154
|
+
* This lives beside the converter rather than in a transform because it belongs
|
|
155
|
+
* to the format being written, not to the syntax being read. A literal is the
|
|
156
|
+
* same literal whether it came from a template literal or from JSX, and the
|
|
157
|
+
* moment there is a second target format its reserved characters — and the way
|
|
158
|
+
* it spells an escape — are its own.
|
|
159
|
+
*/
|
|
160
|
+
/**
|
|
161
|
+
* Escape literal text so ICU MessageFormat 1 reads it as the text it is.
|
|
162
|
+
*
|
|
163
|
+
* MF1 escapes with an apostrophe: a run wrapped in one is taken literally, and a
|
|
164
|
+
* doubled apostrophe is a single apostrophe. The subtlety is that an apostrophe
|
|
165
|
+
* only opens a quoted run when a character that needs quoting follows it —
|
|
166
|
+
* anything else and the apostrophe is itself literal. So `'#'` is a real escape
|
|
167
|
+
* inside a plural and the three characters `'#'` anywhere else, which is why
|
|
168
|
+
* `hash` has to be told rather than assumed.
|
|
169
|
+
*
|
|
170
|
+
* @param text Literal text as the author wrote it
|
|
171
|
+
* @param hash Whether `#` stands for a number here, i.e. whether some `plural`
|
|
172
|
+
* or `ordinal` encloses this text
|
|
173
|
+
*/
|
|
174
|
+
declare function escapeIcuLiteral(text: string, hash?: boolean): string;
|
|
175
|
+
//#endregion
|
|
150
176
|
//#region src/features/messages/hash.d.ts
|
|
151
177
|
declare function generateHash(input: string, context?: string): string;
|
|
152
178
|
//#endregion
|
|
153
|
-
export { ARGUMENT_STYLES, ARGUMENT_TYPES, AUTO_INCREMENT_IDENTIFIER, ArgumentFormat, ArgumentMessage, ArgumentType, ChoiceMessage, CompositeMessage, ElementMessage, LiteralMessage, Message, PlaceholderEquivalence, assignSequenceIdentifiers, convertMessageToIcu, generateHash, getBranchCase, isArgumentType, validateArgumentStyle, validateBranchIdentifier };
|
|
179
|
+
export { ARGUMENT_STYLES, ARGUMENT_TYPES, AUTO_INCREMENT_IDENTIFIER, ArgumentFormat, ArgumentMessage, ArgumentType, ChoiceMessage, CompositeMessage, ElementMessage, LiteralMessage, Message, PlaceholderEquivalence, assignSequenceIdentifiers, convertMessageToIcu, escapeIcuLiteral, generateHash, getBranchCase, isArgumentType, validateArgumentStyle, validateBranchIdentifier };
|
|
@@ -147,7 +147,33 @@ type Message = LiteralMessage | ArgumentMessage | ElementMessage | ChoiceMessage
|
|
|
147
147
|
//#region src/features/messages/convert.d.ts
|
|
148
148
|
declare function convertMessageToIcu(message: Message): string;
|
|
149
149
|
//#endregion
|
|
150
|
+
//#region src/features/messages/escape.d.ts
|
|
151
|
+
/**
|
|
152
|
+
* Quoting for text that has to survive a message format unchanged.
|
|
153
|
+
*
|
|
154
|
+
* This lives beside the converter rather than in a transform because it belongs
|
|
155
|
+
* to the format being written, not to the syntax being read. A literal is the
|
|
156
|
+
* same literal whether it came from a template literal or from JSX, and the
|
|
157
|
+
* moment there is a second target format its reserved characters — and the way
|
|
158
|
+
* it spells an escape — are its own.
|
|
159
|
+
*/
|
|
160
|
+
/**
|
|
161
|
+
* Escape literal text so ICU MessageFormat 1 reads it as the text it is.
|
|
162
|
+
*
|
|
163
|
+
* MF1 escapes with an apostrophe: a run wrapped in one is taken literally, and a
|
|
164
|
+
* doubled apostrophe is a single apostrophe. The subtlety is that an apostrophe
|
|
165
|
+
* only opens a quoted run when a character that needs quoting follows it —
|
|
166
|
+
* anything else and the apostrophe is itself literal. So `'#'` is a real escape
|
|
167
|
+
* inside a plural and the three characters `'#'` anywhere else, which is why
|
|
168
|
+
* `hash` has to be told rather than assumed.
|
|
169
|
+
*
|
|
170
|
+
* @param text Literal text as the author wrote it
|
|
171
|
+
* @param hash Whether `#` stands for a number here, i.e. whether some `plural`
|
|
172
|
+
* or `ordinal` encloses this text
|
|
173
|
+
*/
|
|
174
|
+
declare function escapeIcuLiteral(text: string, hash?: boolean): string;
|
|
175
|
+
//#endregion
|
|
150
176
|
//#region src/features/messages/hash.d.ts
|
|
151
177
|
declare function generateHash(input: string, context?: string): string;
|
|
152
178
|
//#endregion
|
|
153
|
-
export { ARGUMENT_STYLES, ARGUMENT_TYPES, AUTO_INCREMENT_IDENTIFIER, ArgumentFormat, ArgumentMessage, ArgumentType, ChoiceMessage, CompositeMessage, ElementMessage, LiteralMessage, Message, PlaceholderEquivalence, assignSequenceIdentifiers, convertMessageToIcu, generateHash, getBranchCase, isArgumentType, validateArgumentStyle, validateBranchIdentifier };
|
|
179
|
+
export { ARGUMENT_STYLES, ARGUMENT_TYPES, AUTO_INCREMENT_IDENTIFIER, ArgumentFormat, ArgumentMessage, ArgumentType, ChoiceMessage, CompositeMessage, ElementMessage, LiteralMessage, Message, PlaceholderEquivalence, assignSequenceIdentifiers, convertMessageToIcu, escapeIcuLiteral, generateHash, getBranchCase, isArgumentType, validateArgumentStyle, validateBranchIdentifier };
|
|
@@ -1,4 +1,32 @@
|
|
|
1
1
|
import { t as generateHash } from "../../hash-DvzpieJD.mjs";
|
|
2
|
+
//#region src/features/messages/escape.ts
|
|
3
|
+
/**
|
|
4
|
+
* Quoting for text that has to survive a message format unchanged.
|
|
5
|
+
*
|
|
6
|
+
* This lives beside the converter rather than in a transform because it belongs
|
|
7
|
+
* to the format being written, not to the syntax being read. A literal is the
|
|
8
|
+
* same literal whether it came from a template literal or from JSX, and the
|
|
9
|
+
* moment there is a second target format its reserved characters — and the way
|
|
10
|
+
* it spells an escape — are its own.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Escape literal text so ICU MessageFormat 1 reads it as the text it is.
|
|
14
|
+
*
|
|
15
|
+
* MF1 escapes with an apostrophe: a run wrapped in one is taken literally, and a
|
|
16
|
+
* doubled apostrophe is a single apostrophe. The subtlety is that an apostrophe
|
|
17
|
+
* only opens a quoted run when a character that needs quoting follows it —
|
|
18
|
+
* anything else and the apostrophe is itself literal. So `'#'` is a real escape
|
|
19
|
+
* inside a plural and the three characters `'#'` anywhere else, which is why
|
|
20
|
+
* `hash` has to be told rather than assumed.
|
|
21
|
+
*
|
|
22
|
+
* @param text Literal text as the author wrote it
|
|
23
|
+
* @param hash Whether `#` stands for a number here, i.e. whether some `plural`
|
|
24
|
+
* or `ordinal` encloses this text
|
|
25
|
+
*/
|
|
26
|
+
function escapeIcuLiteral(text, hash = false) {
|
|
27
|
+
return text.replace(/'/gu, "''").replace(hash ? /[{}#]+/gu : /[{}]+/gu, (run) => `'${run}'`);
|
|
28
|
+
}
|
|
29
|
+
//#endregion
|
|
2
30
|
//#region src/features/messages/types.ts
|
|
3
31
|
var Base = class {
|
|
4
32
|
toICUString() {
|
|
@@ -134,9 +162,28 @@ function assignSequenceIdentifiers(message, sequence = { current: 0 }, equivalen
|
|
|
134
162
|
while (reserved.has(identifier)) identifier = `${sequence.current++}`;
|
|
135
163
|
return identifier;
|
|
136
164
|
}
|
|
165
|
+
const numbered = {
|
|
166
|
+
tags: [],
|
|
167
|
+
values: []
|
|
168
|
+
};
|
|
169
|
+
/**
|
|
170
|
+
* The number this expression already has, or a fresh one. An anonymous
|
|
171
|
+
* placeholder is named by its position, so the same value written twice would
|
|
172
|
+
* otherwise arrive as two names — `{0} x {1}` for one length, which asks a
|
|
173
|
+
* translator to keep two holes in step and the caller to supply one value
|
|
174
|
+
* under two props. This is the rule explicit names already follow, where a
|
|
175
|
+
* repeat is allowed precisely when nothing distinguishes it.
|
|
176
|
+
*/
|
|
177
|
+
function number(assigned, expression) {
|
|
178
|
+
const seen = assigned.find(([e]) => equivalent(e, expression));
|
|
179
|
+
if (seen) return seen[1];
|
|
180
|
+
const identifier = next();
|
|
181
|
+
assigned.push([expression, identifier]);
|
|
182
|
+
return identifier;
|
|
183
|
+
}
|
|
137
184
|
function walk(message) {
|
|
138
185
|
if (message instanceof ArgumentMessage || message instanceof ElementMessage || message instanceof ChoiceMessage) {
|
|
139
|
-
if (message.identifier === AUTO_INCREMENT_IDENTIFIER) message.identifier =
|
|
186
|
+
if (message.identifier === AUTO_INCREMENT_IDENTIFIER) message.identifier = number(message instanceof ElementMessage ? numbered.tags : numbered.values, message.expression);
|
|
140
187
|
}
|
|
141
188
|
if (message instanceof CompositeMessage || message instanceof ElementMessage) for (const child of message.children) walk(child);
|
|
142
189
|
if (message instanceof ChoiceMessage) for (const branch of message.branches) {
|
|
@@ -176,10 +223,11 @@ function collectAssignedIdentifiers(message, equivalent) {
|
|
|
176
223
|
//#endregion
|
|
177
224
|
//#region src/features/messages/convert.ts
|
|
178
225
|
function convertMessageToIcu(message) {
|
|
179
|
-
function internalConvertMessageToIcu(message) {
|
|
226
|
+
function internalConvertMessageToIcu(message, hash) {
|
|
180
227
|
switch (true) {
|
|
181
|
-
case message instanceof LiteralMessage: return String(message.text);
|
|
228
|
+
case message instanceof LiteralMessage: return escapeIcuLiteral(String(message.text), hash !== void 0);
|
|
182
229
|
case message instanceof ArgumentMessage: {
|
|
230
|
+
if (!message.format && hash !== void 0 && String(message.identifier) === hash) return "#";
|
|
183
231
|
const parts = [String(message.identifier)];
|
|
184
232
|
if (message.format) parts.push(message.format.type);
|
|
185
233
|
if (message.format?.style) parts.push(message.format.style);
|
|
@@ -187,23 +235,24 @@ function convertMessageToIcu(message) {
|
|
|
187
235
|
}
|
|
188
236
|
case message instanceof ElementMessage: {
|
|
189
237
|
if (message.children.length === 0) return `<${String(message.identifier)}/>`;
|
|
190
|
-
const children = message.children.map((m) => internalConvertMessageToIcu(m)).join("");
|
|
238
|
+
const children = message.children.map((m) => internalConvertMessageToIcu(m, hash)).join("");
|
|
191
239
|
return `<${String(message.identifier)}>${children}</${String(message.identifier)}>`;
|
|
192
240
|
}
|
|
193
241
|
case message instanceof ChoiceMessage: {
|
|
242
|
+
const scope = message.kind === "select" ? hash : String(message.identifier);
|
|
194
243
|
const branches = message.branches.map(({ identifier, value }) => ({
|
|
195
244
|
identifier: getBranchCase(message.kind, identifier),
|
|
196
|
-
value: internalConvertMessageToIcu(value)
|
|
245
|
+
value: internalConvertMessageToIcu(value, scope)
|
|
197
246
|
})).map(({ identifier, value }) => ` ${identifier} {${value}}\n`).join("");
|
|
198
247
|
const format = message.kind === "ordinal" ? "selectordinal" : message.kind;
|
|
199
248
|
const offset = message.offset === void 0 || message.kind === "select" ? "" : ` offset:${message.offset}`;
|
|
200
249
|
return `{${String(message.identifier)}, ${format},${offset}\n${branches}}`;
|
|
201
250
|
}
|
|
202
|
-
case message instanceof CompositeMessage: return Object.entries(message.children).map(([, m]) => internalConvertMessageToIcu(m)).join("");
|
|
251
|
+
case message instanceof CompositeMessage: return Object.entries(message.children).map(([, m]) => internalConvertMessageToIcu(m, hash)).join("");
|
|
203
252
|
default: throw new Error("Unknown message type", { cause: message });
|
|
204
253
|
}
|
|
205
254
|
}
|
|
206
|
-
return internalConvertMessageToIcu(message).trim();
|
|
255
|
+
return internalConvertMessageToIcu(message, void 0).trim();
|
|
207
256
|
}
|
|
208
257
|
//#endregion
|
|
209
258
|
//#region src/features/messages/format.ts
|
|
@@ -271,4 +320,4 @@ function validateArgumentStyle(type, style) {
|
|
|
271
320
|
throw new Error(`Invalid ${type} style '${style}', expected ${expected}` + (type === "number" ? ", or a literal number pattern such as #,##0.00" : ""));
|
|
272
321
|
}
|
|
273
322
|
//#endregion
|
|
274
|
-
export { ARGUMENT_STYLES, ARGUMENT_TYPES, AUTO_INCREMENT_IDENTIFIER, ArgumentMessage, ChoiceMessage, CompositeMessage, ElementMessage, LiteralMessage, assignSequenceIdentifiers, convertMessageToIcu, generateHash, getBranchCase, isArgumentType, validateArgumentStyle, validateBranchIdentifier };
|
|
323
|
+
export { ARGUMENT_STYLES, ARGUMENT_TYPES, AUTO_INCREMENT_IDENTIFIER, ArgumentMessage, ChoiceMessage, CompositeMessage, ElementMessage, LiteralMessage, assignSequenceIdentifiers, convertMessageToIcu, escapeIcuLiteral, generateHash, getBranchCase, isArgumentType, validateArgumentStyle, validateBranchIdentifier };
|