mirror of
https://github.com/locomotivemtl/locomotive-boilerplate.git
synced 2026-01-15 00:55:08 +08:00
Refactor build utilities
Separated generic functions from build helpers. Changed: - Moved 'utils/*.js' to 'helpers/*.js' - From 'utils/config.js': - Moved function `merge` to 'utils/index.js'. - Moved function `isObjectLike` to 'utils/index.js'. - From 'utils/template.js': - Moved function `flatten` to 'utils/index.js'. - Moved function `escapeRegExp` to 'utils/index.js'. - From 'tasks/styles.js': - Moved function `createPostCSSProcessor` to 'helpers/postcss.js' as `createProcessor`. - Replaced function `Object.assign` with `merge` for task options parsing in all tasks.
This commit is contained in:
19
build/helpers/config.js
Normal file
19
build/helpers/config.js
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* @file Provides simple user configuration options.
|
||||
*/
|
||||
|
||||
import loconfig from '../../loconfig.json' assert { type: 'json' };
|
||||
import { merge } from '../utils/index.js';
|
||||
|
||||
try {
|
||||
const usrconfig = await import('../../loconfig.local.json', {
|
||||
assert: { type: "json" },
|
||||
});
|
||||
usrconfig = usrconfig.default;
|
||||
|
||||
merge(loconfig, usrconfig);
|
||||
} catch (err) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
export default loconfig;
|
||||
111
build/helpers/postcss.js
Normal file
111
build/helpers/postcss.js
Normal file
@@ -0,0 +1,111 @@
|
||||
/**
|
||||
* @file If available, returns the PostCSS Processor creator and
|
||||
* any the Autoprefixer PostCSS plugin.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {import('autoprefixer').autoprefixer.Options} AutoprefixerOptions
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {import('postcss').AcceptedPlugin} AcceptedPlugin
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {import('postcss').ProcessOptions} ProcessOptions
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {import('postcss').Processor} Processor
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {AcceptedPlugin[]} PluginList
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object<string, AcceptedPlugin>} PluginMap
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {PluginList|PluginMap} PluginCollection
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} PostCSSOptions
|
||||
*
|
||||
* @property {ProcessOptions} processor - The `Processor#process()` options.
|
||||
* @property {AutoprefixerOptions} autoprefixer - The `autoprefixer()` options.
|
||||
*/
|
||||
|
||||
let postcss, autoprefixer;
|
||||
|
||||
try {
|
||||
postcss = await import('postcss');
|
||||
postcss = postcss.default;
|
||||
|
||||
autoprefixer = await import('autoprefixer');
|
||||
autoprefixer = autoprefixer.default;
|
||||
} catch (err) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
export const supportsPostCSS = (typeof postcss === 'function');
|
||||
|
||||
export default postcss;
|
||||
export const pluginsList = [
|
||||
autoprefixer,
|
||||
];
|
||||
export const pluginsMap = {
|
||||
'autoprefixer': autoprefixer,
|
||||
};
|
||||
export {
|
||||
autoprefixer
|
||||
};
|
||||
|
||||
/**
|
||||
* Attempts to create a PostCSS Processor with the given plugins and options.
|
||||
*
|
||||
* @param {PluginCollection} pluginsListOrMap - A list or map of plugins.
|
||||
* If a map of plugins, the plugin name looks up `options`.
|
||||
* @param {PostCSSOptions} options - The PostCSS wrapper options.
|
||||
* @return {?Processor}
|
||||
*/
|
||||
export function createProcessor(pluginsListOrMap, options)
|
||||
{
|
||||
if (!postcss) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const plugins = parsePlugins(pluginsListOrMap, options);
|
||||
|
||||
return postcss(plugins);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the PostCSS plugins and options.
|
||||
*
|
||||
* @param {PluginCollection} pluginsListOrMap - A list or map of plugins.
|
||||
* If a map of plugins, the plugin name looks up `options`.
|
||||
* @param {PostCSSOptions} options - The PostCSS wrapper options.
|
||||
* @return {PluginList}
|
||||
*/
|
||||
export function parsePlugins(pluginsListOrMap, options)
|
||||
{
|
||||
if (Array.isArray(pluginsListOrMap)) {
|
||||
return pluginsListOrMap;
|
||||
}
|
||||
|
||||
/** @type {PluginList} */
|
||||
const plugins = [];
|
||||
|
||||
for (let [ name, plugin ] of Object.entries(pluginsListOrMap)) {
|
||||
if (name in options) {
|
||||
plugin = plugin[name](options[name]);
|
||||
}
|
||||
|
||||
plugins.push(plugin);
|
||||
}
|
||||
|
||||
return plugins;
|
||||
}
|
||||
@@ -3,6 +3,10 @@
|
||||
*/
|
||||
|
||||
import loconfig from './config.js';
|
||||
import {
|
||||
escapeRegExp,
|
||||
flatten
|
||||
} from '../utils/index.js';
|
||||
|
||||
const templateData = flatten({
|
||||
paths: loconfig.paths
|
||||
@@ -92,56 +96,3 @@ export function resolveValue(input, data = templateData) {
|
||||
return '';
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new object with all nested object properties
|
||||
* concatenated into it recursively.
|
||||
*
|
||||
* Nested keys are flattened into a property path:
|
||||
*
|
||||
* ```js
|
||||
* {
|
||||
* a: {
|
||||
* b: {
|
||||
* c: 1
|
||||
* }
|
||||
* },
|
||||
* d: 1
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* ```js
|
||||
* {
|
||||
* "a.b.c": 1,
|
||||
* "d": 1
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param {object} input - The object to flatten.
|
||||
* @param {string} prefix - The parent key prefix.
|
||||
* @param {object} target - The object that will receive the flattened properties.
|
||||
* @return {object} Returns the `target` object.
|
||||
*/
|
||||
function flatten(input, prefix, target = {}) {
|
||||
for (let key in input) {
|
||||
let field = (prefix ? prefix + '.' + key : key);
|
||||
|
||||
if (typeof input[key] === 'object') {
|
||||
flatten(input[key], field, target);
|
||||
} else {
|
||||
target[field] = input[key];
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Quotes regular expression characters.
|
||||
*
|
||||
* @param {string} str - The input string.
|
||||
* @return {string} Returns the quoted (escaped) string.
|
||||
*/
|
||||
function escapeRegExp(str) {
|
||||
return str.replace(/[\[\]\{\}\(\)\-\*\+\?\.\,\\\^\$\|\#\s]/g, '\\$&');
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
import loconfig from '../utils/config.js';
|
||||
import glob, { supportsGlob } from '../utils/glob.js';
|
||||
import message from '../utils/message.js';
|
||||
import notification from '../utils/notification.js';
|
||||
import resolve from '../utils/template.js';
|
||||
import loconfig from '../helpers/config.js';
|
||||
import glob, { supportsGlob } from '../helpers/glob.js';
|
||||
import message from '../helpers/message.js';
|
||||
import notification from '../helpers/notification.js';
|
||||
import resolve from '../helpers/template.js';
|
||||
import { merge } from '../utils/index.js';
|
||||
import concat from 'concat';
|
||||
import {
|
||||
basename,
|
||||
@@ -72,7 +73,7 @@ export default async function concatFiles(globOptions = null, concatOptions = nu
|
||||
globOptions !== developmentGlobOptions &&
|
||||
globOptions !== productionGlobOptions
|
||||
) {
|
||||
globOptions = Object.assign({}, defaultGlobOptions, globOptions);
|
||||
globOptions = merge({}, defaultGlobOptions, globOptions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +83,7 @@ export default async function concatFiles(globOptions = null, concatOptions = nu
|
||||
concatOptions !== developmentConcatOptions &&
|
||||
concatOptions !== productionConcatOptions
|
||||
) {
|
||||
concatOptions = Object.assign({}, defaultConcatOptions, concatOptions);
|
||||
concatOptions = merge({}, defaultConcatOptions, concatOptions);
|
||||
}
|
||||
|
||||
loconfig.tasks.concats.forEach(async ({
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import loconfig from '../utils/config.js';
|
||||
import message from '../utils/message.js';
|
||||
import notification from '../utils/notification.js';
|
||||
import resolve from '../utils/template.js';
|
||||
import loconfig from '../helpers/config.js';
|
||||
import message from '../helpers/message.js';
|
||||
import notification from '../helpers/notification.js';
|
||||
import resolve from '../helpers/template.js';
|
||||
import { merge } from '../utils/index.js';
|
||||
import esbuild from 'esbuild';
|
||||
import { basename } from 'node:path';
|
||||
|
||||
@@ -50,7 +51,7 @@ export default async function compileScripts(esBuildOptions = null) {
|
||||
esBuildOptions !== developmentESBuildOptions &&
|
||||
esBuildOptions !== productionESBuildOptions
|
||||
) {
|
||||
esBuildOptions = Object.assign({}, defaultESBuildOptions, esBuildOptions);
|
||||
esBuildOptions = merge({}, defaultESBuildOptions, esBuildOptions);
|
||||
}
|
||||
|
||||
loconfig.tasks.scripts.forEach(async ({
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import loconfig from '../utils/config.js';
|
||||
import message from '../utils/message.js';
|
||||
import notification from '../utils/notification.js';
|
||||
import postcss, {
|
||||
import loconfig from '../helpers/config.js';
|
||||
import message from '../helpers/message.js';
|
||||
import notification from '../helpers/notification.js';
|
||||
import {
|
||||
createProcessor,
|
||||
pluginsMap as postcssPluginsMap,
|
||||
supportsPostCSS
|
||||
} from '../utils/postcss.js';
|
||||
import resolve from '../utils/template.js';
|
||||
} from '../helpers/postcss.js';
|
||||
import resolve from '../helpers/template.js';
|
||||
import { merge } from '../utils/index.js';
|
||||
import { writeFile } from 'node:fs/promises';
|
||||
import { basename } from 'node:path';
|
||||
import { promisify } from 'node:util';
|
||||
@@ -85,7 +87,7 @@ export default async function compileStyles(sassOptions = null, postcssOptions =
|
||||
sassOptions !== developmentSassOptions &&
|
||||
sassOptions !== productionSassOptions
|
||||
) {
|
||||
sassOptions = Object.assign({}, defaultSassOptions, sassOptions);
|
||||
sassOptions = merge({}, defaultSassOptions, sassOptions);
|
||||
}
|
||||
|
||||
if (supportsPostCSS) {
|
||||
@@ -96,7 +98,7 @@ export default async function compileStyles(sassOptions = null, postcssOptions =
|
||||
postcssOptions !== developmentPostCSSOptions &&
|
||||
postcssOptions !== productionPostCSSOptions
|
||||
) {
|
||||
postcssOptions = Object.assign({}, defaultPostCSSOptions, postcssOptions);
|
||||
postcssOptions = merge({}, defaultPostCSSOptions, postcssOptions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +123,7 @@ export default async function compileStyles(sassOptions = null, postcssOptions =
|
||||
|
||||
if (supportsPostCSS && postcssOptions) {
|
||||
if (typeof postcssProcessor === 'undefined') {
|
||||
postcssProcessor = createPostCSSProcessor(
|
||||
postcssProcessor = createProcessor(
|
||||
postcssPluginsMap,
|
||||
postcssOptions
|
||||
);
|
||||
@@ -194,35 +196,6 @@ export default async function compileStyles(sassOptions = null, postcssOptions =
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a PostCSS Processor with the given plugins and options.
|
||||
*
|
||||
* @param {array<(function|object)>|object<string, (function|object)>} pluginsListOrMap -
|
||||
* A list or map of plugins.
|
||||
* If a map of plugins, the plugin name looks up `options`.
|
||||
* @param {object} options - The PostCSS options.
|
||||
*/
|
||||
function createPostCSSProcessor(pluginsListOrMap, options)
|
||||
{
|
||||
let plugins;
|
||||
|
||||
if (Array.isArray(pluginsListOrMap)) {
|
||||
plugins = pluginsListOrMap;
|
||||
} else {
|
||||
plugins = [];
|
||||
|
||||
for (let [ name, plugin ] of Object.entries(pluginsListOrMap)) {
|
||||
if (name in options) {
|
||||
plugin = plugin[name](options[name]);
|
||||
}
|
||||
|
||||
plugins.push(plugin);
|
||||
}
|
||||
}
|
||||
|
||||
return postcss(plugins);
|
||||
}
|
||||
|
||||
/**
|
||||
* Purge unused styles from CSS files.
|
||||
*
|
||||
@@ -235,22 +208,23 @@ function createPostCSSProcessor(pluginsListOrMap, options)
|
||||
*/
|
||||
async function purgeUnusedCSS(outfile, label) {
|
||||
label = label ?? basename(outfile);
|
||||
|
||||
const timeLabel = `${label} purged in`;
|
||||
console.time(timeLabel);
|
||||
|
||||
const purgeCSSContentFiles = Array.from(loconfig.tasks.purgeCSS.content);
|
||||
|
||||
const purgeCSSResults = await new PurgeCSS().purge({
|
||||
const purgeCSSResults = await (new PurgeCSS()).purge({
|
||||
content: purgeCSSContentFiles,
|
||||
css: [ outfile ],
|
||||
rejected: true,
|
||||
defaultExtractor: content => content.match(/[a-z0-9_\-\\\/\@]+/gi) || [],
|
||||
defaultExtractor: (content) => content.match(/[a-z0-9_\-\\\/\@]+/gi) || [],
|
||||
safelist: {
|
||||
standard: [ /^((?!\bu-gc-).)*$/ ]
|
||||
}
|
||||
})
|
||||
|
||||
for(let result of purgeCSSResults) {
|
||||
for (let result of purgeCSSResults) {
|
||||
await writeFile(outfile, result.css)
|
||||
|
||||
message(`${label} purged`, 'chore', timeLabel);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import loconfig from '../utils/config.js';
|
||||
import message from '../utils/message.js';
|
||||
import notification from '../utils/notification.js';
|
||||
import resolve from '../utils/template.js';
|
||||
import loconfig from '../helpers/config.js';
|
||||
import message from '../helpers/message.js';
|
||||
import notification from '../helpers/notification.js';
|
||||
import resolve from '../helpers/template.js';
|
||||
import { merge } from '../utils/index.js';
|
||||
import { basename } from 'node:path';
|
||||
import mixer from 'svg-mixer';
|
||||
|
||||
@@ -44,7 +45,7 @@ export default async function compileSVGs(mixerOptions = null) {
|
||||
mixerOptions !== developmentMixerOptions &&
|
||||
mixerOptions !== productionMixerOptions
|
||||
) {
|
||||
mixerOptions = Object.assign({}, defaultMixerOptions, mixerOptions);
|
||||
mixerOptions = merge({}, defaultMixerOptions, mixerOptions);
|
||||
}
|
||||
|
||||
loconfig.tasks.svgs.forEach(async ({
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import loconfig from '../utils/config.js';
|
||||
import message from '../utils/message.js';
|
||||
import resolve from '../utils/template.js';
|
||||
import loconfig from '../helpers/config.js';
|
||||
import message from '../helpers/message.js';
|
||||
import resolve from '../helpers/template.js';
|
||||
import { merge } from '../utils/index.js';
|
||||
import { randomBytes } from 'node:crypto';
|
||||
import events from 'node:events';
|
||||
import {
|
||||
@@ -94,7 +95,7 @@ export default async function bumpVersions(versionOptions = null) {
|
||||
versionOptions !== developmentVersionOptions &&
|
||||
versionOptions !== productionVersionOptions
|
||||
) {
|
||||
versionOptions = Object.assign({}, defaultVersionOptions, versionOptions);
|
||||
versionOptions = merge({}, defaultVersionOptions, versionOptions);
|
||||
}
|
||||
|
||||
const queue = new Map();
|
||||
|
||||
@@ -1,23 +1,70 @@
|
||||
/**
|
||||
* @file Provides simple user configuration options.
|
||||
* @file Provides generic functions and constants.
|
||||
*/
|
||||
|
||||
import loconfig from '../../loconfig.json' assert { type: 'json' };
|
||||
|
||||
let usrconfig;
|
||||
|
||||
try {
|
||||
usrconfig = await import('../../loconfig.local.json', {
|
||||
assert: { type: 'json' }
|
||||
});
|
||||
usrconfig = usrconfig.default;
|
||||
|
||||
merge(loconfig, usrconfig);
|
||||
} catch (err) {
|
||||
// do nothing
|
||||
/**
|
||||
* Quotes regular expression characters.
|
||||
*
|
||||
* @param {string} str - The input string.
|
||||
* @return {string} Returns the quoted (escaped) string.
|
||||
*/
|
||||
export function escapeRegExp(str) {
|
||||
return str.replace(regexUnescaped, '\\$&');
|
||||
}
|
||||
|
||||
export default loconfig;
|
||||
/**
|
||||
* Creates a new object with all nested object properties
|
||||
* concatenated into it recursively.
|
||||
*
|
||||
* Nested keys are flattened into a property path:
|
||||
*
|
||||
* ```js
|
||||
* {
|
||||
* a: {
|
||||
* b: {
|
||||
* c: 1
|
||||
* }
|
||||
* },
|
||||
* d: 1
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* ```js
|
||||
* {
|
||||
* "a.b.c": 1,
|
||||
* "d": 1
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param {object} input - The object to flatten.
|
||||
* @param {string} prefix - The parent key prefix.
|
||||
* @param {object} target - The object that will receive the flattened properties.
|
||||
* @return {object} Returns the `target` object.
|
||||
*/
|
||||
export function flatten(input, prefix, target = {}) {
|
||||
for (const key in input) {
|
||||
const field = (prefix ? prefix + '.' + key : key);
|
||||
|
||||
if (isObjectLike(input[key])) {
|
||||
flatten(input[key], field, target);
|
||||
} else {
|
||||
target[field] = input[key];
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the passed value is an `Object`.
|
||||
*
|
||||
* @param {*} value - The value to be checked.
|
||||
* @return {boolean} Returns `true` if the value is an `Object`,
|
||||
* otherwise `false`.
|
||||
*/
|
||||
export function isObjectLike(value) {
|
||||
return (value != null && typeof value === 'object');
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new object with all nested object properties
|
||||
@@ -55,12 +102,6 @@ export function merge(target, ...sources) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the passed value is an `Object`.
|
||||
*
|
||||
* @param {*} value - The value to be checked.
|
||||
* @return {boolean} Returns `true` if the value is an `Object`,
|
||||
* otherwise `false`.
|
||||
* @type {RegExp} - Match all special characters.
|
||||
*/
|
||||
function isObjectLike(value) {
|
||||
return (value != null && typeof value === 'object');
|
||||
}
|
||||
export const regexUnescaped = /[\[\]\{\}\(\)\-\*\+\?\.\,\\\^\$\|\#\s]/g;
|
||||
@@ -1,29 +0,0 @@
|
||||
/**
|
||||
* @file If available, returns the PostCSS Processor creator and
|
||||
* any the Autoprefixer PostCSS plugin.
|
||||
*/
|
||||
|
||||
let postcss, autoprefixer;
|
||||
|
||||
try {
|
||||
postcss = await import('postcss');
|
||||
postcss = postcss.default;
|
||||
|
||||
autoprefixer = await import('autoprefixer');
|
||||
autoprefixer = autoprefixer.default;
|
||||
} catch (err) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
export const supportsPostCSS = (typeof postcss === 'function');
|
||||
|
||||
export default postcss;
|
||||
export const pluginsList = [
|
||||
autoprefixer,
|
||||
];
|
||||
export const pluginsMap = {
|
||||
'autoprefixer': autoprefixer,
|
||||
};
|
||||
export {
|
||||
autoprefixer
|
||||
};
|
||||
@@ -2,10 +2,10 @@ import concatFiles, { developmentConcatFilesArgs } from './tasks/concats.js';
|
||||
import compileScripts, { developmentScriptsArgs } from './tasks/scripts.js';
|
||||
import compileStyles, { developmentStylesArgs } from './tasks/styles.js' ;
|
||||
import compileSVGs, { developmentSVGsArgs } from './tasks/svgs.js';
|
||||
import loconfig, { merge } from './utils/config.js';
|
||||
import message from './utils/message.js';
|
||||
import notification from './utils/notification.js';
|
||||
import resolve from './utils/template.js';
|
||||
import loconfig, { merge } from './helpers/config.js';
|
||||
import message from './helpers/message.js';
|
||||
import notification from './helpers/notification.js';
|
||||
import resolve from './helpers/template.js';
|
||||
import browserSync from 'browser-sync';
|
||||
import { join } from 'node:path';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user