This is the template project that's checked out and configured when you run the bando-up command from ljsthw-bandolier. This is where the code really lives.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
bandolier-template/commands/build.js

195 lines
5.2 KiB

import fs from "fs/promises";
import { mkdir_to, exec_i } from "../lib/builderator.js";
import glob from "fast-glob";
import esbuild from "esbuild";
import sveltePlugin from "esbuild-svelte";
import path from 'path';
import { main as build_icons } from "./icons.js";
import { io } from "socket.io-client";
export const description = "Builds the Svelte app specified by --input and places the bundle in --output."
export const options = [
["--config <file>", "specify a build.json file", "build.json"],
["--watch", "have esbuild watch and rebuild"],
["--watch-pattern", "glob pattern for esbuild to watch file"],
["--log-level <level>", "esbuild log level"],
["--legal-comments", "how to handle copyright comments"],
];
const CWD = process.cwd();
const socket = io("ws://127.0.0.1:5001");
const dollarImport = () => ({
name: "dollarImport",
setup(build) {
build.onResolve({ filter: /.*/ }, (args) => {
if(args.path.startsWith("$")) {
args.path = path.join(CWD, args.path.slice(1));
return {
path: args.path,
external: false,
}
} else {
return {};
}
})
}
})
const buildIcons = (production) => ({
name: "buildIcons",
setup(build) {
build.onStart(async () => {
const config = {
allIcons: "./static/icons/*.svg",
spriteSheet: "./public/icon-sprites.svg",
iconsIndex: "./public/icons/index.json",
dontExit: true
}
if(production) {
config.prodList = "./static/prod_icons.json";
}
await build_icons(config);
});
}
})
const syncContent = (watch_pattern) => ({
name: "syncContent",
setup(build) {
build.onResolve({filter: /.*main\.js/}, (args) => {
// when there's no watch pattern we don't tell esbuild to watch
if(watch_pattern) {
const same_path = path.join(CWD, args.path);
const watch_files = glob.sync(path.join(CWD, watch_pattern));
return {
path: same_path,
watchFiles: watch_files
}
} else {
return undefined;
}
});
build.onEnd(async (result) => {
socket.emit("/reloader/notify", {});
if(result.errors.length > 0) {
console.error(`Build ended with ${result.errors.length} errors`);
} else {
if(process.platform === "win32") {
try {
exec_i("robocopy static\\ public\\ /e /NFL /NDL /NJH /NJS /nc /ns /np");
} catch(error) {
if(error.status === 16) {
console.error(error);
}
}
} else {
exec_i("rsync -a static/ public/");
}
}
});
}
})
const saveMetaFile = (build_meta, error_file) => ({
name: "saveMetaFile",
setup(build) {
build.onEnd(async (result) => {
// when there's an error esbuild doesn't include the metafile portion
if(result.metafile) {
mkdir_to(build_meta);
await fs.writeFile(build_meta, JSON.stringify(result.metafile, null, 4));
}
mkdir_to(error_file);
await fs.writeFile(error_file, JSON.stringify(result, null, 4));
});
}
})
const devMode = () => ({
name: 'devMode',
setup(build) {
const options = build.initialOptions;
options.define = options.define || {};
// esbuild 0.16 requires this to be a string, but changes it to code so this will be an actual boolean type in the code
options.define['process.env.DANGER_ADMIN'] = options.minify ? "0" : "1";
}
})
export const run_build = async (config, opts) => {
let build_meta;
let error_file;
// assign the opts to the config
Object.assign(config, opts);
// tack on the plugins list for esbuild
config.plugins = [
devMode(),
dollarImport(),
sveltePlugin({ compilerOptions: { css: true } }),
buildIcons(config.prod),
syncContent(config.watchPattern),
];
// make the directories if they don't exist
mkdir_to(config.outfile);
// HACK: esbuild is too strict about stray params so remove
delete config.prod;
delete config.watchPattern;
// fix up the metafile since esbuild is weird about it
if(typeof config.metafile === "string") {
build_meta = config.metafile;
error_file = config.errorFile;
config.metafile = true;
delete config.errorFile;
}
// add the plugin that saves the build results file if requested
if(build_meta) {
config.plugins.push(saveMetaFile(build_meta, error_file));
}
// when using JS to build, it returns the Object rather than write JSON out
let result;
try {
result = await esbuild.build(config);
} catch(error) {
// in this case, there is no result because of an error
result = error;
}
return result.errors.length;
}
export const main = async (opts) => {
const config = JSON.parse(await fs.readFile(opts.config));
let error_count = 0;
// remove this since esbuild doesn't support it
delete opts.config;
for(let i = 0; i < config.length; i++) {
const build = config[i];
// BUG: I'd like this to be not-sequential but that's too hard to make reliable right now
error_count += await run_build(build, opts);
}
if(error_count > 0) {
console.error(`Build finished with ${error_count} errors.`);
}
if(!opts.watch) {
process.exit(error_count > 0 ? 1 : 0);
}
}