Skip to content

Commit 89ba466

Browse files
committed
feat: support runtime fallback for webcontainer
1 parent ca32760 commit 89ba466

File tree

14 files changed

+244
-132
lines changed

14 files changed

+244
-132
lines changed

.github/workflows/release-napi.yml

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ on:
88
branches:
99
- main
1010
paths:
11-
- npm/package.json # Please only commit this file, so we don't need to wait for test CI to pass.
11+
- package.json
1212

1313
env:
1414
DEBUG: napi:*
@@ -32,7 +32,7 @@ jobs:
3232
with:
3333
static-checking: localIsNew
3434
file-url: https://unpkg.com/unrs-resolver@latest/package.json
35-
file-name: npm/package.json
35+
file-name: package.json
3636

3737
- name: Set version name
3838
if: steps.version.outputs.changed == 'true'
@@ -102,7 +102,7 @@ jobs:
102102
target: wasm32-wasip1-threads
103103
build: pnpm build
104104

105-
name: Package ${{ matrix.target }}
105+
name: Build ${{ matrix.target }}
106106
runs-on: ${{ matrix.os }}
107107
steps:
108108
- uses: taiki-e/checkout-action@b13d20b7cda4e2f325ef19895128f7ff735c0b3d # v1.3.1
@@ -148,10 +148,10 @@ jobs:
148148
RUSTUP_IO_THREADS: 1
149149
with:
150150
operating_system: freebsd
151-
version: "14.2"
151+
version: 14.2
152152
memory: 8G
153153
cpu_count: 3
154-
environment_variables: "DEBUG RUSTUP_IO_THREADS"
154+
environment_variables: DEBUG RUSTUP_IO_THREADS
155155
shell: bash
156156
run: |
157157
sudo pkg install -y -f curl libnghttp2 node22 npm cmake
@@ -211,18 +211,15 @@ jobs:
211211

212212
- name: Prepare dirs and artifacts
213213
run: |
214-
pnpm napi create-npm-dirs --package-json-path npm/package.json
215-
pnpm napi artifacts --package-json-path npm/package.json --build-output-dir napi
214+
cp package.json napi/package.json
215+
pnpm napi create-npm-dirs
216+
pnpm napi artifacts --npm-dir npm --build-output-dir napi
216217
217218
- name: Publish npm packages as latest
218219
env:
219220
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
220221
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
221-
shell: bash
222222
run: |
223-
cp napi/index.js npm/index.js
224-
cp napi/index.d.ts npm/index.d.ts
225-
cp napi/browser.js npm/browser.js
226223
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc
227-
pnpm napi pre-publish --no-gh-release -t npm --package-json-path npm/package.json
228-
npm publish npm/ --tag latest --provenance --access public
224+
pnpm napi pre-publish --no-gh-release --tagstyle npm --npm-dir npm
225+
npm publish napi/ --tag latest --provenance --access public

.github/workflows/release-plz.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ jobs:
4747
if [[ -n "$pr_number" ]]; then
4848
version="${VERSION}"
4949
50-
jq --arg version "${version}" '.version = ($version) | .scripts.postinstall = "napi-postinstall unrs-resolver \($version) check"' npm/package.json > tmp
51-
mv tmp npm/package.json
50+
jq --arg version "${version}" '.version = ($version) | .scripts.postinstall = "napi-postinstall unrs-resolver \($version) check"' package.json > tmp
51+
mv tmp package.json
5252
5353
gh pr checkout $pr_number
5454
git add .

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
target/
2+
/npm
23
/node_modules
34
fuzz/Cargo.lock

.prettierrc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"semi": false,
3+
"singleQuote": true,
4+
"plugins": ["prettier-plugin-pkg"]
5+
}

README.md

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,52 @@ Rust port of [enhanced-resolve].
5555

5656
## Usage
5757

58+
### npm package
59+
60+
See `index.d.ts` for `resolveSync` and `ResolverFactory` API.
61+
62+
Quick example:
63+
64+
```javascript
65+
import assert from 'node:assert';
66+
import path from 'node:path';
67+
68+
import resolve, { ResolverFactory } from 'unrs-resolver';
69+
70+
// `resolve`
71+
assert(resolve.sync(process.cwd(), './index.js').path, path.join(cwd, 'index.js'));
72+
73+
// `ResolverFactory`
74+
const resolver = new ResolverFactory();
75+
assert(resolver.sync(process.cwd(), './index.js').path, path.join(cwd, 'index.js'));
76+
```
77+
78+
### Rust
79+
80+
See [docs.rs/unrs_resolver](https://docs.rs/unrs_resolver/latest/unrs_resolver/).
81+
82+
## Terminology
83+
84+
### `directory`
85+
86+
An **absolute** path to a directory where the specifier is resolved against.
87+
88+
For CommonJS modules, it is the `__dirname` variable that contains the absolute path to the folder containing current module.
89+
90+
For ECMAScript modules, it is the value of `import.meta.url`.
91+
92+
Behavior is undefined when given a path to a file.
93+
94+
### `specifier`
95+
96+
The string passed to `require` or `import`, i.e. `require("specifier")` or `import "specifier"`
97+
98+
## Errors and Trouble Shooting
99+
100+
- `Error: Package subpath '.' is not defined by "exports" in` - occurs when resolving without `conditionNames`.
101+
102+
## Configuration
103+
58104
The following usages apply to both Rust and Node.js; the code snippets are written in JavaScript.
59105

60106
To handle the `exports` field in `package.json`, ESM and CJS need to be differentiated.
@@ -132,10 +178,6 @@ Quoting esbuild's documentation:
132178
- `module` - This field came from a [proposal](https://github.com/dherman/defense-of-dot-js/blob/f31319be735b21739756b87d551f6711bd7aa283/proposal.md) for how to integrate ECMAScript modules into node. Because of this, it's reasonable to expect that the file path in this field is an ECMAScript-style module. This proposal wasn't adopted by node (node uses "type": "module" instead) but it was adopted by major bundlers because ECMAScript-style modules lead to better tree shaking, or dead code removal.
133179
- `browser` - This field came from a [proposal](https://gist.github.com/defunctzombie/4339901/49493836fb873ddaa4b8a7aa0ef2352119f69211) that allows bundlers to replace node-specific files or modules with their browser-friendly versions. It lets you specify an alternate browser-specific entry point. Note that it is possible for a package to use both the browser and module field together (see the note below).
134180

135-
## Errors & Trouble Shooting
136-
137-
- `Error: Package subpath '.' is not defined by "exports" in` - occurs when resolving without `conditionNames`.
138-
139181
## Options
140182

141183
The following options are aligned with [enhanced-resolve], and is implemented for Rust crate usage.

napi/fallback.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
const { execFileSync } = require('node:child_process')
2+
3+
const pkg = require('unrs-resolver/package.json')
4+
5+
const userAgent =
6+
(process.env.npm_config_user_agent || '').split('/')[0] || 'npm'
7+
8+
const EXECUTORS = {
9+
npm: 'npx',
10+
pnpm: 'pnpm',
11+
yarn: 'yarn',
12+
bun: 'bun',
13+
deno: (args) => ['deno', 'run', `npm:${args[0]}`, ...args.slice(1)],
14+
}
15+
16+
const executor = EXECUTORS[userAgent]
17+
18+
if (!executor) {
19+
console.error(
20+
`Unsupported package manager: ${userAgent}. Supported managers are: ${Object.keys(
21+
EXECUTORS,
22+
).join(', ')}.`,
23+
)
24+
return
25+
}
26+
27+
function constructCommand(value, args) {
28+
const list = typeof value === 'function' ? value(args) : [value].concat(args)
29+
return {
30+
command: list[0],
31+
args: list.slice(1),
32+
}
33+
}
34+
35+
const { command, args } = constructCommand(executor, [
36+
'napi-postinstall',
37+
'unrs-resolver',
38+
pkg.version,
39+
'check',
40+
])
41+
42+
execFileSync(command, args, {
43+
cwd: __dirname,
44+
stdio: 'inherit',
45+
})
46+
47+
process.env.SKIP_UNRS_RESOLVER_FALLBACK = '1'
48+
49+
const UNRS_RESOLVER_PATH = require.resolve('unrs-resolver')
50+
51+
delete require.cache[UNRS_RESOLVER_PATH]
52+
53+
module.exports = require('unrs-resolver')

napi/index.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,14 @@ if (!nativeBinding || process.env.NAPI_RS_FORCE_WASI) {
363363
}
364364
}
365365

366+
if (!nativeBinding && process.env.SKIP_UNRS_RESOLVER_FALLBACK !== '1') {
367+
try {
368+
nativeBinding = require('./fallback.js');
369+
} catch (err) {
370+
loadErrors.push(err)
371+
}
372+
}
373+
366374
if (!nativeBinding) {
367375
if (loadErrors.length > 0) {
368376
// TODO Link to documentation with potential fixes

napi/patch.mjs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import fs from 'node:fs'
2+
3+
const fileUrl = new URL('index.js', import.meta.url)
4+
5+
let data = fs.readFileSync(fileUrl, 'utf-8')
6+
7+
data = data.replace(
8+
'\nif (!nativeBinding) {',
9+
(value) =>
10+
`
11+
if (!nativeBinding && process.env.SKIP_UNRS_RESOLVER_FALLBACK !== '1') {
12+
try {
13+
nativeBinding = require('./fallback.js');
14+
} catch (err) {
15+
loadErrors.push(err)
16+
}
17+
}
18+
` + value,
19+
)
20+
21+
fs.writeFileSync(fileUrl, data)

npm/.gitignore

Lines changed: 0 additions & 4 deletions
This file was deleted.

npm/README.md

Lines changed: 0 additions & 39 deletions
This file was deleted.

0 commit comments

Comments
 (0)