summaryrefslogtreecommitdiff
path: root/ext/node
diff options
context:
space:
mode:
Diffstat (limited to 'ext/node')
-rw-r--r--ext/node/lib.rs1
-rw-r--r--ext/node/ops/mod.rs1
-rw-r--r--ext/node/ops/require.rs4
-rw-r--r--ext/node/ops/worker_threads.rs87
-rw-r--r--ext/node/polyfills/worker_threads.ts115
5 files changed, 109 insertions, 99 deletions
diff --git a/ext/node/lib.rs b/ext/node/lib.rs
index f4541f886..16e69250b 100644
--- a/ext/node/lib.rs
+++ b/ext/node/lib.rs
@@ -329,6 +329,7 @@ deno_core::extension!(deno_node,
ops::require::op_require_package_imports_resolve<P>,
ops::require::op_require_break_on_next_statement,
ops::util::op_node_guess_handle_type,
+ ops::worker_threads::op_worker_threads_filename<P>,
ops::crypto::op_node_create_private_key,
ops::crypto::op_node_create_public_key,
ops::ipc::op_node_child_ipc_pipe,
diff --git a/ext/node/ops/mod.rs b/ext/node/ops/mod.rs
index ae798d181..8aed274bc 100644
--- a/ext/node/ops/mod.rs
+++ b/ext/node/ops/mod.rs
@@ -11,4 +11,5 @@ pub mod require;
pub mod util;
pub mod v8;
pub mod winerror;
+pub mod worker_threads;
pub mod zlib;
diff --git a/ext/node/ops/require.rs b/ext/node/ops/require.rs
index 6ca0b4a1a..d14e1d732 100644
--- a/ext/node/ops/require.rs
+++ b/ext/node/ops/require.rs
@@ -195,7 +195,9 @@ pub fn op_require_resolve_deno_dir(
resolver
.resolve_package_folder_from_package(
&request,
- &ModuleSpecifier::from_file_path(parent_filename).unwrap(),
+ &ModuleSpecifier::from_file_path(&parent_filename).unwrap_or_else(|_| {
+ panic!("Url::from_file_path: [{:?}]", parent_filename)
+ }),
NodeResolutionMode::Execution,
)
.ok()
diff --git a/ext/node/ops/worker_threads.rs b/ext/node/ops/worker_threads.rs
new file mode 100644
index 000000000..18a4157d4
--- /dev/null
+++ b/ext/node/ops/worker_threads.rs
@@ -0,0 +1,87 @@
+// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::error::generic_error;
+use deno_core::error::AnyError;
+use deno_core::op2;
+use deno_core::url::Url;
+use deno_core::OpState;
+use deno_fs::FileSystemRc;
+use std::path::Path;
+use std::path::PathBuf;
+use std::rc::Rc;
+
+use crate::resolution;
+use crate::NodePermissions;
+use crate::NodeResolver;
+use crate::NpmResolverRc;
+
+fn ensure_read_permission<P>(
+ state: &mut OpState,
+ file_path: &Path,
+) -> Result<(), AnyError>
+where
+ P: NodePermissions + 'static,
+{
+ let resolver = state.borrow::<NpmResolverRc>();
+ let permissions = state.borrow::<P>();
+ resolver.ensure_read_permission(permissions, file_path)
+}
+
+#[op2]
+#[string]
+pub fn op_worker_threads_filename<P>(
+ state: &mut OpState,
+ #[string] specifier: String,
+) -> Result<String, AnyError>
+where
+ P: NodePermissions + 'static,
+{
+ if specifier.starts_with("data:") {
+ return Ok(specifier);
+ }
+ let url: Url = if specifier.starts_with("file:") {
+ Url::parse(&specifier)?
+ } else {
+ let path = PathBuf::from(&specifier);
+ if path.is_relative() && !specifier.starts_with('.') {
+ return Err(generic_error(
+ "Relative path entries must start with '.' or '..'",
+ ));
+ }
+ ensure_read_permission::<P>(state, &path)?;
+ let fs = state.borrow::<FileSystemRc>();
+ let canonicalized_path =
+ deno_core::strip_unc_prefix(fs.realpath_sync(&path)?);
+ Url::from_file_path(canonicalized_path)
+ .map_err(|e| generic_error(format!("URL from Path-String: {:#?}", e)))?
+ };
+ let url_path = url
+ .to_file_path()
+ .map_err(|e| generic_error(format!("URL to Path-String: {:#?}", e)))?;
+ ensure_read_permission::<P>(state, &url_path)?;
+ let fs = state.borrow::<FileSystemRc>();
+ if !fs.exists_sync(&url_path) {
+ return Err(generic_error(format!("File not found [{:?}]", url_path)));
+ }
+ let node_resolver = state.borrow::<Rc<NodeResolver>>();
+ match node_resolver.url_to_node_resolution(url)? {
+ resolution::NodeResolution::Esm(u) => Ok(u.to_string()),
+ resolution::NodeResolution::CommonJs(u) => wrap_cjs(u),
+ _ => Err(generic_error("Neither ESM nor CJS")),
+ }
+}
+
+///
+/// Wrap a CJS file-URL and the required setup in a stringified `data:`-URL
+///
+fn wrap_cjs(url: Url) -> Result<String, AnyError> {
+ let path = url
+ .to_file_path()
+ .map_err(|e| generic_error(format!("URL to Path: {:#?}", e)))?;
+ let filename = path.file_name().unwrap().to_string_lossy();
+ Ok(format!(
+ "data:text/javascript,import {{ createRequire }} from \"node:module\";\
+ const require = createRequire(\"{}\"); require(\"./{}\");",
+ url, filename,
+ ))
+}
diff --git a/ext/node/polyfills/worker_threads.ts b/ext/node/polyfills/worker_threads.ts
index 49f2f3e3e..ab3834132 100644
--- a/ext/node/polyfills/worker_threads.ts
+++ b/ext/node/polyfills/worker_threads.ts
@@ -9,7 +9,7 @@ import {
op_host_recv_message,
op_host_terminate_worker,
op_message_port_recv_message_sync,
- op_require_read_closest_package_json,
+ op_worker_threads_filename,
} from "ext:core/ops";
import {
deserializeJsMessageData,
@@ -24,7 +24,6 @@ import { log } from "ext:runtime/06_util.js";
import { notImplemented } from "ext:deno_node/_utils.ts";
import { EventEmitter } from "node:events";
import { BroadcastChannel } from "ext:deno_broadcast_channel/01_broadcast_channel.js";
-import { isAbsolute, resolve } from "node:path";
const { ObjectPrototypeIsPrototypeOf } = primordials;
const {
@@ -32,14 +31,8 @@ const {
Symbol,
SymbolFor,
SymbolIterator,
- StringPrototypeEndsWith,
- StringPrototypeReplace,
- StringPrototypeMatch,
- StringPrototypeReplaceAll,
- StringPrototypeToString,
StringPrototypeTrim,
SafeWeakMap,
- SafeRegExp,
SafeMap,
TypeError,
} = primordials;
@@ -66,74 +59,6 @@ export interface WorkerOptions {
name?: string;
}
-const WHITESPACE_ENCODINGS: Record<string, string> = {
- "\u0009": "%09",
- "\u000A": "%0A",
- "\u000B": "%0B",
- "\u000C": "%0C",
- "\u000D": "%0D",
- "\u0020": "%20",
-};
-
-function encodeWhitespace(string: string): string {
- return StringPrototypeReplaceAll(string, new SafeRegExp(/[\s]/g), (c) => {
- return WHITESPACE_ENCODINGS[c] ?? c;
- });
-}
-
-function toFileUrlPosix(path: string): URL {
- if (!isAbsolute(path)) {
- throw new TypeError("Must be an absolute path.");
- }
- const url = new URL("file:///");
- url.pathname = encodeWhitespace(
- StringPrototypeReplace(
- StringPrototypeReplace(path, new SafeRegExp(/%/g), "%25"),
- new SafeRegExp(/\\/g),
- "%5C",
- ),
- );
- return url;
-}
-
-function toFileUrlWin32(path: string): URL {
- if (!isAbsolute(path)) {
- throw new TypeError("Must be an absolute path.");
- }
- const { 0: _, 1: hostname, 2: pathname } = StringPrototypeMatch(
- path,
- new SafeRegExp(/^(?:[/\\]{2}([^/\\]+)(?=[/\\](?:[^/\\]|$)))?(.*)/),
- );
- const url = new URL("file:///");
- url.pathname = encodeWhitespace(
- StringPrototypeReplace(pathname, new SafeRegExp(/%/g), "%25"),
- );
- if (hostname != null && hostname != "localhost") {
- url.hostname = hostname;
- if (!url.hostname) {
- throw new TypeError("Invalid hostname.");
- }
- }
- return url;
-}
-
-/**
- * Converts a path string to a file URL.
- *
- * ```ts
- * toFileUrl("/home/foo"); // new URL("file:///home/foo")
- * toFileUrl("\\home\\foo"); // new URL("file:///home/foo")
- * toFileUrl("C:\\Users\\foo"); // new URL("file:///C:/Users/foo")
- * toFileUrl("\\\\127.0.0.1\\home\\foo"); // new URL("file://127.0.0.1/home/foo")
- * ```
- * @param path to convert to file URL
- */
-function toFileUrl(path: string): URL {
- return core.build.os == "windows"
- ? toFileUrlWin32(path)
- : toFileUrlPosix(path);
-}
-
const privateWorkerRef = Symbol("privateWorkerRef");
class NodeWorker extends EventEmitter {
#id = 0;
@@ -162,29 +87,23 @@ class NodeWorker extends EventEmitter {
constructor(specifier: URL | string, options?: WorkerOptions) {
super();
- if (options?.eval === true) {
+
+ if (
+ typeof specifier === "object" &&
+ !(specifier.protocol === "data:" || specifier.protocol === "file:")
+ ) {
+ throw new TypeError(
+ "node:worker_threads support only 'file:' and 'data:' URLs",
+ );
+ }
+ if (options?.eval) {
specifier = `data:text/javascript,${specifier}`;
- } else if (typeof specifier === "string") {
- specifier = resolve(specifier);
- let pkg;
- try {
- pkg = op_require_read_closest_package_json(specifier);
- } catch (_) {
- // empty catch block when package json might not be present
- }
- if (
- !(StringPrototypeEndsWith(
- StringPrototypeToString(specifier),
- ".mjs",
- )) ||
- (pkg && pkg.exists && pkg.typ == "module")
- ) {
- const cwdFileUrl = toFileUrl(Deno.cwd());
- specifier =
- `data:text/javascript,(async function() {const { createRequire } = await import("node:module");const require = createRequire("${cwdFileUrl}");require("${specifier}");})();`;
- } else {
- specifier = toFileUrl(specifier as string);
- }
+ } else if (
+ !(typeof specifier === "object" && specifier.protocol === "data:")
+ ) {
+ // deno-lint-ignore prefer-primordials
+ specifier = specifier.toString();
+ specifier = op_worker_threads_filename(specifier);
}
// TODO(bartlomieu): this doesn't match the Node.js behavior, it should be