ts-const-value-transformer 0.4.1 → 0.5.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/CHANGELOG.md +16 -0
- package/README.md +29 -1
- package/dist/createPortalTransformer.d.mts +42 -0
- package/dist/createPortalTransformer.mjs +113 -0
- package/dist/createTransformer.mjs +10 -5
- package/dist/index.d.mts +2 -1
- package/dist/index.mjs +2 -1
- package/dist/loader.d.mts +1 -1
- package/dist/loader.mjs +10 -62
- package/dist/transform.d.mts +12 -0
- package/dist/transform.mjs +141 -53
- package/dist/version.d.mts +1 -1
- package/dist/version.mjs +1 -1
- package/package.json +11 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v0.5.1
|
|
4
|
+
|
|
5
|
+
- Fix missing for handling `ignoreFiles` for `createTransformer` (used from ts-loader, etc.)
|
|
6
|
+
|
|
7
|
+
## v0.5.0
|
|
8
|
+
|
|
9
|
+
- Add `useUndefinedSymbolForUndefinedValue` and `hoistUndefinedSymbol` options
|
|
10
|
+
- Add `createPortalTransformer` to use transformer easily
|
|
11
|
+
- Skip update process if unchanged
|
|
12
|
+
- Skip some declarations for performance
|
|
13
|
+
- Add hoisting computed property name
|
|
14
|
+
- Add test for element access
|
|
15
|
+
- Avoid hosting indexed property access
|
|
16
|
+
- Add test case for OmittedExpression and SatisfiesExpression
|
|
17
|
+
- Refactor to avoid handling unexpected expressions
|
|
18
|
+
|
|
3
19
|
## v0.4.1
|
|
4
20
|
|
|
5
21
|
Fix for not hositing some more unary expressions and parenthesized expression
|
package/README.md
CHANGED
|
@@ -162,12 +162,25 @@ export interface TransformOptions {
|
|
|
162
162
|
unsafeHoistAsExpresion?: boolean | undefined;
|
|
163
163
|
/** Hoist properties/variables that can write (i.e. `let` / `var` variables or properies without `readonly`). Default is false because although the value is literal type at some point, the value may change to another literal type. */
|
|
164
164
|
unsafeHoistWritableValues?: boolean | undefined;
|
|
165
|
+
/** Uses `undefined` symbol for `undefined` type values. Default is false and replaces to `void 0`. */
|
|
166
|
+
useUndefinedSymbolForUndefinedValue?: boolean | undefined;
|
|
167
|
+
/** Hoist `undefined` symbol to `void 0` (or `undefined` if useUndefinedSymbolForUndefinedValue is true). Default is true. */
|
|
168
|
+
hoistUndefinedSymbol?: boolean | undefined;
|
|
165
169
|
/**
|
|
166
170
|
* External names (tested with `.includes()` for string, with `.test()` for RegExp) for `hoistExternalValues` settings (If `hoistExternalValues` is not specified, this setting will be used).
|
|
167
171
|
* - Path separators for input file name are always normalized to '/' internally.
|
|
168
172
|
* - Default is `['/node_modules/']`.
|
|
169
173
|
*/
|
|
170
174
|
externalNames?: ReadonlyArray<string | RegExp> | undefined;
|
|
175
|
+
/**
|
|
176
|
+
* Specifies for file name list or function to skip transformation. This option is used by webpack loader, the transformed called from ts-loader, and createPortalTransformer only.
|
|
177
|
+
* - For list, if the token is `string`, the transformation will be skipped if `fileName.indexOf(token) >= 0` is true.
|
|
178
|
+
* If the token is `RegExp`, the transformation will be skipped if `fileName.indexOf(token) >= 0` is true.
|
|
179
|
+
* - For function, the transformation will be skipped if `fn(fileName)` is true.
|
|
180
|
+
*/
|
|
181
|
+
ignoreFiles?:
|
|
182
|
+
| ReadonlyArray<string | RegExp>
|
|
183
|
+
| ((fileName: string) => boolean);
|
|
171
184
|
}
|
|
172
185
|
```
|
|
173
186
|
|
|
@@ -183,6 +196,10 @@ import {
|
|
|
183
196
|
transformSource,
|
|
184
197
|
version,
|
|
185
198
|
type TransformOptions,
|
|
199
|
+
createPortalTransformer,
|
|
200
|
+
createPortalTransformerSync,
|
|
201
|
+
type CreatePortalTransformerOptions,
|
|
202
|
+
type PortalTransformer,
|
|
186
203
|
} from 'ts-const-value-transformer';
|
|
187
204
|
```
|
|
188
205
|
|
|
@@ -245,7 +262,9 @@ Prints (generates) source code from `SourceFile`, along with raw source-map data
|
|
|
245
262
|
|
|
246
263
|
#### transformSource: (sourceFile: ts.SourceFile, program: ts.Program, context: ts.TransformationContext, options?: TransformOptions) => ts.SourceFile
|
|
247
264
|
|
|
248
|
-
Transforms the source file with TypeScript project. You don't need to call this function directly; use `createTransformer` instead.
|
|
265
|
+
Transforms the source file with TypeScript project. You don't need to call this function directly; use `createTransformer` or `createPortalTransformer` instead.
|
|
266
|
+
|
|
267
|
+
Note that `ignoreFiles` of `options` will be ignored for this function.
|
|
249
268
|
|
|
250
269
|
#### version: string
|
|
251
270
|
|
|
@@ -255,6 +274,15 @@ The version string of this package.
|
|
|
255
274
|
|
|
256
275
|
See [Transform options](#transform-options).
|
|
257
276
|
|
|
277
|
+
#### createPortalTransformer: (options?: CreatePortalTransformerOptions) => Promise<PortalTransformer>
|
|
278
|
+
|
|
279
|
+
Creates 'portal transformer', which can be used the transformer easily from the code which does not use TypeScript Compiler API.
|
|
280
|
+
The return object has `transform` method with signature: `(content: string, fileName: string, sourceMap?: string | RawSourceMap | null, options?: TransformOptions) => [newSource: string, newSourceMap: RawSourceMap | undefined]`. You can call to transform TypeScript source code. (Note that this API does not transpile to JavaScript; the output code is still TypeScript code.)
|
|
281
|
+
|
|
282
|
+
`CreatePortalTransformerOptions` has three optional options: `project` (path to tsconfig.json), `typescript` (package path to `typescript` or `typescript` namespace object), and `cwd` (current directory for file search). Also, `ignoreFiles` can be used.
|
|
283
|
+
|
|
284
|
+
If `Promise` cannot be used for some reason, use `createPortalTransformerSync` instead.
|
|
285
|
+
|
|
258
286
|
## Notice
|
|
259
287
|
|
|
260
288
|
Starting from v0.4.0, `unsafeHoistWritableValues` option is introduced. Since TypeScript sometimes narrows non-constant values to literal types such as:
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { RawSourceMap } from 'source-map';
|
|
2
|
+
import type * as tsNamespace from 'typescript';
|
|
3
|
+
import { type TransformOptions } from './transform.mjs';
|
|
4
|
+
export interface CreatePortalTransformerOptions extends TransformOptions {
|
|
5
|
+
project?: string;
|
|
6
|
+
typescript?: string | typeof tsNamespace;
|
|
7
|
+
cwd?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface PortalTransformer {
|
|
10
|
+
/** The `typescript` namespace object */
|
|
11
|
+
readonly ts: typeof tsNamespace;
|
|
12
|
+
/** Active `Program` instance for the transformer */
|
|
13
|
+
readonly program: tsNamespace.Program;
|
|
14
|
+
/**
|
|
15
|
+
* Performs transformation.
|
|
16
|
+
* @param content Base source code. If null, uses loaded source code in the TS project.
|
|
17
|
+
* @param fileName Base file name (If not included in the TS project, transformation will not be performed.)
|
|
18
|
+
* @param sourceMap Base source map if exists
|
|
19
|
+
* @param options Transform options (addition to `options` passed to `createPortalTransformer`)
|
|
20
|
+
* @returns Tuple of new source code and source map. Source map may be undefined if source code is unchanged.
|
|
21
|
+
*/
|
|
22
|
+
transform(content: string, fileName: string, sourceMap?: string | RawSourceMap | null, options?: TransformOptions): [newSource: string, newSourceMap: RawSourceMap | undefined];
|
|
23
|
+
/**
|
|
24
|
+
* Performs transformation.
|
|
25
|
+
* @param content Base source code. If null, uses loaded source code in the TS project.
|
|
26
|
+
* @param fileName Base file name (If not included in the TS project, transformation will not be performed.)
|
|
27
|
+
* @param sourceMap Base source map if exists
|
|
28
|
+
* @param options Transform options (addition to `options` passed to `createPortalTransformer`)
|
|
29
|
+
* @returns Tuple of new source code and source map. Source map may be undefined if source code is unchanged.
|
|
30
|
+
*/
|
|
31
|
+
transform(content: string | null, fileName: string, sourceMap?: string | RawSourceMap | null, options?: TransformOptions): [newSource: string | null, newSourceMap: RawSourceMap | undefined];
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Creates the new portal transformer instance for the TS project.
|
|
35
|
+
* After creation, the transformation process can be performed by calling {@link PortalTransformer.transform}.
|
|
36
|
+
*/
|
|
37
|
+
export default function createPortalTransformer(options?: CreatePortalTransformerOptions): Promise<PortalTransformer>;
|
|
38
|
+
/**
|
|
39
|
+
* Creates the new portal transformer instance for the TS project (using `require` function).
|
|
40
|
+
* After creation, the transformation process can be performed by calling {@link PortalTransformer.transform}.
|
|
41
|
+
*/
|
|
42
|
+
export declare function createPortalTransformerSync(options?: CreatePortalTransformerOptions): PortalTransformer;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import { createRequire } from 'module';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import createTransformer from './createTransformer.mjs';
|
|
5
|
+
import { getIgnoreFilesFunction, printSourceWithMap, } from './transform.mjs';
|
|
6
|
+
const require = createRequire(import.meta.url);
|
|
7
|
+
function createPortalTransformerImpl(options, ts) {
|
|
8
|
+
const project = options.project ?? 'tsconfig.json';
|
|
9
|
+
const ignoreFiles = getIgnoreFilesFunction(options.ignoreFiles);
|
|
10
|
+
const cwd = options.cwd ?? process.cwd();
|
|
11
|
+
const getCurrentDirectory = () => cwd;
|
|
12
|
+
const config = ts.getParsedCommandLineOfConfigFile(project, void 0, {
|
|
13
|
+
fileExists: fs.existsSync,
|
|
14
|
+
getCurrentDirectory,
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
16
|
+
readDirectory: ts.sys.readDirectory,
|
|
17
|
+
readFile: (file) => fs.readFileSync(path.isAbsolute(file) ? file : path.join(getCurrentDirectory(), file), 'utf-8'),
|
|
18
|
+
useCaseSensitiveFileNames: ts.sys.useCaseSensitiveFileNames,
|
|
19
|
+
onUnRecoverableConfigFileDiagnostic: (diag) => {
|
|
20
|
+
throw new Error(ts.formatDiagnostics([diag], {
|
|
21
|
+
getCanonicalFileName: (f) => f,
|
|
22
|
+
getCurrentDirectory,
|
|
23
|
+
getNewLine: () => '\n',
|
|
24
|
+
}));
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
if (!config) {
|
|
28
|
+
throw new Error(`[ts-const-value-transformer] Unable to load tsconfig file (effective name = '${project}')`);
|
|
29
|
+
}
|
|
30
|
+
const program = ts.createProgram({
|
|
31
|
+
options: config.options,
|
|
32
|
+
rootNames: config.fileNames,
|
|
33
|
+
});
|
|
34
|
+
return {
|
|
35
|
+
ts,
|
|
36
|
+
program,
|
|
37
|
+
transform: (content, fileName, sourceMap, individualOptions) => {
|
|
38
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
39
|
+
const rawSourceMap = typeof sourceMap === 'string'
|
|
40
|
+
? JSON.parse(sourceMap)
|
|
41
|
+
: (sourceMap ?? void 0);
|
|
42
|
+
if (ignoreFiles(fileName)) {
|
|
43
|
+
return [content, rawSourceMap];
|
|
44
|
+
}
|
|
45
|
+
const sourceFile = program.getSourceFile(fileName);
|
|
46
|
+
if (!sourceFile) {
|
|
47
|
+
return [content, rawSourceMap];
|
|
48
|
+
}
|
|
49
|
+
// If input content is changed, replace it
|
|
50
|
+
if (content != null && sourceFile.getFullText() !== content) {
|
|
51
|
+
sourceFile.update(content, {
|
|
52
|
+
span: { start: 0, length: sourceFile.end },
|
|
53
|
+
newLength: content.length,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
const transformer = createTransformer(program, {
|
|
57
|
+
options: { ...options, ...individualOptions, ts },
|
|
58
|
+
});
|
|
59
|
+
const result = ts.transform(sourceFile, [transformer], program.getCompilerOptions());
|
|
60
|
+
const transformedSource = result.transformed[0];
|
|
61
|
+
// If unchanged, return base file as-is
|
|
62
|
+
if (transformedSource === sourceFile) {
|
|
63
|
+
return [content ?? sourceFile.getFullText(), rawSourceMap];
|
|
64
|
+
}
|
|
65
|
+
const printed = printSourceWithMap(transformedSource, fileName, rawSourceMap);
|
|
66
|
+
return printed;
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Creates the new portal transformer instance for the TS project.
|
|
72
|
+
* After creation, the transformation process can be performed by calling {@link PortalTransformer.transform}.
|
|
73
|
+
*/
|
|
74
|
+
export default async function createPortalTransformer(options = {}) {
|
|
75
|
+
let ts;
|
|
76
|
+
if (options.typescript != null) {
|
|
77
|
+
if (typeof options.typescript === 'string') {
|
|
78
|
+
ts = (await import(options.typescript));
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
ts = options.typescript;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
else if (options.ts != null) {
|
|
85
|
+
ts = options.ts;
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
ts = await import('typescript');
|
|
89
|
+
}
|
|
90
|
+
return createPortalTransformerImpl(options, ts);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Creates the new portal transformer instance for the TS project (using `require` function).
|
|
94
|
+
* After creation, the transformation process can be performed by calling {@link PortalTransformer.transform}.
|
|
95
|
+
*/
|
|
96
|
+
export function createPortalTransformerSync(options = {}) {
|
|
97
|
+
let ts;
|
|
98
|
+
if (options.typescript != null) {
|
|
99
|
+
if (typeof options.typescript === 'string') {
|
|
100
|
+
ts = require(options.typescript);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
ts = options.typescript;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
else if (options.ts != null) {
|
|
107
|
+
ts = options.ts;
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
ts = require('typescript');
|
|
111
|
+
}
|
|
112
|
+
return createPortalTransformerImpl(options, ts);
|
|
113
|
+
}
|
|
@@ -1,15 +1,20 @@
|
|
|
1
|
-
import { transformSource } from './transform.mjs';
|
|
1
|
+
import { getIgnoreFilesFunction, transformSource, } from './transform.mjs';
|
|
2
2
|
export default function createTransformer(program,
|
|
3
3
|
// for ttypescript and ts-patch
|
|
4
4
|
config,
|
|
5
5
|
// for ts-patch
|
|
6
6
|
extras) {
|
|
7
|
+
const options = {
|
|
8
|
+
...config?.options,
|
|
9
|
+
...(extras?.ts && { ts: extras?.ts }),
|
|
10
|
+
};
|
|
11
|
+
const ignoreFiles = getIgnoreFilesFunction(options.ignoreFiles);
|
|
7
12
|
return (context) => {
|
|
8
13
|
return (sourceFile) => {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
14
|
+
if (ignoreFiles(sourceFile.fileName)) {
|
|
15
|
+
return sourceFile;
|
|
16
|
+
}
|
|
17
|
+
return transformSource(sourceFile, program, context, options);
|
|
13
18
|
};
|
|
14
19
|
};
|
|
15
20
|
}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
+
import createPortalTransformer, { createPortalTransformerSync, type CreatePortalTransformerOptions, type PortalTransformer } from './createPortalTransformer.mjs';
|
|
1
2
|
import createTransformer from './createTransformer.mjs';
|
|
2
3
|
import version from './version.mjs';
|
|
3
4
|
export { printSource, printSourceWithMap, transformSource, type TransformOptions, } from './transform.mjs';
|
|
4
|
-
export { createTransformer, version };
|
|
5
|
+
export { createPortalTransformer, createPortalTransformerSync, createTransformer, type CreatePortalTransformerOptions, type PortalTransformer, version, };
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
+
import createPortalTransformer, { createPortalTransformerSync, } from './createPortalTransformer.mjs';
|
|
1
2
|
import createTransformer from './createTransformer.mjs';
|
|
2
3
|
import version from './version.mjs';
|
|
3
4
|
export { printSource, printSourceWithMap, transformSource, } from './transform.mjs';
|
|
4
|
-
export { createTransformer, version };
|
|
5
|
+
export { createPortalTransformer, createPortalTransformerSync, createTransformer, version, };
|
package/dist/loader.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type * as tsNamespace from 'typescript';
|
|
2
2
|
import type * as webpack from 'webpack';
|
|
3
|
-
import {
|
|
3
|
+
import type { TransformOptions } from './transform.mjs';
|
|
4
4
|
export interface TsConstValueTransformerLoaderOptions extends TransformOptions {
|
|
5
5
|
project?: string;
|
|
6
6
|
typescript?: string | typeof tsNamespace;
|
package/dist/loader.mjs
CHANGED
|
@@ -1,74 +1,22 @@
|
|
|
1
|
-
import * as fs from 'fs';
|
|
2
1
|
import * as path from 'path';
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
const programMap = new Map();
|
|
2
|
+
import createPortalTransformer, {} from './createPortalTransformer.mjs';
|
|
3
|
+
const transformerMap = new Map();
|
|
6
4
|
const loader = function (content, sourceMap) {
|
|
7
5
|
this.async();
|
|
8
6
|
void (async () => {
|
|
9
7
|
try {
|
|
10
8
|
const options = this.getOptions() || {};
|
|
11
|
-
let ts;
|
|
12
|
-
if (options.typescript != null) {
|
|
13
|
-
if (typeof options.typescript === 'string') {
|
|
14
|
-
ts = (await import(options.typescript));
|
|
15
|
-
}
|
|
16
|
-
else {
|
|
17
|
-
ts = options.typescript;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
else if (options.ts != null) {
|
|
21
|
-
ts = options.ts;
|
|
22
|
-
}
|
|
23
|
-
else {
|
|
24
|
-
ts = await import('typescript');
|
|
25
|
-
}
|
|
26
9
|
const project = options.project ?? 'tsconfig.json';
|
|
27
|
-
let
|
|
28
|
-
if (!
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
getCurrentDirectory,
|
|
33
|
-
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
34
|
-
readDirectory: ts.sys.readDirectory,
|
|
35
|
-
readFile: (file) => fs.readFileSync(path.isAbsolute(file)
|
|
36
|
-
? file
|
|
37
|
-
: path.join(getCurrentDirectory(), file), 'utf-8'),
|
|
38
|
-
useCaseSensitiveFileNames: ts.sys.useCaseSensitiveFileNames,
|
|
39
|
-
onUnRecoverableConfigFileDiagnostic: (diag) => {
|
|
40
|
-
throw new Error(ts.formatDiagnostics([diag], {
|
|
41
|
-
getCanonicalFileName: (f) => f,
|
|
42
|
-
getCurrentDirectory,
|
|
43
|
-
getNewLine: () => '\n',
|
|
44
|
-
}));
|
|
45
|
-
},
|
|
10
|
+
let transformer = transformerMap.get(project);
|
|
11
|
+
if (!transformer) {
|
|
12
|
+
transformer = await createPortalTransformer({
|
|
13
|
+
...options,
|
|
14
|
+
cwd: path.dirname(this.resourcePath),
|
|
46
15
|
});
|
|
47
|
-
|
|
48
|
-
throw new Error(`[ts-const-value-transformer-loader] Unable to load tsconfig file (effective name = '${project}')`);
|
|
49
|
-
}
|
|
50
|
-
program = ts.createProgram({
|
|
51
|
-
options: config.options,
|
|
52
|
-
rootNames: config.fileNames,
|
|
53
|
-
});
|
|
54
|
-
programMap.set(project, program);
|
|
55
|
-
}
|
|
56
|
-
const sourceFile = program.getSourceFile(this.resource);
|
|
57
|
-
if (!sourceFile) {
|
|
58
|
-
throw new Error(`'${this.resource}' is not in the TypeScript project.`);
|
|
16
|
+
transformerMap.set(project, transformer);
|
|
59
17
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
newLength: content.length,
|
|
63
|
-
});
|
|
64
|
-
const transformer = createTransformer(program, {
|
|
65
|
-
options: { ...options, ts },
|
|
66
|
-
});
|
|
67
|
-
const result = ts.transform(sourceFile, [transformer], program.getCompilerOptions());
|
|
68
|
-
const printed = printSourceWithMap(result.transformed[0], this.resource,
|
|
69
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
70
|
-
typeof sourceMap === 'string' ? JSON.parse(sourceMap) : sourceMap);
|
|
71
|
-
this.callback(null, printed[0], printed[1]);
|
|
18
|
+
const result = transformer.transform(content, this.resource, sourceMap);
|
|
19
|
+
this.callback(null, result[0], result[1]);
|
|
72
20
|
}
|
|
73
21
|
catch (e) {
|
|
74
22
|
this.callback(e);
|
package/dist/transform.d.mts
CHANGED
|
@@ -17,13 +17,25 @@ export interface TransformOptions {
|
|
|
17
17
|
unsafeHoistAsExpresion?: boolean | undefined;
|
|
18
18
|
/** Hoist properties/variables that can write (i.e. `let` / `var` variables or properies without `readonly`). Default is false because although the value is literal type at some point, the value may change to another literal type. */
|
|
19
19
|
unsafeHoistWritableValues?: boolean | undefined;
|
|
20
|
+
/** Uses `undefined` symbol for `undefined` type values. Default is false and replaces to `void 0`. */
|
|
21
|
+
useUndefinedSymbolForUndefinedValue?: boolean | undefined;
|
|
22
|
+
/** Hoist `undefined` symbol to `void 0` (or `undefined` if {@linkcode useUndefinedSymbolForUndefinedValue} is true). Default is true. */
|
|
23
|
+
hoistUndefinedSymbol?: boolean | undefined;
|
|
20
24
|
/**
|
|
21
25
|
* External names (tested with `.includes()` for string, with `.test()` for RegExp) for `hoistExternalValues` settings (If `hoistExternalValues` is not specified, this setting will be used).
|
|
22
26
|
* - Path separators for input file name are always normalized to '/' internally.
|
|
23
27
|
* - Default is `['/node_modules/']`.
|
|
24
28
|
*/
|
|
25
29
|
externalNames?: ReadonlyArray<string | RegExp> | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* Specifies for file name list or function to skip transformation. This option is used by webpack loader, the transformed called from ts-loader, and {@link createPortalTransformer} only.
|
|
32
|
+
* - For list, if the token is `string`, the transformation will be skipped if `fileName.indexOf(token) >= 0` is true.
|
|
33
|
+
* If the token is `RegExp`, the transformation will be skipped if `fileName.indexOf(token) >= 0` is true.
|
|
34
|
+
* - For function, the transformation will be skipped if `fn(fileName)` is true.
|
|
35
|
+
*/
|
|
36
|
+
ignoreFiles?: ReadonlyArray<string | RegExp> | ((fileName: string) => boolean);
|
|
26
37
|
}
|
|
38
|
+
export declare function getIgnoreFilesFunction(ignoreFiles: TransformOptions['ignoreFiles']): (fileName: string) => boolean;
|
|
27
39
|
export declare function transformSource(sourceFile: ts.SourceFile, program: ts.Program, context: ts.TransformationContext, options?: TransformOptions): ts.SourceFile;
|
|
28
40
|
export declare function printSource(sourceFile: ts.SourceFile): string;
|
|
29
41
|
export declare function printSourceWithMap(sourceFile: ts.SourceFile, originalSourceName: string, startOfSourceMap?: sourceMap.RawSourceMap): [string, sourceMap.RawSourceMap];
|
package/dist/transform.mjs
CHANGED
|
@@ -12,77 +12,107 @@ function assignDefaultValues(options = {}) {
|
|
|
12
12
|
hoistPureFunctionCall: options.hoistPureFunctionCall ?? false,
|
|
13
13
|
unsafeHoistFunctionCall: options.unsafeHoistFunctionCall ?? false,
|
|
14
14
|
unsafeHoistWritableValues: options.unsafeHoistWritableValues ?? false,
|
|
15
|
+
useUndefinedSymbolForUndefinedValue: options.useUndefinedSymbolForUndefinedValue ?? false,
|
|
16
|
+
hoistUndefinedSymbol: options.hoistUndefinedSymbol ?? true,
|
|
15
17
|
externalNames: options.externalNames ?? [],
|
|
18
|
+
ignoreFiles: options.ignoreFiles ?? [],
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
22
|
+
export function getIgnoreFilesFunction(ignoreFiles) {
|
|
23
|
+
if (!ignoreFiles) {
|
|
24
|
+
return () => false;
|
|
25
|
+
}
|
|
26
|
+
if (typeof ignoreFiles === 'function') {
|
|
27
|
+
return ignoreFiles;
|
|
28
|
+
}
|
|
29
|
+
const a = ignoreFiles;
|
|
30
|
+
return (fileName) => {
|
|
31
|
+
return a.some((t) => {
|
|
32
|
+
if (typeof t === 'string') {
|
|
33
|
+
return fileName.indexOf(t) >= 0;
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
return t.test(fileName);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
16
39
|
};
|
|
17
40
|
}
|
|
18
41
|
////////////////////////////////////////////////////////////////////////////////
|
|
19
42
|
export function transformSource(sourceFile, program, context, options) {
|
|
20
|
-
return visitNodeChildren(sourceFile, sourceFile, program, context, assignDefaultValues(options));
|
|
43
|
+
return visitNodeChildren(sourceFile, sourceFile, sourceFile, program, context, assignDefaultValues(options));
|
|
21
44
|
}
|
|
22
|
-
function visitNodeChildren(node, sourceFile, program, context, options) {
|
|
23
|
-
const newNode = visitNodeAndReplaceIfNeeded(node, sourceFile, program, context, options);
|
|
45
|
+
function visitNodeChildren(node, parent, sourceFile, program, context, options) {
|
|
46
|
+
const newNode = visitNodeAndReplaceIfNeeded(node, parent, sourceFile, program, context, options);
|
|
24
47
|
if (newNode[SYMBOL_ORIGINAL_NODE]) {
|
|
25
48
|
return newNode;
|
|
26
49
|
}
|
|
27
|
-
|
|
50
|
+
// skip children for satisifes expression
|
|
51
|
+
if (ts.isSatisfiesExpression(newNode)) {
|
|
52
|
+
return newNode;
|
|
53
|
+
}
|
|
54
|
+
// skip statements which would not have 'value' expressions
|
|
55
|
+
if (ts.isInterfaceDeclaration(newNode) ||
|
|
56
|
+
ts.isTypeAliasDeclaration(newNode) ||
|
|
57
|
+
// Identifies in import clause should not be parsed
|
|
58
|
+
ts.isImportDeclaration(newNode) ||
|
|
59
|
+
ts.isTypeOnlyExportDeclaration(newNode)) {
|
|
60
|
+
return newNode;
|
|
61
|
+
}
|
|
62
|
+
return ts.visitEachChild(newNode, (node) => visitNodeChildren(node, newNode, sourceFile, program, context, options), context);
|
|
28
63
|
}
|
|
29
|
-
function visitNodeAndReplaceIfNeeded(node, sourceFile, program, context, options) {
|
|
64
|
+
function visitNodeAndReplaceIfNeeded(node, parent, sourceFile, program, context, options) {
|
|
30
65
|
const ts = options.ts;
|
|
31
|
-
if (
|
|
32
|
-
|
|
66
|
+
if (ts.isCallLikeExpression(node)) {
|
|
67
|
+
if (!ts.isExpression(node) ||
|
|
68
|
+
(!options.unsafeHoistFunctionCall &&
|
|
69
|
+
(!options.hoistPureFunctionCall ||
|
|
70
|
+
!hasPureAnnotation(node, sourceFile, ts)))) {
|
|
71
|
+
return node;
|
|
72
|
+
}
|
|
33
73
|
}
|
|
34
|
-
if (ts.
|
|
35
|
-
ts.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
(
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
return node;
|
|
74
|
+
else if (ts.isIdentifier(node)) {
|
|
75
|
+
if (!ts.isComputedPropertyName(parent) &&
|
|
76
|
+
((!ts.isExpression(parent) &&
|
|
77
|
+
(!('initializer' in parent) || node !== parent.initializer)) ||
|
|
78
|
+
(ts.isPropertyAccessExpression(parent) && node === parent.name))) {
|
|
79
|
+
return node;
|
|
80
|
+
}
|
|
81
|
+
if (!options.hoistEnumValues && isEnumIdentifier(node, program, ts)) {
|
|
82
|
+
return node;
|
|
83
|
+
}
|
|
84
|
+
if (!options.hoistUndefinedSymbol &&
|
|
85
|
+
isUndefinedIdentifier(node, parent, program, ts)) {
|
|
86
|
+
return node;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
else if (ts.isPropertyAccessExpression(node) ||
|
|
90
|
+
ts.isElementAccessExpression(node)) {
|
|
91
|
+
if (!isHoistablePropertyAccess(node, program, ts)) {
|
|
92
|
+
return node;
|
|
93
|
+
}
|
|
94
|
+
if ((!options.hoistProperty && !isEnumAccess(node, program, ts)) ||
|
|
95
|
+
(!options.hoistEnumValues &&
|
|
96
|
+
(isEnumAccess(node, program, ts) ||
|
|
97
|
+
(ts.isIdentifier(node) && isEnumIdentifier(node, program, ts))))) {
|
|
98
|
+
return node;
|
|
99
|
+
}
|
|
61
100
|
}
|
|
62
|
-
if (
|
|
63
|
-
(!options.
|
|
64
|
-
if (ts.isCallLikeExpression(node)) {
|
|
101
|
+
else if (ts.isAsExpression(node)) {
|
|
102
|
+
if (!options.unsafeHoistAsExpresion) {
|
|
65
103
|
return node;
|
|
66
104
|
}
|
|
67
105
|
}
|
|
106
|
+
else {
|
|
107
|
+
return node;
|
|
108
|
+
}
|
|
68
109
|
if (!options.hoistExternalValues &&
|
|
69
110
|
isExternalReference(node, program, options.externalNames)) {
|
|
70
111
|
return node;
|
|
71
112
|
}
|
|
72
|
-
if (ts.isIdentifier(node)) {
|
|
73
|
-
if (
|
|
74
|
-
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
|
75
|
-
!node.parent ||
|
|
76
|
-
(!ts.isExpression(node.parent) &&
|
|
77
|
-
(!('initializer' in node.parent) ||
|
|
78
|
-
node !== node.parent.initializer)) ||
|
|
79
|
-
(ts.isPropertyAccessExpression(node.parent) && node === node.parent.name)) {
|
|
80
|
-
return node;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
113
|
if (!options.unsafeHoistAsExpresion &&
|
|
84
114
|
(hasAsExpression(node, context, ts) ||
|
|
85
|
-
hasParentAsExpression(
|
|
115
|
+
hasParentAsExpression(parent, context, ts))) {
|
|
86
116
|
return node;
|
|
87
117
|
}
|
|
88
118
|
if (!options.unsafeHoistWritableValues) {
|
|
@@ -96,6 +126,9 @@ function visitNodeAndReplaceIfNeeded(node, sourceFile, program, context, options
|
|
|
96
126
|
const type = typeChecker.getTypeAtLocation(node);
|
|
97
127
|
const flags = type.getFlags();
|
|
98
128
|
let newNode;
|
|
129
|
+
if (type.isUnionOrIntersection()) {
|
|
130
|
+
return node;
|
|
131
|
+
}
|
|
99
132
|
if (type.isStringLiteral()) {
|
|
100
133
|
newNode = context.factory.createStringLiteral(type.value);
|
|
101
134
|
}
|
|
@@ -121,7 +154,12 @@ function visitNodeAndReplaceIfNeeded(node, sourceFile, program, context, options
|
|
|
121
154
|
newNode = context.factory.createNull();
|
|
122
155
|
}
|
|
123
156
|
else if (flags & ts.TypeFlags.Undefined) {
|
|
124
|
-
|
|
157
|
+
if (options.useUndefinedSymbolForUndefinedValue) {
|
|
158
|
+
newNode = context.factory.createIdentifier('undefined');
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
newNode = context.factory.createVoidZero();
|
|
162
|
+
}
|
|
125
163
|
}
|
|
126
164
|
else {
|
|
127
165
|
return node;
|
|
@@ -224,7 +262,8 @@ function hasParentAsExpression(node, context, tsInstance) {
|
|
|
224
262
|
if (ts.isAsExpression(node) && !isAsConstExpression(node)) {
|
|
225
263
|
return true;
|
|
226
264
|
}
|
|
227
|
-
if (ts.isPropertyAccessExpression(node)
|
|
265
|
+
if (ts.isPropertyAccessExpression(node) ||
|
|
266
|
+
ts.isElementAccessExpression(node)) {
|
|
228
267
|
if (hasAsExpression(node.expression, context, ts)) {
|
|
229
268
|
return true;
|
|
230
269
|
}
|
|
@@ -246,6 +285,13 @@ function hasPureAnnotation(node, sourceFile, tsInstance) {
|
|
|
246
285
|
}
|
|
247
286
|
return false;
|
|
248
287
|
}
|
|
288
|
+
function getNameFromElementAccessExpression(node, typeChecker) {
|
|
289
|
+
const type = typeChecker.getTypeAtLocation(node.argumentExpression);
|
|
290
|
+
if (type.isStringLiteral() || type.isNumberLiteral()) {
|
|
291
|
+
return `${type.value}`;
|
|
292
|
+
}
|
|
293
|
+
return null;
|
|
294
|
+
}
|
|
249
295
|
function getMemberName(m, tsInstance) {
|
|
250
296
|
if (!m || !m.name) {
|
|
251
297
|
return '';
|
|
@@ -267,7 +313,12 @@ function getMemberName(m, tsInstance) {
|
|
|
267
313
|
function isReadonlyPropertyAccess(a, typeChecker, tsInstance) {
|
|
268
314
|
const ts = tsInstance;
|
|
269
315
|
const type = typeChecker.getTypeAtLocation(a.expression);
|
|
270
|
-
const memberName =
|
|
316
|
+
const memberName = ts.isPropertyAccessExpression(a)
|
|
317
|
+
? a.name.getText()
|
|
318
|
+
: getNameFromElementAccessExpression(a, typeChecker);
|
|
319
|
+
if (memberName == null) {
|
|
320
|
+
return false;
|
|
321
|
+
}
|
|
271
322
|
if (type.getFlags() & ts.TypeFlags.Object) {
|
|
272
323
|
const dummyTypeNode = typeChecker.typeToTypeNode(type, a, ts.NodeBuilderFlags.NoTruncation);
|
|
273
324
|
if (dummyTypeNode && ts.isTypeLiteralNode(dummyTypeNode)) {
|
|
@@ -317,7 +368,8 @@ function isReadonlyExpression(node, program, tsInstance) {
|
|
|
317
368
|
}
|
|
318
369
|
}
|
|
319
370
|
}
|
|
320
|
-
if (ts.isPropertyAccessExpression(node)
|
|
371
|
+
if (ts.isPropertyAccessExpression(node) ||
|
|
372
|
+
ts.isElementAccessExpression(node)) {
|
|
321
373
|
if (isEnumAccess(node, program, ts)) {
|
|
322
374
|
return true;
|
|
323
375
|
}
|
|
@@ -325,6 +377,42 @@ function isReadonlyExpression(node, program, tsInstance) {
|
|
|
325
377
|
}
|
|
326
378
|
return null;
|
|
327
379
|
}
|
|
380
|
+
function isHoistablePropertyAccess(a, program, tsInstance) {
|
|
381
|
+
const ts = tsInstance;
|
|
382
|
+
const typeChecker = program.getTypeChecker();
|
|
383
|
+
const type = typeChecker.getTypeAtLocation(a.expression);
|
|
384
|
+
const memberName = ts.isPropertyAccessExpression(a)
|
|
385
|
+
? a.name.getText()
|
|
386
|
+
: getNameFromElementAccessExpression(a, typeChecker);
|
|
387
|
+
if (memberName == null) {
|
|
388
|
+
return false;
|
|
389
|
+
}
|
|
390
|
+
if (type.getFlags() & ts.TypeFlags.Object) {
|
|
391
|
+
const prop = type.getProperty(memberName);
|
|
392
|
+
// If the property access uses indexed access, `prop` will be undefined
|
|
393
|
+
if (prop) {
|
|
394
|
+
return true;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
return false;
|
|
398
|
+
}
|
|
399
|
+
function isUndefinedIdentifier(node, parent, program, tsInstance) {
|
|
400
|
+
if (tsInstance.isPropertyAccessExpression(parent) ||
|
|
401
|
+
tsInstance.isElementAccessExpression(parent)) {
|
|
402
|
+
return false;
|
|
403
|
+
}
|
|
404
|
+
const typeChecker = program.getTypeChecker();
|
|
405
|
+
const type = typeChecker.getTypeAtLocation(node);
|
|
406
|
+
const sym = typeChecker.getSymbolAtLocation(node);
|
|
407
|
+
if (!sym || sym.getEscapedName().toString() !== 'undefined') {
|
|
408
|
+
return false;
|
|
409
|
+
}
|
|
410
|
+
if (type.isUnionOrIntersection() ||
|
|
411
|
+
!(type.getFlags() & ts.TypeFlags.Undefined)) {
|
|
412
|
+
return false;
|
|
413
|
+
}
|
|
414
|
+
return true;
|
|
415
|
+
}
|
|
328
416
|
////////////////////////////////////////////////////////////////////////////////
|
|
329
417
|
export function printSource(sourceFile) {
|
|
330
418
|
return printSourceImpl(sourceFile)[0];
|
package/dist/version.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: "0.
|
|
1
|
+
declare const _default: "0.5.1";
|
|
2
2
|
export default _default;
|
package/dist/version.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export default '0.
|
|
1
|
+
export default '0.5.1';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-const-value-transformer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"engines": {
|
|
5
5
|
"node": ">=20.19.3"
|
|
6
6
|
},
|
|
@@ -23,6 +23,16 @@
|
|
|
23
23
|
"import": "./dist/createTransformer.mjs",
|
|
24
24
|
"types": "./dist/createTransformer.d.mts"
|
|
25
25
|
},
|
|
26
|
+
"./createPortalTransformer": {
|
|
27
|
+
"default": "./dist/createPortalTransformer.mjs",
|
|
28
|
+
"import": "./dist/createPortalTransformer.mjs",
|
|
29
|
+
"types": "./dist/createPortalTransformer.d.mts"
|
|
30
|
+
},
|
|
31
|
+
"./createPortalTransformer.mjs": {
|
|
32
|
+
"default": "./dist/createPortalTransformer.mjs",
|
|
33
|
+
"import": "./dist/createPortalTransformer.mjs",
|
|
34
|
+
"types": "./dist/createPortalTransformer.d.mts"
|
|
35
|
+
},
|
|
26
36
|
"./getCustomTransformers": {
|
|
27
37
|
"default": "./dist/getCustomTransformers.mjs",
|
|
28
38
|
"import": "./dist/getCustomTransformers.mjs",
|