Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: resolve actual subpaths before guessing #527

Merged
merged 14 commits into from
Aug 7, 2023
Merged
68 changes: 26 additions & 42 deletions src/rollup/plugins/externals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,16 @@ import { existsSync, promises as fsp } from "node:fs";
import { platform } from "node:os";
import { resolve, dirname, normalize, join, isAbsolute, relative } from "pathe";
import type { PackageJson } from "pkg-types";
import { readPackageJSON } from "pkg-types";
import { nodeFileTrace, NodeFileTraceOptions } from "@vercel/nft";
import type { Plugin } from "rollup";
import { resolvePath, isValidNodeImport, normalizeid } from "mlly";
import {
resolvePath,
isValidNodeImport,
lookupNodeModuleSubpath,
normalizeid,
parseNodeModulePath,
} from "mlly";
import semver from "semver";
import { isDirectory } from "../../utils";

Expand All @@ -31,7 +38,7 @@ export function externals(opts: NodeExternalsOptions): Plugin {
const trackedExternals = new Set<string>();

const _resolveCache = new Map();
const _resolve = async (id: string) => {
const _resolve = async (id: string): Promise<string> => {
let resolved = _resolveCache.get(id);
if (resolved) {
return resolved;
Expand Down Expand Up @@ -116,7 +123,7 @@ export function externals(opts: NodeExternalsOptions): Plugin {
// -- Trace externals --

// Try to extract package name from path
const { pkgName, subpath } = parseNodeModulePath(resolved.id);
const { name: pkgName } = parseNodeModulePath(resolved.id);

// Inline if cannot detect package name
if (!pkgName) {
Expand All @@ -140,15 +147,17 @@ export function externals(opts: NodeExternalsOptions): Plugin {
// Guess as main subpath export
const packageEntry = await _resolve(pkgName).catch(() => null);
if (packageEntry !== originalId) {
// Guess subpathexport
const guessedSubpath = pkgName + subpath.replace(/\.[a-z]+$/, "");
const resolvedGuess = await _resolve(guessedSubpath).catch(
() => null
);
// Reverse engineer subpath export
const guessedSubpath: string | null = await lookupNodeModuleSubpath(
originalId
).catch(() => null);
const resolvedGuess =
guessedSubpath &&
(await _resolve(join(pkgName, guessedSubpath)).catch(() => null));
if (resolvedGuess === originalId) {
trackedExternals.add(resolvedGuess);
return {
id: guessedSubpath,
id: join(pkgName, guessedSubpath),
external: true,
};
}
Expand Down Expand Up @@ -182,19 +191,6 @@ export function externals(opts: NodeExternalsOptions): Plugin {
...opts.traceOptions,
});

// Read package.json with cache
const packageJSONCache = new Map(); // pkgDir => contents
const getPackageJson = async (pkgDir: string) => {
if (packageJSONCache.has(pkgDir)) {
return packageJSONCache.get(pkgDir) as PackageJson;
}
const pkgJSON = JSON.parse(
await fsp.readFile(resolve(pkgDir, "package.json"), "utf8")
);
packageJSONCache.set(pkgDir, pkgJSON);
return pkgJSON as PackageJson;
};

// Resolve traced files
type TracedFile = {
path: string;
Expand All @@ -220,7 +216,11 @@ export function externals(opts: NodeExternalsOptions): Plugin {
if (!(await isFile(path))) {
return;
}
const { baseDir, pkgName, subpath } = parseNodeModulePath(path);
const {
dir: baseDir,
name: pkgName,
subpath,
} = parseNodeModulePath(path);
const pkgPath = join(baseDir, pkgName);
const parents = await Promise.all(
[...reasons.parents].map((p) => _resolveTracedPath(p))
Expand Down Expand Up @@ -257,7 +257,9 @@ export function externals(opts: NodeExternalsOptions): Plugin {
let tracedPackage = tracedPackages[pkgName];

// Read package.json for file
let pkgJSON = await getPackageJson(tracedFile.pkgPath).catch(
let pkgJSON = await readPackageJSON(tracedFile.pkgPath, {
cache: true,
}).catch(
() => {} // TODO: Only catch ENOENT
);
if (!pkgJSON) {
Expand Down Expand Up @@ -465,24 +467,6 @@ function compareVersions(v1 = "0.0.0", v2 = "0.0.0") {
}
}

function parseNodeModulePath(path: string) {
if (!path) {
return {};
}
const match = /^(.+\/node_modules\/)([^/@]+|@[^/]+\/[^/]+)(\/?.*?)?$/.exec(
normalize(path)
);
if (!match) {
return {};
}
const [, baseDir, pkgName, subpath] = match;
return {
baseDir,
pkgName,
subpath,
};
}

export function applyProductionCondition(exports: PackageJson["exports"]) {
if (!exports || typeof exports === "string") {
return;
Expand Down