This is the code that runs https://bandolier.learnjsthehardway.com/ for you to review. It uses the https://git.learnjsthehardway.com/learn-javascript-the-hard-way/bandolier-template to create the documentation for the project.
https://bandolier.learnjsthehardway.com/
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.
194 lines
5.2 KiB
194 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);
|
|
}
|
|
}
|
|
|