@formatjs/ts-transformer 3.14.1 → 4.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/index.d.ts +3 -3
- package/index.js +3 -6
- package/package.json +3 -2
- package/src/console_utils.js +12 -16
- package/src/interpolate-name.js +6 -9
- package/src/transform.d.ts +1 -1
- package/src/transform.js +18 -22
- package/src/types.js +1 -2
- package/ts-jest-integration.js +5 -9
- package/lib_esnext/index.d.ts +0 -3
- package/lib_esnext/index.js +0 -3
- package/lib_esnext/src/console_utils.d.ts +0 -3
- package/lib_esnext/src/console_utils.js +0 -25
- package/lib_esnext/src/interpolate-name.d.ts +0 -14
- package/lib_esnext/src/interpolate-name.js +0 -85
- package/lib_esnext/src/transform.d.ts +0 -77
- package/lib_esnext/src/transform.js +0 -510
- package/lib_esnext/src/types.d.ts +0 -11
- package/lib_esnext/src/types.js +0 -1
- package/lib_esnext/ts-jest-integration.d.ts +0 -6
- package/lib_esnext/ts-jest-integration.js +0 -6
package/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export * from './src/transform';
|
|
2
|
-
export * from './src/types';
|
|
3
|
-
export * from './src/interpolate-name';
|
|
1
|
+
export * from './src/transform.js';
|
|
2
|
+
export * from './src/types.js';
|
|
3
|
+
export * from './src/interpolate-name.js';
|
package/index.js
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
tslib_1.__exportStar(require("./src/transform"), exports);
|
|
5
|
-
tslib_1.__exportStar(require("./src/types"), exports);
|
|
6
|
-
tslib_1.__exportStar(require("./src/interpolate-name"), exports);
|
|
1
|
+
export * from './src/transform.js';
|
|
2
|
+
export * from './src/types.js';
|
|
3
|
+
export * from './src/interpolate-name.js';
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@formatjs/ts-transformer",
|
|
3
3
|
"description": "TS Compiler transformer for formatjs",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "4.0.0",
|
|
5
5
|
"author": "Long Ho <holevietlong@gmail.com>",
|
|
6
6
|
"bugs": "https://github.com/formatjs/formatjs/issues",
|
|
7
7
|
"dependencies": {
|
|
8
|
-
"@formatjs/icu-messageformat-parser": "
|
|
8
|
+
"@formatjs/icu-messageformat-parser": "3.0.0",
|
|
9
9
|
"@types/node": "^22.0.0",
|
|
10
10
|
"chalk": "^4.1.2",
|
|
11
11
|
"json-stable-stringify": "^1.3.0",
|
|
@@ -35,5 +35,6 @@
|
|
|
35
35
|
}
|
|
36
36
|
},
|
|
37
37
|
"repository": "formatjs/formatjs.git",
|
|
38
|
+
"type": "module",
|
|
38
39
|
"types": "index.d.ts"
|
|
39
40
|
}
|
package/src/console_utils.js
CHANGED
|
@@ -1,30 +1,26 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
exports.warn = warn;
|
|
5
|
-
exports.error = error;
|
|
6
|
-
const chalk_1 = require("chalk");
|
|
7
|
-
const util_1 = require("util");
|
|
1
|
+
import * as chalkNs from 'chalk';
|
|
2
|
+
import { format } from 'util';
|
|
3
|
+
const chalk = chalkNs.default ?? chalkNs;
|
|
8
4
|
const LEVEL_COLORS = {
|
|
9
|
-
debug:
|
|
10
|
-
warn:
|
|
11
|
-
error:
|
|
5
|
+
debug: chalk.green,
|
|
6
|
+
warn: chalk.yellow,
|
|
7
|
+
error: chalk.red,
|
|
12
8
|
};
|
|
13
9
|
function label(level, message) {
|
|
14
10
|
return `[@formatjs/ts-transformer] [${LEVEL_COLORS[level](level.toUpperCase())}] ${message}`;
|
|
15
11
|
}
|
|
16
|
-
async function debug(message, ...args) {
|
|
12
|
+
export async function debug(message, ...args) {
|
|
17
13
|
if (process.env.LOG_LEVEL !== 'debug') {
|
|
18
14
|
return;
|
|
19
15
|
}
|
|
20
|
-
console.error(
|
|
16
|
+
console.error(format(label('debug', message), ...args));
|
|
21
17
|
console.error('\n');
|
|
22
18
|
}
|
|
23
|
-
function warn(message, ...args) {
|
|
24
|
-
console.error(
|
|
19
|
+
export function warn(message, ...args) {
|
|
20
|
+
console.error(format(label('warn', message), ...args));
|
|
25
21
|
console.error('\n');
|
|
26
22
|
}
|
|
27
|
-
function error(message, ...args) {
|
|
28
|
-
console.error(
|
|
23
|
+
export function error(message, ...args) {
|
|
24
|
+
console.error(format(label('error', message), ...args));
|
|
29
25
|
console.error('\n');
|
|
30
26
|
}
|
package/src/interpolate-name.js
CHANGED
|
@@ -1,15 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
exports.interpolateName = interpolateName;
|
|
4
|
-
const tslib_1 = require("tslib");
|
|
5
|
-
const crypto_1 = require("crypto");
|
|
6
|
-
const path = tslib_1.__importStar(require("path"));
|
|
1
|
+
import { createHash } from 'crypto';
|
|
2
|
+
import * as path from 'path';
|
|
7
3
|
function getHashDigest(content, hashType = 'md5', digestType = 'hex', length = 9999) {
|
|
8
|
-
const hasher =
|
|
4
|
+
const hasher = createHash(hashType);
|
|
9
5
|
hasher.update(content);
|
|
10
6
|
return hasher.digest(digestType).slice(0, length);
|
|
11
7
|
}
|
|
12
|
-
function interpolateName(loaderContext, name, options) {
|
|
8
|
+
export function interpolateName(loaderContext, name, options) {
|
|
13
9
|
let filename;
|
|
14
10
|
const hasQuery = loaderContext.resourceQuery && loaderContext.resourceQuery.length > 1;
|
|
15
11
|
if (typeof name === 'function') {
|
|
@@ -76,10 +72,11 @@ function interpolateName(loaderContext, name, options) {
|
|
|
76
72
|
.replace(/\[query\]/gi, () => query);
|
|
77
73
|
if (regExp && loaderContext.resourcePath) {
|
|
78
74
|
const match = loaderContext.resourcePath.match(new RegExp(regExp));
|
|
79
|
-
match
|
|
75
|
+
if (match) {
|
|
80
76
|
match.forEach((matched, i) => {
|
|
81
77
|
url = url.replace(new RegExp('\\[' + i + '\\]', 'ig'), matched);
|
|
82
78
|
});
|
|
79
|
+
}
|
|
83
80
|
}
|
|
84
81
|
if (typeof loaderContext.options === 'object' &&
|
|
85
82
|
typeof loaderContext.options.customInterpolateName === 'function') {
|
package/src/transform.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as typescript from 'typescript';
|
|
2
|
-
import { MessageDescriptor } from './types';
|
|
2
|
+
import { MessageDescriptor } from './types.js';
|
|
3
3
|
export type Extractor = (filePath: string, msgs: MessageDescriptor[]) => void;
|
|
4
4
|
export type MetaExtractor = (filePath: string, meta: Record<string, string>) => void;
|
|
5
5
|
export type InterpolateNameFn = (id?: MessageDescriptor['id'], defaultMessage?: MessageDescriptor['defaultMessage'], description?: MessageDescriptor['description'], filePath?: string) => string;
|
package/src/transform.js
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
const json_stable_stringify_1 = tslib_1.__importDefault(require("json-stable-stringify"));
|
|
8
|
-
const typescript = tslib_1.__importStar(require("typescript"));
|
|
9
|
-
const console_utils_1 = require("./console_utils");
|
|
10
|
-
const interpolate_name_1 = require("./interpolate-name");
|
|
1
|
+
import { parse } from '@formatjs/icu-messageformat-parser';
|
|
2
|
+
import * as stringifyNs from 'json-stable-stringify';
|
|
3
|
+
import * as typescript from 'typescript';
|
|
4
|
+
import { debug } from './console_utils.js';
|
|
5
|
+
import { interpolateName } from './interpolate-name.js';
|
|
6
|
+
const stringify = stringifyNs.default || stringifyNs;
|
|
11
7
|
const MESSAGE_DESC_KEYS = [
|
|
12
8
|
'id',
|
|
13
9
|
'defaultMessage',
|
|
@@ -29,7 +25,7 @@ function isValidIdentifier(k) {
|
|
|
29
25
|
new Function(`return {${k}:1}`);
|
|
30
26
|
return true;
|
|
31
27
|
}
|
|
32
|
-
catch
|
|
28
|
+
catch {
|
|
33
29
|
return false;
|
|
34
30
|
}
|
|
35
31
|
}
|
|
@@ -292,11 +288,11 @@ function extractMessageDescriptor(ts, node, { overrideIdFn, extractSourceLocatio
|
|
|
292
288
|
switch (typeof overrideIdFn) {
|
|
293
289
|
case 'string':
|
|
294
290
|
if (!msg.id) {
|
|
295
|
-
msg.id =
|
|
291
|
+
msg.id = interpolateName({ resourcePath: sf.fileName }, overrideIdFn, {
|
|
296
292
|
content: msg.description
|
|
297
293
|
? `${msg.defaultMessage}#${typeof msg.description === 'string'
|
|
298
294
|
? msg.description
|
|
299
|
-
: (
|
|
295
|
+
: stringify(msg.description)}`
|
|
300
296
|
: msg.defaultMessage,
|
|
301
297
|
});
|
|
302
298
|
}
|
|
@@ -364,7 +360,7 @@ function setAttributesInObject(ts, factory, node, msg, ast) {
|
|
|
364
360
|
...(msg.defaultMessage
|
|
365
361
|
? [
|
|
366
362
|
factory.createPropertyAssignment('defaultMessage', ast
|
|
367
|
-
? messageASTToTSNode(factory,
|
|
363
|
+
? messageASTToTSNode(factory, parse(msg.defaultMessage))
|
|
368
364
|
: factory.createStringLiteral(msg.defaultMessage)),
|
|
369
365
|
]
|
|
370
366
|
: []),
|
|
@@ -387,7 +383,7 @@ function generateNewProperties(ts, factory, node, msg, ast) {
|
|
|
387
383
|
...(msg.defaultMessage
|
|
388
384
|
? [
|
|
389
385
|
factory.createJsxAttribute(factory.createIdentifier('defaultMessage'), ast
|
|
390
|
-
? factory.createJsxExpression(undefined, messageASTToTSNode(factory,
|
|
386
|
+
? factory.createJsxExpression(undefined, messageASTToTSNode(factory, parse(msg.defaultMessage)))
|
|
391
387
|
: factory.createStringLiteral(msg.defaultMessage)),
|
|
392
388
|
]
|
|
393
389
|
: []),
|
|
@@ -426,7 +422,7 @@ function extractMessagesFromCallExpression(ts, factory, node, opts, sf) {
|
|
|
426
422
|
if (!msgs.length) {
|
|
427
423
|
return node;
|
|
428
424
|
}
|
|
429
|
-
|
|
425
|
+
debug('Multiple messages extracted from "%s": %s', sf.fileName, msgs);
|
|
430
426
|
if (typeof onMsgExtracted === 'function') {
|
|
431
427
|
onMsgExtracted(sf.fileName, msgs);
|
|
432
428
|
}
|
|
@@ -454,7 +450,7 @@ function extractMessagesFromCallExpression(ts, factory, node, opts, sf) {
|
|
|
454
450
|
if (!msg) {
|
|
455
451
|
return node;
|
|
456
452
|
}
|
|
457
|
-
|
|
453
|
+
debug('Message extracted from "%s": %s', sf.fileName, msg);
|
|
458
454
|
if (typeof onMsgExtracted === 'function') {
|
|
459
455
|
onMsgExtracted(sf.fileName, [msg]);
|
|
460
456
|
}
|
|
@@ -483,14 +479,14 @@ function getVisitor(ts, ctx, sf, opts) {
|
|
|
483
479
|
};
|
|
484
480
|
return visitor;
|
|
485
481
|
}
|
|
486
|
-
function transformWithTs(ts, opts) {
|
|
482
|
+
export function transformWithTs(ts, opts) {
|
|
487
483
|
opts = { ...DEFAULT_OPTS, ...opts };
|
|
488
|
-
|
|
484
|
+
debug('Transforming options', opts);
|
|
489
485
|
const transformFn = ctx => {
|
|
490
486
|
return sf => {
|
|
491
487
|
const pragmaResult = PRAGMA_REGEX.exec(sf.text);
|
|
492
488
|
if (pragmaResult) {
|
|
493
|
-
|
|
489
|
+
debug('Pragma found', pragmaResult);
|
|
494
490
|
const [, pragma, kvString] = pragmaResult;
|
|
495
491
|
if (pragma === opts.pragma) {
|
|
496
492
|
const kvs = kvString.split(' ');
|
|
@@ -499,7 +495,7 @@ function transformWithTs(ts, opts) {
|
|
|
499
495
|
const [k, v] = kv.split(':');
|
|
500
496
|
result[k] = v;
|
|
501
497
|
}
|
|
502
|
-
|
|
498
|
+
debug('Pragma extracted', result);
|
|
503
499
|
if (typeof opts.onMetaExtracted === 'function') {
|
|
504
500
|
opts.onMetaExtracted(sf.fileName, result);
|
|
505
501
|
}
|
|
@@ -510,6 +506,6 @@ function transformWithTs(ts, opts) {
|
|
|
510
506
|
};
|
|
511
507
|
return transformFn;
|
|
512
508
|
}
|
|
513
|
-
function transform(opts) {
|
|
509
|
+
export function transform(opts) {
|
|
514
510
|
return transformWithTs(typescript, opts);
|
|
515
511
|
}
|
package/src/types.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1
|
+
export {};
|
package/ts-jest-integration.js
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
exports.name = '@formatjs/ts-transformer';
|
|
7
|
-
exports.version = '2.10.1';
|
|
8
|
-
function factory(compilerInstance, opts) {
|
|
9
|
-
return (0, _1.transformWithTs)(compilerInstance.configSet.compilerModule, opts);
|
|
1
|
+
import { transformWithTs } from '.';
|
|
2
|
+
export const name = '@formatjs/ts-transformer';
|
|
3
|
+
export const version = '2.10.1';
|
|
4
|
+
export function factory(compilerInstance, opts) {
|
|
5
|
+
return transformWithTs(compilerInstance.configSet.compilerModule, opts);
|
|
10
6
|
}
|
package/lib_esnext/index.d.ts
DELETED
package/lib_esnext/index.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { green, red, yellow } from 'chalk';
|
|
2
|
-
import { format } from 'util';
|
|
3
|
-
const LEVEL_COLORS = {
|
|
4
|
-
debug: green,
|
|
5
|
-
warn: yellow,
|
|
6
|
-
error: red,
|
|
7
|
-
};
|
|
8
|
-
function label(level, message) {
|
|
9
|
-
return `[@formatjs/ts-transformer] [${LEVEL_COLORS[level](level.toUpperCase())}] ${message}`;
|
|
10
|
-
}
|
|
11
|
-
export async function debug(message, ...args) {
|
|
12
|
-
if (process.env.LOG_LEVEL !== 'debug') {
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
|
-
console.error(format(label('debug', message), ...args));
|
|
16
|
-
console.error('\n');
|
|
17
|
-
}
|
|
18
|
-
export function warn(message, ...args) {
|
|
19
|
-
console.error(format(label('warn', message), ...args));
|
|
20
|
-
console.error('\n');
|
|
21
|
-
}
|
|
22
|
-
export function error(message, ...args) {
|
|
23
|
-
console.error(format(label('error', message), ...args));
|
|
24
|
-
console.error('\n');
|
|
25
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
export interface LoaderContext {
|
|
2
|
-
resourceQuery?: string;
|
|
3
|
-
resourcePath?: string;
|
|
4
|
-
options?: {
|
|
5
|
-
customInterpolateName(this: LoaderContext, url: string, name: string | NameFn, options: Options): string;
|
|
6
|
-
};
|
|
7
|
-
}
|
|
8
|
-
export interface Options {
|
|
9
|
-
context?: string;
|
|
10
|
-
content?: string;
|
|
11
|
-
regExp?: RegExp;
|
|
12
|
-
}
|
|
13
|
-
export type NameFn = (resourcePath?: string, resourceQuery?: string) => string;
|
|
14
|
-
export declare function interpolateName(loaderContext: LoaderContext, name: string | NameFn, options: Options): string;
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import { createHash } from 'crypto';
|
|
2
|
-
import * as path from 'path';
|
|
3
|
-
function getHashDigest(content, hashType = 'md5', digestType = 'hex', length = 9999) {
|
|
4
|
-
const hasher = createHash(hashType);
|
|
5
|
-
hasher.update(content);
|
|
6
|
-
return hasher.digest(digestType).slice(0, length);
|
|
7
|
-
}
|
|
8
|
-
export function interpolateName(loaderContext, name, options) {
|
|
9
|
-
let filename;
|
|
10
|
-
const hasQuery = loaderContext.resourceQuery && loaderContext.resourceQuery.length > 1;
|
|
11
|
-
if (typeof name === 'function') {
|
|
12
|
-
filename = name(loaderContext.resourcePath, hasQuery ? loaderContext.resourceQuery : undefined);
|
|
13
|
-
}
|
|
14
|
-
else {
|
|
15
|
-
filename = name || '[hash].[ext]';
|
|
16
|
-
}
|
|
17
|
-
const context = options.context;
|
|
18
|
-
const content = options.content;
|
|
19
|
-
const regExp = options.regExp;
|
|
20
|
-
let ext = 'bin';
|
|
21
|
-
let basename = 'file';
|
|
22
|
-
let directory = '';
|
|
23
|
-
let folder = '';
|
|
24
|
-
let query = '';
|
|
25
|
-
if (loaderContext.resourcePath) {
|
|
26
|
-
const parsed = path.parse(loaderContext.resourcePath);
|
|
27
|
-
let resourcePath = loaderContext.resourcePath;
|
|
28
|
-
if (parsed.ext) {
|
|
29
|
-
ext = parsed.ext.slice(1);
|
|
30
|
-
}
|
|
31
|
-
if (parsed.dir) {
|
|
32
|
-
basename = parsed.name;
|
|
33
|
-
resourcePath = parsed.dir + path.sep;
|
|
34
|
-
}
|
|
35
|
-
if (typeof context !== 'undefined') {
|
|
36
|
-
directory = path
|
|
37
|
-
.relative(context, resourcePath + '_')
|
|
38
|
-
.replace(/\\/g, '/')
|
|
39
|
-
.replace(/\.\.(\/)?/g, '_$1');
|
|
40
|
-
directory = directory.slice(0, -1);
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
directory = resourcePath.replace(/\\/g, '/').replace(/\.\.(\/)?/g, '_$1');
|
|
44
|
-
}
|
|
45
|
-
if (directory.length === 1) {
|
|
46
|
-
directory = '';
|
|
47
|
-
}
|
|
48
|
-
else if (directory.length > 1) {
|
|
49
|
-
folder = path.basename(directory);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
if (loaderContext.resourceQuery && loaderContext.resourceQuery.length > 1) {
|
|
53
|
-
query = loaderContext.resourceQuery;
|
|
54
|
-
const hashIdx = query.indexOf('#');
|
|
55
|
-
if (hashIdx >= 0) {
|
|
56
|
-
query = query.slice(0, hashIdx);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
let url = filename;
|
|
60
|
-
if (content) {
|
|
61
|
-
// Match hash template
|
|
62
|
-
url = url
|
|
63
|
-
// `hash` and `contenthash` are same in `loader-utils` context
|
|
64
|
-
// let's keep `hash` for backward compatibility
|
|
65
|
-
.replace(/\[(?:([^:\]]+):)?(?:hash|contenthash)(?::([a-z]+\d*[a-z]*))?(?::(\d+))?\]/gi, (_, hashType, digestType, maxLength) => getHashDigest(content, hashType, digestType, parseInt(maxLength, 10)));
|
|
66
|
-
}
|
|
67
|
-
url = url
|
|
68
|
-
.replace(/\[ext\]/gi, () => ext)
|
|
69
|
-
.replace(/\[name\]/gi, () => basename)
|
|
70
|
-
.replace(/\[path\]/gi, () => directory)
|
|
71
|
-
.replace(/\[folder\]/gi, () => folder)
|
|
72
|
-
.replace(/\[query\]/gi, () => query);
|
|
73
|
-
if (regExp && loaderContext.resourcePath) {
|
|
74
|
-
const match = loaderContext.resourcePath.match(new RegExp(regExp));
|
|
75
|
-
match &&
|
|
76
|
-
match.forEach((matched, i) => {
|
|
77
|
-
url = url.replace(new RegExp('\\[' + i + '\\]', 'ig'), matched);
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
if (typeof loaderContext.options === 'object' &&
|
|
81
|
-
typeof loaderContext.options.customInterpolateName === 'function') {
|
|
82
|
-
url = loaderContext.options.customInterpolateName.call(loaderContext, url, name, options);
|
|
83
|
-
}
|
|
84
|
-
return url;
|
|
85
|
-
}
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import * as typescript from 'typescript';
|
|
2
|
-
import { MessageDescriptor } from './types';
|
|
3
|
-
export type Extractor = (filePath: string, msgs: MessageDescriptor[]) => void;
|
|
4
|
-
export type MetaExtractor = (filePath: string, meta: Record<string, string>) => void;
|
|
5
|
-
export type InterpolateNameFn = (id?: MessageDescriptor['id'], defaultMessage?: MessageDescriptor['defaultMessage'], description?: MessageDescriptor['description'], filePath?: string) => string;
|
|
6
|
-
type TypeScript = typeof typescript;
|
|
7
|
-
export interface Opts {
|
|
8
|
-
/**
|
|
9
|
-
* Parse specific additional custom pragma.
|
|
10
|
-
* This allows you to tag certain file with metadata such as `project`.
|
|
11
|
-
* For example with this file:
|
|
12
|
-
* ```tsx
|
|
13
|
-
* // @intl-meta project:my-custom-project
|
|
14
|
-
* import {FormattedMessage} from 'react-intl';
|
|
15
|
-
* <FormattedMessage defaultMessage="foo" id="bar" />;
|
|
16
|
-
* ```
|
|
17
|
-
* and with option `{pragma: "@intl-meta"}`,
|
|
18
|
-
* we'll parse out `// @intl-meta project:my-custom-project`
|
|
19
|
-
* into `{project: 'my-custom-project'}` in the result file.
|
|
20
|
-
*/
|
|
21
|
-
pragma?: string;
|
|
22
|
-
/**
|
|
23
|
-
* Whether the metadata about the location of the message in the source file
|
|
24
|
-
* should be extracted. If `true`, then `file`, `start`, and `end`
|
|
25
|
-
* fields will exist for each extracted message descriptors.
|
|
26
|
-
* Defaults to `false`.
|
|
27
|
-
*/
|
|
28
|
-
extractSourceLocation?: boolean;
|
|
29
|
-
/**
|
|
30
|
-
* Remove `defaultMessage` field in generated js after extraction.
|
|
31
|
-
*/
|
|
32
|
-
removeDefaultMessage?: boolean;
|
|
33
|
-
/**
|
|
34
|
-
* Additional component names to extract messages from,
|
|
35
|
-
* e.g: `['FormattedFooBarMessage']`.
|
|
36
|
-
*/
|
|
37
|
-
additionalComponentNames?: string[];
|
|
38
|
-
/**
|
|
39
|
-
* Additional function names to extract messages from,
|
|
40
|
-
* e.g: `['formatMessage', '$t']`
|
|
41
|
-
* Default to `['formatMessage']`
|
|
42
|
-
*/
|
|
43
|
-
additionalFunctionNames?: string[];
|
|
44
|
-
/**
|
|
45
|
-
* Callback function that gets called everytime we encountered something
|
|
46
|
-
* that looks like a MessageDescriptor
|
|
47
|
-
*
|
|
48
|
-
* @type {Extractor}
|
|
49
|
-
* @memberof Opts
|
|
50
|
-
*/
|
|
51
|
-
onMsgExtracted?: Extractor;
|
|
52
|
-
/**
|
|
53
|
-
* Callback function that gets called when we successfully parsed meta
|
|
54
|
-
* declared in pragma
|
|
55
|
-
*/
|
|
56
|
-
onMetaExtracted?: MetaExtractor;
|
|
57
|
-
/**
|
|
58
|
-
* webpack-style name interpolation.
|
|
59
|
-
* Can also be a string like '[sha512:contenthash:hex:6]'
|
|
60
|
-
*
|
|
61
|
-
* @type {(InterpolateNameFn | string)}
|
|
62
|
-
* @memberof Opts
|
|
63
|
-
*/
|
|
64
|
-
overrideIdFn?: InterpolateNameFn | string;
|
|
65
|
-
/**
|
|
66
|
-
* Whether to compile `defaultMessage` to AST.
|
|
67
|
-
* This is no-op if `removeDefaultMessage` is `true`
|
|
68
|
-
*/
|
|
69
|
-
ast?: boolean;
|
|
70
|
-
/**
|
|
71
|
-
* Whether to preserve whitespace and newlines.
|
|
72
|
-
*/
|
|
73
|
-
preserveWhitespace?: boolean;
|
|
74
|
-
}
|
|
75
|
-
export declare function transformWithTs(ts: TypeScript, opts: Opts): typescript.TransformerFactory<typescript.SourceFile>;
|
|
76
|
-
export declare function transform(opts: Opts): typescript.TransformerFactory<typescript.SourceFile>;
|
|
77
|
-
export {};
|
|
@@ -1,510 +0,0 @@
|
|
|
1
|
-
import { parse } from '@formatjs/icu-messageformat-parser';
|
|
2
|
-
import stringify from 'json-stable-stringify';
|
|
3
|
-
import * as typescript from 'typescript';
|
|
4
|
-
import { debug } from './console_utils';
|
|
5
|
-
import { interpolateName } from './interpolate-name';
|
|
6
|
-
const MESSAGE_DESC_KEYS = [
|
|
7
|
-
'id',
|
|
8
|
-
'defaultMessage',
|
|
9
|
-
'description',
|
|
10
|
-
];
|
|
11
|
-
function primitiveToTSNode(factory, v) {
|
|
12
|
-
return typeof v === 'string'
|
|
13
|
-
? factory.createStringLiteral(v)
|
|
14
|
-
: typeof v === 'number'
|
|
15
|
-
? factory.createNumericLiteral(v + '')
|
|
16
|
-
: typeof v === 'boolean'
|
|
17
|
-
? v
|
|
18
|
-
? factory.createTrue()
|
|
19
|
-
: factory.createFalse()
|
|
20
|
-
: undefined;
|
|
21
|
-
}
|
|
22
|
-
function isValidIdentifier(k) {
|
|
23
|
-
try {
|
|
24
|
-
new Function(`return {${k}:1}`);
|
|
25
|
-
return true;
|
|
26
|
-
}
|
|
27
|
-
catch (e) {
|
|
28
|
-
return false;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
function objToTSNode(factory, obj) {
|
|
32
|
-
if (typeof obj === 'object' && !obj) {
|
|
33
|
-
return factory.createNull();
|
|
34
|
-
}
|
|
35
|
-
const props = Object.entries(obj)
|
|
36
|
-
.filter(([_, v]) => typeof v !== 'undefined')
|
|
37
|
-
.map(([k, v]) => factory.createPropertyAssignment(isValidIdentifier(k) ? k : factory.createStringLiteral(k), primitiveToTSNode(factory, v) ||
|
|
38
|
-
(Array.isArray(v)
|
|
39
|
-
? factory.createArrayLiteralExpression(v
|
|
40
|
-
.filter(n => typeof n !== 'undefined')
|
|
41
|
-
.map(n => objToTSNode(factory, n)))
|
|
42
|
-
: objToTSNode(factory, v))));
|
|
43
|
-
return factory.createObjectLiteralExpression(props);
|
|
44
|
-
}
|
|
45
|
-
function messageASTToTSNode(factory, ast) {
|
|
46
|
-
return factory.createArrayLiteralExpression(ast.map(el => objToTSNode(factory, el)));
|
|
47
|
-
}
|
|
48
|
-
function literalToObj(ts, n) {
|
|
49
|
-
if (ts.isNumericLiteral(n)) {
|
|
50
|
-
return +n.text;
|
|
51
|
-
}
|
|
52
|
-
if (ts.isStringLiteral(n)) {
|
|
53
|
-
return n.text;
|
|
54
|
-
}
|
|
55
|
-
if (n.kind === ts.SyntaxKind.TrueKeyword) {
|
|
56
|
-
return true;
|
|
57
|
-
}
|
|
58
|
-
if (n.kind === ts.SyntaxKind.FalseKeyword) {
|
|
59
|
-
return false;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
function objectLiteralExpressionToObj(ts, obj) {
|
|
63
|
-
return obj.properties.reduce((all, prop) => {
|
|
64
|
-
if (ts.isPropertyAssignment(prop) && prop.name) {
|
|
65
|
-
if (ts.isIdentifier(prop.name)) {
|
|
66
|
-
all[prop.name.escapedText.toString()] = literalToObj(ts, prop.initializer);
|
|
67
|
-
}
|
|
68
|
-
else if (ts.isStringLiteral(prop.name)) {
|
|
69
|
-
all[prop.name.text] = literalToObj(ts, prop.initializer);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
return all;
|
|
73
|
-
}, {});
|
|
74
|
-
}
|
|
75
|
-
const DEFAULT_OPTS = {
|
|
76
|
-
onMsgExtracted: () => undefined,
|
|
77
|
-
onMetaExtracted: () => undefined,
|
|
78
|
-
};
|
|
79
|
-
function isMultipleMessageDecl(ts, node) {
|
|
80
|
-
return (ts.isIdentifier(node.expression) &&
|
|
81
|
-
node.expression.text === 'defineMessages');
|
|
82
|
-
}
|
|
83
|
-
function isSingularMessageDecl(ts, node, additionalComponentNames) {
|
|
84
|
-
const compNames = new Set([
|
|
85
|
-
'FormattedMessage',
|
|
86
|
-
'defineMessage',
|
|
87
|
-
'formatMessage',
|
|
88
|
-
'$formatMessage',
|
|
89
|
-
'$t',
|
|
90
|
-
...additionalComponentNames,
|
|
91
|
-
]);
|
|
92
|
-
let fnName = '';
|
|
93
|
-
if (ts.isCallExpression(node) && ts.isIdentifier(node.expression)) {
|
|
94
|
-
fnName = node.expression.text;
|
|
95
|
-
}
|
|
96
|
-
else if (ts.isJsxOpeningElement(node) && ts.isIdentifier(node.tagName)) {
|
|
97
|
-
fnName = node.tagName.text;
|
|
98
|
-
}
|
|
99
|
-
else if (ts.isJsxSelfClosingElement(node) &&
|
|
100
|
-
ts.isIdentifier(node.tagName)) {
|
|
101
|
-
fnName = node.tagName.text;
|
|
102
|
-
}
|
|
103
|
-
return compNames.has(fnName);
|
|
104
|
-
}
|
|
105
|
-
function evaluateStringConcat(ts, node) {
|
|
106
|
-
const { right, left } = node;
|
|
107
|
-
if (!ts.isStringLiteral(right)) {
|
|
108
|
-
return ['', false];
|
|
109
|
-
}
|
|
110
|
-
if (ts.isStringLiteral(left)) {
|
|
111
|
-
return [left.text + right.text, true];
|
|
112
|
-
}
|
|
113
|
-
if (ts.isBinaryExpression(left)) {
|
|
114
|
-
const [result, isStatic] = evaluateStringConcat(ts, left);
|
|
115
|
-
return [result + right.text, isStatic];
|
|
116
|
-
}
|
|
117
|
-
return ['', false];
|
|
118
|
-
}
|
|
119
|
-
function extractMessageDescriptor(ts, node, { overrideIdFn, extractSourceLocation, preserveWhitespace }, sf) {
|
|
120
|
-
let properties = undefined;
|
|
121
|
-
if (ts.isObjectLiteralExpression(node)) {
|
|
122
|
-
properties = node.properties;
|
|
123
|
-
}
|
|
124
|
-
else if (ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node)) {
|
|
125
|
-
properties = node.attributes.properties;
|
|
126
|
-
}
|
|
127
|
-
const msg = { id: '' };
|
|
128
|
-
if (!properties) {
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
properties.forEach(prop => {
|
|
132
|
-
const { name } = prop;
|
|
133
|
-
const initializer = ts.isPropertyAssignment(prop) || ts.isJsxAttribute(prop)
|
|
134
|
-
? prop.initializer
|
|
135
|
-
: undefined;
|
|
136
|
-
if (name && ts.isIdentifier(name) && initializer) {
|
|
137
|
-
// {id: 'id'}
|
|
138
|
-
if (ts.isStringLiteral(initializer)) {
|
|
139
|
-
switch (name.text) {
|
|
140
|
-
case 'id':
|
|
141
|
-
msg.id = initializer.text;
|
|
142
|
-
break;
|
|
143
|
-
case 'defaultMessage':
|
|
144
|
-
msg.defaultMessage = initializer.text;
|
|
145
|
-
break;
|
|
146
|
-
case 'description':
|
|
147
|
-
msg.description = initializer.text;
|
|
148
|
-
break;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
// {id: `id`}
|
|
152
|
-
else if (ts.isNoSubstitutionTemplateLiteral(initializer)) {
|
|
153
|
-
switch (name.text) {
|
|
154
|
-
case 'id':
|
|
155
|
-
msg.id = initializer.text;
|
|
156
|
-
break;
|
|
157
|
-
case 'defaultMessage':
|
|
158
|
-
msg.defaultMessage = initializer.text;
|
|
159
|
-
break;
|
|
160
|
-
case 'description':
|
|
161
|
-
msg.description = initializer.text;
|
|
162
|
-
break;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
// {id: dedent`id`}
|
|
166
|
-
else if (ts.isTaggedTemplateExpression(initializer)) {
|
|
167
|
-
const { template } = initializer;
|
|
168
|
-
if (!ts.isNoSubstitutionTemplateLiteral(template)) {
|
|
169
|
-
throw new Error('Tagged template expression must be no substitution');
|
|
170
|
-
}
|
|
171
|
-
switch (name.text) {
|
|
172
|
-
case 'id':
|
|
173
|
-
msg.id = template.text;
|
|
174
|
-
break;
|
|
175
|
-
case 'defaultMessage':
|
|
176
|
-
msg.defaultMessage = template.text;
|
|
177
|
-
break;
|
|
178
|
-
case 'description':
|
|
179
|
-
msg.description = template.text;
|
|
180
|
-
break;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
else if (ts.isJsxExpression(initializer) && initializer.expression) {
|
|
184
|
-
// <FormattedMessage foo={'barbaz'} />
|
|
185
|
-
if (ts.isStringLiteral(initializer.expression)) {
|
|
186
|
-
switch (name.text) {
|
|
187
|
-
case 'id':
|
|
188
|
-
msg.id = initializer.expression.text;
|
|
189
|
-
break;
|
|
190
|
-
case 'defaultMessage':
|
|
191
|
-
msg.defaultMessage = initializer.expression.text;
|
|
192
|
-
break;
|
|
193
|
-
case 'description':
|
|
194
|
-
msg.description = initializer.expression.text;
|
|
195
|
-
break;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
// description={{custom: 1}}
|
|
199
|
-
else if (ts.isObjectLiteralExpression(initializer.expression) &&
|
|
200
|
-
name.text === 'description') {
|
|
201
|
-
msg.description = objectLiteralExpressionToObj(ts, initializer.expression);
|
|
202
|
-
}
|
|
203
|
-
// <FormattedMessage foo={`bar`} />
|
|
204
|
-
else if (ts.isNoSubstitutionTemplateLiteral(initializer.expression)) {
|
|
205
|
-
const { expression } = initializer;
|
|
206
|
-
switch (name.text) {
|
|
207
|
-
case 'id':
|
|
208
|
-
msg.id = expression.text;
|
|
209
|
-
break;
|
|
210
|
-
case 'defaultMessage':
|
|
211
|
-
msg.defaultMessage = expression.text;
|
|
212
|
-
break;
|
|
213
|
-
case 'description':
|
|
214
|
-
msg.description = expression.text;
|
|
215
|
-
break;
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
// <FormattedMessage foo={dedent`dedent Hello World!`} />
|
|
219
|
-
else if (ts.isTaggedTemplateExpression(initializer.expression)) {
|
|
220
|
-
const { expression: { template }, } = initializer;
|
|
221
|
-
if (!ts.isNoSubstitutionTemplateLiteral(template)) {
|
|
222
|
-
throw new Error('Tagged template expression must be no substitution');
|
|
223
|
-
}
|
|
224
|
-
switch (name.text) {
|
|
225
|
-
case 'id':
|
|
226
|
-
msg.id = template.text;
|
|
227
|
-
break;
|
|
228
|
-
case 'defaultMessage':
|
|
229
|
-
msg.defaultMessage = template.text;
|
|
230
|
-
break;
|
|
231
|
-
case 'description':
|
|
232
|
-
msg.description = template.text;
|
|
233
|
-
break;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
// <FormattedMessage foo={'bar' + 'baz'} />
|
|
237
|
-
else if (ts.isBinaryExpression(initializer.expression)) {
|
|
238
|
-
const { expression } = initializer;
|
|
239
|
-
const [result, isStatic] = evaluateStringConcat(ts, expression);
|
|
240
|
-
if (isStatic) {
|
|
241
|
-
switch (name.text) {
|
|
242
|
-
case 'id':
|
|
243
|
-
msg.id = result;
|
|
244
|
-
break;
|
|
245
|
-
case 'defaultMessage':
|
|
246
|
-
msg.defaultMessage = result;
|
|
247
|
-
break;
|
|
248
|
-
case 'description':
|
|
249
|
-
msg.description = result;
|
|
250
|
-
break;
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
// {defaultMessage: 'asd' + bar'}
|
|
256
|
-
else if (ts.isBinaryExpression(initializer)) {
|
|
257
|
-
const [result, isStatic] = evaluateStringConcat(ts, initializer);
|
|
258
|
-
if (isStatic) {
|
|
259
|
-
switch (name.text) {
|
|
260
|
-
case 'id':
|
|
261
|
-
msg.id = result;
|
|
262
|
-
break;
|
|
263
|
-
case 'defaultMessage':
|
|
264
|
-
msg.defaultMessage = result;
|
|
265
|
-
break;
|
|
266
|
-
case 'description':
|
|
267
|
-
msg.description = result;
|
|
268
|
-
break;
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
// description: {custom: 1}
|
|
273
|
-
else if (ts.isObjectLiteralExpression(initializer) &&
|
|
274
|
-
name.text === 'description') {
|
|
275
|
-
msg.description = objectLiteralExpressionToObj(ts, initializer);
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
});
|
|
279
|
-
// We extracted nothing
|
|
280
|
-
if (!msg.defaultMessage && !msg.id) {
|
|
281
|
-
return;
|
|
282
|
-
}
|
|
283
|
-
if (msg.defaultMessage && !preserveWhitespace) {
|
|
284
|
-
msg.defaultMessage = msg.defaultMessage.trim().replace(/\s+/gm, ' ');
|
|
285
|
-
}
|
|
286
|
-
if (msg.defaultMessage && overrideIdFn) {
|
|
287
|
-
switch (typeof overrideIdFn) {
|
|
288
|
-
case 'string':
|
|
289
|
-
if (!msg.id) {
|
|
290
|
-
msg.id = interpolateName({ resourcePath: sf.fileName }, overrideIdFn, {
|
|
291
|
-
content: msg.description
|
|
292
|
-
? `${msg.defaultMessage}#${typeof msg.description === 'string'
|
|
293
|
-
? msg.description
|
|
294
|
-
: stringify(msg.description)}`
|
|
295
|
-
: msg.defaultMessage,
|
|
296
|
-
});
|
|
297
|
-
}
|
|
298
|
-
break;
|
|
299
|
-
case 'function':
|
|
300
|
-
msg.id = overrideIdFn(msg.id, msg.defaultMessage, msg.description, sf.fileName);
|
|
301
|
-
break;
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
if (extractSourceLocation) {
|
|
305
|
-
return {
|
|
306
|
-
...msg,
|
|
307
|
-
file: sf.fileName,
|
|
308
|
-
start: node.pos,
|
|
309
|
-
end: node.end,
|
|
310
|
-
};
|
|
311
|
-
}
|
|
312
|
-
return msg;
|
|
313
|
-
}
|
|
314
|
-
/**
|
|
315
|
-
* Check if node is `foo.bar.formatMessage` node
|
|
316
|
-
* @param node
|
|
317
|
-
* @param sf
|
|
318
|
-
*/
|
|
319
|
-
function isMemberMethodFormatMessageCall(ts, node, additionalFunctionNames) {
|
|
320
|
-
const fnNames = new Set([
|
|
321
|
-
'formatMessage',
|
|
322
|
-
'$formatMessage',
|
|
323
|
-
...additionalFunctionNames,
|
|
324
|
-
]);
|
|
325
|
-
const method = node.expression;
|
|
326
|
-
// Handle foo.formatMessage()
|
|
327
|
-
if (ts.isPropertyAccessExpression(method)) {
|
|
328
|
-
return fnNames.has(method.name.text);
|
|
329
|
-
}
|
|
330
|
-
// Handle formatMessage()
|
|
331
|
-
return ts.isIdentifier(method) && fnNames.has(method.text);
|
|
332
|
-
}
|
|
333
|
-
function extractMessageFromJsxComponent(ts, factory, node, opts, sf) {
|
|
334
|
-
const { onMsgExtracted } = opts;
|
|
335
|
-
if (!isSingularMessageDecl(ts, node, opts.additionalComponentNames || [])) {
|
|
336
|
-
return node;
|
|
337
|
-
}
|
|
338
|
-
const msg = extractMessageDescriptor(ts, node, opts, sf);
|
|
339
|
-
if (!msg) {
|
|
340
|
-
return node;
|
|
341
|
-
}
|
|
342
|
-
if (typeof onMsgExtracted === 'function') {
|
|
343
|
-
onMsgExtracted(sf.fileName, [msg]);
|
|
344
|
-
}
|
|
345
|
-
const newProps = generateNewProperties(ts, factory, node.attributes, {
|
|
346
|
-
defaultMessage: opts.removeDefaultMessage
|
|
347
|
-
? undefined
|
|
348
|
-
: msg.defaultMessage,
|
|
349
|
-
id: msg.id,
|
|
350
|
-
}, opts.ast);
|
|
351
|
-
if (ts.isJsxOpeningElement(node)) {
|
|
352
|
-
return factory.updateJsxOpeningElement(node, node.tagName, node.typeArguments, factory.createJsxAttributes(newProps));
|
|
353
|
-
}
|
|
354
|
-
return factory.updateJsxSelfClosingElement(node, node.tagName, node.typeArguments, factory.createJsxAttributes(newProps));
|
|
355
|
-
}
|
|
356
|
-
function setAttributesInObject(ts, factory, node, msg, ast) {
|
|
357
|
-
const newProps = [
|
|
358
|
-
factory.createPropertyAssignment('id', factory.createStringLiteral(msg.id)),
|
|
359
|
-
...(msg.defaultMessage
|
|
360
|
-
? [
|
|
361
|
-
factory.createPropertyAssignment('defaultMessage', ast
|
|
362
|
-
? messageASTToTSNode(factory, parse(msg.defaultMessage))
|
|
363
|
-
: factory.createStringLiteral(msg.defaultMessage)),
|
|
364
|
-
]
|
|
365
|
-
: []),
|
|
366
|
-
];
|
|
367
|
-
for (const prop of node.properties) {
|
|
368
|
-
if (ts.isPropertyAssignment(prop) &&
|
|
369
|
-
ts.isIdentifier(prop.name) &&
|
|
370
|
-
MESSAGE_DESC_KEYS.includes(prop.name.text)) {
|
|
371
|
-
continue;
|
|
372
|
-
}
|
|
373
|
-
if (ts.isPropertyAssignment(prop)) {
|
|
374
|
-
newProps.push(prop);
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
return factory.createObjectLiteralExpression(factory.createNodeArray(newProps));
|
|
378
|
-
}
|
|
379
|
-
function generateNewProperties(ts, factory, node, msg, ast) {
|
|
380
|
-
const newProps = [
|
|
381
|
-
factory.createJsxAttribute(factory.createIdentifier('id'), factory.createStringLiteral(msg.id)),
|
|
382
|
-
...(msg.defaultMessage
|
|
383
|
-
? [
|
|
384
|
-
factory.createJsxAttribute(factory.createIdentifier('defaultMessage'), ast
|
|
385
|
-
? factory.createJsxExpression(undefined, messageASTToTSNode(factory, parse(msg.defaultMessage)))
|
|
386
|
-
: factory.createStringLiteral(msg.defaultMessage)),
|
|
387
|
-
]
|
|
388
|
-
: []),
|
|
389
|
-
];
|
|
390
|
-
for (const prop of node.properties) {
|
|
391
|
-
if (ts.isJsxAttribute(prop) &&
|
|
392
|
-
ts.isIdentifier(prop.name) &&
|
|
393
|
-
MESSAGE_DESC_KEYS.includes(prop.name.text)) {
|
|
394
|
-
continue;
|
|
395
|
-
}
|
|
396
|
-
if (ts.isJsxAttribute(prop)) {
|
|
397
|
-
newProps.push(prop);
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
return newProps;
|
|
401
|
-
}
|
|
402
|
-
function extractMessagesFromCallExpression(ts, factory, node, opts, sf) {
|
|
403
|
-
const { onMsgExtracted, additionalFunctionNames } = opts;
|
|
404
|
-
if (isMultipleMessageDecl(ts, node)) {
|
|
405
|
-
const [arg, ...restArgs] = node.arguments;
|
|
406
|
-
let descriptorsObj;
|
|
407
|
-
if (ts.isObjectLiteralExpression(arg)) {
|
|
408
|
-
descriptorsObj = arg;
|
|
409
|
-
}
|
|
410
|
-
else if (ts.isAsExpression(arg) &&
|
|
411
|
-
ts.isObjectLiteralExpression(arg.expression)) {
|
|
412
|
-
descriptorsObj = arg.expression;
|
|
413
|
-
}
|
|
414
|
-
if (descriptorsObj) {
|
|
415
|
-
const properties = descriptorsObj.properties;
|
|
416
|
-
const msgs = properties
|
|
417
|
-
.filter((prop) => ts.isPropertyAssignment(prop))
|
|
418
|
-
.map(prop => ts.isObjectLiteralExpression(prop.initializer) &&
|
|
419
|
-
extractMessageDescriptor(ts, prop.initializer, opts, sf))
|
|
420
|
-
.filter((msg) => !!msg);
|
|
421
|
-
if (!msgs.length) {
|
|
422
|
-
return node;
|
|
423
|
-
}
|
|
424
|
-
debug('Multiple messages extracted from "%s": %s', sf.fileName, msgs);
|
|
425
|
-
if (typeof onMsgExtracted === 'function') {
|
|
426
|
-
onMsgExtracted(sf.fileName, msgs);
|
|
427
|
-
}
|
|
428
|
-
const clonedProperties = factory.createNodeArray(properties.map((prop, i) => {
|
|
429
|
-
if (!ts.isPropertyAssignment(prop) ||
|
|
430
|
-
!ts.isObjectLiteralExpression(prop.initializer)) {
|
|
431
|
-
return prop;
|
|
432
|
-
}
|
|
433
|
-
return factory.createPropertyAssignment(prop.name, setAttributesInObject(ts, factory, prop.initializer, {
|
|
434
|
-
defaultMessage: opts.removeDefaultMessage
|
|
435
|
-
? undefined
|
|
436
|
-
: msgs[i].defaultMessage,
|
|
437
|
-
id: msgs[i] ? msgs[i].id : '',
|
|
438
|
-
}, opts.ast));
|
|
439
|
-
}));
|
|
440
|
-
const clonedDescriptorsObj = factory.createObjectLiteralExpression(clonedProperties);
|
|
441
|
-
return factory.updateCallExpression(node, node.expression, node.typeArguments, [clonedDescriptorsObj, ...restArgs]);
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
else if (isSingularMessageDecl(ts, node, opts.additionalComponentNames || []) ||
|
|
445
|
-
isMemberMethodFormatMessageCall(ts, node, additionalFunctionNames || [])) {
|
|
446
|
-
const [descriptorsObj, ...restArgs] = node.arguments;
|
|
447
|
-
if (ts.isObjectLiteralExpression(descriptorsObj)) {
|
|
448
|
-
const msg = extractMessageDescriptor(ts, descriptorsObj, opts, sf);
|
|
449
|
-
if (!msg) {
|
|
450
|
-
return node;
|
|
451
|
-
}
|
|
452
|
-
debug('Message extracted from "%s": %s', sf.fileName, msg);
|
|
453
|
-
if (typeof onMsgExtracted === 'function') {
|
|
454
|
-
onMsgExtracted(sf.fileName, [msg]);
|
|
455
|
-
}
|
|
456
|
-
return factory.updateCallExpression(node, node.expression, node.typeArguments, [
|
|
457
|
-
setAttributesInObject(ts, factory, descriptorsObj, {
|
|
458
|
-
defaultMessage: opts.removeDefaultMessage
|
|
459
|
-
? undefined
|
|
460
|
-
: msg.defaultMessage,
|
|
461
|
-
id: msg.id,
|
|
462
|
-
}, opts.ast),
|
|
463
|
-
...restArgs,
|
|
464
|
-
]);
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
return node;
|
|
468
|
-
}
|
|
469
|
-
const PRAGMA_REGEX = /^\/\/ @([^\s]*) (.*)$/m;
|
|
470
|
-
function getVisitor(ts, ctx, sf, opts) {
|
|
471
|
-
const visitor = (node) => {
|
|
472
|
-
const newNode = ts.isCallExpression(node)
|
|
473
|
-
? extractMessagesFromCallExpression(ts, ctx.factory, node, opts, sf)
|
|
474
|
-
: ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node)
|
|
475
|
-
? extractMessageFromJsxComponent(ts, ctx.factory, node, opts, sf)
|
|
476
|
-
: node;
|
|
477
|
-
return ts.visitEachChild(newNode, visitor, ctx);
|
|
478
|
-
};
|
|
479
|
-
return visitor;
|
|
480
|
-
}
|
|
481
|
-
export function transformWithTs(ts, opts) {
|
|
482
|
-
opts = { ...DEFAULT_OPTS, ...opts };
|
|
483
|
-
debug('Transforming options', opts);
|
|
484
|
-
const transformFn = ctx => {
|
|
485
|
-
return sf => {
|
|
486
|
-
const pragmaResult = PRAGMA_REGEX.exec(sf.text);
|
|
487
|
-
if (pragmaResult) {
|
|
488
|
-
debug('Pragma found', pragmaResult);
|
|
489
|
-
const [, pragma, kvString] = pragmaResult;
|
|
490
|
-
if (pragma === opts.pragma) {
|
|
491
|
-
const kvs = kvString.split(' ');
|
|
492
|
-
const result = {};
|
|
493
|
-
for (const kv of kvs) {
|
|
494
|
-
const [k, v] = kv.split(':');
|
|
495
|
-
result[k] = v;
|
|
496
|
-
}
|
|
497
|
-
debug('Pragma extracted', result);
|
|
498
|
-
if (typeof opts.onMetaExtracted === 'function') {
|
|
499
|
-
opts.onMetaExtracted(sf.fileName, result);
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
return ts.visitEachChild(sf, getVisitor(ts, ctx, sf, opts), ctx);
|
|
504
|
-
};
|
|
505
|
-
};
|
|
506
|
-
return transformFn;
|
|
507
|
-
}
|
|
508
|
-
export function transform(opts) {
|
|
509
|
-
return transformWithTs(typescript, opts);
|
|
510
|
-
}
|
package/lib_esnext/src/types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { TsCompilerInstance } from 'ts-jest';
|
|
2
|
-
import { SourceFile, TransformerFactory } from 'typescript';
|
|
3
|
-
import { Opts } from '.';
|
|
4
|
-
export declare const name = "@formatjs/ts-transformer";
|
|
5
|
-
export declare const version = "2.10.1";
|
|
6
|
-
export declare function factory(compilerInstance: TsCompilerInstance, opts: Opts): TransformerFactory<SourceFile>;
|