mirror of
https://github.com/denoland/deno.git
synced 2025-01-12 09:03:42 -05:00
feat: add ext/node for require support (#15362)
This commit adds "ext/node" extension that implementes CommonJS module system. In the future this extension might be extended to actually contain implementation of Node compatibility layer in favor of "deno_std/node". Currently this functionality is not publicly exposed, it is available via "Deno[Deno.internal].require" namespace and is meant to be used by other functionality to be landed soon. This is a minimal first pass, things that still don't work: support for dynamic imports in CJS conditional exports
This commit is contained in:
parent
af618e3b8f
commit
1f54d87789
27 changed files with 1648 additions and 0 deletions
9
Cargo.lock
generated
9
Cargo.lock
generated
|
@ -801,6 +801,7 @@ dependencies = [
|
||||||
"deno_graph",
|
"deno_graph",
|
||||||
"deno_lint",
|
"deno_lint",
|
||||||
"deno_net",
|
"deno_net",
|
||||||
|
"deno_node",
|
||||||
"deno_runtime",
|
"deno_runtime",
|
||||||
"deno_task_shell",
|
"deno_task_shell",
|
||||||
"deno_url",
|
"deno_url",
|
||||||
|
@ -1114,6 +1115,13 @@ dependencies = [
|
||||||
"trust-dns-resolver",
|
"trust-dns-resolver",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "deno_node"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"deno_core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deno_ops"
|
name = "deno_ops"
|
||||||
version = "0.24.0"
|
version = "0.24.0"
|
||||||
|
@ -1139,6 +1147,7 @@ dependencies = [
|
||||||
"deno_ffi",
|
"deno_ffi",
|
||||||
"deno_http",
|
"deno_http",
|
||||||
"deno_net",
|
"deno_net",
|
||||||
|
"deno_node",
|
||||||
"deno_tls",
|
"deno_tls",
|
||||||
"deno_url",
|
"deno_url",
|
||||||
"deno_web",
|
"deno_web",
|
||||||
|
|
|
@ -31,6 +31,7 @@ deno_core = { version = "0.146.0", path = "../core" }
|
||||||
deno_crypto = { version = "0.78.0", path = "../ext/crypto" }
|
deno_crypto = { version = "0.78.0", path = "../ext/crypto" }
|
||||||
deno_fetch = { version = "0.87.0", path = "../ext/fetch" }
|
deno_fetch = { version = "0.87.0", path = "../ext/fetch" }
|
||||||
deno_net = { version = "0.56.0", path = "../ext/net" }
|
deno_net = { version = "0.56.0", path = "../ext/net" }
|
||||||
|
deno_node = { version = "0.1.0", path = "../ext/node" }
|
||||||
deno_url = { version = "0.64.0", path = "../ext/url" }
|
deno_url = { version = "0.64.0", path = "../ext/url" }
|
||||||
deno_web = { version = "0.95.0", path = "../ext/web" }
|
deno_web = { version = "0.95.0", path = "../ext/web" }
|
||||||
deno_webgpu = { version = "0.65.0", path = "../ext/webgpu" }
|
deno_webgpu = { version = "0.65.0", path = "../ext/webgpu" }
|
||||||
|
|
|
@ -75,12 +75,18 @@ static NODE_COMPAT_URL: Lazy<String> = Lazy::new(|| {
|
||||||
static GLOBAL_URL_STR: Lazy<String> =
|
static GLOBAL_URL_STR: Lazy<String> =
|
||||||
Lazy::new(|| format!("{}node/global.ts", NODE_COMPAT_URL.as_str()));
|
Lazy::new(|| format!("{}node/global.ts", NODE_COMPAT_URL.as_str()));
|
||||||
|
|
||||||
|
static PROCESS_URL_STR: Lazy<String> =
|
||||||
|
Lazy::new(|| format!("{}node/process.ts", NODE_COMPAT_URL.as_str()));
|
||||||
|
|
||||||
pub static GLOBAL_URL: Lazy<Url> =
|
pub static GLOBAL_URL: Lazy<Url> =
|
||||||
Lazy::new(|| Url::parse(&GLOBAL_URL_STR).unwrap());
|
Lazy::new(|| Url::parse(&GLOBAL_URL_STR).unwrap());
|
||||||
|
|
||||||
static MODULE_URL_STR: Lazy<String> =
|
static MODULE_URL_STR: Lazy<String> =
|
||||||
Lazy::new(|| format!("{}node/module.ts", NODE_COMPAT_URL.as_str()));
|
Lazy::new(|| format!("{}node/module.ts", NODE_COMPAT_URL.as_str()));
|
||||||
|
|
||||||
|
static MODULE_ALL_URL_STR: Lazy<String> =
|
||||||
|
Lazy::new(|| format!("{}node/module_all.ts", NODE_COMPAT_URL.as_str()));
|
||||||
|
|
||||||
pub static MODULE_URL: Lazy<Url> =
|
pub static MODULE_URL: Lazy<Url> =
|
||||||
Lazy::new(|| Url::parse(&MODULE_URL_STR).unwrap());
|
Lazy::new(|| Url::parse(&MODULE_URL_STR).unwrap());
|
||||||
|
|
||||||
|
@ -106,6 +112,46 @@ fn try_resolve_builtin_module(specifier: &str) -> Option<Url> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub async fn load_builtin_node_modules(
|
||||||
|
js_runtime: &mut JsRuntime,
|
||||||
|
) -> Result<(), AnyError> {
|
||||||
|
let source_code = &format!(
|
||||||
|
r#"(async function loadBuiltinNodeModules(moduleAllUrl, processUrl) {{
|
||||||
|
const [moduleAll, processModule] = await Promise.all([
|
||||||
|
import(moduleAllUrl),
|
||||||
|
import(processUrl)
|
||||||
|
]);
|
||||||
|
Deno[Deno.internal].require.initializeCommonJs(moduleAll.default, processModule.default);
|
||||||
|
}})('{}', '{}');"#,
|
||||||
|
MODULE_ALL_URL_STR.as_str(),
|
||||||
|
PROCESS_URL_STR.as_str(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let value =
|
||||||
|
js_runtime.execute_script(&located_script_name!(), source_code)?;
|
||||||
|
js_runtime.resolve_value(value).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub fn load_cjs_module_from_ext_node(
|
||||||
|
js_runtime: &mut JsRuntime,
|
||||||
|
module: &str,
|
||||||
|
main: bool,
|
||||||
|
) -> Result<(), AnyError> {
|
||||||
|
let source_code = &format!(
|
||||||
|
r#"(function loadCjsModule(module) {{
|
||||||
|
Deno[Deno.internal].require.Module._load(module, null, {main});
|
||||||
|
}})('{module}');"#,
|
||||||
|
main = main,
|
||||||
|
module = escape_for_single_quote_string(module),
|
||||||
|
);
|
||||||
|
|
||||||
|
js_runtime.execute_script(&located_script_name!(), source_code)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn load_cjs_module(
|
pub fn load_cjs_module(
|
||||||
js_runtime: &mut JsRuntime,
|
js_runtime: &mut JsRuntime,
|
||||||
module: &str,
|
module: &str,
|
||||||
|
|
|
@ -172,3 +172,22 @@ fn native_modules_as_global_vars() {
|
||||||
);
|
);
|
||||||
assert!(out.contains("true"));
|
assert!(out.contains("true"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ext_node_cjs_execution() {
|
||||||
|
let (out, _err) = util::run_and_collect_output_with_args(
|
||||||
|
true,
|
||||||
|
vec![
|
||||||
|
"run",
|
||||||
|
"-A",
|
||||||
|
"--unstable",
|
||||||
|
"--quiet",
|
||||||
|
"commonjs/init.js",
|
||||||
|
"./example.js",
|
||||||
|
],
|
||||||
|
None,
|
||||||
|
Some(vec![("DENO_NODE_COMPAT_URL".to_string(), std_file_url())]),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
assert!(out.contains("{ hello: \"world\" }"));
|
||||||
|
}
|
||||||
|
|
3
cli/tests/testdata/commonjs/data.json
vendored
Normal file
3
cli/tests/testdata/commonjs/data.json
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"hello": "world"
|
||||||
|
}
|
11
cli/tests/testdata/commonjs/example.js
vendored
Normal file
11
cli/tests/testdata/commonjs/example.js
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
||||||
|
// deno-lint-ignore no-undef
|
||||||
|
const processMod = require("process");
|
||||||
|
const osMod = require("node:os");
|
||||||
|
console.log("process.pid", processMod.pid);
|
||||||
|
console.log("os.EOL", osMod.EOL);
|
||||||
|
const leftPad = require("left-pad");
|
||||||
|
const json = require("./data");
|
||||||
|
console.log(json);
|
||||||
|
console.log(leftPad("foo", 5)); // => " foo"
|
||||||
|
console.log("main module", processMod.mainModule.filename);
|
17
cli/tests/testdata/commonjs/init.js
vendored
Normal file
17
cli/tests/testdata/commonjs/init.js
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import { fromFileUrl } from "../../../../test_util/std/path/mod.ts";
|
||||||
|
|
||||||
|
const DENO_NODE_COMPAT_URL = Deno.env.get("DENO_NODE_COMPAT_URL");
|
||||||
|
const moduleAllUrl = `${DENO_NODE_COMPAT_URL}node/module_all.ts`;
|
||||||
|
const processUrl = `${DENO_NODE_COMPAT_URL}node/process.ts`;
|
||||||
|
let moduleName = import.meta.resolve(Deno.args[0]);
|
||||||
|
moduleName = fromFileUrl(moduleName);
|
||||||
|
|
||||||
|
const [moduleAll, processModule] = await Promise.all([
|
||||||
|
import(moduleAllUrl),
|
||||||
|
import(processUrl),
|
||||||
|
]);
|
||||||
|
Deno[Deno.internal].require.initializeCommonJs(
|
||||||
|
moduleAll.default,
|
||||||
|
processModule.default,
|
||||||
|
);
|
||||||
|
Deno[Deno.internal].require.Module._load(moduleName, null, true);
|
74
cli/tests/testdata/commonjs/node_modules/colorette/index.cjs
generated
vendored
Normal file
74
cli/tests/testdata/commonjs/node_modules/colorette/index.cjs
generated
vendored
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
// Copyright Jorge Bucaran. All rights reserved. MIT license.
|
||||||
|
let enabled = !("NO_COLOR" in process.env) &&
|
||||||
|
("FORCE_COLOR" in process.env ||
|
||||||
|
process.platform === "win32" ||
|
||||||
|
(process.stdout != null &&
|
||||||
|
process.stdout.isTTY &&
|
||||||
|
process.env.TERM &&
|
||||||
|
process.env.TERM !== "dumb"));
|
||||||
|
|
||||||
|
const raw = (open, close, searchRegex, replaceValue) =>
|
||||||
|
(s) =>
|
||||||
|
enabled
|
||||||
|
? open +
|
||||||
|
(~(s += "").indexOf(close, 4) // skip opening \x1b[
|
||||||
|
? s.replace(searchRegex, replaceValue)
|
||||||
|
: s) +
|
||||||
|
close
|
||||||
|
: s;
|
||||||
|
|
||||||
|
const init = (open, close) => {
|
||||||
|
return raw(
|
||||||
|
`\x1b[${open}m`,
|
||||||
|
`\x1b[${close}m`,
|
||||||
|
new RegExp(`\\x1b\\[${close}m`, "g"),
|
||||||
|
`\x1b[${open}m`,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.options = Object.defineProperty({}, "enabled", {
|
||||||
|
get: () => enabled,
|
||||||
|
set: (value) => (enabled = value),
|
||||||
|
});
|
||||||
|
|
||||||
|
exports.reset = init(0, 0);
|
||||||
|
exports.bold = raw("\x1b[1m", "\x1b[22m", /\x1b\[22m/g, "\x1b[22m\x1b[1m");
|
||||||
|
exports.dim = raw("\x1b[2m", "\x1b[22m", /\x1b\[22m/g, "\x1b[22m\x1b[2m");
|
||||||
|
exports.italic = init(3, 23);
|
||||||
|
exports.underline = init(4, 24);
|
||||||
|
exports.inverse = init(7, 27);
|
||||||
|
exports.hidden = init(8, 28);
|
||||||
|
exports.strikethrough = init(9, 29);
|
||||||
|
exports.black = init(30, 39);
|
||||||
|
exports.red = init(31, 39);
|
||||||
|
exports.green = init(32, 39);
|
||||||
|
exports.yellow = init(33, 39);
|
||||||
|
exports.blue = init(34, 39);
|
||||||
|
exports.magenta = init(35, 39);
|
||||||
|
exports.cyan = init(36, 39);
|
||||||
|
exports.white = init(37, 39);
|
||||||
|
exports.gray = init(90, 39);
|
||||||
|
exports.bgBlack = init(40, 49);
|
||||||
|
exports.bgRed = init(41, 49);
|
||||||
|
exports.bgGreen = init(42, 49);
|
||||||
|
exports.bgYellow = init(43, 49);
|
||||||
|
exports.bgBlue = init(44, 49);
|
||||||
|
exports.bgMagenta = init(45, 49);
|
||||||
|
exports.bgCyan = init(46, 49);
|
||||||
|
exports.bgWhite = init(47, 49);
|
||||||
|
exports.blackBright = init(90, 39);
|
||||||
|
exports.redBright = init(91, 39);
|
||||||
|
exports.greenBright = init(92, 39);
|
||||||
|
exports.yellowBright = init(93, 39);
|
||||||
|
exports.blueBright = init(94, 39);
|
||||||
|
exports.magentaBright = init(95, 39);
|
||||||
|
exports.cyanBright = init(96, 39);
|
||||||
|
exports.whiteBright = init(97, 39);
|
||||||
|
exports.bgBlackBright = init(100, 49);
|
||||||
|
exports.bgRedBright = init(101, 49);
|
||||||
|
exports.bgGreenBright = init(102, 49);
|
||||||
|
exports.bgYellowBright = init(103, 49);
|
||||||
|
exports.bgBlueBright = init(104, 49);
|
||||||
|
exports.bgMagentaBright = init(105, 49);
|
||||||
|
exports.bgCyanBright = init(106, 49);
|
||||||
|
exports.bgWhiteBright = init(107, 49);
|
75
cli/tests/testdata/commonjs/node_modules/colorette/index.js
generated
vendored
Normal file
75
cli/tests/testdata/commonjs/node_modules/colorette/index.js
generated
vendored
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
// Copyright Jorge Bucaran. All rights reserved. MIT license.
|
||||||
|
// deno-lint-ignore-file no-undef no-control-regex
|
||||||
|
let enabled = !("NO_COLOR" in process.env) &&
|
||||||
|
("FORCE_COLOR" in process.env ||
|
||||||
|
process.platform === "win32" ||
|
||||||
|
(process.stdout != null &&
|
||||||
|
process.stdout.isTTY &&
|
||||||
|
process.env.TERM &&
|
||||||
|
process.env.TERM !== "dumb"));
|
||||||
|
|
||||||
|
const raw = (open, close, searchRegex, replaceValue) =>
|
||||||
|
(s) =>
|
||||||
|
enabled
|
||||||
|
? open +
|
||||||
|
(~(s += "").indexOf(close, 4) // skip opening \x1b[
|
||||||
|
? s.replace(searchRegex, replaceValue)
|
||||||
|
: s) +
|
||||||
|
close
|
||||||
|
: s;
|
||||||
|
|
||||||
|
const init = (open, close) => {
|
||||||
|
return raw(
|
||||||
|
`\x1b[${open}m`,
|
||||||
|
`\x1b[${close}m`,
|
||||||
|
new RegExp(`\\x1b\\[${close}m`, "g"),
|
||||||
|
`\x1b[${open}m`,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const options = Object.defineProperty({}, "enabled", {
|
||||||
|
get: () => enabled,
|
||||||
|
set: (value) => (enabled = value),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const reset = init(0, 0);
|
||||||
|
export const bold = raw("\x1b[1m", "\x1b[22m", /\x1b\[22m/g, "\x1b[22m\x1b[1m");
|
||||||
|
export const dim = raw("\x1b[2m", "\x1b[22m", /\x1b\[22m/g, "\x1b[22m\x1b[2m");
|
||||||
|
export const italic = init(3, 23);
|
||||||
|
export const underline = init(4, 24);
|
||||||
|
export const inverse = init(7, 27);
|
||||||
|
export const hidden = init(8, 28);
|
||||||
|
export const strikethrough = init(9, 29);
|
||||||
|
export const black = init(30, 39);
|
||||||
|
export const red = init(31, 39);
|
||||||
|
export const green = init(32, 39);
|
||||||
|
export const yellow = init(33, 39);
|
||||||
|
export const blue = init(34, 39);
|
||||||
|
export const magenta = init(35, 39);
|
||||||
|
export const cyan = init(36, 39);
|
||||||
|
export const white = init(37, 39);
|
||||||
|
export const gray = init(90, 39);
|
||||||
|
export const bgBlack = init(40, 49);
|
||||||
|
export const bgRed = init(41, 49);
|
||||||
|
export const bgGreen = init(42, 49);
|
||||||
|
export const bgYellow = init(43, 49);
|
||||||
|
export const bgBlue = init(44, 49);
|
||||||
|
export const bgMagenta = init(45, 49);
|
||||||
|
export const bgCyan = init(46, 49);
|
||||||
|
export const bgWhite = init(47, 49);
|
||||||
|
export const blackBright = init(90, 39);
|
||||||
|
export const redBright = init(91, 39);
|
||||||
|
export const greenBright = init(92, 39);
|
||||||
|
export const yellowBright = init(93, 39);
|
||||||
|
export const blueBright = init(94, 39);
|
||||||
|
export const magentaBright = init(95, 39);
|
||||||
|
export const cyanBright = init(96, 39);
|
||||||
|
export const whiteBright = init(97, 39);
|
||||||
|
export const bgBlackBright = init(100, 49);
|
||||||
|
export const bgRedBright = init(101, 49);
|
||||||
|
export const bgGreenBright = init(102, 49);
|
||||||
|
export const bgYellowBright = init(103, 49);
|
||||||
|
export const bgBlueBright = init(104, 49);
|
||||||
|
export const bgMagentaBright = init(105, 49);
|
||||||
|
export const bgCyanBright = init(106, 49);
|
||||||
|
export const bgWhiteBright = init(107, 49);
|
23
cli/tests/testdata/commonjs/node_modules/colorette/package.json
generated
vendored
Normal file
23
cli/tests/testdata/commonjs/node_modules/colorette/package.json
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"author": {
|
||||||
|
"name": "Jorge Bucaran"
|
||||||
|
},
|
||||||
|
"description": "Color your terminal using pure idiomatic JavaScript.",
|
||||||
|
"exports": {
|
||||||
|
"./package.json": "./package.json",
|
||||||
|
".": {
|
||||||
|
"require": "./index.cjs",
|
||||||
|
"import": "./index.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"main": "index.cjs",
|
||||||
|
"module": "index.js",
|
||||||
|
"name": "colorette",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/jorgebucaran/colorette.git"
|
||||||
|
},
|
||||||
|
"type": "module",
|
||||||
|
"version": "1.2.1"
|
||||||
|
}
|
6
cli/tests/testdata/commonjs/node_modules/imports_exports/import_export.js
generated
vendored
Normal file
6
cli/tests/testdata/commonjs/node_modules/imports_exports/import_export.js
generated
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import dep from "#dep";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
bar: "bar",
|
||||||
|
dep,
|
||||||
|
};
|
3
cli/tests/testdata/commonjs/node_modules/imports_exports/import_polyfill.js
generated
vendored
Normal file
3
cli/tests/testdata/commonjs/node_modules/imports_exports/import_polyfill.js
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export default {
|
||||||
|
polyfill: "import",
|
||||||
|
};
|
17
cli/tests/testdata/commonjs/node_modules/imports_exports/package.json
generated
vendored
Normal file
17
cli/tests/testdata/commonjs/node_modules/imports_exports/package.json
generated
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"name": "imports_exports",
|
||||||
|
"main": "./require_export.cjs",
|
||||||
|
"imports": {
|
||||||
|
"#dep": {
|
||||||
|
"import": "./import_polyfill.js",
|
||||||
|
"require": "./require_polyfill.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"import": "./import_export.js",
|
||||||
|
"require": "./require_export.cjs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
6
cli/tests/testdata/commonjs/node_modules/imports_exports/require_export.cjs
generated
vendored
Normal file
6
cli/tests/testdata/commonjs/node_modules/imports_exports/require_export.cjs
generated
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
const dep = require("#dep");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
foo: "foo",
|
||||||
|
dep,
|
||||||
|
};
|
3
cli/tests/testdata/commonjs/node_modules/imports_exports/require_polyfill.js
generated
vendored
Normal file
3
cli/tests/testdata/commonjs/node_modules/imports_exports/require_polyfill.js
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
module.exports = {
|
||||||
|
polyfill: "require",
|
||||||
|
};
|
41
cli/tests/testdata/commonjs/node_modules/left-pad/README.md
generated
vendored
Normal file
41
cli/tests/testdata/commonjs/node_modules/left-pad/README.md
generated
vendored
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
## left-pad
|
||||||
|
|
||||||
|
String left pad
|
||||||
|
|
||||||
|
[![Build Status][travis-image]][travis-url]
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm install left-pad
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```js
|
||||||
|
const leftPad = require("left-pad");
|
||||||
|
|
||||||
|
leftPad("foo", 5);
|
||||||
|
// => " foo"
|
||||||
|
|
||||||
|
leftPad("foobar", 6);
|
||||||
|
// => "foobar"
|
||||||
|
|
||||||
|
leftPad(1, 2, "0");
|
||||||
|
// => "01"
|
||||||
|
|
||||||
|
leftPad(17, 5, 0);
|
||||||
|
// => "00017"
|
||||||
|
```
|
||||||
|
|
||||||
|
**NOTE:** The third argument should be a single `char`. However the module
|
||||||
|
doesn't throw an error if you supply more than one `char`s. See
|
||||||
|
[#28](https://github.com/stevemao/left-pad/pull/28).
|
||||||
|
|
||||||
|
**NOTE:** Characters having code points outside of
|
||||||
|
[BMP plan](https://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane)
|
||||||
|
are considered a two distinct characters. See
|
||||||
|
[#58](https://github.com/stevemao/left-pad/issues/58).
|
||||||
|
|
||||||
|
[travis-image]: https://travis-ci.org/stevemao/left-pad.svg?branch=master
|
||||||
|
[travis-url]: https://travis-ci.org/stevemao/left-pad
|
54
cli/tests/testdata/commonjs/node_modules/left-pad/index.js
generated
vendored
Normal file
54
cli/tests/testdata/commonjs/node_modules/left-pad/index.js
generated
vendored
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
// deno-lint-ignore-file
|
||||||
|
|
||||||
|
/* This program is free software. It comes without any warranty, to
|
||||||
|
* the extent permitted by applicable law. You can redistribute it
|
||||||
|
* and/or modify it under the terms of the Do What The Fuck You Want
|
||||||
|
* To Public License, Version 2, as published by Sam Hocevar. See
|
||||||
|
* http://www.wtfpl.net/ for more details. */
|
||||||
|
"use strict";
|
||||||
|
module.exports = leftPad;
|
||||||
|
|
||||||
|
var cache = [
|
||||||
|
"",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
];
|
||||||
|
|
||||||
|
function leftPad(str, len, ch) {
|
||||||
|
// convert `str` to a `string`
|
||||||
|
str = str + "";
|
||||||
|
// `len` is the `pad`'s length now
|
||||||
|
len = len - str.length;
|
||||||
|
// doesn't need to pad
|
||||||
|
if (len <= 0) return str;
|
||||||
|
// `ch` defaults to `' '`
|
||||||
|
if (!ch && ch !== 0) ch = " ";
|
||||||
|
// convert `ch` to a `string` cuz it could be a number
|
||||||
|
ch = ch + "";
|
||||||
|
// cache common use cases
|
||||||
|
if (ch === " " && len < 10) return cache[len] + str;
|
||||||
|
// `pad` starts with an empty string
|
||||||
|
var pad = "";
|
||||||
|
// loop
|
||||||
|
while (true) {
|
||||||
|
// add `ch` to `pad` if `len` is odd
|
||||||
|
if (len & 1) pad += ch;
|
||||||
|
// divide `len` by 2, ditch the remainder
|
||||||
|
len >>= 1;
|
||||||
|
// "double" the `ch` so this operation count grows logarithmically on `len`
|
||||||
|
// each time `ch` is "doubled", the `len` would need to be "doubled" too
|
||||||
|
// similar to finding a value in binary search tree, hence O(log(n))
|
||||||
|
if (len) ch += ch;
|
||||||
|
// `len` is 0, exit the loop
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
// pad `str`!
|
||||||
|
return pad + str;
|
||||||
|
}
|
68
cli/tests/testdata/commonjs/node_modules/left-pad/package.json
generated
vendored
Normal file
68
cli/tests/testdata/commonjs/node_modules/left-pad/package.json
generated
vendored
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
{
|
||||||
|
"_from": "left-pad",
|
||||||
|
"_id": "left-pad@1.3.0",
|
||||||
|
"_inBundle": false,
|
||||||
|
"_integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==",
|
||||||
|
"_location": "/left-pad",
|
||||||
|
"_phantomChildren": {},
|
||||||
|
"_requested": {
|
||||||
|
"type": "tag",
|
||||||
|
"registry": true,
|
||||||
|
"raw": "left-pad",
|
||||||
|
"name": "left-pad",
|
||||||
|
"escapedName": "left-pad",
|
||||||
|
"rawSpec": "",
|
||||||
|
"saveSpec": null,
|
||||||
|
"fetchSpec": "latest"
|
||||||
|
},
|
||||||
|
"_requiredBy": [
|
||||||
|
"#USER",
|
||||||
|
"/"
|
||||||
|
],
|
||||||
|
"_resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz",
|
||||||
|
"_shasum": "5b8a3a7765dfe001261dde915589e782f8c94d1e",
|
||||||
|
"_spec": "left-pad",
|
||||||
|
"_where": "/Users/kun/Projects/Deno/deno/std/node/tests",
|
||||||
|
"author": {
|
||||||
|
"name": "azer"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/stevemao/left-pad/issues"
|
||||||
|
},
|
||||||
|
"bundleDependencies": false,
|
||||||
|
"deprecated": "use String.prototype.padStart()",
|
||||||
|
"description": "String left pad",
|
||||||
|
"devDependencies": {
|
||||||
|
"benchmark": "^2.1.0",
|
||||||
|
"fast-check": "0.0.8",
|
||||||
|
"tape": "*"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/stevemao/left-pad#readme",
|
||||||
|
"keywords": [
|
||||||
|
"leftpad",
|
||||||
|
"left",
|
||||||
|
"pad",
|
||||||
|
"padding",
|
||||||
|
"string",
|
||||||
|
"repeat"
|
||||||
|
],
|
||||||
|
"license": "WTFPL",
|
||||||
|
"main": "index.js",
|
||||||
|
"maintainers": [
|
||||||
|
{
|
||||||
|
"name": "Cameron Westland",
|
||||||
|
"email": "camwest@gmail.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "left-pad",
|
||||||
|
"repository": {
|
||||||
|
"url": "git+ssh://git@github.com/stevemao/left-pad.git",
|
||||||
|
"type": "git"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"bench": "node perf/perf.js",
|
||||||
|
"test": "node test"
|
||||||
|
},
|
||||||
|
"types": "index.d.ts",
|
||||||
|
"version": "1.3.0"
|
||||||
|
}
|
17
cli/tests/testdata/commonjs/package.json
vendored
Normal file
17
cli/tests/testdata/commonjs/package.json
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"name": "example",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "",
|
||||||
|
"main": "example.js",
|
||||||
|
"dependencies": {
|
||||||
|
"colorette": "1.2.1",
|
||||||
|
"left-pad": "1.3.0",
|
||||||
|
"imports_exports": "1.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {},
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC"
|
||||||
|
}
|
815
ext/node/01_require.js
Normal file
815
ext/node/01_require.js
Normal file
|
@ -0,0 +1,815 @@
|
||||||
|
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
// deno-lint-ignore-file
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
((window) => {
|
||||||
|
const {
|
||||||
|
ArrayIsArray,
|
||||||
|
ArrayPrototypeIncludes,
|
||||||
|
ArrayPrototypeIndexOf,
|
||||||
|
ArrayPrototypeJoin,
|
||||||
|
ArrayPrototypePush,
|
||||||
|
ArrayPrototypeSlice,
|
||||||
|
ArrayPrototypeSplice,
|
||||||
|
ObjectGetOwnPropertyDescriptor,
|
||||||
|
ObjectGetPrototypeOf,
|
||||||
|
ObjectEntries,
|
||||||
|
ObjectPrototypeHasOwnProperty,
|
||||||
|
ObjectSetPrototypeOf,
|
||||||
|
ObjectKeys,
|
||||||
|
ObjectCreate,
|
||||||
|
SafeMap,
|
||||||
|
SafeWeakMap,
|
||||||
|
JSONParse,
|
||||||
|
StringPrototypeEndsWith,
|
||||||
|
StringPrototypeIndexOf,
|
||||||
|
StringPrototypeSlice,
|
||||||
|
StringPrototypeStartsWith,
|
||||||
|
StringPrototypeCharCodeAt,
|
||||||
|
RegExpPrototypeTest,
|
||||||
|
} = window.__bootstrap.primordials;
|
||||||
|
const core = window.Deno.core;
|
||||||
|
|
||||||
|
// Map used to store CJS parsing data.
|
||||||
|
const cjsParseCache = new SafeWeakMap();
|
||||||
|
|
||||||
|
function pathDirname(filepath) {
|
||||||
|
return core.opSync("op_require_path_dirname", filepath);
|
||||||
|
}
|
||||||
|
|
||||||
|
function pathResolve(...args) {
|
||||||
|
return core.opSync("op_require_path_resolve", args);
|
||||||
|
}
|
||||||
|
|
||||||
|
function assert(cond) {
|
||||||
|
if (!cond) {
|
||||||
|
throw Error("assert");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
function isProxy() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(bartlomieju): verify in other parts of this file that
|
||||||
|
// we have initialized the system before making APIs work
|
||||||
|
let cjsInitialized = false;
|
||||||
|
let processGlobal = null;
|
||||||
|
const nativeModulePolyfill = new SafeMap();
|
||||||
|
const nativeModuleExports = ObjectCreate(null);
|
||||||
|
|
||||||
|
const relativeResolveCache = ObjectCreate(null);
|
||||||
|
let requireDepth = 0;
|
||||||
|
let statCache = null;
|
||||||
|
let isPreloading = false;
|
||||||
|
let mainModule = null;
|
||||||
|
|
||||||
|
function stat(filename) {
|
||||||
|
// TODO: required only on windows
|
||||||
|
// filename = path.toNamespacedPath(filename);
|
||||||
|
if (statCache !== null) {
|
||||||
|
const result = statCache.get(filename);
|
||||||
|
if (result !== undefined) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const result = core.opSync("op_require_stat", filename);
|
||||||
|
if (statCache !== null && result >= 0) {
|
||||||
|
statCache.set(filename, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateChildren(parent, child, scan) {
|
||||||
|
if (!parent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const children = parent.children;
|
||||||
|
if (children && !(scan && ArrayPrototypeIncludes(children, child))) {
|
||||||
|
ArrayPrototypePush(children, child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function tryFile(requestPath, _isMain) {
|
||||||
|
const rc = stat(requestPath);
|
||||||
|
if (rc !== 0) return;
|
||||||
|
return toRealPath(requestPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
function tryPackage(requestPath, exts, isMain, originalPath) {
|
||||||
|
// const pkg = readPackage(requestPath)?.main;
|
||||||
|
let pkg = false;
|
||||||
|
|
||||||
|
if (!pkg) {
|
||||||
|
return tryExtensions(
|
||||||
|
pathResolve(requestPath, "index"),
|
||||||
|
exts,
|
||||||
|
isMain,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const filename = path.resolve(requestPath, pkg);
|
||||||
|
let actual = tryFile(filename, isMain) ||
|
||||||
|
tryExtensions(filename, exts, isMain) ||
|
||||||
|
tryExtensions(
|
||||||
|
pathResolve(filename, "index"),
|
||||||
|
exts,
|
||||||
|
isMain,
|
||||||
|
);
|
||||||
|
if (actual === false) {
|
||||||
|
actual = tryExtensions(
|
||||||
|
pathResolve(requestPath, "index"),
|
||||||
|
exts,
|
||||||
|
isMain,
|
||||||
|
);
|
||||||
|
if (!actual) {
|
||||||
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
|
const err = new Error(
|
||||||
|
`Cannot find module '${filename}'. ` +
|
||||||
|
'Please verify that the package.json has a valid "main" entry',
|
||||||
|
);
|
||||||
|
err.code = "MODULE_NOT_FOUND";
|
||||||
|
err.path = pathResolve(
|
||||||
|
requestPath,
|
||||||
|
"package.json",
|
||||||
|
);
|
||||||
|
err.requestPath = originalPath;
|
||||||
|
throw err;
|
||||||
|
} else {
|
||||||
|
const jsonPath = pathResolve(
|
||||||
|
requestPath,
|
||||||
|
"package.json",
|
||||||
|
);
|
||||||
|
process.emitWarning(
|
||||||
|
`Invalid 'main' field in '${jsonPath}' of '${pkg}'. ` +
|
||||||
|
"Please either fix that or report it to the module author",
|
||||||
|
"DeprecationWarning",
|
||||||
|
"DEP0128",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return actual;
|
||||||
|
}
|
||||||
|
|
||||||
|
const realpathCache = new SafeMap();
|
||||||
|
function toRealPath(requestPath) {
|
||||||
|
const maybeCached = realpathCache.get(requestPath);
|
||||||
|
if (maybeCached) {
|
||||||
|
return maybeCached;
|
||||||
|
}
|
||||||
|
const rp = core.opSync("op_require_real_path", requestPath);
|
||||||
|
realpathCache.set(requestPath, rp);
|
||||||
|
return rp;
|
||||||
|
}
|
||||||
|
|
||||||
|
function tryExtensions(p, exts, isMain) {
|
||||||
|
for (let i = 0; i < exts.length; i++) {
|
||||||
|
const filename = tryFile(p + exts[i], isMain);
|
||||||
|
|
||||||
|
if (filename) {
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the longest (possibly multi-dot) extension registered in
|
||||||
|
// Module._extensions
|
||||||
|
function findLongestRegisteredExtension(filename) {
|
||||||
|
const name = core.opSync("op_require_path_basename", filename);
|
||||||
|
let currentExtension;
|
||||||
|
let index;
|
||||||
|
let startIndex = 0;
|
||||||
|
while ((index = StringPrototypeIndexOf(name, ".", startIndex)) !== -1) {
|
||||||
|
startIndex = index + 1;
|
||||||
|
if (index === 0) continue; // Skip dotfiles like .gitignore
|
||||||
|
currentExtension = StringPrototypeSlice(name, index);
|
||||||
|
if (Module._extensions[currentExtension]) {
|
||||||
|
return currentExtension;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ".js";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getExportsForCircularRequire(module) {
|
||||||
|
if (
|
||||||
|
module.exports &&
|
||||||
|
!isProxy(module.exports) &&
|
||||||
|
ObjectGetPrototypeOf(module.exports) === ObjectPrototype &&
|
||||||
|
// Exclude transpiled ES6 modules / TypeScript code because those may
|
||||||
|
// employ unusual patterns for accessing 'module.exports'. That should
|
||||||
|
// be okay because ES6 modules have a different approach to circular
|
||||||
|
// dependencies anyway.
|
||||||
|
!module.exports.__esModule
|
||||||
|
) {
|
||||||
|
// This is later unset once the module is done loading.
|
||||||
|
ObjectSetPrototypeOf(
|
||||||
|
module.exports,
|
||||||
|
CircularRequirePrototypeWarningProxy,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return module.exports;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Proxy that can be used as the prototype of a module.exports object and
|
||||||
|
// warns when non-existent properties are accessed.
|
||||||
|
const CircularRequirePrototypeWarningProxy = new Proxy({}, {
|
||||||
|
get(target, prop) {
|
||||||
|
// Allow __esModule access in any case because it is used in the output
|
||||||
|
// of transpiled code to determine whether something comes from an
|
||||||
|
// ES module, and is not used as a regular key of `module.exports`.
|
||||||
|
if (prop in target || prop === "__esModule") return target[prop];
|
||||||
|
// TODO:
|
||||||
|
// emitCircularRequireWarning(prop);
|
||||||
|
console.log("TODO: emitCircularRequireWarning");
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
|
||||||
|
getOwnPropertyDescriptor(target, prop) {
|
||||||
|
if (
|
||||||
|
ObjectPrototypeHasOwnProperty(target, prop) || prop === "__esModule"
|
||||||
|
) {
|
||||||
|
return ObjectGetOwnPropertyDescriptor(target, prop);
|
||||||
|
}
|
||||||
|
// TODO:
|
||||||
|
// emitCircularRequireWarning(prop);
|
||||||
|
console.log("TODO: emitCircularRequireWarning");
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const moduleParentCache = new SafeWeakMap();
|
||||||
|
function Module(id = "", parent) {
|
||||||
|
this.id = id;
|
||||||
|
this.path = pathDirname(id);
|
||||||
|
this.exports = {};
|
||||||
|
moduleParentCache.set(this, parent);
|
||||||
|
updateChildren(parent, this, false);
|
||||||
|
this.filename = null;
|
||||||
|
this.loaded = false;
|
||||||
|
this.children = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const builtinModules = [];
|
||||||
|
// TODO(bartlomieju): handle adding native modules
|
||||||
|
Module.builtinModules = builtinModules;
|
||||||
|
|
||||||
|
Module._extensions = Object.create(null);
|
||||||
|
Module._cache = Object.create(null);
|
||||||
|
Module._pathCache = Object.create(null);
|
||||||
|
let modulePaths = [];
|
||||||
|
Module.globalPaths = modulePaths;
|
||||||
|
|
||||||
|
const CHAR_FORWARD_SLASH = 47;
|
||||||
|
const TRAILING_SLASH_REGEX = /(?:^|\/)\.?\.$/;
|
||||||
|
Module._findPath = function (request, paths, isMain) {
|
||||||
|
const absoluteRequest = core.opSync("op_require_path_is_absolute", request);
|
||||||
|
if (absoluteRequest) {
|
||||||
|
paths = [""];
|
||||||
|
} else if (!paths || paths.length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cacheKey = request + "\x00" + ArrayPrototypeJoin(paths, "\x00");
|
||||||
|
const entry = Module._pathCache[cacheKey];
|
||||||
|
if (entry) {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
let exts;
|
||||||
|
let trailingSlash = request.length > 0 &&
|
||||||
|
StringPrototypeCharCodeAt(request, request.length - 1) ===
|
||||||
|
CHAR_FORWARD_SLASH;
|
||||||
|
if (!trailingSlash) {
|
||||||
|
trailingSlash = RegExpPrototypeTest(TRAILING_SLASH_REGEX, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For each path
|
||||||
|
for (let i = 0; i < paths.length; i++) {
|
||||||
|
// Don't search further if path doesn't exist
|
||||||
|
const curPath = paths[i];
|
||||||
|
if (curPath && stat(curPath) < 1) continue;
|
||||||
|
|
||||||
|
if (!absoluteRequest) {
|
||||||
|
const exportsResolved = false;
|
||||||
|
// TODO:
|
||||||
|
console.log("TODO: Module._findPath resolveExports");
|
||||||
|
// const exportsResolved = resolveExports(curPath, request);
|
||||||
|
if (exportsResolved) {
|
||||||
|
return exportsResolved;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const basePath = pathResolve(curPath, request);
|
||||||
|
let filename;
|
||||||
|
|
||||||
|
const rc = stat(basePath);
|
||||||
|
if (!trailingSlash) {
|
||||||
|
if (rc === 0) { // File.
|
||||||
|
if (!isMain) {
|
||||||
|
filename = toRealPath(basePath);
|
||||||
|
} else {
|
||||||
|
filename = toRealPath(basePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!filename) {
|
||||||
|
// Try it with each of the extensions
|
||||||
|
if (exts === undefined) {
|
||||||
|
exts = ObjectKeys(Module._extensions);
|
||||||
|
}
|
||||||
|
filename = tryExtensions(basePath, exts, isMain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!filename && rc === 1) { // Directory.
|
||||||
|
// try it with each of the extensions at "index"
|
||||||
|
if (exts === undefined) {
|
||||||
|
exts = ObjectKeys(Module._extensions);
|
||||||
|
}
|
||||||
|
filename = tryPackage(basePath, exts, isMain, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filename) {
|
||||||
|
Module._pathCache[cacheKey] = filename;
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
Module._nodeModulePaths = function (from) {
|
||||||
|
return core.opSync("op_require_node_module_paths", from);
|
||||||
|
};
|
||||||
|
|
||||||
|
Module._resolveLookupPaths = function (request, parent) {
|
||||||
|
return core.opSync(
|
||||||
|
"op_require_resolve_lookup_paths",
|
||||||
|
request,
|
||||||
|
parent?.paths,
|
||||||
|
parent?.filename ?? "",
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Module._load = function (request, parent, isMain) {
|
||||||
|
let relResolveCacheIdentifier;
|
||||||
|
if (parent) {
|
||||||
|
// Fast path for (lazy loaded) modules in the same directory. The indirect
|
||||||
|
// caching is required to allow cache invalidation without changing the old
|
||||||
|
// cache key names.
|
||||||
|
relResolveCacheIdentifier = `${parent.path}\x00${request}`;
|
||||||
|
const filename = relativeResolveCache[relResolveCacheIdentifier];
|
||||||
|
if (filename !== undefined) {
|
||||||
|
const cachedModule = Module._cache[filename];
|
||||||
|
if (cachedModule !== undefined) {
|
||||||
|
updateChildren(parent, cachedModule, true);
|
||||||
|
if (!cachedModule.loaded) {
|
||||||
|
return getExportsForCircularRequire(cachedModule);
|
||||||
|
}
|
||||||
|
return cachedModule.exports;
|
||||||
|
}
|
||||||
|
delete relativeResolveCache[relResolveCacheIdentifier];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const filename = Module._resolveFilename(request, parent, isMain);
|
||||||
|
if (StringPrototypeStartsWith(filename, "node:")) {
|
||||||
|
// Slice 'node:' prefix
|
||||||
|
const id = StringPrototypeSlice(filename, 5);
|
||||||
|
|
||||||
|
const module = loadNativeModule(id, id);
|
||||||
|
if (!module) {
|
||||||
|
// TODO:
|
||||||
|
// throw new ERR_UNKNOWN_BUILTIN_MODULE(filename);
|
||||||
|
throw new Error("Unknown built-in module");
|
||||||
|
}
|
||||||
|
|
||||||
|
return module.exports;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cachedModule = Module._cache[filename];
|
||||||
|
if (cachedModule !== undefined) {
|
||||||
|
updateChildren(parent, cachedModule, true);
|
||||||
|
if (!cachedModule.loaded) {
|
||||||
|
const parseCachedModule = cjsParseCache.get(cachedModule);
|
||||||
|
if (!parseCachedModule || parseCachedModule.loaded) {
|
||||||
|
return getExportsForCircularRequire(cachedModule);
|
||||||
|
}
|
||||||
|
parseCachedModule.loaded = true;
|
||||||
|
} else {
|
||||||
|
return cachedModule.exports;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mod = loadNativeModule(filename, request);
|
||||||
|
if (
|
||||||
|
mod
|
||||||
|
) {
|
||||||
|
return mod.exports;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't call updateChildren(), Module constructor already does.
|
||||||
|
const module = cachedModule || new Module(filename, parent);
|
||||||
|
|
||||||
|
if (isMain) {
|
||||||
|
processGlobal.mainModule = module;
|
||||||
|
module.id = ".";
|
||||||
|
}
|
||||||
|
|
||||||
|
Module._cache[filename] = module;
|
||||||
|
if (parent !== undefined) {
|
||||||
|
relativeResolveCache[relResolveCacheIdentifier] = filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
let threw = true;
|
||||||
|
try {
|
||||||
|
module.load(filename);
|
||||||
|
threw = false;
|
||||||
|
} finally {
|
||||||
|
if (threw) {
|
||||||
|
delete Module._cache[filename];
|
||||||
|
if (parent !== undefined) {
|
||||||
|
delete relativeResolveCache[relResolveCacheIdentifier];
|
||||||
|
const children = parent?.children;
|
||||||
|
if (ArrayIsArray(children)) {
|
||||||
|
const index = ArrayPrototypeIndexOf(children, module);
|
||||||
|
if (index !== -1) {
|
||||||
|
ArrayPrototypeSplice(children, index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
module.exports &&
|
||||||
|
!isProxy(module.exports) &&
|
||||||
|
ObjectGetPrototypeOf(module.exports) ===
|
||||||
|
CircularRequirePrototypeWarningProxy
|
||||||
|
) {
|
||||||
|
ObjectSetPrototypeOf(module.exports, ObjectPrototype);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return module.exports;
|
||||||
|
};
|
||||||
|
|
||||||
|
Module._resolveFilename = function (
|
||||||
|
request,
|
||||||
|
parent,
|
||||||
|
isMain,
|
||||||
|
options,
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
StringPrototypeStartsWith(request, "node:") ||
|
||||||
|
nativeModuleCanBeRequiredByUsers(request)
|
||||||
|
) {
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
let paths;
|
||||||
|
|
||||||
|
if (typeof options === "object" && options !== null) {
|
||||||
|
if (ArrayIsArray(options.paths)) {
|
||||||
|
const isRelative = core.opSync(
|
||||||
|
"op_require_specifier_is_relative",
|
||||||
|
request,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isRelative) {
|
||||||
|
paths = options.paths;
|
||||||
|
} else {
|
||||||
|
const fakeParent = new Module("", null);
|
||||||
|
|
||||||
|
paths = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < options.paths.length; i++) {
|
||||||
|
const path = options.paths[i];
|
||||||
|
fakeParent.paths = Module._nodeModulePaths(path);
|
||||||
|
const lookupPaths = Module._resolveLookupPaths(request, fakeParent);
|
||||||
|
|
||||||
|
for (let j = 0; j < lookupPaths.length; j++) {
|
||||||
|
if (!ArrayPrototypeIncludes(paths, lookupPaths[j])) {
|
||||||
|
ArrayPrototypePush(paths, lookupPaths[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (options.paths === undefined) {
|
||||||
|
paths = Module._resolveLookupPaths(request, parent);
|
||||||
|
} else {
|
||||||
|
// TODO:
|
||||||
|
// throw new ERR_INVALID_ARG_VALUE("options.paths", options.paths);
|
||||||
|
throw new Error("Invalid arg value options.paths", options.path);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
paths = Module._resolveLookupPaths(request, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parent?.filename) {
|
||||||
|
if (request[0] === "#") {
|
||||||
|
console.log("TODO: Module._resolveFilename with #specifier");
|
||||||
|
// const pkg = readPackageScope(parent.filename) || {};
|
||||||
|
// if (pkg.data?.imports != null) {
|
||||||
|
// try {
|
||||||
|
// return finalizeEsmResolution(
|
||||||
|
// packageImportsResolve(
|
||||||
|
// request,
|
||||||
|
// pathToFileURL(parent.filename),
|
||||||
|
// cjsConditions,
|
||||||
|
// ),
|
||||||
|
// parent.filename,
|
||||||
|
// pkg.path,
|
||||||
|
// );
|
||||||
|
// } catch (e) {
|
||||||
|
// if (e.code === "ERR_MODULE_NOT_FOUND") {
|
||||||
|
// throw createEsmNotFoundErr(request);
|
||||||
|
// }
|
||||||
|
// throw e;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try module self resolution first
|
||||||
|
// TODO(bartlomieju): make into a single op
|
||||||
|
const parentPath = core.opSync(
|
||||||
|
"op_require_try_self_parent_path",
|
||||||
|
!!parent,
|
||||||
|
parent?.filename,
|
||||||
|
parent?.id,
|
||||||
|
);
|
||||||
|
// const selfResolved = core.opSync("op_require_try_self", parentPath, request);
|
||||||
|
const selfResolved = false;
|
||||||
|
if (selfResolved) {
|
||||||
|
const cacheKey = request + "\x00" +
|
||||||
|
(paths.length === 1 ? paths[0] : ArrayPrototypeJoin(paths, "\x00"));
|
||||||
|
Module._pathCache[cacheKey] = selfResolved;
|
||||||
|
return selfResolved;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look up the filename first, since that's the cache key.
|
||||||
|
const filename = Module._findPath(request, paths, isMain, false);
|
||||||
|
if (filename) return filename;
|
||||||
|
const requireStack = [];
|
||||||
|
for (let cursor = parent; cursor; cursor = moduleParentCache.get(cursor)) {
|
||||||
|
ArrayPrototypePush(requireStack, cursor.filename || cursor.id);
|
||||||
|
}
|
||||||
|
let message = `Cannot find module '${request}'`;
|
||||||
|
if (requireStack.length > 0) {
|
||||||
|
message = message + "\nRequire stack:\n- " +
|
||||||
|
ArrayPrototypeJoin(requireStack, "\n- ");
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
|
const err = new Error(message);
|
||||||
|
err.code = "MODULE_NOT_FOUND";
|
||||||
|
err.requireStack = requireStack;
|
||||||
|
throw err;
|
||||||
|
};
|
||||||
|
|
||||||
|
Module.prototype.load = function (filename) {
|
||||||
|
assert(!this.loaded);
|
||||||
|
this.filename = filename;
|
||||||
|
this.paths = Module._nodeModulePaths(
|
||||||
|
pathDirname(filename),
|
||||||
|
);
|
||||||
|
const extension = findLongestRegisteredExtension(filename);
|
||||||
|
// allow .mjs to be overriden
|
||||||
|
if (
|
||||||
|
StringPrototypeEndsWith(filename, ".mjs") && !Module._extensions[".mjs"]
|
||||||
|
) {
|
||||||
|
// TODO: use proper error class
|
||||||
|
throw new Error("require ESM", filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
Module._extensions[extension](this, filename);
|
||||||
|
this.loaded = true;
|
||||||
|
|
||||||
|
// TODO: do caching
|
||||||
|
};
|
||||||
|
|
||||||
|
// Loads a module at the given file path. Returns that module's
|
||||||
|
// `exports` property.
|
||||||
|
Module.prototype.require = function (id) {
|
||||||
|
if (typeof id !== "string") {
|
||||||
|
// TODO(bartlomieju): it should use different error type
|
||||||
|
// ("ERR_INVALID_ARG_VALUE")
|
||||||
|
throw new TypeError("Invalid argument type");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id === "") {
|
||||||
|
// TODO(bartlomieju): it should use different error type
|
||||||
|
// ("ERR_INVALID_ARG_VALUE")
|
||||||
|
throw new TypeError("id must be non empty");
|
||||||
|
}
|
||||||
|
requireDepth++;
|
||||||
|
try {
|
||||||
|
return Module._load(id, this, /* isMain */ false);
|
||||||
|
} finally {
|
||||||
|
requireDepth--;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Module.wrapper = [
|
||||||
|
// TODO:
|
||||||
|
// We provide non standard timer APIs in the CommonJS wrapper
|
||||||
|
// to avoid exposing them in global namespace.
|
||||||
|
"(function (exports, require, module, __filename, __dirname, setTimeout, clearTimeout, setInterval, clearInterval) { (function (exports, require, module, __filename, __dirname) {",
|
||||||
|
"\n}).call(this, exports, require, module, __filename, __dirname); })",
|
||||||
|
];
|
||||||
|
Module.wrap = function (script) {
|
||||||
|
script = script.replace(/^#!.*?\n/, "");
|
||||||
|
return `${Module.wrapper[0]}${script}${Module.wrapper[1]}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
function wrapSafe(
|
||||||
|
filename,
|
||||||
|
content,
|
||||||
|
_cjsModuleInstance,
|
||||||
|
) {
|
||||||
|
const wrapper = Module.wrap(content);
|
||||||
|
const [f, err] = core.evalContext(wrapper, filename);
|
||||||
|
if (err) {
|
||||||
|
console.log("TODO: wrapSafe check if main module");
|
||||||
|
throw err.thrown;
|
||||||
|
}
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
Module.prototype._compile = function (content, filename) {
|
||||||
|
const compiledWrapper = wrapSafe(filename, content, this);
|
||||||
|
|
||||||
|
const dirname = pathDirname(filename);
|
||||||
|
const require = makeRequireFunction(this);
|
||||||
|
const exports = this.exports;
|
||||||
|
const thisValue = exports;
|
||||||
|
const module = this;
|
||||||
|
if (requireDepth === 0) {
|
||||||
|
statCache = new SafeMap();
|
||||||
|
}
|
||||||
|
const result = compiledWrapper.call(
|
||||||
|
thisValue,
|
||||||
|
exports,
|
||||||
|
require,
|
||||||
|
this,
|
||||||
|
filename,
|
||||||
|
dirname,
|
||||||
|
);
|
||||||
|
if (requireDepth === 0) {
|
||||||
|
statCache = null;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
Module._extensions[".js"] = function (module, filename) {
|
||||||
|
const content = core.opSync("op_require_read_file", filename);
|
||||||
|
|
||||||
|
console.log(`TODO: Module._extensions[".js"] is ESM`);
|
||||||
|
|
||||||
|
module._compile(content, filename);
|
||||||
|
};
|
||||||
|
|
||||||
|
function stripBOM(content) {
|
||||||
|
if (content.charCodeAt(0) === 0xfeff) {
|
||||||
|
content = content.slice(1);
|
||||||
|
}
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Native extension for .json
|
||||||
|
Module._extensions[".json"] = function (module, filename) {
|
||||||
|
const content = core.opSync("op_require_read_file", filename);
|
||||||
|
|
||||||
|
try {
|
||||||
|
module.exports = JSONParse(stripBOM(content));
|
||||||
|
} catch (err) {
|
||||||
|
err.message = filename + ": " + err.message;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Native extension for .node
|
||||||
|
Module._extensions[".node"] = function (module, filename) {
|
||||||
|
throw new Error("not implemented loading .node files");
|
||||||
|
};
|
||||||
|
|
||||||
|
function createRequireFromPath(filename) {
|
||||||
|
const proxyPath = core.opSync("op_require_proxy_path", filename);
|
||||||
|
const mod = new Module(proxyPath);
|
||||||
|
mod.filename = proxyPath;
|
||||||
|
mod.paths = Module._nodeModulePaths(mod.path);
|
||||||
|
return makeRequireFunction(mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeRequireFunction(mod) {
|
||||||
|
const require = function require(path) {
|
||||||
|
return mod.require(path);
|
||||||
|
};
|
||||||
|
|
||||||
|
function resolve(request, options) {
|
||||||
|
return Module._resolveFilename(request, mod, false, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
require.resolve = resolve;
|
||||||
|
|
||||||
|
function paths(request) {
|
||||||
|
return Module._resolveLookupPaths(request, mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve.paths = paths;
|
||||||
|
require.main = mainModule;
|
||||||
|
// Enable support to add extra extension types.
|
||||||
|
require.extensions = Module._extensions;
|
||||||
|
require.cache = Module._cache;
|
||||||
|
|
||||||
|
return require;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createRequire(filename) {
|
||||||
|
// FIXME: handle URLs and validation
|
||||||
|
return createRequireFromPath(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
Module.createRequire = createRequire;
|
||||||
|
|
||||||
|
Module._initPaths = function () {
|
||||||
|
const paths = core.opSync("op_require_init_paths");
|
||||||
|
modulePaths = paths;
|
||||||
|
Module.globalPaths = ArrayPrototypeSlice(modulePaths);
|
||||||
|
};
|
||||||
|
|
||||||
|
Module.syncBuiltinESMExports = function syncBuiltinESMExports() {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
};
|
||||||
|
|
||||||
|
Module.Module = Module;
|
||||||
|
|
||||||
|
const m = {
|
||||||
|
_cache: Module._cache,
|
||||||
|
_extensions: Module._extensions,
|
||||||
|
_findPath: Module._findPath,
|
||||||
|
_initPaths: Module._initPaths,
|
||||||
|
_load: Module._load,
|
||||||
|
_nodeModulePaths: Module._nodeModulePaths,
|
||||||
|
_pathCache: Module._pathCache,
|
||||||
|
_preloadModules: Module._preloadModules,
|
||||||
|
_resolveFilename: Module._resolveFilename,
|
||||||
|
_resolveLookupPaths: Module._resolveLookupPaths,
|
||||||
|
builtinModules: Module.builtinModules,
|
||||||
|
createRequire: Module.createRequire,
|
||||||
|
globalPaths: Module.globalPaths,
|
||||||
|
Module,
|
||||||
|
wrap: Module.wrap,
|
||||||
|
};
|
||||||
|
|
||||||
|
nativeModuleExports.module = m;
|
||||||
|
|
||||||
|
function loadNativeModule(_id, request) {
|
||||||
|
if (nativeModulePolyfill.has(request)) {
|
||||||
|
return nativeModulePolyfill.get(request);
|
||||||
|
}
|
||||||
|
const modExports = nativeModuleExports[request];
|
||||||
|
if (modExports) {
|
||||||
|
const nodeMod = new Module(request);
|
||||||
|
nodeMod.exports = modExports;
|
||||||
|
nodeMod.loaded = true;
|
||||||
|
nativeModulePolyfill.set(request, nodeMod);
|
||||||
|
return nodeMod;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function nativeModuleCanBeRequiredByUsers(request) {
|
||||||
|
return !!nativeModuleExports[request];
|
||||||
|
}
|
||||||
|
|
||||||
|
function initializeCommonJs(nodeModules, process) {
|
||||||
|
assert(!cjsInitialized);
|
||||||
|
cjsInitialized = true;
|
||||||
|
for (const [name, exports] of ObjectEntries(nodeModules)) {
|
||||||
|
nativeModuleExports[name] = exports;
|
||||||
|
ArrayPrototypePush(Module.builtinModules, name);
|
||||||
|
}
|
||||||
|
processGlobal = process;
|
||||||
|
}
|
||||||
|
|
||||||
|
function readPackageScope() {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
window.__bootstrap.internals = {
|
||||||
|
...window.__bootstrap.internals ?? {},
|
||||||
|
require: {
|
||||||
|
Module,
|
||||||
|
wrapSafe,
|
||||||
|
toRealPath,
|
||||||
|
cjsParseCache,
|
||||||
|
readPackageScope,
|
||||||
|
initializeCommonJs,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
})(globalThis);
|
17
ext/node/Cargo.toml
Normal file
17
ext/node/Cargo.toml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
[package]
|
||||||
|
name = "deno_node"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["the Deno authors"]
|
||||||
|
edition = "2021"
|
||||||
|
license = "MIT"
|
||||||
|
readme = "README.md"
|
||||||
|
repository = "https://github.com/denoland/deno"
|
||||||
|
description = "Node compatibility for Deno"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
path = "lib.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
deno_core = { version = "0.146.0", path = "../../core" }
|
317
ext/node/lib.rs
Normal file
317
ext/node/lib.rs
Normal file
|
@ -0,0 +1,317 @@
|
||||||
|
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
use deno_core::error::AnyError;
|
||||||
|
use deno_core::include_js_files;
|
||||||
|
use deno_core::normalize_path;
|
||||||
|
use deno_core::op;
|
||||||
|
use deno_core::Extension;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
pub fn init() -> Extension {
|
||||||
|
Extension::builder()
|
||||||
|
.js(include_js_files!(
|
||||||
|
prefix "deno:ext/node",
|
||||||
|
"01_require.js",
|
||||||
|
))
|
||||||
|
.ops(vec![
|
||||||
|
op_require_init_paths::decl(),
|
||||||
|
op_require_node_module_paths::decl(),
|
||||||
|
op_require_proxy_path::decl(),
|
||||||
|
op_require_is_request_relative::decl(),
|
||||||
|
op_require_resolve_lookup_paths::decl(),
|
||||||
|
op_require_try_self_parent_path::decl(),
|
||||||
|
op_require_try_self::decl(),
|
||||||
|
op_require_real_path::decl(),
|
||||||
|
op_require_path_is_absolute::decl(),
|
||||||
|
op_require_path_dirname::decl(),
|
||||||
|
op_require_stat::decl(),
|
||||||
|
op_require_path_resolve::decl(),
|
||||||
|
op_require_path_basename::decl(),
|
||||||
|
op_require_read_file::decl(),
|
||||||
|
])
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[op]
|
||||||
|
pub fn op_require_init_paths() -> Vec<String> {
|
||||||
|
let (home_dir, node_path) = if cfg!(windows) {
|
||||||
|
(
|
||||||
|
std::env::var("USERPROFILE").unwrap_or_else(|_| "".into()),
|
||||||
|
std::env::var("NODE_PATH").unwrap_or_else(|_| "".into()),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
std::env::var("HOME").unwrap_or_else(|_| "".into()),
|
||||||
|
std::env::var("NODE_PATH").unwrap_or_else(|_| "".into()),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut prefix_dir = std::env::current_exe().unwrap();
|
||||||
|
if cfg!(windows) {
|
||||||
|
prefix_dir = prefix_dir.join("..").join("..")
|
||||||
|
} else {
|
||||||
|
prefix_dir = prefix_dir.join("..")
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut paths = vec![prefix_dir.join("lib").join("node")];
|
||||||
|
|
||||||
|
if !home_dir.is_empty() {
|
||||||
|
paths.insert(0, PathBuf::from(&home_dir).join(".node_libraries"));
|
||||||
|
paths.insert(0, PathBuf::from(&home_dir).join(".nod_modules"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut paths = paths
|
||||||
|
.into_iter()
|
||||||
|
.map(|p| p.to_string_lossy().to_string())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if !node_path.is_empty() {
|
||||||
|
let delimiter = if cfg!(windows) { ";" } else { ":" };
|
||||||
|
let mut node_paths: Vec<String> = node_path
|
||||||
|
.split(delimiter)
|
||||||
|
.filter(|e| !e.is_empty())
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
.collect();
|
||||||
|
node_paths.append(&mut paths);
|
||||||
|
paths = node_paths;
|
||||||
|
}
|
||||||
|
|
||||||
|
paths
|
||||||
|
}
|
||||||
|
|
||||||
|
#[op]
|
||||||
|
pub fn op_require_node_module_paths(from: String) -> Vec<String> {
|
||||||
|
// Guarantee that "from" is absolute.
|
||||||
|
let from = deno_core::resolve_path(&from)
|
||||||
|
.unwrap()
|
||||||
|
.to_file_path()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if cfg!(windows) {
|
||||||
|
// return root node_modules when path is 'D:\\'.
|
||||||
|
let from_str = from.to_str().unwrap();
|
||||||
|
if from_str.len() >= 3 {
|
||||||
|
let bytes = from_str.as_bytes();
|
||||||
|
if bytes[from_str.len() - 1] == b'\\' && bytes[from_str.len() - 2] == b':'
|
||||||
|
{
|
||||||
|
let p = from_str.to_owned() + "node_modules";
|
||||||
|
return vec![p];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Return early not only to avoid unnecessary work, but to *avoid* returning
|
||||||
|
// an array of two items for a root: [ '//node_modules', '/node_modules' ]
|
||||||
|
if from.to_string_lossy() == "/" {
|
||||||
|
return vec!["/node_modules".to_string()];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut paths = vec![];
|
||||||
|
let mut current_path = from.as_path();
|
||||||
|
let mut maybe_parent = Some(current_path);
|
||||||
|
while let Some(parent) = maybe_parent {
|
||||||
|
if !parent.ends_with("/node_modules") {
|
||||||
|
paths.push(parent.join("node_modules").to_string_lossy().to_string());
|
||||||
|
current_path = parent;
|
||||||
|
maybe_parent = current_path.parent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cfg!(windows) {
|
||||||
|
// Append /node_modules to handle root paths.
|
||||||
|
paths.push("/node_modules".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
paths
|
||||||
|
}
|
||||||
|
|
||||||
|
#[op]
|
||||||
|
fn op_require_proxy_path(filename: String) -> String {
|
||||||
|
// Allow a directory to be passed as the filename
|
||||||
|
let trailing_slash = if cfg!(windows) {
|
||||||
|
filename.ends_with('\\')
|
||||||
|
} else {
|
||||||
|
filename.ends_with('/')
|
||||||
|
};
|
||||||
|
|
||||||
|
if trailing_slash {
|
||||||
|
let p = PathBuf::from(filename);
|
||||||
|
p.join("noop.js").to_string_lossy().to_string()
|
||||||
|
} else {
|
||||||
|
filename
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[op]
|
||||||
|
fn op_require_is_request_relative(request: String) -> bool {
|
||||||
|
if request.starts_with("./") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if request.starts_with("../") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg!(windows) {
|
||||||
|
if request.starts_with(".\\") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if request.starts_with("..\\") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
#[op]
|
||||||
|
fn op_require_resolve_lookup_paths(
|
||||||
|
request: String,
|
||||||
|
maybe_parent_paths: Option<Vec<String>>,
|
||||||
|
parent_filename: String,
|
||||||
|
) -> Option<Vec<String>> {
|
||||||
|
if !request.starts_with('.')
|
||||||
|
|| (request.len() > 1
|
||||||
|
&& !request.starts_with("..")
|
||||||
|
&& !request.starts_with("./")
|
||||||
|
&& (!cfg!(windows) || !request.starts_with(".\\")))
|
||||||
|
{
|
||||||
|
let module_paths = vec![];
|
||||||
|
let mut paths = module_paths;
|
||||||
|
if let Some(mut parent_paths) = maybe_parent_paths {
|
||||||
|
if !parent_paths.is_empty() {
|
||||||
|
paths.append(&mut parent_paths);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !paths.is_empty() {
|
||||||
|
return Some(paths);
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// In REPL, parent.filename is null.
|
||||||
|
// if (!parent || !parent.id || !parent.filename) {
|
||||||
|
// // Make require('./path/to/foo') work - normally the path is taken
|
||||||
|
// // from realpath(__filename) but in REPL there is no filename
|
||||||
|
// const mainPaths = ['.'];
|
||||||
|
|
||||||
|
// debug('looking for %j in %j', request, mainPaths);
|
||||||
|
// return mainPaths;
|
||||||
|
// }
|
||||||
|
|
||||||
|
let p = PathBuf::from(parent_filename);
|
||||||
|
Some(vec![p.parent().unwrap().to_string_lossy().to_string()])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[op]
|
||||||
|
fn op_require_path_is_absolute(p: String) -> bool {
|
||||||
|
PathBuf::from(p).is_absolute()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[op]
|
||||||
|
fn op_require_stat(filename: String) -> i32 {
|
||||||
|
if let Ok(metadata) = std::fs::metadata(&filename) {
|
||||||
|
if metadata.is_file() {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-1
|
||||||
|
}
|
||||||
|
|
||||||
|
#[op]
|
||||||
|
fn op_require_real_path(request: String) -> Result<String, AnyError> {
|
||||||
|
let mut canonicalized_path = PathBuf::from(request).canonicalize()?;
|
||||||
|
if cfg!(windows) {
|
||||||
|
canonicalized_path = PathBuf::from(
|
||||||
|
canonicalized_path
|
||||||
|
.display()
|
||||||
|
.to_string()
|
||||||
|
.trim_start_matches("\\\\?\\"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(canonicalized_path.to_string_lossy().to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[op]
|
||||||
|
fn op_require_path_resolve(parts: Vec<String>) -> String {
|
||||||
|
assert!(!parts.is_empty());
|
||||||
|
let mut p = PathBuf::from(&parts[0]);
|
||||||
|
if parts.len() > 1 {
|
||||||
|
for part in &parts[1..] {
|
||||||
|
p = p.join(part);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
normalize_path(p).to_string_lossy().to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[op]
|
||||||
|
fn op_require_path_dirname(request: String) -> String {
|
||||||
|
let p = PathBuf::from(request);
|
||||||
|
p.parent().unwrap().to_string_lossy().to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[op]
|
||||||
|
fn op_require_path_basename(request: String) -> String {
|
||||||
|
let p = PathBuf::from(request);
|
||||||
|
p.file_name().unwrap().to_string_lossy().to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[op]
|
||||||
|
fn op_require_try_self_parent_path(
|
||||||
|
has_parent: bool,
|
||||||
|
maybe_parent_filename: Option<String>,
|
||||||
|
maybe_parent_id: Option<String>,
|
||||||
|
) -> Option<String> {
|
||||||
|
if !has_parent {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(parent_filename) = maybe_parent_filename {
|
||||||
|
return Some(parent_filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(parent_id) = maybe_parent_id {
|
||||||
|
if parent_id == "<repl>" || parent_id == "internal/preload" {
|
||||||
|
if let Ok(cwd) = std::env::current_dir() {
|
||||||
|
return Some(cwd.to_string_lossy().to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[op]
|
||||||
|
fn op_require_try_self(
|
||||||
|
has_parent: bool,
|
||||||
|
maybe_parent_filename: Option<String>,
|
||||||
|
maybe_parent_id: Option<String>,
|
||||||
|
) -> Option<String> {
|
||||||
|
if !has_parent {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(parent_filename) = maybe_parent_filename {
|
||||||
|
return Some(parent_filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(parent_id) = maybe_parent_id {
|
||||||
|
if parent_id == "<repl>" || parent_id == "internal/preload" {
|
||||||
|
if let Ok(cwd) = std::env::current_dir() {
|
||||||
|
return Some(cwd.to_string_lossy().to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[op]
|
||||||
|
fn op_require_read_file(filename: String) -> Result<String, AnyError> {
|
||||||
|
let contents = std::fs::read_to_string(filename)?;
|
||||||
|
Ok(contents)
|
||||||
|
}
|
|
@ -30,6 +30,7 @@ deno_fetch = { version = "0.87.0", path = "../ext/fetch" }
|
||||||
deno_ffi = { version = "0.51.0", path = "../ext/ffi" }
|
deno_ffi = { version = "0.51.0", path = "../ext/ffi" }
|
||||||
deno_http = { version = "0.58.0", path = "../ext/http" }
|
deno_http = { version = "0.58.0", path = "../ext/http" }
|
||||||
deno_net = { version = "0.56.0", path = "../ext/net" }
|
deno_net = { version = "0.56.0", path = "../ext/net" }
|
||||||
|
deno_node = { version = "0.1.0", path = "../ext/node" }
|
||||||
deno_tls = { version = "0.51.0", path = "../ext/tls" }
|
deno_tls = { version = "0.51.0", path = "../ext/tls" }
|
||||||
deno_url = { version = "0.64.0", path = "../ext/url" }
|
deno_url = { version = "0.64.0", path = "../ext/url" }
|
||||||
deno_web = { version = "0.95.0", path = "../ext/web" }
|
deno_web = { version = "0.95.0", path = "../ext/web" }
|
||||||
|
@ -53,6 +54,7 @@ deno_fetch = { version = "0.87.0", path = "../ext/fetch" }
|
||||||
deno_ffi = { version = "0.51.0", path = "../ext/ffi" }
|
deno_ffi = { version = "0.51.0", path = "../ext/ffi" }
|
||||||
deno_http = { version = "0.58.0", path = "../ext/http" }
|
deno_http = { version = "0.58.0", path = "../ext/http" }
|
||||||
deno_net = { version = "0.56.0", path = "../ext/net" }
|
deno_net = { version = "0.56.0", path = "../ext/net" }
|
||||||
|
deno_node = { version = "0.1.0", path = "../ext/node" }
|
||||||
deno_tls = { version = "0.51.0", path = "../ext/tls" }
|
deno_tls = { version = "0.51.0", path = "../ext/tls" }
|
||||||
deno_url = { version = "0.64.0", path = "../ext/url" }
|
deno_url = { version = "0.64.0", path = "../ext/url" }
|
||||||
deno_web = { version = "0.95.0", path = "../ext/web" }
|
deno_web = { version = "0.95.0", path = "../ext/web" }
|
||||||
|
|
|
@ -158,6 +158,7 @@ mod not_docs {
|
||||||
deno_broadcast_channel::InMemoryBroadcastChannel::default(),
|
deno_broadcast_channel::InMemoryBroadcastChannel::default(),
|
||||||
false, // No --unstable.
|
false, // No --unstable.
|
||||||
),
|
),
|
||||||
|
deno_node::init(),
|
||||||
deno_ffi::init::<Permissions>(false),
|
deno_ffi::init::<Permissions>(false),
|
||||||
deno_net::init::<Permissions>(
|
deno_net::init::<Permissions>(
|
||||||
None, false, // No --unstable.
|
None, false, // No --unstable.
|
||||||
|
|
|
@ -8,6 +8,7 @@ pub use deno_fetch;
|
||||||
pub use deno_ffi;
|
pub use deno_ffi;
|
||||||
pub use deno_http;
|
pub use deno_http;
|
||||||
pub use deno_net;
|
pub use deno_net;
|
||||||
|
pub use deno_node;
|
||||||
pub use deno_tls;
|
pub use deno_tls;
|
||||||
pub use deno_url;
|
pub use deno_url;
|
||||||
pub use deno_web;
|
pub use deno_web;
|
||||||
|
|
|
@ -419,6 +419,7 @@ impl WebWorker {
|
||||||
unstable,
|
unstable,
|
||||||
options.unsafely_ignore_certificate_errors.clone(),
|
options.unsafely_ignore_certificate_errors.clone(),
|
||||||
),
|
),
|
||||||
|
deno_node::init(),
|
||||||
ops::os::init_for_worker(),
|
ops::os::init_for_worker(),
|
||||||
ops::permissions::init(),
|
ops::permissions::init(),
|
||||||
ops::process::init(),
|
ops::process::init(),
|
||||||
|
|
|
@ -161,6 +161,7 @@ impl MainWorker {
|
||||||
unstable,
|
unstable,
|
||||||
options.unsafely_ignore_certificate_errors.clone(),
|
options.unsafely_ignore_certificate_errors.clone(),
|
||||||
),
|
),
|
||||||
|
deno_node::init(),
|
||||||
ops::os::init(exit_code.clone()),
|
ops::os::init(exit_code.clone()),
|
||||||
ops::permissions::init(),
|
ops::permissions::init(),
|
||||||
ops::process::init(),
|
ops::process::init(),
|
||||||
|
|
Loading…
Reference in a new issue