ts-const-value-transformer 0.1.1 → 0.2.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 +12 -0
- package/README.md +7 -1
- package/dist/transform.d.mts +6 -0
- package/dist/transform.mjs +78 -7
- package/dist/version.d.mts +1 -1
- package/dist/version.mjs +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -150,14 +150,20 @@ export interface TransformOptions {
|
|
|
150
150
|
ts?: typeof ts;
|
|
151
151
|
/** Hoist property expressions (`x.prop`) which the value is constant. Default is true, but if the property getter has side effects (not recommended), set false explicitly. */
|
|
152
152
|
hoistProperty?: boolean | undefined;
|
|
153
|
+
/** Hoist TypeScript's `enum` values (which are constant). Default is true, but if you want to preserve references, set false explicitly. Note that TypeScript compiler erases `const enum` references unless `preserveConstEnums` is true. */
|
|
154
|
+
hoistEnumValues?: boolean | undefined;
|
|
155
|
+
/** Hoist values defined in the external libraries. Default is true, but if the external libraries are not bundled, set false explicitly to keep references. */
|
|
156
|
+
hoistExternalValues?: boolean | undefined;
|
|
153
157
|
/** Hoist function calls which the return value is constant. Default is false because function calls may have side effects. */
|
|
154
158
|
unsafeHoistFunctionCall?: boolean | undefined;
|
|
159
|
+
/** Hoist function calls, with `@__PURE__` (or `#__PURE__`) comment annotation (must be a multi-line comment), which the return value is constant. Default is false, but if the function really has no side effects, you can safely specify true. If true, `unsafeHoistFunctionCall` option is ignored for `@__PURE__` functions */
|
|
160
|
+
hoistPureFunctionCall?: boolean | undefined;
|
|
155
161
|
/** Hoist expressions with `as XXX`. Default is false because the base (non-`as`) value may be non-constant. */
|
|
156
162
|
unsafeHoistAsExpresion?: boolean | undefined;
|
|
157
163
|
}
|
|
158
164
|
```
|
|
159
165
|
|
|
160
|
-
Note that you must pass `options` field to `createTransformer` function or `"plugins"` specifier.
|
|
166
|
+
Note that you must pass `options` field to `createTransformer` function or `"plugins"` specifier (see above examples).
|
|
161
167
|
|
|
162
168
|
### APIs
|
|
163
169
|
|
package/dist/transform.d.mts
CHANGED
|
@@ -5,8 +5,14 @@ export interface TransformOptions {
|
|
|
5
5
|
ts?: typeof ts;
|
|
6
6
|
/** Hoist property expressions (`x.prop`) which the value is constant. Default is true, but if the property getter has side effects (not recommended), set false explicitly. */
|
|
7
7
|
hoistProperty?: boolean | undefined;
|
|
8
|
+
/** Hoist TypeScript's `enum` values (which are constant). Default is true, but if you want to preserve references, set false explicitly. Note that TypeScript compiler erases `const enum` references unless `preserveConstEnums` is true. */
|
|
9
|
+
hoistEnumValues?: boolean | undefined;
|
|
10
|
+
/** Hoist values defined in the external libraries. Default is true, but if the external libraries are not bundled, set false explicitly to keep references. */
|
|
11
|
+
hoistExternalValues?: boolean | undefined;
|
|
8
12
|
/** Hoist function calls which the return value is constant. Default is false because function calls may have side effects. */
|
|
9
13
|
unsafeHoistFunctionCall?: boolean | undefined;
|
|
14
|
+
/** Hoist function calls, with `@__PURE__` (or `#__PURE__`) comment annotation (must be a multi-line comment), which the return value is constant. Default is false, but if the function really has no side effects, you can safely specify true. If true, `unsafeHoistFunctionCall` option is ignored for `@__PURE__` functions */
|
|
15
|
+
hoistPureFunctionCall?: boolean | undefined;
|
|
10
16
|
/** Hoist expressions with `as XXX`. Default is false because the base (non-`as`) value may be non-constant. */
|
|
11
17
|
unsafeHoistAsExpresion?: boolean | undefined;
|
|
12
18
|
}
|
package/dist/transform.mjs
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import * as sourceMap from 'source-map';
|
|
2
2
|
import * as ts from 'typescript';
|
|
3
3
|
const SYMBOL_ORIGINAL_NODE = Symbol('originalNode');
|
|
4
|
-
function assignDefaultValues(options) {
|
|
4
|
+
function assignDefaultValues(options = {}) {
|
|
5
5
|
return {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
// avoid using spread syntax to override `undefined` (not missing) values
|
|
7
|
+
ts: options.ts ?? ts,
|
|
8
|
+
hoistProperty: options.hoistProperty ?? true,
|
|
9
|
+
hoistEnumValues: options.hoistEnumValues ?? true,
|
|
10
|
+
hoistExternalValues: options.hoistExternalValues ?? true,
|
|
11
|
+
unsafeHoistAsExpresion: options.unsafeHoistAsExpresion ?? false,
|
|
12
|
+
hoistPureFunctionCall: options.hoistPureFunctionCall ?? false,
|
|
13
|
+
unsafeHoistFunctionCall: options.unsafeHoistFunctionCall ?? false,
|
|
11
14
|
};
|
|
12
15
|
}
|
|
13
16
|
////////////////////////////////////////////////////////////////////////////////
|
|
@@ -37,12 +40,24 @@ function visitNodeAndReplaceIfNeeded(node, sourceFile, program, context, options
|
|
|
37
40
|
(!options.hoistProperty &&
|
|
38
41
|
ts.isPropertyAccessExpression(node) &&
|
|
39
42
|
!isEnumAccess(node, program, ts)) ||
|
|
40
|
-
(!options.
|
|
43
|
+
(!options.hoistEnumValues &&
|
|
44
|
+
((ts.isPropertyAccessExpression(node) &&
|
|
45
|
+
isEnumAccess(node, program, ts)) ||
|
|
46
|
+
(ts.isIdentifier(node) && isEnumIdentifier(node, program, ts)))) ||
|
|
41
47
|
node.kind === ts.SyntaxKind.TrueKeyword ||
|
|
42
48
|
node.kind === ts.SyntaxKind.FalseKeyword ||
|
|
43
49
|
node.kind === ts.SyntaxKind.NullKeyword) {
|
|
44
50
|
return node;
|
|
45
51
|
}
|
|
52
|
+
if (!options.unsafeHoistFunctionCall &&
|
|
53
|
+
(!options.hoistPureFunctionCall || !hasPureAnnotation(node, sourceFile, ts))) {
|
|
54
|
+
if (ts.isCallLikeExpression(node)) {
|
|
55
|
+
return node;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (!options.hoistExternalValues && isExternalReference(node, program)) {
|
|
59
|
+
return node;
|
|
60
|
+
}
|
|
46
61
|
if (ts.isIdentifier(node)) {
|
|
47
62
|
if (
|
|
48
63
|
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
|
@@ -111,6 +126,47 @@ function isEnumAccess(node, program, tsInstance) {
|
|
|
111
126
|
const type = typeChecker.getTypeAtLocation(node);
|
|
112
127
|
return (type.getFlags() & ts.TypeFlags.EnumLiteral) !== 0;
|
|
113
128
|
}
|
|
129
|
+
function isEnumIdentifier(node, program, tsInstance) {
|
|
130
|
+
const ts = tsInstance;
|
|
131
|
+
const typeChecker = program.getTypeChecker();
|
|
132
|
+
const type = typeChecker.getTypeAtLocation(node);
|
|
133
|
+
return (type.getFlags() & ts.TypeFlags.EnumLiteral) !== 0;
|
|
134
|
+
}
|
|
135
|
+
function isExternalReference(node, program) {
|
|
136
|
+
const typeChecker = program.getTypeChecker();
|
|
137
|
+
const nodeSym = typeChecker.getSymbolAtLocation(node);
|
|
138
|
+
let nodeFrom = nodeSym?.getDeclarations()?.[0];
|
|
139
|
+
while (nodeFrom) {
|
|
140
|
+
if (program.isSourceFileFromExternalLibrary(nodeFrom.getSourceFile())) {
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
// Walk into the 'import' variables
|
|
144
|
+
if (!ts.isImportSpecifier(nodeFrom)) {
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
const baseName = nodeFrom.propertyName ?? nodeFrom.name;
|
|
148
|
+
const baseSym = typeChecker.getSymbolAtLocation(baseName);
|
|
149
|
+
// We must follow 'aliased' symbol for parsing the symbol which name is not changed from the exported symbol name
|
|
150
|
+
const exportedSym = baseSym && baseSym.getFlags() & ts.SymbolFlags.Alias
|
|
151
|
+
? typeChecker.getAliasedSymbol(baseSym)
|
|
152
|
+
: baseSym;
|
|
153
|
+
nodeFrom = exportedSym?.getDeclarations()?.[0];
|
|
154
|
+
}
|
|
155
|
+
const type = typeChecker.getTypeAtLocation(node);
|
|
156
|
+
const sym = type.getSymbol();
|
|
157
|
+
if (!sym) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
const def = sym.getDeclarations()?.[0];
|
|
161
|
+
if (!def) {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
const typeDefinitionSource = def.getSourceFile();
|
|
165
|
+
if (program.isSourceFileFromExternalLibrary(typeDefinitionSource)) {
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
114
170
|
function isAsConstExpression(node) {
|
|
115
171
|
return node.type.getText() === 'const';
|
|
116
172
|
}
|
|
@@ -145,6 +201,21 @@ function hasParentAsExpression(node, context, tsInstance) {
|
|
|
145
201
|
}
|
|
146
202
|
return hasParentAsExpression(node.parent, context, ts);
|
|
147
203
|
}
|
|
204
|
+
function hasPureAnnotation(node, sourceFile, tsInstance) {
|
|
205
|
+
const ts = tsInstance;
|
|
206
|
+
const fullText = node.getFullText(sourceFile);
|
|
207
|
+
const ranges = ts.getLeadingCommentRanges(fullText, 0) ?? [];
|
|
208
|
+
for (const range of ranges) {
|
|
209
|
+
if (range.kind !== ts.SyntaxKind.MultiLineCommentTrivia) {
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
const text = fullText.slice(range.pos + 2, range.end - 2).trim();
|
|
213
|
+
if ((text[0] === '@' || text[0] === '#') && text.slice(1) === '__PURE__') {
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
148
219
|
////////////////////////////////////////////////////////////////////////////////
|
|
149
220
|
export function printSource(sourceFile) {
|
|
150
221
|
return printSourceImpl(sourceFile)[0];
|
package/dist/version.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: "0.
|
|
1
|
+
declare const _default: "0.2.1";
|
|
2
2
|
export default _default;
|
package/dist/version.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export default '0.
|
|
1
|
+
export default '0.2.1';
|