From 92f23b3328b8fed4ff4b616776607f6d55ed460e Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Wed, 8 Aug 2018 18:13:34 -0700 Subject: [PATCH 01/42] Pass project references to language service --- src/instances.ts | 8 +++++++- src/servicesHost.ts | 5 ++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/instances.ts b/src/instances.ts index 6fb78c2d9..9a666730b 100644 --- a/src/instances.ts +++ b/src/instances.ts @@ -266,7 +266,13 @@ function successfulTypeScriptInstance( .getProgram() .getProgram(); } else { - const servicesHost = makeServicesHost(scriptRegex, log, loader, instance); + const servicesHost = makeServicesHost( + scriptRegex, + log, + loader, + instance, + configFile.config!.references + ); instance.languageService = compiler.createLanguageService( servicesHost, compiler.createDocumentRegistry() diff --git a/src/servicesHost.ts b/src/servicesHost.ts index 8fee9c29e..7e407927b 100644 --- a/src/servicesHost.ts +++ b/src/servicesHost.ts @@ -21,7 +21,8 @@ export function makeServicesHost( scriptRegex: RegExp, log: logger.Logger, loader: Webpack, - instance: TSInstance + instance: TSInstance, + projectReferences: ReadonlyArray = [] ) { const { compiler, @@ -61,6 +62,8 @@ export function makeServicesHost( const servicesHost: typescript.LanguageServiceHost = { getProjectVersion: () => `${instance.version}`, + getProjectReferences: () => projectReferences, + getScriptFileNames: () => [...files.keys()].filter(filePath => filePath.match(scriptRegex)), From 55d8efa1699cf997e70fc8b044e9378cbf467c0c Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Thu, 9 Aug 2018 11:20:17 -0600 Subject: [PATCH 02/42] Use parsed project references, not raw content --- src/instances.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/instances.ts b/src/instances.ts index 9a666730b..985a715d2 100644 --- a/src/instances.ts +++ b/src/instances.ts @@ -271,7 +271,7 @@ function successfulTypeScriptInstance( log, loader, instance, - configFile.config!.references + configParseResult.projectReferences ); instance.languageService = compiler.createLanguageService( servicesHost, From 75a5bdba3d13be6eb83e07a15bb9a008691961ad Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Thu, 9 Aug 2018 12:01:03 -0600 Subject: [PATCH 03/42] =?UTF-8?q?Don=E2=80=99t=20fail=20while=20trying=20t?= =?UTF-8?q?o=20emit/diagnose=20files=20in=20project=20references;=20just?= =?UTF-8?q?=20skip=20them?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/after-compile.ts | 10 ++++++++++ src/instances.ts | 21 +++++++++++++-------- src/servicesHost.ts | 2 +- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/after-compile.ts b/src/after-compile.ts index 5ba828dcb..7d9713657 100644 --- a/src/after-compile.ts +++ b/src/after-compile.ts @@ -181,6 +181,16 @@ function provideErrorsToWebpack( } const sourceFile = program && program.getSourceFile(filePath); + + // If the source file is undefined, that probably means it’s actually part of a project reference. + // 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. + const usesProjectReferences = !!(program || + languageService!.getProgram())!.getProjectReferences(); + if (usesProjectReferences && !sourceFile) { + continue; + } + const errors = program ? [ ...program.getSyntacticDiagnostics(sourceFile), diff --git a/src/instances.ts b/src/instances.ts index 985a715d2..9c33868b5 100644 --- a/src/instances.ts +++ b/src/instances.ts @@ -304,16 +304,21 @@ export function getEmitOutput(instance: TSInstance, filePath: string) { writeByteOrderMark: boolean ) => outputFiles.push({ name: fileName, writeByteOrderMark, text }); const sourceFile = program.getSourceFile(filePath); - program.emit( - sourceFile, - writeFile, - /*cancellationToken*/ undefined, - /*emitOnlyDtsFiles*/ false, - instance.transformers - ); + // The source file will be undefined if it’s part of a project reference + if (sourceFile || !program.getProjectReferences()) { + program.emit( + sourceFile, + writeFile, + /*cancellationToken*/ undefined, + /*emitOnlyDtsFiles*/ false, + instance.transformers + ); + } return outputFiles; } else { // Emit Javascript - return instance.languageService!.getEmitOutput(filePath).outputFiles; + return instance.languageService!.getProgram()!.getSourceFile(filePath) + ? instance.languageService!.getEmitOutput(filePath).outputFiles + : []; } } diff --git a/src/servicesHost.ts b/src/servicesHost.ts index 7e407927b..57db72fd7 100644 --- a/src/servicesHost.ts +++ b/src/servicesHost.ts @@ -22,7 +22,7 @@ export function makeServicesHost( log: logger.Logger, loader: Webpack, instance: TSInstance, - projectReferences: ReadonlyArray = [] + projectReferences?: ReadonlyArray ) { const { compiler, From 05ee14d04a9358e62d867278bce5f14cc6517873 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Thu, 9 Aug 2018 12:47:03 -0600 Subject: [PATCH 04/42] Be tolerant of older TypeScript versions --- src/after-compile.ts | 11 +++++++---- src/instances.ts | 26 +++++++------------------- src/utils.ts | 34 +++++++++++++++++++++++++++++++++- 3 files changed, 47 insertions(+), 24 deletions(-) diff --git a/src/after-compile.ts b/src/after-compile.ts index 7d9713657..3c0f6a20e 100644 --- a/src/after-compile.ts +++ b/src/after-compile.ts @@ -1,5 +1,10 @@ import * as path from 'path'; -import { collectAllDependants, formatErrors } from './utils'; + +import { + collectAllDependants, + formatErrors, + isUsingProjectReferences +} from './utils'; import * as constants from './constants'; import { TSFiles, @@ -185,9 +190,7 @@ function provideErrorsToWebpack( // If the source file is undefined, that probably means it’s actually part of a project reference. // 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. - const usesProjectReferences = !!(program || - languageService!.getProgram())!.getProjectReferences(); - if (usesProjectReferences && !sourceFile) { + if (isUsingProjectReferences(instance) && !sourceFile) { continue; } diff --git a/src/instances.ts b/src/instances.ts index 9c33868b5..55fd24dd5 100644 --- a/src/instances.ts +++ b/src/instances.ts @@ -7,7 +7,12 @@ import { makeAfterCompile } from './after-compile'; import { getConfigFile, getConfigParseResult } from './config'; import { EOL, dtsDtsxOrDtsDtsxMapRegex } from './constants'; import { getCompilerOptions, getCompiler } from './compilerSetup'; -import { makeError, formatErrors } from './utils'; +import { + makeError, + formatErrors, + ensureProgram, + isUsingProjectReferences +} from './utils'; import * as logger from './logger'; import { makeServicesHost, makeWatchHost } from './servicesHost'; import { makeWatchRun } from './watch-run'; @@ -23,23 +28,6 @@ import { const instances = {}; -function ensureProgram(instance: TSInstance) { - if (instance && instance.watchHost) { - if (instance.hasUnaccountedModifiedFiles) { - if (instance.changedFilesList) { - instance.watchHost.updateRootFileNames(); - } - if (instance.watchOfFilesAndCompilerOptions) { - instance.program = instance.watchOfFilesAndCompilerOptions - .getProgram() - .getProgram(); - } - instance.hasUnaccountedModifiedFiles = false; - } - return instance.program; - } - return undefined; -} /** * The loader is executed once for each file seen by webpack. However, we need to keep * a persistent instance of TypeScript that contains all of the files in the program @@ -305,7 +293,7 @@ export function getEmitOutput(instance: TSInstance, filePath: string) { ) => outputFiles.push({ name: fileName, writeByteOrderMark, text }); const sourceFile = program.getSourceFile(filePath); // The source file will be undefined if it’s part of a project reference - if (sourceFile || !program.getProjectReferences()) { + if (sourceFile || !isUsingProjectReferences(instance)) { program.emit( sourceFile, writeFile, diff --git a/src/utils.ts b/src/utils.ts index 405adb10e..c5c0e8ec8 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -12,7 +12,8 @@ import { Severity, WebpackError, WebpackModule, - ErrorInfo + ErrorInfo, + TSInstance } from './interfaces'; /** @@ -227,3 +228,34 @@ export function arrify(val: T | T[]) { return Array.isArray(val) ? val : [val]; } + +export function ensureProgram(instance: TSInstance) { + if (instance && instance.watchHost) { + if (instance.hasUnaccountedModifiedFiles) { + if (instance.changedFilesList) { + instance.watchHost.updateRootFileNames(); + } + if (instance.watchOfFilesAndCompilerOptions) { + instance.program = instance.watchOfFilesAndCompilerOptions + .getProgram() + .getProgram(); + } + instance.hasUnaccountedModifiedFiles = false; + } + return instance.program; + } + return undefined; +} + +export function supportsProjectReferences(instance: TSInstance) { + const program = ensureProgram(instance); + return program && !!program.getProjectReferences; +} + +export function isUsingProjectReferences(instance: TSInstance) { + if (supportsProjectReferences(instance)) { + const program = ensureProgram(instance); + return program && program.getProjectReferences(); + } + return false; +} From 157211e851278122616e8ba37a7a18aaf516e375 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Thu, 9 Aug 2018 14:38:04 -0600 Subject: [PATCH 05/42] Add project references to transpileOnly mode --- src/instances.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/instances.ts b/src/instances.ts index 55fd24dd5..461d97b05 100644 --- a/src/instances.ts +++ b/src/instances.ts @@ -155,7 +155,13 @@ function successfulTypeScriptInstance( if (loaderOptions.transpileOnly) { // quick return for transpiling // we do need to check for any issues with TS options though - const program = compiler!.createProgram([], compilerOptions); + const program = configParseResult.projectReferences + ? compiler!.createProgram({ + rootNames: configParseResult.fileNames, + options: configParseResult.options, + projectReferences: configParseResult.projectReferences + }) + : compiler!.createProgram([], compilerOptions); // happypack does not have _module.errors - see https://github.com/TypeStrong/ts-loader/issues/336 if (!loaderOptions.happyPackMode) { From ee61c63de6e13c5aac64421954a48bb98be346b9 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Fri, 10 Aug 2018 08:55:10 -0600 Subject: [PATCH 06/42] Add project references to experimental watch API path --- src/instances.ts | 3 ++- src/servicesHost.ts | 34 ++++++++++++++++++++++++++++++++-- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/instances.ts b/src/instances.ts index 461d97b05..d76b0d179 100644 --- a/src/instances.ts +++ b/src/instances.ts @@ -251,7 +251,8 @@ function successfulTypeScriptInstance( loader, instance, loaderOptions.appendTsSuffixTo, - loaderOptions.appendTsxSuffixTo + loaderOptions.appendTsxSuffixTo, + configParseResult.projectReferences ); instance.watchOfFilesAndCompilerOptions = compiler.createWatchProgram( instance.watchHost diff --git a/src/servicesHost.ts b/src/servicesHost.ts index 57db72fd7..86dd6bba5 100644 --- a/src/servicesHost.ts +++ b/src/servicesHost.ts @@ -152,7 +152,8 @@ export function makeWatchHost( loader: Webpack, instance: TSInstance, appendTsSuffixTo: RegExp[], - appendTsxSuffixTo: RegExp[] + appendTsxSuffixTo: RegExp[], + projectReferences?: ReadonlyArray ) { const { compiler, compilerOptions, files, otherFiles } = instance; @@ -242,7 +243,9 @@ export function makeWatchHost( ); } }, - createProgram: compiler.createAbstractBuilder + createProgram: projectReferences + ? createBuilderProgramWithReferences + : compiler.createAbstractBuilder }; return watchHost; @@ -369,6 +372,33 @@ export function makeWatchHost( callback ); } + + function createBuilderProgramWithReferences( + rootNames: ReadonlyArray | undefined, + options: typescript.CompilerOptions | undefined, + host: typescript.CompilerHost | undefined, + oldProgram: typescript.BuilderProgram | undefined, + configFileParsingDiagnostics: + | ReadonlyArray + | undefined + ) { + const program = compiler.createProgram({ + rootNames: rootNames!, + options: options!, + host, + oldProgram: oldProgram && oldProgram.getProgram(), + configFileParsingDiagnostics, + projectReferences + }); + + const builderProgramHost: typescript.BuilderProgramHost = host!; + return compiler.createAbstractBuilder( + program, + builderProgramHost, + oldProgram, + configFileParsingDiagnostics + ); + } } function resolveModuleNames( From 0b81aa3de472d5841708bd1db735f5cb9e58a49b Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Mon, 13 Aug 2018 11:50:40 -0600 Subject: [PATCH 07/42] Use JS output for files in project references --- src/index.ts | 85 +++++++++++++++++++++++++++++++++++++++++++++--- src/instances.ts | 1 + src/utils.ts | 19 ++++++++++- 3 files changed, 99 insertions(+), 6 deletions(-) diff --git a/src/index.ts b/src/index.ts index 872b9e553..4aff843df 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,7 +3,12 @@ import * as loaderUtils from 'loader-utils'; import * as typescript from 'typescript'; import { getTypeScriptInstance, getEmitOutput } from './instances'; -import { appendSuffixesIfMatch, arrify, formatErrors } from './utils'; +import { + appendSuffixesIfMatch, + arrify, + formatErrors, + getProjectReferenceForFile +} from './utils'; import * as constants from './constants'; import { AsyncCallback, @@ -63,11 +68,81 @@ function successLoader( : rawFilePath; const fileVersion = updateFileInCache(filePath, contents, instance); + const referencedProject = getProjectReferenceForFile(filePath, instance); + if (referencedProject) { + if (referencedProject.commandLine.options.outFile) { + throw new Error( + `The referenced project at ${ + referencedProject.sourceFile.fileName + } is using the ` + + `outFile' option, which is not supported with ts-loader.` + ); + } - const { outputText, sourceMapText } = options.transpileOnly - ? getTranspilationEmit(filePath, contents, instance, loader) - : getEmit(rawFilePath, filePath, instance, loader); + const jsFileName = instance.compiler.getOutputJavaScriptFileName( + filePath, + referencedProject.commandLine + ); + const mapFileName = jsFileName + '.map'; + if (!instance.compiler.sys.fileExists(jsFileName)) { + throw new Error( + `Could not find output JavaScript file for input ${filePath} ` + + `(looked at ${jsFileName}).\nThe input file is part of a project ` + + `reference located at ${referencedProject.sourceFile.fileName}, ` + + 'so ts-loader is looking for the project’s pre-built output on ' + + 'disk. Try running `tsc --build` to build project references.' + ); + } + if (!instance.compiler.sys.fileExists(mapFileName)) { + loader.emitWarning( + 'Could not find source map file for referenced project output ' + + `${jsFileName}. Ensure the 'sourceMap' compiler option is enabled ` + + `in ${referencedProject.sourceFile.fileName} to ensure Webpack ` + + 'can map project references to the appropriate source files.' + ); + } + + const outputText = instance.compiler.sys.readFile(jsFileName); + const sourceMapText = instance.compiler.sys.readFile(mapFileName); + makeSourceMapAndFinish( + sourceMapText, + outputText, + filePath, + contents, + loader, + options, + fileVersion, + callback + ); + } else { + const { outputText, sourceMapText } = options.transpileOnly + ? getTranspilationEmit(filePath, contents, instance, loader) + : getEmit(rawFilePath, filePath, instance, loader); + + makeSourceMapAndFinish( + sourceMapText, + outputText, + filePath, + contents, + loader, + options, + fileVersion, + callback + ); + } +} + +function makeSourceMapAndFinish( + sourceMapText: string | undefined, + outputText: string | undefined, + filePath: string, + contents: string, + loader: Webpack, + options: LoaderOptions, + fileVersion: number, + callback: AsyncCallback +) { if (outputText === null || outputText === undefined) { const additionalGuidance = !options.allowTsInNodeModules && filePath.indexOf('node_modules') !== -1 @@ -77,7 +152,7 @@ function successLoader( : ''; throw new Error( - `Typescript emitted no output for ${filePath}.${additionalGuidance}` + `TypeScript emitted no output for ${filePath}.${additionalGuidance}` ); } diff --git a/src/instances.ts b/src/instances.ts index d76b0d179..bb8d87ee5 100644 --- a/src/instances.ts +++ b/src/instances.ts @@ -184,6 +184,7 @@ function successfulTypeScriptInstance( loaderOptions, files, otherFiles, + program, dependencyGraph: {}, reverseDependencyGraph: {}, transformers: getCustomTransformers(), diff --git a/src/utils.ts b/src/utils.ts index c5c0e8ec8..740320f68 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -244,7 +244,7 @@ export function ensureProgram(instance: TSInstance) { } return instance.program; } - return undefined; + return instance.program; } export function supportsProjectReferences(instance: TSInstance) { @@ -259,3 +259,20 @@ export function isUsingProjectReferences(instance: TSInstance) { } return false; } + +export function getProjectReferenceForFile( + filePath: string, + instance: TSInstance +) { + if (isUsingProjectReferences(instance)) { + const program = ensureProgram(instance); + return ( + program && + program + .getProjectReferences()! + .find(ref => ref!.commandLine.fileNames.indexOf(filePath) > -1) + ); + } + + return; +} From 0a420452521af19c36d81be99b28bdcae8af19a6 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Tue, 14 Aug 2018 11:01:35 -0600 Subject: [PATCH 08/42] Fix loader emit typings --- src/interfaces.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interfaces.ts b/src/interfaces.ts index a66395fdf..de04985dc 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -94,11 +94,11 @@ export interface Webpack { /** * Emit a warning. */ - emitWarning: (message: string) => void; + emitWarning: (message: Error) => void; /** * Emit an error. */ - emitError: (message: string) => void; + emitError: (message: Error) => void; /** * Emit a file. This is webpack-specific */ From c01af177201b8ae9460f22b4c020eb94e625391e Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Tue, 14 Aug 2018 11:01:51 -0600 Subject: [PATCH 09/42] Get program out of language service too --- src/utils.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/utils.ts b/src/utils.ts index 740320f68..f54549109 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -244,6 +244,9 @@ export function ensureProgram(instance: TSInstance) { } return instance.program; } + if (instance.languageService) { + return instance.languageService.getProgram(); + } return instance.program; } From 0d2854cc2c82d2304fe1d826103a11821f2a2e29 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Tue, 14 Aug 2018 11:02:02 -0600 Subject: [PATCH 10/42] Add comparison test for project references without source maps --- .../projectReferencesNoSourceMap/app.ts | 3 + .../expectedOutput-3.0/bundle.js | 113 ++++++++++++++++++ .../expectedOutput-3.0/output.txt | 10 ++ .../lib/index.d.ts | 5 + .../projectReferencesNoSourceMap/lib/index.js | 9 ++ .../projectReferencesNoSourceMap/lib/index.ts | 5 + .../lib/tsconfig.json | 8 ++ .../tsconfig.json | 5 + .../webpack.config.js | 19 +++ 9 files changed, 177 insertions(+) create mode 100644 test/comparison-tests/projectReferencesNoSourceMap/app.ts create mode 100644 test/comparison-tests/projectReferencesNoSourceMap/expectedOutput-3.0/bundle.js create mode 100644 test/comparison-tests/projectReferencesNoSourceMap/expectedOutput-3.0/output.txt create mode 100644 test/comparison-tests/projectReferencesNoSourceMap/lib/index.d.ts create mode 100644 test/comparison-tests/projectReferencesNoSourceMap/lib/index.js create mode 100644 test/comparison-tests/projectReferencesNoSourceMap/lib/index.ts create mode 100644 test/comparison-tests/projectReferencesNoSourceMap/lib/tsconfig.json create mode 100644 test/comparison-tests/projectReferencesNoSourceMap/tsconfig.json create mode 100644 test/comparison-tests/projectReferencesNoSourceMap/webpack.config.js diff --git a/test/comparison-tests/projectReferencesNoSourceMap/app.ts b/test/comparison-tests/projectReferencesNoSourceMap/app.ts new file mode 100644 index 000000000..a83f2065b --- /dev/null +++ b/test/comparison-tests/projectReferencesNoSourceMap/app.ts @@ -0,0 +1,3 @@ +import { lib } from './lib'; + +console.log(lib.one, lib.two, lib.three); diff --git a/test/comparison-tests/projectReferencesNoSourceMap/expectedOutput-3.0/bundle.js b/test/comparison-tests/projectReferencesNoSourceMap/expectedOutput-3.0/bundle.js new file mode 100644 index 000000000..715c05596 --- /dev/null +++ b/test/comparison-tests/projectReferencesNoSourceMap/expectedOutput-3.0/bundle.js @@ -0,0 +1,113 @@ +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); +/******/ } +/******/ }; +/******/ +/******/ // define __esModule on exports +/******/ __webpack_require__.r = function(exports) { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ +/******/ // create a fake namespace object +/******/ // mode & 1: value is a module id, require it +/******/ // mode & 2: merge all properties of value into the ns +/******/ // mode & 4: return value when already ns object +/******/ // mode & 8|1: behave like require +/******/ __webpack_require__.t = function(value, mode) { +/******/ if(mode & 1) value = __webpack_require__(value); +/******/ if(mode & 8) return value; +/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; +/******/ var ns = Object.create(null); +/******/ __webpack_require__.r(ns); +/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); +/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); +/******/ return ns; +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = "./app.ts"); +/******/ }) +/************************************************************************/ +/******/ ({ + +/***/ "./app.ts": +/*!****************!*\ + !*** ./app.ts ***! + \****************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\nexports.__esModule = true;\nvar lib_1 = __webpack_require__(/*! ./lib */ \"./lib/index.ts\");\nconsole.log(lib_1.lib.one, lib_1.lib.two, lib_1.lib.three);\n\n\n//# sourceURL=webpack:///./app.ts?"); + +/***/ }), + +/***/ "./lib/index.ts": +/*!**********************!*\ + !*** ./lib/index.ts ***! + \**********************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\nexports.__esModule = true;\nexports.lib = {\n one: 1,\n two: 2,\n three: 3\n // I am adding this comment here by hand to ensure\n // Webpack is using the JS output for project references\n};\n\n\n//# sourceURL=webpack:///./lib/index.ts?"); + +/***/ }) + +/******/ }); \ No newline at end of file diff --git a/test/comparison-tests/projectReferencesNoSourceMap/expectedOutput-3.0/output.txt b/test/comparison-tests/projectReferencesNoSourceMap/expectedOutput-3.0/output.txt new file mode 100644 index 000000000..eab148a92 --- /dev/null +++ b/test/comparison-tests/projectReferencesNoSourceMap/expectedOutput-3.0/output.txt @@ -0,0 +1,10 @@ + Asset Size Chunks Chunk Names +bundle.js 4.4 KiB main [emitted] main +Entrypoint main = bundle.js +[./app.ts] 131 bytes {main} [built] +[./lib/index.ts] 213 bytes {main} [built] [1 warning] + +WARNING in ./lib/index.ts +Module Warning (from index.js): +Could not find source map file for referenced project output lib/index.js. Ensure the 'sourceMap' compiler option is enabled in lib/tsconfig.json to ensure Webpack can map project references to the appropriate source files. + @ ./app.ts 3:12-28 \ No newline at end of file diff --git a/test/comparison-tests/projectReferencesNoSourceMap/lib/index.d.ts b/test/comparison-tests/projectReferencesNoSourceMap/lib/index.d.ts new file mode 100644 index 000000000..73d752279 --- /dev/null +++ b/test/comparison-tests/projectReferencesNoSourceMap/lib/index.d.ts @@ -0,0 +1,5 @@ +export declare const lib: { + one: number; + two: number; + three: number; +}; diff --git a/test/comparison-tests/projectReferencesNoSourceMap/lib/index.js b/test/comparison-tests/projectReferencesNoSourceMap/lib/index.js new file mode 100644 index 000000000..2bcc995d9 --- /dev/null +++ b/test/comparison-tests/projectReferencesNoSourceMap/lib/index.js @@ -0,0 +1,9 @@ +"use strict"; +exports.__esModule = true; +exports.lib = { + one: 1, + two: 2, + three: 3 + // I am adding this comment here by hand to ensure + // Webpack is using the JS output for project references +}; diff --git a/test/comparison-tests/projectReferencesNoSourceMap/lib/index.ts b/test/comparison-tests/projectReferencesNoSourceMap/lib/index.ts new file mode 100644 index 000000000..669ca7b3d --- /dev/null +++ b/test/comparison-tests/projectReferencesNoSourceMap/lib/index.ts @@ -0,0 +1,5 @@ +export const lib = { + one: 1, + two: 2, + three: 3 +}; diff --git a/test/comparison-tests/projectReferencesNoSourceMap/lib/tsconfig.json b/test/comparison-tests/projectReferencesNoSourceMap/lib/tsconfig.json new file mode 100644 index 000000000..937062a9d --- /dev/null +++ b/test/comparison-tests/projectReferencesNoSourceMap/lib/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "composite": true + }, + "files": [ + "./index.ts" + ] +} \ No newline at end of file diff --git a/test/comparison-tests/projectReferencesNoSourceMap/tsconfig.json b/test/comparison-tests/projectReferencesNoSourceMap/tsconfig.json new file mode 100644 index 000000000..03974daa7 --- /dev/null +++ b/test/comparison-tests/projectReferencesNoSourceMap/tsconfig.json @@ -0,0 +1,5 @@ +{ + "references": [ + { "path": "./lib" } + ] +} \ No newline at end of file diff --git a/test/comparison-tests/projectReferencesNoSourceMap/webpack.config.js b/test/comparison-tests/projectReferencesNoSourceMap/webpack.config.js new file mode 100644 index 000000000..be37cdc01 --- /dev/null +++ b/test/comparison-tests/projectReferencesNoSourceMap/webpack.config.js @@ -0,0 +1,19 @@ +var path = require('path') + +module.exports = { + mode: 'development', + entry: './app.ts', + output: { + filename: 'bundle.js' + }, + resolve: { + extensions: ['.ts', '.js'] + }, + module: { + rules: [ + { test: /\.ts$/, loader: 'ts-loader' } + ] + } +} + + From 615bcbb0d74e5aa65a9a954c255ea95f9be6b3b4 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Tue, 14 Aug 2018 14:03:08 -0600 Subject: [PATCH 11/42] Add comparison test for project references with source map (no warnings) --- .../comparison-tests/projectReferences/app.ts | 3 + .../expectedOutput-3.0/bundle.js | 113 ++++++++++++++++++ .../expectedOutput-3.0/output.txt | 5 + .../projectReferences/lib/index.d.ts | 5 + .../projectReferences/lib/index.js | 10 ++ .../projectReferences/lib/index.ts | 5 + .../projectReferences/lib/tsconfig.json | 9 ++ .../projectReferences/tsconfig.json | 5 + .../projectReferences/webpack.config.js | 19 +++ .../expectedOutput-3.0/output.txt | 1 - 10 files changed, 174 insertions(+), 1 deletion(-) create mode 100644 test/comparison-tests/projectReferences/app.ts create mode 100644 test/comparison-tests/projectReferences/expectedOutput-3.0/bundle.js create mode 100644 test/comparison-tests/projectReferences/expectedOutput-3.0/output.txt create mode 100644 test/comparison-tests/projectReferences/lib/index.d.ts create mode 100644 test/comparison-tests/projectReferences/lib/index.js create mode 100644 test/comparison-tests/projectReferences/lib/index.ts create mode 100644 test/comparison-tests/projectReferences/lib/tsconfig.json create mode 100644 test/comparison-tests/projectReferences/tsconfig.json create mode 100644 test/comparison-tests/projectReferences/webpack.config.js diff --git a/test/comparison-tests/projectReferences/app.ts b/test/comparison-tests/projectReferences/app.ts new file mode 100644 index 000000000..a83f2065b --- /dev/null +++ b/test/comparison-tests/projectReferences/app.ts @@ -0,0 +1,3 @@ +import { lib } from './lib'; + +console.log(lib.one, lib.two, lib.three); diff --git a/test/comparison-tests/projectReferences/expectedOutput-3.0/bundle.js b/test/comparison-tests/projectReferences/expectedOutput-3.0/bundle.js new file mode 100644 index 000000000..715c05596 --- /dev/null +++ b/test/comparison-tests/projectReferences/expectedOutput-3.0/bundle.js @@ -0,0 +1,113 @@ +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); +/******/ } +/******/ }; +/******/ +/******/ // define __esModule on exports +/******/ __webpack_require__.r = function(exports) { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ +/******/ // create a fake namespace object +/******/ // mode & 1: value is a module id, require it +/******/ // mode & 2: merge all properties of value into the ns +/******/ // mode & 4: return value when already ns object +/******/ // mode & 8|1: behave like require +/******/ __webpack_require__.t = function(value, mode) { +/******/ if(mode & 1) value = __webpack_require__(value); +/******/ if(mode & 8) return value; +/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; +/******/ var ns = Object.create(null); +/******/ __webpack_require__.r(ns); +/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); +/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); +/******/ return ns; +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = "./app.ts"); +/******/ }) +/************************************************************************/ +/******/ ({ + +/***/ "./app.ts": +/*!****************!*\ + !*** ./app.ts ***! + \****************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\nexports.__esModule = true;\nvar lib_1 = __webpack_require__(/*! ./lib */ \"./lib/index.ts\");\nconsole.log(lib_1.lib.one, lib_1.lib.two, lib_1.lib.three);\n\n\n//# sourceURL=webpack:///./app.ts?"); + +/***/ }), + +/***/ "./lib/index.ts": +/*!**********************!*\ + !*** ./lib/index.ts ***! + \**********************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\nexports.__esModule = true;\nexports.lib = {\n one: 1,\n two: 2,\n three: 3\n // I am adding this comment here by hand to ensure\n // Webpack is using the JS output for project references\n};\n\n\n//# sourceURL=webpack:///./lib/index.ts?"); + +/***/ }) + +/******/ }); \ No newline at end of file diff --git a/test/comparison-tests/projectReferences/expectedOutput-3.0/output.txt b/test/comparison-tests/projectReferences/expectedOutput-3.0/output.txt new file mode 100644 index 000000000..678bcdcab --- /dev/null +++ b/test/comparison-tests/projectReferences/expectedOutput-3.0/output.txt @@ -0,0 +1,5 @@ + Asset Size Chunks Chunk Names +bundle.js 4.4 KiB main [emitted] main +Entrypoint main = bundle.js +[./app.ts] 131 bytes {main} [built] +[./lib/index.ts] 213 bytes {main} [built] \ No newline at end of file diff --git a/test/comparison-tests/projectReferences/lib/index.d.ts b/test/comparison-tests/projectReferences/lib/index.d.ts new file mode 100644 index 000000000..73d752279 --- /dev/null +++ b/test/comparison-tests/projectReferences/lib/index.d.ts @@ -0,0 +1,5 @@ +export declare const lib: { + one: number; + two: number; + three: number; +}; diff --git a/test/comparison-tests/projectReferences/lib/index.js b/test/comparison-tests/projectReferences/lib/index.js new file mode 100644 index 000000000..94f953e3e --- /dev/null +++ b/test/comparison-tests/projectReferences/lib/index.js @@ -0,0 +1,10 @@ +"use strict"; +exports.__esModule = true; +exports.lib = { + one: 1, + two: 2, + three: 3 + // I am adding this comment here by hand to ensure + // Webpack is using the JS output for project references +}; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/test/comparison-tests/projectReferences/lib/index.ts b/test/comparison-tests/projectReferences/lib/index.ts new file mode 100644 index 000000000..669ca7b3d --- /dev/null +++ b/test/comparison-tests/projectReferences/lib/index.ts @@ -0,0 +1,5 @@ +export const lib = { + one: 1, + two: 2, + three: 3 +}; diff --git a/test/comparison-tests/projectReferences/lib/tsconfig.json b/test/comparison-tests/projectReferences/lib/tsconfig.json new file mode 100644 index 000000000..506c325eb --- /dev/null +++ b/test/comparison-tests/projectReferences/lib/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "composite": true, + "sourceMap": true + }, + "files": [ + "./index.ts" + ] +} \ No newline at end of file diff --git a/test/comparison-tests/projectReferences/tsconfig.json b/test/comparison-tests/projectReferences/tsconfig.json new file mode 100644 index 000000000..03974daa7 --- /dev/null +++ b/test/comparison-tests/projectReferences/tsconfig.json @@ -0,0 +1,5 @@ +{ + "references": [ + { "path": "./lib" } + ] +} \ No newline at end of file diff --git a/test/comparison-tests/projectReferences/webpack.config.js b/test/comparison-tests/projectReferences/webpack.config.js new file mode 100644 index 000000000..be37cdc01 --- /dev/null +++ b/test/comparison-tests/projectReferences/webpack.config.js @@ -0,0 +1,19 @@ +var path = require('path') + +module.exports = { + mode: 'development', + entry: './app.ts', + output: { + filename: 'bundle.js' + }, + resolve: { + extensions: ['.ts', '.js'] + }, + module: { + rules: [ + { test: /\.ts$/, loader: 'ts-loader' } + ] + } +} + + diff --git a/test/comparison-tests/projectReferencesNoSourceMap/expectedOutput-3.0/output.txt b/test/comparison-tests/projectReferencesNoSourceMap/expectedOutput-3.0/output.txt index eab148a92..52f9b2c89 100644 --- a/test/comparison-tests/projectReferencesNoSourceMap/expectedOutput-3.0/output.txt +++ b/test/comparison-tests/projectReferencesNoSourceMap/expectedOutput-3.0/output.txt @@ -5,6 +5,5 @@ Entrypoint main = bundle.js [./lib/index.ts] 213 bytes {main} [built] [1 warning] WARNING in ./lib/index.ts -Module Warning (from index.js): Could not find source map file for referenced project output lib/index.js. Ensure the 'sourceMap' compiler option is enabled in lib/tsconfig.json to ensure Webpack can map project references to the appropriate source files. @ ./app.ts 3:12-28 \ No newline at end of file From 2f4ff59a19b90381b3297cf79188d4bf3ccf890a Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Tue, 14 Aug 2018 14:05:49 -0600 Subject: [PATCH 12/42] Add test for unbuilt project references --- .../projectReferencesNotBuilt/app.ts | 3 + .../expectedOutput-3.0/bundle.js | 112 ++++++++++++++++++ .../expectedOutput-3.0/bundle.transpiled.js | 112 ++++++++++++++++++ .../expectedOutput-3.0/output.transpiled.txt | 16 +++ .../expectedOutput-3.0/output.txt | 16 +++ .../projectReferencesNotBuilt/lib/index.ts | 5 + .../lib/tsconfig.json | 9 ++ .../projectReferencesNotBuilt/tsconfig.json | 5 + .../webpack.config.js | 19 +++ 9 files changed, 297 insertions(+) create mode 100644 test/comparison-tests/projectReferencesNotBuilt/app.ts create mode 100644 test/comparison-tests/projectReferencesNotBuilt/expectedOutput-3.0/bundle.js create mode 100644 test/comparison-tests/projectReferencesNotBuilt/expectedOutput-3.0/bundle.transpiled.js create mode 100644 test/comparison-tests/projectReferencesNotBuilt/expectedOutput-3.0/output.transpiled.txt create mode 100644 test/comparison-tests/projectReferencesNotBuilt/expectedOutput-3.0/output.txt create mode 100644 test/comparison-tests/projectReferencesNotBuilt/lib/index.ts create mode 100644 test/comparison-tests/projectReferencesNotBuilt/lib/tsconfig.json create mode 100644 test/comparison-tests/projectReferencesNotBuilt/tsconfig.json create mode 100644 test/comparison-tests/projectReferencesNotBuilt/webpack.config.js diff --git a/test/comparison-tests/projectReferencesNotBuilt/app.ts b/test/comparison-tests/projectReferencesNotBuilt/app.ts new file mode 100644 index 000000000..a83f2065b --- /dev/null +++ b/test/comparison-tests/projectReferencesNotBuilt/app.ts @@ -0,0 +1,3 @@ +import { lib } from './lib'; + +console.log(lib.one, lib.two, lib.three); diff --git a/test/comparison-tests/projectReferencesNotBuilt/expectedOutput-3.0/bundle.js b/test/comparison-tests/projectReferencesNotBuilt/expectedOutput-3.0/bundle.js new file mode 100644 index 000000000..3b84a9ad4 --- /dev/null +++ b/test/comparison-tests/projectReferencesNotBuilt/expectedOutput-3.0/bundle.js @@ -0,0 +1,112 @@ +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); +/******/ } +/******/ }; +/******/ +/******/ // define __esModule on exports +/******/ __webpack_require__.r = function(exports) { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ +/******/ // create a fake namespace object +/******/ // mode & 1: value is a module id, require it +/******/ // mode & 2: merge all properties of value into the ns +/******/ // mode & 4: return value when already ns object +/******/ // mode & 8|1: behave like require +/******/ __webpack_require__.t = function(value, mode) { +/******/ if(mode & 1) value = __webpack_require__(value); +/******/ if(mode & 8) return value; +/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; +/******/ var ns = Object.create(null); +/******/ __webpack_require__.r(ns); +/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); +/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); +/******/ return ns; +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = "./app.ts"); +/******/ }) +/************************************************************************/ +/******/ ({ + +/***/ "./app.ts": +/*!****************!*\ + !*** ./app.ts ***! + \****************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\nexports.__esModule = true;\nvar lib_1 = __webpack_require__(/*! ./lib */ \"./lib/index.ts\");\nconsole.log(lib_1.lib.one, lib_1.lib.two, lib_1.lib.three);\n\n\n//# sourceURL=webpack:///./app.ts?"); + +/***/ }), + +/***/ "./lib/index.ts": +/*!**********************!*\ + !*** ./lib/index.ts ***! + \**********************/ +/*! no static exports found */ +/***/ (function(module, exports) { + +eval("throw new Error(\"Module build failed: Error: Could not find output JavaScript file for input /projectReferencesNotBuilt/lib/index.ts (looked at /projectReferencesNotBuilt/lib/index.js)./nThe input file is part of a project reference located at /projectReferencesNotBuilt/lib/tsconfig.json, so ts-loader is looking for the project’s pre-built output on disk. Try running `tsc --build` to build project references./n at successLoader (/Users/Andrew/Developer/ts-loader/dist/index.js:45:19)/n at Object.loader (/Users/Andrew/Developer/ts-loader/dist/index.js:21:12)\");\n\n//# sourceURL=webpack:///./lib/index.ts?"); + +/***/ }) + +/******/ }); \ No newline at end of file diff --git a/test/comparison-tests/projectReferencesNotBuilt/expectedOutput-3.0/bundle.transpiled.js b/test/comparison-tests/projectReferencesNotBuilt/expectedOutput-3.0/bundle.transpiled.js new file mode 100644 index 000000000..0f5ee2025 --- /dev/null +++ b/test/comparison-tests/projectReferencesNotBuilt/expectedOutput-3.0/bundle.transpiled.js @@ -0,0 +1,112 @@ +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); +/******/ } +/******/ }; +/******/ +/******/ // define __esModule on exports +/******/ __webpack_require__.r = function(exports) { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ +/******/ // create a fake namespace object +/******/ // mode & 1: value is a module id, require it +/******/ // mode & 2: merge all properties of value into the ns +/******/ // mode & 4: return value when already ns object +/******/ // mode & 8|1: behave like require +/******/ __webpack_require__.t = function(value, mode) { +/******/ if(mode & 1) value = __webpack_require__(value); +/******/ if(mode & 8) return value; +/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; +/******/ var ns = Object.create(null); +/******/ __webpack_require__.r(ns); +/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); +/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); +/******/ return ns; +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = "./app.ts"); +/******/ }) +/************************************************************************/ +/******/ ({ + +/***/ "./app.ts": +/*!****************!*\ + !*** ./app.ts ***! + \****************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\nexports.__esModule = true;\nvar lib_1 = __webpack_require__(/*! ./lib */ \"./lib/index.ts\");\nconsole.log(lib_1.lib.one, lib_1.lib.two, lib_1.lib.three);\n\n\n//# sourceURL=webpack:///./app.ts?"); + +/***/ }), + +/***/ "./lib/index.ts": +/*!**********************!*\ + !*** ./lib/index.ts ***! + \**********************/ +/*! no static exports found */ +/***/ (function(module, exports) { + +eval("throw new Error(\"Module build failed: Error: Could not find output JavaScript file for input /projectReferencesNotBuilt.transpile/lib/index.ts (looked at /projectReferencesNotBuilt.transpile/lib/index.js)./nThe input file is part of a project reference located at /projectReferencesNotBuilt.transpile/lib/tsconfig.json, so ts-loader is looking for the project’s pre-built output on disk. Try running `tsc --build` to build project references./n at successLoader (/Users/Andrew/Developer/ts-loader/dist/index.js:45:19)/n at Object.loader (/Users/Andrew/Developer/ts-loader/dist/index.js:21:12)\");\n\n//# sourceURL=webpack:///./lib/index.ts?"); + +/***/ }) + +/******/ }); \ No newline at end of file diff --git a/test/comparison-tests/projectReferencesNotBuilt/expectedOutput-3.0/output.transpiled.txt b/test/comparison-tests/projectReferencesNotBuilt/expectedOutput-3.0/output.transpiled.txt new file mode 100644 index 000000000..0fc5a3524 --- /dev/null +++ b/test/comparison-tests/projectReferencesNotBuilt/expectedOutput-3.0/output.transpiled.txt @@ -0,0 +1,16 @@ + Asset Size Chunks Chunk Names +bundle.js 4.86 KiB main [emitted] main +Entrypoint main = bundle.js +[./app.ts] 131 bytes {main} [built] [1 error] +[./lib/index.ts] 718 bytes {main} [built] [failed] [1 error] + +ERROR in tsconfig.json +[tsl] ERROR + TS6305: Output file 'lib/index.d.ts' has not been built from source file 'lib/index.ts'. + +ERROR in ./lib/index.ts +Module build failed: Error: Could not find output JavaScript file for input lib/index.ts (looked at lib/index.js). +The input file is part of a project reference located at lib/tsconfig.json, so ts-loader is looking for the project’s pre-built output on disk. Try running `tsc --build` to build project references. + at successLoader (dist/index.js:45:19) + at Object.loader (dist/index.js:21:12) + @ ./app.ts 3:12-28 \ No newline at end of file diff --git a/test/comparison-tests/projectReferencesNotBuilt/expectedOutput-3.0/output.txt b/test/comparison-tests/projectReferencesNotBuilt/expectedOutput-3.0/output.txt new file mode 100644 index 000000000..c4eff07f7 --- /dev/null +++ b/test/comparison-tests/projectReferencesNotBuilt/expectedOutput-3.0/output.txt @@ -0,0 +1,16 @@ + Asset Size Chunks Chunk Names +bundle.js 4.83 KiB main [emitted] main +Entrypoint main = bundle.js +[./app.ts] 131 bytes {main} [built] +[./lib/index.ts] 688 bytes {main} [built] [failed] [1 error] + +ERROR in ./lib/index.ts +Module build failed: Error: Could not find output JavaScript file for input lib/index.ts (looked at lib/index.js). +The input file is part of a project reference located at lib/tsconfig.json, so ts-loader is looking for the project’s pre-built output on disk. Try running `tsc --build` to build project references. + at successLoader (dist/index.js:45:19) + at Object.loader (dist/index.js:21:12) + @ ./app.ts 3:12-28 + +ERROR in tsconfig.json +[tsl] ERROR + TS6305: Output file 'lib/index.d.ts' has not been built from source file 'lib/index.ts'. \ No newline at end of file diff --git a/test/comparison-tests/projectReferencesNotBuilt/lib/index.ts b/test/comparison-tests/projectReferencesNotBuilt/lib/index.ts new file mode 100644 index 000000000..669ca7b3d --- /dev/null +++ b/test/comparison-tests/projectReferencesNotBuilt/lib/index.ts @@ -0,0 +1,5 @@ +export const lib = { + one: 1, + two: 2, + three: 3 +}; diff --git a/test/comparison-tests/projectReferencesNotBuilt/lib/tsconfig.json b/test/comparison-tests/projectReferencesNotBuilt/lib/tsconfig.json new file mode 100644 index 000000000..506c325eb --- /dev/null +++ b/test/comparison-tests/projectReferencesNotBuilt/lib/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "composite": true, + "sourceMap": true + }, + "files": [ + "./index.ts" + ] +} \ No newline at end of file diff --git a/test/comparison-tests/projectReferencesNotBuilt/tsconfig.json b/test/comparison-tests/projectReferencesNotBuilt/tsconfig.json new file mode 100644 index 000000000..03974daa7 --- /dev/null +++ b/test/comparison-tests/projectReferencesNotBuilt/tsconfig.json @@ -0,0 +1,5 @@ +{ + "references": [ + { "path": "./lib" } + ] +} \ No newline at end of file diff --git a/test/comparison-tests/projectReferencesNotBuilt/webpack.config.js b/test/comparison-tests/projectReferencesNotBuilt/webpack.config.js new file mode 100644 index 000000000..be37cdc01 --- /dev/null +++ b/test/comparison-tests/projectReferencesNotBuilt/webpack.config.js @@ -0,0 +1,19 @@ +var path = require('path') + +module.exports = { + mode: 'development', + entry: './app.ts', + output: { + filename: 'bundle.js' + }, + resolve: { + extensions: ['.ts', '.js'] + }, + module: { + rules: [ + { test: /\.ts$/, loader: 'ts-loader' } + ] + } +} + + From 1e765b2ce9a0b341f60505c91829c9007a275495 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Mon, 20 Aug 2018 11:13:38 -0700 Subject: [PATCH 13/42] Only warn once per project per compilation about source maps --- src/after-compile.ts | 1 + src/index.ts | 20 +++++++------- src/interfaces.ts | 6 +++++ src/utils.ts | 27 ++++++++++++++++++- .../expectedOutput-3.0/bundle.js | 14 +++++++++- .../expectedOutput-3.0/output.txt | 7 ++--- .../projectReferencesNoSourceMap/lib/foo.d.ts | 1 + .../projectReferencesNoSourceMap/lib/foo.js | 3 +++ .../projectReferencesNoSourceMap/lib/foo.ts | 1 + .../lib/index.d.ts | 1 + .../projectReferencesNoSourceMap/lib/index.js | 4 ++- .../projectReferencesNoSourceMap/lib/index.ts | 5 +++- .../lib/tsconfig.json | 3 ++- 13 files changed, 75 insertions(+), 18 deletions(-) create mode 100644 test/comparison-tests/projectReferencesNoSourceMap/lib/foo.d.ts create mode 100644 test/comparison-tests/projectReferencesNoSourceMap/lib/foo.js create mode 100644 test/comparison-tests/projectReferencesNoSourceMap/lib/foo.ts diff --git a/src/after-compile.ts b/src/after-compile.ts index 3c0f6a20e..b9e6e1c8a 100644 --- a/src/after-compile.ts +++ b/src/after-compile.ts @@ -65,6 +65,7 @@ export function makeAfterCompile( instance.filesWithErrors = filesWithErrors; instance.modifiedFiles = null; + instance.projectsMissingSourceMaps = new Set(); callback(); }; diff --git a/src/index.ts b/src/index.ts index 4aff843df..bd4cf0f92 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,7 +7,8 @@ import { appendSuffixesIfMatch, arrify, formatErrors, - getProjectReferenceForFile + getProjectReferenceForFile, + validateSourceMapOncePerProject } from './utils'; import * as constants from './constants'; import { @@ -83,7 +84,7 @@ function successLoader( filePath, referencedProject.commandLine ); - const mapFileName = jsFileName + '.map'; + if (!instance.compiler.sys.fileExists(jsFileName)) { throw new Error( `Could not find output JavaScript file for input ${filePath} ` + @@ -94,15 +95,14 @@ function successLoader( ); } - if (!instance.compiler.sys.fileExists(mapFileName)) { - loader.emitWarning( - 'Could not find source map file for referenced project output ' + - `${jsFileName}. Ensure the 'sourceMap' compiler option is enabled ` + - `in ${referencedProject.sourceFile.fileName} to ensure Webpack ` + - 'can map project references to the appropriate source files.' - ); - } + validateSourceMapOncePerProject( + instance, + loader, + jsFileName, + referencedProject + ); + const mapFileName = jsFileName + '.map'; const outputText = instance.compiler.sys.readFile(jsFileName); const sourceMapText = instance.compiler.sys.readFile(mapFileName); makeSourceMapAndFinish( diff --git a/src/interfaces.ts b/src/interfaces.ts index de04985dc..fa3bc4094 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -239,6 +239,12 @@ export interface TSInstance { * contains the modified files - cleared each time after-compile is called */ modifiedFiles?: TSFiles | null; + /** + * Paths to project references that are missing source maps. + * Cleared each time after-compile is called. Used to dedupe + * warnings about source maps during a single compilation. + */ + projectsMissingSourceMaps?: Set; languageService?: typescript.LanguageService | null; version?: number; dependencyGraph: DependencyGraph; diff --git a/src/utils.ts b/src/utils.ts index f54549109..66a52e9e5 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -13,7 +13,8 @@ import { WebpackError, WebpackModule, ErrorInfo, - TSInstance + TSInstance, + Webpack } from './interfaces'; /** @@ -279,3 +280,27 @@ export function getProjectReferenceForFile( return; } + +export function validateSourceMapOncePerProject( + instance: TSInstance, + loader: Webpack, + jsFileName: string, + project: typescript.ResolvedProjectReference +) { + const { projectsMissingSourceMaps = new Set() } = instance; + if (!projectsMissingSourceMaps.has(project.sourceFile.fileName)) { + instance.projectsMissingSourceMaps = projectsMissingSourceMaps; + projectsMissingSourceMaps.add(project.sourceFile.fileName); + const mapFileName = jsFileName + '.map'; + if (!instance.compiler.sys.fileExists(mapFileName)) { + loader.emitWarning( + new Error( + 'Could not find source map file for referenced project output ' + + `${jsFileName}. Ensure the 'sourceMap' compiler option is enabled ` + + `in ${project.sourceFile.fileName} to ensure Webpack ` + + 'can map project references to the appropriate source files.' + ) + ); + } + } +} diff --git a/test/comparison-tests/projectReferencesNoSourceMap/expectedOutput-3.0/bundle.js b/test/comparison-tests/projectReferencesNoSourceMap/expectedOutput-3.0/bundle.js index 715c05596..7c5f9bb80 100644 --- a/test/comparison-tests/projectReferencesNoSourceMap/expectedOutput-3.0/bundle.js +++ b/test/comparison-tests/projectReferencesNoSourceMap/expectedOutput-3.0/bundle.js @@ -98,6 +98,18 @@ eval("\nexports.__esModule = true;\nvar lib_1 = __webpack_require__(/*! ./lib */ /***/ }), +/***/ "./lib/foo.ts": +/*!********************!*\ + !*** ./lib/foo.ts ***! + \********************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\nexports.__esModule = true;\nexports.foo = 'foo';\n\n\n//# sourceURL=webpack:///./lib/foo.ts?"); + +/***/ }), + /***/ "./lib/index.ts": /*!**********************!*\ !*** ./lib/index.ts ***! @@ -106,7 +118,7 @@ eval("\nexports.__esModule = true;\nvar lib_1 = __webpack_require__(/*! ./lib */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -eval("\nexports.__esModule = true;\nexports.lib = {\n one: 1,\n two: 2,\n three: 3\n // I am adding this comment here by hand to ensure\n // Webpack is using the JS output for project references\n};\n\n\n//# sourceURL=webpack:///./lib/index.ts?"); +eval("\nexports.__esModule = true;\nvar foo_1 = __webpack_require__(/*! ./foo */ \"./lib/foo.ts\");\nexports.lib = {\n one: 1,\n two: 2,\n three: 3,\n foo: foo_1.foo\n // I am adding this comment here by hand to ensure\n // Webpack is using the JS output for project references\n};\n\n\n//# sourceURL=webpack:///./lib/index.ts?"); /***/ }) diff --git a/test/comparison-tests/projectReferencesNoSourceMap/expectedOutput-3.0/output.txt b/test/comparison-tests/projectReferencesNoSourceMap/expectedOutput-3.0/output.txt index 52f9b2c89..5e3491e3d 100644 --- a/test/comparison-tests/projectReferencesNoSourceMap/expectedOutput-3.0/output.txt +++ b/test/comparison-tests/projectReferencesNoSourceMap/expectedOutput-3.0/output.txt @@ -1,8 +1,9 @@ - Asset Size Chunks Chunk Names -bundle.js 4.4 KiB main [emitted] main + Asset Size Chunks Chunk Names +bundle.js 4.79 KiB main [emitted] main Entrypoint main = bundle.js [./app.ts] 131 bytes {main} [built] -[./lib/index.ts] 213 bytes {main} [built] [1 warning] +[./lib/foo.ts] 62 bytes {main} [built] +[./lib/index.ts] 263 bytes {main} [built] [1 warning] WARNING in ./lib/index.ts Could not find source map file for referenced project output lib/index.js. Ensure the 'sourceMap' compiler option is enabled in lib/tsconfig.json to ensure Webpack can map project references to the appropriate source files. diff --git a/test/comparison-tests/projectReferencesNoSourceMap/lib/foo.d.ts b/test/comparison-tests/projectReferencesNoSourceMap/lib/foo.d.ts new file mode 100644 index 000000000..07265a895 --- /dev/null +++ b/test/comparison-tests/projectReferencesNoSourceMap/lib/foo.d.ts @@ -0,0 +1 @@ +export declare const foo = "foo"; diff --git a/test/comparison-tests/projectReferencesNoSourceMap/lib/foo.js b/test/comparison-tests/projectReferencesNoSourceMap/lib/foo.js new file mode 100644 index 000000000..9cf21a23a --- /dev/null +++ b/test/comparison-tests/projectReferencesNoSourceMap/lib/foo.js @@ -0,0 +1,3 @@ +"use strict"; +exports.__esModule = true; +exports.foo = 'foo'; diff --git a/test/comparison-tests/projectReferencesNoSourceMap/lib/foo.ts b/test/comparison-tests/projectReferencesNoSourceMap/lib/foo.ts new file mode 100644 index 000000000..3329a7d97 --- /dev/null +++ b/test/comparison-tests/projectReferencesNoSourceMap/lib/foo.ts @@ -0,0 +1 @@ +export const foo = 'foo'; diff --git a/test/comparison-tests/projectReferencesNoSourceMap/lib/index.d.ts b/test/comparison-tests/projectReferencesNoSourceMap/lib/index.d.ts index 73d752279..b05ab0fd1 100644 --- a/test/comparison-tests/projectReferencesNoSourceMap/lib/index.d.ts +++ b/test/comparison-tests/projectReferencesNoSourceMap/lib/index.d.ts @@ -2,4 +2,5 @@ export declare const lib: { one: number; two: number; three: number; + foo: string; }; diff --git a/test/comparison-tests/projectReferencesNoSourceMap/lib/index.js b/test/comparison-tests/projectReferencesNoSourceMap/lib/index.js index 2bcc995d9..0b08652e8 100644 --- a/test/comparison-tests/projectReferencesNoSourceMap/lib/index.js +++ b/test/comparison-tests/projectReferencesNoSourceMap/lib/index.js @@ -1,9 +1,11 @@ "use strict"; exports.__esModule = true; +var foo_1 = require("./foo"); exports.lib = { one: 1, two: 2, - three: 3 + three: 3, + foo: foo_1.foo // I am adding this comment here by hand to ensure // Webpack is using the JS output for project references }; diff --git a/test/comparison-tests/projectReferencesNoSourceMap/lib/index.ts b/test/comparison-tests/projectReferencesNoSourceMap/lib/index.ts index 669ca7b3d..802a90a9b 100644 --- a/test/comparison-tests/projectReferencesNoSourceMap/lib/index.ts +++ b/test/comparison-tests/projectReferencesNoSourceMap/lib/index.ts @@ -1,5 +1,8 @@ +import { foo } from './foo'; + export const lib = { one: 1, two: 2, - three: 3 + three: 3, + foo }; diff --git a/test/comparison-tests/projectReferencesNoSourceMap/lib/tsconfig.json b/test/comparison-tests/projectReferencesNoSourceMap/lib/tsconfig.json index 937062a9d..396dadea4 100644 --- a/test/comparison-tests/projectReferencesNoSourceMap/lib/tsconfig.json +++ b/test/comparison-tests/projectReferencesNoSourceMap/lib/tsconfig.json @@ -3,6 +3,7 @@ "composite": true }, "files": [ - "./index.ts" + "./index.ts", + "./foo.ts" ] } \ No newline at end of file From d262970d60871ee41e95a443b9edadd0506a688a Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Mon, 20 Aug 2018 12:44:14 -0700 Subject: [PATCH 14/42] Make comments more accurate --- src/after-compile.ts | 3 ++- src/instances.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/after-compile.ts b/src/after-compile.ts index b9e6e1c8a..304016c6a 100644 --- a/src/after-compile.ts +++ b/src/after-compile.ts @@ -188,7 +188,8 @@ function provideErrorsToWebpack( const sourceFile = program && program.getSourceFile(filePath); - // If the source file is undefined, that probably means it’s actually part of a project reference. + // 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) { diff --git a/src/instances.ts b/src/instances.ts index bb8d87ee5..625ab99e7 100644 --- a/src/instances.ts +++ b/src/instances.ts @@ -300,7 +300,7 @@ export function getEmitOutput(instance: TSInstance, filePath: string) { writeByteOrderMark: boolean ) => outputFiles.push({ name: fileName, writeByteOrderMark, text }); const sourceFile = program.getSourceFile(filePath); - // The source file will be undefined if it’s part of a project reference + // The source file will be undefined if it’s part of an unbuilt project reference if (sourceFile || !isUsingProjectReferences(instance)) { program.emit( sourceFile, From 700e9674be20c559c0e6c6a877738093f40d0d39 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Sat, 1 Sep 2018 09:43:59 -0700 Subject: [PATCH 15/42] Implement getOutputJavaScriptFileName --- src/constants.ts | 3 +++ src/index.ts | 8 +++----- src/utils.ts | 23 +++++++++++++++++++++++ 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/constants.ts b/src/constants.ts index 80ca922b0..71f565d08 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -11,6 +11,8 @@ 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; @@ -18,4 +20,5 @@ 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; diff --git a/src/index.ts b/src/index.ts index bd4cf0f92..1e2e01da9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,7 +8,8 @@ import { arrify, formatErrors, getProjectReferenceForFile, - validateSourceMapOncePerProject + validateSourceMapOncePerProject, + getOutputJavaScriptFileName } from './utils'; import * as constants from './constants'; import { @@ -80,10 +81,7 @@ function successLoader( ); } - const jsFileName = instance.compiler.getOutputJavaScriptFileName( - filePath, - referencedProject.commandLine - ); + const jsFileName = getOutputJavaScriptFileName(filePath, referencedProject); if (!instance.compiler.sys.fileExists(jsFileName)) { throw new Error( diff --git a/src/utils.ts b/src/utils.ts index 66a52e9e5..4cfba39ee 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -304,3 +304,26 @@ export function validateSourceMapOncePerProject( } } } + +// Adapted from https://github.com/Microsoft/TypeScript/blob/45101491c0b077c509b25830ef0ee5f85b293754/src/compiler/tsbuild.ts#L305 +export function getOutputJavaScriptFileName( + inputFileName: string, + projectReference: typescript.ResolvedProjectReference +) { + const relativePath = path.relative( + projectReference.sourceFile.fileName, + inputFileName + ); + const outputPath = path.resolve( + projectReference.commandLine.options.outDir || + projectReference.sourceFile.fileName, + relativePath + ); + const newExtension = constants.jsonRegex.test(inputFileName) + ? '.json' + : constants.tsxRegex.test(inputFileName) && + projectReference.commandLine.options.jsx === typescript.JsxEmit.Preserve + ? '.jsx' + : '.js'; + return outputPath.replace(constants.extensionRegex, newExtension); +} From 741bf9be2905299ec95bd23caf15135eaecfbe35 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Sat, 1 Sep 2018 10:04:10 -0700 Subject: [PATCH 16/42] Add test for project references with outDir (and fix implementation) --- src/utils.ts | 12 +- .../projectReferencesOutDir/app.ts | 3 + .../expectedOutput-3.0/bundle.js | 113 ++++++++++++++++++ .../expectedOutput-3.0/output.txt | 5 + .../projectReferencesOutDir/lib/index.ts | 5 + .../projectReferencesOutDir/lib/tsconfig.json | 10 ++ .../projectReferencesOutDir/tsconfig.json | 5 + .../projectReferencesOutDir/webpack.config.js | 19 +++ 8 files changed, 165 insertions(+), 7 deletions(-) create mode 100644 test/comparison-tests/projectReferencesOutDir/app.ts create mode 100644 test/comparison-tests/projectReferencesOutDir/expectedOutput-3.0/bundle.js create mode 100644 test/comparison-tests/projectReferencesOutDir/expectedOutput-3.0/output.txt create mode 100644 test/comparison-tests/projectReferencesOutDir/lib/index.ts create mode 100644 test/comparison-tests/projectReferencesOutDir/lib/tsconfig.json create mode 100644 test/comparison-tests/projectReferencesOutDir/tsconfig.json create mode 100644 test/comparison-tests/projectReferencesOutDir/webpack.config.js diff --git a/src/utils.ts b/src/utils.ts index 4cfba39ee..d5a390762 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -310,19 +310,17 @@ export function getOutputJavaScriptFileName( inputFileName: string, projectReference: typescript.ResolvedProjectReference ) { - const relativePath = path.relative( - projectReference.sourceFile.fileName, - inputFileName - ); + const { options } = projectReference.commandLine; + const projectDirectory = path.dirname(projectReference.sourceFile.fileName); + const relativePath = path.relative(projectDirectory, inputFileName); const outputPath = path.resolve( - projectReference.commandLine.options.outDir || - projectReference.sourceFile.fileName, + options.outDir || projectDirectory, relativePath ); const newExtension = constants.jsonRegex.test(inputFileName) ? '.json' : constants.tsxRegex.test(inputFileName) && - projectReference.commandLine.options.jsx === typescript.JsxEmit.Preserve + options.jsx === typescript.JsxEmit.Preserve ? '.jsx' : '.js'; return outputPath.replace(constants.extensionRegex, newExtension); diff --git a/test/comparison-tests/projectReferencesOutDir/app.ts b/test/comparison-tests/projectReferencesOutDir/app.ts new file mode 100644 index 000000000..a83f2065b --- /dev/null +++ b/test/comparison-tests/projectReferencesOutDir/app.ts @@ -0,0 +1,3 @@ +import { lib } from './lib'; + +console.log(lib.one, lib.two, lib.three); diff --git a/test/comparison-tests/projectReferencesOutDir/expectedOutput-3.0/bundle.js b/test/comparison-tests/projectReferencesOutDir/expectedOutput-3.0/bundle.js new file mode 100644 index 000000000..0f1d8bc9b --- /dev/null +++ b/test/comparison-tests/projectReferencesOutDir/expectedOutput-3.0/bundle.js @@ -0,0 +1,113 @@ +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); +/******/ } +/******/ }; +/******/ +/******/ // define __esModule on exports +/******/ __webpack_require__.r = function(exports) { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ +/******/ // create a fake namespace object +/******/ // mode & 1: value is a module id, require it +/******/ // mode & 2: merge all properties of value into the ns +/******/ // mode & 4: return value when already ns object +/******/ // mode & 8|1: behave like require +/******/ __webpack_require__.t = function(value, mode) { +/******/ if(mode & 1) value = __webpack_require__(value); +/******/ if(mode & 8) return value; +/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; +/******/ var ns = Object.create(null); +/******/ __webpack_require__.r(ns); +/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); +/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); +/******/ return ns; +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = "./app.ts"); +/******/ }) +/************************************************************************/ +/******/ ({ + +/***/ "./app.ts": +/*!****************!*\ + !*** ./app.ts ***! + \****************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\nexports.__esModule = true;\nvar lib_1 = __webpack_require__(/*! ./lib */ \"./lib/index.ts\");\nconsole.log(lib_1.lib.one, lib_1.lib.two, lib_1.lib.three);\n\n\n//# sourceURL=webpack:///./app.ts?"); + +/***/ }), + +/***/ "./lib/index.ts": +/*!**********************!*\ + !*** ./lib/index.ts ***! + \**********************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\nexports.__esModule = true;\nexports.lib = {\n one: 1,\n two: 2,\n three: 3\n};\n\n\n//# sourceURL=webpack:///./lib/index.ts?"); + +/***/ }) + +/******/ }); \ No newline at end of file diff --git a/test/comparison-tests/projectReferencesOutDir/expectedOutput-3.0/output.txt b/test/comparison-tests/projectReferencesOutDir/expectedOutput-3.0/output.txt new file mode 100644 index 000000000..5d7048512 --- /dev/null +++ b/test/comparison-tests/projectReferencesOutDir/expectedOutput-3.0/output.txt @@ -0,0 +1,5 @@ + Asset Size Chunks Chunk Names +bundle.js 4.28 KiB main [emitted] main +Entrypoint main = bundle.js +[./app.ts] 131 bytes {main} [built] +[./lib/index.ts] 97 bytes {main} [built] \ No newline at end of file diff --git a/test/comparison-tests/projectReferencesOutDir/lib/index.ts b/test/comparison-tests/projectReferencesOutDir/lib/index.ts new file mode 100644 index 000000000..669ca7b3d --- /dev/null +++ b/test/comparison-tests/projectReferencesOutDir/lib/index.ts @@ -0,0 +1,5 @@ +export const lib = { + one: 1, + two: 2, + three: 3 +}; diff --git a/test/comparison-tests/projectReferencesOutDir/lib/tsconfig.json b/test/comparison-tests/projectReferencesOutDir/lib/tsconfig.json new file mode 100644 index 000000000..a5356d522 --- /dev/null +++ b/test/comparison-tests/projectReferencesOutDir/lib/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "sourceMap": true, + "outDir": "./dist" + }, + "files": [ + "./index.ts" + ] +} \ No newline at end of file diff --git a/test/comparison-tests/projectReferencesOutDir/tsconfig.json b/test/comparison-tests/projectReferencesOutDir/tsconfig.json new file mode 100644 index 000000000..03974daa7 --- /dev/null +++ b/test/comparison-tests/projectReferencesOutDir/tsconfig.json @@ -0,0 +1,5 @@ +{ + "references": [ + { "path": "./lib" } + ] +} \ No newline at end of file diff --git a/test/comparison-tests/projectReferencesOutDir/webpack.config.js b/test/comparison-tests/projectReferencesOutDir/webpack.config.js new file mode 100644 index 000000000..be37cdc01 --- /dev/null +++ b/test/comparison-tests/projectReferencesOutDir/webpack.config.js @@ -0,0 +1,19 @@ +var path = require('path') + +module.exports = { + mode: 'development', + entry: './app.ts', + output: { + filename: 'bundle.js' + }, + resolve: { + extensions: ['.ts', '.js'] + }, + module: { + rules: [ + { test: /\.ts$/, loader: 'ts-loader' } + ] + } +} + + From f36a841ab90a7299572ffa8f49c85b873cb3e095 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Sat, 1 Sep 2018 10:13:10 -0700 Subject: [PATCH 17/42] Be safer in getProjectReferenceForFile --- src/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils.ts b/src/utils.ts index d5a390762..49eecaa34 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -274,7 +274,7 @@ export function getProjectReferenceForFile( program && program .getProjectReferences()! - .find(ref => ref!.commandLine.fileNames.indexOf(filePath) > -1) + .find(ref => ref && ref.commandLine.fileNames.indexOf(filePath) > -1) ); } From 05ab35d7d34effb0f3bc9fb8d46d26aef230fa32 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Sat, 1 Sep 2018 10:57:13 -0700 Subject: [PATCH 18/42] Fix type error --- src/utils.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/utils.ts b/src/utils.ts index 49eecaa34..048b7b3cc 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -274,7 +274,10 @@ export function getProjectReferenceForFile( program && program .getProjectReferences()! - .find(ref => ref && ref.commandLine.fileNames.indexOf(filePath) > -1) + .find( + ref => + (ref && ref.commandLine.fileNames.indexOf(filePath) > -1) || false + ) ); } From 565dab2b451e3742042b6cd7b3cc12ff541d7977 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Sun, 2 Sep 2018 17:10:07 -0700 Subject: [PATCH 19/42] Add JS files instead of TS files to loader dependencies for project refs --- src/index.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/index.ts b/src/index.ts index 1e2e01da9..bac19e948 100644 --- a/src/index.ts +++ b/src/index.ts @@ -93,6 +93,9 @@ function successLoader( ); } + loader.clearDependencies(); + loader.addDependency(jsFileName); + validateSourceMapOncePerProject( instance, loader, @@ -163,7 +166,7 @@ function makeSourceMapAndFinish( ); // _module.meta is not available inside happypack - if (!options.happyPackMode) { + if (!options.happyPackMode && loader._module.buildMeta) { // Make sure webpack is aware that even though the emitted JavaScript may be the same as // a previously cached version the TypeScript may be different and therefore should be // treated as new @@ -376,7 +379,16 @@ function getEmit( const additionalDependencies = fileDependencies === undefined ? [] - : fileDependencies.map(module => module.originalFileName); + : fileDependencies.map(({ resolvedFileName, originalFileName }) => { + const projectReference = getProjectReferenceForFile( + resolvedFileName, + instance + ); + return projectReference + ? getOutputJavaScriptFileName(resolvedFileName, projectReference) + : originalFileName; + }); + if (additionalDependencies) { additionalDependencies.forEach(addDependency); } From a224efcb50c4288b0fde75354b65063df64f5ac6 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Sun, 2 Sep 2018 17:15:18 -0700 Subject: [PATCH 20/42] Add comments --- src/index.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/index.ts b/src/index.ts index bac19e948..031ab150c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -93,6 +93,9 @@ function successLoader( ); } + // Since the output JS file is being read from disk instead of using the + // input TS file, we need to tell the loader that the compilation doesn’t + // actually depend on the current file, but depends on the JS file instead. loader.clearDependencies(); loader.addDependency(jsFileName); @@ -384,6 +387,8 @@ function getEmit( resolvedFileName, instance ); + // In the case of dependencies that are part of a project reference, + // the real dependency that webpack should watch is the JS output file. return projectReference ? getOutputJavaScriptFileName(resolvedFileName, projectReference) : originalFileName; From 80528ead58c82f9fe40fa64d78d1ed4ece6ebbb4 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Sun, 2 Sep 2018 17:44:20 -0700 Subject: [PATCH 21/42] Cache project reference info in files map --- src/index.ts | 20 +++++++++++++------ src/interfaces.ts | 4 ++++ src/utils.ts | 51 +++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 67 insertions(+), 8 deletions(-) diff --git a/src/index.ts b/src/index.ts index 031ab150c..129bdb8e5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,9 +7,9 @@ import { appendSuffixesIfMatch, arrify, formatErrors, - getProjectReferenceForFile, validateSourceMapOncePerProject, - getOutputJavaScriptFileName + getAndCacheProjectReference, + getAndCacheOutputJSFileName } from './utils'; import * as constants from './constants'; import { @@ -70,7 +70,7 @@ function successLoader( : rawFilePath; const fileVersion = updateFileInCache(filePath, contents, instance); - const referencedProject = getProjectReferenceForFile(filePath, instance); + const referencedProject = getAndCacheProjectReference(filePath, instance); if (referencedProject) { if (referencedProject.commandLine.options.outFile) { throw new Error( @@ -81,7 +81,11 @@ function successLoader( ); } - const jsFileName = getOutputJavaScriptFileName(filePath, referencedProject); + const jsFileName = getAndCacheOutputJSFileName( + filePath, + referencedProject, + instance + ); if (!instance.compiler.sys.fileExists(jsFileName)) { throw new Error( @@ -383,14 +387,18 @@ function getEmit( fileDependencies === undefined ? [] : fileDependencies.map(({ resolvedFileName, originalFileName }) => { - const projectReference = getProjectReferenceForFile( + const projectReference = getAndCacheProjectReference( resolvedFileName, instance ); // In the case of dependencies that are part of a project reference, // the real dependency that webpack should watch is the JS output file. return projectReference - ? getOutputJavaScriptFileName(resolvedFileName, projectReference) + ? getAndCacheOutputJSFileName( + resolvedFileName, + projectReference, + instance + ) : originalFileName; }); diff --git a/src/interfaces.ts b/src/interfaces.ts index fa3bc4094..c17b766fd 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -313,6 +313,10 @@ export interface LoaderOptions { export interface TSFile { text?: string; version: number; + projectReference?: { + project: typescript.ResolvedProjectReference; + outputFileName?: string; + }; } /** where key is filepath */ diff --git a/src/utils.ts b/src/utils.ts index 048b7b3cc..c4bfe3ce6 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -264,10 +264,28 @@ export function isUsingProjectReferences(instance: TSInstance) { return false; } -export function getProjectReferenceForFile( +/** + * Gets the project reference for a file from the cache if it exists, + * or gets it from TypeScript and caches it otherwise. + */ +export function getAndCacheProjectReference( filePath: string, instance: TSInstance ) { + const file = instance.files.get(filePath); + if (file && file.projectReference) { + return file.projectReference.project; + } + + const projectReference = getProjectReferenceForFile(filePath, instance); + if (file && projectReference) { + file.projectReference = { project: projectReference }; + } + + return projectReference; +} + +function getProjectReferenceForFile(filePath: string, instance: TSInstance) { if (isUsingProjectReferences(instance)) { const program = ensureProgram(instance); return ( @@ -308,8 +326,37 @@ export function validateSourceMapOncePerProject( } } +/** + * Gets the output JS file path for an input file governed by a composite project. + * Pulls from the cache if it exists; computes and caches the result otherwise. + */ +export function getAndCacheOutputJSFileName( + inputFileName: string, + projectReference: typescript.ResolvedProjectReference, + instance: TSInstance +) { + const file = instance.files.get(inputFileName); + if (file && file.projectReference && file.projectReference.outputFileName) { + return file.projectReference.outputFileName; + } + + const outputFileName = getOutputJavaScriptFileName( + inputFileName, + projectReference + ); + + if (file) { + file.projectReference = file.projectReference || { + project: projectReference + }; + file.projectReference.outputFileName = outputFileName; + } + + return outputFileName; +} + // Adapted from https://github.com/Microsoft/TypeScript/blob/45101491c0b077c509b25830ef0ee5f85b293754/src/compiler/tsbuild.ts#L305 -export function getOutputJavaScriptFileName( +function getOutputJavaScriptFileName( inputFileName: string, projectReference: typescript.ResolvedProjectReference ) { From 47515ed9af80832ef13d708fb27caae15e405243 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Sun, 2 Sep 2018 18:07:53 -0700 Subject: [PATCH 22/42] Update test for updated stack trace --- .../expectedOutput-3.0/bundle.js | 2 +- .../expectedOutput-3.0/output.txt | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/test/comparison-tests/nodeModulesMeaningfulErrorWhenImportingTs/expectedOutput-3.0/bundle.js b/test/comparison-tests/nodeModulesMeaningfulErrorWhenImportingTs/expectedOutput-3.0/bundle.js index d9b92eac5..27dd88a96 100644 --- a/test/comparison-tests/nodeModulesMeaningfulErrorWhenImportingTs/expectedOutput-3.0/bundle.js +++ b/test/comparison-tests/nodeModulesMeaningfulErrorWhenImportingTs/expectedOutput-3.0/bundle.js @@ -105,7 +105,7 @@ eval("\nexports.__esModule = true;\nvar a = __webpack_require__(/*! a */ \"./nod /*! no static exports found */ /***/ (function(module, exports) { -eval("throw new Error(\"Module build failed: Error: Typescript emitted no output for C://source//ts-loader//.test//nodeModulesMeaningfulErrorWhenImportingTs//node_modules//a//index.ts. By default, ts-loader will not compile .ts files in node_modules./nYou should not need to recompile .ts files there, but if you really want to, use the allowTsInNodeModules option./nSee: https://github.com/Microsoft/TypeScript/issues/12358/n at successLoader (C://source//ts-loader//dist//index.js:41:15)/n at Object.loader (C://source//ts-loader//dist//index.js:21:12)\");\n\n//# sourceURL=webpack:///./node_modules/a/index.ts?"); +eval("throw new Error(\"Module build failed: Error: TypeScript emitted no output for C://source//ts-loader//.test//nodeModulesMeaningfulErrorWhenImportingTs//node_modules//a//index.ts. By default, ts-loader will not compile .ts files in node_modules./nYou should not need to recompile .ts files there, but if you really want to, use the allowTsInNodeModules option./nSee: https://github.com/Microsoft/TypeScript/issues/12358/n at makeSourceMapAndFinish (C://source//ts-loader//dist//index.js:71:15)/n at successLoader (C://source//ts-loader//dist//index.js:61:9)/n at Object.loader (C://source//ts-loader//dist//index.js:21:12)\");\n\n//# sourceURL=webpack:///./node_modules/a/index.ts?"); /***/ }) diff --git a/test/comparison-tests/nodeModulesMeaningfulErrorWhenImportingTs/expectedOutput-3.0/output.txt b/test/comparison-tests/nodeModulesMeaningfulErrorWhenImportingTs/expectedOutput-3.0/output.txt index 3071a7906..a4dcc30fc 100644 --- a/test/comparison-tests/nodeModulesMeaningfulErrorWhenImportingTs/expectedOutput-3.0/output.txt +++ b/test/comparison-tests/nodeModulesMeaningfulErrorWhenImportingTs/expectedOutput-3.0/output.txt @@ -1,13 +1,14 @@ Asset Size Chunks Chunk Names -bundle.js 4.75 KiB main [emitted] main +bundle.js 4.83 KiB main [emitted] main Entrypoint main = bundle.js [./app.ts] 79 bytes {main} [built] -[./node_modules/a/index.ts] 556 bytes {main} [built] [failed] [1 error] +[./node_modules/a/index.ts] 669 bytes {main} [built] [failed] [1 error] ERROR in ./node_modules/a/index.ts -Module build failed: Error: Typescript emitted no output for node_modules\a\index.ts. By default, ts-loader will not compile .ts files in node_modules. +Module build failed: Error: TypeScript emitted no output for node_modules\a\index.ts. By default, ts-loader will not compile .ts files in node_modules. You should not need to recompile .ts files there, but if you really want to, use the allowTsInNodeModules option. See: https://github.com/Microsoft/TypeScript/issues/12358 - at successLoader (dist\index.js:41:15) + at makeSourceMapAndFinish (dist\index.js:71:15) + at successLoader (dist\index.js:61:9) at Object.loader (dist\index.js:21:12) @ ./app.ts 3:8-20 \ No newline at end of file From 451c462439ef2bb447921f32c74b0e451a0baac1 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Sun, 2 Sep 2018 18:23:55 -0700 Subject: [PATCH 23/42] =?UTF-8?q?Don=E2=80=99t=20repeatedly=20check=20file?= =?UTF-8?q?s=20that=20don=E2=80=99t=20have=20project=20refs=20for=20projec?= =?UTF-8?q?t=20refs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/interfaces.ts | 6 +++++- src/utils.ts | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/interfaces.ts b/src/interfaces.ts index c17b766fd..097c869e2 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -314,7 +314,11 @@ export interface TSFile { text?: string; version: number; projectReference?: { - project: typescript.ResolvedProjectReference; + /** + * Undefined here means we’ve already checked and confirmed there is no + * project reference for the file. Don’t bother checking again. + */ + project?: typescript.ResolvedProjectReference; outputFileName?: string; }; } diff --git a/src/utils.ts b/src/utils.ts index c4bfe3ce6..b0cca5ee7 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -278,7 +278,7 @@ export function getAndCacheProjectReference( } const projectReference = getProjectReferenceForFile(filePath, instance); - if (file && projectReference) { + if (file) { file.projectReference = { project: projectReference }; } From 266a4ba1225de4dcc3655ba05a678ef068530e6b Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Sun, 2 Sep 2018 18:59:25 -0700 Subject: [PATCH 24/42] =?UTF-8?q?Rename=20test=20project=20reference=20out?= =?UTF-8?q?put=20to=20a=20folder=20that=E2=80=99s=20not=20gitignored=20?= =?UTF-8?q?=F0=9F=98=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../projectReferencesOutDir/lib/out/index.d.ts | 5 +++++ .../projectReferencesOutDir/lib/out/index.js | 8 ++++++++ .../projectReferencesOutDir/lib/tsconfig.json | 2 +- 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 test/comparison-tests/projectReferencesOutDir/lib/out/index.d.ts create mode 100644 test/comparison-tests/projectReferencesOutDir/lib/out/index.js diff --git a/test/comparison-tests/projectReferencesOutDir/lib/out/index.d.ts b/test/comparison-tests/projectReferencesOutDir/lib/out/index.d.ts new file mode 100644 index 000000000..73d752279 --- /dev/null +++ b/test/comparison-tests/projectReferencesOutDir/lib/out/index.d.ts @@ -0,0 +1,5 @@ +export declare const lib: { + one: number; + two: number; + three: number; +}; diff --git a/test/comparison-tests/projectReferencesOutDir/lib/out/index.js b/test/comparison-tests/projectReferencesOutDir/lib/out/index.js new file mode 100644 index 000000000..3caf10d82 --- /dev/null +++ b/test/comparison-tests/projectReferencesOutDir/lib/out/index.js @@ -0,0 +1,8 @@ +"use strict"; +exports.__esModule = true; +exports.lib = { + one: 1, + two: 2, + three: 3 +}; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/test/comparison-tests/projectReferencesOutDir/lib/tsconfig.json b/test/comparison-tests/projectReferencesOutDir/lib/tsconfig.json index a5356d522..2ca71a202 100644 --- a/test/comparison-tests/projectReferencesOutDir/lib/tsconfig.json +++ b/test/comparison-tests/projectReferencesOutDir/lib/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "composite": true, "sourceMap": true, - "outDir": "./dist" + "outDir": "./out" }, "files": [ "./index.ts" From d532f78f9f9e7d40c5e04b38edf4586e152c9183 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Sun, 16 Sep 2018 13:31:57 -0700 Subject: [PATCH 25/42] =?UTF-8?q?Don=E2=80=99t=20ignore=20map=20files=20im?= =?UTF-8?q?portant=20to=20projectReferences=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/comparison-tests/projectReferences/lib/.gitignore | 1 + test/comparison-tests/projectReferences/lib/index.js.map | 1 + test/comparison-tests/projectReferencesOutDir/lib/.gitignore | 1 + .../projectReferencesOutDir/lib/out/index.js.map | 1 + 4 files changed, 4 insertions(+) create mode 100644 test/comparison-tests/projectReferences/lib/.gitignore create mode 100644 test/comparison-tests/projectReferences/lib/index.js.map create mode 100644 test/comparison-tests/projectReferencesOutDir/lib/.gitignore create mode 100644 test/comparison-tests/projectReferencesOutDir/lib/out/index.js.map diff --git a/test/comparison-tests/projectReferences/lib/.gitignore b/test/comparison-tests/projectReferences/lib/.gitignore new file mode 100644 index 000000000..7b7f62099 --- /dev/null +++ b/test/comparison-tests/projectReferences/lib/.gitignore @@ -0,0 +1 @@ +!*.js.map \ No newline at end of file diff --git a/test/comparison-tests/projectReferences/lib/index.js.map b/test/comparison-tests/projectReferences/lib/index.js.map new file mode 100644 index 000000000..af3aabd86 --- /dev/null +++ b/test/comparison-tests/projectReferences/lib/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":";;AAAa,QAAA,GAAG,GAAG;IACjB,GAAG,EAAE,CAAC;IACN,GAAG,EAAE,CAAC;IACN,KAAK,EAAE,CAAC;CACT,CAAC"} \ No newline at end of file diff --git a/test/comparison-tests/projectReferencesOutDir/lib/.gitignore b/test/comparison-tests/projectReferencesOutDir/lib/.gitignore new file mode 100644 index 000000000..7b7f62099 --- /dev/null +++ b/test/comparison-tests/projectReferencesOutDir/lib/.gitignore @@ -0,0 +1 @@ +!*.js.map \ No newline at end of file diff --git a/test/comparison-tests/projectReferencesOutDir/lib/out/index.js.map b/test/comparison-tests/projectReferencesOutDir/lib/out/index.js.map new file mode 100644 index 000000000..604babfc8 --- /dev/null +++ b/test/comparison-tests/projectReferencesOutDir/lib/out/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":";;AAAa,QAAA,GAAG,GAAG;IACjB,GAAG,EAAE,CAAC;IACN,GAAG,EAAE,CAAC;IACN,KAAK,EAAE,CAAC;CACT,CAAC"} \ No newline at end of file From 29d02b98581b5ceb83e08c6340ee20c69efdd6a6 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Sun, 16 Sep 2018 20:39:34 -0700 Subject: [PATCH 26/42] Normalize windows paths before comparing --- src/utils.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/utils.ts b/src/utils.ts index b0cca5ee7..070a7c4d6 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -294,7 +294,11 @@ function getProjectReferenceForFile(filePath: string, instance: TSInstance) { .getProjectReferences()! .find( ref => - (ref && ref.commandLine.fileNames.indexOf(filePath) > -1) || false + (ref && + ref.commandLine.fileNames.some( + file => path.normalize(file) === filePath + )) || + false ) ); } From 433630bae9328aa0fd9951d3756bb7921c216bca Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Sun, 16 Sep 2018 21:45:27 -0700 Subject: [PATCH 27/42] Use only relative paths in error messages --- src/index.ts | 22 +++++++++++-------- src/interfaces.ts | 5 +++++ src/utils.ts | 8 +++++-- .../expectedOutput-3.0/bundle.js | 2 +- .../expectedOutput-3.0/output.transpiled.txt | 4 ++-- .../expectedOutput-3.0/output.txt | 4 ++-- 6 files changed, 29 insertions(+), 16 deletions(-) diff --git a/src/index.ts b/src/index.ts index e10d526bd..5f61535d2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -72,12 +72,14 @@ function successLoader( const fileVersion = updateFileInCache(filePath, contents, instance); const referencedProject = getAndCacheProjectReference(filePath, instance); if (referencedProject) { + const [relativeProjectConfigPath, relativeFilePath] = [ + path.relative(loader.rootContext, referencedProject.sourceFile.fileName), + path.relative(loader.rootContext, filePath) + ]; if (referencedProject.commandLine.options.outFile) { throw new Error( - `The referenced project at ${ - referencedProject.sourceFile.fileName - } is using the ` + - `outFile' option, which is not supported with ts-loader.` + `The referenced project at ${relativeProjectConfigPath} is using ` + + `the outFile' option, which is not supported with ts-loader.` ); } @@ -87,13 +89,15 @@ function successLoader( instance ); + const relativeJSFileName = path.relative(loader.rootContext, jsFileName); if (!instance.compiler.sys.fileExists(jsFileName)) { throw new Error( - `Could not find output JavaScript file for input ${filePath} ` + - `(looked at ${jsFileName}).\nThe input file is part of a project ` + - `reference located at ${referencedProject.sourceFile.fileName}, ` + - 'so ts-loader is looking for the project’s pre-built output on ' + - 'disk. Try running `tsc --build` to build project references.' + `Could not find output JavaScript file for input ` + + `${relativeFilePath} (looked at ${relativeJSFileName}).\n` + + `The input file is part of a project reference located at ` + + `${relativeProjectConfigPath}, so ts-loader is looking for the ` + + 'project’s pre-built output on disk. Try running `tsc --build` ' + + 'to build project references.' ); } diff --git a/src/interfaces.ts b/src/interfaces.ts index ea1523adb..e63b4d57b 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -38,6 +38,11 @@ export interface Webpack { * The directory of the module. Can be used as context for resolving other stuff. */ context: string; + /** + * The root directory of the Webpack project. + * Starting with webpack 4, the formerly `this.options.context` is provided as `this.rootContext`. + */ + rootContext: string; /** * The resolved request string. * eg: "/abc/loader1.js?xyz!/abc/node_modules/loader2/index.js!/abc/resource.js?rrr" diff --git a/src/utils.ts b/src/utils.ts index 070a7c4d6..a24860585 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -318,11 +318,15 @@ export function validateSourceMapOncePerProject( projectsMissingSourceMaps.add(project.sourceFile.fileName); const mapFileName = jsFileName + '.map'; if (!instance.compiler.sys.fileExists(mapFileName)) { + const [relativeJSPath, relativeProjectConfigPath] = [ + path.relative(loader.rootContext, jsFileName), + path.relative(loader.rootContext, project.sourceFile.fileName) + ]; loader.emitWarning( new Error( 'Could not find source map file for referenced project output ' + - `${jsFileName}. Ensure the 'sourceMap' compiler option is enabled ` + - `in ${project.sourceFile.fileName} to ensure Webpack ` + + `${relativeJSPath}. Ensure the 'sourceMap' compiler option ` + + `is enabled in ${relativeProjectConfigPath} to ensure Webpack ` + 'can map project references to the appropriate source files.' ) ); diff --git a/test/comparison-tests/projectReferencesNotBuilt/expectedOutput-3.0/bundle.js b/test/comparison-tests/projectReferencesNotBuilt/expectedOutput-3.0/bundle.js index 3b84a9ad4..7fca2e544 100644 --- a/test/comparison-tests/projectReferencesNotBuilt/expectedOutput-3.0/bundle.js +++ b/test/comparison-tests/projectReferencesNotBuilt/expectedOutput-3.0/bundle.js @@ -105,7 +105,7 @@ eval("\nexports.__esModule = true;\nvar lib_1 = __webpack_require__(/*! ./lib */ /*! no static exports found */ /***/ (function(module, exports) { -eval("throw new Error(\"Module build failed: Error: Could not find output JavaScript file for input /projectReferencesNotBuilt/lib/index.ts (looked at /projectReferencesNotBuilt/lib/index.js)./nThe input file is part of a project reference located at /projectReferencesNotBuilt/lib/tsconfig.json, so ts-loader is looking for the project’s pre-built output on disk. Try running `tsc --build` to build project references./n at successLoader (/Users/Andrew/Developer/ts-loader/dist/index.js:45:19)/n at Object.loader (/Users/Andrew/Developer/ts-loader/dist/index.js:21:12)\");\n\n//# sourceURL=webpack:///./lib/index.ts?"); +eval("throw new Error(\"Module build failed: Error: Could not find output JavaScript file for input lib/index.ts (looked at lib/index.js)./nThe input file is part of a project reference located at lib/tsconfig.json, so ts-loader is looking for the project’s pre-built output on disk. Try running `tsc --build` to build project references./n at successLoader (/Users/Andrew/Developer/ts-loader/dist/index.js:45:19)/n at Object.loader (/Users/Andrew/Developer/ts-loader/dist/index.js:21:12)\");\n\n//# sourceURL=webpack:///./lib/index.ts?"); /***/ }) diff --git a/test/comparison-tests/projectReferencesNotBuilt/expectedOutput-3.0/output.transpiled.txt b/test/comparison-tests/projectReferencesNotBuilt/expectedOutput-3.0/output.transpiled.txt index 0fc5a3524..ac59a2eb1 100644 --- a/test/comparison-tests/projectReferencesNotBuilt/expectedOutput-3.0/output.transpiled.txt +++ b/test/comparison-tests/projectReferencesNotBuilt/expectedOutput-3.0/output.transpiled.txt @@ -1,8 +1,8 @@ Asset Size Chunks Chunk Names -bundle.js 4.86 KiB main [emitted] main +bundle.js 4.64 KiB main [emitted] main Entrypoint main = bundle.js [./app.ts] 131 bytes {main} [built] [1 error] -[./lib/index.ts] 718 bytes {main} [built] [failed] [1 error] +[./lib/index.ts] 490 bytes {main} [built] [failed] [1 error] ERROR in tsconfig.json [tsl] ERROR diff --git a/test/comparison-tests/projectReferencesNotBuilt/expectedOutput-3.0/output.txt b/test/comparison-tests/projectReferencesNotBuilt/expectedOutput-3.0/output.txt index c4eff07f7..d9064cb0d 100644 --- a/test/comparison-tests/projectReferencesNotBuilt/expectedOutput-3.0/output.txt +++ b/test/comparison-tests/projectReferencesNotBuilt/expectedOutput-3.0/output.txt @@ -1,8 +1,8 @@ Asset Size Chunks Chunk Names -bundle.js 4.83 KiB main [emitted] main +bundle.js 4.64 KiB main [emitted] main Entrypoint main = bundle.js [./app.ts] 131 bytes {main} [built] -[./lib/index.ts] 688 bytes {main} [built] [failed] [1 error] +[./lib/index.ts] 490 bytes {main} [built] [failed] [1 error] ERROR in ./lib/index.ts Module build failed: Error: Could not find output JavaScript file for input lib/index.ts (looked at lib/index.js). From 80a28cdcc444601130106946cdbb01b11accdca3 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Mon, 17 Sep 2018 18:20:26 -0700 Subject: [PATCH 28/42] Update expected output to remove absolute path from error message --- .../expectedOutput-3.0/bundle.transpiled.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/comparison-tests/projectReferencesNotBuilt/expectedOutput-3.0/bundle.transpiled.js b/test/comparison-tests/projectReferencesNotBuilt/expectedOutput-3.0/bundle.transpiled.js index 0f5ee2025..7fca2e544 100644 --- a/test/comparison-tests/projectReferencesNotBuilt/expectedOutput-3.0/bundle.transpiled.js +++ b/test/comparison-tests/projectReferencesNotBuilt/expectedOutput-3.0/bundle.transpiled.js @@ -105,7 +105,7 @@ eval("\nexports.__esModule = true;\nvar lib_1 = __webpack_require__(/*! ./lib */ /*! no static exports found */ /***/ (function(module, exports) { -eval("throw new Error(\"Module build failed: Error: Could not find output JavaScript file for input /projectReferencesNotBuilt.transpile/lib/index.ts (looked at /projectReferencesNotBuilt.transpile/lib/index.js)./nThe input file is part of a project reference located at /projectReferencesNotBuilt.transpile/lib/tsconfig.json, so ts-loader is looking for the project’s pre-built output on disk. Try running `tsc --build` to build project references./n at successLoader (/Users/Andrew/Developer/ts-loader/dist/index.js:45:19)/n at Object.loader (/Users/Andrew/Developer/ts-loader/dist/index.js:21:12)\");\n\n//# sourceURL=webpack:///./lib/index.ts?"); +eval("throw new Error(\"Module build failed: Error: Could not find output JavaScript file for input lib/index.ts (looked at lib/index.js)./nThe input file is part of a project reference located at lib/tsconfig.json, so ts-loader is looking for the project’s pre-built output on disk. Try running `tsc --build` to build project references./n at successLoader (/Users/Andrew/Developer/ts-loader/dist/index.js:45:19)/n at Object.loader (/Users/Andrew/Developer/ts-loader/dist/index.js:21:12)\");\n\n//# sourceURL=webpack:///./lib/index.ts?"); /***/ }) From fe888e2be1b4c138ffa1e1e3ada1cb327fd2b4bd Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Mon, 17 Sep 2018 20:03:20 -0700 Subject: [PATCH 29/42] Ignore path differences in TS error message --- test/comparison-tests/create-and-execute-test.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/comparison-tests/create-and-execute-test.js b/test/comparison-tests/create-and-execute-test.js index 405a3368f..6b9584ba3 100644 --- a/test/comparison-tests/create-and-execute-test.js +++ b/test/comparison-tests/create-and-execute-test.js @@ -401,6 +401,10 @@ function getNormalisedFileContent(file, location) { .replace(/(\(dist[\/|\\]\w*.js:)(\d*)(:)(\d*)(\))/g, function(match, openingBracketPathAndColon, lineNumber, colon, columnNumber, closingBracket){ return openingBracketPathAndColon + 'irrelevant-line-number' + colon + 'irrelevant-column-number' + closingBracket; }) + // Ignore path differences in TS error output + .replace(/(TS6305:[^']+')([^']+?)([^\\\/']+')([^']+')([^']+?)([^\\\/']+'.*)$/gm, function(match, messageStart, outputFileBaseDir, outputFileName, messageMiddle, sourceFileBaseDir, sourceFileName) { + return messageStart + outputFileName + messageMiddle + sourceFileName; + }) : normaliseString(originalContent)) // Ignore 'at C:/source/ts-loader/dist/index.js:90:19' style row number / column number differences .replace(/at (.*)(dist[\/|\\]\w*.js:)(\d*)(:)(\d*)/g, function(match, spaceAndStartOfPath, remainingPathAndColon, lineNumber, colon, columnNumber){ From ddc5cc722ccbf068c0e6dd4ffa2be6f4163694b9 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Wed, 19 Sep 2018 22:05:50 -0700 Subject: [PATCH 30/42] Theoretically protect against getProjectReferences changes in typescript@master --- src/utils.ts | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/src/utils.ts b/src/utils.ts index a24860585..caf1e7d42 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -259,7 +259,7 @@ export function supportsProjectReferences(instance: TSInstance) { export function isUsingProjectReferences(instance: TSInstance) { if (supportsProjectReferences(instance)) { const program = ensureProgram(instance); - return program && program.getProjectReferences(); + return Boolean(program && program.getProjectReferences()); } return false; } @@ -285,21 +285,31 @@ export function getAndCacheProjectReference( return projectReference; } +function getResolvedProjectReferences( + program: typescript.Program +): typescript.ResolvedProjectReference[] | undefined { + const getProjectReferences = + (program as any).getResolvedProjectReferences || + program.getProjectReferences; + if (getProjectReferences) { + return getProjectReferences(); + } + return; +} + function getProjectReferenceForFile(filePath: string, instance: TSInstance) { if (isUsingProjectReferences(instance)) { const program = ensureProgram(instance); return ( program && - program - .getProjectReferences()! - .find( - ref => - (ref && - ref.commandLine.fileNames.some( - file => path.normalize(file) === filePath - )) || - false - ) + getResolvedProjectReferences(program)!.find( + ref => + (ref && + ref.commandLine.fileNames.some( + file => path.normalize(file) === filePath + )) || + false + ) ); } From 6c87188a5bf65b432250b29758aaf5d82a43577f Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Thu, 20 Sep 2018 21:42:41 -0700 Subject: [PATCH 31/42] Update README with project references --- README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/README.md b/README.md index 65dde9e09..985c3ec63 100644 --- a/README.md +++ b/README.md @@ -181,6 +181,32 @@ module.exports = { } ``` +### Project references + +**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. + +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). + ### Options There are two types of options: TypeScript options (aka "compiler options") and loader options. TypeScript options should be set using a tsconfig.json file. Loader options can be specified through the `options` property in the webpack configuration: From 611e8d98694875acd50858f87be61c7ec7ba5306 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Thu, 20 Sep 2018 21:58:56 -0700 Subject: [PATCH 32/42] Put projectReferences behind a loaderOption --- README.md | 54 +++++++++++++++++++++++------------------------ src/interfaces.ts | 1 + src/utils.ts | 5 ++++- 3 files changed, 32 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 985c3ec63..202f323a4 100644 --- a/README.md +++ b/README.md @@ -181,32 +181,6 @@ module.exports = { } ``` -### Project references - -**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. - -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). - ### Options There are two types of options: TypeScript options (aka "compiler options") and loader options. TypeScript options should be set using a tsconfig.json file. Loader options can be specified through the `options` property in the webpack configuration: @@ -569,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). @@ -578,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: diff --git a/src/interfaces.ts b/src/interfaces.ts index e63b4d57b..8cea13098 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -315,6 +315,7 @@ export interface LoaderOptions { experimentalWatchApi: boolean; allowTsInNodeModules: boolean; experimentalFileCaching: boolean; + projectReferences: boolean; } export interface TSFile { diff --git a/src/utils.ts b/src/utils.ts index caf1e7d42..fa6d19615 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -257,7 +257,10 @@ export function supportsProjectReferences(instance: TSInstance) { } export function isUsingProjectReferences(instance: TSInstance) { - if (supportsProjectReferences(instance)) { + if ( + instance.loaderOptions.projectReferences && + supportsProjectReferences(instance) + ) { const program = ensureProgram(instance); return Boolean(program && program.getProjectReferences()); } From 32e28825c9c50a154dfcbb472b6fec33b7778b4f Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Thu, 20 Sep 2018 22:04:01 -0700 Subject: [PATCH 33/42] Update validateLoaderOptions and test loader options --- src/index.ts | 3 ++- test/comparison-tests/projectReferences/webpack.config.js | 2 +- .../projectReferencesNoSourceMap/webpack.config.js | 2 +- .../projectReferencesNotBuilt/webpack.config.js | 2 +- .../comparison-tests/projectReferencesOutDir/webpack.config.js | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/index.ts b/src/index.ts index 5f61535d2..3d0ddf37b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -245,7 +245,8 @@ const validLoaderOptions: ValidLoaderOptions[] = [ 'reportFiles', 'experimentalWatchApi', 'allowTsInNodeModules', - 'experimentalFileCaching' + 'experimentalFileCaching', + 'projectReferences' ]; /** diff --git a/test/comparison-tests/projectReferences/webpack.config.js b/test/comparison-tests/projectReferences/webpack.config.js index be37cdc01..0dca2dae2 100644 --- a/test/comparison-tests/projectReferences/webpack.config.js +++ b/test/comparison-tests/projectReferences/webpack.config.js @@ -11,7 +11,7 @@ module.exports = { }, module: { rules: [ - { test: /\.ts$/, loader: 'ts-loader' } + { test: /\.ts$/, loader: 'ts-loader', options: { projectReferences: true } } ] } } diff --git a/test/comparison-tests/projectReferencesNoSourceMap/webpack.config.js b/test/comparison-tests/projectReferencesNoSourceMap/webpack.config.js index be37cdc01..0dca2dae2 100644 --- a/test/comparison-tests/projectReferencesNoSourceMap/webpack.config.js +++ b/test/comparison-tests/projectReferencesNoSourceMap/webpack.config.js @@ -11,7 +11,7 @@ module.exports = { }, module: { rules: [ - { test: /\.ts$/, loader: 'ts-loader' } + { test: /\.ts$/, loader: 'ts-loader', options: { projectReferences: true } } ] } } diff --git a/test/comparison-tests/projectReferencesNotBuilt/webpack.config.js b/test/comparison-tests/projectReferencesNotBuilt/webpack.config.js index be37cdc01..0dca2dae2 100644 --- a/test/comparison-tests/projectReferencesNotBuilt/webpack.config.js +++ b/test/comparison-tests/projectReferencesNotBuilt/webpack.config.js @@ -11,7 +11,7 @@ module.exports = { }, module: { rules: [ - { test: /\.ts$/, loader: 'ts-loader' } + { test: /\.ts$/, loader: 'ts-loader', options: { projectReferences: true } } ] } } diff --git a/test/comparison-tests/projectReferencesOutDir/webpack.config.js b/test/comparison-tests/projectReferencesOutDir/webpack.config.js index be37cdc01..0dca2dae2 100644 --- a/test/comparison-tests/projectReferencesOutDir/webpack.config.js +++ b/test/comparison-tests/projectReferencesOutDir/webpack.config.js @@ -11,7 +11,7 @@ module.exports = { }, module: { rules: [ - { test: /\.ts$/, loader: 'ts-loader' } + { test: /\.ts$/, loader: 'ts-loader', options: { projectReferences: true } } ] } } From e47664b033a3c4cc5cc94f37347046cfb41460fd Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Thu, 20 Sep 2018 22:31:44 -0700 Subject: [PATCH 34/42] Update validateLoaderOptionNames test --- .../expectedOutput-3.0/bundle.js | 2 +- .../expectedOutput-3.0/output.txt | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/comparison-tests/validateLoaderOptionNames/expectedOutput-3.0/bundle.js b/test/comparison-tests/validateLoaderOptionNames/expectedOutput-3.0/bundle.js index 30a41d8b7..8264d0575 100644 --- a/test/comparison-tests/validateLoaderOptionNames/expectedOutput-3.0/bundle.js +++ b/test/comparison-tests/validateLoaderOptionNames/expectedOutput-3.0/bundle.js @@ -93,7 +93,7 @@ /*! no static exports found */ /***/ (function(module, exports) { -eval("throw new Error(\"Module build failed: Error: ts-loader was supplied with an unexpected loader option: notRealOption/n/nPlease take a look at the options you are supplying; the following are valid options:/nsilent / logLevel / logInfoToStdOut / instance / compiler / context / configFile / transpileOnly / ignoreDiagnostics / errorFormatter / colors / compilerOptions / appendTsSuffixTo / appendTsxSuffixTo / onlyCompileBundledFiles / happyPackMode / getCustomTransformers / reportFiles / experimentalWatchApi / allowTsInNodeModules / experimentalFileCaching/n/n at validateLoaderOptions (C://source//ts-loader//dist//index.js:111:19)/n at getLoaderOptions (C://source//ts-loader//dist//index.js:72:5)/n at Object.loader (C://source//ts-loader//dist//index.js:15:21)\");\n\n//# sourceURL=webpack:///./app.ts?"); +eval("throw new Error(\"Module build failed: Error: ts-loader was supplied with an unexpected loader option: notRealOption/n/nPlease take a look at the options you are supplying; the following are valid options:/nsilent / logLevel / logInfoToStdOut / instance / compiler / context / configFile / transpileOnly / ignoreDiagnostics / errorFormatter / colors / compilerOptions / appendTsSuffixTo / appendTsxSuffixTo / onlyCompileBundledFiles / happyPackMode / getCustomTransformers / reportFiles / experimentalWatchApi / allowTsInNodeModules / experimentalFileCaching / projectReferences/n/n at validateLoaderOptions (/Users/Andrew/Developer/ts-loader/dist/index.js:148:19)/n at getLoaderOptions (/Users/Andrew/Developer/ts-loader/dist/index.js:108:5)/n at Object.loader (/Users/Andrew/Developer/ts-loader/dist/index.js:15:21)\");\n\n//# sourceURL=webpack:///./app.ts?"); /***/ }) diff --git a/test/comparison-tests/validateLoaderOptionNames/expectedOutput-3.0/output.txt b/test/comparison-tests/validateLoaderOptionNames/expectedOutput-3.0/output.txt index 4ed6644d1..52c412c64 100644 --- a/test/comparison-tests/validateLoaderOptionNames/expectedOutput-3.0/output.txt +++ b/test/comparison-tests/validateLoaderOptionNames/expectedOutput-3.0/output.txt @@ -1,14 +1,14 @@ - Asset Size Chunks Chunk Names -bundle.js 4.5 KiB main [emitted] main + Asset Size Chunks Chunk Names +bundle.js 4.55 KiB main [emitted] main Entrypoint main = bundle.js -[./app.ts] 751 bytes {main} [built] [failed] [1 error] +[./app.ts] 828 bytes {main} [built] [failed] [1 error] ERROR in ./app.ts Module build failed: Error: ts-loader was supplied with an unexpected loader option: notRealOption Please take a look at the options you are supplying; the following are valid options: -silent / logLevel / logInfoToStdOut / instance / compiler / context / configFile / transpileOnly / ignoreDiagnostics / errorFormatter / colors / compilerOptions / appendTsSuffixTo / appendTsxSuffixTo / onlyCompileBundledFiles / happyPackMode / getCustomTransformers / reportFiles / experimentalWatchApi / allowTsInNodeModules / experimentalFileCaching +silent / logLevel / logInfoToStdOut / instance / compiler / context / configFile / transpileOnly / ignoreDiagnostics / errorFormatter / colors / compilerOptions / appendTsSuffixTo / appendTsxSuffixTo / onlyCompileBundledFiles / happyPackMode / getCustomTransformers / reportFiles / experimentalWatchApi / allowTsInNodeModules / experimentalFileCaching / projectReferences - at validateLoaderOptions (dist\index.js:111:19) - at getLoaderOptions (dist\index.js:72:5) - at Object.loader (dist\index.js:15:21) \ No newline at end of file + at validateLoaderOptions (dist/index.js:148:19) + at getLoaderOptions (dist/index.js:108:5) + at Object.loader (dist/index.js:15:21) \ No newline at end of file From 2f196b3137bf61d99dcf85dc51d7120d1e31c4a0 Mon Sep 17 00:00:00 2001 From: John Reilly Date: Fri, 21 Sep 2018 10:13:25 +0100 Subject: [PATCH 35/42] ignore projectReferencesOutDir on windows in non-transpile mode --- test/comparison-tests/create-and-execute-test.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/comparison-tests/create-and-execute-test.js b/test/comparison-tests/create-and-execute-test.js index 6b9584ba3..9ab134532 100644 --- a/test/comparison-tests/create-and-execute-test.js +++ b/test/comparison-tests/create-and-execute-test.js @@ -51,8 +51,10 @@ if (fs.statSync(testPath).isDirectory() && // @ts-ignore describe(`${testToRun}${extraOption ? ` - ${extraOption}: true` : ''}`, function () { - // @ts-ignore - it('should have the correct output', createTest(testToRun, testPath, {})); + if (testToRun !== 'projectReferencesOutDir' || require('os').platform() !== 'win32') { + // @ts-ignore + it('should have the correct output', createTest(testToRun, testPath, {})); + } if (testToRun === 'declarationOutput' || testToRun === 'declarationOutputWithMaps' || From b235502c925c673e8abd8d51caa1c6d9a770ac81 Mon Sep 17 00:00:00 2001 From: John Reilly Date: Sat, 22 Sep 2018 05:52:11 +0100 Subject: [PATCH 36/42] add execution test for project references --- .../3.0.1_projectReferences/karma.conf.js | 45 +++++++++++++++++++ .../3.0.1_projectReferences/lib/.gitignore | 1 + .../3.0.1_projectReferences/lib/index.d.ts | 5 +++ .../3.0.1_projectReferences/lib/index.js | 10 +++++ .../3.0.1_projectReferences/lib/index.js.map | 1 + .../3.0.1_projectReferences/lib/index.ts | 5 +++ .../3.0.1_projectReferences/lib/tsconfig.json | 9 ++++ .../3.0.1_projectReferences/main.js | 2 + .../3.0.1_projectReferences/package.json | 10 +++++ .../3.0.1_projectReferences/src/app.ts | 5 +++ .../3.0.1_projectReferences/test/app.tests.ts | 7 +++ .../3.0.1_projectReferences/tsconfig.json | 8 ++++ .../3.0.1_projectReferences/webpack.config.js | 18 ++++++++ .../3.0.1_projectReferences/yarn.lock | 11 +++++ 14 files changed, 137 insertions(+) create mode 100644 test/execution-tests/3.0.1_projectReferences/karma.conf.js create mode 100644 test/execution-tests/3.0.1_projectReferences/lib/.gitignore create mode 100644 test/execution-tests/3.0.1_projectReferences/lib/index.d.ts create mode 100644 test/execution-tests/3.0.1_projectReferences/lib/index.js create mode 100644 test/execution-tests/3.0.1_projectReferences/lib/index.js.map create mode 100644 test/execution-tests/3.0.1_projectReferences/lib/index.ts create mode 100644 test/execution-tests/3.0.1_projectReferences/lib/tsconfig.json create mode 100644 test/execution-tests/3.0.1_projectReferences/main.js create mode 100644 test/execution-tests/3.0.1_projectReferences/package.json create mode 100644 test/execution-tests/3.0.1_projectReferences/src/app.ts create mode 100644 test/execution-tests/3.0.1_projectReferences/test/app.tests.ts create mode 100644 test/execution-tests/3.0.1_projectReferences/tsconfig.json create mode 100644 test/execution-tests/3.0.1_projectReferences/webpack.config.js create mode 100644 test/execution-tests/3.0.1_projectReferences/yarn.lock diff --git a/test/execution-tests/3.0.1_projectReferences/karma.conf.js b/test/execution-tests/3.0.1_projectReferences/karma.conf.js new file mode 100644 index 000000000..926196284 --- /dev/null +++ b/test/execution-tests/3.0.1_projectReferences/karma.conf.js @@ -0,0 +1,45 @@ +/* eslint-disable no-var, strict */ +'use strict'; +var webpackConfig = require('./webpack.config.js'); +var reporterOptions = require('../../reporterOptions'); + +module.exports = function(config) { + config.set({ + browsers: [ 'ChromeHeadless' ], + + files: [ + // This loads all the tests + 'main.js' + ], + + port: 9876, + + frameworks: [ 'jasmine' ], + + logLevel: config.LOG_INFO, //config.LOG_DEBUG + + preprocessors: { + 'main.js': [ 'webpack', 'sourcemap' ] + }, + + webpack: { + devtool: 'inline-source-map', + mode: webpackConfig.mode, + module: webpackConfig.module, + resolve: webpackConfig.resolve, + + // for test harness purposes only, you would not need this in a normal project + resolveLoader: webpackConfig.resolveLoader + }, + + webpackMiddleware: { + quiet: true, + stats: { + colors: true + } + }, + + // reporter options + mochaReporter: reporterOptions + }); +}; diff --git a/test/execution-tests/3.0.1_projectReferences/lib/.gitignore b/test/execution-tests/3.0.1_projectReferences/lib/.gitignore new file mode 100644 index 000000000..7b7f62099 --- /dev/null +++ b/test/execution-tests/3.0.1_projectReferences/lib/.gitignore @@ -0,0 +1 @@ +!*.js.map \ No newline at end of file diff --git a/test/execution-tests/3.0.1_projectReferences/lib/index.d.ts b/test/execution-tests/3.0.1_projectReferences/lib/index.d.ts new file mode 100644 index 000000000..73d752279 --- /dev/null +++ b/test/execution-tests/3.0.1_projectReferences/lib/index.d.ts @@ -0,0 +1,5 @@ +export declare const lib: { + one: number; + two: number; + three: number; +}; diff --git a/test/execution-tests/3.0.1_projectReferences/lib/index.js b/test/execution-tests/3.0.1_projectReferences/lib/index.js new file mode 100644 index 000000000..94f953e3e --- /dev/null +++ b/test/execution-tests/3.0.1_projectReferences/lib/index.js @@ -0,0 +1,10 @@ +"use strict"; +exports.__esModule = true; +exports.lib = { + one: 1, + two: 2, + three: 3 + // I am adding this comment here by hand to ensure + // Webpack is using the JS output for project references +}; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/test/execution-tests/3.0.1_projectReferences/lib/index.js.map b/test/execution-tests/3.0.1_projectReferences/lib/index.js.map new file mode 100644 index 000000000..af3aabd86 --- /dev/null +++ b/test/execution-tests/3.0.1_projectReferences/lib/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":";;AAAa,QAAA,GAAG,GAAG;IACjB,GAAG,EAAE,CAAC;IACN,GAAG,EAAE,CAAC;IACN,KAAK,EAAE,CAAC;CACT,CAAC"} \ No newline at end of file diff --git a/test/execution-tests/3.0.1_projectReferences/lib/index.ts b/test/execution-tests/3.0.1_projectReferences/lib/index.ts new file mode 100644 index 000000000..669ca7b3d --- /dev/null +++ b/test/execution-tests/3.0.1_projectReferences/lib/index.ts @@ -0,0 +1,5 @@ +export const lib = { + one: 1, + two: 2, + three: 3 +}; diff --git a/test/execution-tests/3.0.1_projectReferences/lib/tsconfig.json b/test/execution-tests/3.0.1_projectReferences/lib/tsconfig.json new file mode 100644 index 000000000..506c325eb --- /dev/null +++ b/test/execution-tests/3.0.1_projectReferences/lib/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "composite": true, + "sourceMap": true + }, + "files": [ + "./index.ts" + ] +} \ No newline at end of file diff --git a/test/execution-tests/3.0.1_projectReferences/main.js b/test/execution-tests/3.0.1_projectReferences/main.js new file mode 100644 index 000000000..118f90112 --- /dev/null +++ b/test/execution-tests/3.0.1_projectReferences/main.js @@ -0,0 +1,2 @@ +const testsContext = require.context('./', true, /\.tests\.ts(x?)$/); +testsContext.keys().forEach(testsContext); diff --git a/test/execution-tests/3.0.1_projectReferences/package.json b/test/execution-tests/3.0.1_projectReferences/package.json new file mode 100644 index 000000000..12bc6974a --- /dev/null +++ b/test/execution-tests/3.0.1_projectReferences/package.json @@ -0,0 +1,10 @@ +{ + "name": "basic", + "license": "MIT", + "version": "1.0.0", + "main": "index.js", + "devDependencies": { + "@types/jasmine": "^2.5.35", + "jasmine-core": "^2.3.4" + } +} diff --git a/test/execution-tests/3.0.1_projectReferences/src/app.ts b/test/execution-tests/3.0.1_projectReferences/src/app.ts new file mode 100644 index 000000000..102964b70 --- /dev/null +++ b/test/execution-tests/3.0.1_projectReferences/src/app.ts @@ -0,0 +1,5 @@ +import { lib } from '../lib'; + +export function whatNumbersDoYouHave() { + return [lib.one, lib.two, lib.three]; +} diff --git a/test/execution-tests/3.0.1_projectReferences/test/app.tests.ts b/test/execution-tests/3.0.1_projectReferences/test/app.tests.ts new file mode 100644 index 000000000..25c9b229e --- /dev/null +++ b/test/execution-tests/3.0.1_projectReferences/test/app.tests.ts @@ -0,0 +1,7 @@ +import { whatNumbersDoYouHave } from "../src/app"; + +describe("app", () => { + it("code compiled using projectReferences can be consumed", () => { + expect(whatNumbersDoYouHave()).toEqual([1, 2, 3]); + }); +}); diff --git a/test/execution-tests/3.0.1_projectReferences/tsconfig.json b/test/execution-tests/3.0.1_projectReferences/tsconfig.json new file mode 100644 index 000000000..fa0e76df5 --- /dev/null +++ b/test/execution-tests/3.0.1_projectReferences/tsconfig.json @@ -0,0 +1,8 @@ +{ + "files": [ + "./src/app.ts" + ], + "references": [ + { "path": "./lib" } + ] +} diff --git a/test/execution-tests/3.0.1_projectReferences/webpack.config.js b/test/execution-tests/3.0.1_projectReferences/webpack.config.js new file mode 100644 index 000000000..a0a40c93b --- /dev/null +++ b/test/execution-tests/3.0.1_projectReferences/webpack.config.js @@ -0,0 +1,18 @@ +module.exports = { + mode: 'development', + entry: './src/app.ts', + output: { + filename: 'bundle.js' + }, + resolve: { + extensions: ['.ts', '.js'] + }, + module: { + rules: [ + { test: /\.ts$/, loader: 'ts-loader', options: { projectReferences: true } } + ] + } +} + +// for test harness purposes only, you would not need this in a normal project +module.exports.resolveLoader = { alias: { 'ts-loader': require('path').join(__dirname, "../../../index.js") } } \ No newline at end of file diff --git a/test/execution-tests/3.0.1_projectReferences/yarn.lock b/test/execution-tests/3.0.1_projectReferences/yarn.lock new file mode 100644 index 000000000..2bf33ec6c --- /dev/null +++ b/test/execution-tests/3.0.1_projectReferences/yarn.lock @@ -0,0 +1,11 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@types/jasmine@^2.5.35": + version "2.8.5" + resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-2.8.5.tgz#96e58872583fa80c7ea0dd29024b180d5e133678" + +jasmine-core@^2.3.4: + version "2.9.1" + resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.9.1.tgz#b6bbc1d8e65250d56f5888461705ebeeeb88f22f" From 5d190fa272e53a281c362c2328d56289864d651e Mon Sep 17 00:00:00 2001 From: John Reilly Date: Sat, 22 Sep 2018 07:01:48 +0100 Subject: [PATCH 37/42] General tweaks; I should really get tslint properly set up --- src/after-compile.ts | 18 +++++++++--------- src/index.ts | 29 +++++++++++++++-------------- src/instances.ts | 17 +++++++++-------- src/interfaces.ts | 20 ++++++++++++-------- src/utils.ts | 4 ++-- src/watch-run.ts | 6 +++--- 6 files changed, 50 insertions(+), 44 deletions(-) diff --git a/src/after-compile.ts b/src/after-compile.ts index 19be73242..0d9153731 100644 --- a/src/after-compile.ts +++ b/src/after-compile.ts @@ -1,20 +1,20 @@ import * as path from 'path'; -import { - collectAllDependants, - formatErrors, - isUsingProjectReferences -} 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, @@ -177,7 +177,7 @@ function provideErrorsToWebpack( otherFiles } = instance; - let filePathRegex = !!compilerOptions.checkJs + const filePathRegex = !!compilerOptions.checkJs ? constants.dtsTsTsxJsJsxRegex : constants.dtsTsTsxRegex; diff --git a/src/index.ts b/src/index.ts index 3d0ddf37b..12360554c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,27 +1,27 @@ -import * as path from 'path'; import * as loaderUtils from 'loader-utils'; +import * as path from 'path'; import * as typescript from 'typescript'; -import { getTypeScriptInstance, getEmitOutput } from './instances'; -import { - appendSuffixesIfMatch, - arrify, - formatErrors, - validateSourceMapOncePerProject, - getAndCacheProjectReference, - getAndCacheOutputJSFileName -} from './utils'; import * as constants from './constants'; +import { getEmitOutput, getTypeScriptInstance } from './instances'; import { AsyncCallback, Compiler, LoaderOptions, LoaderOptionsCache, + LogLevel, TSFile, TSInstance, - Webpack, - LogLevel + Webpack } from './interfaces'; +import { + appendSuffixesIfMatch, + arrify, + formatErrors, + getAndCacheOutputJSFileName, + getAndCacheProjectReference, + validateSourceMapOncePerProject +} from './utils'; const webpackInstances: Compiler[] = []; const loaderOptionsCache: LoaderOptionsCache = {}; @@ -30,6 +30,7 @@ const loaderOptionsCache: LoaderOptionsCache = {}; * The entry point for ts-loader */ function loader(this: Webpack, contents: string) { + // tslint:disable-next-line:no-unused-expression this.cacheable && this.cacheable(); const callback = this.async(); const options = getLoaderOptions(this); @@ -71,7 +72,7 @@ function successLoader( const fileVersion = updateFileInCache(filePath, contents, instance); const referencedProject = getAndCacheProjectReference(filePath, instance); - if (referencedProject) { + if (referencedProject !== undefined) { const [relativeProjectConfigPath, relativeFilePath] = [ path.relative(loader.rootContext, referencedProject.sourceFile.fileName), path.relative(loader.rootContext, filePath) @@ -400,7 +401,7 @@ function getEmit( ); // In the case of dependencies that are part of a project reference, // the real dependency that webpack should watch is the JS output file. - return projectReference + return projectReference !== undefined ? getAndCacheOutputJSFileName( resolvedFileName, projectReference, diff --git a/src/instances.ts b/src/instances.ts index 15a967ac5..c130a213b 100644 --- a/src/instances.ts +++ b/src/instances.ts @@ -155,13 +155,14 @@ function successfulTypeScriptInstance( if (loaderOptions.transpileOnly) { // quick return for transpiling // we do need to check for any issues with TS options though - const program = configParseResult.projectReferences - ? compiler!.createProgram({ - rootNames: configParseResult.fileNames, - options: configParseResult.options, - projectReferences: configParseResult.projectReferences - }) - : compiler!.createProgram([], compilerOptions); + const program = + configParseResult.projectReferences !== undefined + ? compiler!.createProgram({ + rootNames: configParseResult.fileNames, + options: configParseResult.options, + projectReferences: configParseResult.projectReferences + }) + : compiler!.createProgram([], compilerOptions); // happypack does not have _module.errors - see https://github.com/TypeStrong/ts-loader/issues/336 if (!loaderOptions.happyPackMode) { @@ -307,7 +308,7 @@ export function getEmitOutput(instance: TSInstance, filePath: string) { ) => outputFiles.push({ name: fileName, writeByteOrderMark, text }); const sourceFile = program.getSourceFile(filePath); // The source file will be undefined if it’s part of an unbuilt project reference - if (sourceFile || !isUsingProjectReferences(instance)) { + if (sourceFile !== undefined || !isUsingProjectReferences(instance)) { program.emit( sourceFile, writeFile, diff --git a/src/interfaces.ts b/src/interfaces.ts index 8cea13098..49fdf34bc 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -18,9 +18,11 @@ export interface ErrorInfo { context: string; } -export interface AsyncCallback { - (err: Error | WebpackError | null, source?: string, map?: string): void; -} +export type AsyncCallback = ( + err: Error | WebpackError | null, + source?: string, + map?: string +) => void; /** * Details here: https://webpack.github.io/docs/loaders.html#loader-context @@ -206,9 +208,9 @@ export interface Resolve { */ extensions?: string[]; /** Check these fields in the package.json for suitable files. */ - packageMains?: (string | string[])[]; + packageMains?: Array; /** Check this field in the package.json for an object. Key-value-pairs are threaded as aliasing according to this spec */ - packageAlias?: (string | string[])[]; + packageAlias?: Array; /** * Enable aggressive but unsafe caching for the resolving of a part of your files. * Changes to cached paths may cause failure (in rare cases). An array of RegExps, only a RegExp or true (all files) is expected. @@ -217,9 +219,11 @@ export interface Resolve { unsafeCache?: RegExp | RegExp[] | boolean; } -export interface ResolveSync { - (context: string | undefined, path: string, moduleName: string): string; -} +export type ResolveSync = ( + context: string | undefined, + path: string, + moduleName: string +) => string; export interface WatchHost extends typescript.WatchCompilerHostOfFilesAndCompilerOptions< diff --git a/src/utils.ts b/src/utils.ts index fa6d19615..1d3083fd7 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -276,12 +276,12 @@ export function getAndCacheProjectReference( instance: TSInstance ) { const file = instance.files.get(filePath); - if (file && file.projectReference) { + if (file !== undefined && file.projectReference) { return file.projectReference.project; } const projectReference = getProjectReferenceForFile(filePath, instance); - if (file) { + if (file !== undefined) { file.projectReference = { project: projectReference }; } diff --git a/src/watch-run.ts b/src/watch-run.ts index f2ebb5e08..47d57c81d 100644 --- a/src/watch-run.ts +++ b/src/watch-run.ts @@ -1,8 +1,8 @@ import * as path from 'path'; -import { readFile } from './utils'; import * as constants from './constants'; -import { TSInstance, WebpackCompiler, TSFile } from './interfaces'; +import { TSFile, TSInstance, WebpackCompiler } from './interfaces'; +import { readFile } from './utils'; /** * Make function which will manually update changed files @@ -10,7 +10,7 @@ import { TSInstance, WebpackCompiler, TSFile } from './interfaces'; export function makeWatchRun(instance: TSInstance) { // Called Before starting compilation after watch const lastTimes = new Map(); - let startTime = 0; + const startTime = 0; return (compiler: WebpackCompiler, callback: () => void) => { if (null === instance.modifiedFiles) { From 16a9e7cbd3931af245345bed2361a94a96c43739 Mon Sep 17 00:00:00 2001 From: John Reilly Date: Sun, 23 Sep 2018 07:04:27 +0100 Subject: [PATCH 38/42] some yarn lint stuff --- src/index.ts | 76 +++++++++++++++++++++++++++--------------------- src/instances.ts | 40 ++++++++++++------------- 2 files changed, 62 insertions(+), 54 deletions(-) diff --git a/src/index.ts b/src/index.ts index 12360554c..14d00d1b3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -51,13 +51,13 @@ function loader(this: Webpack, contents: string) { } function successLoader( - loader: Webpack, + loaderContext: Webpack, contents: string, callback: AsyncCallback, options: LoaderOptions, instance: TSInstance ) { - const rawFilePath = path.normalize(loader.resourcePath); + const rawFilePath = path.normalize(loaderContext.resourcePath); const filePath = options.appendTsSuffixTo.length > 0 || options.appendTsxSuffixTo.length > 0 @@ -74,8 +74,11 @@ function successLoader( const referencedProject = getAndCacheProjectReference(filePath, instance); if (referencedProject !== undefined) { const [relativeProjectConfigPath, relativeFilePath] = [ - path.relative(loader.rootContext, referencedProject.sourceFile.fileName), - path.relative(loader.rootContext, filePath) + path.relative( + loaderContext.rootContext, + referencedProject.sourceFile.fileName + ), + path.relative(loaderContext.rootContext, filePath) ]; if (referencedProject.commandLine.options.outFile) { throw new Error( @@ -90,7 +93,10 @@ function successLoader( instance ); - const relativeJSFileName = path.relative(loader.rootContext, jsFileName); + const relativeJSFileName = path.relative( + loaderContext.rootContext, + jsFileName + ); if (!instance.compiler.sys.fileExists(jsFileName)) { throw new Error( `Could not find output JavaScript file for input ` + @@ -105,12 +111,12 @@ function successLoader( // Since the output JS file is being read from disk instead of using the // input TS file, we need to tell the loader that the compilation doesn’t // actually depend on the current file, but depends on the JS file instead. - loader.clearDependencies(); - loader.addDependency(jsFileName); + loaderContext.clearDependencies(); + loaderContext.addDependency(jsFileName); validateSourceMapOncePerProject( instance, - loader, + loaderContext, jsFileName, referencedProject ); @@ -123,22 +129,22 @@ function successLoader( outputText, filePath, contents, - loader, + loaderContext, options, fileVersion, callback ); } else { const { outputText, sourceMapText } = options.transpileOnly - ? getTranspilationEmit(filePath, contents, instance, loader) - : getEmit(rawFilePath, filePath, instance, loader); + ? getTranspilationEmit(filePath, contents, instance, loaderContext) + : getEmit(rawFilePath, filePath, instance, loaderContext); makeSourceMapAndFinish( sourceMapText, outputText, filePath, contents, - loader, + loaderContext, options, fileVersion, callback @@ -151,7 +157,7 @@ function makeSourceMapAndFinish( outputText: string | undefined, filePath: string, contents: string, - loader: Webpack, + loaderContext: Webpack, options: LoaderOptions, fileVersion: number, callback: AsyncCallback @@ -174,15 +180,15 @@ function makeSourceMapAndFinish( outputText, filePath, contents, - loader + loaderContext ); // _module.meta is not available inside happypack - if (!options.happyPackMode && loader._module.buildMeta) { + if (!options.happyPackMode && loaderContext._module.buildMeta) { // Make sure webpack is aware that even though the emitted JavaScript may be the same as // a previously cached version the TypeScript may be different and therefore should be // treated as new - loader._module.buildMeta.tsLoaderFileVersion = fileVersion; + loaderContext._module.buildMeta.tsLoaderFileVersion = fileVersion; } callback(null, output, sourceMap); @@ -192,15 +198,16 @@ function makeSourceMapAndFinish( * either retrieves loader options from the cache * or creates them, adds them to the cache and returns */ -function getLoaderOptions(loader: Webpack) { +function getLoaderOptions(loaderContext: Webpack) { // differentiate the TypeScript instance based on the webpack instance - let webpackIndex = webpackInstances.indexOf(loader._compiler); + let webpackIndex = webpackInstances.indexOf(loaderContext._compiler); if (webpackIndex === -1) { - webpackIndex = webpackInstances.push(loader._compiler) - 1; + webpackIndex = webpackInstances.push(loaderContext._compiler) - 1; } const loaderOptions = - loaderUtils.getOptions(loader) || ({} as LoaderOptions); + loaderUtils.getOptions(loaderContext) || + ({} as LoaderOptions); const instanceName = webpackIndex + '_' + (loaderOptions.instance || 'default'); @@ -257,6 +264,7 @@ const validLoaderOptions: ValidLoaderOptions[] = [ */ function validateLoaderOptions(loaderOptions: LoaderOptions) { const loaderOptionKeys = Object.keys(loaderOptions); + // tslint:disable-next-line:prefer-for-of for (let i = 0; i < loaderOptionKeys.length; i++) { const option = loaderOptionKeys[i]; const isUnexpectedOption = @@ -374,19 +382,19 @@ function getEmit( rawFilePath: string, filePath: string, instance: TSInstance, - loader: Webpack + loaderContext: Webpack ) { const outputFiles = getEmitOutput(instance, filePath); - loader.clearDependencies(); - loader.addDependency(rawFilePath); + loaderContext.clearDependencies(); + loaderContext.addDependency(rawFilePath); const allDefinitionFiles = [...instance.files.keys()].filter(defFilePath => defFilePath.match(constants.dtsDtsxOrDtsDtsxMapRegex) ); // Make this file dependent on *all* definition files in the program - const addDependency = loader.addDependency.bind(loader); + const addDependency = loaderContext.addDependency.bind(loaderContext); allDefinitionFiles.forEach(addDependency); // Additionally make this file dependent on all imported files @@ -414,7 +422,7 @@ function getEmit( additionalDependencies.forEach(addDependency); } - loader._module.buildMeta.tsLoaderDefinitionFileVersions = allDefinitionFiles + loaderContext._module.buildMeta.tsLoaderDefinitionFileVersions = allDefinitionFiles .concat(additionalDependencies) .map( defFilePath => @@ -424,12 +432,12 @@ function getEmit( ); const outputFile = outputFiles - .filter(outputFile => outputFile.name.match(constants.jsJsx)) + .filter(file => file.name.match(constants.jsJsx)) .pop(); const outputText = outputFile ? outputFile.text : undefined; const sourceMapFile = outputFiles - .filter(outputFile => outputFile.name.match(constants.jsJsxMap)) + .filter(file => file.name.match(constants.jsJsxMap)) .pop(); const sourceMapText = sourceMapFile ? sourceMapFile.text : undefined; @@ -443,7 +451,7 @@ function getTranspilationEmit( filePath: string, contents: string, instance: TSInstance, - loader: Webpack + loaderContext: Webpack ) { const fileName = path.basename(filePath); @@ -465,11 +473,11 @@ function getTranspilationEmit( instance.loaderOptions, instance.colors, instance.compiler, - { module: loader._module }, - loader.context + { module: loaderContext._module }, + loaderContext.context ); - loader._module.errors.push(...errors); + loaderContext._module.errors.push(...errors); } return { outputText, sourceMapText }; @@ -480,7 +488,7 @@ function makeSourceMap( outputText: string, filePath: string, contents: string, - loader: Webpack + loaderContext: Webpack ) { if (sourceMapText === undefined) { return { output: outputText, sourceMap: undefined }; @@ -489,7 +497,7 @@ function makeSourceMap( return { output: outputText.replace(/^\/\/# sourceMappingURL=[^\r\n]*/gm, ''), sourceMap: Object.assign(JSON.parse(sourceMapText), { - sources: [loaderUtils.getRemainingRequest(loader)], + sources: [loaderUtils.getRemainingRequest(loaderContext)], file: filePath, sourcesContent: [contents] }) @@ -501,6 +509,8 @@ export = loader; /** * expose public types via declaration merging */ +// tslint:disable-next-line:no-namespace namespace loader { + // tslint:disable-next-line:no-empty-interface export interface Options extends LoaderOptions {} } diff --git a/src/instances.ts b/src/instances.ts index c130a213b..407e0e911 100644 --- a/src/instances.ts +++ b/src/instances.ts @@ -1,32 +1,32 @@ -import * as typescript from 'typescript'; -import * as path from 'path'; -import * as fs from 'fs'; import chalk, { Chalk } from 'chalk'; +import * as fs from 'fs'; +import * as path from 'path'; +import * as typescript from 'typescript'; import { makeAfterCompile } from './after-compile'; +import { getCompiler, getCompilerOptions } from './compilerSetup'; import { getConfigFile, getConfigParseResult } from './config'; -import { EOL, dtsDtsxOrDtsDtsxMapRegex } from './constants'; -import { getCompilerOptions, getCompiler } from './compilerSetup'; -import { - makeError, - formatErrors, - ensureProgram, - isUsingProjectReferences -} from './utils'; -import * as logger from './logger'; -import { makeServicesHost, makeWatchHost } from './servicesHost'; -import { makeWatchRun } from './watch-run'; +import { dtsDtsxOrDtsDtsxMapRegex, EOL } from './constants'; import { LoaderOptions, + TSFile, TSFiles, TSInstance, TSInstances, Webpack, - WebpackError, - TSFile + WebpackError } from './interfaces'; +import * as logger from './logger'; +import { makeServicesHost, makeWatchHost } from './servicesHost'; +import { + ensureProgram, + formatErrors, + isUsingProjectReferences, + makeError +} from './utils'; +import { makeWatchRun } from './watch-run'; -const instances = {}; +const instances = {} as TSInstances; /** * The loader is executed once for each file seen by webpack. However, we need to keep @@ -179,7 +179,7 @@ function successfulTypeScriptInstance( loader._module.errors.push(...errors); } - const instance: TSInstance = { + instances[loaderOptions.instance] = { compiler, compilerOptions, loaderOptions, @@ -192,9 +192,7 @@ function successfulTypeScriptInstance( colors }; - instances[loaderOptions.instance] = instance; - - return { instance }; + return { instance: instances[loaderOptions.instance] }; } // Load initial files (core lib files, any files specified in tsconfig.json) From b6510a0128c6acd71f57b5bcad585f0d94f796b0 Mon Sep 17 00:00:00 2001 From: John Reilly Date: Sun, 23 Sep 2018 08:54:58 +0100 Subject: [PATCH 39/42] further tslint fixes --- src/compilerSetup.ts | 4 ++-- src/config.ts | 6 +++--- src/logger.ts | 4 +++- src/resolver.ts | 1 + src/servicesHost.ts | 35 ++++++++++++++++++++--------------- src/utils.ts | 39 ++++++++++++++++++++------------------- 6 files changed, 49 insertions(+), 40 deletions(-) diff --git a/src/compilerSetup.ts b/src/compilerSetup.ts index cad4d80cb..967e7c11c 100644 --- a/src/compilerSetup.ts +++ b/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; diff --git a/src/config.ts b/src/config.ts index 42a090e03..a51891998 100644 --- a/src/config.ts +++ b/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; diff --git a/src/logger.ts b/src/logger.ts index 65aa0b44d..dc0649b28 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -1,6 +1,7 @@ +// tslint:disable:no-empty +import { Chalk } from 'chalk'; import { Console } from 'console'; import { LoaderOptions } from './interfaces'; -import { Chalk } from 'chalk'; type InternalLoggerFunc = (whereToLog: any, message: string) => void; @@ -28,6 +29,7 @@ const makeLoggerFunc = (loaderOptions: LoaderOptions): InternalLoggerFunc => loaderOptions.silent ? (_whereToLog: any, _message: string) => {} : (whereToLog: any, message: string) => + // tslint:disable-next-line:no-console console.log.call(whereToLog, message); const makeExternalLogger = ( diff --git a/src/resolver.ts b/src/resolver.ts index 92d575a22..cb77ae53d 100644 --- a/src/resolver.ts +++ b/src/resolver.ts @@ -1,5 +1,6 @@ import { Resolve, ResolveSync } from './interfaces'; +// tslint:disable-next-line:no-submodule-imports const node = require('enhanced-resolve/lib/node'); export function makeResolver(options: { resolve: Resolve }): ResolveSync { diff --git a/src/servicesHost.ts b/src/servicesHost.ts index e1344a73d..de705e388 100644 --- a/src/servicesHost.ts +++ b/src/servicesHost.ts @@ -1,18 +1,18 @@ -import * as typescript from 'typescript'; import * as path from 'path'; +import * as typescript from 'typescript'; import * as constants from './constants'; -import * as logger from './logger'; -import { makeResolver } from './resolver'; -import { appendSuffixesIfMatch, readFile, unorderedRemoveItem } from './utils'; import { - WatchHost, ModuleResolutionHost, ResolvedModule, ResolveSync, TSInstance, + WatchHost, Webpack } from './interfaces'; +import * as logger from './logger'; +import { makeResolver } from './resolver'; +import { appendSuffixesIfMatch, readFile, unorderedRemoveItem } from './utils'; export type Action = () => void; @@ -50,13 +50,14 @@ export function makeServicesHost( const resolveSync = makeResolver(loader._compiler.options); const readFileWithFallback = ( - path: string, + filePath: string, encoding?: string | undefined ): string | undefined => - compiler.sys.readFile(path, encoding) || readFile(path, encoding); + compiler.sys.readFile(filePath, encoding) || readFile(filePath, encoding); - const fileExists = (path: string) => - compiler.sys.fileExists(path) || readFile(path) !== undefined; + const fileExists = (filePathToCheck: string) => + compiler.sys.fileExists(filePathToCheck) || + readFile(filePathToCheck) !== undefined; const moduleResolutionHost: ModuleResolutionHost = { fileExists, @@ -147,7 +148,7 @@ export function makeServicesHost( instance, moduleNames, containingFile, - resolutionStrategy + getResolutionStrategy ), getCustomTransformers: () => instance.transformers @@ -181,10 +182,10 @@ export function makeWatchHost( const resolveSync = makeResolver(loader._compiler.options); const readFileWithFallback = ( - path: string, + filePath: string, encoding?: string | undefined ): string | undefined => - compiler.sys.readFile(path, encoding) || readFile(path, encoding); + compiler.sys.readFile(filePath, encoding) || readFile(filePath, encoding); const moduleResolutionHost: ModuleResolutionHost = { fileExists, @@ -243,7 +244,7 @@ export function makeWatchHost( instance, moduleNames, containingFile, - resolutionStrategy + getResolutionStrategy ), invokeFileWatcher, @@ -358,6 +359,7 @@ export function makeWatchHost( } return { close: () => { + // tslint:disable-next-line:no-shadowed-variable const existing = callbacks[file]; if (existing) { unorderedRemoveItem(existing, callback); @@ -490,6 +492,7 @@ function resolveModuleName( if (resolvedFileName.match(scriptRegex)) { resolutionResult = { resolvedFileName, originalFileName }; } + // tslint:disable-next-line:no-empty } catch (e) {} const tsResolution = compiler.resolveModuleName( @@ -520,7 +523,7 @@ type ResolutionStrategy = ( tsResolutionResult: ResolvedModule ) => ResolvedModule; -function resolutionStrategy( +function getResolutionStrategy( resolutionResult: ResolvedModule | undefined, tsResolutionResult: ResolvedModule ): ResolvedModule { @@ -572,7 +575,9 @@ function addCache(servicesHost: typescript.ModuleResolutionHost) { cacheableFunctions.forEach((functionToCache: CacheableFunction) => { const originalFunction = servicesHost[functionToCache]; if (originalFunction !== undefined) { - const cache = createCache>(originalFunction); + const cache = createCache>( + originalFunction + ); servicesHost[ functionToCache ] = cache.getCached as typescript.ModuleResolutionHost[CacheableFunction]; diff --git a/src/utils.ts b/src/utils.ts index 1d3083fd7..dd4cd2ee5 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,20 +1,20 @@ -import * as typescript from 'typescript'; -import * as path from 'path'; -import * as fs from 'fs'; import { Chalk } from 'chalk'; +import * as fs from 'fs'; import * as micromatch from 'micromatch'; +import * as path from 'path'; +import * as typescript from 'typescript'; import constants = require('./constants'); import { DependencyGraph, + ErrorInfo, LoaderOptions, ReverseDependencyGraph, Severity, - WebpackError, - WebpackModule, - ErrorInfo, TSInstance, - Webpack + Webpack, + WebpackError, + WebpackModule } from './interfaces'; /** @@ -103,7 +103,7 @@ export function formatErrors( : { line: errorInfo.line, character: errorInfo.character } ); - return Object.assign(error, merge); + return Object.assign(error, merge) as WebpackError; }) : []; } @@ -135,27 +135,28 @@ export function makeError( export function appendSuffixIfMatch( patterns: RegExp[], - path: string, + filePath: string, suffix: string ): string { if (patterns.length > 0) { - for (let regexp of patterns) { - if (path.match(regexp)) { - return path + suffix; + for (const regexp of patterns) { + if (filePath.match(regexp)) { + return filePath + suffix; } } } - return path; + return filePath; } export function appendSuffixesIfMatch( suffixDict: { [suffix: string]: RegExp[] }, - path: string + filePath: string ): string { - for (let suffix in suffixDict) { - path = appendSuffixIfMatch(suffixDict[suffix], path, suffix); + let amendedPath = filePath; + for (const suffix in suffixDict) { + amendedPath = appendSuffixIfMatch(suffixDict[suffix], filePath, suffix); } - return path; + return amendedPath; } export function unorderedRemoveItem(array: T[], item: T): boolean { @@ -207,7 +208,7 @@ export function collectAllDependencies( const result = {}; result[filePath] = true; collected[filePath] = true; - let directDependencies = dependencyGraph[filePath]; + const directDependencies = dependencyGraph[filePath]; if (directDependencies !== undefined) { directDependencies.forEach(dependencyModule => { if (!collected[dependencyModule.originalFileName]) { @@ -215,7 +216,7 @@ export function collectAllDependencies( dependencyGraph, dependencyModule.resolvedFileName, collected - ).forEach(filePath => (result[filePath] = true)); + ).forEach(depFilePath => (result[depFilePath] = true)); } }); } From fbed24b2b1764c0898d488caf4e8f73211f641b5 Mon Sep 17 00:00:00 2001 From: John Reilly Date: Sun, 23 Sep 2018 09:15:21 +0100 Subject: [PATCH 40/42] fix broken appenSuffixes --- src/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils.ts b/src/utils.ts index dd4cd2ee5..dc0251757 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -154,7 +154,7 @@ export function appendSuffixesIfMatch( ): string { let amendedPath = filePath; for (const suffix in suffixDict) { - amendedPath = appendSuffixIfMatch(suffixDict[suffix], filePath, suffix); + amendedPath = appendSuffixIfMatch(suffixDict[suffix], amendedPath, suffix); } return amendedPath; } From 308f0dd91d6d5e2f00e7a4871592ecbffa6ffeff Mon Sep 17 00:00:00 2001 From: John Reilly Date: Sun, 23 Sep 2018 09:41:54 +0100 Subject: [PATCH 41/42] add linting to build --- .travis.yml | 1 + appveyor.yml | 1 + package.json | 3 +++ src/tslint.json | 26 +++++++++++----------- yarn.lock | 57 ++++++++++++++++++++++++++++++++++++++++++------- 5 files changed, 68 insertions(+), 20 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1bda14aff..39bfcf1d9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,7 @@ sudo: required install: - yarn install - yarn build + - yarn lint - yarn add $TYPESCRIPT env: - TYPESCRIPT=typescript@3.0.1 diff --git a/appveyor.yml b/appveyor.yml index ac6d9902d..0d92705e6 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -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 diff --git a/package.json b/package.json index 14b64d529..8ef54104e 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "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", @@ -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" diff --git a/src/tslint.json b/src/tslint.json index 4e5070bcb..8e3d78aa5 100644 --- a/src/tslint.json +++ b/src/tslint.json @@ -1,20 +1,22 @@ { - "extends": "tslint:latest", + "extends": ["tslint:latest", "tslint-config-prettier"], "rules": { - "max-line-length": [false, 140], - "object-literal-sort-keys": false, + "ban-types": false, + "forin": false, + "interface-over-type-literal": false, "interface-name": [true, "never-prefix"], - "no-unused-variable": [true, "check-parameters"], + "max-line-length": [false, 140], + "member-ordering": false, + "no-implicit-dependencies": false, + "no-object-literal-type-assertion": false, "no-var-requires": false, - "quotemark": [ - true, - "single" - ], + "object-literal-sort-keys": false, + "prefer-object-spread": false, + "strict-boolean-expressions": false, "trailing-comma": [false], - "triple-equals": [ - true - ], - "variable-name": [true, + "triple-equals": [true], + "variable-name": [ + true, "ban-keywords", "check-format", "allow-leading-underscore", diff --git a/yarn.lock b/yarn.lock index a0ff407db..8e127f496 100644 --- a/yarn.lock +++ b/yarn.lock @@ -483,7 +483,7 @@ axios@^0.15.3: dependencies: follow-redirects "1.0.0" -babel-code-frame@^6.26.0: +babel-code-frame@^6.22.0, babel-code-frame@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" dependencies: @@ -1499,7 +1499,7 @@ buildmail@4.0.1: nodemailer-shared "1.1.0" punycode "1.4.1" -builtin-modules@^1.0.0: +builtin-modules@^1.0.0, builtin-modules@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" @@ -1841,6 +1841,10 @@ commander@2.15.1, commander@^2.14.1, commander@^2.9.0: version "2.15.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" +commander@^2.12.1: + version "2.18.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.18.0.tgz#2bf063ddee7c7891176981a2cc798e5754bc6970" + commander@~2.13.0: version "2.13.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c" @@ -2224,7 +2228,7 @@ di@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c" -diff@3.5.0, diff@^3.3.1, diff@^3.5.0: +diff@3.5.0, diff@^3.2.0, diff@^3.3.1, diff@^3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" @@ -3836,7 +3840,7 @@ js-tokens@^3.0.0, js-tokens@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" -js-yaml@^3.9.0: +js-yaml@^3.7.0, js-yaml@^3.9.0: version "3.12.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1" dependencies: @@ -5269,8 +5273,8 @@ preserve@^0.2.0: resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" prettier@^1.11.1: - version "1.13.5" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.13.5.tgz#7ae2076998c8edce79d63834e9b7b09fead6bfd0" + version "1.14.3" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.14.3.tgz#90238dd4c0684b7edce5f83b0fb7328e48bd0895" prettier@^1.12.1: version "1.13.4" @@ -5788,6 +5792,12 @@ resolve@^1.1.6: dependencies: path-parse "^1.0.5" +resolve@^1.3.2: + version "1.8.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26" + dependencies: + path-parse "^1.0.5" + responselike@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" @@ -6540,14 +6550,45 @@ trim-right@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" +tslib@^1.8.0, tslib@^1.8.1: + version "1.9.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" + tslib@^1.9.0: version "1.9.2" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.2.tgz#8be0cc9a1f6dc7727c38deb16c2ebd1a2892988e" +tslint-config-prettier@^1.15.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/tslint-config-prettier/-/tslint-config-prettier-1.15.0.tgz#76b9714399004ab6831fdcf76d89b73691c812cf" + +tslint@^5.11.0: + version "5.11.0" + resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.11.0.tgz#98f30c02eae3cde7006201e4c33cb08b48581eed" + dependencies: + babel-code-frame "^6.22.0" + builtin-modules "^1.1.1" + chalk "^2.3.0" + commander "^2.12.1" + diff "^3.2.0" + glob "^7.1.1" + js-yaml "^3.7.0" + minimatch "^3.0.4" + resolve "^1.3.2" + semver "^5.3.0" + tslib "^1.8.0" + tsutils "^2.27.2" + tsscmp@~1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/tsscmp/-/tsscmp-1.0.5.tgz#7dc4a33af71581ab4337da91d85ca5427ebd9a97" +tsutils@^2.27.2: + version "2.29.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" + dependencies: + tslib "^1.8.1" + tty-browserify@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" @@ -6588,8 +6629,8 @@ typedarray@^0.0.6, typedarray@~0.0.5: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" typescript@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.0.1.tgz#43738f29585d3a87575520a4b93ab6026ef11fdb" + version "3.0.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.0.3.tgz#4853b3e275ecdaa27f78fda46dc273a7eb7fc1c8" uglify-es@^3.3.4: version "3.3.9" From a6b203c17fb2eec6f00fb6cb34b9e8cd7a590bbd Mon Sep 17 00:00:00 2001 From: John Reilly Date: Sun, 23 Sep 2018 15:15:45 +0100 Subject: [PATCH 42/42] update changelog --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 497ca1d47..e130fbb3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 5.2.0 + +* [feat: Initial support for project references - `projectReferences`](https://github.com/TypeStrong/ts-loader/pull/817) - thanks @andrewbranch! + ## 5.1.0 * [feat: Added cache for some FS operations while compiling - `experimentalFileCaching`](https://github.com/TypeStrong/ts-loader/pull/829) - thanks @timocov! diff --git a/package.json b/package.json index 8ef54104e..42847dae2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ts-loader", - "version": "5.1.0", + "version": "5.2.0", "description": "TypeScript loader for webpack", "main": "index.js", "types": "dist/types/index.d.ts",