// @cerxes import {host, Host} from "@cerxes/host"; // Observables import {Observable} from "zen-observable/lib/Observable"; import {merge, combineLatest, zip} from "zen-observable/lib/extras"; // Rollup and plugins import * as rollup from 'rollup'; import babel from 'rollup-plugin-babel'; import resolve from 'rollup-plugin-node-resolve'; import commonjs from 'rollup-plugin-commonjs'; import json from "rollup-plugin-json"; import postcss from "rollup-plugin-postcss"; import { terser } from 'rollup-plugin-terser'; /** @type {Host} */ const srcDir = host.from("tests"); /** @type {Host} */ const distDir = host.from("public"); const assetsGlob = `**/*.@(${[ "html", "otf", 'svg', 'ico', 'png', 'jpg', 'jpeg' ].join('|')})`; const sourcesGlob = `**/index.@(js|jsx)`; const args = process.argv.slice(2); const entryConfig = (entry, output)=>({ input: srcDir.resolve(entry), output: { file: distDir.resolve(output), format: 'esm', sourcemap: true }, plugins: [ // json json(), // PostCSS-style postcss({ plugins: [], inject: false, minimize: !!args.includes('build') }), // babel babel(), // node_modules resolve({ extensions: ['.mjs', '.js', '.jsx', '.json'], }), // CJS-modules commonjs({ namedExports: { // If there were any... } }), // minify, but only in production args.includes('build') && terser(), ] }); function logBundle(output, duration){ let relativeOut = distDir.relative(output); console.log(relativeOut + " built in " + (duration/1000)+"s"); } /** * Build the tests * @returns {Promise} */ async function build(){ // Clean host await host.remove(distDir.workingDirectory, {recursive: true}); console.log("Dist cleaned!"); let assetsJob = srcDir.glob(assetsGlob).then( matched=>Promise.all( matched.map(asset=>host.copy(srcDir.resolve(asset), distDir.resolve(asset)).then(()=>asset)) ) ).then((assets)=>{ console.log("Assets copied"); return assets; }); let sourceJobs = srcDir.glob(sourcesGlob).then( matched=>Promise.all( matched.map(async (source)=>{ let sourceStart = new Date(); let entry = source; let output = distDir.resolve(source,'..','index.js'); let rollupCfg = entryConfig(entry,output); return await rollup.rollup(rollupCfg).then(async (bundle)=>{ let written = await bundle.write(rollupCfg.output); logBundle(output, (new Date()).getTime()-sourceStart.getTime()); return {bundle, written}; }); }) ) ); // Wait for both to be done let [app, assets] = await Promise.all([sourceJobs, assetsJob]); } /** * Watch the app (watch asset files and automatically rebuild, this triggers development mode, as it should never be used on the server!) */ async function watch(){ let lastUpdate = new Date(); // Clean host await host.remove(distDir.workingDirectory, { recursive: true }); console.log("Dist cleaned!"); // Configure a WebPackage (containing build-info) // Watch sources and map them to an observable let sourceJobs = await srcDir.glob(sourcesGlob).then( matched=>matched.map((source)=>{ let entry = source; let output = distDir.resolve(source,'..','index.js'); let rollupCfg = entryConfig(entry,output); let sourceWatcher = rollup.watch(rollupCfg); return new Observable(observer => { sourceWatcher.on('event', event => { if(event.code==='BUNDLE_END'){ logBundle(output, event.duration); observer.next(event); }else if(event.code==='FATAL'){ observer.complete(); }else if(event.code==='ERROR'){ console.error(event.error.toString()); } }); return () =>{ // On unsubscription, do what? }; }); }) ); let assetsObservable = srcDir.watch().glob(assetsGlob).sync(distDir.workingDirectory).map(event=>{ console.log("Assets synced!"); // console.log(event.files.join('\n')); return event.files; }); combineLatest(...sourceJobs, assetsObservable).subscribe(async (built)=>{ let newUpdate = new Date(); console.log(`Updated in ${(newUpdate.getTime()- lastUpdate.getTime())/1000}s`); lastUpdate = newUpdate; }); } // Run! if(args.includes('watch')){ watch(); }else{ build(); }