@formatjs/cli-lib 6.6.6 → 7.0.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.
- package/lib_esnext/index.d.ts +7 -0
- package/lib_esnext/index.js +2 -0
- package/lib_esnext/main.d.ts +1 -0
- package/lib_esnext/main.js +3 -0
- package/lib_esnext/src/cli.d.ts +2 -0
- package/lib_esnext/src/cli.js +153 -0
- package/lib_esnext/src/compile.d.ts +54 -0
- package/lib_esnext/src/compile.js +90 -0
- package/lib_esnext/src/compile_folder.d.ts +2 -0
- package/lib_esnext/src/compile_folder.js +8 -0
- package/lib_esnext/src/console_utils.d.ts +7 -0
- package/lib_esnext/src/console_utils.js +67 -0
- package/lib_esnext/src/extract.d.ts +74 -0
- package/lib_esnext/src/extract.js +192 -0
- package/lib_esnext/src/formatters/crowdin.d.ts +7 -0
- package/lib_esnext/src/formatters/crowdin.js +22 -0
- package/lib_esnext/src/formatters/default.d.ts +6 -0
- package/lib_esnext/src/formatters/default.js +8 -0
- package/lib_esnext/src/formatters/index.d.ts +9 -0
- package/lib_esnext/src/formatters/index.js +36 -0
- package/lib_esnext/src/formatters/lokalise.d.ts +9 -0
- package/lib_esnext/src/formatters/lokalise.js +19 -0
- package/lib_esnext/src/formatters/simple.d.ts +4 -0
- package/lib_esnext/src/formatters/simple.js +7 -0
- package/lib_esnext/src/formatters/smartling.d.ts +23 -0
- package/lib_esnext/src/formatters/smartling.js +44 -0
- package/lib_esnext/src/formatters/transifex.d.ts +9 -0
- package/lib_esnext/src/formatters/transifex.js +19 -0
- package/lib_esnext/src/gts_extractor.d.ts +1 -0
- package/lib_esnext/src/gts_extractor.js +14 -0
- package/lib_esnext/src/hbs_extractor.d.ts +1 -0
- package/lib_esnext/src/hbs_extractor.js +45 -0
- package/lib_esnext/src/parse_script.d.ts +7 -0
- package/lib_esnext/src/parse_script.js +46 -0
- package/lib_esnext/src/pseudo_locale.d.ts +22 -0
- package/lib_esnext/src/pseudo_locale.js +115 -0
- package/lib_esnext/src/vue_extractor.d.ts +2 -0
- package/lib_esnext/src/vue_extractor.js +68 -0
- package/package.json +10 -9
- package/index.js.map +0 -1
- package/main.js.map +0 -1
- package/src/cli.js.map +0 -1
- package/src/compile.js.map +0 -1
- package/src/compile_folder.js.map +0 -1
- package/src/console_utils.js.map +0 -1
- package/src/extract.js.map +0 -1
- package/src/formatters/crowdin.js.map +0 -1
- package/src/formatters/default.js.map +0 -1
- package/src/formatters/index.js.map +0 -1
- package/src/formatters/lokalise.js.map +0 -1
- package/src/formatters/simple.js.map +0 -1
- package/src/formatters/smartling.js.map +0 -1
- package/src/formatters/transifex.js.map +0 -1
- package/src/gts_extractor.js.map +0 -1
- package/src/hbs_extractor.js.map +0 -1
- package/src/parse_script.js.map +0 -1
- package/src/pseudo_locale.js.map +0 -1
- package/src/vue_extractor.js.map +0 -1
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { interpolateName, } from '@formatjs/ts-transformer';
|
|
2
|
+
import { outputFile, readFile } from 'fs-extra';
|
|
3
|
+
import { debug, getStdinAsString, warn, writeStdout } from './console_utils';
|
|
4
|
+
import { parse } from '@formatjs/icu-messageformat-parser';
|
|
5
|
+
import { hoistSelectors } from '@formatjs/icu-messageformat-parser/manipulator';
|
|
6
|
+
import { printAST } from '@formatjs/icu-messageformat-parser/printer';
|
|
7
|
+
import stringify from 'json-stable-stringify';
|
|
8
|
+
import { resolveBuiltinFormatter } from './formatters';
|
|
9
|
+
import { parseScript } from './parse_script';
|
|
10
|
+
function calculateLineColFromOffset(text, start) {
|
|
11
|
+
if (!start) {
|
|
12
|
+
return { line: 1, col: 1 };
|
|
13
|
+
}
|
|
14
|
+
const chunk = text.slice(0, start);
|
|
15
|
+
const lines = chunk.split('\n');
|
|
16
|
+
const lastLine = lines[lines.length - 1];
|
|
17
|
+
return { line: lines.length, col: lastLine.length };
|
|
18
|
+
}
|
|
19
|
+
async function processFile(source, fn, { idInterpolationPattern, ...opts }) {
|
|
20
|
+
let messages = [];
|
|
21
|
+
let meta;
|
|
22
|
+
const onMsgExtracted = opts.onMsgExtracted;
|
|
23
|
+
const onMetaExtracted = opts.onMetaExtracted;
|
|
24
|
+
opts = {
|
|
25
|
+
...opts,
|
|
26
|
+
additionalComponentNames: [
|
|
27
|
+
'$formatMessage',
|
|
28
|
+
...(opts.additionalComponentNames || []),
|
|
29
|
+
],
|
|
30
|
+
onMsgExtracted(filePath, msgs) {
|
|
31
|
+
if (opts.extractSourceLocation) {
|
|
32
|
+
msgs = msgs.map(msg => ({
|
|
33
|
+
...msg,
|
|
34
|
+
...calculateLineColFromOffset(source, msg.start),
|
|
35
|
+
}));
|
|
36
|
+
}
|
|
37
|
+
messages = messages.concat(msgs);
|
|
38
|
+
if (onMsgExtracted) {
|
|
39
|
+
onMsgExtracted(filePath, msgs);
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
onMetaExtracted(filePath, m) {
|
|
43
|
+
meta = m;
|
|
44
|
+
if (onMetaExtracted) {
|
|
45
|
+
onMetaExtracted(filePath, m);
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
if (!opts.overrideIdFn && idInterpolationPattern) {
|
|
50
|
+
opts = {
|
|
51
|
+
...opts,
|
|
52
|
+
overrideIdFn: (id, defaultMessage, description, fileName) => id ||
|
|
53
|
+
interpolateName({
|
|
54
|
+
resourcePath: fileName,
|
|
55
|
+
}, idInterpolationPattern, {
|
|
56
|
+
content: description
|
|
57
|
+
? `${defaultMessage}#${typeof description === 'string'
|
|
58
|
+
? description
|
|
59
|
+
: stringify(description)}`
|
|
60
|
+
: defaultMessage,
|
|
61
|
+
}),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
debug('Processing opts for %s: %s', fn, opts);
|
|
65
|
+
const scriptParseFn = parseScript(opts, fn);
|
|
66
|
+
if (fn.endsWith('.vue')) {
|
|
67
|
+
debug('Processing %s using vue extractor', fn);
|
|
68
|
+
const { parseFile } = await import('./vue_extractor.js');
|
|
69
|
+
parseFile(source, fn, scriptParseFn);
|
|
70
|
+
}
|
|
71
|
+
else if (fn.endsWith('.hbs')) {
|
|
72
|
+
debug('Processing %s using hbs extractor', fn);
|
|
73
|
+
const { parseFile } = await import('./hbs_extractor.js');
|
|
74
|
+
parseFile(source, fn, opts);
|
|
75
|
+
}
|
|
76
|
+
else if (fn.endsWith('.gts') || fn.endsWith('.gjs')) {
|
|
77
|
+
debug('Processing %s as gts/gjs file', fn);
|
|
78
|
+
const { parseFile } = await import('./gts_extractor.js');
|
|
79
|
+
parseFile(source, fn, opts);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
debug('Processing %s using typescript extractor', fn);
|
|
83
|
+
scriptParseFn(source);
|
|
84
|
+
}
|
|
85
|
+
debug('Done extracting %s messages: %s', fn, messages);
|
|
86
|
+
if (meta) {
|
|
87
|
+
debug('Extracted meta:', meta);
|
|
88
|
+
messages.forEach(m => (m.meta = meta));
|
|
89
|
+
}
|
|
90
|
+
return { messages, meta };
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Extract strings from source files
|
|
94
|
+
* @param files list of files
|
|
95
|
+
* @param extractOpts extract options
|
|
96
|
+
* @returns messages serialized as JSON string since key order
|
|
97
|
+
* matters for some `format`
|
|
98
|
+
*/
|
|
99
|
+
export async function extract(files, extractOpts) {
|
|
100
|
+
const { throws, readFromStdin, flatten, ...opts } = extractOpts;
|
|
101
|
+
let rawResults = [];
|
|
102
|
+
try {
|
|
103
|
+
if (readFromStdin) {
|
|
104
|
+
debug(`Reading input from stdin`);
|
|
105
|
+
// Read from stdin
|
|
106
|
+
if (process.stdin.isTTY) {
|
|
107
|
+
warn('Reading source file from TTY.');
|
|
108
|
+
}
|
|
109
|
+
const stdinSource = await getStdinAsString();
|
|
110
|
+
rawResults = [await processFile(stdinSource, 'dummy', opts)];
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
rawResults = await Promise.all(files.map(async (fn) => {
|
|
114
|
+
debug('Extracting file:', fn);
|
|
115
|
+
const source = await readFile(fn, 'utf8');
|
|
116
|
+
return processFile(source, fn, opts);
|
|
117
|
+
}));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
catch (e) {
|
|
121
|
+
if (throws) {
|
|
122
|
+
throw e;
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
warn(String(e));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const formatter = await resolveBuiltinFormatter(opts.format);
|
|
129
|
+
const extractionResults = rawResults.filter((r) => !!r);
|
|
130
|
+
const extractedMessages = new Map();
|
|
131
|
+
for (const { messages } of extractionResults) {
|
|
132
|
+
for (const message of messages) {
|
|
133
|
+
const { id, description, defaultMessage } = message;
|
|
134
|
+
if (!id) {
|
|
135
|
+
const error = new Error(`[FormatJS CLI] Missing message id for message:
|
|
136
|
+
${JSON.stringify(message, undefined, 2)}`);
|
|
137
|
+
if (throws) {
|
|
138
|
+
throw error;
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
warn(error.message);
|
|
142
|
+
}
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
if (extractedMessages.has(id)) {
|
|
146
|
+
const existing = extractedMessages.get(id);
|
|
147
|
+
if (stringify(description) !== stringify(existing.description) ||
|
|
148
|
+
defaultMessage !== existing.defaultMessage) {
|
|
149
|
+
const error = new Error(`[FormatJS CLI] Duplicate message id: "${id}", ` +
|
|
150
|
+
'but the `description` and/or `defaultMessage` are different.');
|
|
151
|
+
if (throws) {
|
|
152
|
+
throw error;
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
warn(error.message);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
extractedMessages.set(id, message);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
const results = {};
|
|
163
|
+
const messages = Array.from(extractedMessages.values());
|
|
164
|
+
for (const { id, ...msg } of messages) {
|
|
165
|
+
if (flatten && msg.defaultMessage) {
|
|
166
|
+
msg.defaultMessage = printAST(hoistSelectors(parse(msg.defaultMessage)));
|
|
167
|
+
}
|
|
168
|
+
results[id] = msg;
|
|
169
|
+
}
|
|
170
|
+
if (typeof formatter.serialize === 'function') {
|
|
171
|
+
return formatter.serialize(formatter.format(results));
|
|
172
|
+
}
|
|
173
|
+
return stringify(formatter.format(results), {
|
|
174
|
+
space: 2,
|
|
175
|
+
cmp: formatter.compareMessages || undefined,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Extract strings from source files, also writes to a file.
|
|
180
|
+
* @param files list of files
|
|
181
|
+
* @param extractOpts extract options
|
|
182
|
+
* @returns A Promise that resolves if output file was written successfully
|
|
183
|
+
*/
|
|
184
|
+
export default async function extractAndWrite(files, extractOpts) {
|
|
185
|
+
const { outFile, ...opts } = extractOpts;
|
|
186
|
+
const serializedResult = (await extract(files, opts)) + '\n';
|
|
187
|
+
if (outFile) {
|
|
188
|
+
debug('Writing output file:', outFile);
|
|
189
|
+
return outputFile(outFile, serializedResult);
|
|
190
|
+
}
|
|
191
|
+
await writeStdout(serializedResult);
|
|
192
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export const format = msgs => {
|
|
2
|
+
const results = {};
|
|
3
|
+
for (const [id, msg] of Object.entries(msgs)) {
|
|
4
|
+
results[id] = {
|
|
5
|
+
message: msg.defaultMessage,
|
|
6
|
+
description: typeof msg.description === 'string'
|
|
7
|
+
? msg.description
|
|
8
|
+
: JSON.stringify(msg.description),
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
return results;
|
|
12
|
+
};
|
|
13
|
+
export const compile = msgs => {
|
|
14
|
+
const results = {};
|
|
15
|
+
for (const [id, msg] of Object.entries(msgs)) {
|
|
16
|
+
if (id === 'smartling') {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
results[id] = msg.message;
|
|
20
|
+
}
|
|
21
|
+
return results;
|
|
22
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { MessageDescriptor } from '@formatjs/ts-transformer';
|
|
2
|
+
export type FormatFn<T = Record<string, MessageDescriptor>> = (msgs: Record<string, MessageDescriptor>) => T;
|
|
3
|
+
export type CompileFn<T = Record<string, MessageDescriptor>> = (msgs: T) => Record<string, string>;
|
|
4
|
+
export type SerializeFn<T = Record<string, MessageDescriptor>> = (msgs: T) => string;
|
|
5
|
+
export declare const format: FormatFn;
|
|
6
|
+
export declare const compile: CompileFn;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Comparator } from 'json-stable-stringify';
|
|
2
|
+
import { CompileFn, FormatFn, SerializeFn } from './default';
|
|
3
|
+
export interface Formatter<T> {
|
|
4
|
+
serialize?: SerializeFn<T>;
|
|
5
|
+
format: FormatFn<T>;
|
|
6
|
+
compile: CompileFn<T>;
|
|
7
|
+
compareMessages?: Comparator;
|
|
8
|
+
}
|
|
9
|
+
export declare function resolveBuiltinFormatter(format?: string | Formatter<unknown>): Promise<any>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { resolve } from 'path';
|
|
2
|
+
import { pathToFileURL } from 'url';
|
|
3
|
+
import * as crowdin from './crowdin';
|
|
4
|
+
import * as defaultFormatter from './default';
|
|
5
|
+
import * as lokalise from './lokalise';
|
|
6
|
+
import * as simple from './simple';
|
|
7
|
+
import * as smartling from './smartling';
|
|
8
|
+
import * as transifex from './transifex';
|
|
9
|
+
export async function resolveBuiltinFormatter(format) {
|
|
10
|
+
if (!format) {
|
|
11
|
+
return defaultFormatter;
|
|
12
|
+
}
|
|
13
|
+
if (typeof format !== 'string') {
|
|
14
|
+
return format;
|
|
15
|
+
}
|
|
16
|
+
switch (format) {
|
|
17
|
+
case 'transifex':
|
|
18
|
+
return transifex;
|
|
19
|
+
case 'smartling':
|
|
20
|
+
return smartling;
|
|
21
|
+
case 'simple':
|
|
22
|
+
return simple;
|
|
23
|
+
case 'lokalise':
|
|
24
|
+
return lokalise;
|
|
25
|
+
case 'crowdin':
|
|
26
|
+
return crowdin;
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
// eslint-disable-next-line import/dynamic-import-chunkname
|
|
30
|
+
return import(pathToFileURL(resolve(process.cwd(), format)).href);
|
|
31
|
+
}
|
|
32
|
+
catch (e) {
|
|
33
|
+
console.error(`Cannot resolve formatter ${format}`);
|
|
34
|
+
throw e;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { CompileFn, FormatFn } from './default';
|
|
2
|
+
export type StructuredJson = Record<string, {
|
|
3
|
+
translation: string;
|
|
4
|
+
notes?: string;
|
|
5
|
+
context?: string;
|
|
6
|
+
limit?: string;
|
|
7
|
+
}>;
|
|
8
|
+
export declare const format: FormatFn<StructuredJson>;
|
|
9
|
+
export declare const compile: CompileFn<StructuredJson>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export const format = msgs => {
|
|
2
|
+
const results = {};
|
|
3
|
+
for (const [id, msg] of Object.entries(msgs)) {
|
|
4
|
+
results[id] = {
|
|
5
|
+
translation: msg.defaultMessage,
|
|
6
|
+
notes: typeof msg.description === 'string'
|
|
7
|
+
? msg.description
|
|
8
|
+
: JSON.stringify(msg.description),
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
return results;
|
|
12
|
+
};
|
|
13
|
+
export const compile = msgs => {
|
|
14
|
+
const results = {};
|
|
15
|
+
for (const [id, msg] of Object.entries(msgs)) {
|
|
16
|
+
results[id] = msg.translation;
|
|
17
|
+
}
|
|
18
|
+
return results;
|
|
19
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Comparator } from 'json-stable-stringify';
|
|
2
|
+
import { CompileFn, FormatFn } from './default';
|
|
3
|
+
export interface SmartlingDirectives {
|
|
4
|
+
translate_paths: [
|
|
5
|
+
{
|
|
6
|
+
path: string;
|
|
7
|
+
key: string;
|
|
8
|
+
instruction: string;
|
|
9
|
+
}
|
|
10
|
+
];
|
|
11
|
+
variants_enabled: boolean;
|
|
12
|
+
string_format: string;
|
|
13
|
+
[k: string]: any;
|
|
14
|
+
}
|
|
15
|
+
export type SmartlingJson = {
|
|
16
|
+
smartling: SmartlingDirectives;
|
|
17
|
+
} & Record<string, {
|
|
18
|
+
message: string;
|
|
19
|
+
description?: string;
|
|
20
|
+
}>;
|
|
21
|
+
export declare const format: FormatFn<SmartlingJson>;
|
|
22
|
+
export declare const compareMessages: Comparator;
|
|
23
|
+
export declare const compile: CompileFn<SmartlingJson>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export const format = msgs => {
|
|
2
|
+
const results = {
|
|
3
|
+
smartling: {
|
|
4
|
+
translate_paths: [
|
|
5
|
+
{
|
|
6
|
+
path: '*/message',
|
|
7
|
+
key: '{*}/message',
|
|
8
|
+
instruction: '*/description',
|
|
9
|
+
},
|
|
10
|
+
],
|
|
11
|
+
variants_enabled: true,
|
|
12
|
+
string_format: 'icu',
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
for (const [id, msg] of Object.entries(msgs)) {
|
|
16
|
+
results[id] = {
|
|
17
|
+
message: msg.defaultMessage,
|
|
18
|
+
description: typeof msg.description === 'string'
|
|
19
|
+
? msg.description
|
|
20
|
+
: JSON.stringify(msg.description),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
return results;
|
|
24
|
+
};
|
|
25
|
+
export const compareMessages = (el1, el2) => {
|
|
26
|
+
// `smartling` has to be the 1st key
|
|
27
|
+
if (el1.key === 'smartling') {
|
|
28
|
+
return -1;
|
|
29
|
+
}
|
|
30
|
+
if (el2.key === 'smartling') {
|
|
31
|
+
return 1;
|
|
32
|
+
}
|
|
33
|
+
return el1.key < el2.key ? -1 : el1.key === el2.key ? 0 : 1;
|
|
34
|
+
};
|
|
35
|
+
export const compile = msgs => {
|
|
36
|
+
const results = {};
|
|
37
|
+
for (const [id, msg] of Object.entries(msgs)) {
|
|
38
|
+
if (id === 'smartling') {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
results[id] = msg.message;
|
|
42
|
+
}
|
|
43
|
+
return results;
|
|
44
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { CompileFn, FormatFn } from './default';
|
|
2
|
+
export type StructuredJson = Record<string, {
|
|
3
|
+
string: string;
|
|
4
|
+
developer_comment?: string;
|
|
5
|
+
context?: string;
|
|
6
|
+
character_limit?: string;
|
|
7
|
+
}>;
|
|
8
|
+
export declare const format: FormatFn<StructuredJson>;
|
|
9
|
+
export declare const compile: CompileFn<StructuredJson>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export const format = msgs => {
|
|
2
|
+
const results = {};
|
|
3
|
+
for (const [id, msg] of Object.entries(msgs)) {
|
|
4
|
+
results[id] = {
|
|
5
|
+
string: msg.defaultMessage,
|
|
6
|
+
developer_comment: typeof msg.description === 'string'
|
|
7
|
+
? msg.description
|
|
8
|
+
: JSON.stringify(msg.description),
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
return results;
|
|
12
|
+
};
|
|
13
|
+
export const compile = msgs => {
|
|
14
|
+
const results = {};
|
|
15
|
+
for (const [id, msg] of Object.entries(msgs)) {
|
|
16
|
+
results[id] = msg.string;
|
|
17
|
+
}
|
|
18
|
+
return results;
|
|
19
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function parseFile(source: string, fileName: string, options: any): void;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Preprocessor } from 'content-tag';
|
|
2
|
+
import { parseFile as parseHbsFile } from './hbs_extractor';
|
|
3
|
+
import { parseScript } from './parse_script';
|
|
4
|
+
let p = new Preprocessor();
|
|
5
|
+
export function parseFile(source, fileName, options) {
|
|
6
|
+
const scriptParseFn = parseScript(options, fileName);
|
|
7
|
+
const transformedSource = p.process(source, { filename: fileName });
|
|
8
|
+
scriptParseFn(transformedSource.code);
|
|
9
|
+
// extract template from transformed source to then run through hbs processor
|
|
10
|
+
const parseResult = p.parse(source, { filename: fileName });
|
|
11
|
+
for (let parsed of parseResult) {
|
|
12
|
+
parseHbsFile(parsed.contents, fileName, options);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function parseFile(source: string, fileName: string, options: any): void;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { transform } from 'ember-template-recast';
|
|
2
|
+
function extractText(node, fileName, options) {
|
|
3
|
+
if (!options.onMsgExtracted)
|
|
4
|
+
return;
|
|
5
|
+
if (!options.overrideIdFn)
|
|
6
|
+
return;
|
|
7
|
+
if (node.path.type !== 'PathExpression')
|
|
8
|
+
return;
|
|
9
|
+
if (['format-message', 'formatMessage'].includes(node.path.original)) {
|
|
10
|
+
let [first, second] = node.params;
|
|
11
|
+
if (first.type !== 'StringLiteral')
|
|
12
|
+
return;
|
|
13
|
+
let message = first?.value;
|
|
14
|
+
let desc;
|
|
15
|
+
if (second?.type === 'StringLiteral') {
|
|
16
|
+
desc = second.value?.trim().replace(/\s+/gm, ' ');
|
|
17
|
+
}
|
|
18
|
+
let defaultMessage = message?.trim().replace(/\s+/gm, ' ');
|
|
19
|
+
let id = typeof options.overrideIdFn === 'string'
|
|
20
|
+
? options.overrideIdFn
|
|
21
|
+
: options.overrideIdFn(undefined, defaultMessage, desc, fileName);
|
|
22
|
+
options.onMsgExtracted(fileName, [
|
|
23
|
+
{
|
|
24
|
+
id: id,
|
|
25
|
+
defaultMessage: defaultMessage,
|
|
26
|
+
description: desc,
|
|
27
|
+
},
|
|
28
|
+
]);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export function parseFile(source, fileName, options) {
|
|
32
|
+
let visitor = function () {
|
|
33
|
+
return {
|
|
34
|
+
MustacheStatement(node) {
|
|
35
|
+
extractText(node, fileName, options);
|
|
36
|
+
},
|
|
37
|
+
SubExpression(node) {
|
|
38
|
+
extractText(node, fileName, options);
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
// SAFETY: ember-template-recast's types are out of date,
|
|
43
|
+
// but it does not affect runtime
|
|
44
|
+
transform(source, visitor);
|
|
45
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Opts } from '@formatjs/ts-transformer';
|
|
2
|
+
/**
|
|
3
|
+
* Invoid TypeScript module transpilation with our TS transformer
|
|
4
|
+
* @param opts Formatjs TS Transformer opt
|
|
5
|
+
* @param fn filename
|
|
6
|
+
*/
|
|
7
|
+
export declare function parseScript(opts: Opts, fn?: string): (source: string) => void;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { transformWithTs } from '@formatjs/ts-transformer';
|
|
2
|
+
import ts from 'typescript';
|
|
3
|
+
import { debug } from './console_utils';
|
|
4
|
+
/**
|
|
5
|
+
* Invoid TypeScript module transpilation with our TS transformer
|
|
6
|
+
* @param opts Formatjs TS Transformer opt
|
|
7
|
+
* @param fn filename
|
|
8
|
+
*/
|
|
9
|
+
export function parseScript(opts, fn) {
|
|
10
|
+
return (source) => {
|
|
11
|
+
let output;
|
|
12
|
+
try {
|
|
13
|
+
debug('Using TS compiler to process file', fn);
|
|
14
|
+
output = ts.transpileModule(source, {
|
|
15
|
+
compilerOptions: {
|
|
16
|
+
allowJs: true,
|
|
17
|
+
target: ts.ScriptTarget.ESNext,
|
|
18
|
+
noEmit: true,
|
|
19
|
+
experimentalDecorators: true,
|
|
20
|
+
},
|
|
21
|
+
reportDiagnostics: true,
|
|
22
|
+
fileName: fn,
|
|
23
|
+
transformers: {
|
|
24
|
+
before: [transformWithTs(ts, opts)],
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
catch (e) {
|
|
29
|
+
if (e instanceof Error) {
|
|
30
|
+
e.message = `Error processing file ${fn}
|
|
31
|
+
${e.message || ''}`;
|
|
32
|
+
}
|
|
33
|
+
throw e;
|
|
34
|
+
}
|
|
35
|
+
if (output.diagnostics) {
|
|
36
|
+
const errs = output.diagnostics.filter(d => d.category === ts.DiagnosticCategory.Error);
|
|
37
|
+
if (errs.length) {
|
|
38
|
+
throw new Error(ts.formatDiagnosticsWithColorAndContext(errs, {
|
|
39
|
+
getCanonicalFileName: fileName => fileName,
|
|
40
|
+
getCurrentDirectory: () => process.cwd(),
|
|
41
|
+
getNewLine: () => ts.sys.newLine,
|
|
42
|
+
}));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { MessageFormatElement } from '@formatjs/icu-messageformat-parser';
|
|
2
|
+
export declare function generateXXLS(msg: string | MessageFormatElement[]): MessageFormatElement[];
|
|
3
|
+
export declare function generateXXAC(msg: string | MessageFormatElement[]): MessageFormatElement[];
|
|
4
|
+
export declare function generateXXHA(msg: string | MessageFormatElement[]): MessageFormatElement[];
|
|
5
|
+
/**
|
|
6
|
+
* accented - Ȧȧƈƈḗḗƞŧḗḗḓ Ḗḗƞɠŀīīşħ
|
|
7
|
+
* --------------------------------
|
|
8
|
+
*
|
|
9
|
+
* This locale replaces all Latin characters with their accented equivalents, and duplicates some
|
|
10
|
+
* vowels to create roughly 30% longer strings. Strings are wrapped in markers (square brackets),
|
|
11
|
+
* which help with detecting truncation.
|
|
12
|
+
*/
|
|
13
|
+
export declare function generateENXA(msg: string | MessageFormatElement[]): MessageFormatElement[];
|
|
14
|
+
/**
|
|
15
|
+
* bidi - ɥsıʅƃuƎ ıpıԐ
|
|
16
|
+
* -------------------
|
|
17
|
+
*
|
|
18
|
+
* This strategy replaces all Latin characters with their 180 degree rotated versions and enforces
|
|
19
|
+
* right to left text flow using Unicode UAX#9 Explicit Directional Embeddings. In this mode, the UI
|
|
20
|
+
* directionality will also be set to right-to-left.
|
|
21
|
+
*/
|
|
22
|
+
export declare function generateENXB(msg: string | MessageFormatElement[]): MessageFormatElement[];
|