mirror of
https://github.com/locomotivemtl/locomotive-boilerplate.git
synced 2026-01-15 00:55:08 +08:00
Merge pull request #132 from locomotivemtl/feature/build-task-chores
Various fixes and changes to build utilities
This commit is contained in:
25
build/helpers/config.js
Normal file
25
build/helpers/config.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* @file Provides simple user configuration options.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import loconfig from '../../loconfig.json' assert { type: 'json' };
|
||||||
|
import { merge } from '../utils/index.js';
|
||||||
|
|
||||||
|
let usrconfig;
|
||||||
|
|
||||||
|
try {
|
||||||
|
usrconfig = await import('../../loconfig.local.json', {
|
||||||
|
assert: { type: 'json' },
|
||||||
|
});
|
||||||
|
usrconfig = usrconfig.default;
|
||||||
|
|
||||||
|
merge(loconfig, usrconfig);
|
||||||
|
} catch (err) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
export default loconfig;
|
||||||
|
|
||||||
|
export {
|
||||||
|
loconfig,
|
||||||
|
};
|
||||||
162
build/helpers/glob.js
Normal file
162
build/helpers/glob.js
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
/**
|
||||||
|
* @file Retrieve the first available glob library.
|
||||||
|
*
|
||||||
|
* Note that options vary between libraries.
|
||||||
|
*
|
||||||
|
* Candidates:
|
||||||
|
*
|
||||||
|
* - {@link https://npmjs.com/package/tiny-glob tiny-glob} [1][5][6]
|
||||||
|
* - {@link https://npmjs.com/package/globby globby} [2][5]
|
||||||
|
* - {@link https://npmjs.com/package/fast-glob fast-glob} [3]
|
||||||
|
* - {@link https://npmjs.com/package/glob glob} [1][4][5]
|
||||||
|
*
|
||||||
|
* Notes:
|
||||||
|
*
|
||||||
|
* - [1] The library's function accepts only a single pattern.
|
||||||
|
* - [2] The library's function accepts only an array of patterns.
|
||||||
|
* - [3] The library's function accepts either a single pattern
|
||||||
|
* or an array of patterns.
|
||||||
|
* - [4] The library's function does not return a Promise but will be
|
||||||
|
* wrapped in a function that does return a Promise.
|
||||||
|
* - [5] The library's function will be wrapped in a function that
|
||||||
|
* supports a single pattern and an array of patterns.
|
||||||
|
* - [6] The library's function returns files and directories but will be
|
||||||
|
* preconfigured to return only files.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { promisify } from 'node:util';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @callback GlobFn
|
||||||
|
*
|
||||||
|
* @param {string|string[]} patterns - A string pattern
|
||||||
|
* or an array of string patterns.
|
||||||
|
* @param {object} options
|
||||||
|
*
|
||||||
|
* @returns {Promise<string[]>}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {object} GlobOptions
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {GlobFn|undefined} The discovered glob function.
|
||||||
|
*/
|
||||||
|
let glob;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {string[]} A list of packages to attempt import.
|
||||||
|
*/
|
||||||
|
const candidates = [
|
||||||
|
'tiny-glob',
|
||||||
|
'globby',
|
||||||
|
'fast-glob',
|
||||||
|
'glob',
|
||||||
|
];
|
||||||
|
|
||||||
|
try {
|
||||||
|
glob = await importGlob();
|
||||||
|
} catch (err) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {boolean} Whether a glob function was discovered (TRUE) or not (FALSE).
|
||||||
|
*/
|
||||||
|
const supportsGlob = (typeof glob === 'function');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Imports the first available glob function.
|
||||||
|
*
|
||||||
|
* @throws {TypeError} If no glob library was found.
|
||||||
|
*
|
||||||
|
* @returns {GlobFn}
|
||||||
|
*/
|
||||||
|
async function importGlob() {
|
||||||
|
for (let name of candidates) {
|
||||||
|
try {
|
||||||
|
let globModule = await import(name);
|
||||||
|
|
||||||
|
if (typeof globModule.default !== 'function') {
|
||||||
|
throw new TypeError(`Expected ${name} to be a function`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrap the function to ensure
|
||||||
|
* a common pattern.
|
||||||
|
*/
|
||||||
|
switch (name) {
|
||||||
|
case 'tiny-glob':
|
||||||
|
/** [1][5] */
|
||||||
|
return createArrayableGlob(
|
||||||
|
/** [6] */
|
||||||
|
createPresetGlob(globModule.default, {
|
||||||
|
filesOnly: true
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
case 'globby':
|
||||||
|
/** [2][5] - If `patterns` is a string, wraps into an array. */
|
||||||
|
return (patterns, options) => globModule.default([].concat(patterns), options);
|
||||||
|
|
||||||
|
case 'glob':
|
||||||
|
/** [1][5] */
|
||||||
|
return createArrayableGlob(
|
||||||
|
/** [4] */
|
||||||
|
promisify(globModule.default)
|
||||||
|
);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return globModule.default;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// swallow this error; skip to the next candidate.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new TypeError(
|
||||||
|
`No glob library was found, expected one of: ${candidates.join(', ')}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a wrapper function for the glob function
|
||||||
|
* to provide support for arrays of patterns.
|
||||||
|
*
|
||||||
|
* @param {function} globFn - The glob function.
|
||||||
|
*
|
||||||
|
* @returns {GlobFn}
|
||||||
|
*/
|
||||||
|
function createArrayableGlob(globFn) {
|
||||||
|
return (patterns, options) => {
|
||||||
|
/** [2] If `patterns` is a string, wraps into an array. */
|
||||||
|
patterns = [].concat(patterns);
|
||||||
|
|
||||||
|
const globs = patterns.map((pattern) => globFn(pattern, options));
|
||||||
|
|
||||||
|
return Promise.all(globs).then((files) => {
|
||||||
|
return [].concat.apply([], files);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a wrapper function for the glob function
|
||||||
|
* to define new default options.
|
||||||
|
*
|
||||||
|
* @param {function} globFn - The glob function.
|
||||||
|
* @param {GlobOptions} presets - The glob function options to preset.
|
||||||
|
*
|
||||||
|
* @returns {GlobFn}
|
||||||
|
*/
|
||||||
|
function createPresetGlob(globFn, presets) {
|
||||||
|
return (patterns, options) => globFn(patterns, Object.assign({}, presets, options));
|
||||||
|
}
|
||||||
|
|
||||||
|
export default glob;
|
||||||
|
|
||||||
|
export {
|
||||||
|
glob,
|
||||||
|
supportsGlob,
|
||||||
|
};
|
||||||
@@ -11,7 +11,7 @@ import kleur from 'kleur';
|
|||||||
* @param {string} [type] - The type of message.
|
* @param {string} [type] - The type of message.
|
||||||
* @param {string} [timerID] - The console time label to output.
|
* @param {string} [timerID] - The console time label to output.
|
||||||
*/
|
*/
|
||||||
export default function message(text, type, timerID) {
|
function message(text, type, timerID) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'success':
|
case 'success':
|
||||||
console.log('✅ ', kleur.bgGreen().black(text));
|
console.log('✅ ', kleur.bgGreen().black(text));
|
||||||
@@ -52,4 +52,10 @@ export default function message(text, type, timerID) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
console.log('');
|
console.log('');
|
||||||
|
}
|
||||||
|
|
||||||
|
export default message;
|
||||||
|
|
||||||
|
export {
|
||||||
|
message,
|
||||||
};
|
};
|
||||||
@@ -16,7 +16,7 @@ import notifier from 'node-notifier';
|
|||||||
* @param {function} callback - The notification callback.
|
* @param {function} callback - The notification callback.
|
||||||
* @return {void}
|
* @return {void}
|
||||||
*/
|
*/
|
||||||
export default function notification(options, callback) {
|
function notification(options, callback) {
|
||||||
if (typeof options === 'string') {
|
if (typeof options === 'string') {
|
||||||
options = {
|
options = {
|
||||||
message: options
|
message: options
|
||||||
@@ -42,4 +42,10 @@ export default function notification(options, callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
notifier.notify(options, callback);
|
notifier.notify(options, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default notification;
|
||||||
|
|
||||||
|
export {
|
||||||
|
notification,
|
||||||
};
|
};
|
||||||
139
build/helpers/postcss.js
Normal file
139
build/helpers/postcss.js
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
/**
|
||||||
|
* @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').Postcss} Postcss
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Postcss|undefined} postcss - The discovered PostCSS function.
|
||||||
|
* @type {AcceptedPlugin|undefined} autoprefixer - The discovered Autoprefixer function.
|
||||||
|
*/
|
||||||
|
let postcss, autoprefixer;
|
||||||
|
|
||||||
|
try {
|
||||||
|
postcss = await import('postcss');
|
||||||
|
postcss = postcss.default;
|
||||||
|
|
||||||
|
autoprefixer = await import('autoprefixer');
|
||||||
|
autoprefixer = autoprefixer.default;
|
||||||
|
} catch (err) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {boolean} Whether PostCSS was discovered (TRUE) or not (FALSE).
|
||||||
|
*/
|
||||||
|
const supportsPostCSS = (typeof postcss === 'function');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {PluginList} A list of supported plugins.
|
||||||
|
*/
|
||||||
|
const pluginsList = [
|
||||||
|
autoprefixer,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {PluginMap} A map of supported plugins.
|
||||||
|
*/
|
||||||
|
const pluginsMap = {
|
||||||
|
'autoprefixer': 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.
|
||||||
|
*
|
||||||
|
* @returns {Processor|null}
|
||||||
|
*/
|
||||||
|
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.
|
||||||
|
*
|
||||||
|
* @returns {PluginList}
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default postcss;
|
||||||
|
|
||||||
|
export {
|
||||||
|
autoprefixer,
|
||||||
|
createProcessor,
|
||||||
|
parsePlugins,
|
||||||
|
pluginsList,
|
||||||
|
pluginsMap,
|
||||||
|
postcss,
|
||||||
|
supportsPostCSS,
|
||||||
|
};
|
||||||
@@ -3,6 +3,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import loconfig from './config.js';
|
import loconfig from './config.js';
|
||||||
|
import {
|
||||||
|
escapeRegExp,
|
||||||
|
flatten
|
||||||
|
} from '../utils/index.js';
|
||||||
|
|
||||||
const templateData = flatten({
|
const templateData = flatten({
|
||||||
paths: loconfig.paths
|
paths: loconfig.paths
|
||||||
@@ -22,7 +26,7 @@ const templateData = flatten({
|
|||||||
* @param {object} [data] - An object in the form `{ 'from': 'to', … }`.
|
* @param {object} [data] - An object in the form `{ 'from': 'to', … }`.
|
||||||
* @return {*} Returns the transformed value.
|
* @return {*} Returns the transformed value.
|
||||||
*/
|
*/
|
||||||
export default function resolve(input, data = templateData) {
|
function resolve(input, data = templateData) {
|
||||||
switch (typeof input) {
|
switch (typeof input) {
|
||||||
case 'string': {
|
case 'string': {
|
||||||
return resolveValue(input, data);
|
return resolveValue(input, data);
|
||||||
@@ -56,7 +60,7 @@ export default function resolve(input, data = templateData) {
|
|||||||
* @param {object} [data] - An object in the form `{ 'from': 'to', … }`.
|
* @param {object} [data] - An object in the form `{ 'from': 'to', … }`.
|
||||||
* @return {string} Returns the translated string.
|
* @return {string} Returns the translated string.
|
||||||
*/
|
*/
|
||||||
export function resolveValue(input, data = templateData) {
|
function resolveValue(input, data = templateData) {
|
||||||
const tags = [];
|
const tags = [];
|
||||||
|
|
||||||
if (data !== templateData) {
|
if (data !== templateData) {
|
||||||
@@ -93,55 +97,9 @@ export function resolveValue(input, data = templateData) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export default resolve;
|
||||||
* 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') {
|
export {
|
||||||
flatten(input[key], field, target);
|
resolve,
|
||||||
} else {
|
resolveValue,
|
||||||
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 loconfig from '../helpers/config.js';
|
||||||
import glob from '../utils/glob.js';
|
import glob, { supportsGlob } from '../helpers/glob.js';
|
||||||
import message from '../utils/message.js';
|
import message from '../helpers/message.js';
|
||||||
import notification from '../utils/notification.js';
|
import notification from '../helpers/notification.js';
|
||||||
import resolve from '../utils/template.js';
|
import resolve from '../helpers/template.js';
|
||||||
|
import { merge } from '../utils/index.js';
|
||||||
import concat from 'concat';
|
import concat from 'concat';
|
||||||
import {
|
import {
|
||||||
basename,
|
basename,
|
||||||
@@ -64,7 +65,7 @@ export const productionConcatFilesArgs = [
|
|||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
export default async function concatFiles(globOptions = null, concatOptions = null) {
|
export default async function concatFiles(globOptions = null, concatOptions = null) {
|
||||||
if (glob) {
|
if (supportsGlob) {
|
||||||
if (globOptions == null) {
|
if (globOptions == null) {
|
||||||
globOptions = productionGlobOptions;
|
globOptions = productionGlobOptions;
|
||||||
} else if (
|
} else if (
|
||||||
@@ -72,7 +73,7 @@ export default async function concatFiles(globOptions = null, concatOptions = nu
|
|||||||
globOptions !== developmentGlobOptions &&
|
globOptions !== developmentGlobOptions &&
|
||||||
globOptions !== productionGlobOptions
|
globOptions !== productionGlobOptions
|
||||||
) {
|
) {
|
||||||
globOptions = Object.assign({}, defaultGlobOptions, globOptions);
|
globOptions = merge({}, defaultGlobOptions, globOptions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,9 +83,18 @@ export default async function concatFiles(globOptions = null, concatOptions = nu
|
|||||||
concatOptions !== developmentConcatOptions &&
|
concatOptions !== developmentConcatOptions &&
|
||||||
concatOptions !== productionConcatOptions
|
concatOptions !== productionConcatOptions
|
||||||
) {
|
) {
|
||||||
concatOptions = Object.assign({}, defaultConcatOptions, concatOptions);
|
concatOptions = merge({}, defaultConcatOptions, concatOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @async
|
||||||
|
* @param {object} entry - The entrypoint to process.
|
||||||
|
* @param {string[]} entry.includes - One or more paths to process.
|
||||||
|
* @param {string} entry.outfile - The file to write to.
|
||||||
|
* @param {?string} [entry.label] - The task label.
|
||||||
|
* Defaults to the outfile name.
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
loconfig.tasks.concats.forEach(async ({
|
loconfig.tasks.concats.forEach(async ({
|
||||||
includes,
|
includes,
|
||||||
outfile,
|
outfile,
|
||||||
@@ -98,25 +108,25 @@ export default async function concatFiles(globOptions = null, concatOptions = nu
|
|||||||
console.time(timeLabel);
|
console.time(timeLabel);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (!Array.isArray(includes)) {
|
||||||
|
includes = [ includes ];
|
||||||
|
}
|
||||||
|
|
||||||
includes = resolve(includes);
|
includes = resolve(includes);
|
||||||
outfile = resolve(outfile);
|
outfile = resolve(outfile);
|
||||||
|
|
||||||
let files;
|
if (supportsGlob && globOptions) {
|
||||||
|
includes = await glob(includes, globOptions);
|
||||||
if (glob && globOptions) {
|
|
||||||
files = await glob(includes, globOptions);
|
|
||||||
} else {
|
|
||||||
files = includes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (concatOptions.removeDuplicates) {
|
if (concatOptions.removeDuplicates) {
|
||||||
files = files.map((path) => normalize(path));
|
includes = includes.map((path) => normalize(path));
|
||||||
files = [ ...new Set(files) ];
|
includes = [ ...new Set(includes) ];
|
||||||
}
|
}
|
||||||
|
|
||||||
await concat(files, outfile);
|
await concat(includes, outfile);
|
||||||
|
|
||||||
if (files.length) {
|
if (includes.length) {
|
||||||
message(`${label} concatenated`, 'success', timeLabel);
|
message(`${label} concatenated`, 'success', timeLabel);
|
||||||
} else {
|
} else {
|
||||||
message(`${label} is empty`, 'notice', timeLabel);
|
message(`${label} is empty`, 'notice', timeLabel);
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import loconfig from '../utils/config.js';
|
import loconfig from '../helpers/config.js';
|
||||||
import message from '../utils/message.js';
|
import message from '../helpers/message.js';
|
||||||
import notification from '../utils/notification.js';
|
import notification from '../helpers/notification.js';
|
||||||
import resolve from '../utils/template.js';
|
import resolve from '../helpers/template.js';
|
||||||
|
import { merge } from '../utils/index.js';
|
||||||
import esbuild from 'esbuild';
|
import esbuild from 'esbuild';
|
||||||
import { basename } from 'node:path';
|
import { basename } from 'node:path';
|
||||||
|
|
||||||
@@ -50,9 +51,20 @@ export default async function compileScripts(esBuildOptions = null) {
|
|||||||
esBuildOptions !== developmentESBuildOptions &&
|
esBuildOptions !== developmentESBuildOptions &&
|
||||||
esBuildOptions !== productionESBuildOptions
|
esBuildOptions !== productionESBuildOptions
|
||||||
) {
|
) {
|
||||||
esBuildOptions = Object.assign({}, defaultESBuildOptions, esBuildOptions);
|
esBuildOptions = merge({}, defaultESBuildOptions, esBuildOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @async
|
||||||
|
* @param {object} entry - The entrypoint to process.
|
||||||
|
* @param {string[]} entry.includes - One or more paths to process.
|
||||||
|
* @param {string} [entry.outdir] - The directory to write to.
|
||||||
|
* @param {string} [entry.outfile] - The file to write to.
|
||||||
|
* @param {?string} [entry.label] - The task label.
|
||||||
|
* Defaults to the outdir or outfile name.
|
||||||
|
* @throws {TypeError} If outdir and outfile are missing.
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
loconfig.tasks.scripts.forEach(async ({
|
loconfig.tasks.scripts.forEach(async ({
|
||||||
includes,
|
includes,
|
||||||
outdir = '',
|
outdir = '',
|
||||||
@@ -67,6 +79,10 @@ export default async function compileScripts(esBuildOptions = null) {
|
|||||||
console.time(timeLabel);
|
console.time(timeLabel);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (!Array.isArray(includes)) {
|
||||||
|
includes = [ includes ];
|
||||||
|
}
|
||||||
|
|
||||||
includes = resolve(includes);
|
includes = resolve(includes);
|
||||||
|
|
||||||
if (outdir) {
|
if (outdir) {
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
import loconfig from '../utils/config.js';
|
import loconfig from '../helpers/config.js';
|
||||||
import message from '../utils/message.js';
|
import message from '../helpers/message.js';
|
||||||
import notification from '../utils/notification.js';
|
import notification from '../helpers/notification.js';
|
||||||
import postcss, { pluginsMap as postcssPluginsMap } from '../utils/postcss.js';
|
import {
|
||||||
import resolve from '../utils/template.js';
|
createProcessor,
|
||||||
|
pluginsMap as postcssPluginsMap,
|
||||||
|
supportsPostCSS
|
||||||
|
} from '../helpers/postcss.js';
|
||||||
|
import resolve from '../helpers/template.js';
|
||||||
|
import { merge } from '../utils/index.js';
|
||||||
import { writeFile } from 'node:fs/promises';
|
import { writeFile } from 'node:fs/promises';
|
||||||
import { basename } from 'node:path';
|
import { basename } from 'node:path';
|
||||||
import { promisify } from 'node:util';
|
import { promisify } from 'node:util';
|
||||||
@@ -82,10 +87,10 @@ export default async function compileStyles(sassOptions = null, postcssOptions =
|
|||||||
sassOptions !== developmentSassOptions &&
|
sassOptions !== developmentSassOptions &&
|
||||||
sassOptions !== productionSassOptions
|
sassOptions !== productionSassOptions
|
||||||
) {
|
) {
|
||||||
sassOptions = Object.assign({}, defaultSassOptions, sassOptions);
|
sassOptions = merge({}, defaultSassOptions, sassOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (postcss) {
|
if (supportsPostCSS) {
|
||||||
if (postcssOptions == null) {
|
if (postcssOptions == null) {
|
||||||
postcssOptions = productionPostCSSOptions;
|
postcssOptions = productionPostCSSOptions;
|
||||||
} else if (
|
} else if (
|
||||||
@@ -93,10 +98,19 @@ export default async function compileStyles(sassOptions = null, postcssOptions =
|
|||||||
postcssOptions !== developmentPostCSSOptions &&
|
postcssOptions !== developmentPostCSSOptions &&
|
||||||
postcssOptions !== productionPostCSSOptions
|
postcssOptions !== productionPostCSSOptions
|
||||||
) {
|
) {
|
||||||
postcssOptions = Object.assign({}, defaultPostCSSOptions, postcssOptions);
|
postcssOptions = merge({}, defaultPostCSSOptions, postcssOptions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @async
|
||||||
|
* @param {object} entry - The entrypoint to process.
|
||||||
|
* @param {string[]} entry.infile - The file to process.
|
||||||
|
* @param {string} entry.outfile - The file to write to.
|
||||||
|
* @param {?string} [entry.label] - The task label.
|
||||||
|
* Defaults to the outfile name.
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
loconfig.tasks.styles.forEach(async ({
|
loconfig.tasks.styles.forEach(async ({
|
||||||
infile,
|
infile,
|
||||||
outfile,
|
outfile,
|
||||||
@@ -116,9 +130,9 @@ export default async function compileStyles(sassOptions = null, postcssOptions =
|
|||||||
outFile: outfile,
|
outFile: outfile,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (postcss && postcssOptions) {
|
if (supportsPostCSS && postcssOptions) {
|
||||||
if (typeof postcssProcessor === 'undefined') {
|
if (typeof postcssProcessor === 'undefined') {
|
||||||
postcssProcessor = createPostCSSProcessor(
|
postcssProcessor = createProcessor(
|
||||||
postcssPluginsMap,
|
postcssPluginsMap,
|
||||||
postcssOptions
|
postcssOptions
|
||||||
);
|
);
|
||||||
@@ -191,35 +205,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.
|
* Purge unused styles from CSS files.
|
||||||
*
|
*
|
||||||
@@ -232,22 +217,23 @@ function createPostCSSProcessor(pluginsListOrMap, options)
|
|||||||
*/
|
*/
|
||||||
async function purgeUnusedCSS(outfile, label) {
|
async function purgeUnusedCSS(outfile, label) {
|
||||||
label = label ?? basename(outfile);
|
label = label ?? basename(outfile);
|
||||||
|
|
||||||
const timeLabel = `${label} purged in`;
|
const timeLabel = `${label} purged in`;
|
||||||
console.time(timeLabel);
|
console.time(timeLabel);
|
||||||
|
|
||||||
const purgeCSSContentFiles = Array.from(loconfig.tasks.purgeCSS.content);
|
const purgeCSSContentFiles = Array.from(loconfig.tasks.purgeCSS.content);
|
||||||
|
|
||||||
const purgeCSSResults = await new PurgeCSS().purge({
|
const purgeCSSResults = await (new PurgeCSS()).purge({
|
||||||
content: purgeCSSContentFiles,
|
content: purgeCSSContentFiles,
|
||||||
css: [ outfile ],
|
css: [ outfile ],
|
||||||
rejected: true,
|
rejected: true,
|
||||||
defaultExtractor: content => content.match(/[a-z0-9_\-\\\/\@]+/gi) || [],
|
defaultExtractor: (content) => content.match(/[a-z0-9_\-\\\/\@]+/gi) || [],
|
||||||
safelist: {
|
safelist: {
|
||||||
standard: [ /^((?!\bu-gc-).)*$/ ]
|
standard: [ /^((?!\bu-gc-).)*$/ ]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
for(let result of purgeCSSResults) {
|
for (let result of purgeCSSResults) {
|
||||||
await writeFile(outfile, result.css)
|
await writeFile(outfile, result.css)
|
||||||
|
|
||||||
message(`${label} purged`, 'chore', timeLabel);
|
message(`${label} purged`, 'chore', timeLabel);
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import loconfig from '../utils/config.js';
|
import loconfig from '../helpers/config.js';
|
||||||
import message from '../utils/message.js';
|
import message from '../helpers/message.js';
|
||||||
import notification from '../utils/notification.js';
|
import notification from '../helpers/notification.js';
|
||||||
import resolve from '../utils/template.js';
|
import resolve from '../helpers/template.js';
|
||||||
|
import { merge } from '../utils/index.js';
|
||||||
import { basename } from 'node:path';
|
import { basename } from 'node:path';
|
||||||
import mixer from 'svg-mixer';
|
import mixer from 'svg-mixer';
|
||||||
|
|
||||||
@@ -44,9 +45,18 @@ export default async function compileSVGs(mixerOptions = null) {
|
|||||||
mixerOptions !== developmentMixerOptions &&
|
mixerOptions !== developmentMixerOptions &&
|
||||||
mixerOptions !== productionMixerOptions
|
mixerOptions !== productionMixerOptions
|
||||||
) {
|
) {
|
||||||
mixerOptions = Object.assign({}, defaultMixerOptions, mixerOptions);
|
mixerOptions = merge({}, defaultMixerOptions, mixerOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @async
|
||||||
|
* @param {object} entry - The entrypoint to process.
|
||||||
|
* @param {string[]} entry.includes - One or more paths to process.
|
||||||
|
* @param {string} entry.outfile - The file to write to.
|
||||||
|
* @param {?string} [entry.label] - The task label.
|
||||||
|
* Defaults to the outfile name.
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
loconfig.tasks.svgs.forEach(async ({
|
loconfig.tasks.svgs.forEach(async ({
|
||||||
includes,
|
includes,
|
||||||
outfile,
|
outfile,
|
||||||
@@ -60,6 +70,10 @@ export default async function compileSVGs(mixerOptions = null) {
|
|||||||
console.time(timeLabel);
|
console.time(timeLabel);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (!Array.isArray(includes)) {
|
||||||
|
includes = [ includes ];
|
||||||
|
}
|
||||||
|
|
||||||
includes = resolve(includes);
|
includes = resolve(includes);
|
||||||
outfile = resolve(outfile);
|
outfile = resolve(outfile);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import loconfig from '../utils/config.js';
|
import loconfig from '../helpers/config.js';
|
||||||
import message from '../utils/message.js';
|
import message from '../helpers/message.js';
|
||||||
import resolve from '../utils/template.js';
|
import resolve from '../helpers/template.js';
|
||||||
|
import { merge } from '../utils/index.js';
|
||||||
import { randomBytes } from 'node:crypto';
|
import { randomBytes } from 'node:crypto';
|
||||||
import events from 'node:events';
|
import events from 'node:events';
|
||||||
import {
|
import {
|
||||||
@@ -94,11 +95,22 @@ export default async function bumpVersions(versionOptions = null) {
|
|||||||
versionOptions !== developmentVersionOptions &&
|
versionOptions !== developmentVersionOptions &&
|
||||||
versionOptions !== productionVersionOptions
|
versionOptions !== productionVersionOptions
|
||||||
) {
|
) {
|
||||||
versionOptions = Object.assign({}, defaultVersionOptions, versionOptions);
|
versionOptions = merge({}, defaultVersionOptions, versionOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
const queue = new Map();
|
const queue = new Map();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @async
|
||||||
|
* @param {object} entry - The entrypoint to process.
|
||||||
|
* @param {string} entry.outfile - The file to write to.
|
||||||
|
* @param {?string} [entry.label] - The task label.
|
||||||
|
* Defaults to the outfile name.
|
||||||
|
* @param {?string} [entry.format] - The version number format.
|
||||||
|
* @param {?string} [entry.key] - The JSON field name assign the version number to.
|
||||||
|
* @param {?string|number} [entry.pretty] - The white space to use to format the JSON file.
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
loconfig.tasks.versions.forEach(({
|
loconfig.tasks.versions.forEach(({
|
||||||
outfile,
|
outfile,
|
||||||
label = null,
|
label = null,
|
||||||
|
|||||||
@@ -1,66 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file Provides simple user configuration options.
|
|
||||||
*/
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
export default loconfig;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new object with all nested object properties
|
|
||||||
* merged into it recursively.
|
|
||||||
*
|
|
||||||
* @param {object} target - The target object.
|
|
||||||
* @param {object[]} ...sources - The source object(s).
|
|
||||||
* @throws {TypeError} If the target and source are the same.
|
|
||||||
* @return {object} Returns the `target` object.
|
|
||||||
*/
|
|
||||||
export function merge(target, ...sources) {
|
|
||||||
for (const source of sources) {
|
|
||||||
if (target === source) {
|
|
||||||
throw new TypeError(
|
|
||||||
'Cannot merge, target and source are the same'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const key in source) {
|
|
||||||
if (source[key] != null) {
|
|
||||||
if (isObjectLike(source[key]) && isObjectLike(target[key])) {
|
|
||||||
merge(target[key], source[key]);
|
|
||||||
continue;
|
|
||||||
} else if (Array.isArray(source[key]) && Array.isArray(target[key])) {
|
|
||||||
target[key] = target[key].concat(source[key]);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
target[key] = source[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`.
|
|
||||||
*/
|
|
||||||
function isObjectLike(value) {
|
|
||||||
return (value != null && typeof value === 'object');
|
|
||||||
}
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file Retrieve the first available glob library.
|
|
||||||
*
|
|
||||||
* Note that options vary between libraries.
|
|
||||||
*
|
|
||||||
* Candidates:
|
|
||||||
*
|
|
||||||
* - {@link https://npmjs.com/package/tiny-glob tiny-glob}
|
|
||||||
* - {@link https://npmjs.com/package/globby globby}
|
|
||||||
* - {@link https://npmjs.com/package/fast-glob fast-glob}
|
|
||||||
* - {@link https://npmjs.com/package/glob glob}
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { promisify } from 'node:util';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {string[]} A list of packages to attempt import.
|
|
||||||
*/
|
|
||||||
const candidates = [
|
|
||||||
'tiny-glob',
|
|
||||||
'globby',
|
|
||||||
'fast-glob',
|
|
||||||
'glob',
|
|
||||||
];
|
|
||||||
|
|
||||||
let glob;
|
|
||||||
|
|
||||||
try {
|
|
||||||
glob = await importGlob();
|
|
||||||
} catch (err) {
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
export default glob;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Imports the first available glob function.
|
|
||||||
*
|
|
||||||
* @throws {TypeError} If no glob library was found.
|
|
||||||
* @return {function}
|
|
||||||
*/
|
|
||||||
async function importGlob() {
|
|
||||||
let glob, module;
|
|
||||||
|
|
||||||
for (let name of candidates) {
|
|
||||||
try {
|
|
||||||
module = await import(name);
|
|
||||||
|
|
||||||
if (typeof module.default !== 'function') {
|
|
||||||
throw new TypeError(`Expected ${name} to be a function`);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrap the function to ensure
|
|
||||||
* a common pattern.
|
|
||||||
*/
|
|
||||||
switch (name) {
|
|
||||||
case 'tiny-glob':
|
|
||||||
return createArrayableGlob(module.default, {
|
|
||||||
filesOnly: true
|
|
||||||
});
|
|
||||||
|
|
||||||
case 'glob':
|
|
||||||
return promisify(module.default);
|
|
||||||
|
|
||||||
default:
|
|
||||||
return module.default;
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
// swallow this error; skip to the next candidate.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new TypeError(
|
|
||||||
`No glob library was found, expected one of: ${candidates.join(', ')}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a wrapper function for the glob function
|
|
||||||
* to provide support for arrays of patterns.
|
|
||||||
*
|
|
||||||
* @param {function} glob - The glob function.
|
|
||||||
* @param {object} options - The glob options.
|
|
||||||
* @return {function}
|
|
||||||
*/
|
|
||||||
function createArrayableGlob(glob, options) {
|
|
||||||
return (patterns, options) => {
|
|
||||||
const globs = patterns.map((pattern) => glob(pattern, options));
|
|
||||||
|
|
||||||
return Promise.all(globs).then((files) => {
|
|
||||||
return [].concat.apply([], files);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
115
build/utils/index.js
Normal file
115
build/utils/index.js
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
/**
|
||||||
|
* @file Provides generic functions and constants.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {RegExp} - Match all special characters.
|
||||||
|
*/
|
||||||
|
const regexUnescaped = /[\[\]\{\}\(\)\-\*\+\?\.\,\\\^\$\|\#\s]/g;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Quotes regular expression characters.
|
||||||
|
*
|
||||||
|
* @param {string} str - The input string.
|
||||||
|
* @return {string} Returns the quoted (escaped) string.
|
||||||
|
*/
|
||||||
|
function escapeRegExp(str) {
|
||||||
|
return str.replace(regexUnescaped, '\\$&');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 (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`.
|
||||||
|
*/
|
||||||
|
function isObjectLike(value) {
|
||||||
|
return (value != null && typeof value === 'object');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new object with all nested object properties
|
||||||
|
* merged into it recursively.
|
||||||
|
*
|
||||||
|
* @param {object} target - The target object.
|
||||||
|
* @param {object[]} ...sources - The source object(s).
|
||||||
|
* @throws {TypeError} If the target and source are the same.
|
||||||
|
* @return {object} Returns the `target` object.
|
||||||
|
*/
|
||||||
|
function merge(target, ...sources) {
|
||||||
|
for (const source of sources) {
|
||||||
|
if (target === source) {
|
||||||
|
throw new TypeError(
|
||||||
|
'Cannot merge, target and source are the same'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const key in source) {
|
||||||
|
if (source[key] != null) {
|
||||||
|
if (isObjectLike(source[key]) && isObjectLike(target[key])) {
|
||||||
|
merge(target[key], source[key]);
|
||||||
|
continue;
|
||||||
|
} else if (Array.isArray(source[key]) && Array.isArray(target[key])) {
|
||||||
|
target[key] = target[key].concat(source[key]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
target[key] = source[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
escapeRegExp,
|
||||||
|
flatten,
|
||||||
|
isObjectLike,
|
||||||
|
merge,
|
||||||
|
regexUnescaped,
|
||||||
|
};
|
||||||
@@ -1,27 +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 default postcss;
|
|
||||||
export const pluginsList = [
|
|
||||||
autoprefixer,
|
|
||||||
];
|
|
||||||
export const pluginsMap = {
|
|
||||||
'autoprefixer': autoprefixer,
|
|
||||||
};
|
|
||||||
export {
|
|
||||||
autoprefixer
|
|
||||||
};
|
|
||||||
@@ -2,10 +2,11 @@ import concatFiles, { developmentConcatFilesArgs } from './tasks/concats.js';
|
|||||||
import compileScripts, { developmentScriptsArgs } from './tasks/scripts.js';
|
import compileScripts, { developmentScriptsArgs } from './tasks/scripts.js';
|
||||||
import compileStyles, { developmentStylesArgs } from './tasks/styles.js' ;
|
import compileStyles, { developmentStylesArgs } from './tasks/styles.js' ;
|
||||||
import compileSVGs, { developmentSVGsArgs } from './tasks/svgs.js';
|
import compileSVGs, { developmentSVGsArgs } from './tasks/svgs.js';
|
||||||
import loconfig, { merge } from './utils/config.js';
|
import loconfig from './helpers/config.js';
|
||||||
import message from './utils/message.js';
|
import message from './helpers/message.js';
|
||||||
import notification from './utils/notification.js';
|
import notification from './helpers/notification.js';
|
||||||
import resolve from './utils/template.js';
|
import resolve from './helpers/template.js';
|
||||||
|
import { merge } from './utils/index.js';
|
||||||
import browserSync from 'browser-sync';
|
import browserSync from 'browser-sync';
|
||||||
import { join } from 'node:path';
|
import { join } from 'node:path';
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user