feat: initial commit
This commit is contained in:
@@ -0,0 +1,284 @@
|
||||
import { DefinePlugin, IgnorePlugin, type Configuration } from "webpack";
|
||||
import { findNatives } from "./src/meta/pack/natives.ts";
|
||||
import { existingSources } from "./src/meta/pack/sources.ts";
|
||||
import CopyPlugin from "copy-webpack-plugin";
|
||||
import { BundleAnalyzerPlugin } from "webpack-bundle-analyzer";
|
||||
import { readdir } from "node:fs/promises";
|
||||
|
||||
const self = new URL(import.meta.url).pathname;
|
||||
function file(path: string): string {
|
||||
return new URL(path, import.meta.url).pathname;
|
||||
}
|
||||
|
||||
export default async (): Promise<Configuration[]> => {
|
||||
// global webpack config options
|
||||
const defines: Record<string, string> = {};
|
||||
const copies: CopyPlugin.Pattern[] = [];
|
||||
const aliases: Record<string, string> = {};
|
||||
const compiledSources: string[] = [];
|
||||
|
||||
// read environment to configure the build
|
||||
const env =
|
||||
process.env.NODE_ENV === "production" ? "production" : "development";
|
||||
|
||||
const dbSources =
|
||||
process.env.DB_SOURCES === "dynamic"
|
||||
? "dynamic"
|
||||
: process.env.DB_SOURCES === "static"
|
||||
? "static"
|
||||
: "shared";
|
||||
|
||||
let disableDbSqlite = process.env.DISABLE_DB_SQLITE === "1";
|
||||
let disableBs3 = disableDbSqlite || process.env.DISABLE_BS3 === "1";
|
||||
let disableNodeSqlite =
|
||||
disableDbSqlite || process.env.DISABLE_NODE_SQLITE === "1";
|
||||
|
||||
const disableDbApi = process.env.DISABLE_DB_API === "1";
|
||||
|
||||
// find natives and add them to the build
|
||||
const natives: Record<string, string | null> = await findNatives();
|
||||
if (disableBs3) natives.sqlite = null;
|
||||
if (!natives.sqlite) disableBs3 = true;
|
||||
for (const [k, v] of Object.entries(natives)) {
|
||||
if (!v) {
|
||||
defines[`natives.${k}`] = "null";
|
||||
continue;
|
||||
}
|
||||
defines[`natives.${k}`] =
|
||||
"(__dirname+" +
|
||||
JSON.stringify("/../natives/" + v.split("/").at(-1)!) +
|
||||
")";
|
||||
copies.push({ from: v, to: "./natives" });
|
||||
}
|
||||
|
||||
// find binaries and add them to the build
|
||||
const binaries = Object.fromEntries(
|
||||
(await readdir(file("./src/bin"))).map((bin) => [
|
||||
bin.replace(/\..+$/, ""),
|
||||
file(`./src/bin/${bin}`),
|
||||
])
|
||||
);
|
||||
for (const bin of Object.keys(binaries)) {
|
||||
copies.push({
|
||||
from: self,
|
||||
to: `./bin/${bin}`,
|
||||
toType: "file",
|
||||
transform: () =>
|
||||
`#!/bin/sh\nexec node --enable-source-maps "$(dirname "$0")/${bin}.cjs" "$@"\n`,
|
||||
});
|
||||
}
|
||||
|
||||
// find schemas and add them to the build
|
||||
const schemas: string[] = [];
|
||||
{
|
||||
async function impl(dir: string) {
|
||||
for (const file of await readdir(dir, { withFileTypes: true })) {
|
||||
if (file.isDirectory()) {
|
||||
await impl(dir + "/" + file.name);
|
||||
} else if (file.isFile() && file.name.endsWith(".schema.json")) {
|
||||
schemas.push(dir + "/" + file.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
await impl(file("./src/schema"));
|
||||
for (const schema of schemas) {
|
||||
copies.push({ from: schema, to: "./schema" });
|
||||
}
|
||||
}
|
||||
|
||||
// resolve db sources statically or dynamically
|
||||
if (dbSources === "dynamic") {
|
||||
console.log("Resolving db interfaces dynamically");
|
||||
aliases[file("./src/db/dbSources.ts")] = file("./src/db/dbSources.dyn.ts");
|
||||
} else if (dbSources === "static") {
|
||||
console.log("Resolving db interfaces statically");
|
||||
aliases[file("./src/db/dbSources.ts")] = file(
|
||||
"./src/db/dbSources.static.ts"
|
||||
);
|
||||
} else {
|
||||
console.log("Resolving db interfaces shared");
|
||||
aliases[file("./src/db/dbSources.ts")] = file(
|
||||
"./src/db/dbSources.shared.ts"
|
||||
);
|
||||
}
|
||||
|
||||
// disable parts or all of the db interface
|
||||
if (disableBs3 && disableNodeSqlite) disableDbSqlite = true;
|
||||
if (disableDbSqlite) {
|
||||
console.log("Disabling sqlite db interface completely");
|
||||
aliases[file("./src/db/sqlite/getdb.static.ts")] = file("./src/db/stub.ts");
|
||||
aliases[file("./src/db/sqlite/getdb.dyn.ts")] = file("./src/db/stub.ts");
|
||||
} else if (disableBs3) {
|
||||
console.log("Disabling better-sqlite3 sqlite db backend");
|
||||
aliases[file("./src/db/sqlite/impl/implementations.ts")] = file(
|
||||
"./src/db/sqlite/impl/implementations.node.ts"
|
||||
);
|
||||
compiledSources.push("sqlite");
|
||||
} else if (disableNodeSqlite) {
|
||||
console.log("Disabling node:sqlite sqlite db backend");
|
||||
aliases[file("./src/db/sqlite/impl/implementations.ts")] = file(
|
||||
"./src/db/sqlite/impl/implementations.bs3.ts"
|
||||
);
|
||||
compiledSources.push("sqlite");
|
||||
} else {
|
||||
console.log(
|
||||
"Enabling sqlite db interface with better-sqlite3 and node:sqlite backends"
|
||||
);
|
||||
aliases[file("./src/db/sqlite/impl/implementations.ts")] = file(
|
||||
"./src/db/sqlite/impl/implementations.all.ts"
|
||||
);
|
||||
compiledSources.push("sqlite");
|
||||
}
|
||||
|
||||
// disable the api db interface
|
||||
if (disableDbApi) {
|
||||
console.log("Disabling api db interface completely");
|
||||
aliases[file("./src/db/api/getdb.static.ts")] = file("./src/db/stub.ts");
|
||||
aliases[file("./src/db/api/getdb.dyn.ts")] = file("./src/db/stub.ts");
|
||||
} else {
|
||||
console.log("Enabling api db interface");
|
||||
compiledSources.push("api");
|
||||
}
|
||||
|
||||
// check the compiled sources and add it to the defines
|
||||
compiledSources.sort();
|
||||
defines.compiledSources = JSON.stringify(compiledSources);
|
||||
for (const source of existingSources)
|
||||
defines[`compiledSources.${source}`] = JSON.stringify(
|
||||
compiledSources.includes(source)
|
||||
);
|
||||
if (!compiledSources.length) {
|
||||
console.warn(
|
||||
"No db interface enabled, the builds will be completely useless"
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// log about the natives we have
|
||||
console.log(
|
||||
`Using ${Object.values(natives).filter(Boolean).length} natives:`
|
||||
);
|
||||
for (const [key, path] of Object.entries(natives)) {
|
||||
if (path) console.log(`- ${key}: ${path}`);
|
||||
else console.warn(`- ${key}: (not found)`);
|
||||
}
|
||||
|
||||
// log about the binaries we're making
|
||||
console.log(`Generating ${Object.entries(binaries).length} binaries:`);
|
||||
for (const [key, path] of Object.entries(binaries)) {
|
||||
console.log(`- ${key}: ${path}`);
|
||||
}
|
||||
|
||||
// generate a json report for the stuff included in the build
|
||||
{
|
||||
const report = {
|
||||
binaries: Object.keys(binaries),
|
||||
natives: Object.entries(natives)
|
||||
.filter((x) => x[1])
|
||||
.map((x) => x[0]),
|
||||
db: { mode: dbSources, sources: compiledSources },
|
||||
schemas: schemas.map((x) => x.split("/").at(-1)!),
|
||||
};
|
||||
if (!disableDbSqlite)
|
||||
Object.assign(report.db, {
|
||||
sqlite: { bs3: !disableBs3, node: !disableNodeSqlite },
|
||||
});
|
||||
copies.push({
|
||||
from: self,
|
||||
to: "./report/build.json",
|
||||
toType: "file",
|
||||
transform: () => JSON.stringify(report, null, 2),
|
||||
});
|
||||
}
|
||||
|
||||
const config: Configuration[] = [];
|
||||
|
||||
config.push({
|
||||
mode: env,
|
||||
devtool: "source-map",
|
||||
target: "node",
|
||||
entry: binaries,
|
||||
output: {
|
||||
path: file("./dist"),
|
||||
filename: "bin/[name].cjs",
|
||||
chunkFilename: "chunk/bin.[name].chunk.cjs",
|
||||
},
|
||||
resolve: {
|
||||
extensionAlias: {
|
||||
".js": [".js", ".ts", ".tsx"],
|
||||
},
|
||||
extensions: [".js", ".ts"],
|
||||
alias: {
|
||||
...aliases,
|
||||
},
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.sql$/,
|
||||
include: file("./src"),
|
||||
loader: "raw-loader",
|
||||
},
|
||||
{
|
||||
type: "javascript/esm",
|
||||
include: file("./src/schema/validators.ts"),
|
||||
use: [
|
||||
{
|
||||
loader: "val-loader",
|
||||
options: {
|
||||
executableFile: file("./src/meta/pack/valLoader.ts"),
|
||||
loader: file("./src/meta/pack/validators.ts"),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
type: "javascript/esm",
|
||||
include: file("./src"),
|
||||
use: [
|
||||
{
|
||||
loader: "ts-loader",
|
||||
options: {
|
||||
ignoreDiagnostics: true,
|
||||
compilerOptions: {
|
||||
noEmit: false,
|
||||
jsx: "react-jsx",
|
||||
diagnostics: false,
|
||||
noEmitOnError: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
new DefinePlugin({
|
||||
...defines,
|
||||
"import.meta.hot": "undefined",
|
||||
}),
|
||||
new IgnorePlugin({
|
||||
resourceRegExp: /^react-devtools-core$|^\.\/devtools\.js$|^bindings$/,
|
||||
}),
|
||||
new CopyPlugin({
|
||||
patterns: copies,
|
||||
}),
|
||||
new BundleAnalyzerPlugin({
|
||||
openAnalyzer: false,
|
||||
analyzerMode: "static",
|
||||
reportFilename: "report/bin.html",
|
||||
}),
|
||||
],
|
||||
ignoreWarnings: [
|
||||
{
|
||||
// webpack doesn't like the `require` used by better-sqlite3
|
||||
// it's an indirect call to a runtime string, so it can't be analyzed
|
||||
// the way we use it however, we force the argument to be constant at build time
|
||||
module: /\/node_modules\/better-sqlite3\/lib\/database\.js/,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
return config;
|
||||
};
|
||||
Reference in New Issue
Block a user