Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add allowTsInNodeModules option for importing .ts files from node_modules. #773

Merged
merged 10 commits into from May 7, 2018
28 changes: 28 additions & 0 deletions README.md
Expand Up @@ -479,6 +479,34 @@ 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we linkify this please? i.e. [https://github.com/Microsoft/TypeScript/issues/12358](https://github.com/Microsoft/TypeScript/issues/12358) I think


```javascript
// in webpack.config.js
{
test: /\.ts$/,
loader: 'ts-loader',
options: { allowTsInNodeModules: true }
}
```

And in your `tsconfig.json`:

```json
{
"files": [
"node_modules/my_module/index.ts"
]
}
```

#### context _(string) (default=undefined)_

If set, will parse the TypeScript configuration file with given **absolute path** as base path.
Expand Down
21 changes: 15 additions & 6 deletions src/index.ts
Expand Up @@ -69,10 +69,17 @@ 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'
: '';
let additionalGuidance: string;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we turn this into a ternary please? Something like:

const additionalGuidance = (!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"
    : "";


if (!options.allowTsInNodeModules && filePath.indexOf('node_modules') !== -1) {
additionalGuidance = " 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";
}
else {
additionalGuidance = "";
}

throw new Error(
`Typescript emitted no output for ${filePath}.${additionalGuidance}`
);
Expand Down Expand Up @@ -147,7 +154,8 @@ const validLoaderOptions: ValidLoaderOptions[] = [
'happyPackMode',
'getCustomTransformers',
'reportFiles',
'experimentalWatchApi'
'experimentalWatchApi',
'allowTsInNodeModules'
];

/**
Expand Down Expand Up @@ -199,7 +207,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>,
loaderOptions
);
Expand Down
1 change: 1 addition & 0 deletions src/interfaces.ts
Expand Up @@ -305,6 +305,7 @@ export interface LoaderOptions {
| string
| (() => typescript.CustomTransformers | undefined);
experimentalWatchApi: boolean;
allowTsInNodeModules: boolean;
}

export interface TSFile {
Expand Down
Expand Up @@ -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:45:15)/n at Object.loader (/Users/alawson/dev/ts-loader/dist/index.js:21:12)\");\n\n//# sourceURL=webpack:///./node_modules/a/index.ts?");

/***/ })

Expand Down
@@ -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:45:15)
at Object.loader (dist/index.js:21:12)
@ ./app.ts 3:8-20
Expand Up @@ -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?");

/***/ })

Expand Down
@@ -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)
at validateLoaderOptions (dist/index.js:110:19)
at getLoaderOptions (dist/index.js:72:5)
at Object.loader (dist/index.js:15:21)
47 changes: 47 additions & 0 deletions 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
});
};
2 changes: 2 additions & 0 deletions test/execution-tests/allowTsInNodeModules/main.js
@@ -0,0 +1,2 @@
const testsContext = require.context('./', true, /\.tests\.ts(x?)$/);
testsContext.keys().forEach(testsContext);
14 changes: 14 additions & 0 deletions 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"
}
}
5 changes: 5 additions & 0 deletions test/execution-tests/allowTsInNodeModules/src/whitelisted.ts
@@ -0,0 +1,5 @@
import whitelistedModule = require('whitelistedModule');

export function get() {
return whitelistedModule;
}
@@ -0,0 +1,5 @@
import whitelistedFile = require('whitelistedFiles/file');

export function get() {
return whitelistedFile;
}
13 changes: 13 additions & 0 deletions 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");
});
});
9 changes: 9 additions & 0 deletions test/execution-tests/allowTsInNodeModules/tsconfig.json
@@ -0,0 +1,9 @@
{
"compilerOptions": { },
"include": [
"./node_modules/whitelistedModule"
],
"files": [
"./node_modules/whitelistedFiles/file.ts"
]
}
23 changes: 23 additions & 0 deletions 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") } }
17 changes: 17 additions & 0 deletions 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"
3 changes: 3 additions & 0 deletions test/testPackages/whitelistedFiles/file.ts
@@ -0,0 +1,3 @@
var whitelistedFile = "a whitelisted file";

export = whitelistedFile;
5 changes: 5 additions & 0 deletions test/testPackages/whitelistedFiles/package.json
@@ -0,0 +1,5 @@
{
"name": "whitelistedFiles",
"version": "1.0.0",
"main": "file.ts"
}
3 changes: 3 additions & 0 deletions test/testPackages/whitelistedModule/index.ts
@@ -0,0 +1,3 @@
var whitelistedModule = "my whitelisted module";

export = whitelistedModule;
5 changes: 5 additions & 0 deletions test/testPackages/whitelistedModule/package.json
@@ -0,0 +1,5 @@
{
"name": "whitelistedModule",
"version": "1.0.0",
"main": "index.js"
}