ts-const-value-transformer 0.5.1 → 0.7.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/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## v0.7.0
4
+
5
+ - Fix for printing 'minus numeric value' and 'void 0', and remove using `ts.createPrinter`
6
+ - Fix that `recreateProgram` creates the new fresh program instead of passing `oldProgram`
7
+ - Fix to cache source code with no transformation
8
+ - Add option to disable caching original source content (changed to false by default)
9
+ - Fix some codes for better memory usage
10
+
11
+ ## v0.6.0
12
+
13
+ - Remove skipping satisfies expression
14
+ - Accept `undefined` for `context` and remove dependencies for `context`
15
+ - Fix referring `ts` instance and add `ts` parameter for printSource
16
+ - Add cache to createPortalTransformer
17
+ - Add `recreateProgramOnTransformCount` option for PortalTransformer
18
+ - Fix to use `createPortalTransformerSync` for webpack loader
19
+ - Search tsconfig before loading
20
+
3
21
  ## v0.5.1
4
22
 
5
23
  - Fix missing for handling `ignoreFiles` for `createTransformer` (used from ts-loader, etc.)
package/README.md CHANGED
@@ -279,7 +279,26 @@ See [Transform options](#transform-options).
279
279
  Creates 'portal transformer', which can be used the transformer easily from the code which does not use TypeScript Compiler API.
280
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
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.
282
+ `CreatePortalTransformerOptions` has a following signature. Also, `ignoreFiles` of `TransformOptions` can be used.
283
+
284
+ ```ts
285
+ interface CreatePortalTransformerOptions extends TransformOptions {
286
+ /** Path to tsconfig.json. If omitted, `tsconfig.json` will be used. */
287
+ project?: string;
288
+ /** Package path to `typescript` or `typescript` namespace object. */
289
+ typescript?: string | typeof tsNamespace;
290
+ /** The current directory for file search. Also affects to `project` option. */
291
+ cwd?: string;
292
+ /**
293
+ * Speficies the count. When the transformation count reaches this value, `program` instance will be recreated (and count will be reset).
294
+ * This is useful if the project is big and out-of-memory occurs during transformation, but the process may be slower.
295
+ * If 0 or `undefined`, recreation will not be performed.
296
+ */
297
+ recreateProgramOnTransformCount?: number;
298
+ /** Specifies to cache base (original) source code for check if the input is changed. Default is false. */
299
+ cacheBaseSource?: boolean;
300
+ }
301
+ ```
283
302
 
284
303
  If `Promise` cannot be used for some reason, use `createPortalTransformerSync` instead.
285
304
 
@@ -2,15 +2,38 @@ import type { RawSourceMap } from 'source-map';
2
2
  import type * as tsNamespace from 'typescript';
3
3
  import { type TransformOptions } from './transform.mjs';
4
4
  export interface CreatePortalTransformerOptions extends TransformOptions {
5
+ /** Path to tsconfig.json. If omitted, `tsconfig.json` will be used. */
5
6
  project?: string;
7
+ /** Package path to `typescript` or `typescript` namespace object. */
6
8
  typescript?: string | typeof tsNamespace;
9
+ /** The current directory for file search. Also affects to `project` option. */
7
10
  cwd?: string;
11
+ /**
12
+ * Specifies the count. When the transformation count reaches this value, `program` instance will be recreated (and count will be reset).
13
+ * This is useful if the project is big and out-of-memory occurs during transformation, but the process may be slower.
14
+ * If 0 or `undefined`, recreation will not be performed.
15
+ */
16
+ recreateProgramOnTransformCount?: number;
17
+ /** Specifies to cache base (original) source code for check if the input is changed. Default is false. */
18
+ cacheBaseSource?: boolean;
8
19
  }
20
+ export type PortalTransformerResult = [
21
+ newSource: string | null,
22
+ newSourceMap: RawSourceMap | undefined
23
+ ];
24
+ export type PortalTransformerResultNonNull = [
25
+ newSource: string,
26
+ newSourceMap: RawSourceMap | undefined
27
+ ];
9
28
  export interface PortalTransformer {
10
29
  /** The `typescript` namespace object */
11
30
  readonly ts: typeof tsNamespace;
12
31
  /** Active `Program` instance for the transformer */
13
32
  readonly program: tsNamespace.Program;
33
+ /** Clears transformed cache. */
34
+ clearCache(): void;
35
+ /** Forces `program` recreation. The transformation count for `recreateProgramOnTransformCount` will also be resetted. */
36
+ recreateProgram(): void;
14
37
  /**
15
38
  * Performs transformation.
16
39
  * @param content Base source code. If null, uses loaded source code in the TS project.
@@ -19,7 +42,7 @@ export interface PortalTransformer {
19
42
  * @param options Transform options (addition to `options` passed to `createPortalTransformer`)
20
43
  * @returns Tuple of new source code and source map. Source map may be undefined if source code is unchanged.
21
44
  */
22
- transform(content: string, fileName: string, sourceMap?: string | RawSourceMap | null, options?: TransformOptions): [newSource: string, newSourceMap: RawSourceMap | undefined];
45
+ transform(content: string, fileName: string, sourceMap?: string | RawSourceMap | null, options?: TransformOptions): PortalTransformerResultNonNull;
23
46
  /**
24
47
  * Performs transformation.
25
48
  * @param content Base source code. If null, uses loaded source code in the TS project.
@@ -28,7 +51,7 @@ export interface PortalTransformer {
28
51
  * @param options Transform options (addition to `options` passed to `createPortalTransformer`)
29
52
  * @returns Tuple of new source code and source map. Source map may be undefined if source code is unchanged.
30
53
  */
31
- transform(content: string | null, fileName: string, sourceMap?: string | RawSourceMap | null, options?: TransformOptions): [newSource: string | null, newSourceMap: RawSourceMap | undefined];
54
+ transform(content: string | null, fileName: string, sourceMap?: string | RawSourceMap | null, options?: TransformOptions): PortalTransformerResult;
32
55
  }
33
56
  /**
34
57
  * Creates the new portal transformer instance for the TS project.
@@ -4,12 +4,30 @@ import * as path from 'path';
4
4
  import createTransformer from './createTransformer.mjs';
5
5
  import { getIgnoreFilesFunction, printSourceWithMap, } from './transform.mjs';
6
6
  const require = createRequire(import.meta.url);
7
+ function optionsToString(options) {
8
+ return JSON.stringify(options, (key, value) => {
9
+ if (typeof value === 'function' || value instanceof RegExp) {
10
+ return value.toString();
11
+ }
12
+ if (key === 'typescript' && typeof value === 'object' && value != null) {
13
+ return '[object typescript]';
14
+ }
15
+ return value;
16
+ });
17
+ }
7
18
  function createPortalTransformerImpl(options, ts) {
8
19
  const project = options.project ?? 'tsconfig.json';
9
20
  const ignoreFiles = getIgnoreFilesFunction(options.ignoreFiles);
10
21
  const cwd = options.cwd ?? process.cwd();
22
+ const recreateProgramOnTransformCount = options.recreateProgramOnTransformCount ?? 0;
23
+ const cacheBaseSource = options.cacheBaseSource ?? false;
24
+ // eslint-disable-next-line @typescript-eslint/unbound-method
25
+ const foundConfigPath = ts.findConfigFile(cwd, ts.sys.fileExists, project);
26
+ if (foundConfigPath == null) {
27
+ throw new Error(`[ts-const-value-transformer] Unable to load tsconfig file (effective name = '${project}')`);
28
+ }
11
29
  const getCurrentDirectory = () => cwd;
12
- const config = ts.getParsedCommandLineOfConfigFile(project, void 0, {
30
+ const config = ts.getParsedCommandLineOfConfigFile(foundConfigPath, void 0, {
13
31
  fileExists: fs.existsSync,
14
32
  getCurrentDirectory,
15
33
  // eslint-disable-next-line @typescript-eslint/unbound-method
@@ -25,16 +43,41 @@ function createPortalTransformerImpl(options, ts) {
25
43
  },
26
44
  });
27
45
  if (!config) {
28
- throw new Error(`[ts-const-value-transformer] Unable to load tsconfig file (effective name = '${project}')`);
46
+ throw new Error(`[ts-const-value-transformer] Unable to load tsconfig file (effective name = '${foundConfigPath}')`);
29
47
  }
30
- const program = ts.createProgram({
48
+ let program = ts.createProgram({
31
49
  options: config.options,
32
50
  rootNames: config.fileNames,
33
51
  });
34
- return {
52
+ let transformationCount = 0;
53
+ const recreateProgram = () => {
54
+ // @ts-expect-error: We must clear reference first to give change for gc
55
+ delete instance.program;
56
+ // @ts-expect-error: We must clear reference first to give change for gc
57
+ program = null;
58
+ // We don't pass `oldProgram` because the transformed source codes should not be necessary (the transformation does not change logics and types)
59
+ // If we pass `oldProgram`, temporal memory usage may increase because gc cannot release `oldProgram` before creating new program
60
+ program = ts.createProgram({
61
+ options: config.options,
62
+ rootNames: config.fileNames,
63
+ });
64
+ instance.program = program;
65
+ transformationCount = 0;
66
+ };
67
+ const cache = new Map();
68
+ const instance = {
35
69
  ts,
36
70
  program,
71
+ clearCache: () => cache.clear(),
72
+ recreateProgram,
37
73
  transform: (content, fileName, sourceMap, individualOptions) => {
74
+ const individualOptionsJson = optionsToString(individualOptions ?? {});
75
+ const cachedData = cache.get(fileName);
76
+ if (cachedData &&
77
+ (!cacheBaseSource || cachedData.content === content) &&
78
+ cachedData.optJson === individualOptionsJson) {
79
+ return cachedData.result;
80
+ }
38
81
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
39
82
  const rawSourceMap = typeof sourceMap === 'string'
40
83
  ? JSON.parse(sourceMap)
@@ -46,26 +89,41 @@ function createPortalTransformerImpl(options, ts) {
46
89
  if (!sourceFile) {
47
90
  return [content, rawSourceMap];
48
91
  }
92
+ transformationCount++;
93
+ if (recreateProgramOnTransformCount > 0 &&
94
+ transformationCount >= recreateProgramOnTransformCount) {
95
+ recreateProgram();
96
+ }
49
97
  // If input content is changed, replace it
50
- if (content != null && sourceFile.getFullText() !== content) {
98
+ if (content != null && sourceFile.text !== content) {
51
99
  sourceFile.update(content, {
52
100
  span: { start: 0, length: sourceFile.end },
53
101
  newLength: content.length,
54
102
  });
103
+ sourceFile.text = content;
55
104
  }
56
105
  const transformer = createTransformer(program, {
57
106
  options: { ...options, ...individualOptions, ts },
58
107
  });
59
- const result = ts.transform(sourceFile, [transformer], program.getCompilerOptions());
60
- const transformedSource = result.transformed[0];
108
+ const transformResult = ts.transform(sourceFile, [transformer], program.getCompilerOptions());
109
+ const transformedSource = transformResult.transformed[0];
110
+ let result;
61
111
  // If unchanged, return base file as-is
62
112
  if (transformedSource === sourceFile) {
63
- return [content ?? sourceFile.getFullText(), rawSourceMap];
113
+ result = [content ?? sourceFile.text, rawSourceMap];
114
+ }
115
+ else {
116
+ result = printSourceWithMap(transformedSource, fileName, rawSourceMap, ts);
64
117
  }
65
- const printed = printSourceWithMap(transformedSource, fileName, rawSourceMap);
66
- return printed;
118
+ cache.set(fileName, {
119
+ content: cacheBaseSource ? content : '',
120
+ optJson: individualOptionsJson,
121
+ result,
122
+ });
123
+ return result;
67
124
  },
68
125
  };
126
+ return instance;
69
127
  }
70
128
  /**
71
129
  * Creates the new portal transformer instance for the TS project.
@@ -3,7 +3,8 @@ import { type TransformOptions } from './transform.mjs';
3
3
  interface Config {
4
4
  options?: TransformOptions;
5
5
  }
6
+ export type TransformerFactory = (context?: ts.TransformationContext) => (sourceFile: ts.SourceFile) => ts.SourceFile;
6
7
  export default function createTransformer(program: ts.Program, config?: Config, extras?: {
7
8
  ts?: typeof ts;
8
- }): ts.TransformerFactory<ts.SourceFile>;
9
+ }): TransformerFactory;
9
10
  export {};
@@ -9,12 +9,12 @@ extras) {
9
9
  ...(extras?.ts && { ts: extras?.ts }),
10
10
  };
11
11
  const ignoreFiles = getIgnoreFilesFunction(options.ignoreFiles);
12
- return (context) => {
12
+ return ((context) => {
13
13
  return (sourceFile) => {
14
14
  if (ignoreFiles(sourceFile.fileName)) {
15
15
  return sourceFile;
16
16
  }
17
17
  return transformSource(sourceFile, program, context, options);
18
18
  };
19
- };
19
+ });
20
20
  }
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import createPortalTransformer, { createPortalTransformerSync, type CreatePortalTransformerOptions, type PortalTransformer } from './createPortalTransformer.mjs';
1
+ import createPortalTransformer, { createPortalTransformerSync, type CreatePortalTransformerOptions, type PortalTransformer, type PortalTransformerResult, type PortalTransformerResultNonNull } from './createPortalTransformer.mjs';
2
2
  import createTransformer from './createTransformer.mjs';
3
3
  import version from './version.mjs';
4
4
  export { printSource, printSourceWithMap, transformSource, type TransformOptions, } from './transform.mjs';
5
- export { createPortalTransformer, createPortalTransformerSync, createTransformer, type CreatePortalTransformerOptions, type PortalTransformer, version, };
5
+ export { createPortalTransformer, createPortalTransformerSync, createTransformer, type CreatePortalTransformerOptions, type PortalTransformer, type PortalTransformerResult, type PortalTransformerResultNonNull, version, };
package/dist/loader.d.mts CHANGED
@@ -1,9 +1,5 @@
1
- import type * as tsNamespace from 'typescript';
2
1
  import type * as webpack from 'webpack';
3
- import type { TransformOptions } from './transform.mjs';
4
- export interface TsConstValueTransformerLoaderOptions extends TransformOptions {
5
- project?: string;
6
- typescript?: string | typeof tsNamespace;
7
- }
2
+ import { type CreatePortalTransformerOptions } from './createPortalTransformer.mjs';
3
+ export type TsConstValueTransformerLoaderOptions = CreatePortalTransformerOptions;
8
4
  declare const loader: webpack.LoaderDefinitionFunction<TsConstValueTransformerLoaderOptions | undefined>;
9
5
  export default loader;
package/dist/loader.mjs CHANGED
@@ -1,17 +1,19 @@
1
1
  import * as path from 'path';
2
- import createPortalTransformer, {} from './createPortalTransformer.mjs';
2
+ import { createPortalTransformerSync, } from './createPortalTransformer.mjs';
3
3
  const transformerMap = new Map();
4
4
  const loader = function (content, sourceMap) {
5
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions, @typescript-eslint/strict-boolean-expressions
6
+ this.cacheable && this.cacheable();
5
7
  this.async();
6
- void (async () => {
8
+ void Promise.resolve().then(() => {
7
9
  try {
8
10
  const options = this.getOptions() || {};
9
11
  const project = options.project ?? 'tsconfig.json';
10
12
  let transformer = transformerMap.get(project);
11
13
  if (!transformer) {
12
- transformer = await createPortalTransformer({
13
- ...options,
14
+ transformer = createPortalTransformerSync({
14
15
  cwd: path.dirname(this.resourcePath),
16
+ ...options,
15
17
  });
16
18
  transformerMap.set(project, transformer);
17
19
  }
@@ -21,6 +23,6 @@ const loader = function (content, sourceMap) {
21
23
  catch (e) {
22
24
  this.callback(e);
23
25
  }
24
- })();
26
+ });
25
27
  };
26
28
  export default loader;
@@ -1,5 +1,5 @@
1
1
  import * as sourceMap from 'source-map';
2
- import * as ts from 'typescript';
2
+ import type * as ts from 'typescript';
3
3
  export interface TransformOptions {
4
4
  /** `typescript` namespace object */
5
5
  ts?: typeof ts;
@@ -36,6 +36,6 @@ export interface TransformOptions {
36
36
  ignoreFiles?: ReadonlyArray<string | RegExp> | ((fileName: string) => boolean);
37
37
  }
38
38
  export declare function getIgnoreFilesFunction(ignoreFiles: TransformOptions['ignoreFiles']): (fileName: string) => boolean;
39
- export declare function transformSource(sourceFile: ts.SourceFile, program: ts.Program, context: ts.TransformationContext, options?: TransformOptions): ts.SourceFile;
40
- export declare function printSource(sourceFile: ts.SourceFile): string;
41
- export declare function printSourceWithMap(sourceFile: ts.SourceFile, originalSourceName: string, startOfSourceMap?: sourceMap.RawSourceMap): [string, sourceMap.RawSourceMap];
39
+ export declare function transformSource(sourceFile: ts.SourceFile, program: ts.Program, context: ts.TransformationContext | undefined, options?: TransformOptions): ts.SourceFile;
40
+ export declare function printSource(sourceFile: ts.SourceFile, tsInstance?: typeof ts): string;
41
+ export declare function printSourceWithMap(sourceFile: ts.SourceFile, originalSourceName: string, startOfSourceMap?: sourceMap.RawSourceMap, tsInstance?: typeof ts): [string, sourceMap.RawSourceMap];
@@ -1,10 +1,10 @@
1
1
  import * as sourceMap from 'source-map';
2
- import * as ts from 'typescript';
3
- const SYMBOL_ORIGINAL_NODE = Symbol('originalNode');
2
+ import * as tsNamespace from 'typescript';
3
+ const SYMBOL_ORIGINAL_NODE_DATA = Symbol('originalNodeData');
4
4
  function assignDefaultValues(options = {}) {
5
5
  return {
6
6
  // avoid using spread syntax to override `undefined` (not missing) values
7
- ts: options.ts ?? ts,
7
+ ts: options.ts ?? tsNamespace,
8
8
  hoistProperty: options.hoistProperty ?? true,
9
9
  hoistEnumValues: options.hoistEnumValues ?? true,
10
10
  hoistExternalValues: options.hoistExternalValues ?? true,
@@ -40,15 +40,13 @@ export function getIgnoreFilesFunction(ignoreFiles) {
40
40
  }
41
41
  ////////////////////////////////////////////////////////////////////////////////
42
42
  export function transformSource(sourceFile, program, context, options) {
43
- return visitNodeChildren(sourceFile, sourceFile, sourceFile, program, context, assignDefaultValues(options));
43
+ const requiredOptions = assignDefaultValues(options);
44
+ return requiredOptions.ts.visitEachChild(sourceFile, (node) => visitNodeChildren(node, sourceFile, sourceFile, program, requiredOptions), context);
44
45
  }
45
- function visitNodeChildren(node, parent, sourceFile, program, context, options) {
46
- const newNode = visitNodeAndReplaceIfNeeded(node, parent, sourceFile, program, context, options);
47
- if (newNode[SYMBOL_ORIGINAL_NODE]) {
48
- return newNode;
49
- }
50
- // skip children for satisifes expression
51
- if (ts.isSatisfiesExpression(newNode)) {
46
+ function visitNodeChildren(node, parent, sourceFile, program, options) {
47
+ const ts = options.ts;
48
+ const newNode = visitNodeAndReplaceIfNeeded(node, parent, sourceFile, program, options);
49
+ if (newNode[SYMBOL_ORIGINAL_NODE_DATA]) {
52
50
  return newNode;
53
51
  }
54
52
  // skip statements which would not have 'value' expressions
@@ -59,9 +57,9 @@ function visitNodeChildren(node, parent, sourceFile, program, context, options)
59
57
  ts.isTypeOnlyExportDeclaration(newNode)) {
60
58
  return newNode;
61
59
  }
62
- return ts.visitEachChild(newNode, (node) => visitNodeChildren(node, newNode, sourceFile, program, context, options), context);
60
+ return ts.visitEachChild(newNode, (node) => visitNodeChildren(node, newNode, sourceFile, program, options), void 0);
63
61
  }
64
- function visitNodeAndReplaceIfNeeded(node, parent, sourceFile, program, context, options) {
62
+ function visitNodeAndReplaceIfNeeded(node, parent, sourceFile, program, options) {
65
63
  const ts = options.ts;
66
64
  if (ts.isCallLikeExpression(node)) {
67
65
  if (!ts.isExpression(node) ||
@@ -107,12 +105,11 @@ function visitNodeAndReplaceIfNeeded(node, parent, sourceFile, program, context,
107
105
  return node;
108
106
  }
109
107
  if (!options.hoistExternalValues &&
110
- isExternalReference(node, program, options.externalNames)) {
108
+ isExternalReference(node, program, options.externalNames, ts)) {
111
109
  return node;
112
110
  }
113
111
  if (!options.unsafeHoistAsExpresion &&
114
- (hasAsExpression(node, context, ts) ||
115
- hasParentAsExpression(parent, context, ts))) {
112
+ (hasAsExpression(node, ts) || hasParentAsExpression(parent, ts))) {
116
113
  return node;
117
114
  }
118
115
  if (!options.unsafeHoistWritableValues) {
@@ -129,36 +126,50 @@ function visitNodeAndReplaceIfNeeded(node, parent, sourceFile, program, context,
129
126
  if (type.isUnionOrIntersection()) {
130
127
  return node;
131
128
  }
129
+ let newSource;
132
130
  if (type.isStringLiteral()) {
133
- newNode = context.factory.createStringLiteral(type.value);
131
+ newNode = ts.factory.createStringLiteral(type.value);
132
+ newSource =
133
+ // TypeScript namespace may export `function escapeNonAsciiString(s: string, quoteChar?: CharacterCodes.doubleQuote | CharacterCodes.singleQuote | CharacterCodes.backtick): string`
134
+ 'escapeNonAsciiString' in ts
135
+ ? `"${ts.escapeNonAsciiString(type.value, 'CharacterCodes' in ts
136
+ ? ts.CharacterCodes.doubleQuote
137
+ : 34 /* doubleQuote */)}"`
138
+ : JSON.stringify(type.value);
134
139
  }
135
140
  else if (type.isNumberLiteral()) {
136
141
  if (type.value < 0) {
137
- newNode = context.factory.createPrefixUnaryExpression(ts.SyntaxKind.MinusToken, context.factory.createNumericLiteral(-type.value));
142
+ newNode = ts.factory.createParenthesizedExpression(ts.factory.createPrefixUnaryExpression(ts.SyntaxKind.MinusToken, ts.factory.createNumericLiteral(-type.value)));
143
+ newSource = `(-${-type.value})`;
138
144
  }
139
145
  else {
140
- newNode = context.factory.createNumericLiteral(type.value);
146
+ newNode = ts.factory.createNumericLiteral(type.value);
147
+ newSource = `${type.value}`;
141
148
  }
142
149
  }
143
150
  else if (flags & ts.TypeFlags.BigIntLiteral) {
144
- newNode = context.factory.createBigIntLiteral(typeChecker.typeToString(type));
151
+ const text = typeChecker.typeToString(type);
152
+ newNode = ts.factory.createBigIntLiteral(text);
153
+ newSource = text;
145
154
  }
146
155
  else if (flags & ts.TypeFlags.BooleanLiteral) {
147
156
  const text = typeChecker.typeToString(type);
148
157
  newNode =
149
- text === 'true'
150
- ? context.factory.createTrue()
151
- : context.factory.createFalse();
158
+ text === 'true' ? ts.factory.createTrue() : ts.factory.createFalse();
159
+ newSource = text;
152
160
  }
153
161
  else if (flags & ts.TypeFlags.Null) {
154
- newNode = context.factory.createNull();
162
+ newNode = ts.factory.createNull();
163
+ newSource = 'null';
155
164
  }
156
165
  else if (flags & ts.TypeFlags.Undefined) {
157
166
  if (options.useUndefinedSymbolForUndefinedValue) {
158
- newNode = context.factory.createIdentifier('undefined');
167
+ newNode = ts.factory.createIdentifier('undefined');
168
+ newSource = 'undefined';
159
169
  }
160
170
  else {
161
- newNode = context.factory.createVoidZero();
171
+ newNode = ts.factory.createParenthesizedExpression(ts.factory.createVoidZero());
172
+ newSource = '(void 0)';
162
173
  }
163
174
  }
164
175
  else {
@@ -166,7 +177,12 @@ function visitNodeAndReplaceIfNeeded(node, parent, sourceFile, program, context,
166
177
  }
167
178
  const result = ts.addSyntheticTrailingComment(newNode, ts.SyntaxKind.MultiLineCommentTrivia, ` ${node.getText(sourceFile)} `);
168
179
  ts.setTextRange(result, node);
169
- result[SYMBOL_ORIGINAL_NODE] = node;
180
+ result[SYMBOL_ORIGINAL_NODE_DATA] = [
181
+ node.getText(sourceFile),
182
+ newSource,
183
+ node.pos,
184
+ node.end,
185
+ ];
170
186
  return result;
171
187
  }
172
188
  catch {
@@ -185,7 +201,8 @@ function isEnumIdentifier(node, program, tsInstance) {
185
201
  const type = typeChecker.getTypeAtLocation(node);
186
202
  return (type.getFlags() & ts.TypeFlags.EnumLiteral) !== 0;
187
203
  }
188
- function isExternalReference(node, program, externalNames) {
204
+ function isExternalReference(node, program, externalNames, tsInstance) {
205
+ const ts = tsInstance;
189
206
  const typeChecker = program.getTypeChecker();
190
207
  const nodeSym = typeChecker.getSymbolAtLocation(node);
191
208
  let nodeFrom = nodeSym?.getDeclarations()?.[0];
@@ -238,7 +255,7 @@ function isExternalReference(node, program, externalNames) {
238
255
  function isAsConstExpression(node) {
239
256
  return node.type.getText() === 'const';
240
257
  }
241
- function hasAsExpression(node, context, tsInstance) {
258
+ function hasAsExpression(node, tsInstance) {
242
259
  const ts = tsInstance;
243
260
  // including 'as const'
244
261
  if (ts.isAsExpression(node)) {
@@ -247,13 +264,13 @@ function hasAsExpression(node, context, tsInstance) {
247
264
  let found = false;
248
265
  ts.visitEachChild(node, (node) => {
249
266
  if (!found) {
250
- found = hasAsExpression(node, context, ts);
267
+ found = hasAsExpression(node, ts);
251
268
  }
252
269
  return node;
253
- }, context);
270
+ }, void 0);
254
271
  return found;
255
272
  }
256
- function hasParentAsExpression(node, context, tsInstance) {
273
+ function hasParentAsExpression(node, tsInstance) {
257
274
  const ts = tsInstance;
258
275
  if (node == null) {
259
276
  return false;
@@ -264,11 +281,11 @@ function hasParentAsExpression(node, context, tsInstance) {
264
281
  }
265
282
  if (ts.isPropertyAccessExpression(node) ||
266
283
  ts.isElementAccessExpression(node)) {
267
- if (hasAsExpression(node.expression, context, ts)) {
284
+ if (hasAsExpression(node.expression, ts)) {
268
285
  return true;
269
286
  }
270
287
  }
271
- return hasParentAsExpression(node.parent, context, ts);
288
+ return hasParentAsExpression(node.parent, ts);
272
289
  }
273
290
  function hasPureAnnotation(node, sourceFile, tsInstance) {
274
291
  const ts = tsInstance;
@@ -408,19 +425,19 @@ function isUndefinedIdentifier(node, parent, program, tsInstance) {
408
425
  return false;
409
426
  }
410
427
  if (type.isUnionOrIntersection() ||
411
- !(type.getFlags() & ts.TypeFlags.Undefined)) {
428
+ !(type.getFlags() & tsInstance.TypeFlags.Undefined)) {
412
429
  return false;
413
430
  }
414
431
  return true;
415
432
  }
416
433
  ////////////////////////////////////////////////////////////////////////////////
417
- export function printSource(sourceFile) {
418
- return printSourceImpl(sourceFile)[0];
434
+ export function printSource(sourceFile, tsInstance) {
435
+ return printSourceImpl(tsInstance, sourceFile)[0];
419
436
  }
420
- export function printSourceWithMap(sourceFile, originalSourceName, startOfSourceMap) {
437
+ export function printSourceWithMap(sourceFile, originalSourceName, startOfSourceMap, tsInstance) {
421
438
  const generator = new sourceMap.SourceMapGenerator(startOfSourceMap);
422
439
  generator.setSourceContent(originalSourceName, sourceFile.getFullText());
423
- return printSourceImpl(sourceFile, originalSourceName, generator);
440
+ return printSourceImpl(tsInstance, sourceFile, originalSourceName, generator);
424
441
  }
425
442
  function positionToLineAndColumn(sourceFile, pos, generatedDiff) {
426
443
  let line = 0;
@@ -434,23 +451,29 @@ function positionToLineAndColumn(sourceFile, pos, generatedDiff) {
434
451
  }
435
452
  return { line, column: pos - lastLinePos + generatedDiff };
436
453
  }
437
- function printSourceImpl(sourceFile, originalSourceName, mapGenerator) {
438
- const printer = ts.createPrinter({ removeComments: true });
439
- const r = printNode(printer, sourceFile.getFullText(), sourceFile, sourceFile, { pos: 0, diff: 0, lastLine: 0 }, originalSourceName, mapGenerator);
440
- return [r, mapGenerator?.toJSON()];
454
+ function printSourceImpl(tsInstance, sourceFile, originalSourceName, mapGenerator) {
455
+ const ts = tsInstance ?? tsNamespace;
456
+ const r = printNode(ts, sourceFile.getFullText(), sourceFile, sourceFile, { pos: 0, diff: 0, lastLine: 0 }, originalSourceName, mapGenerator);
457
+ // This forces to concatenate strings into flatten one to reduce object trees for ConsString
458
+ void (r | 0);
459
+ const json = mapGenerator?.toJSON();
460
+ if (json) {
461
+ void (json.mappings | 0);
462
+ }
463
+ return [r, json];
441
464
  }
442
- function printNode(printer, baseSource, sourceFile, node, posContext, originalSourceName, mapGenerator) {
443
- const originalNode = node[SYMBOL_ORIGINAL_NODE];
444
- if (originalNode) {
445
- let result = printer.printNode(ts.EmitHint.Unspecified, node, sourceFile);
446
- const comments = ts.getSyntheticTrailingComments(node);
465
+ function printNode(tsInstance, baseSource, sourceFile, node, posContext, originalSourceName, mapGenerator) {
466
+ const originalNodeData = node[SYMBOL_ORIGINAL_NODE_DATA];
467
+ if (originalNodeData) {
468
+ let result = originalNodeData[1];
469
+ const comments = tsInstance.getSyntheticTrailingComments(node);
447
470
  if (comments) {
448
471
  for (const comment of comments) {
449
472
  result += ` /*${comment.text}*/`;
450
473
  }
451
474
  }
452
- const old = originalNode.getText(sourceFile);
453
- const oldFull = baseSource.substring(originalNode.pos, originalNode.end);
475
+ const old = originalNodeData[0];
476
+ const oldFull = baseSource.substring(originalNodeData[2], originalNodeData[3]);
454
477
  const i = oldFull.lastIndexOf(old);
455
478
  const leadingUnchanged = i < 0 ? 0 : i;
456
479
  const newText = i < 0
@@ -477,7 +500,7 @@ function printNode(printer, baseSource, sourceFile, node, posContext, originalSo
477
500
  let output = '';
478
501
  let headPrinted = false;
479
502
  let lastChildPos = 0;
480
- ts.visitEachChild(node, (child) => {
503
+ tsInstance.visitEachChild(node, (child) => {
481
504
  if (!headPrinted) {
482
505
  headPrinted = true;
483
506
  if (child.pos > node.pos) {
@@ -493,7 +516,7 @@ function printNode(printer, baseSource, sourceFile, node, posContext, originalSo
493
516
  addMappingForCurrent();
494
517
  posContext.pos = child.pos;
495
518
  }
496
- output += printNode(printer, baseSource, sourceFile, child, posContext, originalSourceName, mapGenerator);
519
+ output += printNode(tsInstance, baseSource, sourceFile, child, posContext, originalSourceName, mapGenerator);
497
520
  lastChildPos = child.end;
498
521
  return child;
499
522
  }, void 0);
@@ -1,2 +1,2 @@
1
- declare const _default: "0.5.1";
1
+ declare const _default: "0.7.0";
2
2
  export default _default;
package/dist/version.mjs CHANGED
@@ -1 +1 @@
1
- export default '0.5.1';
1
+ export default '0.7.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-const-value-transformer",
3
- "version": "0.5.1",
3
+ "version": "0.7.0",
4
4
  "engines": {
5
5
  "node": ">=20.19.3"
6
6
  },