diff --git a/packages/webpack5/__tests__/index.spec.ts b/packages/webpack5/__tests__/index.spec.ts index de6065046..456780fdb 100644 --- a/packages/webpack5/__tests__/index.spec.ts +++ b/packages/webpack5/__tests__/index.spec.ts @@ -1,8 +1,11 @@ -// @ts-ignore -import Config from 'webpack-chain'; -import * as webpack from '../src'; - describe('@nativescript/webpack', () => { + let webpack: typeof import('../src'); + + beforeEach(() => { + jest.resetModules(); + webpack = require('../src'); + }); + it('exports the public api', () => { expect(webpack.init).toBeInstanceOf(Function); expect(webpack.useConfig).toBeInstanceOf(Function); @@ -27,7 +30,29 @@ describe('@nativescript/webpack', () => { expect(chainFn).toHaveBeenCalledTimes(1); expect(chainFn).toHaveBeenCalledWith(config, {}); - expect(config).toBeInstanceOf(Config); + }); + + it('applies chain configs in the right order', () => { + webpack.useConfig(false); + let lastCalled = false; + + // this is registered before chainFnNormal + // however, should be called after chainFnNormal + const chainFnLast = jest.fn((config) => { + lastCalled = true; + expect(config.normal).toBe(true); + }); + webpack.chainWebpack(chainFnLast, { last: true }); + + const chainFnNormal = jest.fn((config) => { + config.normal = true; + + // chainFnLast should not have been called yet + expect(lastCalled).toBe(false); + }); + webpack.chainWebpack(chainFnNormal); + + webpack.resolveChainableConfig(); }); it('applies merge configs', () => { diff --git a/packages/webpack5/src/index.ts b/packages/webpack5/src/index.ts index 73aa37bff..e6b51875a 100644 --- a/packages/webpack5/src/index.ts +++ b/packages/webpack5/src/index.ts @@ -30,7 +30,11 @@ export interface IWebpackEnv { // todo: add others } -let webpackChains: any[] = []; +let webpackChains = { + base: [], + normal: [], + last: [], +}; let webpackMerges: any[] = []; let explicitUseConfig = false; let hasInitialized = false; @@ -54,7 +58,7 @@ export function init(_env: IWebpackEnv) { export function useConfig(config: keyof typeof defaultConfigs | false) { explicitUseConfig = true; if (config) { - webpackChains.unshift(configs[config]); + webpackChains.base.push(configs[config]); } } @@ -62,8 +66,8 @@ export function chainWebpack( chainFn: (config: Config, env: IWebpackEnv) => any, options?: { last?: boolean } ) { - // todo: handle options.last by storing them in a separate array? - webpackChains.push(chainFn); + const type = options?.last ? 'last' : 'normal'; + webpackChains[type].push(chainFn); } export function mergeWebpack( @@ -75,7 +79,7 @@ export function mergeWebpack( webpackMerges.push(mergeFn); } -export function resolveChainableConfig() { +export function resolveChainableConfig(): Config { const config = new Config(); if (!explicitUseConfig) { @@ -86,10 +90,19 @@ export function resolveChainableConfig() { // todo: allow opt-out applyExternalConfigs(); - // this applies all chain configs - webpackChains.forEach((chainFn) => { - return chainFn(config, env); - }); + const applyChains = (chains) => { + // this applies the chain configs + chains.forEach((chainFn) => { + return chainFn(config, env); + }); + }; + + // first we apply base configs + applyChains(webpackChains.base); + // then regular configs + applyChains(webpackChains.normal); + // finally configs that opted to be called last + applyChains(webpackChains.last); if (env.verbose) { info('Resolved chainable config (before merges):'); @@ -99,7 +112,9 @@ export function resolveChainableConfig() { return config; } -export function resolveConfig(chainableConfig = resolveChainableConfig()) { +export function resolveConfig( + chainableConfig = resolveChainableConfig() +): webpack.Configuration { if (!hasInitialized) { throw error('resolveConfig() must be called after init()'); }