Skip to content

Commit 79653e5

Browse files
committed
Add dts bundling
1 parent 41cda7d commit 79653e5

Some content is hidden

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

57 files changed

+739
-464
lines changed

Diff for: .eslintignore

+1
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,4 @@
2020
/scripts/eslint/built/**
2121
/internal/**
2222
/coverage/**
23+
/dtstesting/**

Diff for: .gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ scripts/configurePrerelease.js
4747
scripts/configureLanguageServiceBuild.js
4848
scripts/open-user-pr.js
4949
scripts/open-cherry-pick-pr.js
50+
scripts/dtsBundler.js
5051
scripts/processDiagnosticMessages.d.ts
5152
scripts/processDiagnosticMessages.js
5253
scripts/produceLKG.js

Diff for: Gulpfile.js

+30-4
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,33 @@ const buildSrc = () => buildProject("src");
127127
// But, if we are bundling, we are running only d.ts emit, so maybe this is fast?
128128
task("build-src", series(preSrc, buildSrc));
129129

130+
// TODO(jakebailey): split this in two, only build the relavent parts, move down before tests.
131+
const dtsBundler = async () => {
132+
/**
133+
* @param {string} projectPath
134+
* @param {string} entrypoint
135+
* @param {string} output
136+
*/
137+
async function runDtsBundler(projectPath, entrypoint, output) {
138+
await exec(process.execPath, ["./scripts/dtsBundler.js", projectPath, entrypoint, output]);
139+
}
140+
141+
// TODO(jakebailey): prepend copyright notice, replace const enums with regular enums
142+
if (needsUpdate(["./built/local/typescript/tsconfig.tsbuildinfo", "./scripts/dtsBundler.js"], "./built/local/typescript.d.ts")) {
143+
await runDtsBundler("./src/typescript/tsconfig.json", "./built/local/typescript/typescript.d.ts", "./built/local/typescript.d.ts");
144+
}
145+
146+
if (needsUpdate(["./built/local/tsserverlibrary/tsconfig.tsbuildinfo", "./scripts/dtsBundler.js"], "./built/local/tsserverlibrary.d.ts")) {
147+
await runDtsBundler("./src/tsserverlibrary/tsconfig.json", "./built/local/tsserverlibrary/tsserverlibrary.d.ts", "./built/local/tsserverlibrary.d.ts");
148+
}
149+
};
150+
151+
// TODO(jakebailey): Some tests depend on the extracted output.
152+
// TODO(jakebailey): implicit dependency on scripts
153+
const dts = series(preSrc, buildSrc, dtsBundler);
154+
dts.displayName = "dts";
155+
task("dts", dts);
156+
130157
/** @type {string | undefined} */
131158
let copyrightHeader;
132159
function getCopyrightHeader() {
@@ -474,7 +501,7 @@ const preTest = parallel(buildTsc, buildTests, buildServices, buildLssl);
474501
preTest.displayName = "preTest";
475502

476503
const runTests = () => runConsoleTests(testRunner, "mocha-fivemat-progress-reporter", /*runInParallel*/ false, /*watchMode*/ false);
477-
task("runtests", series(preBuild, preTest, runTests));
504+
task("runtests", series(preBuild, preTest, dts, runTests));
478505
task("runtests").description = "Runs the tests using the built run.js file.";
479506
task("runtests").flags = {
480507
"-t --tests=<regex>": "Pattern for tests to run.",
@@ -493,7 +520,7 @@ task("runtests").flags = {
493520
};
494521

495522
const runTestsParallel = () => runConsoleTests(testRunner, "min", /*runInParallel*/ cmdLineOptions.workers > 1, /*watchMode*/ false);
496-
task("runtests-parallel", series(preBuild, preTest, runTestsParallel));
523+
task("runtests-parallel", series(preBuild, preTest, dts, runTestsParallel));
497524
task("runtests-parallel").description = "Runs all the tests in parallel using the built run.js file.";
498525
task("runtests-parallel").flags = {
499526
" --light": "Run tests in light mode (fewer verifications, but tests run faster).",
@@ -597,8 +624,7 @@ const produceLKG = async () => {
597624
}
598625
};
599626

600-
// TODO(jakebailey): dependencies on dts
601-
task("LKG", series(lkgPreBuild, parallel(localize, buildTsc, buildServer, buildServices, buildLssl, buildOtherOutputs), produceLKG));
627+
task("LKG", series(lkgPreBuild, parallel(localize, buildTsc, buildServer, buildServices, buildLssl, buildOtherOutputs, dts), produceLKG));
602628
task("LKG").description = "Makes a new LKG out of the built js files";
603629
task("LKG").flags = {
604630
" --built": "Compile using the built version of the compiler.",

Diff for: scripts/dtsBundler.ts

+131
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import * as assert from "assert";
2+
import * as fs from "fs";
3+
import * as path from "path";
4+
import * as ts from "../lib/typescript";
5+
6+
// (ts as any).Debug.enableDebugInfo();
7+
8+
const [
9+
projectPath,
10+
entrypoint,
11+
output,
12+
] = process.argv.slice(2);
13+
14+
assert(projectPath);
15+
assert(entrypoint);
16+
assert(output);
17+
18+
console.log(`Bundling ${entrypoint} to ${output}, using project ${projectPath}`);
19+
20+
const newLineKind = ts.NewLineKind.LineFeed;
21+
const newLine = newLineKind === ts.NewLineKind.LineFeed ? "\n" : "\r\n";
22+
23+
function isDeclarationStatement(node: ts.Node): node is ts.DeclarationStatement {
24+
return (ts as any).isDeclarationStatement(node);
25+
}
26+
27+
const compareStringsCaseSensitive: (a: string | undefined, b: string | undefined) => number = (ts as any).compareStringsCaseSensitive;
28+
29+
30+
const tsconfigContents = ts.readJsonConfigFile(path.join(projectPath, "tsconfig.json"), ts.sys.readFile);
31+
const tsconfig = ts.parseJsonConfigFileContent(tsconfigContents, ts.sys, projectPath);
32+
const program = ts.createProgram([entrypoint], tsconfig.options);
33+
34+
const typeChecker = program.getTypeChecker();
35+
36+
const sourceFile = program.getSourceFile(entrypoint);
37+
assert(sourceFile);
38+
const moduleSymbol = typeChecker.getSymbolAtLocation(sourceFile);
39+
assert(moduleSymbol);
40+
41+
const printer = ts.createPrinter({ newLine: newLineKind });
42+
43+
44+
const lines: string[] = [];
45+
const indent = " ";
46+
let currentIndent = "";
47+
48+
function increaseIndent() {
49+
currentIndent += indent;
50+
}
51+
52+
function decreaseIndent() {
53+
currentIndent = currentIndent.slice(indent.length);
54+
}
55+
56+
function write(s = "") {
57+
if (!s) {
58+
lines.push("");
59+
}
60+
else {
61+
lines.push(...s.split(/\r?\n/).map((line) => (currentIndent + line).trimEnd()));
62+
}
63+
}
64+
65+
const copyrightNotice = fs.readFileSync(path.join(__dirname, "..", "CopyrightNotice.txt"), "utf-8");
66+
write(copyrightNotice);
67+
68+
function emitAsNamespace(name: string, moduleSymbol: ts.Symbol) {
69+
if (name === "ts") {
70+
// We will write `export = ts` at the end.
71+
write(`declare namespace ${name} {`);
72+
}
73+
else {
74+
write(`export namespace ${name} {`);
75+
}
76+
increaseIndent();
77+
78+
let moduleExports = typeChecker.getExportsOfModule(moduleSymbol);
79+
moduleExports = moduleExports.slice(0).sort((a, b) => compareStringsCaseSensitive(a.name, b.name));
80+
81+
for (const me of moduleExports) {
82+
if (!me.declarations?.length) {
83+
continue;
84+
}
85+
86+
if (me.flags & ts.SymbolFlags.Alias) {
87+
const resolved = typeChecker.getAliasedSymbol(me);
88+
assert(resolved.flags & ts.SymbolFlags.ValueModule);
89+
emitAsNamespace(me.name, resolved);
90+
continue;
91+
}
92+
93+
for (const decl of me.declarations) {
94+
let output: ts.Node | undefined;
95+
96+
if (ts.isVariableDeclaration(decl)) {
97+
output = ts.factory.createVariableStatement(
98+
[ts.factory.createToken(ts.SyntaxKind.ExportKeyword)],
99+
ts.factory.createVariableDeclarationList([decl], ts.getCombinedNodeFlags(decl))
100+
);
101+
}
102+
else if (isDeclarationStatement(decl)) {
103+
output = decl;
104+
}
105+
106+
if (output) {
107+
let printed = printer.printNode(ts.EmitHint.Unspecified, output, decl.getSourceFile());
108+
// TODO(jakebailey): This could be less hacky.
109+
printed = printed.replace("export declare ", "export ");
110+
printed = printed.replace("export const enum ", "export enum ");
111+
write(printed);
112+
write();
113+
}
114+
else {
115+
const declSourceFile = decl.getSourceFile();
116+
const lc = declSourceFile.getLineAndCharacterOfPosition(decl.pos);
117+
throw new Error(`Unhandled declaration for ${me.name} at ${declSourceFile.fileName}:${lc.line}:${lc.character}`);
118+
}
119+
}
120+
}
121+
122+
decreaseIndent();
123+
write(`}`);
124+
write();
125+
}
126+
127+
emitAsNamespace("ts", moduleSymbol);
128+
129+
write("export = ts;");
130+
131+
fs.writeFileSync(output, lines.join(newLine));

Diff for: src/compiler/commandLineParser.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2081,7 +2081,7 @@ function getTsconfigRootOptionsMap() {
20812081
}
20822082

20832083
/** @internal */
2084-
interface JsonConversionNotifier {
2084+
export interface JsonConversionNotifier {
20852085
/**
20862086
* Notifies parent option object is being set with the optionKey and a valid optionValue
20872087
* Currently it notifies only if there is element with type object (parentOption) and

Diff for: src/compiler/factory/utilities.ts

+6-5
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ import {
22
AccessorDeclaration, addEmitFlags, AdditiveOperator, AdditiveOperatorOrHigher, AssertionLevel,
33
AssignmentOperatorOrHigher, BinaryExpression, BinaryOperator, BinaryOperatorToken, BindingOrAssignmentElement,
44
BindingOrAssignmentElementRestIndicator, BindingOrAssignmentElementTarget, BindingOrAssignmentPattern,
5-
BitwiseOperator, BitwiseOperatorOrHigher, BooleanLiteral, CharacterCodes, CommaListExpression,
5+
BitwiseOperator, BitwiseOperatorOrHigher, Block, BooleanLiteral, CharacterCodes, CommaListExpression,
66
compareStringsCaseSensitive, CompilerOptions, Debug, Declaration, EmitFlags, EmitHelperFactory, EmitHost,
77
EmitResolver, EntityName, EqualityOperator, EqualityOperatorOrHigher, ExclamationToken, ExponentiationOperator,
88
ExportDeclaration, Expression, ExpressionStatement, externalHelpersModuleNameText, first, firstOrUndefined,
99
ForInitializer, GeneratedIdentifier, GeneratedIdentifierFlags, GeneratedNamePart, GeneratedPrivateIdentifier,
10+
GetAccessorDeclaration,
1011
getAllAccessorDeclarations, getEmitFlags, getEmitHelpers, getEmitModuleKind, getESModuleInterop,
1112
getExternalModuleName, getExternalModuleNameFromPath, getJSDocType, getJSDocTypeTag, getModifiers,
1213
getNamespaceDeclarationNode, getOrCreateEmitNode, getOriginalNode, getParseTreeNode,
@@ -26,7 +27,7 @@ import {
2627
NumericLiteral, ObjectLiteralElementLike, ObjectLiteralExpression, or, OuterExpression, OuterExpressionKinds,
2728
outFile, parseNodeFactory, PlusToken, PostfixUnaryExpression, PrefixUnaryExpression, PrivateIdentifier,
2829
PropertyAssignment, PropertyDeclaration, PropertyName, pushIfUnique, QuestionToken, ReadonlyKeyword,
29-
RelationalOperator, RelationalOperatorOrHigher, setOriginalNode, setParent, setStartsOnNewLine, setTextRange,
30+
RelationalOperator, RelationalOperatorOrHigher, SetAccessorDeclaration, setOriginalNode, setParent, setStartsOnNewLine, setTextRange,
3031
ShiftOperator, ShiftOperatorOrHigher, ShorthandPropertyAssignment, some, SourceFile, Statement, StringLiteral,
3132
SyntaxKind, TextRange, ThisTypeNode, Token, TypeNode, TypeParameterDeclaration,
3233
} from "../_namespaces/ts";
@@ -185,7 +186,7 @@ export function createForOfBindingStatement(factory: NodeFactory, node: ForIniti
185186
}
186187

187188
/** @internal */
188-
export function insertLeadingStatement(factory: NodeFactory, dest: Statement, source: Statement) {
189+
export function insertLeadingStatement(factory: NodeFactory, dest: Statement, source: Statement): Block {
189190
if (isBlock(dest)) {
190191
return factory.updateBlock(dest, setTextRange(factory.createNodeArray([source, ...dest.statements]), dest.statements));
191192
}
@@ -1456,7 +1457,7 @@ export function createAccessorPropertyBackingField(factory: NodeFactory, node: P
14561457
*
14571458
* @internal
14581459
*/
1459-
export function createAccessorPropertyGetRedirector(factory: NodeFactory, node: PropertyDeclaration, modifiers: ModifiersArray | undefined, name: PropertyName) {
1460+
export function createAccessorPropertyGetRedirector(factory: NodeFactory, node: PropertyDeclaration, modifiers: ModifiersArray | undefined, name: PropertyName): GetAccessorDeclaration {
14601461
return factory.createGetAccessorDeclaration(
14611462
modifiers,
14621463
name,
@@ -1478,7 +1479,7 @@ export function createAccessorPropertyGetRedirector(factory: NodeFactory, node:
14781479
*
14791480
* @internal
14801481
*/
1481-
export function createAccessorPropertySetRedirector(factory: NodeFactory, node: PropertyDeclaration, modifiers: ModifiersArray | undefined, name: PropertyName) {
1482+
export function createAccessorPropertySetRedirector(factory: NodeFactory, node: PropertyDeclaration, modifiers: ModifiersArray | undefined, name: PropertyName): SetAccessorDeclaration {
14821483
return factory.createSetAccessorDeclaration(
14831484
modifiers,
14841485
name,

Diff for: src/compiler/moduleNameResolver.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ function createResolvedModuleWithFailedLookupLocations(
128128
}
129129

130130
/** @internal */
131-
interface ModuleResolutionState {
131+
export interface ModuleResolutionState {
132132
host: ModuleResolutionHost;
133133
compilerOptions: CompilerOptions;
134134
traceEnabled: boolean;
@@ -146,7 +146,7 @@ interface ModuleResolutionState {
146146
*
147147
* @internal
148148
*/
149-
interface PackageJsonPathFields {
149+
export interface PackageJsonPathFields {
150150
typings?: string;
151151
types?: string;
152152
typesVersions?: MapLike<MapLike<string[]>>;
@@ -226,7 +226,7 @@ function readPackageJsonTypesVersionsField(jsonContent: PackageJson, state: Modu
226226
}
227227

228228
/** @internal */
229-
interface VersionPaths {
229+
export interface VersionPaths {
230230
version: string;
231231
paths: MapLike<string[]>;
232232
}
@@ -1281,7 +1281,7 @@ export function resolveJSModule(moduleName: string, initialDir: string, host: Mo
12811281
}
12821282

12831283
/** @internal */
1284-
enum NodeResolutionFeatures {
1284+
export enum NodeResolutionFeatures {
12851285
None = 0,
12861286
// resolving `#local` names in your own package.json
12871287
Imports = 1 << 1,

Diff for: src/compiler/parser.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -9831,7 +9831,7 @@ export function processCommentPragmas(context: PragmaContext, sourceText: string
98319831
}
98329832

98339833
/** @internal */
9834-
type PragmaDiagnosticReporter = (pos: number, length: number, message: DiagnosticMessage) => void;
9834+
export type PragmaDiagnosticReporter = (pos: number, length: number, message: DiagnosticMessage) => void;
98359835

98369836
/** @internal */
98379837
export function processPragmasIntoFields(context: PragmaContext, reportDiagnostic: PragmaDiagnosticReporter): void {

Diff for: src/compiler/perfLogger.ts

+33-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,38 @@
11
import { noop } from "./_namespaces/ts";
22

3-
type PerfLogger = typeof import("@microsoft/typescript-etw");
3+
/** @internal */
4+
export interface PerfLogger {
5+
logEvent(msg: string): void;
6+
logErrEvent(msg: string): void;
7+
logPerfEvent(msg: string): void;
8+
logInfoEvent(msg: string): void;
9+
logStartCommand(command: string, msg: string): void;
10+
logStopCommand(command: string, msg: string): void;
11+
logStartUpdateProgram(msg: string): void;
12+
logStopUpdateProgram(msg: string): void;
13+
logStartUpdateGraph(): void;
14+
logStopUpdateGraph(): void;
15+
logStartResolveModule(name: string): void;
16+
logStopResolveModule(success: string): void;
17+
logStartParseSourceFile(filename: string): void;
18+
logStopParseSourceFile(): void;
19+
logStartReadFile(filename: string): void;
20+
logStopReadFile(): void;
21+
logStartBindFile(filename: string): void;
22+
logStopBindFile(): void;
23+
logStartScheduledOperation(operationId: string): void;
24+
logStopScheduledOperation(): void;
25+
}
26+
27+
type ImportedPerfLogger = typeof import("@microsoft/typescript-etw");
28+
29+
// Assert that our PerfLogger type is compatible with the library.
30+
// Doing this keeps the above import expression out of our emitted d.ts file.
31+
const _perfLoggerCorrectType: PerfLogger extends ImportedPerfLogger ? true : false = true;
32+
33+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
34+
_perfLoggerCorrectType;
35+
436
const nullLogger: PerfLogger = {
537
logEvent: noop,
638
logErrEvent: noop,

Diff for: src/compiler/program.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ export function createCompilerHostWorker(options: CompilerOptions, setParentNode
210210
}
211211

212212
/** @internal */
213-
interface CompilerHostLikeForCache {
213+
export interface CompilerHostLikeForCache {
214214
fileExists(fileName: string): boolean;
215215
readFile(fileName: string, encoding?: string): string | undefined;
216216
directoryExists?(directory: string): boolean;
@@ -570,7 +570,7 @@ export interface SourceFileImportsList {
570570
* Calculates the resulting resolution mode for some reference in some file - this is generally the explicitly
571571
* provided resolution mode in the reference, unless one is not present, in which case it is the mode of the containing file.
572572
*/
573-
export function getModeForFileReference(ref: FileReference | string, containingFileMode: SourceFile["impliedNodeFormat"]) {
573+
export function getModeForFileReference(ref: FileReference | string, containingFileMode: SourceFile["impliedNodeFormat"]): ModuleKind.CommonJS | ModuleKind.ESNext | undefined {
574574
return (isString(ref) ? containingFileMode : ref.resolutionMode) || containingFileMode;
575575
}
576576

@@ -612,7 +612,7 @@ export function isExclusivelyTypeOnlyImportOrExport(decl: ImportDeclaration | Ex
612612
* @param usage The module reference string
613613
* @returns The final resolution mode of the import
614614
*/
615-
export function getModeForUsageLocation(file: {impliedNodeFormat?: SourceFile["impliedNodeFormat"]}, usage: StringLiteralLike) {
615+
export function getModeForUsageLocation(file: {impliedNodeFormat?: SourceFile["impliedNodeFormat"]}, usage: StringLiteralLike): ModuleKind.CommonJS | ModuleKind.ESNext | undefined {
616616
if (file.impliedNodeFormat === undefined) return undefined;
617617
if ((isImportDeclaration(usage.parent) || isExportDeclaration(usage.parent))) {
618618
const isTypeOnly = isExclusivelyTypeOnlyImportOrExport(usage.parent);
@@ -4318,7 +4318,7 @@ export function filterSemanticDiagnostics(diagnostic: readonly Diagnostic[], opt
43184318
}
43194319

43204320
/** @internal */
4321-
interface CompilerHostLike {
4321+
export interface CompilerHostLike {
43224322
useCaseSensitiveFileNames(): boolean;
43234323
getCurrentDirectory(): string;
43244324
fileExists(fileName: string): boolean;

0 commit comments

Comments
 (0)