[RFC]: Bringing `resolve` Dependency In-House
- Vorherrschende Sprache
- JavaScript
- Sterne
- 6k
- Forks
- 1.3k
- Ø Merge
- 1 T. 3 Std.
- Gemergte PRs (30 T.)
- 611
Beschreibung
# Replace `resolve` Dependency with In-House `@stdlib/utils/resolve`
Replace the external [`resolve`](https://www.npmjs.com/package/resolve) npm dependency with a new stdlib-native package. This eliminates `resolve` and its **3 transitive dependencies** (`is-core-module`, `path-parse`, `supports-preserve-symlinks-flag`) from the supply chain — removing 4 third-party packages total.
## Background
The `resolve` package implements Node.js's `require.resolve()` algorithm with configurable `basedir` and `paths` options.
Only these features are used:
| Feature | Used? |
|---|---|
| `resolve(id, {basedir}, cb)` (async) | ✅ 5 files |
| `resolve.sync(id, {basedir})` | ✅ 7 files |
| `resolve.sync(id, {basedir, paths})` | ✅ 1 file |
| All other options (`packageFilter`, `pathFilter`, `extensions`, `moduleDirectory`, etc.) | ❌ |
## Proposed Changes
### New Package: `@stdlib/utils/resolve`
#### Folder Structure
```
lib/node_modules/@stdlib/utils/resolve/
├── README.md
├── package.json
│
├── lib/
│ ├── index.js # Entry point: exports async + .sync
│ ├── main.js # Async resolve( id, opts, clbk )
│ ├── sync.js # Sync resolve( id, opts ) → string
│ ├── validate.js # Options validation
│ ├── defaults.js # Default options (basedir, extensions)
│ ├── resolve_path.js # Core sync resolution algorithm
│ ├── async_resolve_path.js # Core async resolution algorithm
│ └── is_core_module.js # Built-in module check via module.builtinModules
│
├── docs/
│ ├── repl.txt
│ └── types/
│ ├── index.d.ts
│ └── test.ts
│
├── benchmark/
│ └── benchmark.js # Performance benchmarks (sync vs async)
│
├── examples/
│ └── index.js # Usage examples
│
└── test/
├── test.js # Main export tests
├── test.sync.js # Sync API tests
├── test.async.js # Async API tests
├── test.validate.js # Options validation tests
├── test.is_core_module.js # Core module detection tests
└── fixtures/
├── node_modules/
│ └── mock-pkg/
│ ├── package.json # { "main": "./lib/index.js" }
│ └── lib/
│ └── index.js
├── index.js # Mock directory index
├── foo.js # Mock module
└── foo.json # Mock JSON module
```
#### Key Implementation Details
**`lib/index.js`** — Entry point matching the `resolve` package's API shape:
```js
var resolve = require( './main.js' ); // async
var sync = require( './sync.js' ); // sync
resolve.sync = sync;
module.exports = resolve;
```
**`lib/resolve_path.js`**— Core Node.js resolution algorithm:
1. If `id` is a core module → return `id`
2. If `id` starts with `./` or `../` or `/` → resolve as file, then as directory
3. Otherwise → walk `node_modules` directories upward from `basedir`; if `paths` option given, also search those
4. File resolution: try exact path → `.js` → `.json` → `.node`
5. Directory resolution: read `package.json` `main` field → fallback to `index.js` → `index.json` → `index.node`
**`lib/is_core_module.js`** — Zero-dependency replacement for `is-core-module`:
```js
var builtinModules = require( 'module' ).builtinModules;
function isCoreModule( id ) {
return builtinModules.indexOf( id ) !== -1;
}
```
**Internal dependencies only** — no third-party packages:
- `path` (Node.js built-in)
- `fs` (Node.js built-in)
- `module` (Node.js built-in)
- `@stdlib/assert/is-string` (input validation)
- `@stdlib/assert/is-plain-object` (options validation)
- `@stdlib/assert/has-own-property` (options checking)
- `@stdlib/error/tools/fmtprodmsg` (error formatting)
---
### Consumer Migration (13 files)
Each migration is a 1-line `require` swap. The API shape is identical.
**Sync consumers** (8 files) — `require( 'resolve' ).sync` → `require( '@stdlib/utils/resolve' ).sync`:
| # | File |
|---|---|
| 1 | eslint/require-file-extensions/lib/main.js|
| 2 | modules/pkg-deps/lib/walk_file.sync.js |
| 3 | pkgs/browser-compatible/lib/resolve.sync.js |
| 4 | pkgs/entry-points/lib/resolve.sync.js|
| 5 | pkgs/browser-entry-points/lib/resolve.sync.js |
| 6 | utils/library-manifest/lib/main.js |
| 7 | repl/help/scripts/build.js |
**Async consumers** (6 files) — `require( 'resolve' )` → `require( '@stdlib/utils/resolve' )`:
| # | File |
|---|---|
| 8 | modules/pkg-deps/lib/walk_file.js |
| 9 | modules/pkg-deps/test/test.walk_file.js *(also update `proxyquire` mock key)* |
| 10 | pkgs/browser-compatible/lib/resolve.js|
| 11 | pkgs/entry-points/lib/resolve.js|
| 12 | pkgs/browser-entry-points/lib/resolve.js|
| 13 | lint/namespace-aliases/lib/resolve.js|
---
### Dependency Removal
```
npm uninstall resolve
```
---
## Verification Plan
### Automated Tests
stdlib uses `make test`:
* Run new package tests using stdlib's test infrastructure:
```bash
make EXAMPLES_FILTER=".*/utils/resolve/.*"" examples
make BENCHMARKS_FILTER=".*/utils/resolve/.*"" benchmark
make TESTS_FILTER=". */utils/resolve/.*"" test
```
* Run consumer regression tests (pkg-deps has the most extensive test suite):
```
make TESTS_FILTER=". */_tools/modules/pkg-deps/.*"" test
```
### Manual Verification
{TODO}
Beitragsleitfaden
Rechercherichtung
Beginne mit der Überprüfung der vorgeschlagenen Struktur von @stdlib/utils/resolve und untersuche anschließend die aufgeführten resolve-Verbraucher, insbesondere modules/pkg-deps und dessen test/test.walk_file.js. Führe die vorhandenen Verbrauchertests aus, bevor du Abhängigkeiten änderst, füge dann die Pakettests hinzu und migriere die aufgeführten Imports. Als erledigt gilt die Aufgabe, wenn die Regressionstests bestehen und resolve sowie seine transitiven Abhängigkeiten entfernt wurden; die manuelle Überprüfung ist weiterhin als TODO markiert.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Bewertung
- Tech-Stack
- javascript, node.js
- Bereich
- backend, tooling
- Issue-Typ
- Refactoring
- Schwierigkeit
- 5/5
- Geschätzter Aufwand
- Über eine Woche
- Aktivitätsstatus
- Ruhig
- Klarheit
- Größtenteils klar
- Anfängerfreundlichkeit
- 35/100