fluent-transpiler 0.3.2 → 0.4.1
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/README.md +37 -7
- package/cli.js +81 -78
- package/index.d.ts +34 -0
- package/index.js +432 -389
- package/license.json +28 -0
- package/license.template +2 -0
- package/package.json +94 -51
package/index.js
CHANGED
|
@@ -1,5 +1,77 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// Copyright 2026 will Farrell, and fluent-transpiler contributors.
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
import { parse } from "@fluent/syntax";
|
|
5
|
+
import { camelCase, constantCase, pascalCase, snakeCase } from "change-case";
|
|
6
|
+
|
|
7
|
+
const reservedWords = new Set([
|
|
8
|
+
"abstract",
|
|
9
|
+
"arguments",
|
|
10
|
+
"await",
|
|
11
|
+
"boolean",
|
|
12
|
+
"break",
|
|
13
|
+
"byte",
|
|
14
|
+
"case",
|
|
15
|
+
"catch",
|
|
16
|
+
"char",
|
|
17
|
+
"class",
|
|
18
|
+
"const",
|
|
19
|
+
"continue",
|
|
20
|
+
"debugger",
|
|
21
|
+
"default",
|
|
22
|
+
"delete",
|
|
23
|
+
"do",
|
|
24
|
+
"double",
|
|
25
|
+
"else",
|
|
26
|
+
"enum",
|
|
27
|
+
"eval",
|
|
28
|
+
"export",
|
|
29
|
+
"extends",
|
|
30
|
+
"false",
|
|
31
|
+
"final",
|
|
32
|
+
"finally",
|
|
33
|
+
"float",
|
|
34
|
+
"for",
|
|
35
|
+
"function",
|
|
36
|
+
"goto",
|
|
37
|
+
"if",
|
|
38
|
+
"implements",
|
|
39
|
+
"import",
|
|
40
|
+
"in",
|
|
41
|
+
"instanceof",
|
|
42
|
+
"int",
|
|
43
|
+
"interface",
|
|
44
|
+
"let",
|
|
45
|
+
"long",
|
|
46
|
+
"native",
|
|
47
|
+
"new",
|
|
48
|
+
"null",
|
|
49
|
+
"of",
|
|
50
|
+
"package",
|
|
51
|
+
"private",
|
|
52
|
+
"protected",
|
|
53
|
+
"public",
|
|
54
|
+
"return",
|
|
55
|
+
"short",
|
|
56
|
+
"static",
|
|
57
|
+
"super",
|
|
58
|
+
"switch",
|
|
59
|
+
"synchronized",
|
|
60
|
+
"this",
|
|
61
|
+
"throw",
|
|
62
|
+
"throws",
|
|
63
|
+
"transient",
|
|
64
|
+
"true",
|
|
65
|
+
"try",
|
|
66
|
+
"typeof",
|
|
67
|
+
"undefined",
|
|
68
|
+
"var",
|
|
69
|
+
"void",
|
|
70
|
+
"volatile",
|
|
71
|
+
"while",
|
|
72
|
+
"with",
|
|
73
|
+
"yield",
|
|
74
|
+
]);
|
|
3
75
|
|
|
4
76
|
const exportDefault = `(id, params) => {
|
|
5
77
|
const source = __exports[id] ?? __exports['_'+id]
|
|
@@ -7,377 +79,342 @@ const exportDefault = `(id, params) => {
|
|
|
7
79
|
if (typeof source === 'function') return source(params)
|
|
8
80
|
return source
|
|
9
81
|
}
|
|
10
|
-
|
|
82
|
+
`;
|
|
11
83
|
export const compile = (src, opts) => {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
84
|
+
const options = {
|
|
85
|
+
comments: true,
|
|
86
|
+
errorOnJunk: true,
|
|
87
|
+
includeKey: [],
|
|
88
|
+
excludeKey: [],
|
|
89
|
+
excludeValue: undefined,
|
|
90
|
+
variableNotation: "camelCase",
|
|
91
|
+
disableMinify: false,
|
|
92
|
+
useIsolating: false,
|
|
93
|
+
params: "params",
|
|
94
|
+
exportDefault,
|
|
95
|
+
...opts,
|
|
96
|
+
};
|
|
97
|
+
if (!Array.isArray(options.locale)) options.locale = [options.locale];
|
|
98
|
+
if (!Array.isArray(options.includeKey))
|
|
99
|
+
options.includeKey = [options.includeKey];
|
|
100
|
+
if (!Array.isArray(options.excludeKey))
|
|
101
|
+
options.excludeKey = [options.excludeKey];
|
|
102
|
+
if (options.excludeValue) {
|
|
103
|
+
// cast to template literal
|
|
104
|
+
options.excludeValue = `\`${options.excludeValue}\``;
|
|
105
|
+
}
|
|
35
106
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
107
|
+
const metadata = {};
|
|
108
|
+
const exports = [];
|
|
109
|
+
const functions = {}; // global functions
|
|
110
|
+
let variable;
|
|
40
111
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
112
|
+
const compileAssignment = (data) => {
|
|
113
|
+
variable = compileType(data);
|
|
114
|
+
metadata[variable] = {
|
|
115
|
+
id: data.name,
|
|
116
|
+
term: false,
|
|
117
|
+
params: false,
|
|
118
|
+
};
|
|
119
|
+
return variable;
|
|
120
|
+
};
|
|
51
121
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
122
|
+
const compileFunctionArguments = (data) => {
|
|
123
|
+
const positional = data.arguments?.positional.map((data) => {
|
|
124
|
+
return types[data.type](data);
|
|
125
|
+
});
|
|
126
|
+
const named = data.arguments?.named.reduce((obj, data) => {
|
|
127
|
+
const entry = compileType(data);
|
|
128
|
+
const [key, value] = entry.split(": ");
|
|
129
|
+
obj[key] = value;
|
|
130
|
+
return obj;
|
|
131
|
+
}, {});
|
|
132
|
+
return { positional, named };
|
|
133
|
+
};
|
|
65
134
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
135
|
+
const compileType = (data, parent) => {
|
|
136
|
+
try {
|
|
137
|
+
return types[data.type](data, parent);
|
|
138
|
+
} catch (e) {
|
|
139
|
+
throw new Error(e.message, { cause: { error: e, data } });
|
|
140
|
+
}
|
|
141
|
+
};
|
|
74
142
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
143
|
+
const types = {
|
|
144
|
+
Identifier: (data, parent) => {
|
|
145
|
+
const value =
|
|
146
|
+
parent === "Attribute"
|
|
147
|
+
? data.name
|
|
148
|
+
: variableNotation[options.variableNotation](data.name);
|
|
81
149
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
150
|
+
if (value.includes("-")) {
|
|
151
|
+
return `'${value}'`;
|
|
152
|
+
}
|
|
153
|
+
// Check for reserved words
|
|
154
|
+
if (reservedWords.has(value)) {
|
|
155
|
+
return `_${value}`;
|
|
156
|
+
}
|
|
157
|
+
return value;
|
|
158
|
+
},
|
|
159
|
+
Attribute: (data) => {
|
|
160
|
+
const key = compileType(data.id, data.type);
|
|
161
|
+
const value = compileType(data.value, data.type);
|
|
162
|
+
return ` ${key}: ${value}`;
|
|
163
|
+
},
|
|
164
|
+
Pattern: (data, parent) => {
|
|
165
|
+
return (
|
|
166
|
+
"`" +
|
|
167
|
+
data.elements
|
|
168
|
+
.map((data) => {
|
|
169
|
+
return compileType(data, parent);
|
|
170
|
+
})
|
|
171
|
+
.join("") +
|
|
172
|
+
"`"
|
|
173
|
+
);
|
|
174
|
+
},
|
|
175
|
+
// resources
|
|
176
|
+
Term: (data) => {
|
|
177
|
+
const assignment = compileAssignment(data.id);
|
|
178
|
+
const templateStringLiteral = compileType(data.value);
|
|
179
|
+
metadata[assignment].term = true;
|
|
180
|
+
if (metadata[assignment].params) {
|
|
181
|
+
return `const ${assignment} = (${options.params}) => ${templateStringLiteral}\n`;
|
|
182
|
+
}
|
|
183
|
+
return `const ${assignment} = ${templateStringLiteral}\n`;
|
|
184
|
+
},
|
|
185
|
+
Message: (data) => {
|
|
186
|
+
const assignment = compileAssignment(data.id);
|
|
119
187
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
188
|
+
if (
|
|
189
|
+
options.includeKey.length &&
|
|
190
|
+
!options.includeKey.includes(assignment)
|
|
191
|
+
) {
|
|
192
|
+
return "";
|
|
193
|
+
}
|
|
126
194
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
195
|
+
if (
|
|
196
|
+
options.excludeKey.length &&
|
|
197
|
+
options.excludeKey.includes(assignment)
|
|
198
|
+
) {
|
|
199
|
+
return "";
|
|
200
|
+
}
|
|
133
201
|
|
|
134
|
-
|
|
135
|
-
|
|
202
|
+
let templateStringLiteral =
|
|
203
|
+
data.value && compileType(data.value, data.type);
|
|
136
204
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
205
|
+
if (options.excludeValue === templateStringLiteral) {
|
|
206
|
+
templateStringLiteral = "``";
|
|
207
|
+
}
|
|
140
208
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
message = `(${options.params}) => ({
|
|
209
|
+
metadata[assignment].attributes = data.attributes.length;
|
|
210
|
+
let attributes = {};
|
|
211
|
+
if (metadata[assignment].attributes) {
|
|
212
|
+
attributes = `{\n${data.attributes
|
|
213
|
+
.map((data) => {
|
|
214
|
+
return ` ${compileType(data)}`;
|
|
215
|
+
})
|
|
216
|
+
.join(",\n")}\n }`;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
let message = "";
|
|
220
|
+
if (!options.disableMinify) {
|
|
221
|
+
if (metadata[assignment].attributes) {
|
|
222
|
+
if (metadata[assignment].params) {
|
|
223
|
+
message = `(${options.params}) => ({
|
|
157
224
|
value:${templateStringLiteral},
|
|
158
225
|
attributes:${attributes}
|
|
159
|
-
})\n
|
|
160
|
-
|
|
161
|
-
|
|
226
|
+
})\n`;
|
|
227
|
+
} else {
|
|
228
|
+
message = `{
|
|
162
229
|
value: ${templateStringLiteral},
|
|
163
230
|
attributes: ${attributes}
|
|
164
|
-
}\n
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
231
|
+
}\n`;
|
|
232
|
+
}
|
|
233
|
+
} else if (metadata[assignment].params) {
|
|
234
|
+
message = `(${options.params}) => ${templateStringLiteral}\n`;
|
|
235
|
+
} else {
|
|
236
|
+
message = `${templateStringLiteral}\n`;
|
|
237
|
+
}
|
|
238
|
+
} else {
|
|
239
|
+
// consistent API
|
|
240
|
+
message = `(${metadata[assignment].params ? options.params : ""}) => ({
|
|
174
241
|
value:${templateStringLiteral},
|
|
175
242
|
attributes:${attributes}
|
|
176
|
-
})\n
|
|
177
|
-
|
|
178
|
-
//if (options.treeShaking) {
|
|
179
|
-
if (assignment === metadata[assignment].id) {
|
|
180
|
-
exports.push(`${assignment}`)
|
|
181
|
-
} else {
|
|
182
|
-
exports.push(`'${metadata[assignment].id}': ${assignment}`)
|
|
183
|
-
}
|
|
184
|
-
return `export const ${assignment} = ${message}`
|
|
185
|
-
/*} else {
|
|
186
|
-
if (assignment === metadata[assignment].id) {
|
|
187
|
-
exports.push(`${assignment}: ${message}`)
|
|
188
|
-
} else {
|
|
189
|
-
exports.push(`'${metadata[assignment].id}': ${message}`)
|
|
190
|
-
}
|
|
191
|
-
}*/
|
|
192
|
-
return ''
|
|
193
|
-
},
|
|
194
|
-
Comment: (data) => {
|
|
195
|
-
if (options.comments) return `// # ${data.content}\n`
|
|
196
|
-
return ''
|
|
197
|
-
},
|
|
198
|
-
GroupComment: (data) => {
|
|
199
|
-
if (options.comments) return `// ## ${data.content}\n`
|
|
200
|
-
return ''
|
|
201
|
-
},
|
|
202
|
-
ResourceComment: (data) => {
|
|
203
|
-
if (options.comments) return `// ### ${data.content}\n`
|
|
204
|
-
return ''
|
|
205
|
-
},
|
|
206
|
-
Junk: (data) => {
|
|
207
|
-
if (options.errorOnJunk) {
|
|
208
|
-
throw new Error('Junk found', { cause: data })
|
|
209
|
-
}
|
|
210
|
-
console.error('Error: Skipping Junk', JSON.stringify(data, null, 2))
|
|
211
|
-
return ''
|
|
212
|
-
},
|
|
213
|
-
// Element
|
|
214
|
-
TextElement: (data) => {
|
|
215
|
-
if (data.value === options.emptyString) return
|
|
216
|
-
return data.value.replaceAll('`', '\\`') // escape string literal
|
|
217
|
-
},
|
|
218
|
-
Placeable: (data, parent) => {
|
|
219
|
-
return `${options.useIsolating ? '\u2068' : ''}\${${compileType(
|
|
220
|
-
data.expression,
|
|
221
|
-
parent
|
|
222
|
-
)}}${options.useIsolating ? '\u2069' : ''}`
|
|
223
|
-
},
|
|
224
|
-
// Expression
|
|
225
|
-
StringLiteral: (data, parent) => {
|
|
226
|
-
// JSON.stringify at parent level
|
|
227
|
-
if (['NamedArgument'].includes(parent)) {
|
|
228
|
-
return `${data.value}`
|
|
229
|
-
}
|
|
230
|
-
return `"${data.value}"`
|
|
231
|
-
},
|
|
232
|
-
NumberLiteral: (data) => {
|
|
233
|
-
const decimal = Number.parseFloat(data.value)
|
|
234
|
-
const number = Number.isInteger(decimal)
|
|
235
|
-
? Number.parseInt(data.value)
|
|
236
|
-
: decimal
|
|
237
|
-
return Intl.NumberFormat(options.locale).format(number)
|
|
238
|
-
},
|
|
239
|
-
VariableReference: (data, parent) => {
|
|
240
|
-
functions.__formatVariable = true
|
|
241
|
-
metadata[variable].params = true
|
|
242
|
-
const value = `${options.params}?.${data.id.name}`
|
|
243
|
-
if (['Message', 'Variant', 'Attribute'].includes(parent)) {
|
|
244
|
-
return `__formatVariable(${value})`
|
|
245
|
-
}
|
|
246
|
-
return value
|
|
247
|
-
},
|
|
248
|
-
MessageReference: (data) => {
|
|
249
|
-
const messageName = compileType(data.id)
|
|
250
|
-
metadata[variable].params ||= metadata[messageName].params
|
|
251
|
-
if (!options.disableMinify) {
|
|
252
|
-
if (metadata[messageName].params) {
|
|
253
|
-
return `${messageName}(${options.params})`
|
|
254
|
-
}
|
|
255
|
-
return `${messageName}`
|
|
256
|
-
}
|
|
257
|
-
return `${messageName}(${
|
|
258
|
-
metadata[messageName].params ? options.params : ''
|
|
259
|
-
})`
|
|
260
|
-
},
|
|
261
|
-
TermReference: (data) => {
|
|
262
|
-
const termName = compileType(data.id)
|
|
263
|
-
metadata[variable].params ||= metadata[termName].params
|
|
243
|
+
})\n`;
|
|
244
|
+
}
|
|
264
245
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
246
|
+
if (assignment === metadata[assignment].id) {
|
|
247
|
+
exports.push(`${assignment}`);
|
|
248
|
+
} else {
|
|
249
|
+
exports.push(`'${metadata[assignment].id}': ${assignment}`);
|
|
250
|
+
}
|
|
251
|
+
return `export const ${assignment} = ${message}`;
|
|
252
|
+
},
|
|
253
|
+
Comment: (data) => {
|
|
254
|
+
if (options.comments) return `// # ${data.content}\n`;
|
|
255
|
+
return "";
|
|
256
|
+
},
|
|
257
|
+
GroupComment: (data) => {
|
|
258
|
+
if (options.comments) return `// ## ${data.content}\n`;
|
|
259
|
+
return "";
|
|
260
|
+
},
|
|
261
|
+
ResourceComment: (data) => {
|
|
262
|
+
if (options.comments) return `// ### ${data.content}\n`;
|
|
263
|
+
return "";
|
|
264
|
+
},
|
|
265
|
+
Junk: (data) => {
|
|
266
|
+
if (options.errorOnJunk) {
|
|
267
|
+
throw new Error("Junk found", { cause: data });
|
|
268
|
+
}
|
|
269
|
+
return "";
|
|
270
|
+
},
|
|
271
|
+
// Element
|
|
272
|
+
TextElement: (data) => {
|
|
273
|
+
return data.value.replaceAll("`", "\\`"); // escape string literal
|
|
274
|
+
},
|
|
275
|
+
Placeable: (data, parent) => {
|
|
276
|
+
return `${options.useIsolating ? "\u2068" : ""}\${${compileType(
|
|
277
|
+
data.expression,
|
|
278
|
+
parent,
|
|
279
|
+
)}}${options.useIsolating ? "\u2069" : ""}`;
|
|
280
|
+
},
|
|
281
|
+
// Expression
|
|
282
|
+
StringLiteral: (data, parent) => {
|
|
283
|
+
// JSON.stringify at parent level
|
|
284
|
+
if (["NamedArgument"].includes(parent)) {
|
|
285
|
+
return `${data.value}`;
|
|
286
|
+
}
|
|
287
|
+
return `"${data.value}"`;
|
|
288
|
+
},
|
|
289
|
+
NumberLiteral: (data) => {
|
|
290
|
+
const decimal = Number.parseFloat(data.value);
|
|
291
|
+
const number = Number.isInteger(decimal)
|
|
292
|
+
? Number.parseInt(data.value, 10)
|
|
293
|
+
: decimal;
|
|
294
|
+
return Intl.NumberFormat(options.locale).format(number);
|
|
295
|
+
},
|
|
296
|
+
VariableReference: (data, parent) => {
|
|
297
|
+
functions.__formatVariable = true;
|
|
298
|
+
metadata[variable].params = true;
|
|
299
|
+
const value = `${options.params}?.${data.id.name}`;
|
|
300
|
+
if (["Message", "Variant", "Attribute"].includes(parent)) {
|
|
301
|
+
return `__formatVariable(${value})`;
|
|
302
|
+
}
|
|
303
|
+
return value;
|
|
304
|
+
},
|
|
305
|
+
MessageReference: (data) => {
|
|
306
|
+
const messageName = compileType(data.id);
|
|
307
|
+
metadata[variable].params ||= metadata[messageName].params;
|
|
308
|
+
if (!options.disableMinify) {
|
|
309
|
+
if (metadata[messageName].params) {
|
|
310
|
+
return `${messageName}(${options.params})`;
|
|
311
|
+
}
|
|
312
|
+
return `${messageName}`;
|
|
313
|
+
}
|
|
314
|
+
return `${messageName}(${
|
|
315
|
+
metadata[messageName].params ? options.params : ""
|
|
316
|
+
})`;
|
|
317
|
+
},
|
|
318
|
+
TermReference: (data) => {
|
|
319
|
+
const termName = compileType(data.id);
|
|
320
|
+
metadata[variable].params ||= metadata[termName].params;
|
|
339
321
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
322
|
+
let params;
|
|
323
|
+
if (metadata[termName].params) {
|
|
324
|
+
let { named } = compileFunctionArguments(data);
|
|
325
|
+
named = JSON.stringify(named);
|
|
326
|
+
if (named) {
|
|
327
|
+
params = `{ ...${options.params}, ${named.substring(
|
|
328
|
+
1,
|
|
329
|
+
named.length - 1,
|
|
330
|
+
)} }`;
|
|
331
|
+
} else {
|
|
332
|
+
params = options.params;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
if (!options.disableMinify) {
|
|
336
|
+
if (metadata[termName].params) {
|
|
337
|
+
return `${termName}(${params})`;
|
|
338
|
+
}
|
|
339
|
+
return `${termName}`;
|
|
340
|
+
}
|
|
341
|
+
return `${termName}(${params ? params : ""})`;
|
|
342
|
+
},
|
|
343
|
+
NamedArgument: (data) => {
|
|
344
|
+
// Inconsistent: `NamedArgument` uses `name` instead of `id` for Identifier
|
|
345
|
+
const key = data.name.name; // Don't transform value
|
|
346
|
+
const value = compileType(data.value, data.type);
|
|
347
|
+
return `${key}: ${value}`;
|
|
348
|
+
},
|
|
349
|
+
SelectExpression: (data) => {
|
|
350
|
+
functions.__select = true;
|
|
351
|
+
metadata[variable].params = true;
|
|
352
|
+
const value = compileType(data.selector);
|
|
353
|
+
let fallback;
|
|
354
|
+
return `__select(\n ${value},\n {\n${data.variants
|
|
355
|
+
.filter((data) => {
|
|
356
|
+
if (data.default) {
|
|
357
|
+
fallback = compileType(data.value, data.type);
|
|
358
|
+
}
|
|
359
|
+
return !data.default;
|
|
360
|
+
})
|
|
361
|
+
.map((data) => {
|
|
362
|
+
return ` ${compileType(data)}`;
|
|
363
|
+
})
|
|
364
|
+
.join(",\n")}\n },\n ${fallback}\n )`;
|
|
365
|
+
},
|
|
366
|
+
Variant: (data, parent) => {
|
|
367
|
+
// Inconsistent: `Variant` uses `key` instead of `id` for Identifier
|
|
368
|
+
const key = compileType(data.key);
|
|
369
|
+
const value = compileType(data.value, data.type);
|
|
370
|
+
return ` '${key}': ${value}`;
|
|
371
|
+
},
|
|
372
|
+
FunctionReference: (data) => {
|
|
373
|
+
return `${types[data.id.name](compileFunctionArguments(data))}`;
|
|
374
|
+
},
|
|
375
|
+
// Functions
|
|
376
|
+
DATETIME: (data) => {
|
|
377
|
+
functions.__formatDateTime = true;
|
|
378
|
+
const { positional, named } = data;
|
|
379
|
+
const value = positional[0];
|
|
380
|
+
return `__formatDateTime(${value}, ${JSON.stringify(named)})`;
|
|
381
|
+
},
|
|
382
|
+
RELATIVETIME: (data) => {
|
|
383
|
+
functions.__formatRelativeTime = true;
|
|
384
|
+
const { positional, named } = data;
|
|
385
|
+
const value = positional[0];
|
|
386
|
+
return `__formatRelativeTime(${value}, ${JSON.stringify(named)})`;
|
|
387
|
+
},
|
|
388
|
+
NUMBER: (data) => {
|
|
389
|
+
functions.__formatNumber = true;
|
|
390
|
+
const { positional, named } = data;
|
|
391
|
+
const value = positional[0];
|
|
392
|
+
return `__formatNumber(${value}, ${JSON.stringify(named)})`;
|
|
393
|
+
},
|
|
394
|
+
};
|
|
346
395
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
translations += compileType(data)
|
|
351
|
-
}
|
|
396
|
+
if (/\t/.test(src)) {
|
|
397
|
+
src = src.replace(/\t/g, " ");
|
|
398
|
+
}
|
|
352
399
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
) {
|
|
359
|
-
output += `const __locales = ${JSON.stringify(opts.locale)}\n`
|
|
360
|
-
}
|
|
361
|
-
/*
|
|
362
|
-
const relativeTimeFormat = new Intl.RelativeTimeFormat(lang, {
|
|
363
|
-
localeMatcher: 'best fit',
|
|
364
|
-
numeric: 'always',
|
|
365
|
-
style: 'long'
|
|
366
|
-
})
|
|
400
|
+
const { body } = parse(src);
|
|
401
|
+
let translations = ``;
|
|
402
|
+
for (const data of body) {
|
|
403
|
+
translations += compileType(data);
|
|
404
|
+
}
|
|
367
405
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
output += `
|
|
406
|
+
let output = ``;
|
|
407
|
+
if (
|
|
408
|
+
functions.__formatVariable ||
|
|
409
|
+
functions.__formatDateTime ||
|
|
410
|
+
functions.__formatNumber ||
|
|
411
|
+
functions.__formatRelativeTime ||
|
|
412
|
+
functions.__select
|
|
413
|
+
) {
|
|
414
|
+
output += `const __locales = ${JSON.stringify(options.locale)}\nconst __intlCache = {}\n`;
|
|
415
|
+
}
|
|
416
|
+
if (functions.__formatRelativeTime) {
|
|
417
|
+
output += `
|
|
381
418
|
const __relativeTimeDiff = (d) => {
|
|
382
419
|
const msPerMinute = 60 * 1000
|
|
383
420
|
const msPerHour = msPerMinute * 60
|
|
@@ -411,60 +448,66 @@ const __formatRelativeTime = (value, options) => {
|
|
|
411
448
|
if (typeof value === 'string') value = new Date(value)
|
|
412
449
|
if (isNaN(value.getTime())) return value
|
|
413
450
|
try {
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
`
|
|
451
|
+
const [duration, unit] = __relativeTimeDiff(value)
|
|
452
|
+
const k = JSON.stringify(options) ?? ''
|
|
453
|
+
return (__intlCache['R'+k] ??= new Intl.RelativeTimeFormat(__locales, options)).format(duration, unit)
|
|
454
|
+
} catch (e) {
|
|
455
|
+
// RelativeTimeFormat unsupported or invalid options, fall back to DateTimeFormat
|
|
420
456
|
}
|
|
421
|
-
|
|
422
|
-
|
|
457
|
+
const k = JSON.stringify(options) ?? ''
|
|
458
|
+
return (__intlCache['D'+k] ??= new Intl.DateTimeFormat(__locales, options)).format(value)
|
|
459
|
+
}
|
|
460
|
+
`;
|
|
461
|
+
}
|
|
462
|
+
if (functions.__formatDateTime) {
|
|
463
|
+
output += `
|
|
423
464
|
const __formatDateTime = (value, options) => {
|
|
424
465
|
if (typeof value === 'string') value = new Date(value)
|
|
425
466
|
if (isNaN(value.getTime())) return value
|
|
426
|
-
|
|
467
|
+
const k = JSON.stringify(options) ?? ''
|
|
468
|
+
return (__intlCache['D'+k] ??= new Intl.DateTimeFormat(__locales, options)).format(value)
|
|
427
469
|
}
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
470
|
+
`;
|
|
471
|
+
}
|
|
472
|
+
if (functions.__formatVariable || functions.__formatNumber) {
|
|
473
|
+
output += `
|
|
432
474
|
const __formatNumber = (value, options) => {
|
|
433
|
-
|
|
475
|
+
const k = JSON.stringify(options) ?? ''
|
|
476
|
+
return (__intlCache['N'+k] ??= new Intl.NumberFormat(__locales, options)).format(value)
|
|
434
477
|
}
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
478
|
+
`;
|
|
479
|
+
}
|
|
480
|
+
if (functions.__formatVariable) {
|
|
481
|
+
output += `
|
|
439
482
|
const __formatVariable = (value) => {
|
|
440
483
|
if (typeof value === 'string') return value
|
|
441
|
-
const decimal =
|
|
442
|
-
const number = Number.isInteger(decimal) ? Number.parseInt(value) : decimal
|
|
484
|
+
const decimal = Number.parseFloat(value)
|
|
485
|
+
const number = Number.isInteger(decimal) ? Number.parseInt(value, 10) : decimal
|
|
443
486
|
return __formatNumber(number)
|
|
444
487
|
}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
488
|
+
`;
|
|
489
|
+
}
|
|
490
|
+
if (functions.__select) {
|
|
491
|
+
output += `
|
|
449
492
|
const __select = (value, cases, fallback, options) => {
|
|
450
|
-
const
|
|
451
|
-
const rule =
|
|
493
|
+
const k = JSON.stringify(options) ?? ''
|
|
494
|
+
const rule = (__intlCache['P'+k] ??= new Intl.PluralRules(__locales, options)).select(value)
|
|
452
495
|
return cases[value] ?? cases[rule] ?? fallback
|
|
453
496
|
}
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
497
|
+
`;
|
|
498
|
+
}
|
|
499
|
+
output += `\n${translations}`;
|
|
500
|
+
output += `const __exports = {\n ${exports.join(",\n ")}\n}`;
|
|
501
|
+
output += `\nexport default ${options.exportDefault}`;
|
|
459
502
|
|
|
460
|
-
|
|
461
|
-
}
|
|
503
|
+
return output;
|
|
504
|
+
};
|
|
462
505
|
|
|
463
506
|
const variableNotation = {
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
}
|
|
507
|
+
camelCase,
|
|
508
|
+
pascalCase,
|
|
509
|
+
snakeCase,
|
|
510
|
+
constantCase,
|
|
511
|
+
};
|
|
469
512
|
|
|
470
|
-
export default compile
|
|
513
|
+
export default compile;
|