Skip to content

Commit

Permalink
feat: implement Console object with import node:console or console
Browse files Browse the repository at this point in the history
  • Loading branch information
fredbonin committed Mar 20, 2024
1 parent 49b8a75 commit 72af0bc
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 3 deletions.
3 changes: 3 additions & 0 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ Everything else inherited from [Uint8Array](https://developer.mozilla.org/en-US/
[spawn](https://nodejs.org/api/child_process.html#child_processspawncommand-args-options)

## console
[Console](https://nodejs.org/api/console.html#class-console)

## crypto

[createHash](https://nodejs.org/api/crypto.html#cryptocreatehashalgorithm-options)
Expand Down
2 changes: 2 additions & 0 deletions build.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ const ES_BUILD_OPTIONS = {
platform: "browser",
format: "esm",
external: [
"node:console",
"console",
"crypto",
"uuid",
"hex",
Expand Down
66 changes: 65 additions & 1 deletion src/console.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,17 @@ use std::{
use chrono::{DateTime, Utc};
use fxhash::FxHashSet;
use once_cell::sync::Lazy;
use rquickjs::Array;
use rquickjs::module::{Declarations, Exports, ModuleDef};
use rquickjs::{
atom::PredefinedAtom,
object::Filter,
prelude::{Func, Rest, This},
Ctx, Function, Object, Result, Type, Value,
};
use rquickjs::{Array, Class};

use crate::json::stringify::json_stringify;
use crate::module::export_default;
use crate::{
json::escape::escape_json,
number::float_to_string,
Expand Down Expand Up @@ -84,6 +86,27 @@ impl LogLevel {

pub static LAMBDA_REQUEST_ID: Lazy<Mutex<Option<String>>> = Lazy::new(|| Mutex::new(None));

pub struct ConsoleModule;

impl ModuleDef for ConsoleModule {
fn declare(declare: &mut Declarations) -> Result<()> {
declare.declare(stringify!(Console))?;
declare.declare("default")?;

Ok(())
}

fn evaluate<'js>(ctx: &Ctx<'js>, exports: &mut Exports<'js>) -> Result<()> {
Class::<Console>::register(ctx)?;

export_default(ctx, exports, |default| {
Class::<Console>::define(default)?;

Ok(())
})
}
}

pub fn init(ctx: &Ctx<'_>) -> Result<()> {
let globals = ctx.globals();

Expand Down Expand Up @@ -793,6 +816,47 @@ fn log_std_err<'js>(ctx: Ctx<'js>, args: Rest<Value<'js>>, level: LogLevel) -> R
write_log(stderr(), ctx, args, level)
}

#[derive(rquickjs::class::Trace)]
#[rquickjs::class]
pub struct Console {}

#[rquickjs::methods(rename_all = "camelCase")]
impl Console {
#[qjs(constructor)]
pub fn new() -> Self {
// We ignore the parameters for now since we don't support stream
Self {}
}

pub fn log<'js>(&self, ctx: Ctx<'js>, args: Rest<Value<'js>>) -> Result<()> {
log(ctx, args)
}

pub fn debug<'js>(&self, ctx: Ctx<'js>, args: Rest<Value<'js>>) -> Result<()> {
log_debug(ctx, args)
}
pub fn info<'js>(&self, ctx: Ctx<'js>, args: Rest<Value<'js>>) -> Result<()> {
log(ctx, args)
}
pub fn trace<'js>(&self, ctx: Ctx<'js>, args: Rest<Value<'js>>) -> Result<()> {
log_trace(ctx, args)
}
pub fn error<'js>(&self, ctx: Ctx<'js>, args: Rest<Value<'js>>) -> Result<()> {
log_error(ctx, args)
}
pub fn warn<'js>(&self, ctx: Ctx<'js>, args: Rest<Value<'js>>) -> Result<()> {
log_warn(ctx, args)
}
pub fn assert<'js>(
&self,
ctx: Ctx<'js>,
expression: bool,
args: Rest<Value<'js>>,
) -> Result<()> {
log_assert(ctx, expression, args)
}
}

#[cfg(test)]
mod tests {

Expand Down
28 changes: 26 additions & 2 deletions src/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ use crate::{
bytecode::{BYTECODE_COMPRESSED, BYTECODE_UNCOMPRESSED, BYTECODE_VERSION, SIGNATURE_LENGTH},
child_process::ChildProcessModule,
console,
console::ConsoleModule,
crypto::{CryptoModule, SYSTEM_RANDOM},
encoding::HexModule,
environment,
Expand Down Expand Up @@ -67,8 +68,8 @@ use crate::{
macro_rules! create_modules {
($($name:expr => $module:expr),*) => {

pub fn create_module_instances() -> (BuiltinResolver, ModuleLoader, HashSet<&'static str>) {
let mut builtin_resolver = BuiltinResolver::default();
pub fn create_module_instances() -> (ModuleResolver, ModuleLoader, HashSet<&'static str>) {
let mut builtin_resolver = ModuleResolver::default();
let mut module_loader = ModuleLoader::default();
let mut module_names = HashSet::new();

Expand All @@ -88,6 +89,28 @@ macro_rules! create_modules {
};
}

#[derive(Debug, Default)]
pub struct ModuleResolver {
builtin_resolver: BuiltinResolver,
}

impl ModuleResolver {
#[must_use]
pub fn with_module<P: Into<String>>(mut self, path: P) -> Self {
self.builtin_resolver.add_module(path.into());
self
}
}

impl Resolver for ModuleResolver {
fn resolve(&mut self, ctx: &Ctx<'_>, base: &str, name: &str) -> Result<String> {
// Strip node prefix so that we support both with and without
let name = name.strip_prefix("node:").unwrap_or(name);

self.builtin_resolver.resolve(ctx, base, name)
}
}

create_modules!(
"crypto" => CryptoModule,
"hex" => HexModule,
Expand All @@ -98,6 +121,7 @@ create_modules!(
"events" => EventsModule,
"module" => ModuleModule,
"net" => NetModule,
"console" => ConsoleModule,
"path" => PathModule,
"xml" => XmlModule,
"buffer" => BufferModule,
Expand Down
37 changes: 37 additions & 0 deletions tests/unit/console.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import * as timers from "timers";
import { Console as NodeConsole } from "node:console";
import { Console } from "console";

function log(...args: any[]) {
return (console as any).__formatPlain(...args);
Expand All @@ -25,6 +27,41 @@ it("should log module", () => {
`.trim()
);
});
it("should log using console object", () => {
const consoleObj = new Console({
stdout: process.stdout,
stderr: process.stderr,
});

// we check if the log does not throw an exception when called
consoleObj.log("log");
consoleObj.debug("debug");
consoleObj.info("info");
consoleObj.assert(false, "text for assertion should display");
consoleObj.assert(true, "This text should not be seen");

consoleObj.warn("warn");
consoleObj.error("error");
consoleObj.trace("trace");
});

it("should log using node:console object", () => {
const consoleObj = new NodeConsole({
stdout: process.stdout,
stderr: process.stderr,
});

// we check if the log does not throw an exception when called
consoleObj.log("log");
consoleObj.debug("debug");
consoleObj.info("info");
consoleObj.assert(false, "text for assertion should display");
consoleObj.assert(true, "This text should not be seen");

consoleObj.warn("warn");
consoleObj.error("error");
consoleObj.trace("trace");
});

it("should log complex object", () => {
let date = new Date();
Expand Down

0 comments on commit 72af0bc

Please sign in to comment.