Repository navigation
Multiple TS config/projects, entry files, and shared code #647
Description
I have 3 dirs:
- service-worker
- browser
- shared
service-worker and browser have their own tsconfig.json. This is necessary because I need to specify a different lib compiler option to files in these projects, as they are ran in different environments (service workers and DOM/window/browser, respectively).
I initially thought I could just specify multiple entry points and ts-loader would use the correct tsconfig.json relative to each entry point. Any files it discovers in the dependency graph of the entry files would use the same configuration as the respective entry file.
Unfortunately this won't work—ts-loader will re-use the first TypeScript instance (and therefore config) it creates for both entry points.
I then discovered ts-loader's instance option, which forces ts-loader to create different instances. However, the "shared" code is difficult to place: it can be compiled with either TS configs!
In any case, below is what I have ended up with. Have you got any ideas how I could clean this up? Or how this might be made easier in the future?
Ideally, this would be as simple as tsc makes it. Feed it a configuration file, and it will discover the entry file(s) through files and compile all files in the dependency graph with the same configuration.
import * as path from 'path';
import * as webpack from 'webpack';
const SOURCE_PATH = path.join(__dirname, '..');
const SHARED_SOURCE_PATH = path.join(SOURCE_PATH, './src/shared');
const BROWSER_SOURCE_PATH = path.join(SOURCE_PATH, './src/browser');
const SERVICE_WORKER_SOURCE_PATH = path.join(SOURCE_PATH, './src/service-worker');
const config: webpack.Configuration = {
devtool: 'source-map',
entry: {
browser: path.join(BROWSER_SOURCE_PATH, './main.ts'),
'service-worker': path.join(SERVICE_WORKER_SOURCE_PATH, './index.ts'),
},
output: {
path: path.join(SOURCE_PATH, './target'),
filename: '[name].js',
},
resolve: {
extensions: [
// start defaults
'.js',
'.json',
// end defaults
'.ts',
],
},
module: {
rules: [
{
test: /\.ts$/,
include: [SHARED_SOURCE_PATH],
loader: 'ts-loader',
options: {
instance: 'shared',
},
},
{
test: /\.ts$/,
include: [BROWSER_SOURCE_PATH],
loader: 'ts-loader',
options: {
instance: 'browser',
},
},
{
test: /\.ts$/,
include: [SERVICE_WORKER_SOURCE_PATH],
loader: 'ts-loader',
options: {
instance: 'service-worker',
},
},
],
},
};
export default config;
Activity
johnnyreilly commentedon Sep 29, 2017
That's not the worst of configs even if it's a bit verbose. Completely on a side note but I'd been thinking about how ts-loader might well be used with service workers (I love a PWA) and this is a nice approach.
See #267 - this is not supported at present although if @tkrotoff has a go then it could happen and that might improve your config.
johnnyreilly commentedon Sep 29, 2017
Do you want to try using the
configFileoption as suggested by @dbettini in #54:I believe ts-loader should support this too
OliverJAsh commentedon Sep 29, 2017
The "shared" code doesn't have a TS project of its own, but it is implicitly part of the dependency graph of both the "browser" and "service worker" TS projects. However, using the configuration above, webpack/ts-loader will attempt to find a unique TS config file for "shared", leading to errors:
If I remove the rule for "shared", it will fail to compile the TypeScript altogether:
Ideally, when webpack traces an entry file, it will re-use the same TS config for the dependency graph of that entry file. This is what happens when I run
tscand specify a config file. In this case that would mean "shared" is type checked against the config of both projects.I can workaround this by creating a TS config file for "shared", however this means my IDE/
tscconfiguration will be different from my webpack configuration. 🤔dbettini commentedon Sep 29, 2017
Try setting your rules like this:
OliverJAsh commentedon Sep 29, 2017
@dbettini That does the trick! Using
configFileinstead ofconfigFileNameas the latter is deprecated. Thank you!One improvement could be to remove the need for
instanceif theconfigFiles are different 🤔dbettini commentedon Sep 29, 2017
@OliverJAsh just to be sure, did you only add the
configFileName, or did you also need to addSHARED_SOURCE_PATHto include?OliverJAsh commentedon Sep 29, 2017
@dbettini I added
configFileandSHARED_SOURCE_PATHininclude:dbettini commentedon Sep 29, 2017
@OliverJAsh yep, as expected. The reason it's needed is because webpack never had
.tsrules set for yoursharedfolder. And using a thirdsharedinstance was not an option, because your shared folder is not an entry point, so webpack wouldn't build it even if you did have atsconfig.jsonin it. What was needed was giving webpack access to yoursharedfolder in both of your instances that build your entries, because it is their dependency, but since it's in the same directory level, you need to add it to include.OliverJAsh commentedon Sep 30, 2017
@dbettini Just spotted a problem: for some reason, webpack says there is an error but provides no details why (using the config above):
Note the "[1 error]" beside /src/browser/index.ts.
If I enable only one entry point and the corresponding rule entry, neither "browser" or "service-worker" will error. But when I run both using the above config, webpack reports an error for the file "/src/browser/index.ts" but with no further details.
@johnnyreilly Do you have any ideas here? Is there a way to log more info about the error?
dbettini commentedon Sep 30, 2017
@OliverJAsh like I said, I use at-loader, I always get all errors logged by default when using it. If ts-loader doesn't log errors by default, maybe you can try changing logLevel
johnnyreilly commentedon Sep 30, 2017
It should log all errors - I'd advise forking ts-loader and adding in some extra logging to diagnose. Do you have a minimal repro repo you could share?
OliverJAsh commentedon Sep 30, 2017
@johnnyreilly Here is a minimal repo: https://github.com/OliverJAsh/webpack-ts-loader-shared
OliverJAsh commentedon Sep 30, 2017
For some reason, specifying
"module": "es2015"compiler option in bothtsconfig.jsons will fix the error. I don't really understand why though 🤔 https://github.com/OliverJAsh/webpack-ts-loader-shared/tree/es-module-compiler-optionIn any case, it would be good to get the error message printed.
OliverJAsh commentedon Sep 30, 2017
One problem with the suggested config above is the "shared" code will only be type checked once, using whichever rule is found first. Both "browser" and "service-worker"
include"shared", but when webpack compiles "shared", it won't use thetsconfig.jsoncorresponding to that entry file—it will just use thetsconfig.jsoninstance of the first matching rule. @dbettiniOliverJAsh commentedon Sep 30, 2017
Edit: upon further testing, this is wrong. Ignore this comment.
I just gave this a go withawesome-typescript-loader. A few things to note:tsconfig.jsons, not just the first matchinginclude"shared" in either rules. Somehow at-loader knows to use the sametsconfig.jsonfor all files in the dependency graph of the entry fileExample: https://github.com/OliverJAsh/webpack-ts-loader-shared/tree/awesome-typescript-loader
Here is an example using at-loader where "shared" is type checked for both
tsconfig.jsons, and will fail for one of them: https://github.com/OliverJAsh/webpack-ts-loader-shared/tree/awesome-typescript-loader-shared-type-checkjohnnyreilly commentedon Sep 30, 2017
Sorry, does it work with at or not?
The module thing might be because (at present) ts-loader has different module defaults to tsc. We plan to rectify this with V3 (being worked on now - see #637)
OliverJAsh commentedon Sep 30, 2017
At this time I can't say it does work any better with at-loader—sorry to cause confusion!
johnnyreilly commentedon Oct 13, 2017
No worries 😄
OliverJAsh commentedon Oct 17, 2017
Closing as a solution was found (with trade offs). Thanks again.
johnnyreilly commentedon Oct 17, 2017
Could you share the solution for others please? Always helps!
OliverJAsh commentedon Feb 7, 2020
~3 years later, sorry! My solution was just to use one
tsconfig.json.(We still want to keep separate projects, but project references still have issues with VS Code.)
johnnyreilly commentedon Feb 7, 2020
Haha! Props for replying after 3 years 😄
yemi commentedon Jul 23, 2020
Thought I'd keep this one alive just a bit longer; I'm running ts-loader alongside serverless framework, specifically the
serverless-webpackplugin, which works fine, and FWIW has a helper for declaring multiple entrypoints (one for each lambda). These compile fine when used at the root project (~1s) incremental compilation, but when ANY sub project gets changed, it emits changes for each of the entrypoints, causing a whole sea of recompilations to occur.Any thoughts on what I can do to prevent that from happening?