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

fix(tsconfig): support multiple tsconfigs to be specified #327

Merged
merged 11 commits into from
Aug 7, 2023
60 changes: 38 additions & 22 deletions src/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,20 @@
parseTsconfig,
createFilesMatcher,
type TsConfigResult,
type FileMatcher,
} from 'get-tsconfig';
import type { LoaderOptions } from './types.js';

let foundTsconfig: TsConfigResult | null;
let fileMatcher: FileMatcher;
const tsconfigCache = new Map<string, TsConfigResult>();

async function ESBuildLoader(

Check warning on line 18 in src/loader.ts

View workflow job for this annotation

GitHub Actions / Test

Async function 'ESBuildLoader' has a complexity of 14. Maximum allowed is 10
this: webpack.loader.LoaderContext<LoaderOptions>,
source: string,
): Promise<void> {
const done = this.async()!;

Check warning on line 22 in src/loader.ts

View workflow job for this annotation

GitHub Actions / Test

Forbidden non-null assertion
const options: LoaderOptions = typeof this.getOptions === 'function' ? this.getOptions() : getOptions(this);
const {
implementation,
tsconfig,
tsconfig: tsconfigPath,
...esbuildTransformOptions
} = options;

Expand All @@ -49,26 +47,44 @@
};

if (!('tsconfigRaw' in transformOptions)) {
if (!fileMatcher) {
const tsconfigPath = tsconfig && path.resolve(tsconfig);
foundTsconfig = (
tsconfigPath
? {
config: parseTsconfig(tsconfigPath),
path: tsconfigPath,
}
: getTsconfig()
);
if (foundTsconfig) {
fileMatcher = createFilesMatcher(foundTsconfig);
const { resourcePath } = this;
/**
* If a tsconfig.json path is specified, force apply it
* Same way a provided tsconfigRaw is applied regardless
* of whether it actually matches
*
* However in this case, we also warn if it doesn't match
*/
if (tsconfigPath) {
const tsconfigFullPath = path.resolve(tsconfigPath);
let tsconfig = tsconfigCache.get(tsconfigFullPath);
if (!tsconfig) {
tsconfig = {
config: parseTsconfig(tsconfigFullPath),
path: tsconfigFullPath,
};
tsconfigCache.set(tsconfigFullPath, tsconfig);
}
}

if (fileMatcher) {
transformOptions.tsconfigRaw = fileMatcher(
// Doesn't include query
this.resourcePath,
) as TransformOptions['tsconfigRaw'];
const filesMatcher = createFilesMatcher(tsconfig);
const matches = filesMatcher(resourcePath);

if (!matches) {
this.emitWarning(
new Error(`[esbuild-loader] The specified tsconfig at "${tsconfigFullPath}" was applied to the file "${resourcePath}" but does not match its "include" patterns`),
);
}

transformOptions.tsconfigRaw = tsconfig.config as TransformOptions['tsconfigRaw'];
} else {
/* Detect tsconfig file */

// Webpack shouldn't be loading the same path multiple times so doesn't need to be cached
const tsconfig = getTsconfig(resourcePath);
if (tsconfig) {
const fileMatcher = createFilesMatcher(tsconfig);
transformOptions.tsconfigRaw = fileMatcher(resourcePath) as TransformOptions['tsconfigRaw'];
}
}
}

Expand Down
96 changes: 92 additions & 4 deletions tests/specs/tsconfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,20 @@ export default testSuite(({ describe }) => {
test('finds tsconfig.json and applies strict mode', async ({ onTestFinish }) => {
const fixture = await createFixture({
src: {
'index.ts': `module.exports = [${detectStrictMode}, require("./not-strict.ts")];`,
'index.ts': `module.exports = [
${detectStrictMode},
require("./not-strict.ts"),
require("./different-config/strict.ts"),
];`,
'not-strict.ts': `module.exports = ${detectStrictMode}`,
'different-config': {
'strict.ts': `module.exports = ${detectStrictMode}`,
'tsconfig.json': tsconfigJson({
compilerOptions: {
strict: true,
},
}),
},
},
'webpack.config.js': `
module.exports = {
Expand Down Expand Up @@ -66,7 +78,7 @@ export default testSuite(({ describe }) => {
const require = createRequire(import.meta.url);
expect(
require(path.join(fixture.path, 'dist/main.js')),
).toStrictEqual([true, false]);
).toStrictEqual([true, false, true]);
});

test('handles resource with query', async ({ onTestFinish }) => {
Expand Down Expand Up @@ -174,14 +186,90 @@ export default testSuite(({ describe }) => {

onTestFinish(async () => await fixture.rm());

await execa(webpackCli, {
const { stdout } = await execa(webpackCli, {
cwd: fixture.path,
});

expect(stdout).toMatch('does not match its "include" patterns');

const require = createRequire(import.meta.url);
expect(
require(path.join(fixture.path, 'dist/main.js')),
).toStrictEqual([false, true]);
).toStrictEqual([true, true]);
});

test('applies different tsconfig.json paths', async ({ onTestFinish }) => {
const fixture = await createFixture({
src: {
'index.ts': 'export class C { foo = 100; }',
'index2.ts': 'export class C { foo = 100; }',
},
'webpack.config.js': `
module.exports = {
mode: 'production',

optimization: {
minimize: false,
},

resolveLoader: {
alias: {
'esbuild-loader': ${JSON.stringify(esbuildLoader)},
},
},

module: {
rules: [
{
test: /index\\.ts$/,
loader: 'esbuild-loader',
options: {
tsconfig: './tsconfig.custom1.json',
}
},
{
test: /index2\\.ts$/,
loader: 'esbuild-loader',
options: {
tsconfig: './tsconfig.custom2.json',
}
}
],
},

entry: {
index1: './src/index.ts',
index2: './src/index2.ts',
},

output: {
libraryTarget: 'commonjs2',
},
};
`,
'tsconfig.custom1.json': tsconfigJson({
compilerOptions: {
useDefineForClassFields: false,
},
}),
'tsconfig.custom2.json': tsconfigJson({
compilerOptions: {
useDefineForClassFields: true,
},
}),
});

onTestFinish(async () => await fixture.rm());

await execa(webpackCli, {
cwd: fixture.path,
});

const code1 = await fixture.readFile('dist/index1.js', 'utf8');
expect(code1).toMatch('this.foo = 100;');

const code2 = await fixture.readFile('dist/index2.js', 'utf8');
expect(code2).toMatch('__publicField(this, "foo", 100);');
});
});

Expand Down
Loading