Skip to content

Commit

Permalink
feat: infer externals from package name, exports and imports
Browse files Browse the repository at this point in the history
  • Loading branch information
pi0 committed Dec 27, 2024
1 parent af19b1b commit 82ab4f4
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 2 deletions.
11 changes: 9 additions & 2 deletions src/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@ import { defu } from "defu";
import { createHooks } from "hookable";
import prettyBytes from "pretty-bytes";
import { glob } from "tinyglobby";
import { dumpObject, rmdir, resolvePreset, removeExtension } from "./utils";
import {
dumpObject,
rmdir,
resolvePreset,
removeExtension,
inferPkgExternals,
} from "./utils";
import type { BuildContext, BuildConfig, BuildOptions } from "./types";
import { validatePackage, validateDependencies } from "./validate";
import { rollupBuild } from "./builders/rollup";
Expand Down Expand Up @@ -228,7 +234,8 @@ async function _build(
options.devDependencies = Object.keys(pkg.devDependencies || {});

// Add all dependencies as externals
options.externals.push(...options.dependencies, ...options.peerDependencies);
options.externals.push(...inferPkgExternals(pkg));
options.externals = [...new Set(options.externals)];

// Call build:before
await ctx.hooks.callHook("build:before", ctx);
Expand Down
39 changes: 39 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,3 +162,42 @@ export function arrayIncludes(
export function removeExtension(filename: string): string {
return filename.replace(/\.(js|mjs|cjs|ts|mts|cts|json|jsx|tsx)$/, "");
}

export function inferPkgExternals(pkg: PackageJson): (string | RegExp)[] {
const externals: (string | RegExp)[] = [
...Object.keys(pkg.dependencies || {}),
...Object.keys(pkg.peerDependencies || {}),
...Object.keys(pkg.devDependencies || {}),
...Object.keys(pkg.optionalDependencies || {}),
];

if (pkg.name) {
externals.push(pkg.name);
if (pkg.exports) {
for (const subpath of Object.keys(pkg.exports)) {
if (subpath.startsWith("./")) {
externals.push(pathToRegex(`${pkg.name}/${subpath.slice(2)}`));
}
}
}
}

if (pkg.imports) {
for (const importName of Object.keys(pkg.imports)) {
if (importName.startsWith("#")) {
externals.push(pathToRegex(importName));
}
}
}

return [...new Set(externals)];
}

function pathToRegex(path: string): string | RegExp {
if (!path.includes("*")) {
return path;
}
return new RegExp(
path.replace(/\./g, String.raw`\.`).replace(/\*/g, ".*") + "$",
);
}
36 changes: 36 additions & 0 deletions test/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
arrayIncludes,
extractExportFilenames,
inferExportType,
inferPkgExternals,
} from "../src/utils";

describe("inferExportType", () => {
Expand Down Expand Up @@ -52,3 +53,38 @@ describe("arrayIncludes", () => {
expect(arrayIncludes([/t3$/, "test2"], "test1")).to.eq(false);
});
});

describe("inferPkgExternals", () => {
it("infers externals from package.json", () => {
expect(
inferPkgExternals({
name: "test",
dependencies: { react: "17.0.0" },
peerDependencies: { "react-dom": "17.0.0" },
devDependencies: { "@types/react": "17.0.0" },
optionalDependencies: { test: "1.0.0", optional: "1.0.0" },
exports: {
".": "index.js",
"./extra/utils": "utils.js",
"./drivers/*.js": "drivers/*.js",
invalid: "invalid.js",
},
imports: {
"#*": "src/*",
"#test": "test.js",
invalid: "invalid.js",
},
}),
).to.deep.equal([
"react",
"react-dom",
"@types/react",
"test",
"optional",
"test/extra/utils",
/test\/drivers\/.*\.js$/,
/#.*$/,
"#test",
]);
});
});

0 comments on commit 82ab4f4

Please sign in to comment.