node:module compatibility pt 1 (#18106)

This commit is contained in:
chloe caruso
2025-03-12 15:47:41 -07:00
committed by GitHub
parent d2ecce272c
commit 85376147a4
29 changed files with 483 additions and 159 deletions

View File

@@ -2,3 +2,7 @@
# https://github.com/oven-sh/bun/issues/16289 # https://github.com/oven-sh/bun/issues/16289
[test] [test]
preload = ["./test/js/node/harness.ts", "./test/preload.ts"] preload = ["./test/js/node/harness.ts", "./test/preload.ts"]
[install]
# Node.js never auto-installs modules.
auto = "disable"

View File

@@ -262,7 +262,7 @@ async function runTests() {
await runTest(title, async () => { await runTest(title, async () => {
const { ok, error, stdout } = await spawnBun(execPath, { const { ok, error, stdout } = await spawnBun(execPath, {
cwd: cwd, cwd: cwd,
args: [subcommand, "--config=./bunfig.node-test.toml", absoluteTestPath], args: [subcommand, "--config=" + join(import.meta.dirname, "../bunfig.node-test.toml"), absoluteTestPath],
timeout: getNodeParallelTestTimeout(title), timeout: getNodeParallelTestTimeout(title),
env: { env: {
FORCE_COLOR: "0", FORCE_COLOR: "0",

View File

@@ -2110,6 +2110,7 @@ static JSValue constructProcessConfigObject(VM& vm, JSObject* processObject)
JSC::JSObject* variables = JSC::constructEmptyObject(globalObject, globalObject->objectPrototype(), 2); JSC::JSObject* variables = JSC::constructEmptyObject(globalObject, globalObject->objectPrototype(), 2);
variables->putDirect(vm, JSC::Identifier::fromString(vm, "v8_enable_i8n_support"_s), JSC::jsNumber(1), 0); variables->putDirect(vm, JSC::Identifier::fromString(vm, "v8_enable_i8n_support"_s), JSC::jsNumber(1), 0);
variables->putDirect(vm, JSC::Identifier::fromString(vm, "enable_lto"_s), JSC::jsBoolean(false), 0); variables->putDirect(vm, JSC::Identifier::fromString(vm, "enable_lto"_s), JSC::jsBoolean(false), 0);
variables->putDirect(vm, JSC::Identifier::fromString(vm, "node_module_version"_s), JSC::jsNumber(REPORTED_NODEJS_ABI_VERSION), 0);
config->putDirect(vm, JSC::Identifier::fromString(vm, "target_defaults"_s), JSC::constructEmptyObject(globalObject), 0); config->putDirect(vm, JSC::Identifier::fromString(vm, "target_defaults"_s), JSC::constructEmptyObject(globalObject), 0);
config->putDirect(vm, JSC::Identifier::fromString(vm, "variables"_s), variables, 0); config->putDirect(vm, JSC::Identifier::fromString(vm, "variables"_s), variables, 0);

View File

@@ -384,8 +384,8 @@ JSC_DEFINE_CUSTOM_GETTER(getterParent, (JSC::JSGlobalObject * globalObject, JSC:
return JSValue::encode(jsUndefined()); return JSValue::encode(jsUndefined());
} }
if (thisObject->m_overridenParent) { if (thisObject->m_overriddenParent) {
return JSValue::encode(thisObject->m_overridenParent.get()); return JSValue::encode(thisObject->m_overriddenParent.get());
} }
if (thisObject->m_parent) { if (thisObject->m_parent) {
@@ -400,7 +400,7 @@ JSC_DEFINE_CUSTOM_GETTER(getterParent, (JSC::JSGlobalObject * globalObject, JSC:
auto id = idValue->value(globalObject); auto id = idValue->value(globalObject);
auto idStr = Bun::toString(id); auto idStr = Bun::toString(id);
if (Bun__isBunMain(globalObject, &idStr)) { if (Bun__isBunMain(globalObject, &idStr)) {
thisObject->m_overridenParent.set(globalObject->vm(), thisObject, jsNull()); thisObject->m_overriddenParent.set(globalObject->vm(), thisObject, jsNull());
return JSValue::encode(jsNull()); return JSValue::encode(jsNull());
} }
} }
@@ -494,10 +494,10 @@ JSC_DEFINE_CUSTOM_SETTER(setterParent,
if (auto* parent = jsDynamicCast<JSCommonJSModule*>(decodedValue)) { if (auto* parent = jsDynamicCast<JSCommonJSModule*>(decodedValue)) {
thisObject->m_parent = parent; thisObject->m_parent = parent;
thisObject->m_overridenParent.clear(); thisObject->m_overriddenParent.clear();
} else { } else {
thisObject->m_parent = {}; thisObject->m_parent = {};
thisObject->m_overridenParent.set(globalObject->vm(), thisObject, JSValue::decode(value)); thisObject->m_overriddenParent.set(globalObject->vm(), thisObject, JSValue::decode(value));
} }
return true; return true;
@@ -726,10 +726,10 @@ JSCommonJSModule* JSCommonJSModule::create(
if (auto* parentModule = jsDynamicCast<JSCommonJSModule*>(parent)) { if (auto* parentModule = jsDynamicCast<JSCommonJSModule*>(parent)) {
out->m_parent = JSC::Weak<JSCommonJSModule>(parentModule); out->m_parent = JSC::Weak<JSCommonJSModule>(parentModule);
} else { } else {
out->m_overridenParent.set(vm, out, parent); out->m_overriddenParent.set(vm, out, parent);
} }
} else if (parent) { } else if (parent) {
out->m_overridenParent.set(vm, out, parent); out->m_overriddenParent.set(vm, out, parent);
} }
return out; return out;
@@ -1022,7 +1022,7 @@ void JSCommonJSModule::visitChildrenImpl(JSCell* cell, Visitor& visitor)
visitor.appendHidden(thisObject->m_filename); visitor.appendHidden(thisObject->m_filename);
visitor.appendHidden(thisObject->m_dirname); visitor.appendHidden(thisObject->m_dirname);
visitor.appendHidden(thisObject->m_paths); visitor.appendHidden(thisObject->m_paths);
visitor.appendHidden(thisObject->m_overridenParent); visitor.appendHidden(thisObject->m_overriddenParent);
} }
DEFINE_VISIT_CHILDREN(JSCommonJSModule); DEFINE_VISIT_CHILDREN(JSCommonJSModule);
@@ -1061,8 +1061,8 @@ void JSCommonJSModule::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer)
} }
} }
if (thisObject->m_overridenParent) { if (thisObject->m_overriddenParent) {
JSValue overridenParent = thisObject->m_overridenParent.get(); JSValue overridenParent = thisObject->m_overriddenParent.get();
if (overridenParent.isCell()) { if (overridenParent.isCell()) {
const Identifier overridenParentIdentifier = Identifier::fromString(vm, "parent"_s); const Identifier overridenParentIdentifier = Identifier::fromString(vm, "parent"_s);
analyzer.analyzePropertyNameEdge(cell, overridenParent.asCell(), overridenParentIdentifier.impl()); analyzer.analyzePropertyNameEdge(cell, overridenParent.asCell(), overridenParentIdentifier.impl());

View File

@@ -46,7 +46,7 @@ public:
// //
// module.parent = parent; // module.parent = parent;
// //
mutable JSC::WriteBarrier<Unknown> m_overridenParent; mutable JSC::WriteBarrier<Unknown> m_overriddenParent;
// Not visited by the GC. // Not visited by the GC.
// When the module is assigned a JSCommonJSModule parent, it is assigned to this field. // When the module is assigned a JSCommonJSModule parent, it is assigned to this field.

View File

@@ -0,0 +1,8 @@
import { fn, t } from "bindgen";
export const _stat = fn({
args: {
str: t.UTF8String,
},
ret: t.i32,
});

View File

@@ -64,3 +64,12 @@ fn findPathInner(
); );
return errorable.unwrap() catch null; return errorable.unwrap() catch null;
} }
pub fn _stat(path: []const u8) i32 {
const exists = bun.sys.existsAtType(.cwd(), path).unwrap() catch
return -1; // Returns a negative integer for any other kind of strings.
return switch (exists) {
.file => 0, // Returns 0 for files.
.directory => 1, // Returns 1 for directories.
};
}

View File

@@ -86,9 +86,16 @@ namespace Bun {
bool isBuiltinModule(const String& namePossiblyWithNodePrefix) bool isBuiltinModule(const String& namePossiblyWithNodePrefix)
{ {
String name = namePossiblyWithNodePrefix; String name = namePossiblyWithNodePrefix;
if (name.startsWith("node:"_s)) if (name.startsWith("node:"_s)) {
name = name.substringSharingImpl(5); name = name.substringSharingImpl(5);
// bun doesn't have `node:test` as of writing, but this makes sure that
// `node:module` is compatible (`test/parallel/test-module-isBuiltin.js`)
if (name == "test"_s) {
return true;
}
}
for (auto& builtinModule : builtinModuleNamesSortedLength) { for (auto& builtinModule : builtinModuleNamesSortedLength) {
if (name == builtinModule) if (name == builtinModule)
return true; return true;

View File

@@ -1786,6 +1786,24 @@ pub const ModuleLoader = struct {
}; };
} }
if (parse_result.empty) {
const was_cjs = (loader == .js or loader == .ts) and brk: {
const ext = std.fs.path.extension(parse_result.source.path.text);
break :brk strings.eqlComptime(ext, ".cjs") or strings.eqlComptime(ext, ".cts");
};
if (was_cjs) {
return .{
.allocator = null,
.source_code = bun.String.static("(function(){})"),
.specifier = input_specifier,
.source_url = input_specifier.createIfDifferent(path.text),
.is_commonjs_module = true,
.hash = 0,
.tag = .javascript,
};
}
}
if (cache.entry) |*entry| { if (cache.entry) |*entry| {
jsc_vm.source_mappings.putMappings(parse_result.source, .{ jsc_vm.source_mappings.putMappings(parse_result.source, .{
.list = .{ .items = @constCast(entry.sourcemap), .capacity = entry.sourcemap.len }, .list = .{ .items = @constCast(entry.sourcemap), .capacity = entry.sourcemap.len },

View File

@@ -1,23 +1,21 @@
#include "root.h" #include "root.h"
#include "headers-handwritten.h"
#include "NodeModuleModule.h"
#include <JavaScriptCore/JSCInlines.h> #include <JavaScriptCore/JSCInlines.h>
#include <JavaScriptCore/VM.h> #include <JavaScriptCore/VM.h>
#include <JavaScriptCore/JSString.h> #include <JavaScriptCore/JSString.h>
#include <JavaScriptCore/FunctionPrototype.h>
#include "headers-handwritten.h" #include <JavaScriptCore/LazyPropertyInlines.h>
#include <JavaScriptCore/VMTrapsInlines.h>
#include "PathInlines.h" #include "PathInlines.h"
#include "ZigGlobalObject.h" #include "ZigGlobalObject.h"
#include "headers.h" #include "headers.h"
#include <JavaScriptCore/FunctionPrototype.h>
#include "NodeModuleModule.h"
#include "ErrorCode.h" #include "ErrorCode.h"
#include <JavaScriptCore/LazyPropertyInlines.h>
#include <JavaScriptCore/VMTrapsInlines.h> #include "GeneratedNodeModuleModule.h"
namespace Bun { namespace Bun {
using namespace JSC; using namespace JSC;
@@ -237,20 +235,22 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionNodeModuleCreateRequire,
"createRequire() requires at least one argument"_s); "createRequire() requires at least one argument"_s);
} }
auto val = callFrame->uncheckedArgument(0).toWTFString(globalObject); auto argument = callFrame->uncheckedArgument(0);
auto val = argument.toWTFString(globalObject);
RETURN_IF_EXCEPTION(scope, {}); RETURN_IF_EXCEPTION(scope, {});
if (val.startsWith("file://"_s)) { if (!isAbsolutePath(val)) {
WTF::URL url(val); WTF::URL url(val);
if (!url.isValid()) { if (!url.isValid()) {
throwTypeError(globalObject, scope, ERR::INVALID_ARG_VALUE(scope, globalObject,
makeString("createRequire() was given an invalid URL '"_s, "filename"_s, argument,
url.string(), "'"_s)); "must be a file URL object, file URL string, or absolute path string"_s);
RELEASE_AND_RETURN(scope, JSValue::encode({})); RELEASE_AND_RETURN(scope, JSValue::encode({}));
} }
if (!url.protocolIsFile()) { if (!url.protocolIsFile()) {
throwTypeError(globalObject, scope, ERR::INVALID_ARG_VALUE(scope, globalObject,
"createRequire() does not support non-file URLs"_s); "filename"_s, argument,
"must be a file URL object, file URL string, or absolute path string"_s);
RELEASE_AND_RETURN(scope, JSValue::encode({})); RELEASE_AND_RETURN(scope, JSValue::encode({}));
} }
val = url.fileSystemPath(); val = url.fileSystemPath();
@@ -708,6 +708,7 @@ _pathCache getPathCacheObject PropertyCallback
_preloadModules jsFunctionPreloadModules Function 0 _preloadModules jsFunctionPreloadModules Function 0
_resolveFilename nodeModuleResolveFilename CustomAccessor _resolveFilename nodeModuleResolveFilename CustomAccessor
_resolveLookupPaths jsFunctionResolveLookupPaths Function 2 _resolveLookupPaths jsFunctionResolveLookupPaths Function 2
_stat &Generated::NodeModuleModule::js_stat Function 1
builtinModules getBuiltinModulesObject PropertyCallback builtinModules getBuiltinModulesObject PropertyCallback
constants getConstantsObject PropertyCallback constants getConstantsObject PropertyCallback
createRequire jsFunctionNodeModuleCreateRequire Function 1 createRequire jsFunctionNodeModuleCreateRequire Function 1

View File

@@ -354,7 +354,7 @@ pub const Bunfig = struct {
} }
} }
if (comptime cmd.isNPMRelated() or cmd == .RunCommand or cmd == .AutoCommand) { if (comptime cmd.isNPMRelated() or cmd == .RunCommand or cmd == .AutoCommand or cmd == .TestCommand) {
if (json.getObject("install")) |install_obj| { if (json.getObject("install")) |install_obj| {
var install: *Api.BunInstall = this.ctx.install orelse brk: { var install: *Api.BunInstall = this.ctx.install orelse brk: {
const install = try this.allocator.create(Api.BunInstall); const install = try this.allocator.create(Api.BunInstall);

View File

@@ -877,6 +877,13 @@ pub const Resolver = struct {
// defer r.mutex.unlock(); // defer r.mutex.unlock();
errdefer (r.flushDebugLogs(.fail) catch {}); errdefer (r.flushDebugLogs(.fail) catch {});
// A path with a null byte cannot exist on the filesystem. Continuing
// anyways would cause assertion failures.
if (bun.strings.indexOfChar(import_path, 0) != null) {
r.flushDebugLogs(.fail) catch {};
return .{ .not_found = {} };
}
var tmp = r.resolveWithoutSymlinks(source_dir_normalized, import_path, kind, global_cache); var tmp = r.resolveWithoutSymlinks(source_dir_normalized, import_path, kind, global_cache);
// Fragments in URLs in CSS imports are technically expected to work // Fragments in URLs in CSS imports are technically expected to work
@@ -1108,6 +1115,24 @@ pub const Resolver = struct {
// Treating these paths as absolute paths on all platforms means Windows // Treating these paths as absolute paths on all platforms means Windows
// users will not be able to accidentally make use of these paths. // users will not be able to accidentally make use of these paths.
if (std.fs.path.isAbsolute(import_path)) { if (std.fs.path.isAbsolute(import_path)) {
// Collapse relative directory specifiers if they exist. Extremely
// loose check to avoid always doing this copy, but avoid spending
// too much time on the check.
if (bun.strings.indexOf(import_path, "..") != null) {
const platform = bun.path.Platform.auto;
const ends_with_dir = platform.isSeparator(import_path[import_path.len - 1]) or
(import_path.len > 3 and
platform.isSeparator(import_path[import_path.len - 3]) and
import_path[import_path.len - 2] == '.' and
import_path[import_path.len - 1] == '.');
const buf = bufs(.relative_abs_path);
import_path = r.fs.absBuf(&.{import_path}, buf);
if (ends_with_dir) {
buf[import_path.len] = platform.separator();
import_path.len += 1;
}
}
if (r.debug_logs) |*debug| { if (r.debug_logs) |*debug| {
debug.addNoteFmt("The import \"{s}\" is being treated as an absolute path", .{import_path}); debug.addNoteFmt("The import \"{s}\" is being treated as an absolute path", .{import_path});
} }
@@ -3380,89 +3405,65 @@ pub const Resolver = struct {
const in_str = try argument.toBunString(globalThis); const in_str = try argument.toBunString(globalThis);
defer in_str.deref(); defer in_str.deref();
const r = &globalThis.bunVM().transpiler.resolver; return nodeModulePathsJSValue(in_str, globalThis);
return nodeModulePathsJSValue(r, in_str, globalThis);
} }
pub export fn Resolver__propForRequireMainPaths(globalThis: *bun.JSC.JSGlobalObject) callconv(.C) JSC.JSValue { pub export fn Resolver__propForRequireMainPaths(globalThis: *bun.JSC.JSGlobalObject) callconv(.C) JSC.JSValue {
bun.JSC.markBinding(@src()); bun.JSC.markBinding(@src());
const in_str = bun.String.init("."); const in_str = bun.String.init(".");
const r = &globalThis.bunVM().transpiler.resolver; return nodeModulePathsJSValue(in_str, globalThis);
return nodeModulePathsJSValue(r, in_str, globalThis);
} }
pub fn nodeModulePathsJSValue( pub fn nodeModulePathsJSValue(in_str: bun.String, globalObject: *bun.JSC.JSGlobalObject) bun.JSC.JSValue {
r: *ThisResolver,
in_str: bun.String,
globalObject: *bun.JSC.JSGlobalObject,
) bun.JSC.JSValue {
var list = std.ArrayList(bun.String).init(bun.default_allocator);
defer list.deinit();
const sliced = in_str.toUTF8(bun.default_allocator);
defer sliced.deinit();
const str = brk: {
if (std.fs.path.isAbsolute(sliced.slice())) {
if (comptime Environment.isWindows) {
const dir_path_buf = bufs(.node_modules_paths_buf);
var normalizer = bun.path.PosixToWinNormalizer{};
const normalized = normalizer.resolveCWD(sliced.slice()) catch {
@panic("Failed to get cwd for _nodeModulesPaths");
};
break :brk bun.path.normalizeBuf(normalized, dir_path_buf, .windows);
}
break :brk sliced.slice();
}
const dir_path_buf = bufs(.node_modules_paths_buf);
break :brk bun.path.joinStringBuf(dir_path_buf, &[_]string{ r.fs.top_level_dir, sliced.slice() }, .auto);
};
var arena = std.heap.ArenaAllocator.init(bun.default_allocator); var arena = std.heap.ArenaAllocator.init(bun.default_allocator);
defer arena.deinit(); defer arena.deinit();
var stack_fallback_allocator = std.heap.stackFallback(1024, arena.allocator()); var stack_fallback_allocator = std.heap.stackFallback(1024, arena.allocator());
const alloc = stack_fallback_allocator.get(); const alloc = stack_fallback_allocator.get();
if (r.readDirInfo(str) catch null) |result| { var list = std.ArrayList(bun.String).init(alloc);
var dir_info = result; defer list.deinit();
while (true) { const sliced = in_str.toUTF8(bun.default_allocator);
const path_without_trailing_slash = strings.withoutTrailingSlash(dir_info.abs_path); defer sliced.deinit();
const path_parts = brk: { const buf = bufs(.node_modules_paths_buf);
if (path_without_trailing_slash.len == 1 and path_without_trailing_slash[0] == '/') {
break :brk [2]string{ "", std.fs.path.sep_str ++ "node_modules" };
}
break :brk [2]string{ path_without_trailing_slash, std.fs.path.sep_str ++ "node_modules" }; const full_path = bun.path.joinAbsStringBuf(
}; bun.fs.FileSystem.instance.top_level_dir,
const nodemodules_path = bun.strings.concat(alloc, &path_parts) catch unreachable; buf,
bun.path.posixToPlatformInPlace(u8, nodemodules_path); &.{sliced.slice()},
list.append(bun.String.createUTF8(nodemodules_path)) catch unreachable; .auto,
dir_info = (r.readDirInfo(std.fs.path.dirname(path_without_trailing_slash) orelse break) catch null) orelse break; );
} const root_index = switch (bun.Environment.os) {
} else { .windows => bun.path.windowsFilesystemRoot(full_path).len,
// does not exist else => 1,
const full_path = std.fs.path.resolve(r.allocator, &[1][]const u8{str}) catch unreachable; };
var path = strings.withoutTrailingSlash(full_path); var root_path: []const u8 = full_path[0..root_index];
while (true) { if (full_path.len > root_path.len) {
const path_without_trailing_slash = strings.withoutTrailingSlash(path); var it = std.mem.splitBackwardsScalar(u8, full_path[root_index..], std.fs.path.sep);
while (it.next()) |part| {
if (strings.eqlComptime(part, "node_modules"))
continue;
list.append( list.append(bun.String.createFormat(
bun.String.createUTF8( "{s}{s}" ++ std.fs.path.sep_str ++ "node_modules",
bun.strings.concat( .{
alloc, root_path,
&[_]string{ it.buffer[0 .. (if (it.index) |i| i + 1 else 0) + part.len],
path_without_trailing_slash, },
std.fs.path.sep_str ++ "node_modules", ) catch bun.outOfMemory()) catch bun.outOfMemory();
},
) catch unreachable,
),
) catch unreachable;
path = path[0 .. strings.lastIndexOfChar(path, std.fs.path.sep) orelse break];
} }
} }
while (root_path.len > 0 and bun.path.Platform.auto.isSeparator(root_path[root_path.len - 1])) {
root_path.len -= 1;
}
list.append(bun.String.createFormat(
"{s}" ++ std.fs.path.sep_str ++ "node_modules",
.{root_path},
) catch bun.outOfMemory()) catch bun.outOfMemory();
return bun.String.toJSArray(globalObject, list.items); return bun.String.toJSArray(globalObject, list.items);
} }

View File

@@ -3367,7 +3367,7 @@ pub fn existsAtType(fd: bun.FileDescriptor, subpath: anytype) Maybe(ExistsAtType
if (std.meta.sentinel(@TypeOf(subpath)) == null) { if (std.meta.sentinel(@TypeOf(subpath)) == null) {
const path_buf = bun.PathBufferPool.get(); const path_buf = bun.PathBufferPool.get();
defer bun.PathBufferPool.put(path_buf); defer bun.PathBufferPool.put(path_buf);
@memcpy(path_buf, subpath); @memcpy(path_buf[0..subpath.len], subpath);
path_buf[subpath.len] = 0; path_buf[subpath.len] = 0;
const slice: [:0]const u8 = @ptrCast(path_buf); const slice: [:0]const u8 = @ptrCast(path_buf);
return existsAtType(fd, slice); return existsAtType(fd, slice);

View File

@@ -308,6 +308,7 @@ it("process.config", () => {
expect(process.config).toEqual({ expect(process.config).toEqual({
variables: { variables: {
enable_lto: false, enable_lto: false,
node_module_version: expect.any(Number),
v8_enable_i8n_support: 1, v8_enable_i8n_support: 1,
}, },
target_defaults: {}, target_defaults: {},

View File

@@ -1,61 +0,0 @@
import path from "path";
import fs from "fs";
import { spawn } from "child_process";
const localDir = path.resolve(import.meta.dirname, "./parallel");
const upstreamDir = path.resolve(import.meta.dirname, "../../../node.js/upstream/test/parallel");
const localFiles = fs.readdirSync(localDir);
const upstreamFiles = fs.readdirSync(upstreamDir);
const newFiles = upstreamFiles.filter((file) => !localFiles.includes(file));
process.on('SIGTERM', () => {
console.log("SIGTERM received");
});
process.on('SIGINT', () => {
console.log("SIGINT received");
});
const stdin = process.stdin;
if (stdin.isTTY) {
stdin.setRawMode(true);
stdin.on('data', (data) => {
if (data[0] === 0x03) {
stdin.setRawMode(false);
console.log("Cancelled");
process.exit(0);
}
});
}
process.on('exit', () => {
if (stdin.isTTY) {
stdin.setRawMode(false);
}
});
for (const file of newFiles) {
await new Promise<void>((resolve, reject) => {
// Run with a timeout of 5 seconds
const proc = spawn("bun-debug", ["run", path.join(upstreamDir, file)], {
timeout: 5000,
stdio: "inherit",
env: {
...process.env,
BUN_DEBUG_QUIET_LOGS: "1",
},
});
proc.on("error", (err) => {
console.error(err);
});
proc.on("exit", (code) => {
if (code === 0) {
console.log(`New Pass: ${file}`);
fs.appendFileSync("new-passes.txt", file + "\n");
}
resolve();
});
});
}

View File

@@ -5,10 +5,10 @@ const dgram = require('dgram');
const dns = require('dns'); const dns = require('dns');
if (typeof Bun !== 'undefined') { if (typeof Bun !== 'undefined') {
if (process.platform === 'win32' && require('harness').isCI) { if (process.platform === 'win32' && require('../../../../harness').isCI) {
// TODO(@heimskr): This test mysteriously takes forever in Windows in CI // TODO(@heimskr): This test mysteriously takes forever in Windows in CI
// possibly due to UDP keeping the event loop alive longer than it should. // possibly due to UDP keeping the event loop alive longer than it should.
process.exit(0); common.skip('Windows CI is flaky');
} }
} }

View File

@@ -0,0 +1,32 @@
'use strict';
require('../common');
const fixtures = require('../common/fixtures');
const assert = require('assert');
const { createRequire } = require('module');
const u = fixtures.fileURL('fake.js');
const reqToo = createRequire(u);
assert.deepStrictEqual(reqToo('./experimental'), { ofLife: 42 });
assert.throws(() => {
createRequire('https://github.com/nodejs/node/pull/27405/');
}, {
code: 'ERR_INVALID_ARG_VALUE'
});
assert.throws(() => {
createRequire('../');
}, {
code: 'ERR_INVALID_ARG_VALUE'
});
assert.throws(() => {
createRequire({});
}, {
code: 'ERR_INVALID_ARG_VALUE',
message: 'The argument \'filename\' must be a file URL object, file URL ' +
'string, or absolute path string. Received {}'
});

View File

@@ -0,0 +1,16 @@
'use strict';
require('../common');
const assert = require('assert');
const { isBuiltin } = require('module');
// Includes modules in lib/ (even deprecated ones)
assert(isBuiltin('http'));
assert(isBuiltin('sys'));
assert(isBuiltin('node:fs'));
assert(isBuiltin('node:test'));
// Does not include internal modules
assert(!isBuiltin('internal/errors'));
assert(!isBuiltin('test'));
assert(!isBuiltin(''));
assert(!isBuiltin(undefined));

View File

@@ -0,0 +1,10 @@
'use strict';
const common = require('../common');
const assert = require('assert');
// common.expectWarning('DeprecationWarning', {
// DEP0128: /^Invalid 'main' field in '.+main[/\\]package\.json' of 'doesnotexist\.js'\..+module author/
// });
assert.strictEqual(require('../fixtures/packages/missing-main').ok, 'ok');

View File

@@ -0,0 +1,127 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';
const common = require('../common');
const assert = require('assert');
const _module = require('module');
const cases = {
'WIN': [{
file: 'C:\\Users\\hefangshi\\AppData\\Roaming\
\\npm\\node_modules\\npm\\node_modules\\minimatch',
expect: [
'C:\\Users\\hefangshi\\AppData\\Roaming\
\\npm\\node_modules\\npm\\node_modules\\minimatch\\node_modules',
'C:\\Users\\hefangshi\\AppData\\Roaming\
\\npm\\node_modules\\npm\\node_modules',
'C:\\Users\\hefangshi\\AppData\\Roaming\\npm\\node_modules',
'C:\\Users\\hefangshi\\AppData\\Roaming\\node_modules',
'C:\\Users\\hefangshi\\AppData\\node_modules',
'C:\\Users\\hefangshi\\node_modules',
'C:\\Users\\node_modules',
'C:\\node_modules',
]
}, {
file: 'C:\\Users\\Rocko Artischocko\\node_stuff\\foo',
expect: [
'C:\\Users\\Rocko Artischocko\\node_stuff\\foo\\node_modules',
'C:\\Users\\Rocko Artischocko\\node_stuff\\node_modules',
'C:\\Users\\Rocko Artischocko\\node_modules',
'C:\\Users\\node_modules',
'C:\\node_modules',
]
}, {
file: 'C:\\Users\\Rocko Artischocko\\node_stuff\\foo_node_modules',
expect: [
'C:\\Users\\Rocko \
Artischocko\\node_stuff\\foo_node_modules\\node_modules',
'C:\\Users\\Rocko Artischocko\\node_stuff\\node_modules',
'C:\\Users\\Rocko Artischocko\\node_modules',
'C:\\Users\\node_modules',
'C:\\node_modules',
]
}, {
file: 'C:\\node_modules',
expect: [
'C:\\node_modules',
]
}, {
file: 'C:\\',
expect: [
'C:\\node_modules',
]
}],
'POSIX': [{
file: '/usr/lib/node_modules/npm/node_modules/\
node-gyp/node_modules/glob/node_modules/minimatch',
expect: [
'/usr/lib/node_modules/npm/node_modules/\
node-gyp/node_modules/glob/node_modules/minimatch/node_modules',
'/usr/lib/node_modules/npm/node_modules/\
node-gyp/node_modules/glob/node_modules',
'/usr/lib/node_modules/npm/node_modules/node-gyp/node_modules',
'/usr/lib/node_modules/npm/node_modules',
'/usr/lib/node_modules',
'/usr/node_modules',
'/node_modules',
]
}, {
file: '/usr/test/lib/node_modules/npm/foo',
expect: [
'/usr/test/lib/node_modules/npm/foo/node_modules',
'/usr/test/lib/node_modules/npm/node_modules',
'/usr/test/lib/node_modules',
'/usr/test/node_modules',
'/usr/node_modules',
'/node_modules',
]
}, {
file: '/usr/test/lib/node_modules/npm/foo_node_modules',
expect: [
'/usr/test/lib/node_modules/npm/foo_node_modules/node_modules',
'/usr/test/lib/node_modules/npm/node_modules',
'/usr/test/lib/node_modules',
'/usr/test/node_modules',
'/usr/node_modules',
'/node_modules',
]
}, {
file: '/node_modules',
expect: [
'/node_modules',
]
}, {
file: '/',
expect: [
'/node_modules',
]
}]
};
const platformCases = common.isWindows ? cases.WIN : cases.POSIX;
platformCases.forEach((c) => {
const paths = _module._nodeModulePaths(c.file);
assert.deepStrictEqual(
c.expect, paths,
`case ${c.file} failed, actual paths is ${JSON.stringify(paths)}`);
});

View File

@@ -0,0 +1,14 @@
// Flags: --pending-deprecation
'use strict';
const common = require('../common');
const assert = require('assert');
// common.expectWarning(
// 'DeprecationWarning',
// 'module.parent is deprecated due to accuracy issues. Please use ' +
// 'require.main to find program entry point instead.',
// 'DEP0144'
// );
assert.strictEqual(module.parent, null);

View File

@@ -0,0 +1,13 @@
// Flags: --pending-deprecation
'use strict';
const common = require('../common');
// common.expectWarning(
// 'DeprecationWarning',
// 'module.parent is deprecated due to accuracy issues. Please use ' +
// 'require.main to find program entry point instead.',
// 'DEP0144'
// );
module.parent = undefined;

View File

@@ -0,0 +1,24 @@
'use strict';
require('../common');
// This tests Module._stat.
const Module = require('module');
const fs = require('fs');
const tmpdir = require('../common/tmpdir');
const { ok, strictEqual } = require('assert');
const directory = tmpdir.resolve('directory');
const doesNotExist = tmpdir.resolve('does-not-exist');
const file = tmpdir.resolve('file.js');
tmpdir.refresh();
fs.writeFileSync(file, "module.exports = { a: 'b' }");
fs.mkdirSync(directory);
strictEqual(Module._stat(directory), 1); // Returns 1 for directories.
strictEqual(Module._stat(file), 0); // Returns 0 for files.
ok(Module._stat(doesNotExist) < 0); // Returns a negative integer for any other kind of strings.
// TODO(RaisinTen): Add tests that make sure that Module._stat() does not crash when called
// with a non-string data type. It crashes currently.

View File

@@ -0,0 +1,10 @@
'use strict';
require('../common');
const assert = require('assert');
// check for existence
assert(Object.hasOwn(process.config.variables, 'node_module_version'));
// Ensure that `node_module_version` is an Integer > 0
assert(Number.isInteger(process.config.variables.node_module_version));
assert(process.config.variables.node_module_version > 0);

View File

@@ -0,0 +1,34 @@
'use strict';
const common = require('../common');
const tmpdir = require('../common/tmpdir');
const assert = require('assert');
const fs = require('fs');
const path = require('path');
tmpdir.refresh();
const fooPath = tmpdir.resolve('foo.cjs');
fs.writeFileSync(fooPath, '');
const dirPath = tmpdir.resolve('delete_me');
fs.mkdirSync(dirPath, {
recursive: true,
});
const barPath = path.join(dirPath, 'bar.cjs');
fs.writeFileSync(
barPath,
`
module.exports = () => require('../foo.cjs').call()
`
);
const foo = require(fooPath);
console.log('fooPath', fooPath, foo);
const unique = Symbol('unique');
foo.call = common.mustCall(() => unique);
const bar = require(barPath);
fs.rmSync(dirPath, { recursive: true });
assert.strict.equal(bar(), unique);

View File

@@ -0,0 +1,30 @@
'use strict';
// Fixes regression from v4
require('../common');
const assert = require('assert');
const fixtures = require('../common/fixtures');
const path = require('path');
const fixturesRequire = require(
fixtures.path('module-extension-over-directory', 'inner'));
assert.strictEqual(
fixturesRequire,
require(fixtures.path('module-extension-over-directory', 'inner.js')),
'test-require-extension-over-directory failed to import fixture' +
' requirements'
);
const fakePath = [
fixtures.path('module-extension-over-directory', 'inner'),
'fake',
'..',
].join(path.sep);
const fixturesRequireDir = require(fakePath);
assert.strictEqual(
fixturesRequireDir,
require(fixtures.path('module-extension-over-directory', 'inner/')),
'test-require-extension-over-directory failed to import fixture' +
' requirements'
);

View File

@@ -0,0 +1,9 @@
'use strict';
require('../common');
const assert = require('assert');
// Should be an invalid package path.
assert.throws(() => require('package.json'),
{ code: 'MODULE_NOT_FOUND' }
);

View File

@@ -0,0 +1,11 @@
'use strict';
require('../common');
const assert = require('assert');
// Nul bytes should throw, not abort.
/* eslint-disable no-control-regex */
assert.throws(() => require('\u0000ab'), /'\u0000ab'/);
assert.throws(() => require('a\u0000b'), /'a\u0000b'/);
assert.throws(() => require('ab\u0000'), /'ab\u0000'/);
/* eslint-enable no-control-regex */

View File

@@ -0,0 +1,5 @@
{
"compilerOptions": {
"baseUrl": null,
}
}