diff --git a/packages/webpack5/__tests__/configuration/__snapshots__/base.spec.ts.snap b/packages/webpack5/__tests__/configuration/__snapshots__/base.spec.ts.snap index e6e48102c..5ff6d7fbf 100644 --- a/packages/webpack5/__tests__/configuration/__snapshots__/base.spec.ts.snap +++ b/packages/webpack5/__tests__/configuration/__snapshots__/base.spec.ts.snap @@ -97,6 +97,8 @@ exports[`base configuration for android 1`] = ` { loader: 'ts-loader', options: { + configFile: '__jest__/node_modules/@nativescript/webpack/dist/stubs/tsconfig.default.json', + context: '__jest__', transpileOnly: true, allowTsInNodeModules: true, compilerOptions: { @@ -230,6 +232,8 @@ exports[`base configuration for android 1`] = ` { async: false, typescript: { + configFile: '__jest__/node_modules/@nativescript/webpack/dist/stubs/tsconfig.default.json', + context: '__jest__', memoryLimit: 4096 } } @@ -417,6 +421,8 @@ exports[`base configuration for ios 1`] = ` { loader: 'ts-loader', options: { + configFile: '__jest__/node_modules/@nativescript/webpack/dist/stubs/tsconfig.default.json', + context: '__jest__', transpileOnly: true, allowTsInNodeModules: true, compilerOptions: { @@ -550,6 +556,8 @@ exports[`base configuration for ios 1`] = ` { async: false, typescript: { + configFile: '__jest__/node_modules/@nativescript/webpack/dist/stubs/tsconfig.default.json', + context: '__jest__', memoryLimit: 4096 } } diff --git a/packages/webpack5/__tests__/configuration/__snapshots__/javascript.spec.ts.snap b/packages/webpack5/__tests__/configuration/__snapshots__/javascript.spec.ts.snap index 098dd5a2e..a611ab411 100644 --- a/packages/webpack5/__tests__/configuration/__snapshots__/javascript.spec.ts.snap +++ b/packages/webpack5/__tests__/configuration/__snapshots__/javascript.spec.ts.snap @@ -97,6 +97,8 @@ exports[`javascript configuration for android 1`] = ` { loader: 'ts-loader', options: { + configFile: '__jest__/node_modules/@nativescript/webpack/dist/stubs/tsconfig.default.json', + context: '__jest__', transpileOnly: true, allowTsInNodeModules: true, compilerOptions: { @@ -230,6 +232,8 @@ exports[`javascript configuration for android 1`] = ` { async: false, typescript: { + configFile: '__jest__/node_modules/@nativescript/webpack/dist/stubs/tsconfig.default.json', + context: '__jest__', memoryLimit: 4096 } } @@ -426,6 +430,8 @@ exports[`javascript configuration for ios 1`] = ` { loader: 'ts-loader', options: { + configFile: '__jest__/node_modules/@nativescript/webpack/dist/stubs/tsconfig.default.json', + context: '__jest__', transpileOnly: true, allowTsInNodeModules: true, compilerOptions: { @@ -559,6 +565,8 @@ exports[`javascript configuration for ios 1`] = ` { async: false, typescript: { + configFile: '__jest__/node_modules/@nativescript/webpack/dist/stubs/tsconfig.default.json', + context: '__jest__', memoryLimit: 4096 } } diff --git a/packages/webpack5/__tests__/configuration/__snapshots__/vue.spec.ts.snap b/packages/webpack5/__tests__/configuration/__snapshots__/vue.spec.ts.snap index f5c5ae2e3..810f6b1b0 100644 --- a/packages/webpack5/__tests__/configuration/__snapshots__/vue.spec.ts.snap +++ b/packages/webpack5/__tests__/configuration/__snapshots__/vue.spec.ts.snap @@ -1,6 +1,723 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`vue configuration for android 1`] = ` +"{ + mode: 'development', + externals: [ + 'package.json', + '~/package.json' + ], + externalsPresets: { + node: false + }, + devtool: 'inline-source-map', + target: 'node', + watchOptions: { + ignored: [ + '__jest__/platforms/**', + '__jest__/App_Resources/**' + ] + }, + ignoreWarnings: [ + /System.import\\\\(\\\\) is deprecated/ + ], + output: { + path: '__jest__/platforms/android/app/src/main/assets/app', + pathinfo: false, + publicPath: '', + libraryTarget: 'commonjs', + globalObject: 'global', + clean: true + }, + resolve: { + symlinks: true, + alias: { + '~': '__jest__/src', + '@': '__jest__/src', + vue: 'nativescript-vue' + }, + extensions: [ + '.android.vue', + '.vue', + '.android.ts', + '.ts', + '.android.js', + '.js', + '.android.mjs', + '.mjs', + '.android.css', + '.css', + '.android.scss', + '.scss', + '.android.json', + '.json' + ], + modules: [ + '__jest__/node_modules', + 'node_modules' + ] + }, + resolveLoader: { + modules: [ + '__jest__/node_modules/@nativescript/webpack/dist/loaders', + '__jest__/node_modules', + 'node_modules' + ] + }, + module: { + rules: [ + /* config.module.rule('bundle') */ + { + enforce: 'post', + test: '__jest__/src/app.js', + use: [ + /* config.module.rule('bundle').use('app-css-loader') */ + { + loader: 'app-css-loader', + options: { + platform: 'android' + } + } + ] + }, + /* config.module.rule('workers') */ + { + test: /\\\\.(mjs|js|ts)$/, + use: [ + /* config.module.rule('workers').use('nativescript-worker-loader') */ + { + loader: 'nativescript-worker-loader' + } + ] + }, + /* config.module.rule('ts') */ + { + test: [ + /\\\\.ts$/ + ], + use: [ + /* config.module.rule('ts').use('ts-loader') */ + { + loader: 'ts-loader', + options: { + configFile: '__jest__/node_modules/@nativescript/webpack/dist/stubs/tsconfig.default.json', + context: '__jest__', + transpileOnly: true, + allowTsInNodeModules: true, + compilerOptions: { + sourceMap: true, + declaration: false + }, + getCustomTransformers: function () { /* omitted long function */ }, + appendTsSuffixTo: [ + '\\\\\\\\.vue$' + ] + } + } + ] + }, + /* config.module.rule('js') */ + { + test: /\\\\.js$/, + exclude: [ + /node_modules/ + ] + }, + /* config.module.rule('xml') */ + { + test: /\\\\.xml$/, + use: [ + /* config.module.rule('xml').use('xml-namespace-loader') */ + { + loader: 'xml-namespace-loader' + } + ] + }, + /* config.module.rule('css') */ + { + test: /\\\\.css$/, + use: [ + /* config.module.rule('css').use('apply-css-loader') */ + { + loader: 'apply-css-loader' + }, + /* config.module.rule('css').use('css2json-loader') */ + { + loader: 'css2json-loader' + }, + /* config.module.rule('css').use('vue-css-loader') */ + { + loader: 'vue-loader/lib/loaders/stylePostLoader.js' + }, + /* config.module.rule('css').use('postcss-loader') */ + { + loader: 'postcss-loader', + options: { + postcssOptions: { + plugins: [ + [ + 'postcss-import', + { + resolve: function () { /* omitted long function */ } + } + ] + ] + } + } + } + ] + }, + /* config.module.rule('scss') */ + { + test: /\\\\.scss$/, + use: [ + /* config.module.rule('scss').use('apply-css-loader') */ + { + loader: 'apply-css-loader' + }, + /* config.module.rule('scss').use('css2json-loader') */ + { + loader: 'css2json-loader' + }, + /* config.module.rule('scss').use('vue-css-loader') */ + { + loader: 'vue-loader/lib/loaders/stylePostLoader.js' + }, + /* config.module.rule('scss').use('postcss-loader') */ + { + loader: 'postcss-loader', + options: { + postcssOptions: { + plugins: [ + [ + 'postcss-import', + { + resolve: function () { /* omitted long function */ } + } + ] + ] + } + } + }, + /* config.module.rule('scss').use('sass-loader') */ + { + loader: 'sass-loader' + } + ] + }, + /* config.module.rule('vue') */ + { + test: /\\\\.vue$/, + use: [ + /* config.module.rule('vue').use('vue-loader') */ + { + loader: 'vue-loader', + options: { + compiler: { + compile: function () { /* omitted long function */ }, + compileToFunctions: function () { /* omitted long function */ }, + parseComponent: function () { /* omitted long function */ }, + registerElement: function () { /* omitted long function */ } + } + } + } + ] + } + ] + }, + optimization: { + runtimeChunk: 'single', + splitChunks: { + cacheGroups: { + defaultVendor: { + test: /[\\\\\\\\/]node_modules[\\\\\\\\/]/, + priority: -10, + name: 'vendor', + chunks: 'all' + } + } + }, + minimizer: [ + /* config.optimization.minimizer('TerserPlugin') */ + new TerserPlugin( + { + terserOptions: { + compress: { + collapse_vars: false, + sequences: false, + keep_infinity: true, + drop_console: false, + global_defs: { + __UGLIFIED__: true + } + }, + keep_fnames: true, + keep_classnames: true + } + } + ) + ] + }, + plugins: [ + /* config.plugin('VueLoaderPlugin') */ + new VueLoaderPlugin(), + /* config.plugin('ForkTsCheckerWebpackPlugin') */ + new ForkTsCheckerWebpackPlugin( + { + async: false, + typescript: { + configFile: '__jest__/node_modules/@nativescript/webpack/dist/stubs/tsconfig.default.json', + context: '__jest__', + memoryLimit: 4096, + extensions: { + vue: { + enabled: true, + compiler: 'nativescript-vue-template-compiler' + } + } + } + } + ), + /* config.plugin('PlatformSuffixPlugin') */ + new PlatformSuffixPlugin( + { + platform: 'android' + } + ), + /* config.plugin('ContextExclusionPlugin|App_Resources') */ + new ContextExclusionPlugin( + /(.*)App_Resources(.*)/ + ), + /* config.plugin('ContextExclusionPlugin|Other_Platforms') */ + new ContextExclusionPlugin( + /\\\\.(ios)\\\\.(\\\\w+)$/ + ), + /* config.plugin('DefinePlugin') */ + new DefinePlugin( + { + __DEV__: true, + __NS_WEBPACK__: true, + __NS_ENV_VERBOSE__: false, + __NS_DEV_HOST_IPS__: '[\\"127.0.0.1\\",\\"192.168.0.10\\"]', + __CSS_PARSER__: '\\"css-tree\\"', + __UI_USE_XML_PARSER__: true, + __UI_USE_EXTERNAL_RENDERER__: false, + __ANDROID__: true, + __IOS__: false, + 'global.isAndroid': true, + 'global.isIOS': false, + process: 'global.process' + } + ), + /* config.plugin('CopyWebpackPlugin') */ + new CopyPlugin( + { + patterns: [ + { + from: 'assets/**', + context: '__jest__/src', + noErrorOnMissing: true, + globOptions: { + dot: false, + ignore: [] + } + }, + { + from: 'fonts/**', + context: '__jest__/src', + noErrorOnMissing: true, + globOptions: { + dot: false, + ignore: [] + } + }, + { + from: '**/*.+(jpg|png)', + context: '__jest__/src', + noErrorOnMissing: true, + globOptions: { + dot: false, + ignore: [] + } + } + ] + } + ), + /* config.plugin('WatchStatePlugin') */ + new WatchStatePlugin() + ], + entry: { + bundle: [ + '@nativescript/core/globals/index', + '@nativescript/core/bundle-entry-points', + '__jest__/src/app.js', + '@nativescript/core/ui/frame', + '@nativescript/core/ui/frame/activity' + ] + } +}" +`; + +exports[`vue configuration for ios 1`] = ` +"{ + mode: 'development', + externals: [ + 'package.json', + '~/package.json' + ], + externalsPresets: { + node: false + }, + devtool: 'inline-source-map', + target: 'node', + watchOptions: { + ignored: [ + '__jest__/platforms/**', + '__jest__/App_Resources/**' + ] + }, + ignoreWarnings: [ + /System.import\\\\(\\\\) is deprecated/ + ], + output: { + path: '__jest__/platforms/ios/jest/app', + pathinfo: false, + publicPath: '', + libraryTarget: 'commonjs', + globalObject: 'global', + clean: true + }, + resolve: { + symlinks: true, + alias: { + '~': '__jest__/src', + '@': '__jest__/src', + vue: 'nativescript-vue' + }, + extensions: [ + '.ios.vue', + '.vue', + '.ios.ts', + '.ts', + '.ios.js', + '.js', + '.ios.mjs', + '.mjs', + '.ios.css', + '.css', + '.ios.scss', + '.scss', + '.ios.json', + '.json' + ], + modules: [ + '__jest__/node_modules', + 'node_modules' + ] + }, + resolveLoader: { + modules: [ + '__jest__/node_modules/@nativescript/webpack/dist/loaders', + '__jest__/node_modules', + 'node_modules' + ] + }, + module: { + rules: [ + /* config.module.rule('bundle') */ + { + enforce: 'post', + test: '__jest__/src/app.js', + use: [ + /* config.module.rule('bundle').use('app-css-loader') */ + { + loader: 'app-css-loader', + options: { + platform: 'ios' + } + } + ] + }, + /* config.module.rule('workers') */ + { + test: /\\\\.(mjs|js|ts)$/, + use: [ + /* config.module.rule('workers').use('nativescript-worker-loader') */ + { + loader: 'nativescript-worker-loader' + } + ] + }, + /* config.module.rule('ts') */ + { + test: [ + /\\\\.ts$/ + ], + use: [ + /* config.module.rule('ts').use('ts-loader') */ + { + loader: 'ts-loader', + options: { + configFile: '__jest__/node_modules/@nativescript/webpack/dist/stubs/tsconfig.default.json', + context: '__jest__', + transpileOnly: true, + allowTsInNodeModules: true, + compilerOptions: { + sourceMap: true, + declaration: false + }, + getCustomTransformers: function () { /* omitted long function */ }, + appendTsSuffixTo: [ + '\\\\\\\\.vue$' + ] + } + } + ] + }, + /* config.module.rule('js') */ + { + test: /\\\\.js$/, + exclude: [ + /node_modules/ + ] + }, + /* config.module.rule('xml') */ + { + test: /\\\\.xml$/, + use: [ + /* config.module.rule('xml').use('xml-namespace-loader') */ + { + loader: 'xml-namespace-loader' + } + ] + }, + /* config.module.rule('css') */ + { + test: /\\\\.css$/, + use: [ + /* config.module.rule('css').use('apply-css-loader') */ + { + loader: 'apply-css-loader' + }, + /* config.module.rule('css').use('css2json-loader') */ + { + loader: 'css2json-loader' + }, + /* config.module.rule('css').use('vue-css-loader') */ + { + loader: 'vue-loader/lib/loaders/stylePostLoader.js' + }, + /* config.module.rule('css').use('postcss-loader') */ + { + loader: 'postcss-loader', + options: { + postcssOptions: { + plugins: [ + [ + 'postcss-import', + { + resolve: function () { /* omitted long function */ } + } + ] + ] + } + } + } + ] + }, + /* config.module.rule('scss') */ + { + test: /\\\\.scss$/, + use: [ + /* config.module.rule('scss').use('apply-css-loader') */ + { + loader: 'apply-css-loader' + }, + /* config.module.rule('scss').use('css2json-loader') */ + { + loader: 'css2json-loader' + }, + /* config.module.rule('scss').use('vue-css-loader') */ + { + loader: 'vue-loader/lib/loaders/stylePostLoader.js' + }, + /* config.module.rule('scss').use('postcss-loader') */ + { + loader: 'postcss-loader', + options: { + postcssOptions: { + plugins: [ + [ + 'postcss-import', + { + resolve: function () { /* omitted long function */ } + } + ] + ] + } + } + }, + /* config.module.rule('scss').use('sass-loader') */ + { + loader: 'sass-loader' + } + ] + }, + /* config.module.rule('vue') */ + { + test: /\\\\.vue$/, + use: [ + /* config.module.rule('vue').use('vue-loader') */ + { + loader: 'vue-loader', + options: { + compiler: { + compile: function () { /* omitted long function */ }, + compileToFunctions: function () { /* omitted long function */ }, + parseComponent: function () { /* omitted long function */ }, + registerElement: function () { /* omitted long function */ } + } + } + } + ] + } + ] + }, + optimization: { + runtimeChunk: 'single', + splitChunks: { + cacheGroups: { + defaultVendor: { + test: /[\\\\\\\\/]node_modules[\\\\\\\\/]/, + priority: -10, + name: 'vendor', + chunks: 'all' + } + } + }, + minimizer: [ + /* config.optimization.minimizer('TerserPlugin') */ + new TerserPlugin( + { + terserOptions: { + compress: { + collapse_vars: true, + sequences: true, + keep_infinity: true, + drop_console: false, + global_defs: { + __UGLIFIED__: true + } + }, + keep_fnames: true, + keep_classnames: true + } + } + ) + ] + }, + plugins: [ + /* config.plugin('VueLoaderPlugin') */ + new VueLoaderPlugin(), + /* config.plugin('ForkTsCheckerWebpackPlugin') */ + new ForkTsCheckerWebpackPlugin( + { + async: false, + typescript: { + configFile: '__jest__/node_modules/@nativescript/webpack/dist/stubs/tsconfig.default.json', + context: '__jest__', + memoryLimit: 4096, + extensions: { + vue: { + enabled: true, + compiler: 'nativescript-vue-template-compiler' + } + } + } + } + ), + /* config.plugin('PlatformSuffixPlugin') */ + new PlatformSuffixPlugin( + { + platform: 'ios' + } + ), + /* config.plugin('ContextExclusionPlugin|App_Resources') */ + new ContextExclusionPlugin( + /(.*)App_Resources(.*)/ + ), + /* config.plugin('ContextExclusionPlugin|Other_Platforms') */ + new ContextExclusionPlugin( + /\\\\.(android)\\\\.(\\\\w+)$/ + ), + /* config.plugin('DefinePlugin') */ + new DefinePlugin( + { + __DEV__: true, + __NS_WEBPACK__: true, + __NS_ENV_VERBOSE__: false, + __NS_DEV_HOST_IPS__: '[\\"127.0.0.1\\",\\"192.168.0.10\\"]', + __CSS_PARSER__: '\\"css-tree\\"', + __UI_USE_XML_PARSER__: true, + __UI_USE_EXTERNAL_RENDERER__: false, + __ANDROID__: false, + __IOS__: true, + 'global.isAndroid': false, + 'global.isIOS': true, + process: 'global.process' + } + ), + /* config.plugin('CopyWebpackPlugin') */ + new CopyPlugin( + { + patterns: [ + { + from: 'assets/**', + context: '__jest__/src', + noErrorOnMissing: true, + globOptions: { + dot: false, + ignore: [] + } + }, + { + from: 'fonts/**', + context: '__jest__/src', + noErrorOnMissing: true, + globOptions: { + dot: false, + ignore: [] + } + }, + { + from: '**/*.+(jpg|png)', + context: '__jest__/src', + noErrorOnMissing: true, + globOptions: { + dot: false, + ignore: [] + } + } + ] + } + ), + /* config.plugin('WatchStatePlugin') */ + new WatchStatePlugin() + ], + entry: { + bundle: [ + '@nativescript/core/globals/index', + '@nativescript/core/bundle-entry-points', + '__jest__/src/app.js' + ], + 'tns_modules/inspector_modules': [ + '@nativescript/core/inspector_modules' + ] + } +}" +`; + +exports[`vue configuration with typescript for android 1`] = ` "{ mode: 'development', externals: [ @@ -354,7 +1071,7 @@ exports[`vue configuration for android 1`] = ` }" `; -exports[`vue configuration for ios 1`] = ` +exports[`vue configuration with typescript for ios 1`] = ` "{ mode: 'development', externals: [ diff --git a/packages/webpack5/__tests__/configuration/angular.spec.ts b/packages/webpack5/__tests__/configuration/angular.spec.ts index 8cab9190b..b0f308a18 100644 --- a/packages/webpack5/__tests__/configuration/angular.spec.ts +++ b/packages/webpack5/__tests__/configuration/angular.spec.ts @@ -1,5 +1,12 @@ import Config from 'webpack-chain'; +import { + addMockFile, + mockExistsSync, + restoreExistsSync, + setHasTSConfig, +} from '../../scripts/jest.utils'; + import { default as angular } from '../../src/configuration/angular'; import { init } from '../../src'; @@ -22,29 +29,14 @@ jest.mock( describe('angular configuration', () => { const platforms = ['ios', 'android']; - let fsExistsSyncSpy: jest.SpiedFunction; - let polyfillsPath: string | boolean = false; beforeAll(() => { - const fs = require('fs'); - const original = fs.existsSync; - fsExistsSyncSpy = jest.spyOn(fs, 'existsSync'); - - fsExistsSyncSpy.mockImplementation((path) => { - if (path === '__jest__/tsconfig.json') { - return true; - } - - if (polyfillsPath && path === polyfillsPath) { - return true; - } - - return original.call(fs, path); - }); + mockExistsSync(); + setHasTSConfig(true); }); afterAll(() => { - fsExistsSyncSpy.mockRestore(); + restoreExistsSync(); }); for (let platform of platforms) { @@ -56,25 +48,27 @@ describe('angular configuration', () => { }); it(`loads polyfills.${platform}.ts into the bundle entry if it exists `, () => { - polyfillsPath = `__jest__/src/polyfills.${platform}.ts`; + const cleanupMockFile = addMockFile( + `__jest__/src/polyfills.${platform}.ts` + ); init({ [platform]: true, }); expect(angular(new Config()).entry('bundle').values()).toMatchSnapshot(); - polyfillsPath = false; + cleanupMockFile(); }); } it(`loads polyfills.ts into the bundle entry if it exists `, () => { - polyfillsPath = `__jest__/src/polyfills.ts`; + const cleanupMockFile = addMockFile(`__jest__/src/polyfills.ts`); init({ ios: true, }); expect(angular(new Config()).entry('bundle').values()).toMatchSnapshot(); - polyfillsPath = false; + cleanupMockFile(); }); }); diff --git a/packages/webpack5/__tests__/configuration/base.spec.ts b/packages/webpack5/__tests__/configuration/base.spec.ts index 35e6f9fe4..a269d4aaf 100644 --- a/packages/webpack5/__tests__/configuration/base.spec.ts +++ b/packages/webpack5/__tests__/configuration/base.spec.ts @@ -1,6 +1,13 @@ import Config from 'webpack-chain'; import fs from 'fs'; +import { + mockExistsSync, + restoreExistsSync, + addMockFile, + fsExistsSyncSpy, +} from '../../scripts/jest.utils'; + import base from '../../src/configuration/base'; import { init } from '../../src'; import { applyFileReplacements } from '../../src/helpers/fileReplacements'; @@ -9,6 +16,18 @@ import { additionalCopyRules } from '../../src/helpers/copyRules'; describe('base configuration', () => { const platforms = ['ios', 'android']; + beforeAll(() => { + mockExistsSync(); + }); + + beforeEach(() => { + fsExistsSyncSpy.mockClear(); + }); + + afterAll(() => { + restoreExistsSync(); + }); + for (let platform of platforms) { it(`for ${platform}`, () => { init({ @@ -31,8 +50,7 @@ describe('base configuration', () => { }); it('supports dotenv', () => { - const fsSpy = jest.spyOn(fs, 'existsSync'); - fsSpy.mockReturnValue(true); + const cleanupMockFile = addMockFile('__jest__/.env'); init({ ios: true, @@ -45,12 +63,11 @@ describe('base configuration', () => { return args; }); - fsSpy.mockRestore(); + cleanupMockFile(); }); it('supports env specific dotenv', () => { - const fsSpy = jest.spyOn(fs, 'existsSync'); - fsSpy.mockReturnValue(true); + const cleanupMockFile = addMockFile('__jest__/.env.prod'); init({ ios: true, @@ -58,19 +75,18 @@ describe('base configuration', () => { }); const config = base(new Config()); - expect(fsSpy).toHaveBeenCalledWith('__jest__/.env.prod'); - expect(fsSpy).toHaveBeenCalledTimes(1); + expect(fsExistsSyncSpy).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(); + + cleanupMockFile(); }); it('falls back to default .env', () => { - const fsSpy = jest.spyOn(fs, 'existsSync'); - fsSpy.mockReturnValueOnce(false).mockReturnValueOnce(true); + const cleanupMockFile = addMockFile('__jest__/.env'); init({ ios: true, @@ -78,15 +94,16 @@ describe('base configuration', () => { }); const config = base(new Config()); - expect(fsSpy).toHaveBeenCalledWith('__jest__/.env.prod'); - expect(fsSpy).toHaveBeenCalledWith('__jest__/.env'); - expect(fsSpy).toHaveBeenCalledTimes(2); + expect(fsExistsSyncSpy).toHaveBeenCalledWith('__jest__/.env.prod'); + expect(fsExistsSyncSpy).toHaveBeenCalledWith('__jest__/.env'); + expect(config.plugin('DotEnvPlugin')).toBeDefined(); config.plugin('DotEnvPlugin').tap((args) => { expect(args[0].path).toEqual('__jest__/.env'); return args; }); - fsSpy.mockRestore(); + + cleanupMockFile(); }); it('applies file replacements', () => { diff --git a/packages/webpack5/__tests__/configuration/react.spec.ts b/packages/webpack5/__tests__/configuration/react.spec.ts index 74a10d111..d4a6e25bc 100644 --- a/packages/webpack5/__tests__/configuration/react.spec.ts +++ b/packages/webpack5/__tests__/configuration/react.spec.ts @@ -1,11 +1,26 @@ import Config from 'webpack-chain'; +import { + mockExistsSync, + restoreExistsSync, + setHasTSConfig, +} from '../../scripts/jest.utils'; + import react from '../../src/configuration/react'; import { init } from '../../src'; describe('react configuration', () => { const platforms = ['ios', 'android']; + beforeAll(() => { + mockExistsSync(); + setHasTSConfig(true); + }); + + afterAll(() => { + restoreExistsSync(); + }); + for (let platform of platforms) { describe(`> ${platform} >`, () => { it(`base config`, () => { diff --git a/packages/webpack5/__tests__/configuration/svelte.spec.ts b/packages/webpack5/__tests__/configuration/svelte.spec.ts index 78c903219..5ca295b6c 100644 --- a/packages/webpack5/__tests__/configuration/svelte.spec.ts +++ b/packages/webpack5/__tests__/configuration/svelte.spec.ts @@ -1,5 +1,11 @@ import Config from 'webpack-chain'; +import { + mockExistsSync, + restoreExistsSync, + setHasTSConfig, +} from '../../scripts/jest.utils'; + import svelte from '../../src/configuration/svelte'; import { init } from '../../src'; @@ -8,6 +14,15 @@ jest.mock('__jest__/svelte.config.js', () => {}, { virtual: true }); describe('svelte configuration', () => { const platforms = ['ios', 'android']; + beforeAll(() => { + mockExistsSync(); + setHasTSConfig(true); + }); + + afterAll(() => { + restoreExistsSync(); + }); + for (let platform of platforms) { it(`for ${platform}`, () => { init({ diff --git a/packages/webpack5/__tests__/configuration/typescript.spec.ts b/packages/webpack5/__tests__/configuration/typescript.spec.ts index 0fe124b57..c49c2ec48 100644 --- a/packages/webpack5/__tests__/configuration/typescript.spec.ts +++ b/packages/webpack5/__tests__/configuration/typescript.spec.ts @@ -1,11 +1,26 @@ import Config from 'webpack-chain'; +import { + mockExistsSync, + restoreExistsSync, + setHasTSConfig, +} from '../../scripts/jest.utils'; + import typescript from '../../src/configuration/typescript'; import { init } from '../../src'; describe('typescript configuration', () => { const platforms = ['ios', 'android']; + beforeAll(() => { + mockExistsSync(); + setHasTSConfig(true); + }); + + afterAll(() => { + restoreExistsSync(); + }); + for (let platform of platforms) { it(`for ${platform}`, () => { init({ diff --git a/packages/webpack5/__tests__/configuration/vue.spec.ts b/packages/webpack5/__tests__/configuration/vue.spec.ts index 2234ab7bb..033f8d3b8 100644 --- a/packages/webpack5/__tests__/configuration/vue.spec.ts +++ b/packages/webpack5/__tests__/configuration/vue.spec.ts @@ -1,11 +1,25 @@ import Config from 'webpack-chain'; +import { + mockExistsSync, + restoreExistsSync, + setHasTSConfig, +} from '../../scripts/jest.utils'; + import vue from '../../src/configuration/vue'; import { init } from '../../src'; describe('vue configuration', () => { const platforms = ['ios', 'android']; + beforeAll(() => { + mockExistsSync(); + }); + + afterAll(() => { + restoreExistsSync(); + }); + for (let platform of platforms) { it(`for ${platform}`, () => { init({ @@ -14,4 +28,23 @@ describe('vue configuration', () => { expect(vue(new Config()).toString()).toMatchSnapshot(); }); } + + describe('with typescript', () => { + beforeAll(() => { + setHasTSConfig(true); + }); + + afterAll(() => { + setHasTSConfig(false); + }); + + for (let platform of platforms) { + it(`for ${platform}`, () => { + init({ + [platform]: true, + }); + expect(vue(new Config()).toString()).toMatchSnapshot(); + }); + } + }); }); diff --git a/packages/webpack5/scripts/jest.utils.ts b/packages/webpack5/scripts/jest.utils.ts new file mode 100644 index 000000000..a2af51d26 --- /dev/null +++ b/packages/webpack5/scripts/jest.utils.ts @@ -0,0 +1,41 @@ +let mockedPaths = new Set([]); + +export let fsExistsSyncSpy: jest.SpiedFunction; + +export function mockExistsSync() { + const fs = require('fs'); + const original = fs.existsSync; + fsExistsSyncSpy = jest.spyOn(fs, 'existsSync'); + + fsExistsSyncSpy.mockImplementation((path) => { + if (mockedPaths.has(path)) { + return true; + } + + return original.call(fs, path); + }); +} + +export function restoreExistsSync() { + if (fsExistsSyncSpy) { + fsExistsSyncSpy.mockRestore(); + } +} + +export function setHasTSConfig(value: boolean = true) { + if (value) { + mockedPaths.add('__jest__/tsconfig.json'); + + return; + } + + mockedPaths.delete('__jest__/tsconfig.json'); +} + +export function addMockFile(path: string) { + mockedPaths.add(path); + + return () => { + mockedPaths.delete(path); + }; +} diff --git a/packages/webpack5/src/configuration/base.ts b/packages/webpack5/src/configuration/base.ts index 842f28ea4..fdcb88c79 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, getProjectRootPath } from '../helpers/project'; import { hasDependency } from '../helpers/dependencies'; import { applyDotEnvPlugin } from '../helpers/dotEnv'; import { env as _env, IWebpackEnv } from '../index'; @@ -229,6 +229,16 @@ export default function (config: Config, env: IWebpackEnv = _env): Config { .use('nativescript-worker-loader') .loader('nativescript-worker-loader'); + const hasTSConfig = existsSync(getProjectFilePath('tsconfig.json')); + + let tsDefaultOptions = {}; + if (!hasTSConfig) { + tsDefaultOptions = { + configFile: resolve(__dirname, '../stubs/tsconfig.default.json'), + context: getProjectRootPath(), + }; + } + // set up ts support config.module .rule('ts') @@ -236,9 +246,7 @@ export default function (config: Config, env: IWebpackEnv = _env): Config { .use('ts-loader') .loader('ts-loader') .options({ - // todo: perhaps we can provide a default tsconfig - // and use that if the project doesn't have one? - // configFile: '', + ...tsDefaultOptions, transpileOnly: true, allowTsInNodeModules: true, compilerOptions: { @@ -260,6 +268,7 @@ export default function (config: Config, env: IWebpackEnv = _env): Config { { async: !!env.watch, typescript: { + ...tsDefaultOptions, memoryLimit: 4096, }, }, diff --git a/packages/webpack5/src/configuration/vue.ts b/packages/webpack5/src/configuration/vue.ts index 8a5f7d224..5cefe05bb 100644 --- a/packages/webpack5/src/configuration/vue.ts +++ b/packages/webpack5/src/configuration/vue.ts @@ -62,7 +62,7 @@ export default function (config: Config, env: IWebpackEnv = _env): Config { }); }); - config.when(hasDependency('typescript'), (config) => { + config.when(config.plugins.has('ForkTsCheckerWebpackPlugin'), (config) => { config.plugin('ForkTsCheckerWebpackPlugin').tap((args) => { args[0] = merge(args[0], { typescript: { diff --git a/packages/webpack5/src/stubs/default.config.stub.js b/packages/webpack5/src/stubs/default.config.stub.js index a561e0e2b..45573416d 100644 --- a/packages/webpack5/src/stubs/default.config.stub.js +++ b/packages/webpack5/src/stubs/default.config.stub.js @@ -8,5 +8,3 @@ module.exports = (env) => { return webpack.resolveConfig(); }; - - diff --git a/packages/webpack5/src/stubs/tsconfig.default.json b/packages/webpack5/src/stubs/tsconfig.default.json new file mode 100644 index 000000000..d9ba1d50d --- /dev/null +++ b/packages/webpack5/src/stubs/tsconfig.default.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "module": "esnext", + "target": "es2017", + "moduleResolution": "node", + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "noEmitHelpers": true, + "noEmitOnError": true, + "skipLibCheck": true, + "lib": ["es2017", "dom"], + "baseUrl": ".", + "paths": { + "~/*": ["app/*"], + "@/*": ["app/*"] + } + }, + "include": ["./**/*"], + "exclude": ["node_modules", "platforms"] +}