-
-
Notifications
You must be signed in to change notification settings - Fork 433
Description
We have hit a strange bug that would fail rarely (not deterministically) a build. When it failed, it threw the following error:
ERROR in ./packages/bar/bar.ts
Module build failed (from ../index.js):
Error: TypeScript emitted no output for /Users/davazp/Projects/ts-loader/minimal-repo/packages/bar/bar.ts.
at makeSourceMapAndFinish (/Users/davazp/Projects/ts-loader/dist/index.js:99:15)
at successLoader (/Users/davazp/Projects/ts-loader/dist/index.js:89:9)
at retry (/Users/davazp/Projects/ts-loader/dist/index.js:35:17)
at Object.loader (/Users/davazp/Projects/ts-loader/dist/index.js:42:5)
@ ./packages/foo/foo.ts 3:12-30
The cause turned out to be that such file was not part of the include section of the tsconfig.json. However, this non-deterministic behaviour is really confusing and undesirable, so I went deeper into the causes to really understand the problem and help to improve it. See the Analysis section below.
Expected Behaviour
I think ts-loader should have thrown an error when webpack passes a file to it that is not part of the TS project. This seems to me the most sensible solution. That way tsc and other TS-based tools will behave in the same way.
Actual Behaviour
The build will work or fail depending on the order in which webpack processes the files, which is not deterministic even without making any changes to the files.
Steps to Reproduce the Problem
It's not easy to reproduce because it depends on the order of the files. To reproduce it, I hacked ts-loader to enforce the order in a test project. To try:
- clone https://github.com/davazp/ts-loader
- Build it as usual, and in the
minimal-repo/directory - yarn
- yarn build
You should get the error described at the beginning of the issue.
Location of a Minimal Repository that Demonstrates the Issue.
This shows the changes I did to ts-loader to reproduce the issue:
master...davazp:ts-loader-issue
Analysis
After a lot of hard debugging, I managed to figure out what was going on.
The setup (like in the example repo) is
packages/foo/foo.ts # this file is included in tsconfig.json
packages/bar/bar.ts # this file is not
On a failed build:
-
The order in which webpack provides the files to
ts-loaderis not deterministic.
Two consequent builds could process files in a different order. I instrumentedts-loader
to record the order on failure, and enforced the order to reproduce the problem. -
When
foo/foo.tsis compiled,bar/bar.tsis added toinstance.filesbecause it is a dependency as a result to a call togetScriptSnapshotfrom TS. Additionally, it is marked as a external dependency because it is under node_modules in the TS' program. -
Later on,
bar/bar.tsis found as a toplevel file. updateFileInCache thinks it is already a root file because it is ininstance.files, soinstance.versionis not increased.As a consequence, TS reuses the program and the map
sourceFilesFoundSearchingNodeModulesis not thrown away. As an external file,Typescriptwill not emit any output for it.
This could work if finding a new root level file (even if it was found as a dependency before) increases the instance.version, but then ts-loader would deviate from other TS tools.
Why does it work for other file ordering?
If between 2) and 3), another file outside of the project is processed, which is not a dependency of any other file, then the file is not present in instance.files, so updateFileInCache will add it and increase instance.version, triggering a discard of the file is properly added as a new root file (as it is not in instance.files). This cause an instance.version increase and flushing the sourceFilesFoundSearchingNodeModules variable.