src/services/configurations/pluginsConfiguration.js
const fs = require('fs-extra');
const postcss = require('postcss');
const LazyResult = require('postcss/lib/lazy-result');
const postcssModules = require('postcss-modules');
const builtinModules = require('builtin-modules');
const nodeSass = require('node-sass');
const { provider } = require('jimple');
const ConfigurationFile = require('../../abstracts/configurationFile');
/**
* This service creates a configuration with all the settings for the plugins this build engine
* uses.
* @extends {ConfigurationFile}
*/
class RollupPluginSettingsConfiguration extends ConfigurationFile {
/**
* @param {Logger} appLogger To send to the plugins that support a logger.
* @param {BabelConfiguration} babelConfiguration To get the target Babel configuration.
* @param {BabelHelper} babelHelper To disable the `modules` setting of the Babel
* env preset for a target, as Rollup requires.
* @param {Events} events To reduce the settings.
* @param {Object} packageInfo To get the dependencies and define them as
* externals for Node targets.
* @param {PathUtils} pathUtils Require by `ConfigurationFile` in order to
* build the path to the overwrite file. It's
* also used to build the paths of SSL
* certificates the dev server plugin may use.
* @param {Object} rollupPluginInfo To get the name of the _"sub packages"_ the
* plugin provides and that should be marked as
* external.
* @param {TargetsHTML} targetsHTML To get the path to a target HTML template
* file.
*/
constructor(
appLogger,
babelConfiguration,
babelHelper,
events,
packageInfo,
pathUtils,
rollupPluginInfo,
targetsHTML
) {
super(pathUtils, 'rollup/plugins.config.js');
/**
* A local reference for the `appLogger` service.
* @type {Logger}
*/
this.appLogger = appLogger;
/**
* A local reference for the `babelConfiguration` service.
* @type {BabelConfiguration}
*/
this.babelConfiguration = babelConfiguration;
/**
* A local reference for the `babelHelper` service.
* @type {BabelHelper}
*/
this.babelHelper = babelHelper;
/**
* A local reference for the `events` service.
* @type {Events}
*/
this.events = events;
/**
* The project `package.json` information.
* @type {Object}
*/
this.packageInfo = packageInfo;
/**
* A local reference for the plugin information.
* @type {RollupPluginInfo}
*/
this.rollupPluginInfo = rollupPluginInfo;
/**
* A local reference for the `targetsHTML` service.
* @type {TargetsHTML}
*/
this.targetsHTML = targetsHTML;
}
/**
* Creates the plugins settings for the required target.
* This method uses the reducer events `rollup-plugin-settings-configuration-for-node` or
* `rollup-plugin-settings-configuration-for-browser`, depending on the target type, and then
* `rollup-plugin-settings-configuration`. The event receives the configuration object, the
* `params` and it expects an updated configuration object on return.
* @param {RollupConfigurationParams} params A dictionary generated by the top service building
* the configuration and that includes things like the
* target information, its entry settings, output
* paths, etc.
* @param {Function} stats A function to send to the plugins that support
* logging stats entries.
* @return {Object}
*/
createConfig(params, stats) {
// Get the external settings.
const external = this._getExternalSettings(params);
// Define the basic settings.
const settings = {
external,
globals: this._getGlobalVariablesSettings(params, external.external),
resolve: this._getResolveSettings(params, stats),
extraWatch: this._getExtraWatchSettings(params, stats),
moduleReplace: this._getModuleReplaceSettings(params, stats),
babel: this._getBabelSettings(params, stats),
commonjs: this._getCommonJSSettings(params, stats),
sass: this._getSASSSettings(params, stats),
css: this._getCSSSettings(params, stats),
stylesheetAssets: this._getStyleheetAssetsSettings(params, stats),
stylesheetModulesFixer: this._getStylesheetModulesFixerSettings(params, stats),
html: this._getHTMLSettings(params, stats),
json: this._getJSONSettings(params, stats),
urls: this._getURLsSettings(params, stats),
watch: this._getWatchSettings(params, stats),
terser: this._getTerserSettings(params, stats),
compression: this._getCompressionSettings(params, stats),
copy: this._getCopySettings(params, stats),
visualizer: this._getVisualizerSettings(params, stats),
statsLog: this._getStatsLogSettings(params),
};
let eventName;
// Based on the target type, define the event reducer name and add specific settings.
if (params.target.is.node) {
eventName = 'rollup-plugin-settings-configuration-for-node';
settings.nodeRunner = this._getNodeRunnerSettings(params);
settings.stylesheetAssetsHelper = this._getStyleheetAssetsHelperSettings(params);
} else {
eventName = 'rollup-plugin-settings-configuration-for-browser';
settings.template = this._getTemplateSettings(params, stats);
settings.devServer = this._getDevServerSettings(params);
}
// Return the reduced settings.
return this.events.reduce(
[eventName, 'rollup-plugin-settings-configuration'],
settings,
params
);
}
/**
* Defines the settings for the external dependencies.
* This method uses the reducer event `rollup-external-plugin-settings-configuration-for-browser`
* or `rollup-external-plugin-settings-configuration-for-node`, depending on the target type, and
* then `rollup-external-plugin-settings-configuration`. The event receives the settings, the
* `params` and expects new settings on return.
* @param {RollupConfigurationParams} params A dictionary generated by the top service building
* the configuration and that includes things like the
* target information, its entry settings, output
* paths, etc.
* @return {Object}
* @access protected
* @ignore
*/
_getExternalSettings(params) {
const { target, buildType } = params;
// Define the list of modules that shuld be external.
const external = [];
// Push modules the target is excluding by configuration.
if (target.excludeModules) {
external.push(...target.excludeModules);
}
// If the target is for Node...
if (target.is.node) {
// Push all the Node builtin modules
external.push(...builtinModules);
// Push the plugin _"sub modules"_.
external.push(...this.rollupPluginInfo.external.map((dependencyName) => (
`${this.rollupPluginInfo.name}/${dependencyName}`
)));
// Push the production dependencies.
external.push(...Object.keys(this.packageInfo.dependencies));
// And if the build is for development, push the dev dependencies too.
if (buildType === 'development') {
external.push(...Object.keys(this.packageInfo.devDependencies));
}
}
// Wrap the list on an object.
const settings = { external };
const eventName = target.is.node ?
'rollup-external-plugin-settings-configuration-for-node' :
'rollup-external-plugin-settings-configuration-for-browser';
// Return the reduced configuration.
return this.events.reduce(
[eventName, 'rollup-external-plugin-settings-configuration'],
settings,
params
);
}
/**
* Defines the settings for the global variables on the bundle. They are actually based on the
* modules that are handled as external.
* This method uses the reducer event
* `rollup-global-variables-plugin-settings-configuration-for-browser` or
* `rollup-global-variables-plugin-settings-configuration-for-node`, depending on the target
* type, and then `rollup-global-variables-plugin-settings-configuration`. The event receives
* the settings, the `params` and expects new settings on return.
* @param {RollupConfigurationParams} params A dictionary generated by the top service building
* the configuration and that includes things like the
* target information, its entry settings, output
* paths, etc.
* @param {Array} external The list of modules that will be handled as
* external dependencies.
* @return {Object}
* @access protected
* @ignore
*/
_getGlobalVariablesSettings(params, external) {
// Define the settings dictionary.
const settings = {};
// Loop all the external modules.
external.forEach((name) => {
// Format the module name into a variable.
const globalName = name
.replace(/\//g, '-')
.replace(/-(\w)/g, (match, letter) => letter.toUpperCase());
// Set it on the dictionary using the module name as key.
settings[name] = globalName;
});
const eventName = params.target.is.node ?
'rollup-global-variables-settings-configuration-for-node' :
'rollup-global-variables-settings-configuration-for-browser';
// return the reduced configuration.
return this.events.reduce(
[eventName, 'rollup-global-variables-settings-configuration'],
settings,
params
);
}
/**
* Defines the settings for the `resolve` plugin.
* This method uses the reducer event `rollup-resolve-plugin-settings-configuration-for-browser`
* or `rollup-resolve-plugin-settings-configuration-for-node`, depending on the target type, and
* then `rollup-resolve-plugin-settings-configuration`. The event receives the settings, the
* `params` and expects new settings on return.
* @param {RollupConfigurationParams} params A dictionary generated by the top service building
* the configuration and that includes things like the
* target information, its entry settings, output
* paths, etc.
* @return {Object}
* @access protected
* @ignore
*/
_getResolveSettings(params) {
const { target } = params;
const settings = {
// Add just for basic JS files and JSON.
extensions: ['.js', '.json', '.jsx', '.ts', '.tsx'],
browser: target.is.browser,
preferBuiltins: target.is.node,
};
const eventName = target.is.node ?
'rollup-resolve-plugin-settings-configuration-for-node' :
'rollup-resolve-plugin-settings-configuration-for-browser';
// Return the reduced configuration.
return this.events.reduce(
[eventName, 'rollup-resolve-plugin-settings-configuration'],
settings,
params
);
}
/**
* Defines the settings for the `extraWatch` plugin, which is a projext's plugin that takes
* care watching additional files and reloading the bundle when any of them change.
* This method uses the reducer event
* `rollup-extra-watch-plugin-settings-configuration-for-browser` or
* `rollup-extra-watch-plugin-settings-configuration-for-node`, depending on the target type, and
* then `rollup-extra-watch-plugin-settings-configuration`. The event receives the list of files,
* the `params` and expects a new list on return.
* @param {RollupConfigurationParams} params A dictionary generated by the top service building
* the configuration and that includes things like the
* target information, its entry settings, output
* paths, etc.
* @return {Object}
* @access protected
* @ignore
*/
_getExtraWatchSettings(params) {
const { additionalWatch, input, target } = params;
const settings = [
input,
...additionalWatch.map((filepath) => this.pathUtils.join(filepath)),
];
const eventName = target.is.node ?
'rollup-extra-watch-plugin-settings-configuration-for-node' :
'rollup-extra-watch-plugin-settings-configuration-for-browser';
// Return the reduced configuration.
return this.events.reduce(
[eventName, 'rollup-extra-watch-plugin-settings-configuration'],
settings,
params
);
}
/**
* Defines the settings for the `moduleReplace` plugin, which is a projext's plugin that allows
* the replacement of strings on specific modules.
* For example, `core-js` has a module that does `const location = `, which causes an
* inifinite redirection loop, so (for now), the plugin will take care of changing `location`
* to `_location`.
* This method uses the reducer event
* `rollup-module-replace-plugin-settings-configuration-for-browser` or
* `rollup-module-replace-plugin-settings-configuration-for-node`, depending on the target
* type, and then `rollup-module-replace-plugin-settings-configuration`. The event receives the
* plugin settings, the `params` and expects new settings on return.
* @param {RollupConfigurationParams} params A dictionary generated by the top service building
* the configuration and that includes things like the
* target information, its entry settings, output
* paths, etc.
* @return {Object}
* @access protected
* @ignore
*/
_getModuleReplaceSettings(params) {
const { target, buildType } = params;
const settings = {
instructions: [],
sourceMap: !!(target.sourceMap && target.sourceMap[buildType]),
};
if (target.is.browser) {
settings.instructions.push({
module: /node_modules\/core-js\/internals\/task\.js$/i,
search: /(\s*)location(\.|\s*=)/i,
replace: '$1_location$2',
});
}
const eventName = target.is.node ?
'rollup-module-replace-plugin-settings-configuration-for-node' :
'rollup-module-replace-plugin-settings-configuration-for-browser';
// Return the reduced configuration.
return this.events.reduce(
[eventName, 'rollup-module-replace-plugin-settings-configuration'],
settings,
params
);
}
/**
* Defines the settings for the `babel` plugin.
* This method uses the reducer event `rollup-babel-plugin-settings-configuration-for-browser`
* or `rollup-babel-plugin-settings-configuration-for-node`, depending on the target type, and
* then `rollup-babel-plugin-settings-configuration`. The event receives the settings, the
* `params` and expects new settings on return.
* @param {RollupConfigurationParams} params A dictionary generated by the top service building
* the configuration and that includes things like the
* target information, its entry settings, output
* paths, etc.
* @return {Object}
* @access protected
* @ignore
*/
_getBabelSettings(params) {
const { target, targetRules } = params;
// Get the rule for JS files.
const jsRule = targetRules.js.getRule();
// Get the target Babel configuration.
const baseConfiguration = this.babelConfiguration.getConfigForTarget(target);
// Disable the `modules` feature for the `env` preset.
const configuration = this.babelHelper.disableEnvPresetModules(baseConfiguration);
// Define the plugin settings.
const settings = Object.assign(
{},
configuration,
{
extensions: ['.js', '.jsx', '.ts', '.tsx'],
// The plugin doesn't support RegExp, so it will use the glob patterns.
include: [...jsRule.files.glob.include],
exclude: [...jsRule.files.glob.exclude],
}
);
const eventName = target.is.node ?
'rollup-babel-plugin-settings-configuration-for-node' :
'rollup-babel-plugin-settings-configuration-for-browser';
// Return the reduced configuration.
return this.events.reduce(
[eventName, 'rollup-babel-plugin-settings-configuration'],
settings,
params
);
}
/**
* Defines the settings for the `commonjs` plugin.
* This method uses the reducer event `rollup-commonjs-plugin-settings-configuration-for-browser`
* or `rollup-commonjs-plugin-settings-configuration-for-node`, depending on the target type, and
* then `rollup-commonjs-plugin-settings-configuration`. The event receives the settings, the
* `params` and expects new settings on return.
* @param {RollupConfigurationParams} params A dictionary generated by the top service building
* the configuration and that includes things like the
* target information, its entry settings, output
* paths, etc.
* @return {Object}
* @access protected
* @ignore
*/
_getCommonJSSettings(params) {
// Define the plugin settings.
const settings = {
include: [
new RegExp(this.pathUtils.join('config'), 'i'),
/node_modules\//i,
],
};
const eventName = params.target.is.node ?
'rollup-commonjs-plugin-settings-configuration-for-node' :
'rollup-commonjs-plugin-settings-configuration-for-browser';
// Return the reduced configuration.
return this.events.reduce(
[eventName, 'rollup-commonjs-plugin-settings-configuration'],
settings,
params
);
}
/**
* Defines the settings for the `sass` plugin.
* This method uses the reducer event `rollup-sass-plugin-settings-configuration-for-browser`
* or `rollup-sass-plugin-settings-configuration-for-node`, depending on the target type, and
* then `rollup-sass-plugin-settings-configuration`. The event receives the settings, the
* `params` and expects new settings on return.
* @param {RollupConfigurationParams} params A dictionary generated by the top service building
* the configuration and that includes things like the
* target information, its entry settings, output
* paths, etc.
* @return {Object}
* @access protected
* @ignore
*/
_getSASSSettings(params) {
const { target, paths, targetRules } = params;
// Get the rule for SCSS files.
const scssRule = targetRules.scss.getRule();
// Define the plugin settings.
const settings = {
include: [...scssRule.files.include],
exclude: [...scssRule.files.exclude],
runtime: nodeSass,
options: {
sourceMapEmbed: true,
outputStyle: 'compressed',
includePaths: ['node_modules'],
data: '',
},
processor: this._getStylesProcessor(target.css.modules),
failOnError: true,
};
// If the CSS should be injected, turn on the flag.
if (target.css.inject) {
settings.insert = true;
} else if (target.is.browser) {
// If the CSS shouldn't be injected and the target is for browser, define a bundle path.
settings.output = `${target.paths.build}/${paths.css}`;
} else {
// Otherwise, it means that is a Node target, so just return the code.
settings.output = false;
}
const eventName = target.is.node ?
'rollup-sass-plugin-settings-configuration-for-node' :
'rollup-sass-plugin-settings-configuration-for-browser';
// Return the reduced configuration.
return this.events.reduce(
[eventName, 'rollup-sass-plugin-settings-configuration'],
settings,
params
);
}
/**
* Defines the settings for the `css` plugin.
* This method uses the reducer event `rollup-css-plugin-settings-configuration-for-browser`
* or `rollup-css-plugin-settings-configuration-for-node`, depending on the target type, and
* then `rollup-css-plugin-settings-configuration`. The event receives the settings, the
* `params` and expects new settings on return.
* @param {RollupConfigurationParams} params A dictionary generated by the top service building
* the configuration and that includes things like the
* target information, its entry settings, output
* paths, etc.
* @param {Function} stats A function to send to the plugins that support
* logging stats entries.
* @return {Object}
* @access protected
* @ignore
*/
_getCSSSettings(params, stats) {
const { target, paths, targetRules } = params;
// Get the rule for SCSS files.
const cssRule = targetRules.css.getRule();
// Define the plugin settings.
const settings = {
include: [...cssRule.files.include],
exclude: [...cssRule.files.exclude],
processor: this._getStylesProcessor(false, { map: true }),
stats,
};
// If the CSS should be injected, turn on the flag.
if (target.css.inject) {
settings.insert = true;
} else if (target.is.browser) {
// If the CSS shouldn't be injected and the target is for browser, define a bundle path.
settings.output = `${target.paths.build}/${paths.css}`;
} else {
// Otherwise, it means that is a Node target, so just return the code.
settings.output = false;
}
const eventName = target.is.node ?
'rollup-css-plugin-settings-configuration-for-node' :
'rollup-css-plugin-settings-configuration-for-browser';
// Return the reduced configuration.
return this.events.reduce(
[eventName, 'rollup-css-plugin-settings-configuration'],
settings,
params
);
}
/**
* Defines the settings for the `stylesheetAssets` plugin, which is a projext's plugin that takes
* care of copying and fixing the paths for the files linked on stylesheets.
* This method uses the reducer event
* `rollup-stylesheet-assets-plugin-settings-configuration-for-browser` or
* `rollup-stylesheet-assets-plugin-settings-configuration-for-node`, depending on the target
* type, and then `rollup-stylesheet-assets-plugin-settings-configuration`. The event receives
* the settings, the `params` and expects new settings on return.
* @param {RollupConfigurationParams} params A dictionary generated by the top service building
* the configuration and that includes things like the
* target information, its entry settings, output
* paths, etc.
* @param {Function} stats A function to send to the plugins that support
* logging stats entries.
* @return {Object}
* @access protected
* @ignore
*/
_getStyleheetAssetsSettings(params, stats) {
const {
target,
paths,
output,
} = params;
// Get the rules for common assets.
const assetsRules = this._getAssetsRules(params);
/**
* Define the file the plugin will parse. If the target injects the CSS or is a Node target,
* use the main JS file, otherwise, the path for bundling CSS.
*/
const stylesheet = target.css.inject || target.is.node ?
output.file :
`${target.paths.build}/${paths.css}`;
// Define the plugin settings.
const settings = {
stylesheet,
stats,
urls: [
assetsRules.fonts,
assetsRules.images,
],
};
const eventName = target.is.node ?
'rollup-stylesheet-assets-plugin-settings-configuration-for-node' :
'rollup-stylesheet-assets-plugin-settings-configuration-for-browser';
// Return the reduced configuration.
return this.events.reduce(
[eventName, 'rollup-stylesheet-assets-plugin-settings-configuration'],
settings,
params
);
}
/**
* Defines the settings for the `stylesheetModulesFixer` plugin, which is a projext's plugin that
* parses modules for CSS (stylsheet that Rollup transforms into ES modules) and if they inject
* the CSS on the browser or they code was moved to a separate bundle, replace their default
* exports for the named export for CSS Modules locals.
* This method uses the reducer event
* `rollup-stylesheet-modules-fixer-plugin-settings-configuration-for-browser` or
* `rollup-stylesheet-modules-fixer-plugin-settings-configuration-for-node`, depending on the
* target type, and then `rollup-stylesheet-modules-fixer-plugin-settings-configuration`. The
* event receives the settings, the `params` and expects new settings on return.
* @param {RollupConfigurationParams} params A dictionary generated by the top service building
* the configuration and that includes things like the
* target information, its entry settings, output
* paths, etc.
* @return {Object}
* @access protected
* @ignore
*/
_getStylesheetModulesFixerSettings(params) {
const { target, targetRules } = params;
// Get both SCSS and CSS rules.
const scssRule = targetRules.scss.getRule();
const cssRule = targetRules.css.getRule();
// Define the plugin settings.
const settings = {
include: [
...scssRule.files.include,
...cssRule.files.include,
],
exclude: [
...scssRule.files.exclude,
...cssRule.files.exclude,
],
};
const eventName = target.is.node ?
'rollup-stylesheet-modules-fixer-plugin-settings-configuration-for-node' :
'rollup-stylesheet-modules-fixer-plugin-settings-configuration-for-browser';
// Return the reduced configuration.
return this.events.reduce(
[eventName, 'rollup-stylesheet-modules-fixer-plugin-settings-configuration'],
settings,
params
);
}
/**
* Defines the settings for the `stylesheetAssets` helper plugin, which is a projext's plugin
* that wraps the default exports of stylesheets Rollup transforms into ES modules so
* the `stylesheetAssets` plugin can find them and parse them.
* This method uses the reducer event
* `rollup-stylesheet-assets-helper-plugin-settings-configuration`, it receives the settings,
* the `params` and expects new settings on return.
* @param {RollupConfigurationParams} params A dictionary generated by the top service building
* the configuration and that includes things like the
* target information, its entry settings, output
* paths, etc.
* @return {Object}
* @access protected
* @ignore
*/
_getStyleheetAssetsHelperSettings(params) {
const { targetRules } = params;
// Get both SCSS and CSS rules.
const scssRule = targetRules.scss.getRule();
const cssRule = targetRules.css.getRule();
// Define the plugin settings.
const settings = {
include: [
...scssRule.files.include,
...cssRule.files.include,
],
exclude: [
...scssRule.files.exclude,
...cssRule.files.exclude,
],
};
// Return the reduced configuration.
return this.events.reduce(
'rollup-stylesheet-assets-helper-plugin-settings-configuration',
settings,
params
);
}
/**
* Defines the settings for the `html` plugin.
* This method uses the reducer event `rollup-html-plugin-settings-configuration-for-browser`
* or `rollup-html-plugin-settings-configuration-for-node`, depending on the target type, and
* then `rollup-html-plugin-settings-configuration`. The event receives the settings, the
* `params` and expects new settings on return.
* @param {RollupConfigurationParams} params A dictionary generated by the top service building
* the configuration and that includes things like the
* target information, its entry settings, output
* paths, etc.
* @return {Object}
* @access protected
* @ignore
*/
_getHTMLSettings(params) {
/**
* There are no settings, but because the plugin is used, the method reduces the empty
* configuration so other plugins/services can update it if needed.
*/
const settings = {};
const eventName = params.target.is.node ?
'rollup-html-plugin-settings-configuration-for-node' :
'rollup-html-plugin-settings-configuration-for-browser';
// Return the reduced configuration.
return this.events.reduce(
[eventName, 'rollup-html-plugin-settings-configuration'],
settings,
params
);
}
/**
* Defines the settings for the `json` plugin.
* This method uses the reducer event `rollup-json-plugin-settings-configuration-for-browser`
* or `rollup-json-plugin-settings-configuration-for-node`, depending on the target type, and
* then `rollup-json-plugin-settings-configuration`. The event receives the settings, the
* `params` and expects new settings on return.
* @param {RollupConfigurationParams} params A dictionary generated by the top service building
* the configuration and that includes things like the
* target information, its entry settings, output
* paths, etc.
* @return {Object}
* @access protected
* @ignore
*/
_getJSONSettings(params) {
/**
* There are no settings, but because the plugin is used, the method reduces the empty
* configuration so other plugins/services can update it if needed.
*/
const settings = {};
const eventName = params.target.is.node ?
'rollup-json-plugin-settings-configuration-for-node' :
'rollup-json-plugin-settings-configuration-for-browser';
// Return the reduced configuration.
return this.events.reduce(
[eventName, 'rollup-json-plugin-settings-configuration'],
settings,
params
);
}
/**
* Defines the settings for the `urls` plugin, which is a projext's plugin that transforms
* files matching a filter and copies its contents into a new file on the output directory,
* then it replaces its default export with a URL for them.
* This method uses the reducer event `rollup-urls-plugin-settings-configuration-for-browser` or
* `rollup-urls-plugin-settings-configuration-for-node`, depending on the target type, and then
* `rollup-urls-plugin-settings-configuration`. The event receives the settings, the `params`
* and expects new settings on return.
* @param {RollupConfigurationParams} params A dictionary generated by the top service building
* the configuration and that includes things like the
* target information, its entry settings, output
* paths, etc.
* @param {Function} stats A function to send to the plugins that support
* logging stats entries.
* @return {Object}
* @access protected
* @ignore
*/
_getURLsSettings(params, stats) {
const { target } = params;
// Get the rules for common assets.
const assetsRules = this._getAssetsRules(params);
// Define the plugin settings.
const settings = {
urls: [
assetsRules.fonts,
assetsRules.images,
assetsRules.favicon,
],
stats,
};
const eventName = target.is.node ?
'rollup-urls-plugin-settings-configuration-for-node' :
'rollup-urls-plugin-settings-configuration-for-browser';
// Return the reduced configuration.
return this.events.reduce(
[eventName, 'rollup-urls-plugin-settings-configuration'],
settings,
params
);
}
/**
* Defines the settings for the `template` plugin, which is a projext's plugin that generates
* and HTML file and injects a list of JS and CSS files.
* This method uses the reducer event `rollup-html-plugin-settings-configuration-for-browser` or
* `rollup-html-plugin-settings-configuration-for-node`, depending on the target type, and then
* `rollup-html-plugin-settings-configuration`. The event receives the settings, the `params`
* and expects new settings on return.
* @param {RollupConfigurationParams} params A dictionary generated by the top service building
* the configuration and that includes things like the
* target information, its entry settings, output
* paths, etc.
* @param {Function} stats A function to send to the plugins that support
* logging stats entries.
* @return {Object}
* @access protected
* @ignore
*/
_getTemplateSettings(params, stats) {
const {
target,
paths,
buildType,
output,
} = params;
// Get the rules for common assets.
const assetsRules = this._getAssetsRules(params);
const script = {
src: `/${paths.js}`,
async: 'async',
type: output.format === 'es' ? 'module' : 'text/javascript',
};
// Define the plugin settings.
const settings = {
template: this.targetsHTML.getFilepath(target, false, buildType),
output: `${target.paths.build}/${target.html.filename}`,
stylesheets: target.css.inject ?
[] :
[`/${paths.css}`],
scripts: [script],
urls: [
assetsRules.images,
assetsRules.favicon,
],
stats,
};
// Return the reduced configuration.
return this.events.reduce(
'rollup-template-plugin-settings-configuration',
settings,
params
);
}
/**
* Defines the settings for the Rollup watcher.
* This method uses the reducer event `rollup-watch-plugin-settings-configuration-for-browser` or
* `rollup-watch-plugin-settings-configuration-for-node`, depending on the target type, and then
* `rollup-watch-plugin-settings-configuration`. The event receives the settings, the `params`
* and expects new settings on return.
* @param {RollupConfigurationParams} params A dictionary generated by the top service building
* the configuration and that includes things like the
* target information, its entry settings, output
* paths, etc.
* @return {Object}
* @access protected
* @ignore
*/
_getWatchSettings(params) {
// Define the plugin settings.
const settings = {
clearScreen: false,
};
const eventName = params.target.is.node ?
'rollup-watch-plugin-settings-configuration-for-node' :
'rollup-watch-plugin-settings-configuration-for-browser';
// Return the reduced configuration.
return this.events.reduce(
[eventName, 'rollup-watch-plugin-settings-configuration'],
settings,
params
);
}
/**
* Defines the settings for the `devServer` plugin, which is a projext's plugin for running web
* apps while on development.
* This method uses the reducer event `rollup-dev-server-plugin-settings-configuration`, it
* receives the settings, the `params` and expects new settings on return.
* @param {RollupConfigurationParams} params A dictionary generated by the top service building
* the configuration and that includes things like the
* target information, its entry settings, output
* paths, etc.
* @return {Object}
* @access protected
* @ignore
*/
_getDevServerSettings(params) {
const { target } = params;
const { devServer } = target;
// Define the basic settings.
const settings = {
host: devServer.host,
port: devServer.port,
contentBase: target.paths.build,
historyApiFallback: !!devServer.historyApiFallback,
open: !!devServer.open,
https: null,
logger: this.appLogger,
};
// Loop the SSL settings and load the file for those which have a valid file path.
const sslSettings = {};
/**
* The idea of this flag is that if none of the SSL settings has value, the `ssl` setting
* won't be added.
*/
let atLeastOneSSLSetting = false;
[
'key',
'cert',
'ca',
].forEach((sslSettingName) => {
const file = devServer.ssl[sslSettingName];
// Make sure the setting value is a string.
if (typeof file === 'string') {
const filepath = this.pathUtils.join(file);
// Verify that the file exists.
if (fs.pathExistsSync(filepath)) {
// Turn on the flag.
atLeastOneSSLSetting = true;
// Set the value of the setting with the contents of the file.
sslSettings[sslSettingName] = fs.readFileSync(filepath, 'utf-8');
}
}
});
/**
* If there's at least one setting implemented, set it for the plugin to use, otherwise keep
* it as `undefined`.
*/
if (atLeastOneSSLSetting) {
settings.https = sslSettings;
}
/**
* Build the _"proxied settings"_ in case the server is behind a proxy. This will tell the
* dev server plugin to build a URL using these settings and use it for opening the browser
* and logging messages, instead of the base one.
*/
if (devServer.proxied.enabled) {
/**
* Define the proxied host by checking if one was defined on the settings. If there's no
* host, it will fallback to the one on the base settings.
*/
const proxiedHost = devServer.proxied.host === null ?
settings.host :
devServer.proxied.host;
/**
* Define whether to use HTTPS by checking the settings. If it wasn't specified, it will
* only set it to `true` if one of the SSL files for the base config exists.
*/
const proxiedHTTPS = devServer.proxied.https === null ?
atLeastOneSSLSetting :
devServer.proxied.https;
// Add the `proxied` settings to the object to be returned.
settings.proxied = {
host: proxiedHost,
https: proxiedHTTPS,
};
}
// Return the reduced configuration.
return this.events.reduce(
'rollup-dev-server-plugin-settings-configuration',
settings,
params
);
}
/**
* Defines the settings for the `terser` plugin.
* This method uses the reducer event `rollup-terser-plugin-settings-configuration-for-browser`
* or `rollup-terser-plugin-settings-configuration-for-node`, depending on the target type, and
* then `rollup-terser-plugin-settings-configuration`. The event receives the settings, the
* `params` and expects new settings on return.
* @param {RollupConfigurationParams} params A dictionary generated by the top service building
* the configuration and that includes things like the
* target information, its entry settings, output
* paths, etc.
* @return {Object}
* @access protected
* @ignore
*/
_getTerserSettings(params) {
/**
* There are no settings, but because the plugin is used, the method reduces the empty
* configuration so other plugins/services can update it if needed.
*/
const settings = {};
const eventName = params.target.is.node ?
'rollup-terser-plugin-settings-configuration-for-node' :
'rollup-terser-plugin-settings-configuration-for-browser';
// Return the reduced configuration.
return this.events.reduce(
[eventName, 'rollup-terser-plugin-settings-configuration'],
settings,
params
);
}
/**
* Defines the settings for the `compression` plugin, which is a projext's plugin that takes care
* of compressing the generated assets using Gzip.
* This method uses the reducer event
* `rollup-compression-plugin-settings-configuration-for-browser` or
* `rollup-compression-plugin-settings-configuration-for-node`, depending on the target type,
* and then `rollup-compression-plugin-settings-configuration`. The event receives the settings,
* the `params` and expects new settings on return.
* @param {RollupConfigurationParams} params A dictionary generated by the top service building
* the configuration and that includes things like the
* target information, its entry settings, output
* paths, etc.
* @param {Function} stats A function to send to the plugins that support
* logging stats entries.
* @return {Object}
* @access protected
* @ignore
*/
_getCompressionSettings(params, stats) {
const { target } = params;
// Get the rule to find ALL generated assets.
const rule = this._getARuleForAllTheAssets(params);
// Define the plugin settings.
const settings = {
folder: target.paths.build,
include: rule.include,
exclude: rule.exclude,
stats,
};
const eventName = target.is.node ?
'rollup-compression-plugin-settings-configuration-for-node' :
'rollup-compression-plugin-settings-configuration-for-browser';
// Return the reduced configuration.
return this.events.reduce(
[eventName, 'rollup-compression-plugin-settings-configuration'],
settings,
params
);
}
/**
* Defines the settings for the `copy` plugin, which is a projext's plugin that takes care
* of copying specific files during the bundling process.
* This method uses the reducer event
* `rollup-copy-plugin-settings-configuration-for-browser` or
* `rollup-copy-plugin-settings-configuration-for-node`, depending on the target type,
* and then `rollup-copy-plugin-settings-configuration`. The event receives the settings,
* the `params` and expects new settings on return.
* @param {RollupConfigurationParams} params A dictionary generated by the top service building
* the configuration and that includes things like the
* target information, its entry settings, output
* paths, etc.
* @param {Function} stats A function to send to the plugins that support
* logging stats entries.
* @return {Object}
* @access protected
* @ignore
*/
_getCopySettings(params, stats) {
const { target, copy: files } = params;
// Define the plugin settings.
const settings = {
files,
stats,
};
const eventName = target.is.node ?
'rollup-copy-plugin-settings-configuration-for-node' :
'rollup-copy-plugin-settings-configuration-for-browser';
// Return the reduced configuration.
return this.events.reduce(
[eventName, 'rollup-copy-plugin-settings-configuration'],
settings,
params
);
}
/**
* Defines the settings for the `visualizer` plugin.
* This method uses the reducer event
* `rollup-visualizer-plugin-settings-configuration-for-browser` or
* `rollup-visualizer-plugin-settings-configuration-for-node`, depending on the target type, and
* then `rollup-visualizer-plugin-settings-configuration`. The event receives the settings, the
* `params` and expects new settings on return.
* @param {RollupConfigurationParams} params A dictionary generated by the top service building
* the configuration and that includes things like the
* target information, its entry settings, output
* paths, etc.
* @return {Object}
* @access protected
* @ignore
*/
_getVisualizerSettings(params) {
const { target } = params;
const targetFilename = target.name.replace(/\//g, '-');
const settings = {
filename: `${target.paths.build}/${targetFilename}-stats-visualizer.html`,
open: true,
};
const eventName = target.is.node ?
'rollup-visualizer-plugin-settings-configuration-for-node' :
'rollup-visualizer-plugin-settings-configuration-for-browser';
return this.events.reduce(
[eventName, 'rollup-visualizer-plugin-settings-configuration'],
settings,
params
);
}
/**
* Defines the settings for the `stats` plugin `log` _"sub plugin"_. The `stats` plugin is a
* projext plugin that allows other plugins and services to add stats entries to eventually
* show a report table when the bundle process finishes. The `log` _"sub plugin"_ is the method
* that you would add to the Rollup plugins queue and that it actually logs the report table.
* This method uses the reducer event
* `rollup-stats-plugin-settings-configuration-for-browser` or
* `rollup-stats-plugin-settings-configuration-for-node`, depending on the target type,
* and then `rollup-stats-plugin-settings-configuration`. The event receives the settings,
* the `params` and expects new settings on return.
* @param {RollupConfigurationParams} params A dictionary generated by the top service building
* the configuration and that includes things like the
* target information, its entry settings, output
* paths, etc.
* @return {Object}
* @access protected
* @ignore
*/
_getStatsLogSettings(params) {
const { target, paths, buildType } = params;
// As the first extra entry, add the bundle generated by Rollup.
const extraEntries = [
{
plugin: 'rollup',
filepath: `${target.paths.build}/${paths.js}`,
},
];
// If the target implements source maps, add the entry for the source map.
if (target.sourceMap && target.sourceMap[buildType]) {
extraEntries.push({
plugin: 'rollup',
filepath: `${target.paths.build}/${paths.js}.map`,
});
}
/**
* If the target is for browser and is not injecting the styles, assume the `sass` plugin
* has generated a bundle, so add and entry for it.
* @todo This should be on the `processor` to be able to know if it was the `sass` or `css`
* plugin.
*/
if (target.is.browser && !target.css.inject) {
extraEntries.push({
plugin: 'rollup-plugin-sass',
filepath: `${target.paths.build}/${paths.css}`,
});
}
// Define the plugin settings.
const settings = {
extraEntries,
};
const eventName = target.is.node ?
'rollup-stats-plugin-settings-configuration-for-node' :
'rollup-stats-plugin-settings-configuration-for-browser';
// Return the reduced configuration.
return this.events.reduce(
[eventName, 'rollup-stats-plugin-settings-configuration'],
settings,
params
);
}
/**
* Defines the settings for the `nodeRunner` plugin, which is a projext's plugin for running Node
* apps while on development.
* This method uses the reducer event `rollup-node-runner-plugin-settings-configuration`, it
* receives the settings, the `params` and expects new settings on return.
* @param {RollupConfigurationParams} params A dictionary generated by the top service building
* the configuration and that includes things like the
* target information, its entry settings, output
* paths, etc.
* @return {Object}
* @access protected
* @ignore
*/
_getNodeRunnerSettings(params) {
const { output } = params;
// Define the plugin settings.
const settings = {
file: output.file,
logger: this.appLogger,
inspect: params.target.inspect,
};
// Return the reduced configuration.
return this.events.reduce(
'rollup-node-runner-plugin-settings-configuration',
settings,
params
);
}
/**
* This a helper method that generates a dictionary of {@link ProjextRollupPluginURL}
* definitions for common assets (fonts, images and the favicon). These definitions can be used
* on different plugin settings.
* @param {RollupConfigurationParams} params A dictionary generated by the top service building
* the configuration and that includes things like the
* target information, its entry settings, output
* paths, etc.
* @return {Object}
* @access protected
* @ignore
*/
_getAssetsRules(params) {
const { target, targetRules, paths } = params;
const commonFontsRule = targetRules.fonts.common.getRule();
const svgFontsRule = targetRules.fonts.svg.getRule();
const imagesRule = targetRules.images.getRule();
const faviconRule = targetRules.favicon.getRule();
return {
fonts: {
include: [
...commonFontsRule.files.include,
...svgFontsRule.files.include,
],
exclude: [
...commonFontsRule.files.exclude,
...svgFontsRule.files.exclude,
],
output: `${target.paths.build}/${paths.fonts}`,
url: `/${paths.fonts}`,
},
images: {
include: [...imagesRule.files.include],
exclude: [...imagesRule.files.exclude],
output: `${target.paths.build}/${paths.images}`,
url: `/${paths.images}`,
},
favicon: {
include: [...faviconRule.files.include],
exclude: [...faviconRule.files.exclude],
output: `${target.paths.build}/[name].[ext]`,
url: '/[name].[ext]',
},
};
}
/**
* This a helper method that generates a set of `include` and `exclude` rules to match all
* possible generated assets on the output directory.
* @param {RollupConfigurationParams} params A dictionary generated by the top service building
* the configuration and that includes things like the
* target information, its entry settings, output
* paths, etc.
* @return {Object}
* @access protected
* @ignore
*/
_getARuleForAllTheAssets(params) {
const { target } = params;
const extensions = [
'js',
'jsx',
'ts',
'tsx',
'css',
'html',
'map',
'woff',
'woff2',
'ttf',
'eot',
'jpg',
'jpeg',
'png',
'gif',
'svg',
'ico',
];
const extensionsStr = extensions.join('|');
const extensionsRegex = `\\.(?:${extensionsStr})$`;
return {
include: [new RegExp(`${target.paths.build}/.*?${extensionsRegex}`, 'i')],
exclude: [],
};
}
/**
* Helper method for the stylsheet processor. It searches and retrieves a source map comment
* from a given CSS code.
* @param {string} code The CSS code where the source map will be retrieved from.
* @return {?string} If the source map it's found, it will return it, otherwise, it will return
* `null`.
* @access protected
* @ignore
*/
_getSourceMap(code) {
const regex = /(\/\*# sourceMappingURL=.*? \*\/)/i;
const match = regex.exec(code);
let result = null;
if (match) {
[result] = match;
}
return result;
}
/**
* This is a custom stylesheet processor for the `sass` and `css` plugin. It makes sure all
* generated styles have a source map that plugins like `stylesheetAssets` can use. It also
* takes care of implementing CSS modules if the target has them enabled.
* @param {boolean} modules Whether or not CSS modules are enabled.
* @param {Object} [processorOptions={}] Custom options for the processor.
* @param {boolean} [processorOptions.map=false] Whether or not a source map should be generated
* for the stylesheet. When generated with the
* `sass` plugin, the source map already comes
* with the code; but when generated with the
* `css` plugin, it should be added.
* @return {function}
* @access protected
* @ignore
*/
_getStylesProcessor(modules, processorOptions = {}) {
// Merge the default options with the received custom options.
const options = Object.assign(
{},
{
map: false,
/**
* If the file already has a source map, this value will be filled with the path to
* the file being processed, as required by `postcss`.
*/
from: undefined,
},
processorOptions
);
// Return the processor function.
return (css, filepath) => {
let map;
/**
* If the stylesheet needs a source map, complete the `from` option; otherwise, read it from
* the code.
* The reason the source map is being extracted before processing the stylesheet is because
* `postcss` may update it and the reference for the original sources may get lost.
*/
if (options.map) {
options.from = filepath;
} else {
map = this._getSourceMap(css) || '';
}
// Define the variable that will be used to store the CSS modules locals if enabled.
let locals;
// Define the list of plugins for `postcss`.
const plugins = [];
/**
* If CSS modules are enabled for the target, push the plugin with a callback to obtain the
* locals names.
*/
if (modules) {
plugins.push(postcssModules({
getJSON: (filename, json) => {
locals = json;
},
}));
}
let processor;
// Avoid using `postcss` if not needed
if (plugins.length || options.map || options.from) {
// If we actually have plugins...
if (plugins.length) {
// Let's use `postcss` like you would normally do.
processor = postcss(plugins)
// Process the stylesheet code.
.process(css, options);
} else {
/**
* But if we are using `postcss` just to get the source map, let's wrap it on a
* `LazyResult`. It seems to be the only way to hide the warning that we are not
* using plugins.
*/
processor = new LazyResult(postcss(), css, options);
}
} else {
processor = Promise.resolve({
css: css.replace(map, '').trim(),
});
}
return processor
.then((processed) => {
// Add the source map if needed.
const cssCode = options.map ?
`${processed.css}\n` :
`${processed.css}\n\n${map}\n`;
// Define the return object.
let result;
/**
* If CSS modules are enabled for the target, return a dictionary with the code and the
* locals, otherwise just return the code.
*/
if (modules) {
result = {
css: cssCode,
locals,
};
} else {
result = cssCode;
}
return result;
});
};
}
}
/**
* The service provider that once registered on the app container will set an instance of
* `RollupPluginSettingsConfiguration` as the `rollupPluginSettingsConfiguration` service.
* @example
* // Register it on the container
* container.register(rollupPluginSettingsConfiguration);
* // Getting access to the service instance
* const rollupPluginSettingsConfiguration = container.get('rollupPluginSettingsConfiguration');
* @type {Provider}
*/
const rollupPluginSettingsConfiguration = provider((app) => {
app.set('rollupPluginSettingsConfiguration', () => new RollupPluginSettingsConfiguration(
app.get('appLogger'),
app.get('babelConfiguration'),
app.get('babelHelper'),
app.get('events'),
app.get('packageInfo'),
app.get('pathUtils'),
app.get('rollupPluginInfo'),
app.get('targetsHTML')
));
});
module.exports = {
RollupPluginSettingsConfiguration,
rollupPluginSettingsConfiguration,
};