-
Notifications
You must be signed in to change notification settings - Fork 12.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Temporary commit to show an external implementation of declaration emit.
- Loading branch information
1 parent
17e7807
commit ed568e2
Showing
74 changed files
with
16,957 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
/build | ||
/node_modules | ||
/src/*.tsbuildinfo | ||
/tests/actual | ||
/tsc-tests |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
{ | ||
"name": "external-declarations", | ||
"version": "1.0.0", | ||
"description": "", | ||
"main": "index.js", | ||
"scripts": { | ||
"build": "node ../built/local/tsc.js -p ./src", | ||
"watch": "node ../built/local/tsc.js -w -p ./src", | ||
"run-tests-parallel": "node ./build/test-runner/parallel-run.js --rootPaths=../tests/cases --libPath=../tests/lib --type=all --shardCount=8", | ||
"run-test": "node ./build/test-runner/test-runner-main.js --type=all --rootPaths=c:/dev/TSC/TypeScript/tests/cases ", | ||
"transform-tests-parallel": "node ./build/code-mod/parallel-run.js --rootPaths=../tests/cases --shardCount=8", | ||
"transform-test": "node ./build/code-mod/test-updater.js --rootPaths=../tests/cases", | ||
"run-transformed-tests-parallel": "node ./build/test-runner/parallel-run.js --rootPaths=./tsc-tests/updated-tests --libPath=../tests/lib --type=all --shardCount=8", | ||
"run-transformed-test": "node ./build/test-runner/test-runner-main.js --type=all --rootPaths=c:/dev/TSC/TypeScript/tests/cases " | ||
}, | ||
"author": "", | ||
"license": "ISC", | ||
"dependencies": { | ||
"source-map-support": "^0.5.21", | ||
"typescript": "../" | ||
}, | ||
"devDependencies": { | ||
"@types/node": "^18.11.18" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# Implementation of declaration emit | ||
|
||
This implementation uses the the parser and printer from the TypeScript code base but replaces the binder and emit resolver with rewritten versions that do not depend on the type checker. | ||
|
||
The declaration transform itself is mostly the same as the version in TypeScript (with some code erased and different imports) | ||
|
||
## Package scripts | ||
|
||
- `build`/ `watch` - Build the code | ||
- `run-tests-parallel` - Emits declarations using tsc and the stand alone emitter for the tests in the TypeScript code base in parallel. Outputs to `tsc-tests` | ||
- `run-test` - Emits declarations using tsc and the stand alone emitter for the tests in the TypeScript code base on a single thread, or filtered if you specify a test name. Outputs to `tsc-tests` | ||
- `transform-tests-parallel` - Transforms the TypeScript tests t add missing type annotations and write them to `tsc-tests\updated-tests`. Runs in parallel | ||
- `transform-test` - Transforms the TypeScript tests t add missing type annotations and write them to `tsc-tests\updated-tests`. Runs on a single thread. Accepts a test name or regex filter | ||
- `run-transformed-tests-parallel` - Same as `run-tests-parallel` but runs on the transformed tests in `tsc-tests\updated-tests` | ||
- `run-transformed-test`- Same as `run-test` but runs on the transformed tests in `tsc-tests\updated-tests` | ||
|
||
|
||
Note: Tests currently just output the declarations, there is no console error message. Use an external diff tool to see differences. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,276 @@ | ||
import * as ts from "typescript"; | ||
import { NodeBuilderFlags } from "typescript"; | ||
import { map } from "../compiler/lang-utils"; | ||
import { SymbolTracker } from "../compiler/types"; | ||
|
||
const declarationEmitNodeBuilderFlags = | ||
NodeBuilderFlags.MultilineObjectLiterals | | ||
NodeBuilderFlags.WriteClassExpressionAsTypeLiteral | | ||
NodeBuilderFlags.UseTypeOfFunction | | ||
NodeBuilderFlags.UseStructuralFallback | | ||
NodeBuilderFlags.AllowEmptyTuple | | ||
NodeBuilderFlags.GenerateNamesForShadowedTypeParams | | ||
NodeBuilderFlags.NoTruncation; | ||
|
||
|
||
// Define a transformer function | ||
export function addTypeAnnotationTransformer(program: ts.Program, moduleResolutionHost?: ts.ModuleResolutionHost) { | ||
function tryGetReturnType( | ||
typeChecker: ts.TypeChecker, | ||
node: ts.SignatureDeclaration | ||
): ts.Type | undefined { | ||
const signature = typeChecker.getSignatureFromDeclaration(node); | ||
if (signature) { | ||
return typeChecker.getReturnTypeOfSignature(signature); | ||
} | ||
} | ||
|
||
function isVarConst(node: ts.VariableDeclaration | ts.VariableDeclarationList): boolean { | ||
return !!(ts.getCombinedNodeFlags(node) & ts.NodeFlags.Const); | ||
} | ||
|
||
function isDeclarationReadonly(declaration: ts.Declaration): boolean { | ||
return !!(ts.getCombinedModifierFlags(declaration) & ts.ModifierFlags.Readonly && !ts.isParameterPropertyDeclaration(declaration, declaration.parent)); | ||
} | ||
|
||
function isLiteralConstDeclaration(node: ts.VariableDeclaration | ts.PropertyDeclaration | ts.PropertySignature | ts.ParameterDeclaration): boolean { | ||
if (isDeclarationReadonly(node) || ts.isVariableDeclaration(node) && isVarConst(node)) { | ||
// TODO: Make sure this is a valid approximation for literal types | ||
return !node.type && 'initializer' in node && !!node.initializer && ts.isLiteralExpression(node.initializer); | ||
// Original TS version | ||
// return isFreshLiteralType(getTypeOfSymbol(getSymbolOfNode(node))); | ||
} | ||
return false; | ||
} | ||
|
||
const typeChecker = program.getTypeChecker(); | ||
|
||
return (context: ts.TransformationContext) => { | ||
let hasError = false; | ||
let reportError = () => { | ||
hasError = true; | ||
} | ||
const symbolTracker: SymbolTracker | undefined = !moduleResolutionHost? undefined : { | ||
trackSymbol(){ return false; }, | ||
reportInaccessibleThisError: reportError, | ||
reportInaccessibleUniqueSymbolError: reportError, | ||
reportCyclicStructureError: reportError, | ||
reportPrivateInBaseOfClassExpression: reportError, | ||
reportLikelyUnsafeImportRequiredError: reportError, | ||
reportTruncationError: reportError, | ||
moduleResolverHost: moduleResolutionHost as any, | ||
trackReferencedAmbientModule(){}, | ||
trackExternalModuleSymbolOfImportTypeNode(){}, | ||
reportNonlocalAugmentation(){}, | ||
reportNonSerializableProperty(){}, | ||
reportImportTypeNodeResolutionModeOverride() {}, | ||
}; | ||
|
||
function typeToTypeNode(type: ts.Type, enclosingDeclaration: ts.Node) { | ||
const typeNode = typeChecker.typeToTypeNode( | ||
type, | ||
enclosingDeclaration, | ||
declarationEmitNodeBuilderFlags, | ||
// @ts-expect-error Use undocumented parameters | ||
symbolTracker, | ||
) | ||
if (hasError) { | ||
hasError = false; | ||
return undefined; | ||
} | ||
|
||
return typeNode; | ||
} | ||
// Return a visitor function | ||
return (rootNode: ts.Node) => { | ||
function updateTypesInNodeArray<T extends ts.Node>(nodeArray: ts.NodeArray<T>): ts.NodeArray<T> | ||
function updateTypesInNodeArray<T extends ts.Node>(nodeArray: ts.NodeArray<T> | undefined): ts.NodeArray<T> | undefined | ||
function updateTypesInNodeArray<T extends ts.Node>(nodeArray: ts.NodeArray<T> | undefined) { | ||
if(nodeArray === undefined) return undefined; | ||
return ts.factory.createNodeArray( | ||
nodeArray.map(param => { | ||
return visit(param) as ts.ParameterDeclaration; | ||
}) | ||
) | ||
} | ||
|
||
// Define a visitor function | ||
function visit(node: ts.Node): ts.Node | ts.Node[] { | ||
if(ts.isParameter(node) && !node.type) { | ||
const type = typeChecker.getTypeAtLocation(node); | ||
if(type) { | ||
const typeNode = typeToTypeNode(type, node); | ||
return ts.factory.updateParameterDeclaration( | ||
node, | ||
node.modifiers, | ||
node.dotDotDotToken, | ||
node.name, | ||
node.questionToken, | ||
typeNode, | ||
node.initializer | ||
) | ||
} | ||
} | ||
// Check if node is a variable declaration | ||
if (ts.isVariableDeclaration(node) && !node.type && !isLiteralConstDeclaration(node)) { | ||
const type = typeChecker.getTypeAtLocation(node); | ||
const typeNode = typeToTypeNode(type, node) | ||
return ts.factory.updateVariableDeclaration( | ||
node, | ||
node.name, | ||
undefined, | ||
typeNode, | ||
node.initializer | ||
); | ||
} | ||
|
||
if (ts.isFunctionDeclaration(node) && !node.type) { | ||
const type = tryGetReturnType(typeChecker, node); | ||
if(type) { | ||
|
||
const typeNode = typeToTypeNode(type, node); | ||
return ts.factory.updateFunctionDeclaration( | ||
node, | ||
node.modifiers, | ||
node.asteriskToken, | ||
node.name, | ||
updateTypesInNodeArray(node.typeParameters), | ||
updateTypesInNodeArray(node.parameters), | ||
typeNode, | ||
node.body | ||
) | ||
} | ||
} | ||
if(ts.isPropertySignature(node) && !node.type && !isLiteralConstDeclaration(node)) { | ||
const type = typeChecker.getTypeAtLocation(node); | ||
const typeNode = typeToTypeNode(type, node); | ||
return ts.factory.updatePropertySignature( | ||
node, | ||
node.modifiers, | ||
node.name, | ||
node.questionToken, | ||
typeNode, | ||
); | ||
} | ||
if(ts.isPropertyDeclaration(node) && !node.type && !isLiteralConstDeclaration(node)) { | ||
const type = typeChecker.getTypeAtLocation(node); | ||
const typeNode = typeToTypeNode(type, node); | ||
return ts.factory.updatePropertyDeclaration( | ||
node, | ||
node.modifiers, | ||
node.name, | ||
node.questionToken ?? node.exclamationToken, | ||
typeNode, | ||
node.initializer | ||
); | ||
} | ||
if(ts.isMethodSignature(node) && !node.type) { | ||
const type = tryGetReturnType(typeChecker, node); | ||
if(type) { | ||
|
||
const typeNode = typeToTypeNode(type, node); | ||
return ts.factory.updateMethodSignature( | ||
node, | ||
node.modifiers, | ||
node.name, | ||
node.questionToken, | ||
updateTypesInNodeArray(node.typeParameters), | ||
updateTypesInNodeArray(node.parameters), | ||
typeNode, | ||
); | ||
} | ||
} | ||
if(ts.isCallSignatureDeclaration(node)) { | ||
const type = tryGetReturnType(typeChecker, node); | ||
if(type) { | ||
const typeNode = typeToTypeNode(type, node); | ||
return ts.factory.updateCallSignature( | ||
node, | ||
updateTypesInNodeArray(node.typeParameters), | ||
updateTypesInNodeArray(node.parameters), | ||
typeNode, | ||
) | ||
} | ||
} | ||
if(ts.isMethodDeclaration(node) && !node.type) { | ||
const type = tryGetReturnType(typeChecker, node); | ||
if(type) { | ||
|
||
const typeNode = typeToTypeNode(type, node); | ||
return ts.factory.updateMethodDeclaration( | ||
node, | ||
node.modifiers, | ||
node.asteriskToken, | ||
node.name, | ||
node.questionToken, | ||
updateTypesInNodeArray(node.typeParameters), | ||
updateTypesInNodeArray(node.parameters), | ||
typeNode, | ||
node.body, | ||
); | ||
} | ||
} | ||
if(ts.isGetAccessorDeclaration(node) && !node.type) { | ||
const type = tryGetReturnType(typeChecker, node); | ||
if(type) { | ||
const typeNode = typeToTypeNode(type, node); | ||
return ts.factory.updateGetAccessorDeclaration( | ||
node, | ||
node.modifiers, | ||
node.name, | ||
updateTypesInNodeArray(node.parameters), | ||
typeNode, | ||
node.body, | ||
); | ||
} | ||
} | ||
if(ts.isSetAccessorDeclaration(node) && !node.parameters[0]?.type) { | ||
return ts.factory.updateSetAccessorDeclaration( | ||
node, | ||
node.modifiers, | ||
node.name, | ||
updateTypesInNodeArray(node.parameters), | ||
node.body, | ||
); | ||
} | ||
if ( ts.isConstructorDeclaration(node)) { | ||
return ts.factory.updateConstructorDeclaration( | ||
node, | ||
node.modifiers, | ||
updateTypesInNodeArray(node.parameters), | ||
node.body, | ||
) | ||
} | ||
if(ts.isConstructSignatureDeclaration(node)) { | ||
const type = tryGetReturnType(typeChecker, node); | ||
if(type) { | ||
const typeNode = typeToTypeNode(type, node); | ||
return ts.factory.updateConstructSignature( | ||
node, | ||
updateTypesInNodeArray(node.typeParameters), | ||
updateTypesInNodeArray(node.parameters), | ||
typeNode, | ||
) | ||
} | ||
} | ||
if(ts.isExportAssignment(node) && node.expression.kind !== ts.SyntaxKind.Identifier) { | ||
const type = typeChecker.getTypeAtLocation(node.expression); | ||
if(type) { | ||
const typeNode = typeToTypeNode(type, node); | ||
const newId = ts.factory.createIdentifier("_default"); | ||
const varDecl = ts.factory.createVariableDeclaration(newId, /*exclamationToken*/ undefined, typeNode, /*initializer*/ undefined); | ||
const statement = ts.factory.createVariableStatement( | ||
[], | ||
ts.factory.createVariableDeclarationList([varDecl], ts.NodeFlags.Const) | ||
); | ||
return [statement, ts.factory.updateExportAssignment(node, node.modifiers, newId)]; | ||
} | ||
} | ||
// Otherwise, visit each child node recursively | ||
return ts.visitEachChild(node, visit, context); | ||
} | ||
// Start visiting from root node | ||
return ts.visitNode(rootNode, visit)!; | ||
}; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
// Import TypeScript module | ||
import * as ts from "typescript"; | ||
import { isDeclarationFileName } from "../compiler/utils"; | ||
import { addTypeAnnotationTransformer } from "./code-transform"; | ||
|
||
(ts as any).Debug .enableDebugInfo(); | ||
|
||
// Read tsconfig.json file from disk | ||
const tsconfig = ts.readConfigFile("tsconfig.json", ts.sys.readFile); | ||
|
||
// Parse JSON content to get compiler options and file names | ||
const parsed = ts.parseJsonConfigFileContent(tsconfig.config, ts.sys, "./"); | ||
const options = parsed.options; | ||
// Pass compiler options and file names to createProgram | ||
const program = ts.createProgram(parsed.fileNames, options); | ||
|
||
program.getSemanticDiagnostics(); | ||
const files = program.getSourceFiles(); | ||
for (const file of files) { | ||
if (isDeclarationFileName(file.fileName)) continue; | ||
|
||
const transformedFile = ts.transform(file, [ | ||
addTypeAnnotationTransformer(program), | ||
]); | ||
|
||
const printer = ts.createPrinter({ | ||
onlyPrintJsDocStyle: true, | ||
newLine: options.newLine, | ||
target: options.target, | ||
} as ts.PrinterOptions); | ||
const resultStr = printer.printFile( | ||
transformedFile.transformed[0] as ts.SourceFile | ||
); | ||
console.log(resultStr); | ||
} |
Oops, something went wrong.