178 lines
4.9 KiB
JavaScript
178 lines
4.9 KiB
JavaScript
const fs = require('fs');
|
|
const path = require('path');
|
|
const { minify } = require("terser");
|
|
const { performance } = require('perf_hooks');
|
|
const CleanCSS = require('clean-css');
|
|
|
|
const cleanCSSInstance = new CleanCSS({});
|
|
const productionMode = process.argv.some((arg) => arg === '--prod');
|
|
if (productionMode) {
|
|
console.log("Production mode is enabled");
|
|
}
|
|
|
|
const mapJsonPlugin = {
|
|
name: 'mapTilesJson',
|
|
setup(build) {
|
|
const mapImageFiles = fs.readdirSync("public/map").filter((file) => file.endsWith('.webp')).map((file) => path.basename(file, '.webp'));
|
|
|
|
const tiles = [[], [], [], []];
|
|
for (const mapImageFile of mapImageFiles) {
|
|
const [plane, x, y] = mapImageFile.split('_').map((x) => parseInt(x, 10));
|
|
tiles[plane].push(((x + y) * (x + y + 1)) / 2 + y);
|
|
}
|
|
|
|
const icons = JSON.parse(fs.readFileSync("public/data/map_icons.json", 'utf8'));
|
|
|
|
const labels = JSON.parse(fs.readFileSync("public/data/map_labels.json", 'utf8'));
|
|
|
|
const result = {
|
|
tiles,
|
|
icons,
|
|
labels
|
|
};
|
|
|
|
fs.writeFileSync('public/data/map.json', JSON.stringify(result));
|
|
}
|
|
}
|
|
|
|
const componentBuildPlugin = {
|
|
name: 'componentBuild',
|
|
setup(build) {
|
|
const components = new Set(JSON.parse(fs.readFileSync('components.json', 'utf8')));
|
|
|
|
build.onLoad({ filter: /\.js$/ }, async (args) => {
|
|
const componentDir = path.dirname(args.path);
|
|
const componentName = path.basename(args.path, '.js');
|
|
|
|
const isComponent = components.has(componentName);
|
|
let jsText = await fs.promises.readFile(args.path, 'utf8');
|
|
if (isComponent) {
|
|
try {
|
|
let htmlText = await fs.promises.readFile(`${componentDir}/${componentName}.html`, 'utf8');
|
|
jsText = jsText.replace(`{{${componentName}.html}}`, htmlText);
|
|
} catch {}
|
|
}
|
|
|
|
return {
|
|
contents: jsText,
|
|
loader: 'js'
|
|
};
|
|
});
|
|
}
|
|
}
|
|
|
|
const buildLoggingPlugin = {
|
|
name: "buildLogging",
|
|
setup(build) {
|
|
let start;
|
|
build.onStart(() => {
|
|
start = performance.now();
|
|
console.log('\nBuild started');
|
|
});
|
|
|
|
build.onEnd(() => {
|
|
console.log(`Build finished in ${(performance.now() - start).toFixed(1)}ms`);
|
|
});
|
|
}
|
|
};
|
|
|
|
const htmlBuildPlugin = {
|
|
name: "htmlBuild",
|
|
setup(build) {
|
|
const components = JSON.parse(fs.readFileSync('components.json', 'utf8'));
|
|
const imagesToInline = [
|
|
"/ui/border-button.png",
|
|
"/ui/border-button-dark.png",
|
|
"/ui/checkbox.png",
|
|
"/ui/border.png",
|
|
"/ui/border-dark.png",
|
|
"/ui/border-tiny.png",
|
|
"/ui/border-tiny-dark.png",
|
|
"/ui/297-0.png",
|
|
"/ui/297-0-dark.png"
|
|
];
|
|
|
|
build.onEnd(async () => {
|
|
let htmlFile = await fs.promises.readFile("src/index.html", "utf8");
|
|
|
|
const cssFiles = ['src/main.css', ...components.map((component) => `./src/${component}/${component}.css`)];
|
|
const cssReadResults = await Promise.all(cssFiles.map((cssFile) => fs.promises.readFile(cssFile, "utf8")));
|
|
let css = cssReadResults.join('');
|
|
|
|
for (imagePath of imagesToInline) {
|
|
const imageData = await fs.promises.readFile(`public/${imagePath}`, "base64");
|
|
css = css.replace(imagePath, `data:image/png;base64,${imageData}`);
|
|
}
|
|
|
|
if (productionMode) {
|
|
css = cleanCSSInstance.minify(css).styles;
|
|
}
|
|
htmlFile = htmlFile.replace("{{style}}", css);
|
|
|
|
const jsContent = await fs.promises.readFile('public/app.js', 'utf8');
|
|
htmlFile = htmlFile.replace("{{js}}", jsContent);
|
|
|
|
await fs.promises.writeFile("public/index.html", htmlFile);
|
|
});
|
|
}
|
|
};
|
|
|
|
const minifyJsPlugin = {
|
|
name: "minifyJs",
|
|
setup(build) {
|
|
build.onEnd(async () => {
|
|
if (!productionMode) return;
|
|
|
|
console.log('Minifying app.js');
|
|
const code = await fs.promises.readFile("public/app.js", "utf8");
|
|
const result = await minify(code, {
|
|
sourceMap: {
|
|
filename: "app.js",
|
|
url: "app.js.map"
|
|
},
|
|
ecma: "2017",
|
|
mangle: {
|
|
keep_classnames: false,
|
|
keep_fnames: false,
|
|
module: true,
|
|
reserved: [],
|
|
toplevel: true
|
|
},
|
|
compress: {
|
|
ecma: "2017"
|
|
},
|
|
module: true
|
|
});
|
|
|
|
await fs.promises.writeFile("public/app.js", result.code);
|
|
await fs.promises.writeFile("public/app.js.map", result.map);
|
|
});
|
|
}
|
|
};
|
|
|
|
function build() {
|
|
require('esbuild').build({
|
|
entryPoints: ['src/index.js'],
|
|
bundle: true,
|
|
sourcemap: true,
|
|
minify: false,
|
|
format: 'esm',
|
|
outfile: 'public/app.js',
|
|
plugins: [componentBuildPlugin, minifyJsPlugin, htmlBuildPlugin, buildLoggingPlugin, mapJsonPlugin]
|
|
}).catch((error) => console.error(error));
|
|
}
|
|
|
|
const watch = process.argv.find((arg) => arg === "--watch");
|
|
if (watch) {
|
|
const chokidar = require('chokidar');
|
|
const watcher = chokidar.watch('src', {
|
|
ignorePermissionErrors: true,
|
|
ignored: ".#*"
|
|
});
|
|
watcher.on('change', (event, path) => {
|
|
build();
|
|
});
|
|
}
|
|
|
|
build();
|