TypeStrong / TypeStrong/ts-loader

Proposal to make ts-loader a better webpack citizen

Open
#552 23 comments 31 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

pinned
Dominant language
TypeScript
Stars
3.5k
Forks
439
Avg merge
17h 17m
Merged PRs (30d)
2

Description

Working on pull request #520, and reading the comments in #506, has made me realise that ts-loader is not a well behaved webpack loader.

From the webpack documentation for how to write a loader:

Guidelines

(Ordered by priority, first one should get the highest priority)

Loaders should do only a single task

Loaders can be chained. Create loaders for every step, instead of a loader that does everything at once.

This also means they should not convert to JavaScript if not necessary.

Loaders should do only a single task

ts-loader does only do a single task: compiling TypeScript modules.

Loaders should not convert to JavaScript if not necessary

ts-loader does convert to JavaScript, and it is necessary.

Loaders can be chained

This is where ts-loader is not a well behaved webpack loader.

The TypeScript compiler needs to have access to modules imported into the current module. ts-loader does this by reading directly from the file system, if the file has not been seen previously. It is therefore not always possible to make a previous loader in the same chain work properly (even with pull request #520).

Proposal

Goals

To make ts-loader a better webpack citizen I propose that the ts-loader is changed to achieve the following:

  • TypeScript files should be resolved as webpack modules, using whatever resolution rules are set up for webpack.

  • ts-loader should never read modules directly from the file system

    The method loaderContext.loadModule, or similar, should be used in stead. This will enable other loaders to make changes to source files, or even generate virtual source files.

  • ts-loader should never read a module more than once during a compilation

    This is essential for good performance, and should avoid problems with circular references.

  • During watch ts-loader should only read a module if it has changed

    This is essential for good performance. It should be investigated whether ts-loader needs to track changed files to achieve this, or whether webpack can do so.

Implementation

Because loading modules in webpack is an asynchronous operation, and the TypeScript compiler reads files synchronously, it will probably be necessary to compile each file in several phases:

  1. Save the actual TypeScript source code (as it is after running through previous loaders) in a module cache.

  2. Get a list of all imported modules using ts.preProcessFile.

  3. For each imported module, that is not already in the module cache, do the following (these are asynchonous operations):

    1. Each possible path for the module should be resolved by trying to add .ts, .tsx, .d.ts, etc. and using loaderContext.resolve.

    2. Try loading each of the resolved paths until loading succeeds. Save the succeeded module source code in the module cache, or mark the module as non existent. See if we can load any TypeScript modules without compiling them (for example by adding a query parameter to the module request).
      This could be achieved using loaderContext.loadModule. However, loaderContext.loadModule does not load modules recursively, so it may be necessary to code our own loadModule function (as I have done in ts-css-loader).

  4. Synchronously compile the TypeScript source using source code saved in the module cache.

Conclusion

I would be very happy to work on this, but only if there is interest.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reviewing webpack's loaderContext.resolve and loaderContext.loadModule APIs and TypeScript's ts.preProcessFile, then compare them with ts-loader's current module-loading behavior. Completion should cover webpack-based resolution, no direct filesystem reads, one-read caching per compilation, and changed-only reads during watch mode.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript, webpack
Domain
build-system, tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.