[Bug]: external-directory-import: handles using ../ as external import (#4349) test fails on wasi target
Reproduction link or steps
- just build wasi release
- NAPI_RS_FORCE_WASI=1 pnpm --filter rollup-tests test -- -g external-directory-import
What is expected?
Pass like native targets
What is actually happening?
❯ NAPI_RS_FORCE_WASI=1 pnpm --filter rollup-tests test -- -g external-directory-import
packages/rolldown/npm/darwin-x64 | WARN Unsupported platform: wanted: {"cpu":["x64"],"os":["darwin"],"libc":["any"]} (current: {"os":"darwin","cpu":"arm64","libc":"unknown"})
packages/rolldown/npm/freebsd-x64 | WARN Unsupported platform: wanted: {"cpu":["x64"],"os":["freebsd"],"libc":["any"]} (current: {"os":"darwin","cpu":"arm64","libc":"unknown"})
.../rolldown/npm/linux-arm-gnueabihf | WARN Unsupported platform: wanted: {"cpu":["arm"],"os":["linux"],"libc":["any"]} (current: {"os":"darwin","cpu":"arm64","libc":"unknown"})
packages/rolldown/npm/linux-arm64-gnu | WARN Unsupported platform: wanted: {"cpu":["arm64"],"os":["linux"],"libc":["glibc"]} (current: {"os":"darwin","cpu":"arm64","libc":"unknown"})
packages/rolldown/npm/linux-arm64-musl | WARN Unsupported platform: wanted: {"cpu":["arm64"],"os":["linux"],"libc":["musl"]} (current: {"os":"darwin","cpu":"arm64","libc":"unknown"})
WARN 6 other warnings
> rollup-tests@ test /Users/brooklyn/workspace/github/rolldown/packages/rollup-tests
> cross-env ROLLDOWN_OPTIONS_VALIDATION=loose mocha --file ./src/intercept/check.js test/test.js "-g" "external-directory-import"
(node:65270) ExperimentalWarning: WASI is an experimental feature and might change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
(node:65270) ExperimentalWarning: WASI is an experimental feature and might change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
rollup
function
main.js
"use strict";
//#region rolldown:runtime
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
key = keys[i];
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
get: ((k) => from[k]).bind(null, key),
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
});
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
value: mod,
enumerable: true
}) : target, mod));
//#endregion
const __ = __toESM(require("."));
const ___ = __toESM(require("."));
const __$1 = __toESM(require("."));
const ___$1 = __toESM(require(".."));
const index_js = __toESM(require("./index.js"));
const ____index_js = __toESM(require("../index.js"));
//#region main.js
assert.strictEqual(__.default, ".");
assert.strictEqual(___.default, "..");
assert.strictEqual(__$1.default, ".");
assert.strictEqual(___$1.default, "..");
assert.strictEqual(index_js.default, "./index.js");
assert.strictEqual(____index_js.default, "../index.js");
//#endregion
1) external-directory-import: handles using ../ as external import (#4349)
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
'.' !== '..'
at Object.eval (eval at requireWithContext (test/function/index.js:21:21), <anonymous>:36:8)
at requireWithContext (test/function/index.js:22:13)
at /Users/brooklyn/workspace/github/rolldown/packages/rollup-tests/test/function/index.js:45:30
at runCodeSplitTest (test/function/index.js:63:42)
at /Users/brooklyn/workspace/github/rolldown/packages/rollup-tests/test/function/index.js:161:36 {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: '.',
expected: '..',
operator: 'strictEqual',
exports: {}
}
failures [
"rollup@function@external-directory-import: handles using ../ as external import (#4349)"
]
System Info
System:
OS: macOS 15.3.2
CPU: (16) arm64 Apple M3 Max
Memory: 21.16 GB / 128.00 GB
Shell: 5.9 - /bin/zsh
Binaries:
Node: 22.14.0 - /opt/homebrew/bin/node
npm: 10.9.2 - /opt/homebrew/bin/npm
pnpm: 9.15.4 - /opt/homebrew/bin/pnpm
bun: 1.2.5 - /opt/homebrew/bin/bun
Browsers:
Chrome: 134.0.6998.89
Chrome Canary: 136.0.7075.1
Safari: 18.3.1
npmPackages:
rolldown: workspace:* => 1.0.0-beta.6
Any additional comments?
No response
I digged a little while and here are my conclusions:
The issue is related to the function ExternalModule#get_import_path.
https://github.com/rolldown/rolldown/blob/8f5f8f11eb06c497eb50d907d7fd5258ad8fe4de/crates/rolldown_common/src/module/external_module.rs#L52
- When running wasi rolldown,
env::get_current_dirwould return\instead of the actual cwd. - This causes
ExternalModule#get_import_pathreturns.instead of.. ExternalModule#get_import_pathis ported from https://github.com/rollup/rollup/blob/ab7bfa8fe9c25e41cc62058fa2dcde6b321fd51d/src/utils/relativeId.ts#L23.
Code here
https://github.com/rolldown/rolldown/blob/8f5f8f11eb06c497eb50d907d7fd5258ad8fe4de/crates/rolldown_common/src/module/external_module.rs#L66
,for native rolldown, relative_path is ' '..'
,for wasi rolldown, relative_path is ''
these different result each cause a correct result '..' and a wrong result '.'.
The function/porting code is mainly to generate correct import path for chunk main.js who imported external module ...
Based on the test, the correct generated path should be ...
But even for https://github.com/rollup/rollup/blob/ab7bfa8fe9c25e41cc62058fa2dcde6b321fd51d/src/utils/relativeId.ts#L23 rollup itself, with forcefully settting cwd to /, it generates output ././.
Debugging PR: https://github.com/rolldown/rolldown/pull/3903.
Test Code
import { dirname, normalize, relative, basename } from 'path'
process.cwd = () => '/'
const UPPER_DIR_REGEX = /^(\.\.\/)*\.\.$/
export function getImportPath(
importerId,
targetPath,
stripJsExtension,
ensureFileName,
) {
while (targetPath.startsWith('../')) {
targetPath = targetPath.slice(3)
importerId = '_/' + importerId
}
let relativePath = normalize(relative(dirname(importerId), targetPath))
if (stripJsExtension && relativePath.endsWith('.js')) {
relativePath = relativePath.slice(0, -3)
}
if (ensureFileName) {
if (relativePath === '') return '../' + basename(targetPath)
if (UPPER_DIR_REGEX.test(relativePath)) {
return [...relativePath.split('/'), '..', basename(targetPath)].join('/')
}
}
return relativePath
? relativePath.startsWith('..')
? relativePath
: './' + relativePath
: '.'
}
console.log(getImportPath('main.js', '..', false, true))
So the thing becomes tricky now. Possible reasons:
rolldownorrollupcan't work under path/- Porting code
ExternalModule#get_import_pathis wrong - Original code of
ExternalModule#get_import_pathis wrong
And my idea of how to solve this issue would be:
- Comprehend the logic of computing import path of external modules completely
- Find out if this is a problem of working under
/or a problem of the algorithm that's computing import path - Give the fix.
However, this issue might not be solved quickly, @IWANABETHATGUY might need to decide whether we should enable full tests on wasi rolldown with ignoring this test. @underfin cc
conclusion: try to fix this issue in wasi target, if it is hard to fix we just ignore this test case in wasi target, since this only affects wasi target which is relatively rarely used.