cerxes-examples/cfg/buildup.run.js

284 lines
13 KiB
JavaScript

// Node imports
import path from 'path';
import process from "process";
import { exec } from "child_process";
// Rollup plugin imports
import less from 'rollup-plugin-less';
import sourcemaps from 'rollup-plugin-sourcemaps';
import stringLoader from "rollup-plugin-string";
import jsonPlugin from "rollup-plugin-json";
// Build/dev imports
import {Buildup, assetPlugin, babelPlugin, copyPlugin, handlebarsPlugin, minifyPlugin, commonJSPlugin} from "@cerxes/buildup";
// Util imports
import readArgs from "./util/read-args";
// Control
async function build(options) {
// Build
let error = false, result;
let watch = options.get("watch","w");
let development = options.get("dev", "d");
let server = options.get("server", "s");
let minify = options.get("minify", "m");
if(minify===undefined){minify = !development;}
try {
let settings = {
target: "web",
dev: development,
minify: minify,
es6: false //development
};
let builder = new Buildup(({meta: settings})=>({
outDir: "dist/" + settings.target,
inDir: "src",
clean: true,
alias: [
["common", "src/common"]
],
sourcemap: settings.dev?"inline":true, // 'inline' tends to work more reliably in chrome than just true (which is default and should be preferred)
plugins: [
// Import less as strings
less({// TODO fork/clone the less plugin, it's npm package is annoyingly transpiled (requiring us to have a babel-runtime) and it has other minor issues...
output: (args)=>args// This is how we avoid the .css file from being made, and it feels hacky
}),
// Resolve maps from node_modules
sourcemaps(),
// Resolve non-es6 node_module libraries
commonJSPlugin({
include: /node_modules\/.*$/,
exclude: /.*[\/\.]es6?[\/\.].*/
}),
],
steps: (config)=>[
[
{ // Site Build (www, targeting browsers)
inDir: "src/www",
outDir: config.outDir + (settings.outDir? "/"+settings.outDir : "")+"/www",
plugins: [
...config.plugins,
// Add ability to pass non-js files through the build-system
assetPlugin({
exclude: [/\.svg$/]
}),
// Load gcodes and svgs as strings
stringLoader({
include: ['**/*.gcode', '**/*.svg']
}),
// Transform source files with babel (uses src/.babelrc for configuration)
babelPlugin({
"target":"source"// transform the source inputs (could leave this undefined, its the default)
}),
// Transform output files with babel for browser compatibility
settings.es6? null : babelPlugin({
"target": "bundle", // only transform the output bundle, not the source inputs
"presets": [
[
"@babel/preset-env",
{
"targets": {
"browsers": [
"last 6 Chrome versions",
"last 1 Edge versions",
"last 2 Firefox versions",
]
},
"modules": false // Leave module processing to the custom transform-es-module plugin
}
]
],
"plugins": [
"@babel/plugin-syntax-dynamic-import",
"@cerxes/buildup/dist/babel-transform-es-modules"
],
}),
// Compress output
settings.minify? minifyPlugin() : null,
],
steps: (wwwConfig)=>[
// Serial compilation steps:
// - Compile vendor bundle first (needed/used in the next chunks)
[
{
// array in, single out
in: [
"@cerxes/cxs-ui",
],
out: "lib/vendor.js",
bundle:"flat",
},
],
// - Copy polyfills
Buildup.scanDir(
"node_modules/@webcomponents/webcomponentsjs/bundles/",
/(webcomponents-([\-a-zA-Z]+)\.js)$/
).then(wcPolyfills=>wcPolyfills.map(polyfill=>({
meta: polyfill,
in: polyfill.files,
out: "lib/polyfills/[1]",
plugins: [
copyPlugin()
]
}))),
// - Compile pages
Buildup.scanDir(
wwwConfig.resolveIn("pages"), // Scan directory src/pages for files
/(.*)\.page\.js$/ // (...) RegEx capture-group feeds into chunk-meta step allowing us to use [1]...[n] in strings below
).then(pages=>pages.map(page=>({
meta: page,
in: page.files,
out: "[1].page.js",
steps: (pageChunk)=>[
{
in: {
alias: "[1].polyfill-loader.virtual.js", // Passing in an alias within the src/ map will allow babel to find the appropriate .babelrc to transpile it
content: `import {loadPolyfills} from "pages/polyfill-loader"; loadPolyfills();`
},
out: "[1].loader.virtual.js",
emit: false,
steps: (polyloaderChunk)=>({
in: {
alias: "[1].loader.virtual.js", // Passing in an alias within the src/ map will allow babel to find the appropriate .babelrc to transpile it
content: `import {loadPage} from "pages/page-loader"; import(\`${"./" + path.basename(pageChunk.outPath)}\`).then(module=>loadPage(module));` // Loader-code
},
out: "[1].loader.virtual.js",
emit: false,
steps: (loaderChunk)=>[
{
// Feed the loader as input to html-generator
in: "page.hbs",
plugins: [handlebarsPlugin({
context: {
polyfill: polyloaderChunk.compiled.code,
pageCode: loaderChunk.compiled.code,
es6: settings.es6
}
})],
out: "[1].html"
}
]
})
}
]
}))),
// - Copy assets
Buildup.scanDir(
wwwConfig.resolveIn(""), // Scan directory src/pages for files
/(.*)\.(eot|svg|ttf|woff|png|jpg)$/ // (...) RegEx capture-group feeds into chunk-meta step allowing us to use [1]...[n] in strings below
).then(assets=>assets.map(asset=>({
meta: asset,
in: asset.files[0],
out: "[1].[2]",
asset: true
})))
]
}
],
[
{ // Server Build (targetting node-environment)
inDir: "src/server",
outDir: config.outDir + (settings.outDir? "/"+settings.outDir : ""),
plugins: [
jsonPlugin({
// for tree-shaking, properties will be declared as
// variables, using either `var` or `const`
preferConst: true, // Default: false
}),
...config.plugins,
// Transform source files with babel (uses src/.babelrc for configuration) for nodejs compatibility
babelPlugin({
"target":"source"// transform the source inputs (could leave this undefined, its the default)
}),
// Transform output files with babel for nodejs compatibility
babelPlugin({
"target": "bundle", // only transform the output bundle, not the source inputs
"presets": [
["@babel/preset-env", {
"targets": {
"node": "current"
},
}]
],
}),
// Compress output
settings.minify? minifyPlugin() : null,
],
nodeResolve: {
browser: false,
external: ({importee, importer, resolved, pkg})=> pkg.builtin||(pkg&&pkg.name==='mongodb')// Flag builtins as external
},
steps: (serverConfig)=>[
[
{
in: "main.js",
out: "main.js",
bundle:"flat",
},
],
]
}
],
]
}));
result = await builder.run(settings, {
watch: watch
}
);
}catch(err){
error = true;
console.error(err);
}
// Start server?
if(result&&server) {
console.log("Starting server...");
let started = exec("node \"./main.js\"",{
cwd: './dist/web'
});
started?.stdout.on('data', (data)=>{
console.log(data.toString());
});
started?.stderr.on('data', (data)=>{
console.error(data.toString());
});
}
if(watch) {
// If in watch/dev-server mode, enter this crappy infinite loop (TODO?)
let done = false;
let interval = 500;
while (!done) {
let msg = await new Promise((resolve) => setTimeout(() => resolve(error ? "KILL" : "TICK"), interval));
if (msg === "KILL") {
done = true;
}
}
}
return error;
}
// Start
try {
let args = process.argv.slice(2);
let options = readArgs(args);
build(options).catch(ex=>{
console.error(ex);
});
}catch(ex){
console.error(ex);
}