Ts-loader: 'externals' in webpack.config.js is ignored

Created on 5 Feb 2015  路  15Comments  路  Source: TypeStrong/ts-loader

With a webpack config file that has an externals section eg:

module.exports = {
    ...
    externals: {
        hello: true
    },
    ...
}

This section isn't used by ts-loader and the compile will fail because the module hello can't be found.

The same project compiles fine from javascript files without ts-loader.

Sample project at https://github.com/stkb/ts-loader-issue

Most helpful comment

Thanks @johnnyreilly , installing @types/jquery fixed it. Hmm, I was so confused by the error message.

All 15 comments

While you're looking at this, I'm not sure that the config options target: node and node are working entirely correctly either. Sorry that's a bit vague... I'll do some more investigating... Let me know if you need any more help (though I'm not an expert at any of this and the webpack docs often aren't very helpful!)

So I looked into this yesterday and I've been thinking on it since. I feel there is a fundamental difference between how TypeScript resolves module identifiers and how webpack does it. I think this issue illustrates that point well. From the TS spec:

If the import declaration specifies a top-level external module name and the program contains an
AmbientExternalModuleDeclaration (section 12.2) with a string literal that specifies that exact
name, then the import declaration references that ambient external module

This means that in some cases a require statement doesn't actually hit the filesystem at all which is very different from how webpack's loader's resolve API functions (namely that it doesn't account for externals, nor can it account for AmbientExternalModuleDeclarations). Another example is that TS has fancy rules for resolving a top-level module where it will search a list of directories.

While it's theoretically possible to re-implement some of these things in the loader, I think it's likely best not to try. Instead a better approach may be to drop the loader resolve API altogether and rely on TypeScript for the bulk of module resolution. This was actually how the original prototype for this loader worked, but was changed to attempt to better support things like NormalModuleReplacementPlugin. However, it's becoming more and more clear to me that the original approach is probably the correct one and should resolve issues like this and #4 and hopefully other edge cases that we haven't run across yet.

I think it's likely best not to try. Instead a better approach may be to drop the loader resolve API altogether and rely on TypeScript for the bulk of module resolution. This was actually how the original prototype for this loader worked, but was changed to attempt to better support things like NormalModuleReplacementPlugin. However, it's becoming more and more clear to me that the original approach is probably the correct one and should resolve issues like this and #4 and hopefully other edge cases that we haven't run across yet.

If successful, this would provide a concrete point of differentiation between ts-loader and typescript-loader. I could see both approaches being valuable to different projects, depending on their approach to dependency management and their expectations of webpack.

Coming at it from my na茂ve perspective, where I would normally compile typescript into javascript files, and then run webpack on the javascript files, I had expected a typescript loader behave in exactly the same way, just automagically and without the intermediary .js files being written to disk.

I don't know if it's that easy, but I think it'd be great if your original approach can be pulled off, also because of the point that jgoz makes.

Btw, nice talk at React Conf @jbrantly :)

@jgoz Thanks!

@stkb I've committed a change that should hopefully handle the two issues you reported (according to the unit tests it does). I would like to hold off publishing to NPM until you can verify that the changes fix your issue. Thanks!

BTW @jgoz You may be interested in the replacement unit test. It shows that some fancy module resolution stuff is still possible with this approach (using NormalModuleReplacementPlugin for example). There are probably some cases where using webpack's resolve() may work better but it's nice to know that some of the fancier items aren't completely out of the picture.

Fantastic! It works great :) Much appreciated :D

Just one point: in one of my projects it wasn't working until I realised I'd forgotton to put

resolve: { extensions: ['', '.js', '.ts'] }

in my webpack.config. I know the readme already has that section in the example but I wonder if it's worth putting an extra note there, that you really need those three entries in resolve.extensions, otherwise it won't work.

I just published this to NPM. Thanks again for the detailed reports and testing. I also updated the documentation slightly per your recommendation.

BTW @jgoz You may be interested in the replacement unit test. It shows that some fancy module resolution stuff is still possible with this approach

Cool idea! I don't really use those advanced features of webpack, but it's nice to know that the flexibility is there if we need it.

Hi everyone in this ancient thread. So the comments suggest that the issue is fixed, but I'm trying to use a webpack config like this:

module.exports = {
  // ...
  externals: {
    jquery: 'jQuery'
  },
  // ...
};

And when in index.ts I'm importing it:

import $ from 'jquery'

I'm getting:

ERROR in /path/to/src/index.ts
./src/index.ts
[tsl] ERROR in /path/to/src/index.ts(1,15)
      TS2307: Cannot find module 'jquery'.

I'm using the latest ts-loader 4.5.0.

When I try to remove the typescript part and use plain javascript, it works just fine.

Is there anything I'm missing?

Do you have @types/jquery installed? Also do you want to try import * as $ from 'jquery'?

Thanks @johnnyreilly , installing @types/jquery fixed it. Hmm, I was so confused by the error message.

hi! In my case it works only if I delete this part from webpack.config:
externals: { 'react': 'React', 'react-dom': 'ReactDOM' } }
Is there any better solution?
I do have @types/react and @types/react-dom installed.

Was this page helpful?
0 / 5 - 0 ratings