From f353d00f4db739afefd338ef8be76ecb225fc72e Mon Sep 17 00:00:00 2001 From: wyatex Date: Thu, 5 Jun 2025 18:11:57 +0800 Subject: [PATCH 1/6] fix: Resolve directory separator issue on Windows --- package.json | 1 + pnpm-lock.yaml | 42 +++++++++++++++++++++++++++++++++ src/build.ts | 2 +- src/builders/bundle.ts | 2 +- src/builders/plugins/shebang.ts | 2 +- src/builders/transform.ts | 2 +- src/utils.ts | 2 +- test/obuild.test.ts | 17 ++++++------- 8 files changed, 57 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index aca8cef..68dbdc5 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "oxc-minify": "^0.72.2", "oxc-parser": "^0.72.2", "oxc-transform": "^0.72.2", + "pathe": "^2.0.3", "pretty-bytes": "^7.0.0", "rolldown": "1.0.0-beta.10", "rolldown-plugin-dts": "^0.13.7", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1d568b7..f459dbb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,6 +32,9 @@ importers: oxc-transform: specifier: ^0.72.2 version: 0.72.2 + pathe: + specifier: ^2.0.3 + version: 2.0.3 pretty-bytes: specifier: ^7.0.0 version: 7.0.0 @@ -409,36 +412,42 @@ packages: engines: {node: '>=14.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] '@oxc-minify/binding-linux-arm64-musl@0.72.2': resolution: {integrity: sha512-/j2oDxLknFBGhL/WilR/fJxap4M3e4Y1NgFPZxB7wsifEA7pmWIk7SkfpQCGRcNFzIa/48p5ZOA5gVg8rQ1SxQ==} engines: {node: '>=14.0.0'} cpu: [arm64] os: [linux] + libc: [musl] '@oxc-minify/binding-linux-riscv64-gnu@0.72.2': resolution: {integrity: sha512-rJFIXy6B93Oi4wzG5BrTemnsEZm2f1+cIPPRprzmEqr/Tah/qpp2kbaKUAoZ19dhufMnwAXXsF7UUqBCaQZ6gQ==} engines: {node: '>=14.0.0'} cpu: [riscv64] os: [linux] + libc: [glibc] '@oxc-minify/binding-linux-s390x-gnu@0.72.2': resolution: {integrity: sha512-2lUdMK11jO1cOcE848k16Hn9zMmpXnkwEsNpNQYhNtNlCDpPGYHt50GUvNrgv8Ux3eGcrCiNz5GAZZr+STbqyg==} engines: {node: '>=14.0.0'} cpu: [s390x] os: [linux] + libc: [glibc] '@oxc-minify/binding-linux-x64-gnu@0.72.2': resolution: {integrity: sha512-0Jrk3+0yxHajAFeCTaPHZ+zgxM3UZ8ZDgHFplAAHbQEqCuVPvQDNWBSMwO/bL8s/WONU1SGmhYxX7u1wGo/W6g==} engines: {node: '>=14.0.0'} cpu: [x64] os: [linux] + libc: [glibc] '@oxc-minify/binding-linux-x64-musl@0.72.2': resolution: {integrity: sha512-IqBiYi4R0jgNIWsGmgSQH8UW4TfXyAIT5W7dqnbZUPzIxzmb0VE1PVfhXPBccWyQUthxQlXgNc4zPIJmZG5LUA==} engines: {node: '>=14.0.0'} cpu: [x64] os: [linux] + libc: [musl] '@oxc-minify/binding-wasm32-wasi@0.72.2': resolution: {integrity: sha512-0ljl4B1+rAGCT5VRDbl9sm9FSF3XLadm9mdOU4EEgIDeRmK4IAzy1tB6QvSjnHtAUBD3dMEeF19/KBUS2Jzw5Q==} @@ -492,36 +501,42 @@ packages: engines: {node: '>=14.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] '@oxc-parser/binding-linux-arm64-musl@0.72.2': resolution: {integrity: sha512-mjwp4B3Yqj6Fo1KIRDRQyWkkJ7ydijReo0UQ6wdDdvQt9v3Sjw4VMlkYAc+hAEo7EX6muPwy2WtjMOtzedlzvA==} engines: {node: '>=14.0.0'} cpu: [arm64] os: [linux] + libc: [musl] '@oxc-parser/binding-linux-riscv64-gnu@0.72.2': resolution: {integrity: sha512-xjbjHPIjHzew583ly/2uIR8u4YSX2fWuhJitkvqzKGBd403/wV9fOGKbgbgeFZxcqIqQlZI6WcxTU+fcmMQ5HQ==} engines: {node: '>=14.0.0'} cpu: [riscv64] os: [linux] + libc: [glibc] '@oxc-parser/binding-linux-s390x-gnu@0.72.2': resolution: {integrity: sha512-ohZ70sS4koTbQ+LqMDWK2SOMFmhPzv9DNgC82X/PqjuvzAIDPn13Rt+rUmpMwew/Xtbpe/vPHsEdkxNqsq2XMg==} engines: {node: '>=14.0.0'} cpu: [s390x] os: [linux] + libc: [glibc] '@oxc-parser/binding-linux-x64-gnu@0.72.2': resolution: {integrity: sha512-9lYwvYstsnRHivGpH2k8qZQPf0E/FhMr1YuZPxLqyUYzGmdiEpcU7wQbgU6dNIepLLdu/VLmYqmYYaBh86GzWQ==} engines: {node: '>=14.0.0'} cpu: [x64] os: [linux] + libc: [glibc] '@oxc-parser/binding-linux-x64-musl@0.72.2': resolution: {integrity: sha512-4KYID/lCsXR/8m8lgynZzXQIL5zidi5J9cW1nkje2deAf+YmmG6kKm1Vhvq8DaXOkdfGmtDbYd9luYplxncWBg==} engines: {node: '>=14.0.0'} cpu: [x64] os: [linux] + libc: [musl] '@oxc-parser/binding-wasm32-wasi@0.72.2': resolution: {integrity: sha512-ng+OJ+4MOsdJVt2a7VpcornkYFLu9Faos77UXogJg+HM5NnH1L+8rraGvxzJWf8OhYkwlQCIvLuqkncfPap4RA==} @@ -585,36 +600,42 @@ packages: engines: {node: '>=14.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] '@oxc-transform/binding-linux-arm64-musl@0.72.2': resolution: {integrity: sha512-C72PJTUsaeq5TmB1s/piLn3fnCn0ZJbiPa4UkBeDv+HncBWOpEsAGkF4Ddipw4kd0dKrTJDIXRmrns5VQyY0zw==} engines: {node: '>=14.0.0'} cpu: [arm64] os: [linux] + libc: [musl] '@oxc-transform/binding-linux-riscv64-gnu@0.72.2': resolution: {integrity: sha512-0Ph7Rx+96O2tapleHf5uwrUKR1wYZapik5J+dtNi1miWNWa2lGtRU8BBUSnsHGiaG5aq0rQ3WlruqiuUakMx4w==} engines: {node: '>=14.0.0'} cpu: [riscv64] os: [linux] + libc: [glibc] '@oxc-transform/binding-linux-s390x-gnu@0.72.2': resolution: {integrity: sha512-MaqBEnkrCvgH+Fsg2d4H2QhTCubgkohy0mr+HVngZfiGvP/pOkbeanWUTeiRaOthMKOuHBGkHor61Hiw+woK2Q==} engines: {node: '>=14.0.0'} cpu: [s390x] os: [linux] + libc: [glibc] '@oxc-transform/binding-linux-x64-gnu@0.72.2': resolution: {integrity: sha512-o0BO169Id5/35CFUiogs24/Pc/PT0xh3VEtV1//tT56im2w7F6TlMPcswEru7K7d+Qhzkl70xktWCAOV64D72g==} engines: {node: '>=14.0.0'} cpu: [x64] os: [linux] + libc: [glibc] '@oxc-transform/binding-linux-x64-musl@0.72.2': resolution: {integrity: sha512-wVcZS5cneUdNaB8dR+bD2pWTGmOK45rIJiW1fx5zZwh8vcKwQNgXw8GK+G7oxLqyyh6MJ1FBFvYPRJ9bKZJmlg==} engines: {node: '>=14.0.0'} cpu: [x64] os: [linux] + libc: [musl] '@oxc-transform/binding-wasm32-wasi@0.72.2': resolution: {integrity: sha512-D+gMV9ZoVdvIBI7swiFbPocxRR8rqui3weNtNcSOfLKGYkDEuHA2rBk8z03mnkpR/PlCZD+4a3HcpWyctn1J+w==} @@ -662,36 +683,42 @@ packages: engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] + libc: [glibc] '@parcel/watcher-linux-arm-musl@2.5.1': resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==} engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] + libc: [musl] '@parcel/watcher-linux-arm64-glibc@2.5.1': resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] '@parcel/watcher-linux-arm64-musl@2.5.1': resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] + libc: [musl] '@parcel/watcher-linux-x64-glibc@2.5.1': resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] + libc: [glibc] '@parcel/watcher-linux-x64-musl@2.5.1': resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] + libc: [musl] '@parcel/watcher-win32-arm64@2.5.1': resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==} @@ -743,21 +770,25 @@ packages: resolution: {integrity: sha512-yVtGQ8kNjfC7ex0M4XRJz3wPF4aevoX52B9oIw/7l/gBQQcFxwvLnWtUE52XD/LPII2OncFsoQ2cyh/BZi4kZA==} cpu: [arm64] os: [linux] + libc: [glibc] '@rolldown/binding-linux-arm64-musl@1.0.0-beta.10': resolution: {integrity: sha512-uvmzUIYmIlHTn9pboT3iM+Az5TxO2CX/OKtXFPqb//YhM917Yij4LYX9HlSqEmKWJivDr0OVGeXsFOhF0L+mNg==} cpu: [arm64] os: [linux] + libc: [musl] '@rolldown/binding-linux-x64-gnu@1.0.0-beta.10': resolution: {integrity: sha512-BtfoQK4lWJmARDQk4m025ODfaiHmokpJtTkV3lblLOg6ZuBp/NTuYmoxwY+qbDm3u0x1mU4Iq1Pp29ulv9wRGQ==} cpu: [x64] os: [linux] + libc: [glibc] '@rolldown/binding-linux-x64-musl@1.0.0-beta.10': resolution: {integrity: sha512-UxXIgyumFb9D/KMsB6wIOK+zqkpf0d3Seh2UcPNVlCWchs09N7a9ZrZ8cjv40/zmVI/5HfzZcbWIatW+8Ntb5w==} cpu: [x64] os: [linux] + libc: [musl] '@rolldown/binding-wasm32-wasi@1.0.0-beta.10': resolution: {integrity: sha512-EUmJIQDRZaN99VpI3q4SuMHX+BXFbJMutIKD2tf3uqcRcNbICzRqSZnM1Wc99vVpu0RkYNnr85Pi1fb8dlc+kA==} @@ -816,56 +847,67 @@ packages: resolution: {integrity: sha512-wC53ZNDgt0pqx5xCAgNunkTzFE8GTgdZ9EwYGVcg+jEjJdZGtq9xPjDnFgfFozQI/Xm1mh+D9YlYtl+ueswNEg==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.41.1': resolution: {integrity: sha512-jwKCca1gbZkZLhLRtsrka5N8sFAaxrGz/7wRJ8Wwvq3jug7toO21vWlViihG85ei7uJTpzbXZRcORotE+xyrLA==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.41.1': resolution: {integrity: sha512-g0UBcNknsmmNQ8V2d/zD2P7WWfJKU0F1nu0k5pW4rvdb+BIqMm8ToluW/eeRmxCared5dD76lS04uL4UaNgpNA==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.41.1': resolution: {integrity: sha512-XZpeGB5TKEZWzIrj7sXr+BEaSgo/ma/kCgrZgL0oo5qdB1JlTzIYQKel/RmhT6vMAvOdM2teYlAaOGJpJ9lahg==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loongarch64-gnu@4.41.1': resolution: {integrity: sha512-bkCfDJ4qzWfFRCNt5RVV4DOw6KEgFTUZi2r2RuYhGWC8WhCA8lCAJhDeAmrM/fdiAH54m0mA0Vk2FGRPyzI+tw==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-powerpc64le-gnu@4.41.1': resolution: {integrity: sha512-3mr3Xm+gvMX+/8EKogIZSIEF0WUu0HL9di+YWlJpO8CQBnoLAEL/roTCxuLncEdgcfJcvA4UMOf+2dnjl4Ut1A==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.41.1': resolution: {integrity: sha512-3rwCIh6MQ1LGrvKJitQjZFuQnT2wxfU+ivhNBzmxXTXPllewOF7JR1s2vMX/tWtUYFgphygxjqMl76q4aMotGw==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.41.1': resolution: {integrity: sha512-LdIUOb3gvfmpkgFZuccNa2uYiqtgZAz3PTzjuM5bH3nvuy9ty6RGc/Q0+HDFrHrizJGVpjnTZ1yS5TNNjFlklw==} cpu: [riscv64] os: [linux] + libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.41.1': resolution: {integrity: sha512-oIE6M8WC9ma6xYqjvPhzZYk6NbobIURvP/lEbh7FWplcMO6gn7MM2yHKA1eC/GvYwzNKK/1LYgqzdkZ8YFxR8g==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.41.1': resolution: {integrity: sha512-cWBOvayNvA+SyeQMp79BHPK8ws6sHSsYnK5zDcsC3Hsxr1dgTABKjMnMslPq1DvZIp6uO7kIWhiGwaTdR4Og9A==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.41.1': resolution: {integrity: sha512-y5CbN44M+pUCdGDlZFzGGBSKCA4A/J2ZH4edTYSSxFg7ce1Xt3GtydbVKWLlzL+INfFIZAEg1ZV6hh9+QQf9YQ==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-win32-arm64-msvc@4.41.1': resolution: {integrity: sha512-lZkCxIrjlJlMt1dLO/FbpZbzt6J/A8p4DnqzSa4PWqPEUUUnzXLeki/iyPLfV0BmHItlYgHUqJe+3KiyydmiNQ==} diff --git a/src/build.ts b/src/build.ts index 9822b43..2d7364c 100644 --- a/src/build.ts +++ b/src/build.ts @@ -6,7 +6,7 @@ import type { } from "./types.ts"; import { fileURLToPath } from "node:url"; -import { isAbsolute, join, resolve } from "node:path"; +import { isAbsolute, join, resolve } from "pathe"; import { rm } from "node:fs/promises"; import { consola } from "consola"; import { colors as c } from "consola/utils"; diff --git a/src/builders/bundle.ts b/src/builders/bundle.ts index d01bebd..d53ce0e 100644 --- a/src/builders/bundle.ts +++ b/src/builders/bundle.ts @@ -1,6 +1,6 @@ import { builtinModules } from "node:module"; import { mkdir, readFile, writeFile } from "node:fs/promises"; -import { dirname, relative, join, basename, extname, resolve } from "node:path"; +import { dirname, relative, join, basename, extname, resolve } from "pathe"; import { consola } from "consola"; import { colors as c } from "consola/utils"; import { rolldown } from "rolldown"; diff --git a/src/builders/plugins/shebang.ts b/src/builders/plugins/shebang.ts index 7106fb9..806ed19 100644 --- a/src/builders/plugins/shebang.ts +++ b/src/builders/plugins/shebang.ts @@ -1,7 +1,7 @@ // Ported from https://github.com/unjs/unbuild/blob/main/src/builders/rollup/plugins/shebang.ts import { promises as fsp } from "node:fs"; -import { resolve } from "node:path"; +import { resolve } from "pathe"; import type { Plugin } from "rolldown"; export const SHEBANG_RE: RegExp = /^#![^\n]*/; diff --git a/src/builders/transform.ts b/src/builders/transform.ts index b4b0112..73f73bf 100644 --- a/src/builders/transform.ts +++ b/src/builders/transform.ts @@ -1,7 +1,7 @@ import type { BuildContext, TransformEntry } from "../types.ts"; import { pathToFileURL } from "node:url"; -import { dirname, extname, join, relative } from "node:path"; +import { dirname, extname, join, relative } from "pathe"; import { mkdir, readFile, symlink, writeFile } from "node:fs/promises"; import { consola } from "consola"; import { colors as c } from "consola/utils"; diff --git a/src/utils.ts b/src/utils.ts index 1423797..237d486 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,5 +1,5 @@ import { readdirSync, statSync } from "node:fs"; -import { join, resolve } from "node:path"; +import { join, resolve } from "pathe"; import { type Plugin, rolldown } from "rolldown"; import { minify } from "oxc-minify"; diff --git a/test/obuild.test.ts b/test/obuild.test.ts index 692c872..72bc774 100644 --- a/test/obuild.test.ts +++ b/test/obuild.test.ts @@ -2,9 +2,10 @@ import { describe, test, expect, beforeAll } from "vitest"; import { build } from "../src/build.ts"; import { readdir, readFile, rm, stat } from "node:fs/promises"; +import {resolve} from "pathe"; -const fixtureDir = new URL("fixture/", import.meta.url); -const distDir = new URL("dist/", fixtureDir); +const fixtureDir = resolve(__dirname, "fixture/"); +const distDir = resolve(fixtureDir, "dist/"); describe("obuild", () => { beforeAll(async () => { @@ -24,7 +25,7 @@ describe("obuild", () => { test("dist files match expected", async () => { const distFiles = await readdir(distDir, { recursive: true }).then((r) => - r.sort(), + r.sort().map((p) => p.replace(/\\/g, "/")), ); expect(distFiles).toMatchInlineSnapshot(` [ @@ -47,26 +48,26 @@ describe("obuild", () => { }); test("validate dist entries", async () => { - const distIndex = await import(new URL("index.mjs", distDir).href); + const distIndex = await import(resolve(distDir, "index.mjs")); expect(distIndex.test).instanceOf(Function); - const distRuntimeIndex = await import(new URL("index.mjs", distDir).href); + const distRuntimeIndex = await import(resolve(distDir, "index.mjs")); expect(distRuntimeIndex.test).instanceOf(Function); - const distUtils = await import(new URL("utils.mjs", distDir).href); + const distUtils = await import(resolve(distDir, "utils.mjs")); expect(distUtils.test).instanceOf(Function); }); test("runtime .dts files use .mjs extension", async () => { const runtimeIndexMts = await readFile( - new URL("runtime/index.d.mts", distDir), + resolve(distDir, "runtime/index.d.mts"), "utf8", ); expect(runtimeIndexMts).contain("./test.mjs"); }); test("cli shebang is executable", async () => { - const cliPath = new URL("cli.mjs", distDir); + const cliPath = resolve(distDir, "cli.mjs"); const stats = await stat(cliPath); expect(stats.mode & 0o111).toBe(0o111); // Check if executable }); From 7a413ef2a98f83827bdd8060eba6bb0906265a9a Mon Sep 17 00:00:00 2001 From: wyatex Date: Thu, 12 Jun 2025 16:53:16 +0800 Subject: [PATCH 2/6] feat: infer entries from package.json --- build.config.ts | 10 ---- package.json | 2 + pnpm-lock.yaml | 45 +++-------------- src/auto.ts | 125 ++++++++++++++++++++++++++++++++++++++++++++++++ src/cli.ts | 28 +++++++++-- src/types.ts | 7 +++ src/utils.ts | 84 +++++++++++++++++++++++++++++++- 7 files changed, 247 insertions(+), 54 deletions(-) delete mode 100644 build.config.ts create mode 100644 src/auto.ts diff --git a/build.config.ts b/build.config.ts deleted file mode 100644 index 37a9735..0000000 --- a/build.config.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { defineBuildConfig } from "./src/config.ts"; - -export default defineBuildConfig({ - entries: [ - { - type: "bundle", - input: ["./src/index.ts", "./src/cli.ts", "./src/config.ts"], - }, - ], -}); diff --git a/package.json b/package.json index 68dbdc5..504c977 100644 --- a/package.json +++ b/package.json @@ -32,11 +32,13 @@ "consola": "^3.4.2", "defu": "^6.1.4", "exsolve": "^1.0.5", + "jiti": "^2.4.2", "magic-string": "^0.30.17", "oxc-minify": "^0.72.2", "oxc-parser": "^0.72.2", "oxc-transform": "^0.72.2", "pathe": "^2.0.3", + "pkg-types": "^2.1.0", "pretty-bytes": "^7.0.0", "rolldown": "1.0.0-beta.10", "rolldown-plugin-dts": "^0.13.7", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f459dbb..06bb430 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,9 @@ importers: exsolve: specifier: ^1.0.5 version: 1.0.5 + jiti: + specifier: ^2.4.2 + version: 2.4.2 magic-string: specifier: ^0.30.17 version: 0.30.17 @@ -35,6 +38,9 @@ importers: pathe: specifier: ^2.0.3 version: 2.0.3 + pkg-types: + specifier: ^2.1.0 + version: 2.1.0 pretty-bytes: specifier: ^7.0.0 version: 7.0.0 @@ -412,42 +418,36 @@ packages: engines: {node: '>=14.0.0'} cpu: [arm64] os: [linux] - libc: [glibc] '@oxc-minify/binding-linux-arm64-musl@0.72.2': resolution: {integrity: sha512-/j2oDxLknFBGhL/WilR/fJxap4M3e4Y1NgFPZxB7wsifEA7pmWIk7SkfpQCGRcNFzIa/48p5ZOA5gVg8rQ1SxQ==} engines: {node: '>=14.0.0'} cpu: [arm64] os: [linux] - libc: [musl] '@oxc-minify/binding-linux-riscv64-gnu@0.72.2': resolution: {integrity: sha512-rJFIXy6B93Oi4wzG5BrTemnsEZm2f1+cIPPRprzmEqr/Tah/qpp2kbaKUAoZ19dhufMnwAXXsF7UUqBCaQZ6gQ==} engines: {node: '>=14.0.0'} cpu: [riscv64] os: [linux] - libc: [glibc] '@oxc-minify/binding-linux-s390x-gnu@0.72.2': resolution: {integrity: sha512-2lUdMK11jO1cOcE848k16Hn9zMmpXnkwEsNpNQYhNtNlCDpPGYHt50GUvNrgv8Ux3eGcrCiNz5GAZZr+STbqyg==} engines: {node: '>=14.0.0'} cpu: [s390x] os: [linux] - libc: [glibc] '@oxc-minify/binding-linux-x64-gnu@0.72.2': resolution: {integrity: sha512-0Jrk3+0yxHajAFeCTaPHZ+zgxM3UZ8ZDgHFplAAHbQEqCuVPvQDNWBSMwO/bL8s/WONU1SGmhYxX7u1wGo/W6g==} engines: {node: '>=14.0.0'} cpu: [x64] os: [linux] - libc: [glibc] '@oxc-minify/binding-linux-x64-musl@0.72.2': resolution: {integrity: sha512-IqBiYi4R0jgNIWsGmgSQH8UW4TfXyAIT5W7dqnbZUPzIxzmb0VE1PVfhXPBccWyQUthxQlXgNc4zPIJmZG5LUA==} engines: {node: '>=14.0.0'} cpu: [x64] os: [linux] - libc: [musl] '@oxc-minify/binding-wasm32-wasi@0.72.2': resolution: {integrity: sha512-0ljl4B1+rAGCT5VRDbl9sm9FSF3XLadm9mdOU4EEgIDeRmK4IAzy1tB6QvSjnHtAUBD3dMEeF19/KBUS2Jzw5Q==} @@ -501,42 +501,36 @@ packages: engines: {node: '>=14.0.0'} cpu: [arm64] os: [linux] - libc: [glibc] '@oxc-parser/binding-linux-arm64-musl@0.72.2': resolution: {integrity: sha512-mjwp4B3Yqj6Fo1KIRDRQyWkkJ7ydijReo0UQ6wdDdvQt9v3Sjw4VMlkYAc+hAEo7EX6muPwy2WtjMOtzedlzvA==} engines: {node: '>=14.0.0'} cpu: [arm64] os: [linux] - libc: [musl] '@oxc-parser/binding-linux-riscv64-gnu@0.72.2': resolution: {integrity: sha512-xjbjHPIjHzew583ly/2uIR8u4YSX2fWuhJitkvqzKGBd403/wV9fOGKbgbgeFZxcqIqQlZI6WcxTU+fcmMQ5HQ==} engines: {node: '>=14.0.0'} cpu: [riscv64] os: [linux] - libc: [glibc] '@oxc-parser/binding-linux-s390x-gnu@0.72.2': resolution: {integrity: sha512-ohZ70sS4koTbQ+LqMDWK2SOMFmhPzv9DNgC82X/PqjuvzAIDPn13Rt+rUmpMwew/Xtbpe/vPHsEdkxNqsq2XMg==} engines: {node: '>=14.0.0'} cpu: [s390x] os: [linux] - libc: [glibc] '@oxc-parser/binding-linux-x64-gnu@0.72.2': resolution: {integrity: sha512-9lYwvYstsnRHivGpH2k8qZQPf0E/FhMr1YuZPxLqyUYzGmdiEpcU7wQbgU6dNIepLLdu/VLmYqmYYaBh86GzWQ==} engines: {node: '>=14.0.0'} cpu: [x64] os: [linux] - libc: [glibc] '@oxc-parser/binding-linux-x64-musl@0.72.2': resolution: {integrity: sha512-4KYID/lCsXR/8m8lgynZzXQIL5zidi5J9cW1nkje2deAf+YmmG6kKm1Vhvq8DaXOkdfGmtDbYd9luYplxncWBg==} engines: {node: '>=14.0.0'} cpu: [x64] os: [linux] - libc: [musl] '@oxc-parser/binding-wasm32-wasi@0.72.2': resolution: {integrity: sha512-ng+OJ+4MOsdJVt2a7VpcornkYFLu9Faos77UXogJg+HM5NnH1L+8rraGvxzJWf8OhYkwlQCIvLuqkncfPap4RA==} @@ -600,42 +594,36 @@ packages: engines: {node: '>=14.0.0'} cpu: [arm64] os: [linux] - libc: [glibc] '@oxc-transform/binding-linux-arm64-musl@0.72.2': resolution: {integrity: sha512-C72PJTUsaeq5TmB1s/piLn3fnCn0ZJbiPa4UkBeDv+HncBWOpEsAGkF4Ddipw4kd0dKrTJDIXRmrns5VQyY0zw==} engines: {node: '>=14.0.0'} cpu: [arm64] os: [linux] - libc: [musl] '@oxc-transform/binding-linux-riscv64-gnu@0.72.2': resolution: {integrity: sha512-0Ph7Rx+96O2tapleHf5uwrUKR1wYZapik5J+dtNi1miWNWa2lGtRU8BBUSnsHGiaG5aq0rQ3WlruqiuUakMx4w==} engines: {node: '>=14.0.0'} cpu: [riscv64] os: [linux] - libc: [glibc] '@oxc-transform/binding-linux-s390x-gnu@0.72.2': resolution: {integrity: sha512-MaqBEnkrCvgH+Fsg2d4H2QhTCubgkohy0mr+HVngZfiGvP/pOkbeanWUTeiRaOthMKOuHBGkHor61Hiw+woK2Q==} engines: {node: '>=14.0.0'} cpu: [s390x] os: [linux] - libc: [glibc] '@oxc-transform/binding-linux-x64-gnu@0.72.2': resolution: {integrity: sha512-o0BO169Id5/35CFUiogs24/Pc/PT0xh3VEtV1//tT56im2w7F6TlMPcswEru7K7d+Qhzkl70xktWCAOV64D72g==} engines: {node: '>=14.0.0'} cpu: [x64] os: [linux] - libc: [glibc] '@oxc-transform/binding-linux-x64-musl@0.72.2': resolution: {integrity: sha512-wVcZS5cneUdNaB8dR+bD2pWTGmOK45rIJiW1fx5zZwh8vcKwQNgXw8GK+G7oxLqyyh6MJ1FBFvYPRJ9bKZJmlg==} engines: {node: '>=14.0.0'} cpu: [x64] os: [linux] - libc: [musl] '@oxc-transform/binding-wasm32-wasi@0.72.2': resolution: {integrity: sha512-D+gMV9ZoVdvIBI7swiFbPocxRR8rqui3weNtNcSOfLKGYkDEuHA2rBk8z03mnkpR/PlCZD+4a3HcpWyctn1J+w==} @@ -683,42 +671,36 @@ packages: engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] - libc: [glibc] '@parcel/watcher-linux-arm-musl@2.5.1': resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==} engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] - libc: [musl] '@parcel/watcher-linux-arm64-glibc@2.5.1': resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] - libc: [glibc] '@parcel/watcher-linux-arm64-musl@2.5.1': resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] - libc: [musl] '@parcel/watcher-linux-x64-glibc@2.5.1': resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] - libc: [glibc] '@parcel/watcher-linux-x64-musl@2.5.1': resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] - libc: [musl] '@parcel/watcher-win32-arm64@2.5.1': resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==} @@ -770,25 +752,21 @@ packages: resolution: {integrity: sha512-yVtGQ8kNjfC7ex0M4XRJz3wPF4aevoX52B9oIw/7l/gBQQcFxwvLnWtUE52XD/LPII2OncFsoQ2cyh/BZi4kZA==} cpu: [arm64] os: [linux] - libc: [glibc] '@rolldown/binding-linux-arm64-musl@1.0.0-beta.10': resolution: {integrity: sha512-uvmzUIYmIlHTn9pboT3iM+Az5TxO2CX/OKtXFPqb//YhM917Yij4LYX9HlSqEmKWJivDr0OVGeXsFOhF0L+mNg==} cpu: [arm64] os: [linux] - libc: [musl] '@rolldown/binding-linux-x64-gnu@1.0.0-beta.10': resolution: {integrity: sha512-BtfoQK4lWJmARDQk4m025ODfaiHmokpJtTkV3lblLOg6ZuBp/NTuYmoxwY+qbDm3u0x1mU4Iq1Pp29ulv9wRGQ==} cpu: [x64] os: [linux] - libc: [glibc] '@rolldown/binding-linux-x64-musl@1.0.0-beta.10': resolution: {integrity: sha512-UxXIgyumFb9D/KMsB6wIOK+zqkpf0d3Seh2UcPNVlCWchs09N7a9ZrZ8cjv40/zmVI/5HfzZcbWIatW+8Ntb5w==} cpu: [x64] os: [linux] - libc: [musl] '@rolldown/binding-wasm32-wasi@1.0.0-beta.10': resolution: {integrity: sha512-EUmJIQDRZaN99VpI3q4SuMHX+BXFbJMutIKD2tf3uqcRcNbICzRqSZnM1Wc99vVpu0RkYNnr85Pi1fb8dlc+kA==} @@ -847,67 +825,56 @@ packages: resolution: {integrity: sha512-wC53ZNDgt0pqx5xCAgNunkTzFE8GTgdZ9EwYGVcg+jEjJdZGtq9xPjDnFgfFozQI/Xm1mh+D9YlYtl+ueswNEg==} cpu: [arm] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.41.1': resolution: {integrity: sha512-jwKCca1gbZkZLhLRtsrka5N8sFAaxrGz/7wRJ8Wwvq3jug7toO21vWlViihG85ei7uJTpzbXZRcORotE+xyrLA==} cpu: [arm] os: [linux] - libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.41.1': resolution: {integrity: sha512-g0UBcNknsmmNQ8V2d/zD2P7WWfJKU0F1nu0k5pW4rvdb+BIqMm8ToluW/eeRmxCared5dD76lS04uL4UaNgpNA==} cpu: [arm64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.41.1': resolution: {integrity: sha512-XZpeGB5TKEZWzIrj7sXr+BEaSgo/ma/kCgrZgL0oo5qdB1JlTzIYQKel/RmhT6vMAvOdM2teYlAaOGJpJ9lahg==} cpu: [arm64] os: [linux] - libc: [musl] '@rollup/rollup-linux-loongarch64-gnu@4.41.1': resolution: {integrity: sha512-bkCfDJ4qzWfFRCNt5RVV4DOw6KEgFTUZi2r2RuYhGWC8WhCA8lCAJhDeAmrM/fdiAH54m0mA0Vk2FGRPyzI+tw==} cpu: [loong64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-powerpc64le-gnu@4.41.1': resolution: {integrity: sha512-3mr3Xm+gvMX+/8EKogIZSIEF0WUu0HL9di+YWlJpO8CQBnoLAEL/roTCxuLncEdgcfJcvA4UMOf+2dnjl4Ut1A==} cpu: [ppc64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.41.1': resolution: {integrity: sha512-3rwCIh6MQ1LGrvKJitQjZFuQnT2wxfU+ivhNBzmxXTXPllewOF7JR1s2vMX/tWtUYFgphygxjqMl76q4aMotGw==} cpu: [riscv64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.41.1': resolution: {integrity: sha512-LdIUOb3gvfmpkgFZuccNa2uYiqtgZAz3PTzjuM5bH3nvuy9ty6RGc/Q0+HDFrHrizJGVpjnTZ1yS5TNNjFlklw==} cpu: [riscv64] os: [linux] - libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.41.1': resolution: {integrity: sha512-oIE6M8WC9ma6xYqjvPhzZYk6NbobIURvP/lEbh7FWplcMO6gn7MM2yHKA1eC/GvYwzNKK/1LYgqzdkZ8YFxR8g==} cpu: [s390x] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.41.1': resolution: {integrity: sha512-cWBOvayNvA+SyeQMp79BHPK8ws6sHSsYnK5zDcsC3Hsxr1dgTABKjMnMslPq1DvZIp6uO7kIWhiGwaTdR4Og9A==} cpu: [x64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-musl@4.41.1': resolution: {integrity: sha512-y5CbN44M+pUCdGDlZFzGGBSKCA4A/J2ZH4edTYSSxFg7ce1Xt3GtydbVKWLlzL+INfFIZAEg1ZV6hh9+QQf9YQ==} cpu: [x64] os: [linux] - libc: [musl] '@rollup/rollup-win32-arm64-msvc@4.41.1': resolution: {integrity: sha512-lZkCxIrjlJlMt1dLO/FbpZbzt6J/A8p4DnqzSa4PWqPEUUUnzXLeki/iyPLfV0BmHItlYgHUqJe+3KiyydmiNQ==} diff --git a/src/auto.ts b/src/auto.ts new file mode 100644 index 0000000..07c948b --- /dev/null +++ b/src/auto.ts @@ -0,0 +1,125 @@ +import { existsSync } from "node:fs"; +import type { PackageJson } from "pkg-types"; +import type { BuildEntry } from "./types.ts"; +import { normalize, resolve } from "pathe"; +import { extractExportFilenames } from "./utils.ts"; + +type InferEntriesResult = { + entries: BuildEntry[]; + cjs?: boolean; + dts?: boolean; + warnings: string[]; +}; + +/** + * @param {PackageJson} pkg The contents of a package.json file to serve as the source for inferred entries. + * @param {string[]} sourceFiles A list of source files to use for inferring entries. + * @param {string | undefined} rootDir The root directory of the project. + */ +export function inferEntries( + pkg: PackageJson, + sourceFiles: string[], + rootDir?: string, +): InferEntriesResult { + const warnings = []; + + // Sort files so least-nested files are first + sourceFiles.sort((a, b) => a.split("/").length - b.split("/").length); + + // Come up with a list of all output files & their formats + const outputs = extractExportFilenames(pkg.exports); + + if (pkg.bin) { + const binaries = + typeof pkg.bin === "string" ? [pkg.bin] : Object.values(pkg.bin); + for (const file of binaries) { + outputs.push({ file }); + } + } + if (pkg.main) { + outputs.push({ file: pkg.main }); + } + if (pkg.module) { + outputs.push({ type: "esm", file: pkg.module }); + } + if (pkg.types || pkg.typings) { + outputs.push({ file: pkg.types || pkg.typings! }); + } + + // Try to detect output types + const isESMPkg = pkg.type === "module"; + for (const output of outputs.filter((o) => !o.type)) { + const isJS = output.file.endsWith(".js"); + if ((isESMPkg && isJS) || output.file.endsWith(".mjs")) { + output.type = "esm"; + } else if ((!isESMPkg && isJS) || output.file.endsWith(".cjs")) { + output.type = "cjs"; + } + } + + let cjs = false; + let dts = false; + + // Infer entries from package files + const entries: BuildEntry[] = []; + for (const output of outputs) { + // Supported output file extensions are `.d.ts`, `.cjs` and `.mjs` + // But we support any file extension here in case user has extended rolldown options + const outputSlug = output.file.replace( + /(\*[^/\\]*|\.d\.(m|c)?ts|\.\w+)$/, + "", + ); + const isDir = outputSlug.endsWith("/"); + + // Skip top level directory + if (isDir && ["./", "/"].includes(outputSlug)) { + continue; + } + + const possiblePaths = getEntrypointPaths(outputSlug); + // eslint-disable-next-line unicorn/no-array-reduce + const input = possiblePaths.reduce((source, d) => { + if (source) { + return source; + } + const SOURCE_RE = new RegExp( + `(?<=/|$)${d}${isDir ? "" : String.raw`\.\w+`}$`, + ); + return sourceFiles + .find((i) => SOURCE_RE.test(i)) + ?.replace(/(\.d\.(m|c)?ts|\.\w+)$/, ""); + }, undefined as any); + + if (!input) { + if (!existsSync(resolve(rootDir || ".", output.file))) { + warnings.push(`Could not find entrypoint for \`${output.file}\``); + } + continue; + } + + if (output.type === "cjs") { + cjs = true; + } + + const entry = + entries.find((i) => i.input === input) || + entries[entries.push({ input, type: 'bundle' }) - 1]; + + if (/\.d\.(m|c)?ts$/.test(output.file)) { + dts = true; + } + + if (isDir) { + entry.outDir = outputSlug; + } + } + + return { entries, cjs, dts, warnings }; +} + +export const getEntrypointPaths = (path: string): string[] => { + const segments = normalize(path).split("/"); + return segments + .map((_, index) => segments.slice(index).join("/")) + .filter(Boolean); +}; diff --git a/src/cli.ts b/src/cli.ts index efcb8a1..643f1a8 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -2,10 +2,15 @@ import { parseArgs } from "node:util"; import { consola } from "consola"; -import { build } from "./build.ts"; import { loadConfig } from "c12"; +import type { PackageJson } from "pkg-types"; +import { join } from "pathe"; +import { createJiti } from "jiti"; +import { build } from "./build.ts"; import type { BuildConfig, BuildEntry } from "./types.ts"; +import { inferEntries } from "./auto.ts"; +import { listRecursively } from "./utils.ts"; // https://nodejs.org/api/util.html#utilparseargsconfig const args = parseArgs({ @@ -51,8 +56,25 @@ if (args.values.stub) { } if (rawEntries.length === 0) { - consola.error("No build entries specified."); - process.exit(1); + // If no entries are specified, infer them from the package.json + const jiti = createJiti(process.cwd()); + const pkg: PackageJson = + ((await jiti.import("./package.json", { + try: true, + default: true, + })) as PackageJson) || ({} as PackageJson); + const sourceFiles = listRecursively(join(process.cwd(), "src")); + const res = inferEntries(pkg, sourceFiles, process.cwd()) + console.log(res); + entries.push({ + type: 'bundle', + input: res.entries.map((entry) => entry.input as string), + dts: res.dts, + }) + if (entries.length === 0) { + consola.error("No build entries specified."); + process.exit(1); + } } await build({ diff --git a/src/types.ts b/src/types.ts index 5952c21..2fb5d7a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -62,6 +62,13 @@ export type BundleEntry = _BuildEntry & { * Set to `false` to disable. */ dts?: boolean | DtsOptions; + + /** + * Generate a CommonJS bundle. + * + * Defaults to `false` if not provided. + */ + cjs?: boolean; }; export type TransformEntry = _BuildEntry & { diff --git a/src/utils.ts b/src/utils.ts index 237d486..94fb8a9 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,9 +1,10 @@ import { readdirSync, statSync } from "node:fs"; -import { join, resolve } from "pathe"; +import { gzipSync } from "node:zlib"; +import { join, resolve } from "pathe"; import { type Plugin, rolldown } from "rolldown"; import { minify } from "oxc-minify"; -import { gzipSync } from "node:zlib"; +import type { PackageJson } from "pkg-types"; export function fmtPath(path: string): string { return resolve(path).replace(process.cwd(), "."); @@ -112,3 +113,82 @@ export async function sideEffectSize( return Buffer.byteLength(output[0].code.trim()); } + +export function listRecursively(path: string): string[] { + const filenames = new Set(); + const walk = (path: string): void => { + const files = readdirSync(path); + for (const file of files) { + const fullPath = resolve(path, file); + if (statSync(fullPath).isDirectory()) { + filenames.add(fullPath + "/"); + walk(fullPath); + } else { + filenames.add(fullPath); + } + } + }; + walk(path); + return [...filenames]; +} + +export function inferExportType( + condition: string, + previousConditions: string[] = [], + filename = "", +): "esm" | "cjs" { + if (filename) { + if (filename.endsWith(".d.ts")) { + return "esm"; + } + if (filename.endsWith(".mjs")) { + return "esm"; + } + if (filename.endsWith(".cjs")) { + return "cjs"; + } + } + switch (condition) { + case "import": { + return "esm"; + } + case "require": { + return "cjs"; + } + default: { + if (previousConditions.length === 0) { + // TODO: Check against type:module for default + return "esm"; + } + const [newCondition, ...rest] = previousConditions; + return inferExportType(newCondition, rest, filename); + } + } +} + +export type OutputDescriptor = { file: string; type?: "esm" | "cjs" }; + +export function extractExportFilenames( + exports: PackageJson["exports"], + conditions: string[] = [], +): OutputDescriptor[] { + if (!exports) { + return []; + } + if (typeof exports === "string") { + return [{ file: exports, type: "esm" }]; + } + return ( + Object.entries(exports) + // Filter out .json subpaths such as package.json + .filter(([subpath]) => !subpath.endsWith(".json")) + .flatMap(([condition, exports]) => + typeof exports === "string" + ? { + file: exports, + type: inferExportType(condition, conditions, exports), + } + : extractExportFilenames(exports, [...conditions, condition]), + ) + ); +} From 9b75941456f8744cdbefcc2a1806b830b9c7afe0 Mon Sep 17 00:00:00 2001 From: wyatex Date: Fri, 13 Jun 2025 15:59:49 +0800 Subject: [PATCH 3/6] fix: read package.json fail on Windows --- src/build.ts | 9 ++++++++- src/cli.ts | 1 - src/types.ts | 3 ++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/build.ts b/src/build.ts index 2d7364c..b6b3f34 100644 --- a/src/build.ts +++ b/src/build.ts @@ -14,6 +14,8 @@ import { rolldownBuild } from "./builders/bundle.ts"; import { transformDir } from "./builders/transform.ts"; import { fmtPath, analyzeDir } from "./utils.ts"; import prettyBytes from "pretty-bytes"; +import { createJiti } from "jiti"; +import type { PackageJson } from "pkg-types"; /** * Build dist/ from src/ @@ -22,7 +24,12 @@ export async function build(config: BuildConfig): Promise { const start = Date.now(); const pkgDir = normalizePath(config.cwd); - const pkg = await readJSON(join(pkgDir, "package.json")).catch(() => ({})); + const jiti = createJiti(pkgDir); + const pkg: PackageJson = + ((await jiti.import("./package.json", { + try: true, + default: true, + })) as PackageJson) || ({} as PackageJson); const ctx: BuildContext = { pkg, pkgDir }; consola.log( diff --git a/src/cli.ts b/src/cli.ts index 643f1a8..f329345 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -65,7 +65,6 @@ if (rawEntries.length === 0) { })) as PackageJson) || ({} as PackageJson); const sourceFiles = listRecursively(join(process.cwd(), "src")); const res = inferEntries(pkg, sourceFiles, process.cwd()) - console.log(res); entries.push({ type: 'bundle', input: res.entries.map((entry) => entry.input as string), diff --git a/src/types.ts b/src/types.ts index 2fb5d7a..9d78d5a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -10,10 +10,11 @@ import type { Options as DtsOptions } from "rolldown-plugin-dts"; import type { TransformOptions } from "oxc-transform"; import type { MinifyOptions as OXCMinifyOptions } from "oxc-minify"; import type { ResolveOptions } from "exsolve"; +import type { PackageJson } from "pkg-types"; export interface BuildContext { pkgDir: string; - pkg: { name: string } & Record; + pkg: PackageJson; } export type _BuildEntry = { From 4f67b65a5bf11f58f549db6c1bda5466b8b9928a Mon Sep 17 00:00:00 2001 From: wyatex Date: Fri, 13 Jun 2025 17:00:50 +0800 Subject: [PATCH 4/6] feat: support bundle cjs format --- eslint.config.mjs | 1 + src/builders/bundle.ts | 68 +++++++++++++++++++++-- src/cli.ts | 120 +++++++++++++++++++++++++---------------- 3 files changed, 139 insertions(+), 50 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 8743d40..92e00d7 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -7,6 +7,7 @@ export default unjs({ rules: { "unicorn/no-null": "off", "unicorn/no-nested-ternary": "off", + "unicorn/prefer-top-level-await": "off", }, markdown: { rules: { diff --git a/src/builders/bundle.ts b/src/builders/bundle.ts index d53ce0e..15c15ef 100644 --- a/src/builders/bundle.ts +++ b/src/builders/bundle.ts @@ -81,10 +81,6 @@ export async function rolldownBuild( ], } satisfies InputOptions); - if (entry.dts !== false) { - rolldownConfig.plugins.push(...dts({ ...(entry.dts as DtsOptions) })); - } - await hooks.rolldownConfig?.(rolldownConfig, ctx); const res = await rolldown(rolldownConfig); @@ -102,6 +98,55 @@ export async function rolldownBuild( const { output } = await res.write(outConfig); + if (entry.dts !== false) { + // Bundle d.ts and d.mts files + rolldownConfig.plugins.push(...dts({ ...(entry.dts as DtsOptions), emitDtsOnly: true })); + await res.write({ + ...outConfig, + entryFileNames: "[name].js", + chunkFileNames: "_chunks/[name]-[hash].js", + }); + await res.write({ + ...outConfig, + entryFileNames: "[name].mjs", + chunkFileNames: "_chunks/[name]-[hash].mjs", + }); + } + + let cjsOutput + if (entry.cjs) { + // Bundle cjs files + const cjsRolldownConfig = { + ...rolldownConfig, + plugins: [shebangPlugin()] as Plugin[], + } + + const res = await rolldown(cjsRolldownConfig); + + const outConfig: OutputOptions = { + dir: outDir, + entryFileNames: "[name].cjs", + chunkFileNames: "_chunks/[name]-[hash].cjs", + minify: entry.minify, + format: 'cjs', + }; + + await hooks.rolldownOutput?.(outConfig, res, ctx); + + cjsOutput = (await res.write(outConfig)).output + + if (entry.dts !== false) { + // Bundle d.cts files + cjsRolldownConfig.plugins.push(...dts({ ...(entry.dts as DtsOptions), emitDtsOnly: true })); + await res.write({ + ...outConfig, + format: 'esm', + entryFileNames: "[name].cjs", + chunkFileNames: "_chunks/[name]-[hash].cjs", + }); + } + } + await res.close(); const outputEntries: { @@ -152,6 +197,21 @@ export async function rolldownBuild( }); } + if (cjsOutput) { + for (const chunk of cjsOutput) { + if (chunk.type !== "chunk" || !chunk.isEntry) continue; + if (chunk.fileName.endsWith("ts")) continue; + + outputEntries.push({ + name: chunk.fileName, + exports: chunk.exports, + deps: resolveDeps(chunk), + ...(await distSize(outDir, chunk.fileName)), + sideEffectSize: await sideEffectSize(outDir, chunk.fileName), + }); + } + } + consola.log( `\n${outputEntries .map((o) => diff --git a/src/cli.ts b/src/cli.ts index f329345..51fe550 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -4,8 +4,9 @@ import { parseArgs } from "node:util"; import { consola } from "consola"; import { loadConfig } from "c12"; import type { PackageJson } from "pkg-types"; -import { join } from "pathe"; +import { join, resolve } from "pathe"; import { createJiti } from "jiti"; +import { colors } from "consola/utils"; import { build } from "./build.ts"; import type { BuildConfig, BuildEntry } from "./types.ts"; @@ -28,56 +29,83 @@ const args = parseArgs({ }, }); -const { config = {} } = await loadConfig({ - name: "obuild", - configFile: "build.config", - cwd: args.values.dir, -}); +// rolldown: Top-level await is currently not supported with the 'cjs' output format +// so we need to wrap it in an async function +async function _build() { + const { config = {} } = await loadConfig({ + name: "obuild", + configFile: "build.config", + cwd: args.values.dir, + }); -const rawEntries = - args.positionals.length > 0 - ? (args.positionals as string[]) - : config.entries || []; + const rawEntries = + args.positionals.length > 0 + ? (args.positionals as string[]) + : config.entries || []; -const entries: BuildEntry[] = rawEntries.map((entry) => { - if (typeof entry === "string") { - const [input, outDir] = entry.split(":") as [string, string | undefined]; - return input.endsWith("/") - ? { type: "transform", input, outDir } - : { type: "bundle", input: input.split(","), outDir }; - } - return entry; -}); + const entries: BuildEntry[] = rawEntries.map((entry) => { + if (typeof entry === "string") { + const [input, outDir] = entry.split(":") as [string, string | undefined]; + return input.endsWith("/") + ? { type: "transform", input, outDir } + : { type: "bundle", input: input.split(","), outDir }; + } + return entry; + }); -if (args.values.stub) { - for (const entry of entries) { - entry.stub = true; + if (args.values.stub) { + for (const entry of entries) { + entry.stub = true; + } } -} -if (rawEntries.length === 0) { - // If no entries are specified, infer them from the package.json - const jiti = createJiti(process.cwd()); - const pkg: PackageJson = - ((await jiti.import("./package.json", { - try: true, - default: true, - })) as PackageJson) || ({} as PackageJson); - const sourceFiles = listRecursively(join(process.cwd(), "src")); - const res = inferEntries(pkg, sourceFiles, process.cwd()) - entries.push({ - type: 'bundle', - input: res.entries.map((entry) => entry.input as string), - dts: res.dts, - }) - if (entries.length === 0) { - consola.error("No build entries specified."); - process.exit(1); + if (rawEntries.length === 0) { + // If no entries are specified, infer them from the package.json + const jiti = createJiti(process.cwd()); + const pkg: PackageJson = + ((await jiti.import("./package.json", { + try: true, + default: true, + })) as PackageJson) || ({} as PackageJson); + const sourceFiles = listRecursively(join(process.cwd(), "src")); + const res = inferEntries(pkg, sourceFiles, process.cwd()); + entries.push({ + type: "bundle", + input: res.entries.map((entry) => entry.input as string), + dts: res.dts, + cjs: res.cjs, + }); + if (entries.length === 0) { + consola.error("No build entries specified."); + process.exit(1); + } + consola.log( + "🔍️Automatically detected entries ", + colors.cyan( + res.entries + .map((e) => + colors.bold( + (e.input as string) + .replace(resolve(process.cwd()) + "/", "") + .replace(/\/$/, "/*"), + ), + ) + .join(", "), + ), + colors.gray( + ["esm", res.cjs && "cjs", res.dts && "dts"] + .filter(Boolean) + .map((tag) => `[${tag}]`) + .join(" "), + ), + ); } + + await build({ + cwd: args.values.dir, + ...config, + entries, + }); } -await build({ - cwd: args.values.dir, - ...config, - entries, -}); +_build(); From 9ee5908f288e91b33064745dbce502f84fc74a1c Mon Sep 17 00:00:00 2001 From: wyatex Date: Mon, 16 Jun 2025 09:15:48 +0800 Subject: [PATCH 5/6] Revert "feat: support bundle cjs format" This reverts commit 4f67b65a5bf11f58f549db6c1bda5466b8b9928a. --- eslint.config.mjs | 1 - src/builders/bundle.ts | 68 ++--------------------- src/cli.ts | 120 ++++++++++++++++------------------------- 3 files changed, 50 insertions(+), 139 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 92e00d7..8743d40 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -7,7 +7,6 @@ export default unjs({ rules: { "unicorn/no-null": "off", "unicorn/no-nested-ternary": "off", - "unicorn/prefer-top-level-await": "off", }, markdown: { rules: { diff --git a/src/builders/bundle.ts b/src/builders/bundle.ts index 15c15ef..d53ce0e 100644 --- a/src/builders/bundle.ts +++ b/src/builders/bundle.ts @@ -81,6 +81,10 @@ export async function rolldownBuild( ], } satisfies InputOptions); + if (entry.dts !== false) { + rolldownConfig.plugins.push(...dts({ ...(entry.dts as DtsOptions) })); + } + await hooks.rolldownConfig?.(rolldownConfig, ctx); const res = await rolldown(rolldownConfig); @@ -98,55 +102,6 @@ export async function rolldownBuild( const { output } = await res.write(outConfig); - if (entry.dts !== false) { - // Bundle d.ts and d.mts files - rolldownConfig.plugins.push(...dts({ ...(entry.dts as DtsOptions), emitDtsOnly: true })); - await res.write({ - ...outConfig, - entryFileNames: "[name].js", - chunkFileNames: "_chunks/[name]-[hash].js", - }); - await res.write({ - ...outConfig, - entryFileNames: "[name].mjs", - chunkFileNames: "_chunks/[name]-[hash].mjs", - }); - } - - let cjsOutput - if (entry.cjs) { - // Bundle cjs files - const cjsRolldownConfig = { - ...rolldownConfig, - plugins: [shebangPlugin()] as Plugin[], - } - - const res = await rolldown(cjsRolldownConfig); - - const outConfig: OutputOptions = { - dir: outDir, - entryFileNames: "[name].cjs", - chunkFileNames: "_chunks/[name]-[hash].cjs", - minify: entry.minify, - format: 'cjs', - }; - - await hooks.rolldownOutput?.(outConfig, res, ctx); - - cjsOutput = (await res.write(outConfig)).output - - if (entry.dts !== false) { - // Bundle d.cts files - cjsRolldownConfig.plugins.push(...dts({ ...(entry.dts as DtsOptions), emitDtsOnly: true })); - await res.write({ - ...outConfig, - format: 'esm', - entryFileNames: "[name].cjs", - chunkFileNames: "_chunks/[name]-[hash].cjs", - }); - } - } - await res.close(); const outputEntries: { @@ -197,21 +152,6 @@ export async function rolldownBuild( }); } - if (cjsOutput) { - for (const chunk of cjsOutput) { - if (chunk.type !== "chunk" || !chunk.isEntry) continue; - if (chunk.fileName.endsWith("ts")) continue; - - outputEntries.push({ - name: chunk.fileName, - exports: chunk.exports, - deps: resolveDeps(chunk), - ...(await distSize(outDir, chunk.fileName)), - sideEffectSize: await sideEffectSize(outDir, chunk.fileName), - }); - } - } - consola.log( `\n${outputEntries .map((o) => diff --git a/src/cli.ts b/src/cli.ts index 51fe550..f329345 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -4,9 +4,8 @@ import { parseArgs } from "node:util"; import { consola } from "consola"; import { loadConfig } from "c12"; import type { PackageJson } from "pkg-types"; -import { join, resolve } from "pathe"; +import { join } from "pathe"; import { createJiti } from "jiti"; -import { colors } from "consola/utils"; import { build } from "./build.ts"; import type { BuildConfig, BuildEntry } from "./types.ts"; @@ -29,83 +28,56 @@ const args = parseArgs({ }, }); -// rolldown: Top-level await is currently not supported with the 'cjs' output format -// so we need to wrap it in an async function -async function _build() { - const { config = {} } = await loadConfig({ - name: "obuild", - configFile: "build.config", - cwd: args.values.dir, - }); - - const rawEntries = - args.positionals.length > 0 - ? (args.positionals as string[]) - : config.entries || []; +const { config = {} } = await loadConfig({ + name: "obuild", + configFile: "build.config", + cwd: args.values.dir, +}); - const entries: BuildEntry[] = rawEntries.map((entry) => { - if (typeof entry === "string") { - const [input, outDir] = entry.split(":") as [string, string | undefined]; - return input.endsWith("/") - ? { type: "transform", input, outDir } - : { type: "bundle", input: input.split(","), outDir }; - } - return entry; - }); +const rawEntries = + args.positionals.length > 0 + ? (args.positionals as string[]) + : config.entries || []; - if (args.values.stub) { - for (const entry of entries) { - entry.stub = true; - } +const entries: BuildEntry[] = rawEntries.map((entry) => { + if (typeof entry === "string") { + const [input, outDir] = entry.split(":") as [string, string | undefined]; + return input.endsWith("/") + ? { type: "transform", input, outDir } + : { type: "bundle", input: input.split(","), outDir }; } + return entry; +}); - if (rawEntries.length === 0) { - // If no entries are specified, infer them from the package.json - const jiti = createJiti(process.cwd()); - const pkg: PackageJson = - ((await jiti.import("./package.json", { - try: true, - default: true, - })) as PackageJson) || ({} as PackageJson); - const sourceFiles = listRecursively(join(process.cwd(), "src")); - const res = inferEntries(pkg, sourceFiles, process.cwd()); - entries.push({ - type: "bundle", - input: res.entries.map((entry) => entry.input as string), - dts: res.dts, - cjs: res.cjs, - }); - if (entries.length === 0) { - consola.error("No build entries specified."); - process.exit(1); - } - consola.log( - "🔍️Automatically detected entries ", - colors.cyan( - res.entries - .map((e) => - colors.bold( - (e.input as string) - .replace(resolve(process.cwd()) + "/", "") - .replace(/\/$/, "/*"), - ), - ) - .join(", "), - ), - colors.gray( - ["esm", res.cjs && "cjs", res.dts && "dts"] - .filter(Boolean) - .map((tag) => `[${tag}]`) - .join(" "), - ), - ); +if (args.values.stub) { + for (const entry of entries) { + entry.stub = true; } +} - await build({ - cwd: args.values.dir, - ...config, - entries, - }); +if (rawEntries.length === 0) { + // If no entries are specified, infer them from the package.json + const jiti = createJiti(process.cwd()); + const pkg: PackageJson = + ((await jiti.import("./package.json", { + try: true, + default: true, + })) as PackageJson) || ({} as PackageJson); + const sourceFiles = listRecursively(join(process.cwd(), "src")); + const res = inferEntries(pkg, sourceFiles, process.cwd()) + entries.push({ + type: 'bundle', + input: res.entries.map((entry) => entry.input as string), + dts: res.dts, + }) + if (entries.length === 0) { + consola.error("No build entries specified."); + process.exit(1); + } } -_build(); +await build({ + cwd: args.values.dir, + ...config, + entries, +}); From 023d488df05e67014c710ad12bd7001e20600189 Mon Sep 17 00:00:00 2001 From: wyatex Date: Mon, 16 Jun 2025 10:24:28 +0800 Subject: [PATCH 6/6] feat: remove commonjs support and jiti --- package.json | 1 - src/auto.ts | 18 +++++------------- src/build.ts | 11 +++-------- src/cli.ts | 38 +++++++++++++++++++++++++++----------- 4 files changed, 35 insertions(+), 33 deletions(-) diff --git a/package.json b/package.json index 504c977..3cdb934 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,6 @@ "consola": "^3.4.2", "defu": "^6.1.4", "exsolve": "^1.0.5", - "jiti": "^2.4.2", "magic-string": "^0.30.17", "oxc-minify": "^0.72.2", "oxc-parser": "^0.72.2", diff --git a/src/auto.ts b/src/auto.ts index 07c948b..da67d6c 100644 --- a/src/auto.ts +++ b/src/auto.ts @@ -6,7 +6,6 @@ import { extractExportFilenames } from "./utils.ts"; type InferEntriesResult = { entries: BuildEntry[]; - cjs?: boolean; dts?: boolean; warnings: string[]; }; @@ -52,21 +51,18 @@ export function inferEntries( const isJS = output.file.endsWith(".js"); if ((isESMPkg && isJS) || output.file.endsWith(".mjs")) { output.type = "esm"; - } else if ((!isESMPkg && isJS) || output.file.endsWith(".cjs")) { - output.type = "cjs"; } } - let cjs = false; let dts = false; // Infer entries from package files const entries: BuildEntry[] = []; for (const output of outputs) { - // Supported output file extensions are `.d.ts`, `.cjs` and `.mjs` + // Supported output file extensions are `.d.ts`, and `.mjs` // But we support any file extension here in case user has extended rolldown options const outputSlug = output.file.replace( - /(\*[^/\\]*|\.d\.(m|c)?ts|\.\w+)$/, + /(\*[^/\\]*|\.d\.(m)?ts|\.\w+)$/, "", ); const isDir = outputSlug.endsWith("/"); @@ -87,7 +83,7 @@ export function inferEntries( ); return sourceFiles .find((i) => SOURCE_RE.test(i)) - ?.replace(/(\.d\.(m|c)?ts|\.\w+)$/, ""); + ?.replace(/(\.d\.(m)?ts|\.\w+)$/, ""); }, undefined as any); if (!input) { @@ -97,15 +93,11 @@ export function inferEntries( continue; } - if (output.type === "cjs") { - cjs = true; - } - const entry = entries.find((i) => i.input === input) || entries[entries.push({ input, type: 'bundle' }) - 1]; - if (/\.d\.(m|c)?ts$/.test(output.file)) { + if (/\.d\.(m)?ts$/.test(output.file)) { dts = true; } @@ -114,7 +106,7 @@ export function inferEntries( } } - return { entries, cjs, dts, warnings }; + return { entries, dts, warnings }; } export const getEntrypointPaths = (path: string): string[] => { diff --git a/src/build.ts b/src/build.ts index b6b3f34..fd32600 100644 --- a/src/build.ts +++ b/src/build.ts @@ -6,7 +6,7 @@ import type { } from "./types.ts"; import { fileURLToPath } from "node:url"; -import { isAbsolute, join, resolve } from "pathe"; +import { isAbsolute, resolve } from "pathe"; import { rm } from "node:fs/promises"; import { consola } from "consola"; import { colors as c } from "consola/utils"; @@ -14,7 +14,7 @@ import { rolldownBuild } from "./builders/bundle.ts"; import { transformDir } from "./builders/transform.ts"; import { fmtPath, analyzeDir } from "./utils.ts"; import prettyBytes from "pretty-bytes"; -import { createJiti } from "jiti"; +import { readPackageJSON } from "pkg-types"; import type { PackageJson } from "pkg-types"; /** @@ -24,12 +24,7 @@ export async function build(config: BuildConfig): Promise { const start = Date.now(); const pkgDir = normalizePath(config.cwd); - const jiti = createJiti(pkgDir); - const pkg: PackageJson = - ((await jiti.import("./package.json", { - try: true, - default: true, - })) as PackageJson) || ({} as PackageJson); + const pkg = (await readPackageJSON(pkgDir)) || {} as PackageJson; const ctx: BuildContext = { pkg, pkgDir }; consola.log( diff --git a/src/cli.ts b/src/cli.ts index f329345..a21f047 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -3,12 +3,12 @@ import { parseArgs } from "node:util"; import { consola } from "consola"; import { loadConfig } from "c12"; -import type { PackageJson } from "pkg-types"; -import { join } from "pathe"; -import { createJiti } from "jiti"; +import { readPackageJSON } from 'pkg-types' +import { join, resolve } from "pathe"; +import { colors } from "consola/utils"; -import { build } from "./build.ts"; import type { BuildConfig, BuildEntry } from "./types.ts"; +import { build } from "./build.ts"; import { inferEntries } from "./auto.ts"; import { listRecursively } from "./utils.ts"; @@ -57,13 +57,9 @@ if (args.values.stub) { if (rawEntries.length === 0) { // If no entries are specified, infer them from the package.json - const jiti = createJiti(process.cwd()); - const pkg: PackageJson = - ((await jiti.import("./package.json", { - try: true, - default: true, - })) as PackageJson) || ({} as PackageJson); - const sourceFiles = listRecursively(join(process.cwd(), "src")); + const pkgDir = resolve(process.cwd()) + const pkg = await readPackageJSON(pkgDir) + const sourceFiles = listRecursively(join(pkgDir, "src")); const res = inferEntries(pkg, sourceFiles, process.cwd()) entries.push({ type: 'bundle', @@ -74,6 +70,26 @@ if (rawEntries.length === 0) { consola.error("No build entries specified."); process.exit(1); } + consola.log( + "🔍️Automatically detected entries ", + colors.cyan( + res.entries + .map((e) => + colors.bold( + (e.input as string) + .replace(resolve(process.cwd()) + "/", "") + .replace(/\/$/, "/*"), + ), + ) + .join(", "), + ), + colors.gray( + ["esm", res.dts && "dts"] + .filter(Boolean) + .map((tag) => `[${tag}]`) + .join(" "), + ), + ); } await build({