|
5 | 5 | */ |
6 | 6 |
|
7 | 7 | import * as fs from 'fs'; |
8 | | -import { builtinModules } from 'module'; |
| 8 | +import { builtinModules, isBuiltin } from 'module'; |
9 | 9 | import * as path from 'path'; |
10 | 10 | import { fileURLToPath } from 'url'; |
11 | 11 |
|
@@ -44,18 +44,49 @@ function isSideEffectFreePackage(packageName) { |
44 | 44 |
|
45 | 45 | let sideEffectFree = false; |
46 | 46 | try { |
47 | | - const json = JSON.parse( |
48 | | - fs.readFileSync(path.resolve(repoRoot, 'node_modules', packageName, 'package.json'), { encoding: 'utf8' }), |
49 | | - ); |
| 47 | + const json = JSON.parse(fs.readFileSync(findPackageJson(packageName), { encoding: 'utf8' })); |
50 | 48 | sideEffectFree = json.sideEffects === false; |
51 | 49 | } catch { |
52 | | - // Not resolvable from the repo root - assume side effects. |
| 50 | + // Not resolvable from the built package - assume side effects. |
| 51 | + if (isInstalledPackage(packageName)) { |
| 52 | + // oxlint-disable-next-line no-console |
| 53 | + console.warn( |
| 54 | + `[rollup-utils] Cannot read the package.json of "${packageName}" from ${process.cwd()}, so its imports are kept. Declare it in the package.json of the built package.`, |
| 55 | + ); |
| 56 | + } |
53 | 57 | } |
54 | 58 |
|
55 | 59 | sideEffectsCache.set(packageName, sideEffectFree); |
56 | 60 | return sideEffectFree; |
57 | 61 | } |
58 | 62 |
|
| 63 | +/** |
| 64 | + * The `package.json` of a dependency of the built package (the working directory): the nearest |
| 65 | + * `node_modules/<packageName>` from there up to the repo root, like Node resolves it. |
| 66 | + */ |
| 67 | +function findPackageJson(packageName) { |
| 68 | + for (let dir = process.cwd(); ; dir = path.dirname(dir)) { |
| 69 | + const candidate = path.join(dir, 'node_modules', packageName, 'package.json'); |
| 70 | + if (fs.existsSync(candidate) || dir === repoRoot || dir === path.dirname(dir)) { |
| 71 | + return candidate; |
| 72 | + } |
| 73 | + } |
| 74 | +} |
| 75 | + |
| 76 | +/** |
| 77 | + * Whether `packageName` must have a `package.json` in a `node_modules` folder of the built package. This is false for |
| 78 | + * builtins, for virtual modules and placeholders (e.g. `$app`, `cloudflare:workers`), which are no valid npm package |
| 79 | + * names, for the built package itself, and for optional peers, which are not always installed. |
| 80 | + */ |
| 81 | +function isInstalledPackage(packageName) { |
| 82 | + return ( |
| 83 | + !isBuiltin(packageName) && |
| 84 | + /^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/.test(packageName) && |
| 85 | + packageName !== packageDotJSON.name && |
| 86 | + !packageDotJSON.peerDependenciesMeta?.[packageName]?.optional |
| 87 | + ); |
| 88 | +} |
| 89 | + |
59 | 90 | function getPackageName(id) { |
60 | 91 | const segments = id.split('/'); |
61 | 92 | return id.startsWith('@') ? segments.slice(0, 2).join('/') : segments[0]; |
|
0 commit comments