From ebb827fb8e4cf2ebc82ca66bea386ddc654711f7 Mon Sep 17 00:00:00 2001 From: Igor Randjelovic Date: Tue, 28 Mar 2023 16:17:51 +0200 Subject: [PATCH] feat(webpack): support tsconfig.app.json when present (#10221) --- .../__tests__/configuration/base.spec.ts | 171 ++++++++++++++---- .../webpack5/src/configuration/angular.ts | 9 +- packages/webpack5/src/configuration/base.ts | 12 +- packages/webpack5/src/helpers/project.ts | 8 + 4 files changed, 151 insertions(+), 49 deletions(-) diff --git a/packages/webpack5/__tests__/configuration/base.spec.ts b/packages/webpack5/__tests__/configuration/base.spec.ts index 9f6ffeac7..dc4ab697c 100644 --- a/packages/webpack5/__tests__/configuration/base.spec.ts +++ b/packages/webpack5/__tests__/configuration/base.spec.ts @@ -22,6 +22,84 @@ describe('base configuration', () => { }); } + it('supports tsconfig.app.json if exists', () => { + const fsSpy = jest.spyOn(fs, 'existsSync'); + fsSpy.withImplementation( + (path) => { + return path.toString().endsWith('tsconfig.app.json'); + }, + () => { + init({ + ios: true, + }); + const config = base(new Config()); + + expect(fsSpy).toHaveBeenCalledWith('__jest__/tsconfig.app.json'); + expect(fsSpy).not.toHaveBeenCalledWith('__jest__/tsconfig.json'); + + let configFiles = []; + + config.module + .rule('ts') + .use('ts-loader') + .tap((options) => { + configFiles.push(options.configFile); + return options; + }); + + config.plugin('ForkTsCheckerWebpackPlugin').tap((args) => { + configFiles.push(args.at(0).typescript.configFile); + return args; + }); + + expect(configFiles.length).toBe(2); + expect(configFiles).toEqual([ + '__jest__/tsconfig.app.json', // ts-loader + '__jest__/tsconfig.app.json', // fork-ts-checker + ]); + } + ); + }); + + it('falls back to tsconfig.json if no tsconfig.app.json exists', () => { + const fsSpy = jest.spyOn(fs, 'existsSync'); + fsSpy.withImplementation( + (path) => { + return path.toString().endsWith('tsconfig.json'); + }, + () => { + init({ + ios: true, + }); + const config = base(new Config()); + + expect(fsSpy).toHaveBeenCalledWith('__jest__/tsconfig.app.json'); + expect(fsSpy).toHaveBeenCalledWith('__jest__/tsconfig.json'); + + let configFiles = []; + + config.module + .rule('ts') + .use('ts-loader') + .tap((options) => { + configFiles.push(options.configFile); + return options; + }); + + config.plugin('ForkTsCheckerWebpackPlugin').tap((args) => { + configFiles.push(args.at(0).typescript.configFile); + return args; + }); + + expect(configFiles.length).toBe(2); + expect(configFiles).toEqual([ + '__jest__/tsconfig.json', // ts-loader + '__jest__/tsconfig.json', // fork-ts-checker + ]); + } + ); + }); + it('support env.watchNodeModules', () => { init({ ios: true, @@ -32,60 +110,73 @@ describe('base configuration', () => { it('supports dotenv', () => { const fsSpy = jest.spyOn(fs, 'existsSync'); - fsSpy.mockReturnValue(true); + fsSpy.withImplementation( + (path) => { + return path.toString().endsWith('__jest__/.env'); + }, + () => { + init({ + ios: true, + }); + const config = base(new Config()); - init({ - ios: true, - }); - const config = base(new Config()); - - expect(config.plugin('DotEnvPlugin')).toBeDefined(); - config.plugin('DotEnvPlugin').tap((args) => { - expect(args[0].path).toEqual('__jest__/.env'); - return args; - }); + expect(config.plugin('DotEnvPlugin')).toBeDefined(); + config.plugin('DotEnvPlugin').tap((args) => { + expect(args[0].path).toEqual('__jest__/.env'); + return args; + }); + } + ); fsSpy.mockRestore(); }); it('supports env specific dotenv', () => { const fsSpy = jest.spyOn(fs, 'existsSync'); - fsSpy.mockReturnValue(true); + fsSpy.withImplementation( + (path) => { + return path.toString().endsWith('__jest__/.env.prod'); + }, + () => { + init({ + ios: true, + env: 'prod', + }); + const config = base(new Config()); - init({ - ios: true, - env: 'prod', - }); - const config = base(new Config()); - - expect(fsSpy).toHaveBeenCalledWith('__jest__/.env.prod'); - expect(fsSpy).toHaveBeenCalledTimes(1); - expect(config.plugin('DotEnvPlugin')).toBeDefined(); - config.plugin('DotEnvPlugin').tap((args) => { - expect(args[0].path).toEqual('__jest__/.env.prod'); - return args; - }); + expect(fsSpy).toHaveBeenCalledWith('__jest__/.env.prod'); + expect(config.plugin('DotEnvPlugin')).toBeDefined(); + config.plugin('DotEnvPlugin').tap((args) => { + expect(args[0].path).toEqual('__jest__/.env.prod'); + return args; + }); + } + ); fsSpy.mockRestore(); }); it('falls back to default .env', () => { const fsSpy = jest.spyOn(fs, 'existsSync'); - fsSpy.mockReturnValueOnce(false).mockReturnValueOnce(true); + fsSpy.withImplementation( + (path) => { + return path.toString().endsWith('__jest__/.env'); + }, + () => { + init({ + ios: true, + env: 'prod', + }); + const config = base(new Config()); - init({ - ios: true, - env: 'prod', - }); - const config = base(new Config()); - - expect(fsSpy).toHaveBeenCalledWith('__jest__/.env.prod'); - expect(fsSpy).toHaveBeenCalledWith('__jest__/.env'); - expect(fsSpy).toHaveBeenCalledTimes(2); - expect(config.plugin('DotEnvPlugin')).toBeDefined(); - config.plugin('DotEnvPlugin').tap((args) => { - expect(args[0].path).toEqual('__jest__/.env'); - return args; - }); + expect(fsSpy).toHaveBeenCalledWith('__jest__/.env.prod'); + expect(fsSpy).toHaveBeenCalledWith('__jest__/.env'); + expect(config.plugin('DotEnvPlugin')).toBeDefined(); + config.plugin('DotEnvPlugin').tap((args) => { + expect(args[0].path).toEqual('__jest__/.env'); + return args; + }); + } + ); fsSpy.mockRestore(); }); diff --git a/packages/webpack5/src/configuration/angular.ts b/packages/webpack5/src/configuration/angular.ts index adc2fa915..0f2420fa4 100644 --- a/packages/webpack5/src/configuration/angular.ts +++ b/packages/webpack5/src/configuration/angular.ts @@ -4,7 +4,7 @@ import { existsSync } from 'fs'; import { getTypescript, readTsConfig } from '../helpers/typescript'; import { getDependencyPath } from '../helpers/dependencies'; -import { getProjectFilePath } from '../helpers/project'; +import { getProjectTSConfigPath } from '../helpers/project'; import { env as _env, IWebpackEnv } from '../index'; import { warnOnce } from '../helpers/log'; import { @@ -18,12 +18,7 @@ export default function (config: Config, env: IWebpackEnv = _env): Config { base(config, env); const platform = getPlatformName(); - - const tsConfigPath = [ - getProjectFilePath('tsconfig.app.json'), - getProjectFilePath('tsconfig.json'), - ].find((path) => existsSync(path)); - + const tsConfigPath = getProjectTSConfigPath(); const disableAOT = !!env.disableAOT; // remove default ts rule diff --git a/packages/webpack5/src/configuration/base.ts b/packages/webpack5/src/configuration/base.ts index bea1db718..ac9bfdf27 100644 --- a/packages/webpack5/src/configuration/base.ts +++ b/packages/webpack5/src/configuration/base.ts @@ -15,7 +15,7 @@ import { PlatformSuffixPlugin } from '../plugins/PlatformSuffixPlugin'; import { applyFileReplacements } from '../helpers/fileReplacements'; import { addCopyRule, applyCopyRules } from '../helpers/copyRules'; import { WatchStatePlugin } from '../plugins/WatchStatePlugin'; -import { getProjectFilePath } from '../helpers/project'; +import { getProjectFilePath, getProjectTSConfigPath } from '../helpers/project'; import { hasDependency } from '../helpers/dependencies'; import { applyDotEnvPlugin } from '../helpers/dotEnv'; import { env as _env, IWebpackEnv } from '../index'; @@ -234,6 +234,13 @@ export default function (config: Config, env: IWebpackEnv = _env): Config { .use('nativescript-worker-loader') .loader('nativescript-worker-loader'); + const tsConfigPath = getProjectTSConfigPath(); + const configFile = tsConfigPath + ? { + configFile: tsConfigPath, + } + : undefined; + // set up ts support config.module .rule('ts') @@ -243,7 +250,7 @@ export default function (config: Config, env: IWebpackEnv = _env): Config { .options({ // todo: perhaps we can provide a default tsconfig // and use that if the project doesn't have one? - // configFile: '', + ...configFile, transpileOnly: true, allowTsInNodeModules: true, compilerOptions: { @@ -266,6 +273,7 @@ export default function (config: Config, env: IWebpackEnv = _env): Config { async: !!env.watch, typescript: { memoryLimit: 4096, + ...configFile, }, }, ]); diff --git a/packages/webpack5/src/helpers/project.ts b/packages/webpack5/src/helpers/project.ts index 494735090..1385d787b 100644 --- a/packages/webpack5/src/helpers/project.ts +++ b/packages/webpack5/src/helpers/project.ts @@ -1,3 +1,4 @@ +import { existsSync } from 'fs'; import { resolve } from 'path'; export function getProjectRootPath(): string { @@ -30,6 +31,13 @@ export function getProjectFilePath(filePath: string): string { return resolve(getProjectRootPath(), filePath); } +export function getProjectTSConfigPath(): string | undefined { + return [ + getProjectFilePath('tsconfig.app.json'), + getProjectFilePath('tsconfig.json'), + ].find((path) => existsSync(path)); +} + // unused helper, but keeping it here as we may need it // todo: remove if unused for next few releases // function findFile(fileName, currentDir): string | null {