robuild 0.0.5 ā 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +208 -41
- package/dist/_chunks/build-5gEwHBbl.mjs +2251 -0
- package/dist/_chunks/config-CeOzkcue.d.mts +492 -0
- package/dist/_chunks/package-DIjirV4E.mjs +83 -0
- package/dist/cli.mjs +142 -12
- package/dist/config.d.mts +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +8 -2
- package/dist/_chunks/build-eIjZ3Fk8.mjs +0 -388
- package/dist/_chunks/config-DxLkhDt6.d.mts +0 -99
package/dist/cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { build } from "./_chunks/build-
|
|
2
|
+
import { build } from "./_chunks/build-5gEwHBbl.mjs";
|
|
3
3
|
import { consola } from "consola";
|
|
4
4
|
import { parseArgs } from "node:util";
|
|
5
5
|
import { loadConfig } from "c12";
|
|
@@ -9,16 +9,105 @@ const args = parseArgs({
|
|
|
9
9
|
args: process.argv.slice(2),
|
|
10
10
|
allowPositionals: true,
|
|
11
11
|
options: {
|
|
12
|
-
dir: {
|
|
12
|
+
"dir": {
|
|
13
13
|
type: "string",
|
|
14
14
|
default: "."
|
|
15
15
|
},
|
|
16
|
-
stub: {
|
|
16
|
+
"stub": {
|
|
17
17
|
type: "boolean",
|
|
18
18
|
default: false
|
|
19
|
-
}
|
|
19
|
+
},
|
|
20
|
+
"watch": {
|
|
21
|
+
type: "boolean",
|
|
22
|
+
default: false,
|
|
23
|
+
short: "w"
|
|
24
|
+
},
|
|
25
|
+
"format": {
|
|
26
|
+
type: "string",
|
|
27
|
+
multiple: true
|
|
28
|
+
},
|
|
29
|
+
"platform": { type: "string" },
|
|
30
|
+
"target": { type: "string" },
|
|
31
|
+
"global-name": { type: "string" },
|
|
32
|
+
"clean": {
|
|
33
|
+
type: "boolean",
|
|
34
|
+
default: true
|
|
35
|
+
},
|
|
36
|
+
"no-clean": { type: "boolean" },
|
|
37
|
+
"external": {
|
|
38
|
+
type: "string",
|
|
39
|
+
multiple: true
|
|
40
|
+
},
|
|
41
|
+
"no-external": {
|
|
42
|
+
type: "string",
|
|
43
|
+
multiple: true
|
|
44
|
+
},
|
|
45
|
+
"log-level": { type: "string" },
|
|
46
|
+
"on-success": { type: "string" },
|
|
47
|
+
"fail-on-warn": { type: "boolean" },
|
|
48
|
+
"ignore-watch": {
|
|
49
|
+
type: "string",
|
|
50
|
+
multiple: true
|
|
51
|
+
},
|
|
52
|
+
"from-vite": { type: "boolean" },
|
|
53
|
+
"workspace": { type: "boolean" },
|
|
54
|
+
"filter": {
|
|
55
|
+
type: "string",
|
|
56
|
+
multiple: true
|
|
57
|
+
},
|
|
58
|
+
"generate-exports": { type: "boolean" },
|
|
59
|
+
"cjs-default": { type: "string" },
|
|
60
|
+
"shims": { type: "boolean" },
|
|
61
|
+
"skip-node-modules": { type: "boolean" },
|
|
62
|
+
"unbundle": { type: "boolean" },
|
|
63
|
+
"help": { type: "boolean" },
|
|
64
|
+
"version": { type: "boolean" }
|
|
20
65
|
}
|
|
21
66
|
});
|
|
67
|
+
if (args.values.help) {
|
|
68
|
+
console.log(`
|
|
69
|
+
Usage: robuild [options] [entries...]
|
|
70
|
+
|
|
71
|
+
Options:
|
|
72
|
+
--dir <dir> Working directory (default: ".")
|
|
73
|
+
--stub Generate stub files instead of building
|
|
74
|
+
-w, --watch Enable watch mode
|
|
75
|
+
--format <format> Output format(s): esm, cjs, iife, umd (can be used multiple times)
|
|
76
|
+
--platform <platform> Target platform: browser, node, neutral
|
|
77
|
+
--target <target> Target ES version: es5, es2015, es2016, es2017, es2018, es2019, es2020, es2021, es2022, esnext
|
|
78
|
+
--global-name <name> Global variable name for IIFE/UMD formats
|
|
79
|
+
--clean Clean output directory before build (default: true)
|
|
80
|
+
--no-clean Disable cleaning output directory
|
|
81
|
+
--external <module> Mark dependencies as external (can be used multiple times)
|
|
82
|
+
--no-external <module> Force bundle dependencies (can be used multiple times)
|
|
83
|
+
--log-level <level> Log level: silent, error, warn, info, verbose (default: info)
|
|
84
|
+
--on-success <command> Command to run after successful build
|
|
85
|
+
--fail-on-warn Fail build on warnings
|
|
86
|
+
--ignore-watch <pattern> Ignore patterns in watch mode (can be used multiple times)
|
|
87
|
+
--from-vite Load configuration from Vite config file
|
|
88
|
+
--workspace Enable workspace mode for monorepo builds
|
|
89
|
+
--filter <pattern> Filter workspace packages by name or path pattern (can be used multiple times)
|
|
90
|
+
--generate-exports Generate package.json exports field
|
|
91
|
+
--cjs-default <mode> CommonJS default export handling: true, false, auto (default: auto)
|
|
92
|
+
--shims Enable CJS/ESM compatibility shims
|
|
93
|
+
--skip-node-modules Skip bundling node_modules dependencies
|
|
94
|
+
--unbundle Preserve file structure without bundling
|
|
95
|
+
--help Show this help message
|
|
96
|
+
--version Show version number
|
|
97
|
+
|
|
98
|
+
Examples:
|
|
99
|
+
robuild src/index.ts # Bundle single file
|
|
100
|
+
robuild src/index.ts --format esm cjs # Multiple formats
|
|
101
|
+
robuild src/ --watch # Transform directory in watch mode
|
|
102
|
+
robuild --help # Show help
|
|
103
|
+
`);
|
|
104
|
+
process.exit(0);
|
|
105
|
+
}
|
|
106
|
+
if (args.values.version) {
|
|
107
|
+
const pkg = await import("../package.json", { with: { type: "json" } });
|
|
108
|
+
console.log(pkg.default.version);
|
|
109
|
+
process.exit(0);
|
|
110
|
+
}
|
|
22
111
|
const { config = {} } = await loadConfig({
|
|
23
112
|
name: "robuild",
|
|
24
113
|
configFile: "build.config",
|
|
@@ -28,15 +117,34 @@ const rawEntries = args.positionals.length > 0 ? args.positionals : config.entri
|
|
|
28
117
|
const entries = rawEntries.map((entry) => {
|
|
29
118
|
if (typeof entry === "string") {
|
|
30
119
|
const [input, outDir] = entry.split(":");
|
|
31
|
-
|
|
120
|
+
if (input.endsWith("/")) return {
|
|
32
121
|
type: "transform",
|
|
33
122
|
input,
|
|
34
123
|
outDir
|
|
35
|
-
} : {
|
|
36
|
-
type: "bundle",
|
|
37
|
-
input: input.split(","),
|
|
38
|
-
outDir
|
|
39
124
|
};
|
|
125
|
+
else {
|
|
126
|
+
const baseEntry = {
|
|
127
|
+
type: "bundle",
|
|
128
|
+
input: input.split(","),
|
|
129
|
+
outDir
|
|
130
|
+
};
|
|
131
|
+
if (args.values.format) baseEntry.format = args.values.format;
|
|
132
|
+
if (args.values.platform) baseEntry.platform = args.values.platform;
|
|
133
|
+
if (args.values.target) baseEntry.target = args.values.target;
|
|
134
|
+
if (args.values["global-name"]) baseEntry.globalName = args.values["global-name"];
|
|
135
|
+
if (args.values["no-clean"]) baseEntry.clean = false;
|
|
136
|
+
else if (args.values.clean !== void 0) baseEntry.clean = args.values.clean;
|
|
137
|
+
if (args.values.external) baseEntry.external = args.values.external.map((ext) => ext.startsWith("/") && ext.endsWith("/") ? new RegExp(ext.slice(1, -1)) : ext);
|
|
138
|
+
if (args.values["no-external"]) baseEntry.noExternal = args.values["no-external"].map((ext) => ext.startsWith("/") && ext.endsWith("/") ? new RegExp(ext.slice(1, -1)) : ext);
|
|
139
|
+
if (args.values["cjs-default"]) {
|
|
140
|
+
const mode = args.values["cjs-default"];
|
|
141
|
+
baseEntry.cjsDefault = mode === "true" ? true : mode === "false" ? false : mode;
|
|
142
|
+
}
|
|
143
|
+
if (args.values.shims) baseEntry.shims = true;
|
|
144
|
+
if (args.values["skip-node-modules"]) baseEntry.skipNodeModules = true;
|
|
145
|
+
if (args.values.unbundle) baseEntry.unbundle = true;
|
|
146
|
+
return baseEntry;
|
|
147
|
+
}
|
|
40
148
|
}
|
|
41
149
|
return entry;
|
|
42
150
|
});
|
|
@@ -45,10 +153,32 @@ if (rawEntries.length === 0) {
|
|
|
45
153
|
consola.error("No build entries specified.");
|
|
46
154
|
process.exit(1);
|
|
47
155
|
}
|
|
48
|
-
|
|
156
|
+
const buildConfig = {
|
|
49
157
|
cwd: args.values.dir,
|
|
50
158
|
...config,
|
|
51
|
-
entries
|
|
52
|
-
|
|
159
|
+
entries,
|
|
160
|
+
watch: args.values.watch ? {
|
|
161
|
+
enabled: true,
|
|
162
|
+
...config.watch,
|
|
163
|
+
...args.values["ignore-watch"] ? { exclude: [...config.watch?.exclude || [], ...args.values["ignore-watch"]] } : {}
|
|
164
|
+
} : config.watch
|
|
165
|
+
};
|
|
166
|
+
if (args.values["log-level"]) buildConfig.logLevel = args.values["log-level"];
|
|
167
|
+
if (args.values["on-success"]) buildConfig.onSuccess = args.values["on-success"];
|
|
168
|
+
if (args.values["fail-on-warn"]) buildConfig.failOnWarn = true;
|
|
169
|
+
if (args.values["ignore-watch"]) buildConfig.ignoreWatch = args.values["ignore-watch"];
|
|
170
|
+
if (args.values["from-vite"]) buildConfig.fromVite = true;
|
|
171
|
+
if (args.values.workspace) buildConfig.workspace = {
|
|
172
|
+
packages: ["packages/*", "apps/*"],
|
|
173
|
+
...config.workspace
|
|
174
|
+
};
|
|
175
|
+
if (args.values.filter) buildConfig.filter = args.values.filter;
|
|
176
|
+
if (args.values["generate-exports"]) buildConfig.exports = {
|
|
177
|
+
enabled: true,
|
|
178
|
+
includeTypes: true,
|
|
179
|
+
autoUpdate: true,
|
|
180
|
+
...config.exports
|
|
181
|
+
};
|
|
182
|
+
await build(buildConfig);
|
|
53
183
|
|
|
54
184
|
//#endregion
|
package/dist/config.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { defineConfig } from "./_chunks/config-
|
|
1
|
+
import { defineConfig } from "./_chunks/config-CeOzkcue.mjs";
|
|
2
2
|
export { defineConfig };
|
package/dist/index.d.mts
CHANGED
package/dist/index.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "robuild",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.7",
|
|
5
5
|
"packageManager": "pnpm@10.11.1",
|
|
6
6
|
"description": "Zero-config ESM/TS package builder. Powered by Rolldown and Oxc",
|
|
7
7
|
"license": "MIT",
|
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
"robuild": "esno src/cli.ts",
|
|
25
25
|
"prepack": "pnpm build",
|
|
26
26
|
"release": "pnpm test && changelogen --release && npm publish && git push --follow-tags",
|
|
27
|
-
"test": "
|
|
27
|
+
"test": "vitest run",
|
|
28
|
+
"test:watch": "vitest",
|
|
28
29
|
"test:types": "tsc --noEmit --skipLibCheck",
|
|
29
30
|
"docs:dev": "vitepress dev docs",
|
|
30
31
|
"docs:build": "vitepress build docs",
|
|
@@ -32,10 +33,14 @@
|
|
|
32
33
|
},
|
|
33
34
|
"dependencies": {
|
|
34
35
|
"c12": "^3.0.4",
|
|
36
|
+
"chokidar": "^4.0.3",
|
|
35
37
|
"consola": "^3.4.2",
|
|
36
38
|
"defu": "^6.1.4",
|
|
37
39
|
"exsolve": "^1.0.5",
|
|
40
|
+
"glob": "^11.0.3",
|
|
41
|
+
"js-yaml": "^4.1.0",
|
|
38
42
|
"magic-string": "^0.30.17",
|
|
43
|
+
"minimatch": "^10.0.3",
|
|
39
44
|
"oxc-minify": "^0.89.0",
|
|
40
45
|
"oxc-parser": "^0.89.0",
|
|
41
46
|
"oxc-transform": "^0.89.0",
|
|
@@ -47,6 +52,7 @@
|
|
|
47
52
|
},
|
|
48
53
|
"devDependencies": {
|
|
49
54
|
"@antfu/eslint-config": "^5.3.0",
|
|
55
|
+
"@types/js-yaml": "^4.0.9",
|
|
50
56
|
"@types/node": "^24.4.0",
|
|
51
57
|
"@vitest/coverage-v8": "^3.2.2",
|
|
52
58
|
"automd": "^0.4.0",
|
|
@@ -1,388 +0,0 @@
|
|
|
1
|
-
import { mkdir, readFile, rm, symlink, writeFile } from "node:fs/promises";
|
|
2
|
-
import { basename, dirname, extname, isAbsolute, join, relative, resolve } from "node:path";
|
|
3
|
-
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
4
|
-
import { consola } from "consola";
|
|
5
|
-
import { colors } from "consola/utils";
|
|
6
|
-
import prettyBytes from "pretty-bytes";
|
|
7
|
-
import { builtinModules } from "node:module";
|
|
8
|
-
import { defu } from "defu";
|
|
9
|
-
import { resolveModulePath } from "exsolve";
|
|
10
|
-
import { parseSync } from "oxc-parser";
|
|
11
|
-
import { rolldown } from "rolldown";
|
|
12
|
-
import { dts } from "rolldown-plugin-dts";
|
|
13
|
-
import { promises, readdirSync, statSync } from "node:fs";
|
|
14
|
-
import { gzipSync } from "node:zlib";
|
|
15
|
-
import { minify } from "oxc-minify";
|
|
16
|
-
import MagicString from "magic-string";
|
|
17
|
-
import { transform } from "oxc-transform";
|
|
18
|
-
import { glob } from "tinyglobby";
|
|
19
|
-
|
|
20
|
-
//#region src/utils.ts
|
|
21
|
-
function fmtPath(path) {
|
|
22
|
-
return resolve(path).replace(process.cwd(), ".");
|
|
23
|
-
}
|
|
24
|
-
function analyzeDir(dir) {
|
|
25
|
-
if (Array.isArray(dir)) {
|
|
26
|
-
let totalSize$1 = 0;
|
|
27
|
-
let totalFiles = 0;
|
|
28
|
-
for (const d of dir) {
|
|
29
|
-
const { size, files: files$1 } = analyzeDir(d);
|
|
30
|
-
totalSize$1 += size;
|
|
31
|
-
totalFiles += files$1;
|
|
32
|
-
}
|
|
33
|
-
return {
|
|
34
|
-
size: totalSize$1,
|
|
35
|
-
files: totalFiles
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
let totalSize = 0;
|
|
39
|
-
const files = readdirSync(dir, {
|
|
40
|
-
withFileTypes: true,
|
|
41
|
-
recursive: true
|
|
42
|
-
});
|
|
43
|
-
for (const file of files) {
|
|
44
|
-
const fullPath = join(file.parentPath, file.name);
|
|
45
|
-
if (file.isFile()) {
|
|
46
|
-
const { size } = statSync(fullPath);
|
|
47
|
-
totalSize += size;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
return {
|
|
51
|
-
size: totalSize,
|
|
52
|
-
files: files.length
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
async function distSize(dir, entry) {
|
|
56
|
-
const build$1 = await rolldown({
|
|
57
|
-
input: join(dir, entry),
|
|
58
|
-
plugins: [],
|
|
59
|
-
platform: "neutral",
|
|
60
|
-
external: (id) => id[0] !== "." && !id.startsWith(dir)
|
|
61
|
-
});
|
|
62
|
-
const { output } = await build$1.generate({ inlineDynamicImports: true });
|
|
63
|
-
const code = output[0].code;
|
|
64
|
-
const { code: minified } = await minify(entry, code);
|
|
65
|
-
return {
|
|
66
|
-
size: Buffer.byteLength(code),
|
|
67
|
-
minSize: Buffer.byteLength(minified),
|
|
68
|
-
minGzipSize: gzipSync(minified).length
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
async function sideEffectSize(dir, entry) {
|
|
72
|
-
const virtualEntry = {
|
|
73
|
-
name: "virtual-entry",
|
|
74
|
-
async resolveId(id, importer, opts) {
|
|
75
|
-
if (id === "#entry") return { id };
|
|
76
|
-
const resolved = await this.resolve(id, importer, opts);
|
|
77
|
-
if (!resolved) return null;
|
|
78
|
-
resolved.moduleSideEffects = null;
|
|
79
|
-
return resolved;
|
|
80
|
-
},
|
|
81
|
-
load(id) {
|
|
82
|
-
if (id === "#entry") return `import * as _lib from "${join(dir, entry)}";`;
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
const build$1 = await rolldown({
|
|
86
|
-
input: "#entry",
|
|
87
|
-
platform: "neutral",
|
|
88
|
-
external: (id) => id[0] !== "." && !id.startsWith(dir),
|
|
89
|
-
plugins: [virtualEntry]
|
|
90
|
-
});
|
|
91
|
-
const { output } = await build$1.generate({ inlineDynamicImports: true });
|
|
92
|
-
if (process.env.INSPECT_BUILD) {
|
|
93
|
-
console.log("---------[side effects]---------");
|
|
94
|
-
console.log(entry);
|
|
95
|
-
console.log(output[0].code);
|
|
96
|
-
console.log("-------------------------------");
|
|
97
|
-
}
|
|
98
|
-
return Buffer.byteLength(output[0].code.trim());
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
//#endregion
|
|
102
|
-
//#region src/builders/plugins/shebang.ts
|
|
103
|
-
const SHEBANG_RE = /^#![^\n]*/;
|
|
104
|
-
function shebangPlugin() {
|
|
105
|
-
return {
|
|
106
|
-
name: "robuild-shebang",
|
|
107
|
-
async writeBundle(options, bundle) {
|
|
108
|
-
for (const [fileName, output] of Object.entries(bundle)) {
|
|
109
|
-
if (output.type !== "chunk") continue;
|
|
110
|
-
if (hasShebang(output.code)) {
|
|
111
|
-
const outFile = resolve(options.dir, fileName);
|
|
112
|
-
await makeExecutable(outFile);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
function hasShebang(code) {
|
|
119
|
-
return SHEBANG_RE.test(code);
|
|
120
|
-
}
|
|
121
|
-
async function makeExecutable(filePath) {
|
|
122
|
-
await promises.chmod(filePath, 493).catch(() => {});
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
//#endregion
|
|
126
|
-
//#region src/builders/bundle.ts
|
|
127
|
-
async function rolldownBuild(ctx, entry, hooks) {
|
|
128
|
-
const inputs = normalizeBundleInputs(entry.input, ctx);
|
|
129
|
-
if (entry.stub) {
|
|
130
|
-
for (const [distName, srcPath] of Object.entries(inputs)) {
|
|
131
|
-
const distPath = join(ctx.pkgDir, "dist", `${distName}.mjs`);
|
|
132
|
-
await mkdir(dirname(distPath), { recursive: true });
|
|
133
|
-
consola.log(`${colors.magenta("[stub bundle] ")} ${colors.underline(fmtPath(distPath))}`);
|
|
134
|
-
const srcContents = await readFile(srcPath, "utf8");
|
|
135
|
-
const parsed = parseSync(srcPath, srcContents);
|
|
136
|
-
const exportNames = parsed.module.staticExports.flatMap((e) => e.entries.map((e$1) => e$1.exportName.kind === "Default" ? "default" : e$1.exportName.name));
|
|
137
|
-
const hasDefaultExport = exportNames.includes("default");
|
|
138
|
-
const firstLine = srcContents.split("\n")[0];
|
|
139
|
-
const hasShebangLine = firstLine.startsWith("#!");
|
|
140
|
-
await writeFile(distPath, `${hasShebangLine ? `${firstLine}\n` : ""}export * from "${srcPath}";\n${hasDefaultExport ? `export { default } from "${srcPath}";\n` : ""}`, "utf8");
|
|
141
|
-
if (hasShebangLine) await makeExecutable(distPath);
|
|
142
|
-
await writeFile(distPath.replace(/\.mjs$/, ".d.mts"), `export * from "${srcPath}";\n${hasDefaultExport ? `export { default } from "${srcPath}";\n` : ""}`, "utf8");
|
|
143
|
-
}
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
146
|
-
const rolldownConfig = defu(entry.rolldown, {
|
|
147
|
-
cwd: ctx.pkgDir,
|
|
148
|
-
input: inputs,
|
|
149
|
-
plugins: [shebangPlugin()],
|
|
150
|
-
platform: "neutral",
|
|
151
|
-
external: [
|
|
152
|
-
...builtinModules,
|
|
153
|
-
...builtinModules.map((m) => `node:${m}`),
|
|
154
|
-
...[...Object.keys(ctx.pkg.dependencies || {}), ...Object.keys(ctx.pkg.peerDependencies || {})].flatMap((p) => [p, /* @__PURE__ */ new RegExp(`^${p}/`)])
|
|
155
|
-
]
|
|
156
|
-
});
|
|
157
|
-
if (entry.dts !== false) rolldownConfig.plugins.push(...dts({ ...entry.dts }));
|
|
158
|
-
await hooks.rolldownConfig?.(rolldownConfig, ctx);
|
|
159
|
-
const res = await rolldown(rolldownConfig);
|
|
160
|
-
const outDir = resolve(ctx.pkgDir, entry.outDir || "dist");
|
|
161
|
-
const outConfig = {
|
|
162
|
-
dir: outDir,
|
|
163
|
-
entryFileNames: "[name].mjs",
|
|
164
|
-
chunkFileNames: "_chunks/[name]-[hash].mjs",
|
|
165
|
-
minify: entry.minify
|
|
166
|
-
};
|
|
167
|
-
await hooks.rolldownOutput?.(outConfig, res, ctx);
|
|
168
|
-
const { output } = await res.write(outConfig);
|
|
169
|
-
await res.close();
|
|
170
|
-
const outputEntries = [];
|
|
171
|
-
const depsCache = /* @__PURE__ */ new Map();
|
|
172
|
-
const resolveDeps = (chunk) => {
|
|
173
|
-
if (!depsCache.has(chunk)) depsCache.set(chunk, /* @__PURE__ */ new Set());
|
|
174
|
-
const deps = depsCache.get(chunk);
|
|
175
|
-
for (const id of chunk.imports) {
|
|
176
|
-
if (builtinModules.includes(id) || id.startsWith("node:")) {
|
|
177
|
-
deps.add(`[Node.js]`);
|
|
178
|
-
continue;
|
|
179
|
-
}
|
|
180
|
-
const depChunk = output.find((o) => o.type === "chunk" && o.fileName === id);
|
|
181
|
-
if (depChunk) {
|
|
182
|
-
for (const dep of resolveDeps(depChunk)) deps.add(dep);
|
|
183
|
-
continue;
|
|
184
|
-
}
|
|
185
|
-
deps.add(id);
|
|
186
|
-
}
|
|
187
|
-
return [...deps].sort();
|
|
188
|
-
};
|
|
189
|
-
for (const chunk of output) {
|
|
190
|
-
if (chunk.type !== "chunk" || !chunk.isEntry) continue;
|
|
191
|
-
if (chunk.fileName.endsWith("ts")) continue;
|
|
192
|
-
outputEntries.push({
|
|
193
|
-
name: chunk.fileName,
|
|
194
|
-
exports: chunk.exports,
|
|
195
|
-
deps: resolveDeps(chunk),
|
|
196
|
-
...await distSize(outDir, chunk.fileName),
|
|
197
|
-
sideEffectSize: await sideEffectSize(outDir, chunk.fileName)
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
consola.log(`\n${outputEntries.map((o) => [
|
|
201
|
-
`${colors.magenta(`[bundle] `)}${colors.underline(fmtPath(join(outDir, o.name)))}`,
|
|
202
|
-
colors.dim(`${colors.bold("Size:")} ${prettyBytes(o.size)}, ${colors.bold(prettyBytes(o.minSize))} minified, ${prettyBytes(o.minGzipSize)} min+gzipped (Side effects: ${prettyBytes(o.sideEffectSize)})`),
|
|
203
|
-
o.exports.some((e) => e !== "default") ? colors.dim(`${colors.bold("Exports:")} ${o.exports.map((e) => e).join(", ")}`) : "",
|
|
204
|
-
o.deps.length > 0 ? colors.dim(`${colors.bold("Dependencies:")} ${o.deps.join(", ")}`) : ""
|
|
205
|
-
].filter(Boolean).join("\n")).join("\n\n")}`);
|
|
206
|
-
}
|
|
207
|
-
function normalizeBundleInputs(input, ctx) {
|
|
208
|
-
const inputs = {};
|
|
209
|
-
for (let src of Array.isArray(input) ? input : [input]) {
|
|
210
|
-
src = resolveModulePath(src, {
|
|
211
|
-
from: ctx.pkgDir,
|
|
212
|
-
extensions: [
|
|
213
|
-
".ts",
|
|
214
|
-
".js",
|
|
215
|
-
".mjs",
|
|
216
|
-
".cjs",
|
|
217
|
-
".json"
|
|
218
|
-
]
|
|
219
|
-
});
|
|
220
|
-
let relativeSrc = relative(join(ctx.pkgDir, "src"), src);
|
|
221
|
-
if (relativeSrc.startsWith("..")) relativeSrc = relative(join(ctx.pkgDir), src);
|
|
222
|
-
if (relativeSrc.startsWith("..")) throw new Error(`Source should be within the package directory (${ctx.pkgDir}): ${src}`);
|
|
223
|
-
const distName = join(dirname(relativeSrc), basename(relativeSrc, extname(relativeSrc)));
|
|
224
|
-
if (inputs[distName]) throw new Error(`Rename one of the entries to avoid a conflict in the dist name "${distName}":\n - ${src}\n - ${inputs[distName]}`);
|
|
225
|
-
inputs[distName] = src;
|
|
226
|
-
}
|
|
227
|
-
return inputs;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
//#endregion
|
|
231
|
-
//#region src/builders/transform.ts
|
|
232
|
-
/**
|
|
233
|
-
* Transform all .ts modules in a directory using oxc-transform.
|
|
234
|
-
*/
|
|
235
|
-
async function transformDir(ctx, entry) {
|
|
236
|
-
if (entry.stub) {
|
|
237
|
-
consola.log(`${colors.magenta("[stub transform] ")} ${colors.underline(`${fmtPath(entry.outDir)}/`)}`);
|
|
238
|
-
await symlink(entry.input, entry.outDir, "junction");
|
|
239
|
-
return;
|
|
240
|
-
}
|
|
241
|
-
const promises$1 = [];
|
|
242
|
-
for await (const entryName of await glob("**/*.*", { cwd: entry.input })) promises$1.push((async () => {
|
|
243
|
-
const entryPath = join(entry.input, entryName);
|
|
244
|
-
const ext = extname(entryPath);
|
|
245
|
-
switch (ext) {
|
|
246
|
-
case ".ts": {
|
|
247
|
-
const transformed = await transformModule(entryPath, entry);
|
|
248
|
-
const entryDistPath = join(entry.outDir, entryName.replace(/\.ts$/, ".mjs"));
|
|
249
|
-
await mkdir(dirname(entryDistPath), { recursive: true });
|
|
250
|
-
await writeFile(entryDistPath, transformed.code, "utf8");
|
|
251
|
-
if (SHEBANG_RE.test(transformed.code)) await makeExecutable(entryDistPath);
|
|
252
|
-
if (transformed.declaration) await writeFile(entryDistPath.replace(/\.mjs$/, ".d.mts"), transformed.declaration, "utf8");
|
|
253
|
-
return entryDistPath;
|
|
254
|
-
}
|
|
255
|
-
default: {
|
|
256
|
-
const entryDistPath = join(entry.outDir, entryName);
|
|
257
|
-
await mkdir(dirname(entryDistPath), { recursive: true });
|
|
258
|
-
const code = await readFile(entryPath, "utf8");
|
|
259
|
-
await writeFile(entryDistPath, code, "utf8");
|
|
260
|
-
if (SHEBANG_RE.test(code)) await makeExecutable(entryDistPath);
|
|
261
|
-
return entryDistPath;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
})());
|
|
265
|
-
const writtenFiles = await Promise.all(promises$1);
|
|
266
|
-
consola.log(`\n${colors.magenta("[transform] ")}${colors.underline(`${fmtPath(entry.outDir)}/`)}\n${writtenFiles.map((f) => colors.dim(fmtPath(f))).join("\n\n")}`);
|
|
267
|
-
}
|
|
268
|
-
/**
|
|
269
|
-
* Transform a .ts module using oxc-transform.
|
|
270
|
-
*/
|
|
271
|
-
async function transformModule(entryPath, entry) {
|
|
272
|
-
let sourceText = await readFile(entryPath, "utf8");
|
|
273
|
-
const sourceOptions = {
|
|
274
|
-
lang: "ts",
|
|
275
|
-
sourceType: "module"
|
|
276
|
-
};
|
|
277
|
-
const parsed = parseSync(entryPath, sourceText, { ...sourceOptions });
|
|
278
|
-
if (parsed.errors.length > 0) throw new Error(`Errors while parsing ${entryPath}:`, { cause: parsed.errors });
|
|
279
|
-
const resolveOptions = {
|
|
280
|
-
...entry.resolve,
|
|
281
|
-
from: pathToFileURL(entryPath),
|
|
282
|
-
extensions: entry.resolve?.extensions ?? [
|
|
283
|
-
".ts",
|
|
284
|
-
".js",
|
|
285
|
-
".mjs",
|
|
286
|
-
".cjs",
|
|
287
|
-
".json"
|
|
288
|
-
],
|
|
289
|
-
suffixes: entry.resolve?.suffixes ?? ["", "/index"]
|
|
290
|
-
};
|
|
291
|
-
const magicString = new MagicString(sourceText);
|
|
292
|
-
const updatedStarts = /* @__PURE__ */ new Set();
|
|
293
|
-
const rewriteSpecifier = (req) => {
|
|
294
|
-
const moduleId = req.value;
|
|
295
|
-
if (!moduleId.startsWith(".")) return;
|
|
296
|
-
if (updatedStarts.has(req.start)) return;
|
|
297
|
-
updatedStarts.add(req.start);
|
|
298
|
-
const resolvedAbsolute = resolveModulePath(moduleId, resolveOptions);
|
|
299
|
-
const newId = relative(dirname(entryPath), resolvedAbsolute.replace(/\.ts$/, ".mjs"));
|
|
300
|
-
magicString.remove(req.start, req.end);
|
|
301
|
-
magicString.prependLeft(req.start, JSON.stringify(newId.startsWith(".") ? newId : `./${newId}`));
|
|
302
|
-
};
|
|
303
|
-
for (const staticImport of parsed.module.staticImports) rewriteSpecifier(staticImport.moduleRequest);
|
|
304
|
-
for (const staticExport of parsed.module.staticExports) for (const staticExportEntry of staticExport.entries) if (staticExportEntry.moduleRequest) rewriteSpecifier(staticExportEntry.moduleRequest);
|
|
305
|
-
sourceText = magicString.toString();
|
|
306
|
-
const transformed = transform(entryPath, sourceText, {
|
|
307
|
-
...entry.oxc,
|
|
308
|
-
...sourceOptions,
|
|
309
|
-
cwd: dirname(entryPath),
|
|
310
|
-
typescript: {
|
|
311
|
-
declaration: { stripInternal: true },
|
|
312
|
-
...entry.oxc?.typescript
|
|
313
|
-
}
|
|
314
|
-
});
|
|
315
|
-
const transformErrors = transformed.errors.filter((err) => !err.message.includes("--isolatedDeclarations"));
|
|
316
|
-
if (transformErrors.length > 0) {
|
|
317
|
-
await writeFile("build-dump.ts", `/** Error dump for ${entryPath} */\n\n${sourceText}`, "utf8");
|
|
318
|
-
throw new Error(`Errors while transforming ${entryPath}: (hint: check build-dump.ts)`, { cause: transformErrors });
|
|
319
|
-
}
|
|
320
|
-
if (entry.minify) {
|
|
321
|
-
const res = minify(entryPath, transformed.code, entry.minify === true ? {} : entry.minify);
|
|
322
|
-
transformed.code = res.code;
|
|
323
|
-
transformed.map = res.map;
|
|
324
|
-
}
|
|
325
|
-
return transformed;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
//#endregion
|
|
329
|
-
//#region src/build.ts
|
|
330
|
-
/**
|
|
331
|
-
* Build dist/ from src/
|
|
332
|
-
*/
|
|
333
|
-
async function build(config) {
|
|
334
|
-
const start = Date.now();
|
|
335
|
-
const pkgDir = normalizePath(config.cwd);
|
|
336
|
-
const pkg = await readJSON(join(pkgDir, "package.json")).catch(() => ({}));
|
|
337
|
-
const ctx = {
|
|
338
|
-
pkg,
|
|
339
|
-
pkgDir
|
|
340
|
-
};
|
|
341
|
-
consola.log(`š¦ Building \`${ctx.pkg.name || "<no name>"}\` (\`${ctx.pkgDir}\`)`);
|
|
342
|
-
const hooks = config.hooks || {};
|
|
343
|
-
await hooks.start?.(ctx);
|
|
344
|
-
const entries = (config.entries || []).map((rawEntry) => {
|
|
345
|
-
let entry;
|
|
346
|
-
if (typeof rawEntry === "string") {
|
|
347
|
-
const [input, outDir] = rawEntry.split(":");
|
|
348
|
-
entry = input.endsWith("/") ? {
|
|
349
|
-
type: "transform",
|
|
350
|
-
input,
|
|
351
|
-
outDir
|
|
352
|
-
} : {
|
|
353
|
-
type: "bundle",
|
|
354
|
-
input: input.split(","),
|
|
355
|
-
outDir
|
|
356
|
-
};
|
|
357
|
-
} else entry = rawEntry;
|
|
358
|
-
if (!entry.input) throw new Error(`Build entry missing \`input\`: ${JSON.stringify(entry, null, 2)}`);
|
|
359
|
-
entry = { ...entry };
|
|
360
|
-
entry.outDir = normalizePath(entry.outDir || "dist", pkgDir);
|
|
361
|
-
entry.input = Array.isArray(entry.input) ? entry.input.map((p) => normalizePath(p, pkgDir)) : normalizePath(entry.input, pkgDir);
|
|
362
|
-
return entry;
|
|
363
|
-
});
|
|
364
|
-
await hooks.entries?.(entries, ctx);
|
|
365
|
-
const outDirs = [];
|
|
366
|
-
for (const outDir of entries.map((e) => e.outDir).sort()) if (!outDirs.some((dir) => outDir.startsWith(dir))) outDirs.push(outDir);
|
|
367
|
-
for (const outDir of outDirs) {
|
|
368
|
-
consola.log(`š§» Cleaning up \`${fmtPath(outDir)}\``);
|
|
369
|
-
await rm(outDir, {
|
|
370
|
-
recursive: true,
|
|
371
|
-
force: true
|
|
372
|
-
});
|
|
373
|
-
}
|
|
374
|
-
for (const entry of entries) await (entry.type === "bundle" ? rolldownBuild(ctx, entry, hooks) : transformDir(ctx, entry));
|
|
375
|
-
await hooks.end?.(ctx);
|
|
376
|
-
const dirSize = analyzeDir(outDirs);
|
|
377
|
-
consola.log(colors.dim(`\nΣ Total dist byte size: ${colors.underline(prettyBytes(dirSize.size))} (${colors.underline(dirSize.files)} files)`));
|
|
378
|
-
consola.log(`\nā
robuild finished in ${Date.now() - start}ms`);
|
|
379
|
-
}
|
|
380
|
-
function normalizePath(path, resolveFrom) {
|
|
381
|
-
return typeof path === "string" && isAbsolute(path) ? path : path instanceof URL ? fileURLToPath(path) : resolve(resolveFrom || ".", path || ".");
|
|
382
|
-
}
|
|
383
|
-
function readJSON(specifier) {
|
|
384
|
-
return import(specifier, { with: { type: "json" } }).then((r) => r.default);
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
//#endregion
|
|
388
|
-
export { build };
|