Skip to content

Export API to provide lib.d.ts listing/mapping #54011

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ tests/baselines/reference/testresults.tap
tests/services/baselines/prototyping/local/*
tests/services/browser/typescriptServices.js
src/harness/*.js
src/compiler/libs.generated.ts
src/compiler/diagnosticInformationMap.generated.ts
src/compiler/diagnosticMessages.generated.json
src/parser/diagnosticInformationMap.generated.ts
Expand Down
80 changes: 58 additions & 22 deletions Herebyfile.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -58,20 +58,21 @@ export const buildScripts = task({
run: () => buildProject("scripts"),
});

const libsJson = "src/lib/libs.json";
const libsGenerated = "src/compiler/libs.generated.ts";

const libs = memoize(() => {
/** @type {{ libs: string[]; paths: Record<string, string | undefined>; }} */
const libraries = readJson("./src/lib/libs.json");
const libraries = readJson(libsJson);
const libs = libraries.libs.map(lib => {
const relativeSources = ["header.d.ts", lib + ".d.ts"];
const relativeTarget = libraries.paths && libraries.paths[lib] || ("lib." + lib + ".d.ts");
const target = libraries.paths && libraries.paths[lib] || ("lib." + lib + ".d.ts");
const sources = relativeSources.map(s => path.posix.join("src/lib", s));
const target = `built/local/${relativeTarget}`;
return { target, sources };
});
return libs;
});

export const generateLibs = task({
export const lib = task({
name: "lib",
description: "Builds the library targets",
run: async () => {
Expand All @@ -84,11 +85,41 @@ export const generateLibs = task({
output += "\n" + contents.replace(/\r\n/g, "\n");
}

await fs.promises.writeFile(lib.target, output);
await fs.promises.writeFile(path.join("./built/local", lib.target), output);
}
},
});

export const generateLibs = task({
name: "generate-lib",
description: "Builds the library targets",
run: async () => {
const libNames = libs().map(lib => lib.target).sort();
const result = [
"// <auto-generated />",
`// generated from '${libsJson}'`,
"",
"/** @internal */",
"export const allLibFiles: readonly string[] = [",
];

for (const name of libNames) {
result.push(` ${JSON.stringify(name)},`);
}

result.push("];");

await fs.promises.writeFile(libsGenerated, result.join("\r\n"));
},
});

const cleanLib = task({
name: "clean-lib",
description: "Cleans generated lib files in src.",
hiddenFromTaskList: true,
run: () => rimraf(libsGenerated),
});

const diagnosticInformationMapTs = "src/compiler/diagnosticInformationMap.generated.ts";
const diagnosticMessagesJson = "src/compiler/diagnosticMessages.json";
const diagnosticMessagesGeneratedJson = "src/compiler/diagnosticMessages.generated.json";
Expand All @@ -103,13 +134,18 @@ export const generateDiagnostics = task({

const cleanDiagnostics = task({
name: "clean-diagnostics",
description: "Generates a diagnostic file in TypeScript based on an input JSON file",
description: "Cleans generated diagnostic files in src.",
hiddenFromTaskList: true,
run: async () => {
await rimraf(diagnosticInformationMapTs);
await rimraf(diagnosticMessagesGeneratedJson);
},
});
export const generate = task({
name: "generate",
description: "Generate code for src.",
dependencies: [generateLibs, generateDiagnostics],
});

// Localize diagnostics
/**
Expand Down Expand Up @@ -142,15 +178,15 @@ const localize = task({
export const buildSrc = task({
name: "build-src",
description: "Builds the src project (all code)",
dependencies: [generateDiagnostics],
dependencies: [generate],
run: () => buildProject("src"),
});

export const watchSrc = task({
name: "watch-src",
description: "Watches the src project (all code)",
hiddenFromTaskList: true,
dependencies: [generateDiagnostics],
dependencies: [generate],
run: () => watchProject("src"),
});

Expand Down Expand Up @@ -362,24 +398,24 @@ function entrypointBuildTask(options) {
const { main: tsc, watch: watchTsc } = entrypointBuildTask({
name: "tsc",
description: "Builds the command-line compiler",
buildDeps: [generateDiagnostics],
buildDeps: [generate],
project: "src/tsc",
srcEntrypoint: "./src/tsc/tsc.ts",
builtEntrypoint: "./built/local/tsc/tsc.js",
output: "./built/local/tsc.js",
mainDeps: [generateLibs],
mainDeps: [lib],
});
export { tsc, watchTsc };

const { main: services, build: buildServices, watch: watchServices } = entrypointBuildTask({
name: "services",
description: "Builds the typescript.js library",
buildDeps: [generateDiagnostics],
buildDeps: [generate],
project: "src/typescript",
srcEntrypoint: "./src/typescript/typescript.ts",
builtEntrypoint: "./built/local/typescript/typescript.js",
output: "./built/local/typescript.js",
mainDeps: [generateLibs],
mainDeps: [lib],
bundlerOptions: { exportIsTsObject: true },
});
export { services, watchServices };
Expand All @@ -398,12 +434,12 @@ export const dtsServices = task({
const { main: tsserver, watch: watchTsserver } = entrypointBuildTask({
name: "tsserver",
description: "Builds the language server",
buildDeps: [generateDiagnostics],
buildDeps: [generate],
project: "src/tsserver",
srcEntrypoint: "./src/tsserver/server.ts",
builtEntrypoint: "./built/local/tsserver/server.js",
output: "./built/local/tsserver.js",
mainDeps: [generateLibs],
mainDeps: [lib],
});
export { tsserver, watchTsserver };

Expand Down Expand Up @@ -477,12 +513,12 @@ const watchTestsEmitter = new EventEmitter();
const { main: tests, watch: watchTests } = entrypointBuildTask({
name: "tests",
description: "Builds the test infrastructure",
buildDeps: [generateDiagnostics],
buildDeps: [generate],
project: "src/testRunner",
srcEntrypoint: "./src/testRunner/_namespaces/Harness.ts",
builtEntrypoint: "./built/local/testRunner/runner.js",
output: testRunner,
mainDeps: [generateLibs],
mainDeps: [lib],
bundlerOptions: {
// Ensure we never drop any dead code, which might be helpful while debugging.
treeShaking: false,
Expand Down Expand Up @@ -547,7 +583,7 @@ const { main: cancellationToken, watch: watchCancellationToken } = entrypointBui

const { main: typingsInstaller, watch: watchTypingsInstaller } = entrypointBuildTask({
name: "typings-installer",
buildDeps: [generateDiagnostics],
buildDeps: [generate],
project: "src/typingsInstaller",
srcEntrypoint: "./src/typingsInstaller/nodeTypingsInstaller.ts",
builtEntrypoint: "./built/local/typingsInstaller/nodeTypingsInstaller.js",
Expand Down Expand Up @@ -580,7 +616,7 @@ export const generateTypesMap = task({
const builtLocalDiagnosticMessagesGeneratedJson = "built/local/diagnosticMessages.generated.json";
const copyBuiltLocalDiagnosticMessages = task({
name: "copy-built-local-diagnostic-messages",
dependencies: [generateDiagnostics],
dependencies: [generate],
run: async () => {
const contents = await fs.promises.readFile(diagnosticMessagesGeneratedJson, "utf-8");
JSON.parse(contents); // Validates that the JSON parses.
Expand Down Expand Up @@ -615,7 +651,7 @@ export const watchLocal = task({
dependencies: [localize, watchTsc, watchTsserver, watchServices, lssl, watchOtherOutputs, dts, watchSrc],
});

const runtestsDeps = [tests, generateLibs].concat(cmdLineOptions.typecheck ? [dts] : []);
const runtestsDeps = [tests, lib].concat(cmdLineOptions.typecheck ? [dts] : []);

export const runTests = task({
name: "runtests",
Expand Down Expand Up @@ -855,7 +891,7 @@ export const produceLKG = task({
"built/local/typescript.d.ts",
"built/local/typingsInstaller.js",
"built/local/watchGuard.js",
].concat(libs().map(lib => lib.target));
].concat(libs().map(lib => path.join("built/local", lib.target)));
const missingFiles = expectedFiles
.concat(localizationTargets)
.filter(f => !fs.existsSync(f));
Expand All @@ -882,7 +918,7 @@ export const cleanBuilt = task({
export const clean = task({
name: "clean",
description: "Cleans build outputs",
dependencies: [cleanBuilt, cleanDiagnostics],
dependencies: [cleanBuilt, cleanDiagnostics, cleanLib],
});

export const configureNightly = task({
Expand Down
1 change: 1 addition & 0 deletions src/compiler/_namespaces/ts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export * from "../types";
export * from "../sys";
export * from "../path";
export * from "../diagnosticInformationMap.generated";
export * from "../libs.generated";
export * from "../scanner";
export * from "../utilitiesPublic";
export * from "../utilities";
Expand Down
14 changes: 14 additions & 0 deletions src/compiler/commandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,13 @@ const libEntries: [string, string][] = [
*/
export const libs = libEntries.map(entry => entry[0]);

/**
* Gets the list of valid "lib" names, as used in compiler options or reference directives.
*/
export function getLibs(): readonly string[] {
return libs.slice();
}

/**
* A map of lib names to lib files. This map is used both for parsing the "lib" command line
* option as well as for resolving lib reference directives.
Expand All @@ -244,6 +251,13 @@ export const libs = libEntries.map(entry => entry[0]);
*/
export const libMap = new Map(libEntries);

/**
* Maps a lib name to its filename in the TypeScript package.
*/
export function getLibFileName(libName: string): string | undefined {
return libMap.get(libName);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this lowercase libName eg. ES5 should also get lib.es5.d.ts.. Eg. look at getLibFileNameFromLibReference

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think my intent here was that you only ever give this function strings you get from the other APIs, though I just haven't had time to deal with this PR (besides updating it post-dprint) so I can't remember anymore 😅

}

// Watch related options
/** @internal */
export const optionsForWatch: CommandLineOption[] = [
Expand Down
10 changes: 10 additions & 0 deletions src/compiler/utilitiesPublic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
__String,
AccessExpression,
AccessorDeclaration,
allLibFiles,
ArrayBindingElement,
ArrayBindingOrAssignmentElement,
ArrayBindingOrAssignmentPattern,
Expand Down Expand Up @@ -292,6 +293,15 @@ export function sortAndDeduplicateDiagnostics<T extends Diagnostic>(diagnostics:
return sortAndDeduplicate<T>(diagnostics, compareDiagnostics);
}

/**
* Gets a full list of all lib files included with this build of TypeScript.
* This list includes all files referenced by {@link getLibs} and {@link getLibFileName},
* as well as the "default" lib file names returned by {@link getDefaultLibFileName}.
*/
export function getAllLibFileNames(): readonly string[] {
return allLibFiles.slice();
}

export function getDefaultLibFileName(options: CompilerOptions): string {
switch (getEmitScriptTarget(options)) {
case ScriptTarget.ESNext:
Expand Down
14 changes: 14 additions & 0 deletions tests/baselines/reference/api/typescript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8772,6 +8772,12 @@ declare namespace ts {
}
function isExternalModuleNameRelative(moduleName: string): boolean;
function sortAndDeduplicateDiagnostics<T extends Diagnostic>(diagnostics: readonly T[]): SortedReadonlyArray<T>;
/**
* Gets a full list of all lib files included with this build of TypeScript.
* This list includes all files referenced by {@link getLibs} and {@link getLibFileName},
* as well as the "default" lib file names returned by {@link getDefaultLibFileName}.
*/
function getAllLibFileNames(): readonly string[];
function getDefaultLibFileName(options: CompilerOptions): string;
function textSpanEnd(span: TextSpan): number;
function textSpanIsEmpty(span: TextSpan): boolean;
Expand Down Expand Up @@ -9433,6 +9439,14 @@ declare namespace ts {
*/
setExternalModuleIndicator?: (file: SourceFile) => void;
}
/**
* Gets the list of valid "lib" names, as used in compiler options or reference directives.
*/
function getLibs(): readonly string[];
/**
* Maps a lib name to its filename in the TypeScript package.
*/
function getLibFileName(libName: string): string | undefined;
function parseCommandLine(commandLine: readonly string[], readFile?: (path: string) => string | undefined): ParsedCommandLine;
/**
* Reads the config file, reports errors if any and exits if the config file cannot be found
Expand Down