@alloc/html-bundle
Version:
Bundle your HTML assets with Esbuild and LightningCSS. Custom plugins, HMR platform, and more.
156 lines • 5.53 kB
JavaScript
import { findElements, getAttribute, getTagName } from '@web/parse5-utils';
import browserslist from 'browserslist';
import browserslistToEsbuild from 'browserslist-to-esbuild';
import chokidar from 'chokidar';
import { EventEmitter } from 'events';
import { mkdir } from 'fs/promises';
import * as lightningCss from 'lightningcss';
import * as net from 'net';
import * as path from 'path';
import { loadConfig } from 'unconfig';
const env = JSON.stringify;
export async function loadBundleConfig(flags) {
const result = await loadConfig({
sources: [
{ files: 'bundle.config' },
{ files: 'package.json', rewrite: (config) => config?.bundle },
],
});
const userConfig = result.config;
const defaultPlugins = [];
if (flags.watch) {
defaultPlugins.push(await unwrapDefault(import('./plugins/cssReload.mjs')), await unwrapDefault(import('./plugins/liveScripts.mjs')));
}
if (flags.webext || userConfig.webext) {
defaultPlugins.push(await unwrapDefault(import('./plugins/webext.mjs')));
}
const plugins = defaultPlugins.concat(userConfig.plugins || []);
const browsers = userConfig.browsers ?? '>=0.25%, not dead';
const srcDir = userConfig.src ?? 'src';
const config = {
browsers,
build: 'build',
assets: 'public',
deletePrev: true,
isCritical: false,
...userConfig,
src: srcDir,
plugins: [],
events: new EventEmitter(),
virtualFiles: {},
watcher: flags.watch
? chokidar.watch(srcDir, { ignoreInitial: true })
: undefined,
copy: userConfig.copy ?? [],
webext: userConfig.webext == true ? {} : userConfig.webext || undefined,
htmlMinifierTerser: userConfig.htmlMinifierTerser ?? {},
esbuild: {
...userConfig.esbuild,
target: userConfig.esbuild?.target ?? browserslistToEsbuild(browsers),
define: {
'import.meta.env.DEV': env(flags.watch || false),
'process.env.NODE_ENV': env(process.env.NODE_ENV || 'development'),
...userConfig.esbuild?.define,
},
},
lightningCss: {
...userConfig.lightningCss,
targets: userConfig.lightningCss?.targets ??
lightningCss.browserslistToTargets(browserslist(browsers)),
drafts: {
nesting: true,
...userConfig.lightningCss?.drafts,
},
},
server: {
url: null,
port: 0,
...userConfig.server,
https: userConfig.server?.https != true
? userConfig.server?.https || undefined
: {},
},
getBuildPath(file) {
const wasAbsolute = path.isAbsolute(file);
if (wasAbsolute) {
file = path.relative(process.cwd(), file);
}
const src = config.src.replace(/^\.\//, '') + '/';
if (file.startsWith(src)) {
file = file.replace(src, config.build + '/');
}
else {
file = path.join(config.build, file);
}
if (wasAbsolute) {
file = path.join(process.cwd(), file);
}
return file.replace(/\.([cm]?)(?:jsx|tsx?)$/, '.$1js');
},
resolveDevUrl(id, importer) {
let url = config.resolve(id, importer);
if (url.protocol == 'file:') {
url = new URL(baseRelative(url.pathname), config.server.url);
}
return url;
},
resolve(id, importer = config.server.url) {
if (typeof importer == 'string') {
importer = new URL(importer, 'file:');
}
if (id[0] == '/' && importer.protocol == 'file:') {
return new URL('file://' + process.cwd() + id);
}
return new URL(id, importer);
},
};
await Promise.all(plugins.map(async (setup) => {
config.plugins.push(await setup(config, flags));
}));
return config;
}
function unwrapDefault(m) {
return m.then(m => (m.default ? m.default : Object.values(m)[0]));
}
export function createDir(file) {
return mkdir(path.dirname(file), { recursive: true });
}
export function toArray(value) {
return Array.isArray(value) ? value : [value];
}
export function resolveHome(file) {
if (file?.startsWith('~')) {
file = path.join(process.env.HOME || '', file.slice(1));
}
return file;
}
export function baseRelative(file) {
return '/' + path.relative(process.cwd(), file);
}
export function relative(from, to) {
let result = path.relative(path.dirname(from), to);
if (!result.startsWith('.')) {
result = './' + result;
}
return result;
}
export function findExternalScripts(rootNode) {
return findElements(rootNode, e => getTagName(e) === 'script' && !!getAttribute(e, 'src'));
}
export function findFreeTcpPort() {
return new Promise(resolve => {
const srv = net.createServer();
srv.listen(0, '127.0.0.1', () => {
const freeTcpPort = srv.address().port;
srv.close(() => resolve(freeTcpPort));
});
});
}
export function lowercaseKeys(obj) {
const result = {};
for (const [key, value] of Object.entries(obj)) {
result[key.toLowerCase()] = value;
}
return result;
}
//# sourceMappingURL=utils.mjs.map