diff options
author | Yoshiya Hinosawa <stibium121@gmail.com> | 2021-02-01 17:02:02 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-02-01 17:02:02 +0900 |
commit | 23281be33a378ca548f0a407f642331f20b00a43 (patch) | |
tree | 9d4d96d79e496c9e270fa91b2ed525bcdc6cdb33 | |
parent | 534531e4dd4133ff22f775b385ba488ec747cead (diff) |
fix(cli): fix handling of non-normalized specifier (#9357)
-rw-r--r-- | cli/tests/compiler_api_test.ts | 14 | ||||
-rw-r--r-- | cli/tsc/99_main_compiler.js | 48 |
2 files changed, 55 insertions, 7 deletions
diff --git a/cli/tests/compiler_api_test.ts b/cli/tests/compiler_api_test.ts index 99c7af961..4aac027cb 100644 --- a/cli/tests/compiler_api_test.ts +++ b/cli/tests/compiler_api_test.ts @@ -305,3 +305,17 @@ Deno.test({ }); }, }); + +Deno.test({ + name: "Deno.emit() - non-normalized specifier and source can compile", + async fn() { + const specifier = "https://example.com/foo//bar.ts"; + const { files } = await Deno.emit(specifier, { + sources: { + [specifier]: `export let foo: string = "foo";`, + }, + }); + assertEquals(files[`${specifier}.js`], 'export let foo = "foo";\n'); + assert(typeof files[`${specifier}.js.map`] === "string"); + }, +}); diff --git a/cli/tsc/99_main_compiler.js b/cli/tsc/99_main_compiler.js index 1dc00873d..fa25b207f 100644 --- a/cli/tsc/99_main_compiler.js +++ b/cli/tsc/99_main_compiler.js @@ -19,6 +19,14 @@ delete Object.prototype.__proto__; let logDebug = false; let logSource = "JS"; + // The map from the normalized specifier to the original. + // TypeScript normalizes the specifier in its internal processing, + // but the original specifier is needed when looking up the source from the runtime. + // This map stores that relationship, and the original can be restored by the + // normalized specifier. + // See: https://github.com/denoland/deno/issues/9277#issuecomment-769653834 + const normalizedToOriginalMap = new Map(); + function setLogDebug(debug, source) { logDebug = debug; if (source) { @@ -176,15 +184,15 @@ delete Object.prototype.__proto__; version; /** * @param {string} specifier - * @param {string} version + * @param {string} version */ constructor(specifier, version) { this.specifier = specifier; this.version = version; } /** - * @param {number} start - * @param {number} end + * @param {number} start + * @param {number} end * @returns {string} */ getText(start, end) { @@ -260,6 +268,9 @@ delete Object.prototype.__proto__; return sourceFile; } + // Needs the original specifier + specifier = normalizedToOriginalMap.get(specifier) ?? specifier; + /** @type {{ data: string; hash?: string; scriptKind: ts.ScriptKind }} */ const { data, hash, scriptKind } = core.jsonOpSync( "op_load", @@ -394,7 +405,7 @@ delete Object.prototype.__proto__; } /** - * @param {{ program: ts.Program | ts.EmitAndSemanticDiagnosticsBuilderProgram, fileCount?: number }} options + * @param {{ program: ts.Program | ts.EmitAndSemanticDiagnosticsBuilderProgram, fileCount?: number }} options */ function performanceProgram({ program, fileCount }) { if (program) { @@ -436,6 +447,27 @@ delete Object.prototype.__proto__; * @property {string[]} rootNames */ + /** + * Checks the normalized version of the root name and stores it in + * `normalizedToOriginalMap`. If the normalized specifier is already + * registered for the different root name, it throws an AssertionError. + * + * @param {string} rootName + */ + function checkNormalizedPath(rootName) { + const normalized = ts.normalizePath(rootName); + const originalRootName = normalizedToOriginalMap.get(normalized); + if (typeof originalRootName === "undefined") { + normalizedToOriginalMap.set(normalized, rootName); + } else if (originalRootName !== rootName) { + // The different root names are normalizd to the same path. + // This will cause problem when looking up the source for each. + throw new AssertionError( + `The different names for the same normalized specifier are specified: normalized=${normalized}, rootNames=${originalRootName},${rootName}`, + ); + } + } + /** The API that is called by Rust when executing a request. * @param {Request} request */ @@ -445,6 +477,8 @@ delete Object.prototype.__proto__; debug(">>> exec start", { rootNames }); debug(config); + rootNames.forEach(checkNormalizedPath); + const { options, errors: configFileParsingDiagnostics } = ts .convertCompilerOptionsFromJson(config, ""); // The `allowNonTsExtensions` is a "hidden" compiler option used in VSCode @@ -479,15 +513,15 @@ delete Object.prototype.__proto__; } /** - * @param {number} id - * @param {any} data + * @param {number} id + * @param {any} data */ function respond(id, data = null) { core.jsonOpSync("op_respond", { id, data }); } /** - * @param {LanguageServerRequest} request + * @param {LanguageServerRequest} request */ function serverRequest({ id, ...request }) { debug(`serverRequest()`, { id, ...request }); |