Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support project references #817

Merged
merged 44 commits into from
Sep 23, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
92f23b3
Pass project references to language service
andrewbranch Aug 9, 2018
55d8efa
Use parsed project references, not raw content
andrewbranch Aug 9, 2018
75a5bdb
Don’t fail while trying to emit/diagnose files in project references;…
andrewbranch Aug 9, 2018
05ee14d
Be tolerant of older TypeScript versions
andrewbranch Aug 9, 2018
157211e
Add project references to transpileOnly mode
andrewbranch Aug 9, 2018
ee61c63
Add project references to experimental watch API path
andrewbranch Aug 10, 2018
0b81aa3
Use JS output for files in project references
andrewbranch Aug 13, 2018
0a42045
Fix loader emit typings
andrewbranch Aug 14, 2018
c01af17
Get program out of language service too
andrewbranch Aug 14, 2018
0d2854c
Add comparison test for project references without source maps
andrewbranch Aug 14, 2018
615bcbb
Add comparison test for project references with source map (no warnings)
andrewbranch Aug 14, 2018
2f4ff59
Add test for unbuilt project references
andrewbranch Aug 14, 2018
1e765b2
Only warn once per project per compilation about source maps
andrewbranch Aug 20, 2018
d262970
Make comments more accurate
andrewbranch Aug 20, 2018
700e967
Implement getOutputJavaScriptFileName
andrewbranch Sep 1, 2018
741bf9b
Add test for project references with outDir (and fix implementation)
andrewbranch Sep 1, 2018
f36a841
Be safer in getProjectReferenceForFile
andrewbranch Sep 1, 2018
05ab35d
Fix type error
andrewbranch Sep 1, 2018
565dab2
Add JS files instead of TS files to loader dependencies for project refs
andrewbranch Sep 3, 2018
a224efc
Add comments
andrewbranch Sep 3, 2018
80528ea
Cache project reference info in files map
andrewbranch Sep 3, 2018
47515ed
Update test for updated stack trace
andrewbranch Sep 3, 2018
451c462
Don’t repeatedly check files that don’t have project refs for project…
andrewbranch Sep 3, 2018
266a4ba
Rename test project reference output to a folder that’s not gitignored 😂
andrewbranch Sep 3, 2018
cc90401
Merge branch 'master' into project-references
johnnyreilly Sep 12, 2018
d532f78
Don’t ignore map files important to projectReferences tests
andrewbranch Sep 16, 2018
29d02b9
Normalize windows paths before comparing
Sep 17, 2018
433630b
Use only relative paths in error messages
andrewbranch Sep 17, 2018
80a28cd
Update expected output to remove absolute path from error message
andrewbranch Sep 18, 2018
fe888e2
Ignore path differences in TS error message
andrewbranch Sep 18, 2018
ddc5cc7
Theoretically protect against getProjectReferences changes in typescr…
andrewbranch Sep 20, 2018
6c87188
Update README with project references
andrewbranch Sep 21, 2018
611e8d9
Put projectReferences behind a loaderOption
andrewbranch Sep 21, 2018
32e2882
Update validateLoaderOptions and test loader options
andrewbranch Sep 21, 2018
e47664b
Update validateLoaderOptionNames test
andrewbranch Sep 21, 2018
2f196b3
ignore projectReferencesOutDir on windows in non-transpile mode
johnnyreilly Sep 21, 2018
b235502
add execution test for project references
johnnyreilly Sep 22, 2018
5d190fa
General tweaks; I should really get tslint properly set up
johnnyreilly Sep 22, 2018
16a9e7c
some yarn lint stuff
johnnyreilly Sep 23, 2018
b6510a0
further tslint fixes
johnnyreilly Sep 23, 2018
fbed24b
fix broken appenSuffixes
johnnyreilly Sep 23, 2018
308f0dd
add linting to build
johnnyreilly Sep 23, 2018
a6b203c
update changelog
johnnyreilly Sep 23, 2018
ca2c576
Merge branch 'master' into project-references
johnnyreilly Sep 23, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .travis.yml
Expand Up @@ -9,6 +9,7 @@ sudo: required
install:
- yarn install
- yarn build
- yarn lint
- yarn add $TYPESCRIPT
env:
- TYPESCRIPT=typescript@3.0.1
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
@@ -1,6 +1,11 @@
# Changelog

## 5.2.0

* [feat: Initial support for project references - `projectReferences`](https://github.com/TypeStrong/ts-loader/pull/817) - thanks @andrewbranch!

## 5.1.1

* [fix(getTranspilationEmit): pass the raw path to transpileModule](https://github.com/TypeStrong/ts-loader/pull/835) - thanks @Brooooooklyn

## 5.1.0
Expand Down
28 changes: 27 additions & 1 deletion README.md
Expand Up @@ -543,7 +543,7 @@ Extending `tsconfig.json`:

Note that changes in the extending file while not be respected by `ts-loader`. Its purpose is to satisfy the code editor.

### experimentalFileCaching _(boolean) (default=false)_
#### experimentalFileCaching _(boolean) (default=false)_

By default whenever the TypeScript compiler needs to check that a file/directory exists or resolve symlinks it makes syscalls.
It does not cache the result of these operations and this may result in many syscalls with the same arguments ([see comment](https://github.com/TypeStrong/ts-loader/issues/825#issue-354725524) with example).
Expand All @@ -552,6 +552,32 @@ In some cases it may produce performance degradation.
This flag enables caching for some FS-functions like `fileExists`, `realpath` and `directoryExists` for TypeScript compiler.
Note that caches are cleared between compilations.

#### projectReferences _(boolean) (default=false)_

**TL;DR:** Using project references currently requires building referenced projects outside of ts-loader. We don’t want to keep it that way, but we’re releasing what we’ve got now. To try it out, you’ll need to pass `projectReferences: true` to `loaderOptions`.

ts-loader has partial support for [project references](https://www.typescriptlang.org/docs/handbook/project-references.html) in that it will _load_ dependent composite projects that are already built, but will not currently _build/rebuild_ those upstream projects. The best way to explain exactly what this means is through an example. Say you have a project with a project reference pointing to the `lib/` directory:

```
tsconfig.json
app.ts
lib/
tsconfig.json
niftyUtil.ts
```

And we’ll assume that the root `tsconfig.json` has `{ "references": { "path": "lib" } }`, which means that any import of a file that’s part of the `lib` sub-project is treated as a reference to another project, not just a reference to a TypeScript file. Before discussing how ts-loader handles this, it’s helpful to review at a really basic level what `tsc` itself does here. If you were to run `tsc` on this tiny example project, the build would fail with the error:

```
error TS6305: Output file 'lib/niftyUtil.d.ts' has not been built from source file 'lib/niftyUtil.ts'.
```

Using project references actually instructs `tsc` _not_ to build anything that’s part of another project from source, but rather to look for any `.d.ts` and `.js` files that have already been generated from a previous build. Since we’ve never built the project in `lib` before, those files don’t exist, so building the root project fails. Still just thinking about how `tsc` works, there are two options to make the build succeed: either run `tsc -p lib/tsconfig.json` _first_, or simply run `tsc --build`, which will figure out that `lib` hasn’t been built and build it first for you.

Ok, so how is that relevant to ts-loader? Because the best way to think about what ts-loader does with project references is that it acts like `tsc`, but _not_ like `tsc --build`. If you run ts-loader on a project that’s using project references, and any upstream project hasn’t been built, you’ll get the exact same `error TS6305` that you would get with `tsc`. If you modify a source file in an upstream project and don’t rebuild that project, `ts-loader` won’t have any idea that you’ve changed anything—it will still be looking at the output from the last time you _built_ that file.

**“Hey, don’t you think that sounds kind of useless and terrible?”** Well, sort of. You can consider it a work-in-progress. It’s true that on its own, as of today, ts-loader doesn’t have everything you need to take advantage of project references in Webpack. In practice, though, _consuming_ upstream projects and _building_ upstream projects are somewhat separate concerns. Building them will likely come in a future release. For background, see the [original issue](https://github.com/TypeStrong/ts-loader/issues/815).

### Usage with Webpack watch

Because TS will generate .js and .d.ts files, you should ignore these files, otherwise watchers may go into an infinite watch loop. For example, when using Webpack, you may wish to add this to your webpack.conf.js file:
Expand Down
1 change: 1 addition & 0 deletions appveyor.yml
Expand Up @@ -15,6 +15,7 @@ install:
- ps: Install-Product node $env:nodejs_version
- yarn install
- yarn build
- yarn lint
- yarn add %TYPESCRIPT%
test_script:
- node --version
Expand Down
5 changes: 4 additions & 1 deletion package.json
@@ -1,11 +1,12 @@
{
"name": "ts-loader",
"version": "5.1.1",
"version": "5.2.0",
"description": "TypeScript loader for webpack",
"main": "index.js",
"types": "dist/types/index.d.ts",
"scripts": {
"build": "tsc --version && tsc --project \"./src\"",
"lint": "tslint --project \"./src\"",
"comparison-tests": "tsc --project \"./test/comparison-tests\" && npm link ./test/comparison-tests/testLib && node test/comparison-tests/run-tests.js",
"comparison-tests-generate": "node test/comparison-tests/stub-new-version.js",
"execution-tests": "npm i -g pnpm && node test/execution-tests/run-tests.js",
Expand Down Expand Up @@ -78,6 +79,8 @@
"mocha": "^5.0.0",
"prettier": "^1.11.1",
"rimraf": "^2.6.2",
"tslint": "^5.11.0",
"tslint-config-prettier": "^1.15.0",
"typescript": "^3.0.1",
"webpack": "^4.5.0",
"webpack-cli": "^2.1.2"
Expand Down
25 changes: 20 additions & 5 deletions src/after-compile.ts
@@ -1,15 +1,20 @@
import * as path from 'path';
import { collectAllDependants, formatErrors } from './utils';

import * as constants from './constants';
import { getEmitOutput } from './instances';
import {
TSFile,
TSFiles,
TSInstance,
WebpackCompilation,
WebpackError,
WebpackModule,
TSFile
WebpackModule
} from './interfaces';
import { getEmitOutput } from './instances';
import {
collectAllDependants,
formatErrors,
isUsingProjectReferences
} from './utils';

export function makeAfterCompile(
instance: TSInstance,
Expand Down Expand Up @@ -60,6 +65,7 @@ export function makeAfterCompile(

instance.filesWithErrors = filesWithErrors;
instance.modifiedFiles = null;
instance.projectsMissingSourceMaps = new Set();

callback();
};
Expand Down Expand Up @@ -171,7 +177,7 @@ function provideErrorsToWebpack(
otherFiles
} = instance;

let filePathRegex = !!compilerOptions.checkJs
const filePathRegex = !!compilerOptions.checkJs
? constants.dtsTsTsxJsJsxRegex
: constants.dtsTsTsxRegex;

Expand All @@ -181,6 +187,15 @@ function provideErrorsToWebpack(
}

const sourceFile = program && program.getSourceFile(filePath);

// If the source file is undefined, that probably means it’s actually part of an unbuilt project reference,
// which will have already produced a more useful error than the one we would get by proceeding here.
// If it’s undefined and we’re not using project references at all, I guess carry on so the user will
// get a useful error about which file was unexpectedly missing.
if (isUsingProjectReferences(instance) && !sourceFile) {
continue;
}

const errors = program
? [
...program.getSyntacticDiagnostics(sourceFile),
Expand Down
4 changes: 2 additions & 2 deletions src/compilerSetup.ts
@@ -1,9 +1,9 @@
import * as typescript from 'typescript';
import * as semver from 'semver';
import * as typescript from 'typescript';

import * as constants from './constants';
import * as logger from './logger';
import { LoaderOptions } from './interfaces';
import * as logger from './logger';

export function getCompiler(loaderOptions: LoaderOptions, log: logger.Logger) {
let compiler: typeof typescript | undefined;
Expand Down
6 changes: 3 additions & 3 deletions src/config.ts
@@ -1,9 +1,9 @@
import * as typescript from 'typescript';
import * as path from 'path';
import { Chalk } from 'chalk';
import * as path from 'path';
import * as typescript from 'typescript';
import { LoaderOptions, Webpack, WebpackError } from './interfaces';
import * as logger from './logger';
import { formatErrors } from './utils';
import { LoaderOptions, Webpack, WebpackError } from './interfaces';

interface ConfigFile {
config?: any;
Expand Down
3 changes: 3 additions & 0 deletions src/constants.ts
Expand Up @@ -11,11 +11,14 @@ export const ScriptTargetES2015 = 2;

export const ModuleKindCommonJs = 1;

export const extensionRegex = /\.[^.]+$/;
export const tsxRegex = /\.tsx$/i;
export const tsTsxRegex = /\.ts(x?)$/i;
export const dtsDtsxOrDtsDtsxMapRegex = /\.d\.ts(x?)(\.map)?$/i;
export const dtsTsTsxRegex = /(\.d)?\.ts(x?)$/i;
export const dtsTsTsxJsJsxRegex = /((\.d)?\.ts(x?)|js(x?))$/i;
export const tsTsxJsJsxRegex = /\.tsx?$|\.jsx?$/i;
export const jsJsx = /\.js(x?)$/i;
export const jsJsxMap = /\.js(x?)\.map$/i;
export const jsonRegex = /\.json$/i;
export const nodeModules = /node_modules/i;