Refactor build tasks

Decouple static "main" keys in mconfig.json and refactor tasks to support many output files.

Added:
- scripts.js: Array of output files (such as 'app.js') to iterate over to bundle JS files.
- styles.js: Array of input files (such as 'main.css' and 'critical.css') to iterate over to compile Sass files.
- svgs.js: Array of output files (such as 'sprite.svg') to iterate over to compile SVG spritesheets.

Changed:
- mconfig.json: Decouple entry points to individual tasks to allow for more flexibility in projects.
- concat.js: Refactor function to use promises to build list of JS files to concatenate.
- message.js: Replace if statements with switch for improved readability.
- message.js: If timerID provided with "waiting" type, log time.
- watch.js: Change CSS and JS reload watch paths to include all files.
- Sorted imports by path.
This commit is contained in:
Chauncey McAskill
2021-09-16 14:22:20 -04:00
parent 8b4d758443
commit e9e0e5784e
11 changed files with 217 additions and 148 deletions

View File

@@ -1,9 +1,9 @@
import { buildScripts } from './scripts.js';
import { concatVendors } from './concat.js';
import { compileScripts } from './scripts.js';
import { compileStyles } from './styles.js' ;
import { generateSpriteSVG } from './svgs.js' ;
import { compileSVGs } from './svgs.js' ;
buildScripts();
concatVendors();
compileScripts();
compileStyles();
generateSpriteSVG();
compileSVGs();

View File

@@ -1,27 +1,57 @@
import paths from '../mconfig.json';
import notification from './notification.js';
import message from './utils/message.js';
import fs from 'fs';
import concat from 'concat';
import fs from 'fs';
/**
* Concatenates third-party JavaScript files.
*/
export function concatVendors() {
console.time('Concat in');
const filename = 'vendors.js';
const outfile = paths.scripts.dest + filename;
const external = [
// Add files in node_modules example:
// 'node_modules/gsap/dist/gsap.min.js',
];
// Get all files in scripts/vendors/
const files = fs.readdirSync(paths.scripts.vendors.src);
const timeLabel = `${filename} concatenated in`;
console.time(timeLabel);
// Exclude files that are not JavaScript
var jsFiles = files.filter((file) => {
return file.includes('.js');
// Get all files in `scripts/vendors/`
fs.readdir(paths.scripts.vendors.src, (err, files) => {
if (err) {
message(`Error preparing ${filename}`, 'error');
message(err);
notification({
title: `${filename} concatenation failed 🚨`,
message: `${err.name}: ${e.message}`
});
}
if (files.length) {
// Exclude files that are not JavaScript
files = files.filter((file) => file.includes('.js'));
// Prepend absolute path
files = files.map((file) => paths.scripts.vendors.src + file);
}
if (external.length) {
files = files.concat(external);
}
concat(files, outfile).then(() => {
message(`${filename} concatenated'`, 'success', timeLabel);
}).catch((err) => {
message(`Error concatenating ${filename}`, 'error');
message(err);
notification({
title: `${filename} concatenation failed 🚨`,
message: `${err.name}: ${err.message}`
});
});
});
// Prepend absolute path
jsFiles = jsFiles.map((file) => {
return `${paths.scripts.vendors.src + file}`;
});
// add files in node_modules example:
// jsFiles.push('node_modules/gsap/dist/gsap.min.js');
concat(jsFiles, paths.scripts.dest + paths.scripts.vendors.main + '.js').then(() => {
message('Vendors concatenated', 'success', 'Concat in');
})
}

View File

@@ -2,8 +2,8 @@ import notifier from 'node-notifier';
export default function notification({ title, message }) {
notifier.notify({
title: title,
message: message,
title,
message,
icon: 'https://user-images.githubusercontent.com/4596862/54868065-c2aea200-4d5e-11e9-9ce3-e0013c15f48c.png'
});
}

View File

@@ -1,29 +1,40 @@
import esbuild from 'esbuild';
import paths from '../mconfig.json';
import message from './utils/message.js';
import notification from './notification.js';
import message from './utils/message.js';
import esbuild from 'esbuild';
export function buildScripts() {
console.time('JS built in');
/**
* Bundles and minifies main JavaScript files.
*/
export function compileScripts() {
[
'app.js',
].forEach((filename) => {
const includes = [ paths.scripts.src + filename ];
const outfile = paths.scripts.dest + filename;
esbuild.build({
entryPoints: [ paths.scripts.src + paths.scripts.main + '.js' ],
bundle: true,
minify: true,
sourcemap: true,
color: true,
logLevel: 'error',
target: [ 'es2015' ],
outfile: paths.scripts.dest + paths.scripts.main + '.js'
}).catch((e) => {
// errors managments (already done in esbuild)
const timeLabel = `${filename} compiled in`;
console.time(timeLabel);
notification({
title: 'Javascript built failed 🚨',
message: `${e.errors[0].text} in ${e.errors[0].location.file} line ${e.errors[0].location.line}`
esbuild.build({
entryPoints: includes,
bundle: true,
minify: true,
sourcemap: true,
color: true,
logLevel: 'error',
target: [
'es2015',
],
outfile
}).catch((err) => {
// errors managments (already done in esbuild)
notification({
title: `${filename} compilation failed 🚨`,
message: `${err.errors[0].text} in ${err.errors[0].location.file} line ${err.errors[0].location.line}`
});
}).then(() => {
message(`${filename} compiled`, 'success', timeLabel);
});
}).then(() => {
message('Javascript built','success', 'JS built in')
})
});
}

View File

@@ -1,56 +1,54 @@
import fs from 'fs';
import sass from 'node-sass';
import paths from '../mconfig.json';
import fs from 'fs';
import message from './utils/message.js';
import notification from './notification.js';
/**
* Compiles and minifies main Sass files to CSS.
*/
export function compileStyles() {
console.time('Styles built in');
[
'critical',
'main',
].forEach((name) => {
const infile = paths.styles.src + name + '.scss';
const outfile = paths.styles.dest + name + '.css';
// Compile main scss
sass.render({
file: paths.styles.src + paths.styles.main + '.scss',
outFile: paths.styles.dest + paths.styles.main + '.css',
outputStyle: 'compressed',
sourceMap: true
}, (error, result) => {
if (error) {
message('Error compiling main.scss', 'error');
console.log(error.formatted);
const timeLabel = `${name}.css compiled in`;
console.time(timeLabel);
notification({
title: 'main.scss compilation failed 🚨',
message: `${error.formatted}`
sass.render({
file: infile,
outFile: outfile,
outputStyle: 'compressed',
sourceMap: true
}, (err, result) => {
if (err) {
message(`Error compiling ${name}.scss`, 'error');
message(err.formatted);
notification({
title: `${name}.scss compilation failed 🚨`,
message: err.formatted
});
return;
}
fs.writeFile(outfile, result.css, (err) => {
if (err) {
message(`Error compiling ${name}.scss`, 'error');
message(err.formatted);
notification({
title: `${name}.scss compilation failed 🚨`,
message: `Could not save stylesheet to ${name}.css`
});
return;
}
message(`${name}.css compiled`, 'success', timeLabel);
});
} else {
message('Styles built', 'success', 'Styles built in');
}
if (!error){
// No errors during the compilation, write this result on the disk
fs.writeFile(paths.styles.dest + paths.styles.main + '.css', result.css, (err) => {});
}
});
console.time('Critical style built in');
// Compile critical scss
sass.render({
file: paths.styles.src + paths.styles.critical + '.scss',
outFile: paths.styles.dest + paths.styles.critical + '.css',
outputStyle: 'compressed',
sourceMap: true
}, (error, result) => {
if (error) {
message('Error compiling critical.scss', 'error');
console.log(error);
} else {
message('Critical style built', 'success', 'Critical style built in');
}
if (!error){
// No errors during the compilation, write this result on the disk
fs.writeFile(paths.styles.dest + paths.styles.critical + '.css', result.css, (err) => {});
}
});
});
}

View File

@@ -1,19 +1,42 @@
import mixer from 'svg-mixer';
import paths from '../mconfig.json';
import notification from './notification.js';
import message from './utils/message.js';
import mixer from 'svg-mixer';
export function generateSpriteSVG() {
console.time('Sprite generated in');
/**
* Generates and transforms SVG spritesheets.
*/
export function compileSVGs() {
[
{
includes: [ paths.svgs.src + '*.svg' ],
filename: 'sprite.svg'
},
].forEach(({
includes,
filename
}) => {
const outfile = paths.scripts.dest + filename;
// Write sprite content on disk
mixer([
paths.svgs.src + '*.svg'
], {
spriteConfig: {
usages: false
}
}).then((result) => {
result.write(paths.svgs.dest + 'sprite.svg');
message('SVG Sprite generated', 'success', 'Sprite generated in');
const timeLabel = `${filename} compiled in`;
console.time(timeLabel);
mixer(includes, {
spriteConfig: {
usages: false
}
}).then((result) => {
result.write(outfile).then(() => {
message(`${filename} compiled`, 'success', timeLabel);
});
}).catch((err) => {
message(`Error compiling ${filename}`, 'error');
message(err);
notification({
title: `${filename} compilation failed 🚨`,
message: `${err.name}: ${err.message}`
});
});
});
}

View File

@@ -1,19 +1,31 @@
import kleur from 'kleur';
export default function message(text, type, timerId) {
if (type === 'success') {
console.log(kleur.bgGreen().black(`${text}`));
export default function message(text, type, timerID) {
switch (type) {
case 'success':
console.log(kleur.bgGreen().black(`${text}`));
if (timerId !== undefined) {
console.timeEnd(timerId)
}
} else if (type === 'error') {
console.log(kleur.red().underline(`${text}`));
} else if (type === 'waiting') {
console.log(kleur.blue().italic(`${text}`));
} else {
console.log(text);
if (timerID !== undefined) {
console.timeEnd(timerID);
}
break;
case 'error':
console.log(kleur.red().underline(`${text}`));
break;
case 'waiting':
console.log(kleur.blue().italic(`${text}`));
if (timerID !== undefined) {
console.timeLog(timerID);
}
break;
default:
console.log(text);
break;
}
console.log('');
}
};

View File

@@ -1,8 +1,8 @@
import { buildScripts } from './scripts.js';
import { concatVendors } from './concat.js';
import { compileStyles } from './styles.js' ;
import { generateSpriteSVG } from './svgs.js';
import paths from '../mconfig.json';
import { concatVendors } from './concat.js';
import { compileScripts } from './scripts.js';
import { compileStyles } from './styles.js' ;
import { compileSVGs } from './svgs.js';
import server from 'browser-sync';
const serverConfig = {
@@ -24,35 +24,34 @@ if (typeof paths.url === 'string' && paths.url.length > 0) {
server.init(serverConfig);
// Build scripts, compile styles, concat vendors and generate the svgs sprite on first hit
buildScripts();
concatVendors();
compileScripts();
compileStyles();
generateSpriteSVG();
compileSVGs();
// and call any methods on it.
server.watch(
[
paths.views.src,
paths.scripts.dest + paths.scripts.main + '.js',
paths.scripts.dest + paths.scripts.vendors.main + '.js',
paths.styles.dest + paths.styles.main + '.css',
paths.svgs.dest + 'sprite.svg'
paths.scripts.dest + '*.js',
paths.styles.dest + '*.css',
paths.svgs.dest + '*.svg',
]
).on('change', server.reload);
// Watch scripts
server.watch(
[
paths.scripts.src + '**/*.js'
paths.scripts.src + '**/*.js',
]
).on('change', () => {
buildScripts();
compileScripts();
});
// Watch scripts vendors
server.watch(
[
paths.scripts.vendors.src + '*.js'
paths.scripts.vendors.src + '*.js',
]
).on('change', () => {
concatVendors();
@@ -61,7 +60,7 @@ server.watch(
// Watch styles
server.watch(
[
paths.styles.src + '**/*.scss'
paths.styles.src + '**/*.scss',
]
).on('change', () => {
compileStyles();
@@ -70,8 +69,8 @@ server.watch(
// Watch svgs
server.watch(
[
paths.svgs.src + '*.svg'
paths.svgs.src + '*.svg',
]
).on('change', () => {
generateSpriteSVG();
compileSVGs();
});