Skip to content

Commit

Permalink
feat(externals): write bundledDependencies and detect duplicate versions
Browse files Browse the repository at this point in the history
  • Loading branch information
pi0 committed Mar 15, 2022
1 parent a5262e4 commit a898c8d
Showing 1 changed file with 59 additions and 33 deletions.
92 changes: 59 additions & 33 deletions src/rollup/plugins/externals.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { existsSync, promises as fsp } from 'fs'
import { resolve, dirname, normalize } from 'pathe'
import { resolve, dirname, normalize, join } from 'pathe'
import { nodeFileTrace, NodeFileTraceOptions } from '@vercel/nft'
import type { Plugin } from 'rollup'
import { resolvePath, isValidNodeImport } from 'mlly'
Expand Down Expand Up @@ -87,46 +87,72 @@ export function externals (opts: NodeExternalsOptions): Plugin {
}
},
async buildEnd () {
if (opts.trace !== false) {
for (const pkgName of opts.traceInclude || []) {
const path = await this.resolve(pkgName)
if (path?.id) {
trackedExternals.add(path.id)
}
}
const tracedFiles = await nodeFileTrace(Array.from(trackedExternals), opts.traceOptions)
.then(r => Array.from(r.fileList).map(f => resolve(opts.traceOptions.base, f)))
.then(r => r.filter(file => file.includes('node_modules')))
if (opts.trace === false) {
return
}

// Find all unique package names
const pkgs = new Set<string>()
for (const file of tracedFiles) {
const { baseDir, pkgName } = parseNodeModulePath(file)
pkgs.add(resolve(baseDir, pkgName, 'package.json'))
// Force trace paths
for (const pkgName of opts.traceInclude || []) {
const path = await this.resolve(pkgName)
if (path?.id) {
trackedExternals.add(path.id)
}
}

for (const pkg of pkgs) {
if (!tracedFiles.includes(pkg)) {
tracedFiles.push(pkg)
}
// Trace files
const tracedFiles = await nodeFileTrace(Array.from(trackedExternals), opts.traceOptions)
.then(r => Array.from(r.fileList).map(f => resolve(opts.traceOptions.base, f)))
.then(r => r.filter(file => file.includes('node_modules')))

// Keep track of npm packages
const tracedPackages = new Map() // name => pkgDir
for (const file of tracedFiles) {
const { baseDir, pkgName } = parseNodeModulePath(file)
const pkgDir = resolve(baseDir, pkgName)

// Check for duplicate versions
const existingPkgDir = tracedPackages.get(pkgName)
if (existingPkgDir && existingPkgDir !== pkgDir) {
console.warn(`Multiple versions of package ${pkgName} detected in:\n`, [
existingPkgDir,
pkgDir
].map(p => ' - ' + p).join('\n'))
continue
}

const writeFile = async (file) => {
if (!await isFile(file)) { return }
const src = resolve(opts.traceOptions.base, file)
const dst = resolve(opts.outDir, 'node_modules', file.replace(/^.*?node_modules[\\/](.*)$/, '$1'))
await fsp.mkdir(dirname(dst), { recursive: true })
await fsp.copyFile(src, dst)
// Add to traced packages
tracedPackages.set(pkgName, pkgDir)
}

// Ensure all package.json files are traced
for (const pkgDir of tracedPackages.values()) {
const pkgJSON = join(pkgDir, 'package.json')
if (!tracedFiles.includes(pkgJSON)) {
tracedFiles.push(pkgJSON)
}
if (process.platform === 'win32') {
// Workaround for EBUSY on windows (#424)
for (const file of tracedFiles) {
await writeFile(file)
}
} else {
await Promise.all(tracedFiles.map(writeFile))
}

const writeFile = async (file) => {
if (!await isFile(file)) { return }
const src = resolve(opts.traceOptions.base, file)
const dst = resolve(opts.outDir, 'node_modules', file.replace(/^.*?node_modules[\\/](.*)$/, '$1'))
await fsp.mkdir(dirname(dst), { recursive: true })
await fsp.copyFile(src, dst)
}
if (process.platform === 'win32') {
// Workaround for EBUSY on windows (#424)
for (const file of tracedFiles) {
await writeFile(file)
}
} else {
await Promise.all(tracedFiles.map(writeFile))
}

// Write an informative package.json
await fsp.writeFile(resolve(opts.outDir, 'package.json'), JSON.stringify({
private: true,
bundledDependencies: Array.from(tracedPackages.keys())
}, null, 2), 'utf8')
}
}
}
Expand Down

0 comments on commit a898c8d

Please sign in to comment.