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 for symlinks in project references #1136

Merged
merged 15 commits into from Jul 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
7 changes: 5 additions & 2 deletions .gitignore
@@ -1,6 +1,7 @@
/*.js
!index.js
/src/*.js
/src/.vscode
/*.d.ts
/*.log
*.js.map
Expand All @@ -10,11 +11,13 @@ npm-debug.log
/test/execution-tests/**/typings
!/test/**/expectedOutput-*/**
/**/node_modules
/**/dist
/dist
/test/execution-tests/**/dist
/**/.happypack
/**/.cache-loader
!build.js
/**/debug.log
.pnp
.pnp.js
!.eslintrc.js
!.eslintrc.js
.vs/**
12 changes: 0 additions & 12 deletions .travis.yml
Expand Up @@ -18,16 +18,4 @@ env:
- TYPESCRIPT=typescript@3.8.2
- TYPESCRIPT=typescript@3.7.4
- TYPESCRIPT=typescript@3.6.3
- TYPESCRIPT=typescript@3.5.1
- TYPESCRIPT=typescript@3.4.4
- TYPESCRIPT=typescript@3.3.3
- TYPESCRIPT=typescript@3.2.1
- TYPESCRIPT=typescript@3.1.1
- TYPESCRIPT=typescript@3.0.1
- TYPESCRIPT=typescript@2.9.2
- TYPESCRIPT=typescript@2.8.1
- TYPESCRIPT=typescript@2.7.1
- TYPESCRIPT=typescript@2.6.1
- TYPESCRIPT=typescript@2.5.2
- TYPESCRIPT=typescript@2.4.1

4 changes: 4 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,9 @@
# Changelog

## v8.0.0
* [Support for symlinks in project references](https://github.com/TypeStrong/ts-loader/pull/1136) - thanks @sheetalkamat!
* `ts-loader` now supports TypeScript 3.6 and greater **BREAKING CHANGE**

## v7.0.5
* [Add a delay before starting the comparison tests to avoid failures under WSL](https://github.com/TypeStrong/ts-loader/pull/1109) - thanks @appzuka
* [Apply other loaders when updating files in watch mode](https://github.com/TypeStrong/ts-loader/pull/1115) - thanks @iorate
Expand Down
13 changes: 1 addition & 12 deletions appveyor.yml
Expand Up @@ -8,18 +8,7 @@ environment:
- TYPESCRIPT: typescript@3.8.2
- TYPESCRIPT: typescript@3.7.4
- TYPESCRIPT: typescript@3.6.3
- TYPESCRIPT: typescript@3.5.1
- TYPESCRIPT: typescript@3.4.4
- TYPESCRIPT: typescript@3.3.3
- TYPESCRIPT: typescript@3.2.1
- TYPESCRIPT: typescript@3.1.1
- TYPESCRIPT: typescript@3.0.1
- TYPESCRIPT: typescript@2.9.2
- TYPESCRIPT: typescript@2.8.1
- TYPESCRIPT: typescript@2.7.1
- TYPESCRIPT: typescript@2.6.1
- TYPESCRIPT: typescript@2.5.2
- TYPESCRIPT: typescript@2.4.1

install:
- ps: Install-Product node $env:nodejs_version
- yarn install
Expand Down
2 changes: 1 addition & 1 deletion package.json
@@ -1,6 +1,6 @@
{
"name": "ts-loader",
"version": "7.0.5",
"version": "8.0.0",
"description": "TypeScript loader for webpack",
"main": "index.js",
"types": "dist",
Expand Down
93 changes: 46 additions & 47 deletions src/after-compile.ts
Expand Up @@ -3,23 +3,21 @@ import * as ts from 'typescript';
import * as webpack from 'webpack';

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

export function makeAfterCompile(
Expand Down Expand Up @@ -55,15 +53,15 @@ export function makeAfterCompile(
);
getCompilerOptionDiagnostics = false;

const modules = determineModules(compilation);
const modules = determineModules(compilation, instance);

const filesToCheckForErrors = determineFilesToCheckForErrors(
checkAllFilesForErrors,
instance
);
checkAllFilesForErrors = false;

const filesWithErrors: TSFiles = new Map<string, TSFile>();
const filesWithErrors: TSFiles = new Map();
provideErrorsToWebpack(
filesToCheckForErrors,
filesWithErrors,
Expand Down Expand Up @@ -119,11 +117,14 @@ function provideCompilerOptionDiagnosticErrorsToWebpack(
* this is used for quick-lookup when trying to find modules
* based on filepath
*/
function determineModules(compilation: webpack.compilation.Compilation) {
return compilation.modules.reduce<Map<string, WebpackModule[]>>(
function determineModules(
compilation: webpack.compilation.Compilation,
{ filePathKeyMapper }: TSInstance
) {
return compilation.modules.reduce<Map<FilePathKey, WebpackModule[]>>(
(modules, module) => {
if (module.resource) {
const modulePath = path.normalize(module.resource);
const modulePath = filePathKeyMapper(module.resource);
const existingModules = modules.get(modulePath);
if (existingModules !== undefined) {
if (existingModules.indexOf(module) === -1) {
Expand All @@ -136,7 +137,7 @@ function determineModules(compilation: webpack.compilation.Compilation) {

return modules;
},
new Map<string, WebpackModule[]>()
new Map()
);
}

Expand All @@ -146,43 +147,54 @@ function determineFilesToCheckForErrors(
) {
const { files, modifiedFiles, filesWithErrors, otherFiles } = instance;
// calculate array of files to check
const filesToCheckForErrors: TSFiles = new Map<string, TSFile>();
const filesToCheckForErrors: TSFiles = new Map();
if (checkAllFilesForErrors) {
// check all files on initial run
for (const [filePath, file] of files) {
filesToCheckForErrors.set(filePath, file);
addFileToCheckForErrors(filePath, file);
}
for (const [filePath, file] of otherFiles) {
filesToCheckForErrors.set(filePath, file);
addFileToCheckForErrors(filePath, file);
}
} else if (modifiedFiles !== null && modifiedFiles !== undefined) {
} else if (
modifiedFiles !== null &&
modifiedFiles !== undefined &&
modifiedFiles.size
) {
const reverseDependencyGraph = populateReverseDependencyGraph(instance);
// check all modified files, and all dependants
for (const modifiedFileName of modifiedFiles.keys()) {
collectAllDependants(
instance.reverseDependencyGraph,
for (const fileName of collectAllDependants(
reverseDependencyGraph,
modifiedFileName
).forEach(fileName => {
).keys()) {
const fileToCheckForErrors =
files.get(fileName) || otherFiles.get(fileName);
filesToCheckForErrors.set(fileName, fileToCheckForErrors!);
});
addFileToCheckForErrors(fileName, fileToCheckForErrors!);
}
}
}

// re-check files with errors from previous build
if (filesWithErrors !== undefined) {
for (const [fileWithErrorName, fileWithErrors] of filesWithErrors) {
filesToCheckForErrors.set(fileWithErrorName, fileWithErrors);
addFileToCheckForErrors(fileWithErrorName, fileWithErrors);
}
}
return filesToCheckForErrors;

function addFileToCheckForErrors(filePath: FilePathKey, file: TSFile) {
if (!isReferencedFile(instance, filePath)) {
filesToCheckForErrors.set(filePath, file);
}
}
}

function provideErrorsToWebpack(
filesToCheckForErrors: TSFiles,
filesWithErrors: TSFiles,
compilation: webpack.compilation.Compilation,
modules: Map<string, WebpackModule[]>,
modules: Map<FilePathKey, WebpackModule[]>,
instance: TSInstance
) {
const {
Expand All @@ -200,20 +212,12 @@ function provideErrorsToWebpack(

// I’m pretty sure this will never be undefined here
const program = ensureProgram(instance);
for (const filePath of filesToCheckForErrors.keys()) {
if (filePath.match(filePathRegex) === null) {
continue;
}

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 === undefined) {
for (const [filePath, { fileName }] of filesToCheckForErrors.entries()) {
if (fileName.match(filePathRegex) === null) {
continue;
}

const sourceFile = program && program.getSourceFile(fileName);
const errors: ts.Diagnostic[] = [];
if (program && sourceFile) {
errors.push(
Expand All @@ -231,7 +235,7 @@ function provideErrorsToWebpack(
}

// if we have access to a webpack module, use that
const associatedModules = modules.get(filePath);
const associatedModules = modules.get(instance.filePathKeyMapper(fileName));
if (associatedModules !== undefined) {
associatedModules.forEach(module => {
// remove any existing errors
Expand All @@ -257,7 +261,7 @@ function provideErrorsToWebpack(
loaderOptions,
instance.colors,
compiler,
{ file: filePath },
{ file: fileName },
compilation.compiler.context
);

Expand All @@ -268,7 +272,7 @@ function provideErrorsToWebpack(

function provideSolutionErrorsToWebpack(
compilation: webpack.compilation.Compilation,
modules: Map<string, WebpackModule[]>,
modules: Map<FilePathKey, WebpackModule[]>,
instance: TSInstance
) {
if (
Expand Down Expand Up @@ -315,7 +319,7 @@ function provideSolutionErrorsToWebpack(
loaderOptions,
instance.colors,
compiler,
{ file: filePath },
{ file: path.resolve(perFileDiagnostics[0].file!.fileName) },
compilation.compiler.context
);

Expand Down Expand Up @@ -344,17 +348,12 @@ function provideDeclarationFilesToWebpack(
instance: TSInstance,
compilation: webpack.compilation.Compilation
) {
for (const filePath of filesToCheckForErrors.keys()) {
if (filePath.match(constants.tsTsxRegex) === null) {
for (const { fileName } of filesToCheckForErrors.values()) {
if (fileName.match(constants.tsTsxRegex) === null) {
continue;
}

if (!isReferencedFile(instance, filePath)) {
addDeclarationFilesAsAsset(
getEmitOutput(instance, filePath),
compilation
);
}
addDeclarationFilesAsAsset(getEmitOutput(instance, fileName), compilation);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/compilerSetup.ts
Expand Up @@ -27,7 +27,7 @@ export function getCompiler(loaderOptions: LoaderOptions, log: logger.Logger) {
if (loaderOptions.compiler === 'typescript') {
if (
compiler!.version !== undefined &&
semver.gte(compiler!.version, '2.4.1')
semver.gte(compiler!.version, '3.6.3')
) {
// don't log yet in this case, if a tsconfig.json exists we want to combine the message
compilerCompatible = true;
Expand Down