diff --git a/.gitignore b/.gitignore index b084ac14ecc28..83116a23a1d58 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/Herebyfile.mjs b/Herebyfile.mjs index b9b59d408c924..c9ed0a23cb6c7 100644 --- a/Herebyfile.mjs +++ b/Herebyfile.mjs @@ -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; }} */ - 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 () => { @@ -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 = [ + "// ", + `// 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"; @@ -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 /** @@ -142,7 +178,7 @@ const localize = task({ export const buildSrc = task({ name: "build-src", description: "Builds the src project (all code)", - dependencies: [generateDiagnostics], + dependencies: [generate], run: () => buildProject("src"), }); @@ -150,7 +186,7 @@ export const watchSrc = task({ name: "watch-src", description: "Watches the src project (all code)", hiddenFromTaskList: true, - dependencies: [generateDiagnostics], + dependencies: [generate], run: () => watchProject("src"), }); @@ -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 }; @@ -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 }; @@ -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, @@ -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", @@ -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. @@ -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", @@ -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)); @@ -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({ diff --git a/src/compiler/_namespaces/ts.ts b/src/compiler/_namespaces/ts.ts index 34d731e880695..b36f3dff26921 100644 --- a/src/compiler/_namespaces/ts.ts +++ b/src/compiler/_namespaces/ts.ts @@ -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"; diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 4d754ed33e076..47bef58e0a0a0 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -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. @@ -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); +} + // Watch related options /** @internal */ export const optionsForWatch: CommandLineOption[] = [ diff --git a/src/compiler/utilitiesPublic.ts b/src/compiler/utilitiesPublic.ts index 56ee00f89a04c..f49bc5310ee8f 100644 --- a/src/compiler/utilitiesPublic.ts +++ b/src/compiler/utilitiesPublic.ts @@ -2,6 +2,7 @@ import { __String, AccessExpression, AccessorDeclaration, + allLibFiles, ArrayBindingElement, ArrayBindingOrAssignmentElement, ArrayBindingOrAssignmentPattern, @@ -292,6 +293,15 @@ export function sortAndDeduplicateDiagnostics(diagnostics: return sortAndDeduplicate(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: diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index dcea4be64289a..d6a4955e8bd20 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -8772,6 +8772,12 @@ declare namespace ts { } function isExternalModuleNameRelative(moduleName: string): boolean; function sortAndDeduplicateDiagnostics(diagnostics: readonly T[]): SortedReadonlyArray; + /** + * 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; @@ -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