π Search Terms
transpileModule fileName normalized, transpileDeclaration diagnostic fileName, unstable/sync API canonicalize path, TranspileOptions fileName
π Version & Regression Information
- This is the behavior in every version I tried:
typescript@7.1.0-dev.20260828.1 (nightly), via the typescript/unstable/sync API class. Not a regression β undocumented existing behavior, as far as we can tell.
β― Playground Link
N/A β reproducible only via the typescript/unstable/sync Node API, not the Playground.
π» Code
const { API } = require('typescript/unstable/sync');
const api = new API({});
const result = api.transpileModule('var a\n== 0;\n', {
compilerOptions: {},
fileName: 'C:\\Users\\me\\project\\app.ts',
reportDiagnostics: true,
});
console.log(result.diagnostics[0].fileName);
// "C:/Users/me/project/app.ts" - not what was passed in
π Actual behavior
fileName is silently canonicalized before use (separators to /, ./.. segments resolved β real path.normalize, not a slash swap), and every diagnostic echoes the canonical form instead of the caller's input:
fileName in |
diagnostics[0].fileName out |
C:\Users\me\project\app.ts |
C:/Users/me/project/app.ts |
C:/Users/me/../me/project/app.ts |
C:/Users/me/project/app.ts |
/tmp/foo/app.ts (already canonical) |
/tmp/foo/app.ts (unchanged) |
transpileDeclaration does the same. Neither behavior is documented on TranspileOptions.fileName or Diagnostic.fileName.
Not Windows-specific, though the backslash example is: POSIX paths never contain \, so that row won't reproduce on macOS/Linux, but the ./.. resolution will β pass a fileName like /tmp/foo/../foo/app.ts to see the same canonicalization on any OS.
This is a pain if you're trying to match up a diagnostic to the source file that it relates to. ts-loader's workaround is to canonicalize fileName itself before calling transpileModule, then use that same canonicalized string as the key: https://github.com/TypeStrong/ts-loader/blob/38d364e7d702138fa74672a9ef15c4447e6322ed/src/typeScriptApi.ts#L339
We found this reworking TypeStrong/ts-loader#1704: db8b27c added the canonicalization workaround to fix exactly this mismatch.
π Expected behavior
It feels curious that the fileName that comes out differs from what is passed in. It would be marvellous if that didn't happen, but perhaps there are reasons it is necessary.
Here are some ideas that might improve things for consumers:
- Document on
TranspileOptions.fileName/Diagnostic.fileName that fileName is canonicalized and diagnostics echo the canonical form, not the input.
- Include the original filename as well as the processed one in the diagnostics for consumers to use.
originalFileName perhaps
- Specify the exact rule (or export it as a utility) so callers building their own location lookup can replicate it, rather than reverse-engineering it via a silently-wrong test result.
Additional information about the issue
Full writeup with more probe data: see the linked ts-loader PR/commits above. Andrew Branch (@andrewbranch) this may be interesting to you - but I wouldn't want to presume!
π Search Terms
transpileModule fileName normalized, transpileDeclaration diagnostic fileName, unstable/sync API canonicalize path, TranspileOptions fileName
π Version & Regression Information
typescript@7.1.0-dev.20260828.1(nightly), via thetypescript/unstable/syncAPIclass. Not a regression β undocumented existing behavior, as far as we can tell.β― Playground Link
N/A β reproducible only via the
typescript/unstable/syncNode API, not the Playground.π» Code
π Actual behavior
fileNameis silently canonicalized before use (separators to/,./..segments resolved β realpath.normalize, not a slash swap), and every diagnostic echoes the canonical form instead of the caller's input:fileNameindiagnostics[0].fileNameoutC:\Users\me\project\app.tsC:/Users/me/project/app.tsC:/Users/me/../me/project/app.tsC:/Users/me/project/app.ts/tmp/foo/app.ts(already canonical)/tmp/foo/app.ts(unchanged)transpileDeclarationdoes the same. Neither behavior is documented onTranspileOptions.fileNameorDiagnostic.fileName.Not Windows-specific, though the backslash example is: POSIX paths never contain
\, so that row won't reproduce on macOS/Linux, but the./..resolution will β pass afileNamelike/tmp/foo/../foo/app.tsto see the same canonicalization on any OS.This is a pain if you're trying to match up a diagnostic to the source file that it relates to. ts-loader's workaround is to canonicalize
fileNameitself before callingtranspileModule, then use that same canonicalized string as the key: https://github.com/TypeStrong/ts-loader/blob/38d364e7d702138fa74672a9ef15c4447e6322ed/src/typeScriptApi.ts#L339We found this reworking TypeStrong/ts-loader#1704: db8b27c added the canonicalization workaround to fix exactly this mismatch.
π Expected behavior
It feels curious that the
fileNamethat comes out differs from what is passed in. It would be marvellous if that didn't happen, but perhaps there are reasons it is necessary.Here are some ideas that might improve things for consumers:
TranspileOptions.fileName/Diagnostic.fileNamethatfileNameis canonicalized and diagnostics echo the canonical form, not the input.originalFileNameperhapsAdditional information about the issue
Full writeup with more probe data: see the linked ts-loader PR/commits above. Andrew Branch (@andrewbranch) this may be interesting to you - but I wouldn't want to presume!