From 9b4e4c0e7d5562ff4f6d09f7f95f08952b12be64 Mon Sep 17 00:00:00 2001 From: Eduardo Speroni Date: Sat, 20 Nov 2021 18:25:27 -0300 Subject: [PATCH] feat: dynamic imports --- packages/webpack5/src/bin/index.ts | 5 +-- .../webpack5/src/helpers/dynamicImports.ts | 36 +++++++++++++++++++ packages/webpack5/src/index.ts | 34 +++++++++--------- 3 files changed, 55 insertions(+), 20 deletions(-) create mode 100644 packages/webpack5/src/helpers/dynamicImports.ts diff --git a/packages/webpack5/src/bin/index.ts b/packages/webpack5/src/bin/index.ts index b8e1656bb..001032396 100644 --- a/packages/webpack5/src/bin/index.ts +++ b/packages/webpack5/src/bin/index.ts @@ -8,6 +8,7 @@ import path from 'path'; import fs from 'fs'; import { parseEnvFlags } from '../cli/parseEnvFlags'; +import { tryRequireThenImport } from '../helpers/dynamicImports'; const defaultConfig = path.resolve( __dirname, @@ -47,7 +48,7 @@ program .option('--config [path]', 'config path') .option('--watch', 'watch for changes') .allowUnknownOption() - .action((options, command) => { + .action(async (options, command) => { const env = parseEnvFlags(command.args); // add --env into the env object // for example if we use --env prod @@ -75,7 +76,7 @@ program // todo: guard against invalid config let configuration: webpack.Configuration; try { - configuration = require(configPath)(env); + configuration = await (await tryRequireThenImport(configPath))(env); } catch (err) { console.log(err); } diff --git a/packages/webpack5/src/helpers/dynamicImports.ts b/packages/webpack5/src/helpers/dynamicImports.ts new file mode 100644 index 000000000..42b48bcc9 --- /dev/null +++ b/packages/webpack5/src/helpers/dynamicImports.ts @@ -0,0 +1,36 @@ +export function dynamicImportLoaderFactory() { + let importESM; + + try { + importESM = new Function('id', 'return import(id);'); + } catch (e) { + importESM = null; + } + + return importESM; +} + +export async function tryRequireThenImport(module: string) { + let result; + + try { + result = require(module); + } catch (error) { + const dynamicImportLoader = dynamicImportLoaderFactory(); + if (error.code === 'ERR_REQUIRE_ESM' && dynamicImportLoader) { + result = await dynamicImportLoader(module); + result = result.default; + + return result; + } + + throw error; + } + + // For babel/typescript + if (result && typeof result === 'object' && 'default' in result) { + result = result.default || {}; + } + + return result || {}; +} diff --git a/packages/webpack5/src/index.ts b/packages/webpack5/src/index.ts index d9e606684..419e481a1 100644 --- a/packages/webpack5/src/index.ts +++ b/packages/webpack5/src/index.ts @@ -170,28 +170,26 @@ export async function resolveChainableConfig(): Promise { // todo: allow opt-out await applyExternalConfigs(); - webpackChains - .splice(0) - .sort((a, b) => { - return a.order - b.order; - }) - .forEach(({ chainFn, plugin }) => { - try { - chainFn(config, env); - } catch (err) { - if (plugin) { - // catch and print errors from plugins - return error(` + for (const { chainFn, plugin } of webpackChains.splice(0).sort((a, b) => { + return a.order - b.order; + })) { + try { + await chainFn(config, env); + } catch (err) { + if (plugin) { + // catch and print errors from plugins + error(` Unable to apply chain function from: ${plugin}. Error is: ${err} `); - } - - // otherwise throw - as the error is likely from the user config - // or missing env flags (eg. missing platform) - throw err; + continue; } - }); + + // otherwise throw - as the error is likely from the user config + // or missing env flags (eg. missing platform) + throw err; + } + } if (env.verbose) { info('Resolved chainable config (before merges):');