diff --git a/README.md b/README.md index dcb5bb9c2..3e18fdcad 100644 --- a/README.md +++ b/README.md @@ -479,6 +479,37 @@ loading only those files that are actually bundled by webpack, as well as any `. by the `tsconfig.json` settings. `.d.ts` files are still included because they may be needed for compilation without being explicitly imported, and therefore not picked up by webpack. +#### allowTsInNodeModules _(boolean) (default=false)_ + +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 this option. +Note that this option acts as a *whitelist* - any modules you desire to import must be included in +the `"files"` or `"include"` block of your project's `tsconfig.json`. + +See: [https://github.com/Microsoft/TypeScript/issues/12358](https://github.com/Microsoft/TypeScript/issues/12358) + +```javascript + // in webpack.config.js + { + test: /\.ts$/, + loader: 'ts-loader', + options: { allowTsInNodeModules: true } + } +``` + +And in your `tsconfig.json`: + +```json + { + "include": [ + "node_modules/whitelisted_module.ts" + ], + "files": [ + "node_modules/my_module/whitelisted_file.ts" + ] + } +``` + #### context _(string) (default=undefined)_ If set, will parse the TypeScript configuration file with given **absolute path** as base path. diff --git a/src/index.ts b/src/index.ts index 4397c28bf..675d96ebe 100644 --- a/src/index.ts +++ b/src/index.ts @@ -69,10 +69,12 @@ function successLoader( : getEmit(rawFilePath, filePath, instance, loader); if (outputText === null || outputText === undefined) { - const additionalGuidance = - filePath.indexOf('node_modules') !== -1 - ? '\nYou should not need to recompile .ts files in node_modules.\nPlease contact the package author to advise them to use --declaration --outDir.\nMore https://github.com/Microsoft/TypeScript/issues/12358' - : ''; + const additionalGuidance: string = (!options.allowTsInNodeModules && filePath.indexOf('node_modules') !== -1) + ? " By default, ts-loader will not compile .ts files in node_modules.\n" + + "You should not need to recompile .ts files there, but if you really want to, use the allowTsInNodeModules option.\n" + + "See: https://github.com/Microsoft/TypeScript/issues/12358" + : ""; + throw new Error( `Typescript emitted no output for ${filePath}.${additionalGuidance}` ); @@ -147,7 +149,8 @@ const validLoaderOptions: ValidLoaderOptions[] = [ 'happyPackMode', 'getCustomTransformers', 'reportFiles', - 'experimentalWatchApi' + 'experimentalWatchApi', + 'allowTsInNodeModules' ]; /** @@ -199,7 +202,8 @@ function makeLoaderOptions(instanceName: string, loaderOptions: LoaderOptions) { onlyCompileBundledFiles: false, reportFiles: [], // When the watch API usage stabilises look to remove this option and make watch usage the default behaviour when available - experimentalWatchApi: false + experimentalWatchApi: false, + allowTsInNodeModules: false } as Partial, loaderOptions ); diff --git a/src/interfaces.ts b/src/interfaces.ts index 2cfd5b283..94f0226aa 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -305,6 +305,7 @@ export interface LoaderOptions { | string | (() => typescript.CustomTransformers | undefined); experimentalWatchApi: boolean; + allowTsInNodeModules: boolean; } export interface TSFile { diff --git a/test/comparison-tests/nodeModulesMeaningfulErrorWhenImportingTs/expectedOutput-2.8/bundle.js b/test/comparison-tests/nodeModulesMeaningfulErrorWhenImportingTs/expectedOutput-2.8/bundle.js index c1758738d..b9204db67 100644 --- a/test/comparison-tests/nodeModulesMeaningfulErrorWhenImportingTs/expectedOutput-2.8/bundle.js +++ b/test/comparison-tests/nodeModulesMeaningfulErrorWhenImportingTs/expectedOutput-2.8/bundle.js @@ -90,7 +90,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./nYou should not need to recompile .ts files in node_modules./nPlease contact the package author to advise them to use --declaration --outDir./nMore https://github.com/Microsoft/TypeScript/issues/12358/n at successLoader (C://source//ts-loader//dist//index.js:39: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 /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 (/Users/alawson/dev/ts-loader/dist/index.js:41:15)/n at Object.loader (/Users/alawson/dev/ts-loader/dist/index.js:21:12)\");\n\n//# sourceURL=webpack:///./node_modules/a/index.ts?"); /***/ }) diff --git a/test/comparison-tests/nodeModulesMeaningfulErrorWhenImportingTs/expectedOutput-2.8/output.txt b/test/comparison-tests/nodeModulesMeaningfulErrorWhenImportingTs/expectedOutput-2.8/output.txt index 9e93d8e83..527f522b4 100644 --- a/test/comparison-tests/nodeModulesMeaningfulErrorWhenImportingTs/expectedOutput-2.8/output.txt +++ b/test/comparison-tests/nodeModulesMeaningfulErrorWhenImportingTs/expectedOutput-2.8/output.txt @@ -1,14 +1,13 @@ Asset Size Chunks Chunk Names -bundle.js 3.75 KiB main [emitted] main +bundle.js 3.77 KiB main [emitted] main Entrypoint main = bundle.js [./app.ts] 79 bytes {main} [built] -[./node_modules/a/index.ts] 517 bytes {main} [built] [failed] [1 error] +[./node_modules/a/index.ts] 568 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. -You should not need to recompile .ts files in node_modules. -Please contact the package author to advise them to use --declaration --outDir. -More https://github.com/Microsoft/TypeScript/issues/12358 - at successLoader (dist\index.js:39:15) - at Object.loader (dist\index.js:21:12) +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 Object.loader (dist/index.js:21:12) @ ./app.ts 3:8-20 \ No newline at end of file diff --git a/test/comparison-tests/validateLoaderOptionNames/expectedOutput-2.8/bundle.js b/test/comparison-tests/validateLoaderOptionNames/expectedOutput-2.8/bundle.js index 34d564b72..f8bf7f615 100644 --- a/test/comparison-tests/validateLoaderOptionNames/expectedOutput-2.8/bundle.js +++ b/test/comparison-tests/validateLoaderOptionNames/expectedOutput-2.8/bundle.js @@ -78,7 +78,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/n/n at validateLoaderOptions (C://source//ts-loader//dist//index.js:103:19)/n at getLoaderOptions (C://source//ts-loader//dist//index.js:66: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/n/n at validateLoaderOptions (/Users/alawson/dev/ts-loader/dist/index.js:110:19)/n at getLoaderOptions (/Users/alawson/dev/ts-loader/dist/index.js:72:5)/n at Object.loader (/Users/alawson/dev/ts-loader/dist/index.js:15:21)\");\n\n//# sourceURL=webpack:///./app.ts?"); /***/ }) diff --git a/test/comparison-tests/validateLoaderOptionNames/expectedOutput-2.8/output.txt b/test/comparison-tests/validateLoaderOptionNames/expectedOutput-2.8/output.txt index 6b4cb8fd5..e5b87bfde 100644 --- a/test/comparison-tests/validateLoaderOptionNames/expectedOutput-2.8/output.txt +++ b/test/comparison-tests/validateLoaderOptionNames/expectedOutput-2.8/output.txt @@ -1,14 +1,14 @@ Asset Size Chunks Chunk Names -bundle.js 3.51 KiB main [emitted] main +bundle.js 3.53 KiB main [emitted] main Entrypoint main = bundle.js -[./app.ts] 728 bytes {main} [built] [failed] [1 error] +[./app.ts] 766 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 +silent / logLevel / logInfoToStdOut / instance / compiler / context / configFile / transpileOnly / ignoreDiagnostics / errorFormatter / colors / compilerOptions / appendTsSuffixTo / appendTsxSuffixTo / onlyCompileBundledFiles / happyPackMode / getCustomTransformers / reportFiles / experimentalWatchApi / allowTsInNodeModules - at validateLoaderOptions (dist\index.js:103:19) - at getLoaderOptions (dist\index.js:66:5) - at Object.loader (dist\index.js:15:21) \ No newline at end of file + at validateLoaderOptions (dist/index.js:110:19) + at getLoaderOptions (dist/index.js:72:5) + at Object.loader (dist/index.js:15:21) \ No newline at end of file diff --git a/test/execution-tests/allowTsInNodeModules/karma.conf.js b/test/execution-tests/allowTsInNodeModules/karma.conf.js new file mode 100644 index 000000000..f0cb98bc7 --- /dev/null +++ b/test/execution-tests/allowTsInNodeModules/karma.conf.js @@ -0,0 +1,47 @@ +/* eslint-disable no-var, strict */ +'use strict'; +var path = require('path'); +var webpack = require('webpack'); +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/allowTsInNodeModules/main.js b/test/execution-tests/allowTsInNodeModules/main.js new file mode 100644 index 000000000..5708d2cf7 --- /dev/null +++ b/test/execution-tests/allowTsInNodeModules/main.js @@ -0,0 +1,2 @@ +const testsContext = require.context('./', true, /\.tests\.ts(x?)$/); +testsContext.keys().forEach(testsContext); \ No newline at end of file diff --git a/test/execution-tests/allowTsInNodeModules/package.json b/test/execution-tests/allowTsInNodeModules/package.json new file mode 100644 index 000000000..c8af49f22 --- /dev/null +++ b/test/execution-tests/allowTsInNodeModules/package.json @@ -0,0 +1,14 @@ +{ + "name": "basic", + "license": "MIT", + "version": "1.0.0", + "main": "index.js", + "dependencies": { + "whitelistedModule": "file:../../testPackages/whitelistedModule", + "whitelistedFiles": "file:../../testPackages/whitelistedFiles" + }, + "devDependencies": { + "@types/jasmine": "^2.5.35", + "jasmine-core": "^2.3.4" + } +} diff --git a/test/execution-tests/allowTsInNodeModules/src/whitelisted.ts b/test/execution-tests/allowTsInNodeModules/src/whitelisted.ts new file mode 100644 index 000000000..ac7021c58 --- /dev/null +++ b/test/execution-tests/allowTsInNodeModules/src/whitelisted.ts @@ -0,0 +1,5 @@ +import whitelistedModule = require('whitelistedModule'); + +export function get() { + return whitelistedModule; +} \ No newline at end of file diff --git a/test/execution-tests/allowTsInNodeModules/src/whitelisted_file.ts b/test/execution-tests/allowTsInNodeModules/src/whitelisted_file.ts new file mode 100644 index 000000000..e6e030c58 --- /dev/null +++ b/test/execution-tests/allowTsInNodeModules/src/whitelisted_file.ts @@ -0,0 +1,5 @@ +import whitelistedFile = require('whitelistedFiles/file'); + +export function get() { + return whitelistedFile; +} \ No newline at end of file diff --git a/test/execution-tests/allowTsInNodeModules/test/app.tests.ts b/test/execution-tests/allowTsInNodeModules/test/app.tests.ts new file mode 100644 index 000000000..0df8dd4b2 --- /dev/null +++ b/test/execution-tests/allowTsInNodeModules/test/app.tests.ts @@ -0,0 +1,13 @@ +describe("whitelisted", () => { + it("module can be imported", () => { + const whitelisted = require('../src/whitelisted'); + + expect(whitelisted.get()).toBe("my whitelisted module"); + }); + + it("file can be imported", () => { + const whitelisted = require('../src/whitelisted_file'); + + expect(whitelisted.get()).toBe("a whitelisted file"); + }); +}); \ No newline at end of file diff --git a/test/execution-tests/allowTsInNodeModules/tsconfig.json b/test/execution-tests/allowTsInNodeModules/tsconfig.json new file mode 100644 index 000000000..c1f37a9b2 --- /dev/null +++ b/test/execution-tests/allowTsInNodeModules/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { }, + "include": [ + "./node_modules/whitelistedModule" + ], + "files": [ + "./node_modules/whitelistedFiles/file.ts" + ] +} \ No newline at end of file diff --git a/test/execution-tests/allowTsInNodeModules/webpack.config.js b/test/execution-tests/allowTsInNodeModules/webpack.config.js new file mode 100644 index 000000000..2b443a8ad --- /dev/null +++ b/test/execution-tests/allowTsInNodeModules/webpack.config.js @@ -0,0 +1,23 @@ +var path = require('path') + +module.exports = { + mode: 'development', + entry: [ + './src/whitelisted.ts', + './src/whitelisted_file.ts' + ], + output: { + filename: 'bundle.js' + }, + resolve: { + extensions: ['.ts', '.js'] + }, + module: { + rules: [ + { test: /\.ts$/, loader: 'ts-loader', options: { allowTsInNodeModules: true } } + ] + } +} + +// for test harness purposes only, you would not need this in a normal project +module.exports.resolveLoader = { alias: { 'ts-loader': path.join(__dirname, "../../../index.js") } } \ No newline at end of file diff --git a/test/execution-tests/allowTsInNodeModules/yarn.lock b/test/execution-tests/allowTsInNodeModules/yarn.lock new file mode 100644 index 000000000..a8e06fe04 --- /dev/null +++ b/test/execution-tests/allowTsInNodeModules/yarn.lock @@ -0,0 +1,17 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@types/jasmine@^2.5.35": + version "2.8.6" + resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-2.8.6.tgz#14445b6a1613cf4e05dd61c3c3256d0e95c0421e" + +jasmine-core@^2.3.4: + version "2.99.1" + resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.99.1.tgz#e6400df1e6b56e130b61c4bcd093daa7f6e8ca15" + +"whitelistedFiles@file:../../testPackages/whitelistedFiles": + version "1.0.0" + +"whitelistedModule@file:../../testPackages/whitelistedModule": + version "1.0.0" diff --git a/test/testPackages/whitelistedFiles/file.ts b/test/testPackages/whitelistedFiles/file.ts new file mode 100644 index 000000000..63e7124c2 --- /dev/null +++ b/test/testPackages/whitelistedFiles/file.ts @@ -0,0 +1,3 @@ +var whitelistedFile = "a whitelisted file"; + +export = whitelistedFile; \ No newline at end of file diff --git a/test/testPackages/whitelistedFiles/package.json b/test/testPackages/whitelistedFiles/package.json new file mode 100644 index 000000000..3caf8a8ca --- /dev/null +++ b/test/testPackages/whitelistedFiles/package.json @@ -0,0 +1,5 @@ +{ + "name": "whitelistedFiles", + "version": "1.0.0", + "main": "file.ts" +} \ No newline at end of file diff --git a/test/testPackages/whitelistedModule/index.ts b/test/testPackages/whitelistedModule/index.ts new file mode 100644 index 000000000..4e71d6b80 --- /dev/null +++ b/test/testPackages/whitelistedModule/index.ts @@ -0,0 +1,3 @@ +var whitelistedModule = "my whitelisted module"; + +export = whitelistedModule; \ No newline at end of file diff --git a/test/testPackages/whitelistedModule/package.json b/test/testPackages/whitelistedModule/package.json new file mode 100644 index 000000000..73aa7f31d --- /dev/null +++ b/test/testPackages/whitelistedModule/package.json @@ -0,0 +1,5 @@ +{ + "name": "whitelistedModule", + "version": "1.0.0", + "main": "index.js" +} \ No newline at end of file