Skip to content

Commit

Permalink
separate exportsOnly handling
Browse files Browse the repository at this point in the history
  • Loading branch information
guybedford committed Jun 27, 2020
1 parent 3f2ba34 commit 03ffb07
Show file tree
Hide file tree
Showing 8 changed files with 61 additions and 19 deletions.
19 changes: 12 additions & 7 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,26 +59,31 @@ By default `processCwd` is the same as `base`.
#### Exports
By default tracing of the [Node.js "exports" field](https://nodejs.org/dist/latest-v14.x/docs/api/esm.html#esm_package_entry_points) is not handled, but can be enabled by setting the exports option:
By default tracing of the [Node.js "exports" field](https://nodejs.org/dist/latest-v14.x/docs/api/esm.html#esm_package_entry_points) is supported, with the `"node"`, `"require"`, `"import"` and `"default"` conditions traced as defined.
Alternatively the explicit list of exports can be provided:
```js
const { fileList } = await nodeFileTrace(files, {
exports: true
exports: ['node', 'production']
});
```
By default, the `"node"`, `"require"`, `"import"` and `"default"` conditions will be traced. Any package with an `"exports"` field will have its
exports traced instead of the `"main"`.
Only the `"node"` export should be explicitly included (if needed) when specifying the exact export condition list. The `"require"`, `"import"` and `"default"` conditions will always be traced as defined, no matter what custom conditions are set.
#### Exports Only
In addition to a boolean value, the specific list of exports conditions to trace can be provided as well:
When tracing exports the `"main"` / index field will still be traced for Node.js versions without `"exports"` support.
This can be disabled with the `exportsOnly` option:
```js
const { fileList } = await nodeFileTrace(files, {
exports: ['node', 'production']
exportsOnly: true
});
```
Only the `"node"` export should be explicitly included (if needed) when specifying the exact export condition list. The `"require"`, `"import"` and `"default"` conditions will always be traced as defined, no matter what custom conditions are set.
Any package with `"exports"` will then only have its exports traced, and the main will not be included at all.
#### Paths
Expand Down
38 changes: 28 additions & 10 deletions src/node-file-trace.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ class Job {
constructor ({
base = process.cwd(),
processCwd,
exports = false,
exports = ['node'],
exportsOnly = false,
paths = {},
ignore,
log = false,
Expand Down Expand Up @@ -74,9 +75,8 @@ class Job {
}
this.base = base;
this.cwd = resolve(processCwd || base);
if (!Array.isArray(exports) && typeof exports !== 'boolean')
exports = false;
this.exports = exports === true ? ['node'] : exports;
this.exports = exports;
this.exportsOnly = exportsOnly;
const resolvedPaths = {};
for (const path of Object.keys(paths)) {
const trailer = paths[path].endsWith('/');
Expand Down Expand Up @@ -282,26 +282,44 @@ class Job {
...[...deps].map(async dep => {
try {
var resolved = await resolveDependency(dep, path, this, !isESM);
// ignore builtins
if (resolved.startsWith('node:')) return;
}
catch (e) {
this.warnings.add(new Error(`Failed to resolve dependency ${dep}:\n${e && e.message}`));
return;
}
await this.emitDependency(resolved, path);
if (Array.isArray(resolved)) {
for (const item of resolved) {
// ignore builtins
if (item.startsWith('node:')) return;
await this.emitDependency(item, path);
}
}
else {
// ignore builtins
if (resolved.startsWith('node:')) return;
await this.emitDependency(resolved, path);
}
}),
...[...imports].map(async dep => {
try {
var resolved = await resolveDependency(dep, path, this, false);
// ignore builtins
if (resolved.startsWith('node:')) return;
}
catch (e) {
this.warnings.add(new Error(`Failed to resolve dependency ${dep}:\n${e && e.message}`));
return;
}
await this.emitDependency(resolved, path);
if (Array.isArray(resolved)) {
for (const item of resolved) {
// ignore builtins
if (item.startsWith('node:')) return;
await this.emitDependency(item, path);
}
}
else {
// ignore builtins
if (resolved.startsWith('node:')) return;
await this.emitDependency(resolved, path);
}
})
]);
}
Expand Down
9 changes: 8 additions & 1 deletion src/resolve-dependency.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ module.exports = function resolveDependency (specifier, parent, job, cjsResolve
else {
resolved = resolvePackage(specifier, parent, job, cjsResolve);
}
if (resolved.startsWith('node:')) return resolved;
if (typeof resolved === 'string' && resolved.startsWith('node:')) return resolved;
if (Array.isArray(resolved))
return resolved.map(resolved => job.realpath(resolved, parent));
return job.realpath(resolved, parent);
};

Expand Down Expand Up @@ -145,11 +147,16 @@ function resolvePackage (name, parent, job, cjsResolve) {
if (!stat || !stat.isDirectory()) continue;
const pkgCfg = getPkgCfg(nodeModulesDir + sep + pkgName, job);
if (pkgCfg && job.exports && pkgCfg.exports !== undefined && pkgCfg.exports !== null) {
let legacyResolved;
if (!job.exportsOnly)
legacyResolved = resolveFile(nodeModulesDir + sep + name, parent, job) || resolveDir(nodeModulesDir + sep + name, parent, job);
let resolved = resolveExportsTarget(nodeModulesDir + sep + pkgName, pkgCfg.exports, '.' + name.slice(pkgName.length), job, cjsResolve);
if (resolved && cjsResolve)
resolved = resolveFile(resolved, parent, job) || resolveDir(resolved, parent, job);
if (resolved) {
job.emitFile(nodeModulesDir + sep + pkgName + sep + 'package.json', 'resolve', parent);
if (legacyResolved && legacyResolved !== resolved)
return [resolved, legacyResolved];
return resolved;
}
}
Expand Down
2 changes: 1 addition & 1 deletion test/unit.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ for (const unitTest of fs.readdirSync(join(__dirname, 'unit'))) {
dep: 'test/unit/esm-paths/esm-dep.js',
'dep/': 'test/unit/esm-paths-trailer/'
},
exports: unitTest.startsWith('exports'),
exportsOnly: unitTest.startsWith('exports-only'),
ts: true,
log: true,
// disable analysis for basic-analysis unit tests
Expand Down
2 changes: 2 additions & 0 deletions test/unit/exports-only/input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
require('pkg');

8 changes: 8 additions & 0 deletions test/unit/exports-only/output.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[
"package.json",
"test/unit/exports-only/input.js",
"test/unit/exports-only/node_modules/pkg/package.json",
"test/unit/exports-only/node_modules/pkg/require-main.cjs",
"test/unit/exports-only/node_modules/pkg/subdir/import-main.js",
"test/unit/exports-only/node_modules/pkg/subdir/package.json"
]
1 change: 1 addition & 0 deletions test/unit/exports/node_modules/pkg/package.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions test/unit/exports/output.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[
"package.json",
"test/unit/exports/input.js",
"test/unit/exports/node_modules/pkg/index.js",
"test/unit/exports/node_modules/pkg/package.json",
"test/unit/exports/node_modules/pkg/require-main.cjs",
"test/unit/exports/node_modules/pkg/subdir/import-main.js",
Expand Down

0 comments on commit 03ffb07

Please sign in to comment.