Skip to content

Commit eb3645f

Browse files
authored
Refactor node factory API, use node factory in parser (#35282)
* Refactor node factory API, use node factory in parser * Move UnparsedSource nodes to factory * Make most Node properties read-only * Make pos/end/parent and JSDoc 'comment' read-only * Update function/constructor-type factories * Remove treeStateObserver * Simplify Debug.deprecate * Remove unused factory methods, simplify lazy factory methods * Fix base factory used for source file updates * Update test baseline due to merge from master * Rename factory methods to be more consistent (#39058)
1 parent 0232d4a commit eb3645f

File tree

206 files changed

+23448
-17150
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

206 files changed

+23448
-17150
lines changed

.vscode/launch.template.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
},
4444
"sourceMaps": true,
4545
"smartStep": true,
46-
"preLaunchTask": "tests",
46+
"preLaunchTask": "gulp: tests",
4747
"console": "integratedTerminal",
4848
"outFiles": [
4949
"${workspaceRoot}/built/local/run.js"

.vscode/tasks.json

+27-16
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,42 @@
44
"version": "2.0.0",
55
"tasks": [
66
{
7-
"type": "shell",
8-
"identifier": "local",
7+
"type": "gulp",
98
"label": "gulp: local",
10-
"command": "gulp",
11-
"args": ["local"],
12-
"group": { "kind": "build", "isDefault": true },
13-
"problemMatcher": ["$gulp-tsc"]
9+
"task": "local",
10+
"group": {
11+
"kind": "build",
12+
"isDefault": true
13+
},
14+
"problemMatcher": [
15+
"$tsc"
16+
]
1417
},
1518
{
16-
"type": "shell",
17-
"identifier": "tsc",
19+
"type": "gulp",
1820
"label": "gulp: tsc",
19-
"command": "gulp",
20-
"args": ["tsc"],
21+
"task": "tsc",
2122
"group": "build",
22-
"problemMatcher": ["$gulp-tsc"]
23+
"problemMatcher": [
24+
"$tsc"
25+
]
2326
},
2427
{
25-
"type": "shell",
26-
"identifier": "tests",
28+
"type": "gulp",
2729
"label": "gulp: tests",
28-
"command": "gulp",
29-
"args": ["tests"],
30+
"task": "tests",
3031
"group": "build",
31-
"problemMatcher": ["$gulp-tsc"]
32+
"problemMatcher": [
33+
"$tsc"
34+
]
35+
},
36+
{
37+
"type": "gulp",
38+
"task": "services",
39+
"label": "gulp: services",
40+
"problemMatcher": [
41+
"$tsc"
42+
],
3243
}
3344
]
3445
}

src/compat/deprecations.ts

+1,324
Large diffs are not rendered by default.

src/compat/tsconfig.json

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"extends": "../tsconfig-base",
3+
"compilerOptions": {
4+
"outFile": "../../built/local/compat.js"
5+
},
6+
"references": [
7+
{ "path": "../compiler" }
8+
],
9+
"files": [
10+
"deprecations.ts"
11+
]
12+
}

src/compiler/binder.ts

+29-1,088
Large diffs are not rendered by default.

src/compiler/checker.ts

+506-399
Large diffs are not rendered by default.

src/compiler/core.ts

+14
Original file line numberDiff line numberDiff line change
@@ -1516,6 +1516,20 @@ namespace ts {
15161516
};
15171517
}
15181518

1519+
/** A version of `memoize` that supports a single primitive argument */
1520+
export function memoizeOne<A extends string | number | boolean | undefined, T>(callback: (arg: A) => T): (arg: A) => T {
1521+
const map = createMap<T>();
1522+
return (arg: A) => {
1523+
const key = `${typeof arg}:${arg}`;
1524+
let value = map.get(key);
1525+
if (value === undefined && !map.has(key)) {
1526+
value = callback(arg);
1527+
map.set(key, value);
1528+
}
1529+
return value!;
1530+
};
1531+
}
1532+
15191533
/**
15201534
* High-order function, composes functions. Note that functions are composed inside-out;
15211535
* for example, `compose(a, b)` is the equivalent of `x => b(a(x))`.

src/compiler/corePublic.ts

+1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ namespace ts {
7979
/** Array that is only intended to be pushed to, never read. */
8080
export interface Push<T> {
8181
push(...values: T[]): void;
82+
/* @internal*/ readonly length: number;
8283
}
8384

8485
/* @internal */

src/compiler/debug.ts

+114-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,72 @@
11
/* @internal */
22
namespace ts {
3+
export enum LogLevel {
4+
Off,
5+
Error,
6+
Warning,
7+
Info,
8+
Verbose
9+
}
10+
11+
export interface LoggingHost {
12+
log(level: LogLevel, s: string): void;
13+
}
14+
15+
export interface DeprecationOptions {
16+
message?: string;
17+
error?: boolean;
18+
since?: Version | string;
19+
warnAfter?: Version | string;
20+
errorAfter?: Version | string;
21+
typeScriptVersion?: Version | string;
22+
}
23+
324
export namespace Debug {
4-
let currentAssertionLevel = AssertionLevel.None;
25+
let typeScriptVersion: Version | undefined;
526

6-
// eslint-disable-next-line prefer-const
27+
/* eslint-disable prefer-const */
28+
let currentAssertionLevel = AssertionLevel.None;
29+
export let currentLogLevel = LogLevel.Warning;
730
export let isDebugging = false;
31+
export let loggingHost: LoggingHost | undefined;
32+
/* eslint-enable prefer-const */
833

934
type AssertionKeys = MatchingKeys<typeof Debug, AnyFunction>;
35+
export function getTypeScriptVersion() {
36+
return typeScriptVersion ?? (typeScriptVersion = new Version(version));
37+
}
38+
39+
export function shouldLog(level: LogLevel): boolean {
40+
return currentLogLevel <= level;
41+
}
42+
43+
function logMessage(level: LogLevel, s: string): void {
44+
if (loggingHost && shouldLog(level)) {
45+
loggingHost.log(level, s);
46+
}
47+
}
48+
49+
export function log(s: string): void {
50+
logMessage(LogLevel.Info, s);
51+
}
52+
53+
export namespace log {
54+
export function error(s: string): void {
55+
logMessage(LogLevel.Error, s);
56+
}
57+
58+
export function warn(s: string): void {
59+
logMessage(LogLevel.Warning, s);
60+
}
61+
62+
export function log(s: string): void {
63+
logMessage(LogLevel.Info, s);
64+
}
65+
66+
export function trace(s: string): void {
67+
logMessage(LogLevel.Verbose, s);
68+
}
69+
}
1070

1171
const assertionCache: Partial<Record<AssertionKeys, { level: AssertionLevel, assertion: AnyFunction }>> = {};
1272

@@ -385,7 +445,7 @@ namespace ts {
385445
if (nodeIsSynthesized(this)) return "";
386446
const parseNode = getParseTreeNode(this);
387447
const sourceFile = parseNode && getSourceFileOfNode(parseNode);
388-
return sourceFile ? getSourceTextOfNodeFromSourceFile(sourceFile, parseNode, includeTrivia) : "";
448+
return sourceFile ? getSourceTextOfNodeFromSourceFile(sourceFile, parseNode!, includeTrivia) : "";
389449
}
390450
}
391451
});
@@ -410,5 +470,56 @@ namespace ts {
410470
isDebugInfoEnabled = true;
411471
}
412472

473+
function formatDeprecationMessage(name: string, error: boolean | undefined, errorAfter: Version | undefined, since: Version | undefined, message: string | undefined) {
474+
let deprecationMessage = error ? "DeprecationError: " : "DeprecationWarning: ";
475+
deprecationMessage += `'${name}' `;
476+
deprecationMessage += since ? `has been deprecated since v${since}` : "is deprecated";
477+
deprecationMessage += error ? " and can no longer be used." : errorAfter ? ` and will no longer be usable after v${errorAfter}.` : ".";
478+
deprecationMessage += message ? ` ${formatStringFromArgs(message, [name], 0)}` : "";
479+
return deprecationMessage;
480+
}
481+
482+
function createErrorDeprecation(name: string, errorAfter: Version | undefined, since: Version | undefined, message: string | undefined) {
483+
const deprecationMessage = formatDeprecationMessage(name, /*error*/ true, errorAfter, since, message);
484+
return () => {
485+
throw new TypeError(deprecationMessage);
486+
};
487+
}
488+
489+
function createWarningDeprecation(name: string, errorAfter: Version | undefined, since: Version | undefined, message: string | undefined) {
490+
let hasWrittenDeprecation = false;
491+
return () => {
492+
if (!hasWrittenDeprecation) {
493+
log.warn(formatDeprecationMessage(name, /*error*/ false, errorAfter, since, message));
494+
hasWrittenDeprecation = true;
495+
}
496+
};
497+
}
498+
499+
function createDeprecation(name: string, options: DeprecationOptions & { error: true }): () => never;
500+
function createDeprecation(name: string, options?: DeprecationOptions): () => void;
501+
function createDeprecation(name: string, options: DeprecationOptions = {}) {
502+
const version = typeof options.typeScriptVersion === "string" ? new Version(options.typeScriptVersion) : options.typeScriptVersion ?? getTypeScriptVersion();
503+
const errorAfter = typeof options.errorAfter === "string" ? new Version(options.errorAfter) : options.errorAfter;
504+
const warnAfter = typeof options.warnAfter === "string" ? new Version(options.warnAfter) : options.warnAfter;
505+
const since = typeof options.since === "string" ? new Version(options.since) : options.since ?? warnAfter;
506+
const error = options.error || errorAfter && version.compareTo(errorAfter) <= 0;
507+
const warn = !warnAfter || version.compareTo(warnAfter) >= 0;
508+
return error ? createErrorDeprecation(name, errorAfter, since, options.message) :
509+
warn ? createWarningDeprecation(name, errorAfter, since, options.message) :
510+
noop;
511+
}
512+
513+
function wrapFunction<F extends (...args: any[]) => any>(deprecation: () => void, func: F): F {
514+
return function (this: unknown) {
515+
deprecation();
516+
return func.apply(this, arguments);
517+
} as F;
518+
}
519+
520+
export function deprecate<F extends (...args: any[]) => any>(func: F, options?: DeprecationOptions): F {
521+
const deprecation = createDeprecation(getFunctionName(func), options);
522+
return wrapFunction(deprecation, func);
523+
}
413524
}
414525
}

src/compiler/emitter.ts

+25-25
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ namespace ts {
2828
if (outFile(options)) {
2929
const prepends = host.getPrependNodes();
3030
if (sourceFiles.length || prepends.length) {
31-
const bundle = createBundle(sourceFiles, prepends);
31+
const bundle = factory.createBundle(sourceFiles, prepends);
3232
const result = action(getOutputPathsFor(bundle, host, forceDtsEmit), bundle);
3333
if (result) {
3434
return result;
@@ -356,7 +356,7 @@ namespace ts {
356356
return;
357357
}
358358
// Transform the source files
359-
const transform = transformNodes(resolver, host, compilerOptions, [sourceFileOrBundle], scriptTransformers, /*allowDtsFiles*/ false);
359+
const transform = transformNodes(resolver, host, factory, compilerOptions, [sourceFileOrBundle], scriptTransformers, /*allowDtsFiles*/ false);
360360

361361
const printerOptions: PrinterOptions = {
362362
removeComments: compilerOptions.removeComments,
@@ -404,13 +404,13 @@ namespace ts {
404404
const sourceFiles = isSourceFile(sourceFileOrBundle) ? [sourceFileOrBundle] : sourceFileOrBundle.sourceFiles;
405405
const filesForEmit = forceDtsEmit ? sourceFiles : filter(sourceFiles, isSourceFileNotJson);
406406
// Setup and perform the transformation to retrieve declarations from the input files
407-
const inputListOrBundle = outFile(compilerOptions) ? [createBundle(filesForEmit, !isSourceFile(sourceFileOrBundle) ? sourceFileOrBundle.prepends : undefined)] : filesForEmit;
407+
const inputListOrBundle = outFile(compilerOptions) ? [factory.createBundle(filesForEmit, !isSourceFile(sourceFileOrBundle) ? sourceFileOrBundle.prepends : undefined)] : filesForEmit;
408408
if (emitOnlyDtsFiles && !getEmitDeclarations(compilerOptions)) {
409409
// Checker wont collect the linked aliases since thats only done when declaration is enabled.
410410
// Do that here when emitting only dts files
411411
filesForEmit.forEach(collectLinkedAliases);
412412
}
413-
const declarationTransform = transformNodes(resolver, host, compilerOptions, inputListOrBundle, declarationTransformers, /*allowDtsFiles*/ false);
413+
const declarationTransform = transformNodes(resolver, host, factory, compilerOptions, inputListOrBundle, declarationTransformers, /*allowDtsFiles*/ false);
414414
if (length(declarationTransform.diagnostics)) {
415415
for (const diagnostic of declarationTransform.diagnostics!) {
416416
emitterDiagnostics.add(diagnostic);
@@ -680,30 +680,30 @@ namespace ts {
680680
}
681681

682682
function createSourceFilesFromBundleBuildInfo(bundle: BundleBuildInfo, buildInfoDirectory: string, host: EmitUsingBuildInfoHost): readonly SourceFile[] {
683-
const sourceFiles = bundle.sourceFiles.map(fileName => {
684-
const sourceFile = createNode(SyntaxKind.SourceFile, 0, 0) as SourceFile;
683+
const jsBundle = Debug.checkDefined(bundle.js);
684+
const prologueMap = jsBundle.sources?.prologues && arrayToMap(jsBundle.sources.prologues, prologueInfo => "" + prologueInfo.file);
685+
return bundle.sourceFiles.map((fileName, index) => {
686+
const prologueInfo = prologueMap?.get("" + index);
687+
const statements = prologueInfo?.directives.map(directive => {
688+
const literal = setTextRange(factory.createStringLiteral(directive.expression.text), directive.expression);
689+
const statement = setTextRange(factory.createExpressionStatement(literal), directive);
690+
setParent(literal, statement);
691+
return statement;
692+
});
693+
const eofToken = factory.createToken(SyntaxKind.EndOfFileToken);
694+
const sourceFile = factory.createSourceFile(statements ?? [], eofToken, NodeFlags.None);
685695
sourceFile.fileName = getRelativePathFromDirectory(
686696
host.getCurrentDirectory(),
687697
getNormalizedAbsolutePath(fileName, buildInfoDirectory),
688698
!host.useCaseSensitiveFileNames()
689699
);
690-
sourceFile.text = "";
691-
sourceFile.statements = createNodeArray();
700+
sourceFile.text = prologueInfo?.text ?? "";
701+
setTextRangePosWidth(sourceFile, 0, prologueInfo?.text.length ?? 0);
702+
setEachParent(sourceFile.statements, sourceFile);
703+
setTextRangePosWidth(eofToken, sourceFile.end, 0);
704+
setParent(eofToken, sourceFile);
692705
return sourceFile;
693706
});
694-
const jsBundle = Debug.checkDefined(bundle.js);
695-
forEach(jsBundle.sources && jsBundle.sources.prologues, prologueInfo => {
696-
const sourceFile = sourceFiles[prologueInfo.file];
697-
sourceFile.text = prologueInfo.text;
698-
sourceFile.end = prologueInfo.text.length;
699-
sourceFile.statements = createNodeArray(prologueInfo.directives.map(directive => {
700-
const statement = createNode(SyntaxKind.ExpressionStatement, directive.pos, directive.end) as PrologueDirective;
701-
statement.expression = createNode(SyntaxKind.StringLiteral, directive.expression.pos, directive.expression.end) as StringLiteral;
702-
statement.expression.text = directive.expression.text;
703-
return statement;
704-
}));
705-
});
706-
return sourceFiles;
707707
}
708708

709709
/*@internal*/
@@ -2292,7 +2292,7 @@ namespace ts {
22922292

22932293
function emitPropertyAccessExpression(node: PropertyAccessExpression) {
22942294
const expression = cast(emitExpression(node.expression), isExpression);
2295-
const token = node.questionDotToken || createNode(SyntaxKind.DotToken, node.expression.end, node.name.pos) as DotToken;
2295+
const token = node.questionDotToken || setTextRangePosEnd(factory.createToken(SyntaxKind.DotToken) as DotToken, node.expression.end, node.name.pos);
22962296
const linesBeforeDot = getLinesBetweenNodes(node, node.expression, token);
22972297
const linesAfterDot = getLinesBetweenNodes(node, token, node.name);
22982298

@@ -3559,15 +3559,15 @@ namespace ts {
35593559
}
35603560

35613561
function emitJSDocTypeLiteral(lit: JSDocTypeLiteral) {
3562-
emitList(lit, createNodeArray(lit.jsDocPropertyTags), ListFormat.JSDocComment);
3562+
emitList(lit, factory.createNodeArray(lit.jsDocPropertyTags), ListFormat.JSDocComment);
35633563
}
35643564

35653565
function emitJSDocSignature(sig: JSDocSignature) {
35663566
if (sig.typeParameters) {
3567-
emitList(sig, createNodeArray(sig.typeParameters), ListFormat.JSDocComment);
3567+
emitList(sig, factory.createNodeArray(sig.typeParameters), ListFormat.JSDocComment);
35683568
}
35693569
if (sig.parameters) {
3570-
emitList(sig, createNodeArray(sig.parameters), ListFormat.JSDocComment);
3570+
emitList(sig, factory.createNodeArray(sig.parameters), ListFormat.JSDocComment);
35713571
}
35723572
if (sig.type) {
35733573
writeLine();

0 commit comments

Comments
 (0)