generated from alecodes/base-template
feat: add build logic
This commit is contained in:
parent
e23a81c78e
commit
8ef4d8408c
11 changed files with 541 additions and 1 deletions
205
src/index.ts
Normal file
205
src/index.ts
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
import type { BuildConfig, BunPlugin, PluginBuilder } from "bun";
|
||||
import type { FileImporter } from "sass";
|
||||
import { type Config, type Entrypoint, EntrypointType, LogType } from "./types";
|
||||
|
||||
import { basename, join, normalize } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
import { rm } from "node:fs/promises";
|
||||
import slug from "slug";
|
||||
|
||||
// This is used to prevent creating folders with external packages names
|
||||
slug.extend({ "/": "-" });
|
||||
|
||||
const nodeModuleImporter: FileImporter<"async"> = {
|
||||
findFileUrl(url) {
|
||||
if (url.startsWith("@")) {
|
||||
return new URL(import.meta.resolve(url));
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
};
|
||||
|
||||
const sassPlugin: BunPlugin = {
|
||||
name: "Sass Loader",
|
||||
async setup(build: PluginBuilder) {
|
||||
const sass = await import("sass");
|
||||
|
||||
build.onLoad({ filter: /\.scss$/ }, async ({ path }) => {
|
||||
const result = await sass.compileAsync(path, {
|
||||
importers: [nodeModuleImporter],
|
||||
});
|
||||
|
||||
return {
|
||||
loader: "css",
|
||||
contents: result.css,
|
||||
};
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
function log(logtype: LogType, msg: string) {
|
||||
const reset = "\x1b[0m";
|
||||
let color = Bun.color("white", "ansi");
|
||||
|
||||
switch (logtype) {
|
||||
case LogType.Success:
|
||||
color = Bun.color("green", "ansi");
|
||||
break;
|
||||
case LogType.Error:
|
||||
color = Bun.color("#f24444", "ansi");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!color) {
|
||||
color = reset;
|
||||
}
|
||||
|
||||
console.log(color + msg + reset);
|
||||
}
|
||||
|
||||
// Packages needs to exist in the package.json file for this to work
|
||||
function resolvePackage(pkg: string): Entrypoint {
|
||||
const path = normalize(fileURLToPath(import.meta.resolve(pkg)));
|
||||
const file = Bun.file(import.meta.resolve(pkg));
|
||||
const mimetype = file.type.split(";").at(0);
|
||||
|
||||
let type: EntrypointType;
|
||||
|
||||
switch (mimetype) {
|
||||
case "text/x-scss":
|
||||
type = EntrypointType.Sass;
|
||||
break;
|
||||
case "text/javascript":
|
||||
type = EntrypointType.Js;
|
||||
break;
|
||||
default:
|
||||
throw new Error(`No loader found for type ${mimetype} at path ${path}`);
|
||||
}
|
||||
|
||||
return {
|
||||
path,
|
||||
type,
|
||||
};
|
||||
}
|
||||
|
||||
export default {
|
||||
log,
|
||||
|
||||
build: async (config: Config, entrypoints: Entrypoint[]) => {
|
||||
// Resolve external packages
|
||||
|
||||
const external_cache: string[] = [];
|
||||
const resolved_entrypoints = entrypoints.map((item) => {
|
||||
if (item.type !== EntrypointType.Package) {
|
||||
return item;
|
||||
}
|
||||
|
||||
external_cache.push(item.path);
|
||||
return resolvePackage(item.path);
|
||||
});
|
||||
|
||||
const baseConfig = {
|
||||
minify: config.production,
|
||||
outdir: `${config.outdir}/js`,
|
||||
packages: "external",
|
||||
root: config.root,
|
||||
splitting: config.production,
|
||||
};
|
||||
|
||||
// Apply build config per type
|
||||
const loaders = {
|
||||
assetLoader: {
|
||||
entrypoints: resolved_entrypoints
|
||||
.filter((entry) => entry.type === EntrypointType.Asset)
|
||||
.map((entry) => entry.path),
|
||||
outdir: `${config.outdir}/asset`,
|
||||
},
|
||||
stylesLoader: {
|
||||
...baseConfig,
|
||||
entrypoints: resolved_entrypoints
|
||||
.filter((entry) =>
|
||||
[EntrypointType.Css, EntrypointType.Sass].includes(entry.type),
|
||||
)
|
||||
.map((entry) => entry.path),
|
||||
outdir: `${config.outdir}/css`,
|
||||
plugins: [sassPlugin],
|
||||
},
|
||||
jsLoader: {
|
||||
...baseConfig,
|
||||
entrypoints: resolved_entrypoints
|
||||
.filter((entry) => entry.type === EntrypointType.Js)
|
||||
.map((entry) => entry.path),
|
||||
target: "browser",
|
||||
},
|
||||
};
|
||||
|
||||
// Transform into a list to later use Promise.all
|
||||
const assets = Object.values(loaders).filter(
|
||||
(item) => item.entrypoints.length !== 0,
|
||||
);
|
||||
|
||||
log(LogType.Info, "Building assets...");
|
||||
|
||||
const out = await Promise.all(
|
||||
assets.map(async (item) => {
|
||||
const result = await Bun.build(item as BuildConfig);
|
||||
|
||||
if (!result.success) {
|
||||
throw new AggregateError(result.logs, "Build failed");
|
||||
}
|
||||
|
||||
// Normalize external packages folder structure
|
||||
for (const out of result.outputs) {
|
||||
if (!out.path.includes("node_modules")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let package_name = external_cache.find((pkg) =>
|
||||
out.path.includes(pkg),
|
||||
);
|
||||
|
||||
if (!package_name) {
|
||||
throw new Error(
|
||||
`Could not normilize path for external package: ${out.path}`,
|
||||
);
|
||||
}
|
||||
|
||||
package_name = slug(package_name);
|
||||
|
||||
if (!package_name) {
|
||||
throw new Error(
|
||||
`Could not normilize path for external package: ${out.path}`,
|
||||
);
|
||||
}
|
||||
|
||||
const new_path = join(
|
||||
config.outdir,
|
||||
"pkgs",
|
||||
package_name,
|
||||
basename(out.path),
|
||||
);
|
||||
|
||||
await Bun.write(new_path, out);
|
||||
}
|
||||
|
||||
return result;
|
||||
}),
|
||||
);
|
||||
|
||||
if (out.some((item) => !item.success)) {
|
||||
throw new Error(`Some entrypoint failed to build: ${out}`);
|
||||
}
|
||||
|
||||
await rm(join(config.outdir, "node_modules"), {
|
||||
recursive: true,
|
||||
force: true,
|
||||
});
|
||||
|
||||
log(LogType.Success, "Complete!");
|
||||
},
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue