Skip to content

Commit 9121f0b

Browse files
authored
[FEAT] Support multiple tsconfigs (import-js#9)
[FEAT] Support multiple tsconfigs
2 parents 0fa2628 + 6e54fc8 commit 9121f0b

17 files changed

+225
-65
lines changed

README.md

+24-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ npm install --save-dev eslint-plugin-import @typescript-eslint/parser eslint-imp
1919

2020
Add the following to your `.eslintrc` config:
2121

22-
```CJSON
22+
```JSONC
2323
{
2424
"plugins": ["import"],
2525
"rules": {
@@ -37,6 +37,29 @@ Add the following to your `.eslintrc` config:
3737
// use <root>/path/to/folder/tsconfig.json
3838
"typescript": {
3939
"directory": "./path/to/folder"
40+
},
41+
42+
// Multiple tsconfigs (Useful for monorepos)
43+
44+
// use a glob pattern
45+
"typescript": {
46+
"directory": "./packages/**/tsconfig.json"
47+
},
48+
49+
// use an array
50+
"typescript": {
51+
"directory": [
52+
"./packages/module-a/tsconfig.json",
53+
"./packages/module-b/tsconfig.json"
54+
]
55+
},
56+
57+
// use an array of glob patterns
58+
"typescript": {
59+
"directory": [
60+
"./packages/**/tsconfig.json",
61+
"./other-packages/**/tsconfig.json"
62+
]
4063
}
4164
}
4265
}

index.js

+83-29
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,20 @@ const path = require('path');
44
const resolve = require('resolve');
55
const tsconfigPaths = require('tsconfig-paths');
66
const debug = require('debug');
7+
const globSync = require('glob').sync;
8+
const isGlob = require('is-glob');
79

810
const log = debug('eslint-import-resolver-typescript');
911

12+
const extensions = ['.ts', '.tsx', '.d.ts'].concat(
13+
Object.keys(require.extensions),
14+
);
15+
1016
/**
1117
* @param {string} source the module to resolve; i.e './some-module'
1218
* @param {string} file the importing file's full path; i.e. '/usr/local/bin/file.js'
1319
*/
14-
function resolveFile(source, file, config) {
20+
function resolveFile(source, file, options = {}) {
1521
log('looking for:', source);
1622

1723
// don't worry about core node modules
@@ -24,36 +30,16 @@ function resolveFile(source, file, config) {
2430
};
2531
}
2632

27-
let foundTsPath = null;
28-
const extensions = ['.ts', '.tsx', '.d.ts'].concat(
29-
Object.keys(require.extensions),
30-
);
31-
32-
// setup tsconfig-paths
33-
const searchStart = config.directory || process.cwd();
34-
const configLoaderResult = tsconfigPaths.loadConfig(searchStart);
35-
if (configLoaderResult.resultType === 'success') {
36-
const matchPath = tsconfigPaths.createMatchPath(
37-
configLoaderResult.absoluteBaseUrl,
38-
configLoaderResult.paths,
39-
);
40-
41-
// look for files based on setup tsconfig "paths"
42-
foundTsPath = matchPath(source, undefined, undefined, extensions);
43-
44-
if (foundTsPath) {
45-
log('matched ts path:', foundTsPath);
46-
}
47-
} else {
48-
log('failed to init tsconfig-paths:', configLoaderResult.message);
49-
// this can happen if the user has problems with their tsconfig
50-
// or if it's valid, but they don't have baseUrl set
33+
initMappers(options);
34+
const mappedPath = getMappedPath(source, file);
35+
if (mappedPath) {
36+
log('matched ts path:', mappedPath);
5137
}
5238

53-
// note that even if we match via tsconfig-paths, we still need to do a final resolve
39+
// note that even if we map the path, we still need to do a final resolve
5440
let foundNodePath;
5541
try {
56-
foundNodePath = resolve.sync(foundTsPath || source, {
42+
foundNodePath = resolve.sync(mappedPath || source, {
5743
extensions,
5844
basedir: path.dirname(path.resolve(file)),
5945
packageFilter,
@@ -66,15 +52,15 @@ function resolveFile(source, file, config) {
6652
// if path is neither absolute nor relative
6753
if (
6854
(/\.jsx?$/.test(foundNodePath) ||
69-
(config.alwaysTryTypes && !foundNodePath)) &&
55+
(options.alwaysTryTypes && !foundNodePath)) &&
7056
!/^@types[/\\]/.test(source) &&
7157
!path.isAbsolute(source) &&
7258
source[0] !== '.'
7359
) {
7460
const definitelyTyped = resolveFile(
7561
'@types' + path.sep + mangleScopedPackage(source),
7662
file,
77-
config,
63+
options,
7864
);
7965
if (definitelyTyped.found) {
8066
return definitelyTyped;
@@ -104,6 +90,74 @@ function packageFilter(pkg) {
10490
}
10591

10692
/**
93+
* @param {string} source the module to resolve; i.e './some-module'
94+
* @param {string} file the importing file's full path; i.e. '/usr/local/bin/file.js'
95+
* @returns The mapped path of the module or undefined
96+
*/
97+
function getMappedPath(source, file) {
98+
const paths = mappers
99+
.map(mapper => mapper(source, file))
100+
.filter(path => !!path);
101+
102+
if (paths.length > 1) {
103+
log('found multiple matching ts paths:', paths);
104+
}
105+
106+
return paths[0];
107+
}
108+
109+
let mappers;
110+
function initMappers(options) {
111+
if (mappers) {
112+
return;
113+
}
114+
115+
const isArrayOfStrings = array =>
116+
Array.isArray(array) && array.every(o => typeof o === 'string');
117+
118+
const configPaths =
119+
typeof options.directory === 'string'
120+
? [options.directory]
121+
: isArrayOfStrings(options.directory)
122+
? options.directory
123+
: [process.cwd()];
124+
125+
mappers = configPaths
126+
// turn glob patterns into paths
127+
.reduce(
128+
(paths, path) => paths.concat(isGlob(path) ? globSync(path) : path),
129+
[],
130+
)
131+
132+
.map(path => tsconfigPaths.loadConfig(path))
133+
.filter(configLoaderResult => {
134+
const success = configLoaderResult.resultType === 'success';
135+
if (!success) {
136+
// this can happen if the user has problems with their tsconfig
137+
// or if it's valid, but they don't have baseUrl set
138+
log('failed to init tsconfig-paths:', configLoaderResult.message);
139+
}
140+
return success;
141+
})
142+
.map(configLoaderResult => {
143+
const matchPath = tsconfigPaths.createMatchPath(
144+
configLoaderResult.absoluteBaseUrl,
145+
configLoaderResult.paths,
146+
);
147+
148+
return (source, file) => {
149+
// exclude files that are not part of the config base url
150+
if (!file.includes(configLoaderResult.absoluteBaseUrl)) {
151+
return undefined;
152+
}
153+
154+
// look for files based on setup tsconfig "paths"
155+
return matchPath(source, undefined, undefined, extensions);
156+
};
157+
});
158+
}
159+
160+
/*
107161
* For a scoped package, we must look in `@types/foo__bar` instead of `@types/@foo/bar`.
108162
*
109163
* @param {string} moduleName

package-lock.json

+39-34
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)