@magento/pwa-buildpack
Version:
Build/Layout optimization tooling and Peregrine framework adapters for the Magento PWA
207 lines (179 loc) • 6.87 kB
JavaScript
/**
* Create a Webpack configuration object customized for your project.
* @module Buildpack/WebpackTools
*/
const { promisify } = require('util');
const stat = promisify(require('fs').stat);
const path = require('path');
const loadEnvironment = require('../../Utilities/loadEnvironment');
const getClientConfig = require('./getClientConfig');
const getModuleRules = require('./getModuleRules');
const getResolveLoader = require('./getResolveLoader');
const getSpecialFlags = require('./getSpecialFlags');
const MagentoResolver = require('../MagentoResolver');
const BuildBus = require('../../BuildBus');
const BuildBusPlugin = require('../plugins/BuildBusPlugin');
const ModuleTransformConfig = require('../ModuleTransformConfig');
/**
* Helps convert PWA Studio Buildpack settings and project properties into
* Webpack configuration.
* @typedef {Object} WebpackConfigHelper
* @property {boolean} mode - Webpack mode derived from env:
* 'development'|'production'|'none'
* @property {string} context - Root directory of project
* @property {boolean} babelRootMode - Babel config search mode:
* 'root'|'upward'|'upward-optional
* @property {Object} paths - Relevant Webpack paths
* @property {string} paths.root - Project root, equivalent to Webpack `context`
* @property {string} paths.src - Source code folder, should be a subdirectory
* of root
* @property {Buildpack/WebpackTools~hasSpecialFlags} hasFlag - Returns a list
* of real paths to modules which have set the provided flag. Use for module
* rules.
* @property {Buildpack/Utilities~ProjectConfiguration} projectConfig -
* Configuration object for retrieving any environment variable setting for
* the project being built.
* @property {MagentoResolver} resolver - Module resolver function for this
* build
* @property {Object} transformRequests - Map of requests to apply loader
* transforms to individual modules.
* @property {Object} transformRequests.babel - Map of individual ES modules to
* requests to transform them via Babel.
* @property {Buildpack/BuildBus.BuildBus} bus - {@link BuildBus} for the currently running task.
* Will be used to call build-specific targets.
*/
/**
* @ignore
* We need a root directory for the app in order to build all paths relative to
* that app root. It's not safe to use process.cwd() here because that depends
* on what directory Node is run in. The project root should be the dir of the
* webpack.config.js which called this function.
*
* There is no safe way to get the path of this function's caller, so instead we
* expect that the webpack.config.js will do:
*
* configureWebpack(__dirname);
*/
async function validateRoot(appRoot) {
if (!appRoot) {
throw new Error(
'Must provide the root directory of the PWA as the "context" property of configureWebpack() options. `configureWebpack()`. In webpack.config.js, the recommended code is `configureWebpack({ context: __dirname })` to set the context to the exact location of webpack.config.js.'
);
}
// If root doesn't exist, an ENOENT will throw here and log to stderr.
const dirStat = await stat(appRoot);
if (!dirStat.isDirectory()) {
throw new Error(
`Provided application root "${appRoot}" is not a directory.`
);
}
}
async function getBabelRootMode(appRoot) {
try {
await stat(path.resolve(appRoot, 'babel.config.js'));
return 'root';
} catch (e) {
return 'upward';
}
}
function getMode(cliEnv = {}, projectConfig) {
if (cliEnv.mode) {
return cliEnv.mode;
}
if (projectConfig.isProd) {
return 'production';
}
return 'development';
}
/**
* Create a configuration object to pass to Webpack for build. Use this in
* the `webpack.config.js` file in your project root.
*
* @static
* @returns An array of two Webpack configurations for simultaneously building the client bundles and the ServiceWorker bundle.
*/
async function configureWebpack(options) {
const { context } = options;
await validateRoot(context);
let stats;
/* istanbul ignore next: this is an internal debug mechanism for now */
if (process.env.DEBUG && process.env.DEBUG.includes('BuildBus')) {
BuildBus.enableTracking();
stats = {
logging: 'verbose',
loggingDebug: ['BuildBusPlugin'],
assets: false,
chunks: false,
chunkGroups: false,
chunkModules: false,
chunkOrigins: false,
entrypoints: false,
performance: false,
publicPath: false,
reasons: false,
timings: false,
version: false
};
}
const busTrackingQueue = [];
const bus = BuildBus.for(context);
bus.attach('configureWebpack', (...args) => busTrackingQueue.push(args));
bus.init();
const babelRootMode = await getBabelRootMode(context);
const projectConfig = await loadEnvironment(context);
if (projectConfig.error) {
throw projectConfig.error;
}
const paths = {
src: path.resolve(context, 'src'),
output: path.resolve(context, 'dist')
};
const isAC =
projectConfig.env.MAGENTO_BACKEND_EDITION === 'AC' ||
projectConfig.env.MAGENTO_BACKEND_EDITION === 'EE';
const resolverOpts = {
isAC,
paths: {
root: context
}
};
if (options.alias) {
resolverOpts.alias = { ...options.alias };
}
const resolver = new MagentoResolver(resolverOpts);
const hasFlag = await getSpecialFlags(options.special, bus, resolver);
const mode = getMode(options.env, projectConfig);
const transforms = new ModuleTransformConfig(
resolver,
require(path.resolve(context, 'package.json')).name
);
/** @typedef {import('../../BuildBus/declare-base)} BuiltinTargets */
await bus
.getTargetsOf('@magento/pwa-buildpack')
.transformModules.promise(x => transforms.add(x));
const transformRequests = await transforms.toLoaderOptions();
const configHelper = {
mode,
context,
babelRootMode,
paths,
hasFlag,
projectConfig,
resolver,
stats,
transformRequests,
bus
};
const clientConfig = await getClientConfig({
...configHelper,
vendor: options.vendor || []
});
const buildBusPlugin = new BuildBusPlugin(bus, busTrackingQueue);
clientConfig.plugins.unshift(buildBusPlugin);
return clientConfig;
}
configureWebpack.getClientConfig = getClientConfig;
configureWebpack.getModuleRules = getModuleRules;
configureWebpack.getResolveLoader = getResolveLoader;
configureWebpack.getSpecialFlags = getSpecialFlags;
module.exports = configureWebpack;