Skip to content

Commit

Permalink
fix: improve externals handling
Browse files Browse the repository at this point in the history
  • Loading branch information
pi0 committed Mar 15, 2022
1 parent bb1d4be commit a5262e4
Showing 1 changed file with 46 additions and 21 deletions.
67 changes: 46 additions & 21 deletions src/rollup/plugins/externals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@ import { existsSync, promises as fsp } from 'fs'
import { resolve, dirname, normalize } from 'pathe'
import { nodeFileTrace, NodeFileTraceOptions } from '@vercel/nft'
import type { Plugin } from 'rollup'
import { resolvePath, isValidNodeImport, normalizeid } from 'mlly'
import { resolvePath, isValidNodeImport } from 'mlly'

export interface NodeExternalsOptions {
inline?: string[]
external?: string[]
outDir?: string
trace?: boolean
normalizeId?: boolean
traceOptions?: NodeFileTraceOptions
moduleDirectories?: string[]
exportConditions?: string[]
Expand All @@ -21,34 +20,40 @@ export function externals (opts: NodeExternalsOptions): Plugin {

return {
name: 'node-externals',
async resolveId (id, importer, options) {
async resolveId (originalId, importer, options) {
// Skip internals
if (!id || id.startsWith('\x00') || id.includes('?') || id.startsWith('#')) {
if (!originalId || originalId.startsWith('\x00') || originalId.includes('?') || originalId.startsWith('#')) {
return null
}

// Normalize path on windows
const normalizedId = normalize(id)
// Skip relative paths
if (originalId.startsWith('.')) {
return null
}

const _id = normalizedId.split('node_modules/').pop()
if (!opts.external.find(i => _id.startsWith(i) || id.startsWith(i))) {
// Resolve relative paths and exceptions
// Ensure to take absolute and relative id
if (_id.startsWith('.') || opts.inline.find(i => _id.startsWith(i) || normalizedId.startsWith(i))) {
return null
}
// Normalize path (windows)
const id = normalize(originalId)

// Id without .../node_modules/
const idWithoutNodeModules = id.split('node_modules/').pop()

// const matchedExternal = opts.external.find(i => idWithoutNodeModules.startsWith(i) || id.startsWith(i))

// Check for explicit inlines
if (opts.inline.find(i => (id.startsWith(i) || idWithoutNodeModules.startsWith(i)))) {
return null
}

// Resolve external (rollup => node)
const resolved = await this.resolve(id, importer, { ...options, skipSelf: true }) || { id }
// Resolve id (rollup then native node ESM)
const resolved = await this.resolve(originalId, importer, { ...options, skipSelf: true }) || { id }
if (!existsSync(resolved.id)) {
resolved.id = await resolvePath(resolved.id, {
conditions: opts.exportConditions,
url: opts.moduleDirectories
})
}

// Ensure id is a valid import
// Inline invalid node imports
if (!await isValidNodeImport(resolved.id)) {
return {
...resolved,
Expand All @@ -59,11 +64,23 @@ export function externals (opts: NodeExternalsOptions): Plugin {
// Track externals
trackedExternals.add(resolved.id)

// Normalize id with explicit protocol
if (opts.normalizeId) {
resolved.id = normalizeid(resolved.id)
// Try to extract package name from path
const { pkgName } = parseNodeModulePath(resolved.id)

// Inline in trace-mode when cannot extract package name
if (!pkgName && opts.trace !== false) {
return null
}

// External with package name in trace mode
if (opts.trace !== false) {
return {
id: pkgName,
external: true
}
}

// External with full pack in normal mode
return {
...resolved,
external: true
Expand All @@ -81,10 +98,10 @@ export function externals (opts: NodeExternalsOptions): Plugin {
.then(r => Array.from(r.fileList).map(f => resolve(opts.traceOptions.base, f)))
.then(r => r.filter(file => file.includes('node_modules')))

// // Find all unique package names
// Find all unique package names
const pkgs = new Set<string>()
for (const file of tracedFiles) {
const [, baseDir, pkgName] = /^(.+\/node_modules\/)([^@/]+|@[^/]+\/[^/]+)(\/?.*?)?$/.exec(file)
const { baseDir, pkgName } = parseNodeModulePath(file)
pkgs.add(resolve(baseDir, pkgName, 'package.json'))
}

Expand Down Expand Up @@ -114,6 +131,14 @@ export function externals (opts: NodeExternalsOptions): Plugin {
}
}

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

async function isFile (file: string) {
try {
const stat = await fsp.stat(file)
Expand Down

0 comments on commit a5262e4

Please sign in to comment.