From 9802eaabdccc064251bfc82253d6fa193f1e9d1d Mon Sep 17 00:00:00 2001 From: Sam Gammon Date: Wed, 24 Apr 2024 17:10:20 -0700 Subject: [PATCH 1/4] feat: alpha8 module support Signed-off-by: Sam Gammon --- WORKSPACE | 4 +- elide/runtime/js/BUILD.bazel | 7 +- elide/runtime/js/config.bzl | 15 + elide/runtime/js/entrypoint.js | 101 ++++-- elide/runtime/js/modules/BUILD.bazel | 12 + elide/runtime/js/modules/assert/BUILD.bazel | 15 + elide/runtime/js/modules/assert/assert.ts | 179 +++++++++++ elide/runtime/js/modules/assert/index.ts | 2 + elide/runtime/js/modules/assert/package.json | 12 + elide/runtime/js/modules/buffer/buffer.ts | 3 +- elide/runtime/js/modules/buffer/index.ts | 2 - elide/runtime/js/modules/buffer/package.json | 10 +- elide/runtime/js/modules/esbuild.config.js | 16 + elide/runtime/js/modules/express/index.ts | 4 +- elide/runtime/js/modules/express/package.json | 9 +- elide/runtime/js/modules/fs/BUILD.bazel | 3 + elide/runtime/js/modules/fs/fs.ts | 28 +- elide/runtime/js/modules/fs/index.ts | 4 +- elide/runtime/js/modules/fs/package.json | 14 +- elide/runtime/js/modules/fs/promises.ts | 9 + .../js/modules/fs/promises/fs-async.ts | 2 + elide/runtime/js/modules/fs/promises/index.ts | 2 + .../runtime/js/modules/inspector/BUILD.bazel | 15 + elide/runtime/js/modules/inspector/index.ts | 2 + .../runtime/js/modules/inspector/inspector.ts | 2 + .../runtime/js/modules/inspector/package.json | 12 + elide/runtime/js/modules/os/BUILD.bazel | 15 + elide/runtime/js/modules/os/index.ts | 2 + elide/runtime/js/modules/os/os.ts | 2 + elide/runtime/js/modules/os/package.json | 12 + elide/runtime/js/modules/path/BUILD.bazel | 15 + elide/runtime/js/modules/path/index.ts | 2 + elide/runtime/js/modules/path/package.json | 12 + elide/runtime/js/modules/path/path.ts | 287 ++++++++++++++++++ elide/runtime/js/modules/process/BUILD.bazel | 15 + elide/runtime/js/modules/process/index.ts | 2 + elide/runtime/js/modules/process/package.json | 12 + elide/runtime/js/modules/process/process.ts | 167 ++++++++++ elide/runtime/js/modules/util/BUILD.bazel | 15 + elide/runtime/js/modules/util/index.ts | 2 + elide/runtime/js/modules/util/package.json | 12 + elide/runtime/js/modules/util/util.js | 14 + elide/runtime/js/tools/esbuild.config.js | 4 +- tools/bazel/cache.bazelrc | 1 - tools/defs/elide.bzl | 19 +- 45 files changed, 1047 insertions(+), 47 deletions(-) create mode 100644 elide/runtime/js/modules/assert/BUILD.bazel create mode 100644 elide/runtime/js/modules/assert/assert.ts create mode 100644 elide/runtime/js/modules/assert/index.ts create mode 100644 elide/runtime/js/modules/assert/package.json create mode 100644 elide/runtime/js/modules/esbuild.config.js create mode 100644 elide/runtime/js/modules/fs/promises.ts create mode 100644 elide/runtime/js/modules/fs/promises/fs-async.ts create mode 100644 elide/runtime/js/modules/fs/promises/index.ts create mode 100644 elide/runtime/js/modules/inspector/BUILD.bazel create mode 100644 elide/runtime/js/modules/inspector/index.ts create mode 100644 elide/runtime/js/modules/inspector/inspector.ts create mode 100644 elide/runtime/js/modules/inspector/package.json create mode 100644 elide/runtime/js/modules/os/BUILD.bazel create mode 100644 elide/runtime/js/modules/os/index.ts create mode 100644 elide/runtime/js/modules/os/os.ts create mode 100644 elide/runtime/js/modules/os/package.json create mode 100644 elide/runtime/js/modules/path/BUILD.bazel create mode 100644 elide/runtime/js/modules/path/index.ts create mode 100644 elide/runtime/js/modules/path/package.json create mode 100644 elide/runtime/js/modules/path/path.ts create mode 100644 elide/runtime/js/modules/process/BUILD.bazel create mode 100644 elide/runtime/js/modules/process/index.ts create mode 100644 elide/runtime/js/modules/process/package.json create mode 100644 elide/runtime/js/modules/process/process.ts create mode 100644 elide/runtime/js/modules/util/BUILD.bazel create mode 100644 elide/runtime/js/modules/util/index.ts create mode 100644 elide/runtime/js/modules/util/package.json create mode 100644 elide/runtime/js/modules/util/util.js diff --git a/WORKSPACE b/WORKSPACE index 707c2d5..61693c1 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -345,9 +345,9 @@ http_archive( ## -- Dependency Setup -- ## -load("@google_bazel_common//:workspace_defs.bzl", "google_common_workspace_rules") +#load("@google_bazel_common//:workspace_defs.bzl", "google_common_workspace_rules") -google_common_workspace_rules() +#google_common_workspace_rules() # toolchains diff --git a/elide/runtime/js/BUILD.bazel b/elide/runtime/js/BUILD.bazel index 83e7118..5915563 100644 --- a/elide/runtime/js/BUILD.bazel +++ b/elide/runtime/js/BUILD.bazel @@ -16,6 +16,7 @@ load( "//elide/runtime/js:config.bzl", "BASE_JS_EXTERNS", "TS_MODULES", + "NODE_BUILTINS", ) MODULE_NAME = "elide.runtime.js" @@ -76,10 +77,6 @@ runtime_dist( manifest = "MANIFEST.MF", target = ":runtime", modules = [ - "//elide/runtime/js/modules/%s" % m for m in [ - "buffer", - "express", - "fs", - ] + "//elide/runtime/js/modules/%s" % m for m in NODE_BUILTINS ], ) diff --git a/elide/runtime/js/config.bzl b/elide/runtime/js/config.bzl index bc915ff..5706cac 100644 --- a/elide/runtime/js/config.bzl +++ b/elide/runtime/js/config.bzl @@ -59,3 +59,18 @@ TS_MODULES = ["%s:%s" % (TS_MODULE_PACKAGE, t) for t in [ # General Modules "//elide/runtime/js/bridge:js-error", ] + +## Node Builtins +## ------------- +## Registers each Node API built-in module. +NODE_BUILTINS = [ + "assert", + "buffer", + "express", + "fs", + "inspector", + "os", + "path", + "process", + "util", +] diff --git a/elide/runtime/js/entrypoint.js b/elide/runtime/js/entrypoint.js index 8f2c328..5c931ea 100644 --- a/elide/runtime/js/entrypoint.js +++ b/elide/runtime/js/entrypoint.js @@ -15,7 +15,7 @@ goog.require('elide.runtime.js.intrinsics.url.URL'); * Type structure of a Node process object. * * @typedef {{ - * cwd: (function(): string), + * cwd: string, * NODE_DEBUG: boolean, * noDeprecation: boolean, * browser: boolean, @@ -28,22 +28,69 @@ goog.require('elide.runtime.js.intrinsics.url.URL'); let NodeProcess; /** - * Global Node.js-style `process` object. + * Type structure of a Node process with extra Elide-provided properties. * - * @type {!NodeProcess} + * @typedef {NodeProcess} +*/ +let EnhancedNodeProcess; + +/** + * Global symbol where application environment is injected. + * + * @const + * @type {!string} */ -const process = { - 'pid': -1, - 'cwd': () => "", - 'env': {}, - 'NODE_DEBUG': false, - 'NODE_ENV': "production", - 'noDeprecation': false, - 'browser': false, - 'version': 'v18.9.0' -}; +const APP_ENV = '__Elide_app_env__'; + +/** + * Global symbol where Elide version is injected. + * + * @const + * @type {!string} + */ +const RUNTIME_VERSION = '__Elide_version__'; + +/** + * Global symbol where the intrinsic process object is injected. + * + * @const + * @type {!string} + */ +const RUNTIME_PROCESS = '__Elide_node_process__'; + +/** + * Application environment injected by the Elide runtime. + * + * @type {!Object} + */ +const injectedApplicationEnvironment = globalThis[APP_ENV]; + +/** + * Elide version provided by the runtime. + * + * @type {!string} + */ +const elideVersion = globalThis[RUNTIME_VERSION]; + +/** + * Intrinsic process object, injected by the runtime. + * + * @type {!EnhancedNodeProcess} + */ +const intrinsicProcess = globalThis[RUNTIME_PROCESS]; + +/** + * Return the Node Process API to use. + * + * @returns {!EnhancedNodeProcess} + */ +function nodeProcessAPI() { + if (!intrinsicProcess) { + throw new Error('Node process API is not available.'); + } + return intrinsicProcess; +} -globalThis['process'] = process; globalThis['window'] = undefined; globalThis['gc'] = null; @@ -60,18 +107,32 @@ globalThis['self'] = App; * Global Elide object. * * @type {{ - * process: !NodeProcess, - * context: {build: boolean, runtime: boolean}, - * self: * + * version: !string, + * process: !EnhancedNodeProcess, + * context: {build: boolean, runtime: boolean} * }} */ const Elide = { - 'process': process, - 'self': globalThis, + 'process': nodeProcessAPI(), + 'version': elideVersion, 'context': { 'build': false, 'runtime': true }, - 'App': App, }; + globalThis['Elide'] = Elide; + +/** + * Global process proxy object. + * + * @type {!EnhancedNodeProcess} + */ +const process = /** @type {!EnhancedNodeProcess} */ ({ + 'pid': Elide.process.pid, + 'cwd': Elide.process.cwd, + 'env': Elide.process.env, + ...(Elide.process || {}), +}); + +globalThis['process'] = process; diff --git a/elide/runtime/js/modules/BUILD.bazel b/elide/runtime/js/modules/BUILD.bazel index e69de29..f2140e3 100644 --- a/elide/runtime/js/modules/BUILD.bazel +++ b/elide/runtime/js/modules/BUILD.bazel @@ -0,0 +1,12 @@ +load( + "//tools/defs/esbuild:exports.bzl", + "esbuild_config", +) +package( + default_visibility = ["//visibility:public"], +) + +esbuild_config( + name = "esbuild-config", + config_file = "esbuild.config.js", +) diff --git a/elide/runtime/js/modules/assert/BUILD.bazel b/elide/runtime/js/modules/assert/BUILD.bazel new file mode 100644 index 0000000..a2d3652 --- /dev/null +++ b/elide/runtime/js/modules/assert/BUILD.bazel @@ -0,0 +1,15 @@ +package( + default_visibility = ["//visibility:public"], +) +load( + "//tools/defs:elide.bzl", + "js_module", +) + +js_module( + name = "assert", + srcs = [ + "assert.ts", + "index.ts", + ], +) diff --git a/elide/runtime/js/modules/assert/assert.ts b/elide/runtime/js/modules/assert/assert.ts new file mode 100644 index 0000000..d94fb24 --- /dev/null +++ b/elide/runtime/js/modules/assert/assert.ts @@ -0,0 +1,179 @@ +/** + * Intrinsic: Assertions. + * + * Provides assertion primitives for use in testing and debugging. + */ + +/** + * Type: `AssertionErrorOptions`. + * + * Shape of options that can be provided to a new `AssertionError`. + */ +export interface AssertionErrorOptions { + /** If provided, the error message is set to this value. */ + message?: string; + + /** The `actual` property on the error instance. */ + actual?: any; + + /** The `expected` property on the error instance. */ + expected?: any; + + /** The `operator` property on the error instance. */ + operator?: string; + + /* If provided, the generated stack trace omits frames before this function. */ + stackStartFn?: Function; +} + +/** + * Type: `AssertionError` + * + * Represents an assertion failure. + */ +export class AssertionError implements Error, AssertionErrorOptions { + public name: string; + public message: string; + public actual: any; + public expected: any; + public operator?: string; + public generatedMessage: boolean; + public code: string; + public stack: string; + + /** + * Primary constructor. + + * @param options Options for the assertion error. + */ + constructor(options: AssertionErrorOptions) { + this.name = "AssertionError"; + this.message = options.message || ""; + this.actual = options.actual; + this.expected = options.expected; + this.operator = options.operator; + this.generatedMessage = !options.message; + this.code = "ERR_ASSERTION"; + this.stack = new Error().stack || ""; + } + + toString() { + return this.stack; + } +} + +/** + * Checks if a condition is truthy, throwing an `AssertionError` if it is not. + * + * @param condition Condition to check for truthiness. + * @param message Message to provide if the condition is falsy. + * @throws `AssertionError` if the condition is falsy. + * @see https://nodejs.org/api/assert.html#assertokvalue-message + */ +export function ok(condition: any, message?: string): void { + if (!condition) { + throw new AssertionError({ message }); + } +} + +/** + * Alias for function `ok`. + * + * @see https://nodejs.org/api/assert.html#assertvalue-message + */ +export const assert = ok; + +/** + * Checks if two values are equal, throwing an `AssertionError` if they are not. + * + * @param actual Actual value to test for equality. + * @param expected Expected value to test for equality. + * @param message Message to display, or error to throw, if the equality check fails. + * @throws `AssertionError` if the actual value is not equal to the expected value. + * @see https://nodejs.org/api/assert.html#assertequalactual-expected-message + */ +export function equal(actual: any, expected: any, message?: string | Error): void { + if (actual != expected) { + if (message) { + if (message instanceof Error) { + throw message; + } else { + throw new AssertionError({ + operator: '==', + message: message || `Expected ${actual} to equal ${expected}`, + stackStartFn: equal, + actual, + expected, + }); + } + } + } +} + +/** + * Checks if two object values are deeply equal, throwing an `AssertionError` if they are equal. + * + * Tests for deep equality between the `actual` and `expected` values. Recursively checks the equality of all owned + * properties. + * + * Deep equality means that the enumerable "own" properties of child objects are also recursively evaluated by the + * following rules: + * + * - Primitive values are compared with the `==` operator, with the exception of `NaN`. It is treated as being + * identical if both sides are `NaN`. + * - Type tags of objects should be the same. + * - Only enumerable "own" properties are considered. + * - `Error` names and messages are always compared, even if these are not enumerable properties. + * - Object wrappers are compared both as objects and unwrapped values. + * - `Object` properties are compared unordered. + * - Recursion stops when both sides differ or both sides encounter a circular reference. + * - Implementation does not test the `[[Prototype]]` of objects. + * - `Symbol` properties are not compared. + * - `WeakMap` and `WeakSet` comparison does not rely on their values. + * - `RegExp` lastIndex, flags, and source are always compared, even if these are not enumerable properties. + * + * This method is considered legacy within the Node API, and it is recommended that developers use `deepStrictEqual` + * for most purposes instead. + * + * @param actual Actual object value to test for deep equality. + * @param expected Expected object value to test for deep equality. + * @param message Message to display, or error to throw, if the equality check fails. + * @see https://nodejs.org/api/assert.html#assertdeepequalactual-expected-message + */ +export function deepEqual(actual: any, expected: any, message?: string | Error): void { + throw new Error("Not implemented: `assert.deepEqual`") +} + +/** + * Checks if two object values are deeply and strictly equal, throwing an `AssertionError` if they are equal. + * + * Tests for deep and strict equality between the `actual` and `expected` values. Recursively checks the + * strict equality of all owned properties. + * + * Deep equality means that the enumerable "own" properties of child objects are also recursively evaluated by the + * following rules: + * + * - Primitive values are compared using `Object.is` + * - Type tags of objects should be the same. + * - `[[Prototype]]` of objects is compared using the `===` operator. + * - Only enumerable "own" properties are considered. + * - `Error` names and messages are always compared, even if these are not enumerable properties. + * - Enumerable own `Symbol` properties are compared as well. + * - Object wrappers are compared both as objects and unwrapped values. + * - `Object` properties are compared unordered. + * - `Map` keys and `Set` items are compared unordered. + * - Recursion stops when both sides differ or both sides encounter a circular reference. + * - `WeakMap` and `WeakSet` comparison does not rely on their values. + * - `RegExp` lastIndex, flags, and source are always compared, even if these are not enumerable properties. + * + * @param actual Actual object value to test for deep equality. + * @param expected Expected object value to test for deep equality. + * @param message Message to display, or error to throw, if the equality check fails. + * @see https://nodejs.org/api/assert.html#assertdeepequalactual-expected-message + */ +export function deepStrictEqual(actual: any, expected: any, message?: string | Error): void { + throw new Error("Not implemented: `assert.deepStrictEqual`") +} + +// Export the `assert` function as the default entrypoint for the module. +export default assert; diff --git a/elide/runtime/js/modules/assert/index.ts b/elide/runtime/js/modules/assert/index.ts new file mode 100644 index 0000000..77f515a --- /dev/null +++ b/elide/runtime/js/modules/assert/index.ts @@ -0,0 +1,2 @@ + +export * from "./assert"; diff --git a/elide/runtime/js/modules/assert/package.json b/elide/runtime/js/modules/assert/package.json new file mode 100644 index 0000000..77db890 --- /dev/null +++ b/elide/runtime/js/modules/assert/package.json @@ -0,0 +1,12 @@ +{ + "name": "assert", + "type": "module", + "module": "assert.mjs", + "main": "assert.cjs", + "exports": { + ".": { + "import": "./assert.mjs", + "require": "./assert.cjs" + } + } +} diff --git a/elide/runtime/js/modules/buffer/buffer.ts b/elide/runtime/js/modules/buffer/buffer.ts index 172321b..a968927 100644 --- a/elide/runtime/js/modules/buffer/buffer.ts +++ b/elide/runtime/js/modules/buffer/buffer.ts @@ -7,5 +7,6 @@ /** * Export the intrinsic `Buffer` type as the main export, and also an export called `Buffer`. */ -export const Buffer = "hi"; +export const Buffer = globalThis['Buffer']; + export default Buffer; diff --git a/elide/runtime/js/modules/buffer/index.ts b/elide/runtime/js/modules/buffer/index.ts index a1a9812..2a9e5ab 100644 --- a/elide/runtime/js/modules/buffer/index.ts +++ b/elide/runtime/js/modules/buffer/index.ts @@ -1,4 +1,2 @@ -import Buffer from "./buffer"; export * from "./buffer"; -export default Buffer; diff --git a/elide/runtime/js/modules/buffer/package.json b/elide/runtime/js/modules/buffer/package.json index 456c533..c3b1611 100644 --- a/elide/runtime/js/modules/buffer/package.json +++ b/elide/runtime/js/modules/buffer/package.json @@ -1,6 +1,12 @@ { "name": "buffer", "type": "module", - "module": "buffer", - "main": "buffer.mjs" + "module": "buffer.mjs", + "main": "buffer.cjs", + "exports": { + ".": { + "import": "./buffer.mjs", + "require": "./buffer.cjs" + } + } } diff --git a/elide/runtime/js/modules/esbuild.config.js b/elide/runtime/js/modules/esbuild.config.js new file mode 100644 index 0000000..b64e68b --- /dev/null +++ b/elide/runtime/js/modules/esbuild.config.js @@ -0,0 +1,16 @@ +module.exports = { + charset: "utf8", + drop: ["debugger"], // @TODO: disable console once we have code in each module + minify: true, + target: "es2021", + treeShaking: false, + legalComments: "external", + keepNames: true, + platform: "neutral", + footer: { + js: ( + "// Elide JS Builtins. Copyright (c) 2023, Sam Gammon and the Elide Project Authors. All rights reserved." + + "\n// Components of this software are licensed separately. See https://github.com/elide-dev/elide for more." + ), + } +}; diff --git a/elide/runtime/js/modules/express/index.ts b/elide/runtime/js/modules/express/index.ts index 0e5c624..d8ea634 100644 --- a/elide/runtime/js/modules/express/index.ts +++ b/elide/runtime/js/modules/express/index.ts @@ -1,5 +1,5 @@ -import {Router} from "../../intrinsics/node/core"; +import { Router } from "../../intrinsics/node/core"; -export default function express(): Router { +export function express(): Router { return globalThis['express'](); }; diff --git a/elide/runtime/js/modules/express/package.json b/elide/runtime/js/modules/express/package.json index 979701e..0886779 100644 --- a/elide/runtime/js/modules/express/package.json +++ b/elide/runtime/js/modules/express/package.json @@ -1,5 +1,12 @@ { "name": "express", "type": "module", - "module": "index.mjs" + "module": "express.mjs", + "main": "express.cjs", + "exports": { + ".": { + "import": "./express.mjs", + "require": "./express.cjs" + } + } } diff --git a/elide/runtime/js/modules/fs/BUILD.bazel b/elide/runtime/js/modules/fs/BUILD.bazel index 8b16b7b..9ae8b33 100644 --- a/elide/runtime/js/modules/fs/BUILD.bazel +++ b/elide/runtime/js/modules/fs/BUILD.bazel @@ -12,4 +12,7 @@ js_module( "fs.ts", "index.ts", ], + deps = [ + "//elide/runtime/js/modules/path", + ], ) diff --git a/elide/runtime/js/modules/fs/fs.ts b/elide/runtime/js/modules/fs/fs.ts index 0f4f52f..f50a8be 100644 --- a/elide/runtime/js/modules/fs/fs.ts +++ b/elide/runtime/js/modules/fs/fs.ts @@ -4,6 +4,32 @@ * Provides a shim which offers a `fs` module implementation that is compatible with Node.js-style imports. */ +/** + * File system constants + */ +const constants = { + F_OK: 0, + R_OK: 4, + W_OK: 2, + X_OK: 1, +}; + +/** + * Check access to a file + * + * Tests a user's permissions for the file or directory specified by path. The mode argument is an optional integer + * that specifies the accessibility checks to be performed. mode should be either the value fs.constants.F_OK or a + * mask consisting of the bitwise OR of any of fs.constants.R_OK, fs.constants.W_OK, and fs.constants.X_OK (e.g. + * fs.constants.W_OK | fs.constants.R_OK). Check File access constants for possible values of mode. + * + * @param path Path to check access for + * @param mode Mode to check; defaults to `fs.constants.F_OK` + * @param callback Callback function to dispatch with failures + */ +export function access(path: string | Buffer | URL, mode: number = constants.F_OK, callback: (err: NodeJS.ErrnoException) => void): void { + console.log('access fs', {path, mode, callback}) +} + export default { - hi: "hey" + access, }; diff --git a/elide/runtime/js/modules/fs/index.ts b/elide/runtime/js/modules/fs/index.ts index 51fa346..ac3d480 100644 --- a/elide/runtime/js/modules/fs/index.ts +++ b/elide/runtime/js/modules/fs/index.ts @@ -1,4 +1,2 @@ -import fs from "./fs"; - -export default fs; +export * from "./fs"; diff --git a/elide/runtime/js/modules/fs/package.json b/elide/runtime/js/modules/fs/package.json index 2ca3989..1ff024d 100644 --- a/elide/runtime/js/modules/fs/package.json +++ b/elide/runtime/js/modules/fs/package.json @@ -1,6 +1,16 @@ { "name": "fs", "type": "module", - "module": "fs", - "main": "fs.mjs" + "module": "fs.mjs", + "main": "fs.cjs", + "exports": { + "./promises": { + "import": "./fs/promises.mjs", + "require": "./fs/promises.cjs" + }, + ".": { + "import": "./fs.mjs", + "require": "./fs.cjs" + } + } } diff --git a/elide/runtime/js/modules/fs/promises.ts b/elide/runtime/js/modules/fs/promises.ts new file mode 100644 index 0000000..8d9dfa8 --- /dev/null +++ b/elide/runtime/js/modules/fs/promises.ts @@ -0,0 +1,9 @@ +/** + * Intrinsic: Filesystem (Promises). + * + * Provides a shim which offers a `fs/promises` module implementation that is compatible with Node.js-style imports. + */ + +export default { + hi: "hey" +}; diff --git a/elide/runtime/js/modules/fs/promises/fs-async.ts b/elide/runtime/js/modules/fs/promises/fs-async.ts new file mode 100644 index 0000000..a88f9a3 --- /dev/null +++ b/elide/runtime/js/modules/fs/promises/fs-async.ts @@ -0,0 +1,2 @@ + +export {}; diff --git a/elide/runtime/js/modules/fs/promises/index.ts b/elide/runtime/js/modules/fs/promises/index.ts new file mode 100644 index 0000000..4888aaa --- /dev/null +++ b/elide/runtime/js/modules/fs/promises/index.ts @@ -0,0 +1,2 @@ + +export * from "./fs-async"; diff --git a/elide/runtime/js/modules/inspector/BUILD.bazel b/elide/runtime/js/modules/inspector/BUILD.bazel new file mode 100644 index 0000000..8c7695e --- /dev/null +++ b/elide/runtime/js/modules/inspector/BUILD.bazel @@ -0,0 +1,15 @@ +package( + default_visibility = ["//visibility:public"], +) +load( + "//tools/defs:elide.bzl", + "js_module", +) + +js_module( + name = "inspector", + srcs = [ + "inspector.ts", + "index.ts", + ], +) diff --git a/elide/runtime/js/modules/inspector/index.ts b/elide/runtime/js/modules/inspector/index.ts new file mode 100644 index 0000000..a88f9a3 --- /dev/null +++ b/elide/runtime/js/modules/inspector/index.ts @@ -0,0 +1,2 @@ + +export {}; diff --git a/elide/runtime/js/modules/inspector/inspector.ts b/elide/runtime/js/modules/inspector/inspector.ts new file mode 100644 index 0000000..a88f9a3 --- /dev/null +++ b/elide/runtime/js/modules/inspector/inspector.ts @@ -0,0 +1,2 @@ + +export {}; diff --git a/elide/runtime/js/modules/inspector/package.json b/elide/runtime/js/modules/inspector/package.json new file mode 100644 index 0000000..86b878a --- /dev/null +++ b/elide/runtime/js/modules/inspector/package.json @@ -0,0 +1,12 @@ +{ + "name": "inspector", + "type": "module", + "module": "inspector.mjs", + "main": "inspector.cjs", + "exports": { + ".": { + "import": "./inspector.mjs", + "require": "./inspector.cjs" + } + } +} diff --git a/elide/runtime/js/modules/os/BUILD.bazel b/elide/runtime/js/modules/os/BUILD.bazel new file mode 100644 index 0000000..23808cf --- /dev/null +++ b/elide/runtime/js/modules/os/BUILD.bazel @@ -0,0 +1,15 @@ +package( + default_visibility = ["//visibility:public"], +) +load( + "//tools/defs:elide.bzl", + "js_module", +) + +js_module( + name = "os", + srcs = [ + "os.ts", + "index.ts", + ], +) diff --git a/elide/runtime/js/modules/os/index.ts b/elide/runtime/js/modules/os/index.ts new file mode 100644 index 0000000..a88f9a3 --- /dev/null +++ b/elide/runtime/js/modules/os/index.ts @@ -0,0 +1,2 @@ + +export {}; diff --git a/elide/runtime/js/modules/os/os.ts b/elide/runtime/js/modules/os/os.ts new file mode 100644 index 0000000..a88f9a3 --- /dev/null +++ b/elide/runtime/js/modules/os/os.ts @@ -0,0 +1,2 @@ + +export {}; diff --git a/elide/runtime/js/modules/os/package.json b/elide/runtime/js/modules/os/package.json new file mode 100644 index 0000000..06c9b29 --- /dev/null +++ b/elide/runtime/js/modules/os/package.json @@ -0,0 +1,12 @@ +{ + "name": "os", + "type": "module", + "module": "os.mjs", + "main": "os.cjs", + "exports": { + ".": { + "import": "./os.mjs", + "require": "./os.cjs" + } + } +} diff --git a/elide/runtime/js/modules/path/BUILD.bazel b/elide/runtime/js/modules/path/BUILD.bazel new file mode 100644 index 0000000..5a16986 --- /dev/null +++ b/elide/runtime/js/modules/path/BUILD.bazel @@ -0,0 +1,15 @@ +package( + default_visibility = ["//visibility:public"], +) +load( + "//tools/defs:elide.bzl", + "js_module", +) + +js_module( + name = "path", + srcs = [ + "path.ts", + "index.ts", + ], +) diff --git a/elide/runtime/js/modules/path/index.ts b/elide/runtime/js/modules/path/index.ts new file mode 100644 index 0000000..b8d1e22 --- /dev/null +++ b/elide/runtime/js/modules/path/index.ts @@ -0,0 +1,2 @@ + +export * from "./path"; diff --git a/elide/runtime/js/modules/path/package.json b/elide/runtime/js/modules/path/package.json new file mode 100644 index 0000000..36cfbee --- /dev/null +++ b/elide/runtime/js/modules/path/package.json @@ -0,0 +1,12 @@ +{ + "name": "path", + "type": "module", + "main": "path.cjs", + "module": "path.mjs", + "exports": { + ".": { + "import": "./path.mjs", + "require": "./path.cjs" + } + } +} diff --git a/elide/runtime/js/modules/path/path.ts b/elide/runtime/js/modules/path/path.ts new file mode 100644 index 0000000..a757526 --- /dev/null +++ b/elide/runtime/js/modules/path/path.ts @@ -0,0 +1,287 @@ +/** + * Intrinsic: Path. + * + * Provides a shim which offers a `path` module implementation that is compatible with Node.js-style imports. + */ + +const internals: any = globalThis['__Elide_node_path__']; + +/** + * Represents an object with path properties. + */ +interface PathObject { + root: string; + dir: string; + base: string; + ext: string; + name: string; +} + +/** + * Intrinsic: Path. + * + * Provides a shim which offers a `path` module implementation that is compatible with Node.js-style imports. + */ +export interface NodePathAPI { + /** + * Filesystem path delimiter. + * + * Specifies the path separation delimiter to use for this platform. + */ + delimiter: string; + + /** + * Provides POSIX-oriented path methods. + */ + posix: NodePathAPI; + + /** + * Provides Windows-oriented path methods. + */ + win32: NodePathAPI; + + /** + * The `path.basename()` method returns the last portion of a path, similar to the Unix `basename` command. Trailing + * directory separators are ignored. + * + * @param path Path to get the base-name for + * @param ext Extension to trim (optional) + * @return The base-name of the path + */ + basename(path: string, ext?: string): string; + + /** + * The `path.dirname()` method returns the directory name of a path, similar to the Unix `dirname` command. + * + * @param path Path to get the directory name for + * @return The directory name of the path + */ + dirname(path: string): string; + + /** + * The `path.extname()` method returns the extension of the path, from the last occurrence of the `.` (period) + * character to end of string in the last portion of the path. + * + * @param path Path to get the extension for + * @return The extension of the path + */ + extname(path: string): string; + + /** + * The `path.format()` method returns a path string from an object. This is the reverse operation from `path.parse()`. + * + * @param pathObject Object with path properties + * @return The formatted path string + */ + format(pathObject: PathObject): string; + + /** + * The `path.isAbsolute()` method determines if the given path is an absolute path. + * + * @param path Path to check + * @return True if the path is absolute, false otherwise + */ + isAbsolute(path: string): boolean; + + /** + * The `path.join()` method joins all given path segments together using the platform-specific separator as a code, + * then normalizes the resulting path. + * + * @param paths Path segments to join + * @return The joined path + */ + join(...paths: string[]): string; + + /** + * The `path.normalize()` method normalizes the given path, resolving `'..'` and `'.'` segments. + * + * @param path Path to normalize + * @return The normalized path + */ + normalize(path: string): string; + + /** + * The `path.parse()` method returns an object whose properties represent significant elements of the path. + * + * @param path Path to parse + * @return An object with path properties + */ + parse(path: string): PathObject; + + /** + * The `path.relative()` method returns the relative path from `from` to `to` based on the current working directory. + * + * @param from Starting path + * @param to Destination path + * @return The relative path + */ + relative(from: string, to: string): string; + + /** + * The `path.resolve()` method resolves a sequence of paths or path segments into an absolute path. + * + * @param pathSegments Path segments to resolve + * @return The resolved absolute path + */ + resolve(...pathSegments: string[]): string; + + /** + * The `path.sep` property returns a platform-specific path segment separator. + */ + sep: string; + + /** + * The `path.toNamespacedPath()` method returns an equivalent path for the specified path allowing the use of + * string-based keys for `require()`, instead of the object-literal-based path mapping technique. + * + * @param path Path to convert + * @return The namespaced path + */ + toNamespacedPath(path: string): string; +} + +function getPathAPI(): NodePathAPI { + return internals as NodePathAPI || { + delimiter: ':', + sep: '/' + }; +} + +/** + * Provides POSIX-oriented path methods. + */ +export const posix = getPathAPI().posix; + +/** + * Provides Windows-oriented path methods. + */ +export const win32 = getPathAPI().win32; + +/** + * Filesystem path delimiter. + * + * Specifies the path separation delimiter to use for this platform. + */ +export const delimiter = getPathAPI().delimiter; + +/** + * The `path.sep` property returns a platform-specific path segment separator. + */ +export const sep = getPathAPI().sep; + +/** + * The `path.basename()` method returns the last portion of a path, similar to the Unix `basename` command. Trailing + * directory separators are ignored. + * + * @param path Path to get the base-name for + * @param ext Extension to trim (optional) + * @return The base-name of the path + */ +export function basename(path: string, ext?: string): string { + // @ts-expect-error type difference + return getPathAPI().basename(path, ext || null); +} + +/** + * The `path.dirname()` method returns the directory name of a path, similar to the Unix `dirname` command. + * + * @param path Path to get the directory name for + * @return The directory name of the path + */ +export function dirname(path: string): string { + return getPathAPI().dirname(path); +} + +/** + * The `path.extname()` method returns the extension of the path, from the last occurrence of the `.` (period) + * character to end of string in the last portion of the path. + * + * @param path Path to get the extension for + * @return The extension of the path + */ +export function extname(path: string): string { + return getPathAPI().extname(path); +} + +/** + * The `path.format()` method returns a path string from an object. This is the reverse operation from `path.parse()`. + * + * @param pathObject Object with path properties + * @return The formatted path string + */ +export function format(pathObject: PathObject): string { + return getPathAPI().format(pathObject); +} + +/** + * The `path.isAbsolute()` method determines if the given path is an absolute path. + * + * @param path Path to check + * @return True if the path is absolute, false otherwise + */ +export function isAbsolute(path: string): boolean { + return getPathAPI().isAbsolute(path); +} + +/** + * The `path.join()` method joins all given path segments together using the platform-specific separator as a code, + * then normalizes the resulting path. + * + * @param paths Path segments to join + * @return The joined path + */ +export function join(...paths: string[]): string { + return getPathAPI().join(...paths); +} + +/** + * The `path.normalize()` method normalizes the given path, resolving `'..'` and `'.'` segments. + * + * @param path Path to normalize + * @return The normalized path + */ +export function normalize(path: string): string { + return getPathAPI().normalize(path); +} + +/** + * The `path.parse()` method returns an object whose properties represent significant elements of the path. + * + * @param path Path to parse + * @return An object with path properties + */ +export function parse(path: string): PathObject { + return getPathAPI().parse(path); +} + +/** + * The `path.relative()` method returns the relative path from `from` to `to` based on the current working directory. + * + * @param from Starting path + * @param to Destination path + * @return The relative path + */ +export function relative(from: string, to: string): string { + return getPathAPI().relative(from, to); +} + +/** + * The `path.resolve()` method resolves a sequence of paths or path segments into an absolute path. + * + * @param pathSegments Path segments to resolve + * @return The resolved absolute path + */ +export function resolve(...pathSegments: string[]): string { + return getPathAPI().resolve(...pathSegments); +} + +/** + * The `path.toNamespacedPath()` method returns an equivalent path for the specified path allowing the use of + * string-based keys for `require()`, instead of the object-literal-based path mapping technique. + * + * @param path Path to convert + * @return The namespaced path + */ +export function toNamespacedPath(path: string): string { + return getPathAPI().toNamespacedPath(path); +} diff --git a/elide/runtime/js/modules/process/BUILD.bazel b/elide/runtime/js/modules/process/BUILD.bazel new file mode 100644 index 0000000..9df4a49 --- /dev/null +++ b/elide/runtime/js/modules/process/BUILD.bazel @@ -0,0 +1,15 @@ +package( + default_visibility = ["//visibility:public"], +) +load( + "//tools/defs:elide.bzl", + "js_module", +) + +js_module( + name = "process", + srcs = [ + "process.ts", + "index.ts", + ], +) diff --git a/elide/runtime/js/modules/process/index.ts b/elide/runtime/js/modules/process/index.ts new file mode 100644 index 0000000..8010912 --- /dev/null +++ b/elide/runtime/js/modules/process/index.ts @@ -0,0 +1,2 @@ + +export * from "./process"; diff --git a/elide/runtime/js/modules/process/package.json b/elide/runtime/js/modules/process/package.json new file mode 100644 index 0000000..544812a --- /dev/null +++ b/elide/runtime/js/modules/process/package.json @@ -0,0 +1,12 @@ +{ + "name": "process", + "type": "module", + "main": "process.cjs", + "module": "process.mjs", + "exports": { + ".": { + "import": "./process.mjs", + "require": "./process.cjs" + } + } +} diff --git a/elide/runtime/js/modules/process/process.ts b/elide/runtime/js/modules/process/process.ts new file mode 100644 index 0000000..b4a63fe --- /dev/null +++ b/elide/runtime/js/modules/process/process.ts @@ -0,0 +1,167 @@ +/** + * Intrinsic: Process. + * + * Provides a shim which offers a `process` module implementation that is compatible with Node.js-style imports. + */ + +const internals: any = globalThis['__Elide_node_process__']; + +/** + * Intrinsic: Process. + * + * Defines the API for the `process` module. + */ +export interface NodeProcessAPI { + /** + * Property: `env`. + * + * The environment variables. + */ + env: Record; + + /** + * Property: `argv`. + * + * The command-line arguments. + */ + argv: string[]; + + /** + * Property: `platform`. + * + * The platform identifier. + */ + platform: string; + + /** + * Property: `version`. + * + * The Node.js version. + */ + version: string; + + /** + * Property: `versions`. + * + * The Node.js version information. + */ + versions: Record; + + /** + * Property: `arch`. + * + * The architecture identifier. + */ + arch: string; + + /** + * Property: `pid`. + * + * The process identifier. + */ + pid: number; + + /** + * Property: `cwd`. + * + * The current working directory. + */ + cwd: string; + + /** + * Property: `exit`. + * + * The exit function. + */ + exit: (code?: number) => never; + + /** + * Property: `nextTick`. + * + * The next tick function. + */ + nextTick: (callback: (...args: any[]) => void, ...args: any[]) => void; +} + +function getProcessAPI(): NodeProcessAPI { + return internals as NodeProcessAPI || {}; +} + +/** + * Property: `env`. + * + * Environment variables made available to the program. + */ +export const env = getProcessAPI().env; + +/** + * Property: `argv`. + * + * The command-line arguments. + */ +export const argv = getProcessAPI().argv; + +/** + * Property: `platform`. + * + * The platform identifier. + */ +export const platform = getProcessAPI().platform; + +/** + * Property: `version`. + * + * The Node.js version. + */ +export const version = getProcessAPI().version; + +/** + * Property: `versions`. + * + * The Node.js version information. + */ +export const versions = getProcessAPI().versions; + +/** + * Property: `arch`. + * + * The architecture identifier. + */ +export const arch = getProcessAPI().arch; + +/** + * Property: `pid`. + * + * The process identifier. + */ +export const pid = getProcessAPI().pid; + +/** + * Property: `cwd`. + * + * The current working directory. + */ +export const cwd = getProcessAPI().cwd; + +/** + * Property: `exit`. + * + * Exits the process with the specified exit code. + * + * @param code The exit code. + */ +export function exit(code?: number): never { + return getProcessAPI().exit(code); +} + +/** + * Property: `nextTick`. + * + * Queues a function to be executed on the next tick of the event loop. + * + * @param callback The function to execute. + * @param args The arguments to pass to the function. + */ +export function nextTick(callback: (...args: any[]) => void, ...args: any[]): void { + return getProcessAPI().nextTick(callback, ...args); +} diff --git a/elide/runtime/js/modules/util/BUILD.bazel b/elide/runtime/js/modules/util/BUILD.bazel new file mode 100644 index 0000000..5509719 --- /dev/null +++ b/elide/runtime/js/modules/util/BUILD.bazel @@ -0,0 +1,15 @@ +package( + default_visibility = ["//visibility:public"], +) +load( + "//tools/defs:elide.bzl", + "js_module", +) + +js_module( + name = "util", + srcs = [ + "util.js", + "index.ts", + ], +) diff --git a/elide/runtime/js/modules/util/index.ts b/elide/runtime/js/modules/util/index.ts new file mode 100644 index 0000000..2bdf8f8 --- /dev/null +++ b/elide/runtime/js/modules/util/index.ts @@ -0,0 +1,2 @@ + +export * from "./util"; diff --git a/elide/runtime/js/modules/util/package.json b/elide/runtime/js/modules/util/package.json new file mode 100644 index 0000000..c695e8c --- /dev/null +++ b/elide/runtime/js/modules/util/package.json @@ -0,0 +1,12 @@ +{ + "name": "util", + "type": "module", + "main": "util.cjs", + "module": "util.mjs", + "exports": { + ".": { + "require": "./util.cjs", + "import": "./util.mjs" + } + } +} diff --git a/elide/runtime/js/modules/util/util.js b/elide/runtime/js/modules/util/util.js new file mode 100644 index 0000000..94b1052 --- /dev/null +++ b/elide/runtime/js/modules/util/util.js @@ -0,0 +1,14 @@ +// from `isaccs/inherits` +export function inherits(ctor, superCtor) { + if (superCtor) { + ctor.super_ = superCtor; + var TempCtor = function () {}; + TempCtor.prototype = superCtor.prototype; + ctor.prototype = new TempCtor(); + ctor.prototype.constructor = ctor; + } +} + +export const TextEncoder = globalThis['TextEncoder']; + +export const TextDecoder = globalThis['TextDecoder']; diff --git a/elide/runtime/js/tools/esbuild.config.js b/elide/runtime/js/tools/esbuild.config.js index 5cb47ea..1d88448 100644 --- a/elide/runtime/js/tools/esbuild.config.js +++ b/elide/runtime/js/tools/esbuild.config.js @@ -8,8 +8,8 @@ module.exports = { platform: "neutral", footer: { js: ( - "// Elide JS Runtime. Copyright (c) 2022, Sam Gammon and the Elide Project Authors. All rights reserved." + - "\n// Components of this software are licensed separately. See https://github.com/elide-dev/v3 for more." + "// Elide JS Runtime. Copyright (c) 2023, Sam Gammon and the Elide Project Authors. All rights reserved." + + "\n// Components of this software are licensed separately. See https://github.com/elide-dev/elide for more." ), } }; diff --git a/tools/bazel/cache.bazelrc b/tools/bazel/cache.bazelrc index 4667899..cc15697 100644 --- a/tools/bazel/cache.bazelrc +++ b/tools/bazel/cache.bazelrc @@ -4,7 +4,6 @@ # build --config=disk-cache -build --config=buildless build:buildless --remote_cache=https://bazel.less.build/cache/generic build:buildless --remote_timeout=300s diff --git a/tools/defs/elide.bzl b/tools/defs/elide.bzl index a4813d4..49aae60 100644 --- a/tools/defs/elide.bzl +++ b/tools/defs/elide.bzl @@ -164,12 +164,24 @@ def _js_module( output = "%s.mjs" % (module or name), sourcemap = "external", target = _JS_TARGET, + config = "//elide/runtime/js/modules:esbuild-config", + ) + _esbuild( + name = "%s.jsopt.cjs" % name, + srcs = js_srcs + srcs, + entry_point = entry_point, + format = "cjs", + output = "%s.cjs" % (module or name), + sourcemap = "external", + target = _JS_TARGET, + config = "//elide/runtime/js/modules:esbuild-config", ) native.filegroup( name = "%s_module_src" % name, srcs = [ package_json, "%s.mjs" % name, + "%s.cjs" % name, ], ) else: @@ -230,13 +242,13 @@ def _js_module( _pkg_filegroup( name = "%s.tarfilegroup" % name, srcs = [":%s.tarfiles" % name], - prefix = "node_modules/%s/" % (module or name), + prefix = "__runtime__/%s/" % (module or name), ) _pkg_tar( name = "%s.tarball" % name, out = "%s.tar" % name, srcs = [":%s_module_src" % name], - package_dir = "node_modules/%s/" % (module or name), + package_dir = "__runtime__/%s/" % (module or name), ) native.alias( name = name, @@ -436,6 +448,7 @@ def _runtime_dist(name, language, target, manifest, info = [], configs = [], mod _pkg_tar( name = "%s.modules" % language, out = "%s.modules.tar.gz" % language, + extension = "tar.gz", srcs = modules, ) outs.append(":%s.modules" % language) @@ -460,7 +473,7 @@ def _runtime_dist(name, language, target, manifest, info = [], configs = [], mod _pkg_tar( name = "dist-all", - out = "%s.dist-all.tar.gz" % language, + out = "%s.dist-all.tar" % language, srcs = [":distributions"], ) native.filegroup( From 2517f52406ebd90bb4123839efdc3c64eccf9aaf Mon Sep 17 00:00:00 2001 From: Sam Gammon Date: Wed, 24 Apr 2024 17:12:03 -0700 Subject: [PATCH 2/4] fix: graalvm install in ci Signed-off-by: Sam Gammon --- .github/workflows/build.ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/build.ci.yml b/.github/workflows/build.ci.yml index 15b3618..3c37a69 100644 --- a/.github/workflows/build.ci.yml +++ b/.github/workflows/build.ci.yml @@ -72,8 +72,6 @@ jobs: - name: "Setup: GraalVM (Java ${{ vars.JVM_VERSION }})" uses: graalvm/setup-graalvm@d1891786152ae96fee67f86c3a1eae596291bbed # v1 with: - components: "native-image,js" - version: '22.3.1' java-version: ${{ vars.JVM_VERSION || '19' }} check-for-updates: true github-token: ${{ secrets.GITHUB_TOKEN }} From 88825e1f62ac1fe0d600e7d4f9f1d1173fa95241 Mon Sep 17 00:00:00 2001 From: Sam Gammon Date: Thu, 25 Apr 2024 17:35:24 -0700 Subject: [PATCH 3/4] feat: implement `os` and better module access Signed-off-by: Sam Gammon --- elide/runtime/js/modules/fs/fs.ts | 59 +++++ elide/runtime/js/modules/fs/promises.ts | 5 +- elide/runtime/js/modules/os/index.ts | 2 +- elide/runtime/js/modules/os/os.ts | 266 +++++++++++++++++++- elide/runtime/js/modules/process/process.ts | 11 +- 5 files changed, 336 insertions(+), 7 deletions(-) diff --git a/elide/runtime/js/modules/fs/fs.ts b/elide/runtime/js/modules/fs/fs.ts index f50a8be..8fce61e 100644 --- a/elide/runtime/js/modules/fs/fs.ts +++ b/elide/runtime/js/modules/fs/fs.ts @@ -30,6 +30,65 @@ export function access(path: string | Buffer | URL, mode: number = constants.F_O console.log('access fs', {path, mode, callback}) } +/** + * Read a file + * + * Asynchronously reads the entire contents of a file. The encoding option is ignored if data is a buffer. + * + * @param path Path to the file + * @param options Options for reading the file + * @param callback Callback function to dispatch with the file contents + */ +export function readFile(path: string | Buffer | URL, options: { encoding: string; flag?: string; }, callback: (err: NodeJS.ErrnoException, data: string) => void): void { + console.log('readFile fs', {path, options, callback}) +} + +/** + * Read a file synchronously + * + * Synchronously reads the entire contents of a file. The encoding option is ignored if data is a buffer. + * + * @param path Path to the file + * @param options Options for reading the file + * @returns The file contents + */ +export function readFileSync(path: string | Buffer | URL, options: { encoding: string; flag?: string; }): string { + console.log('readFileSync fs', {path, options}) + return '' +} + +/** + * Write a file + * + * Asynchronously writes data to a file, replacing the file if it already exists. data can be a string or a buffer. + * + * @param path Path to the file + * @param data Data to write to the file + * @param options Options for writing the file + * @param callback Callback function to dispatch with the file contents + */ +export function writeFile(path: string | Buffer | URL, data: any, options: { encoding: string; mode?: number; flag?: string; }, callback: (err: NodeJS.ErrnoException) => void): void { + console.log('writeFile fs', {path, data, options, callback}) +} + +/** + * Write a file synchronously + * + * Synchronously writes data to a file, replacing the file if it already exists. data can be a string or a buffer. + * + * @param path Path to the file + * @param data Data to write to the file + * @param options Options for writing the file + */ +export function writeFileSync(path: string | Buffer | URL, data: any, options: { encoding: string; mode?: number; flag?: string; }): void { + console.log('writeFileSync fs', {path, data, options}) +} + export default { access, + constants, + readFile, + readFileSync, + writeFile, + writeFileSync, }; diff --git a/elide/runtime/js/modules/fs/promises.ts b/elide/runtime/js/modules/fs/promises.ts index 8d9dfa8..758de6e 100644 --- a/elide/runtime/js/modules/fs/promises.ts +++ b/elide/runtime/js/modules/fs/promises.ts @@ -4,6 +4,5 @@ * Provides a shim which offers a `fs/promises` module implementation that is compatible with Node.js-style imports. */ -export default { - hi: "hey" -}; +export default {}; + diff --git a/elide/runtime/js/modules/os/index.ts b/elide/runtime/js/modules/os/index.ts index a88f9a3..cfe38d6 100644 --- a/elide/runtime/js/modules/os/index.ts +++ b/elide/runtime/js/modules/os/index.ts @@ -1,2 +1,2 @@ -export {}; +export * from "./os"; diff --git a/elide/runtime/js/modules/os/os.ts b/elide/runtime/js/modules/os/os.ts index a88f9a3..cc372bc 100644 --- a/elide/runtime/js/modules/os/os.ts +++ b/elide/runtime/js/modules/os/os.ts @@ -1,2 +1,266 @@ +/** + * Intrinsic: OS. + * + * Provides a shim which offers a `os` module implementation that is compatible with Node.js-style imports. + */ -export {}; +function intrinsic(): any { + // @ts-expect-error intrinsic symbol + const api = __Elide_node_os__; + if (!api) { + throw new Error(`The 'os' module failed to load the intrinsic API.`); + } + return api || {}; +} + +/** + * Resolves operating system constants for the current platform. + */ +export const constants = {}; + +/** + * Returns the operating system's end-of-line value. + */ +export const EOL = intrinsic().EOL; + +/** + * Returns the operating system's null device path. + */ +export const devNull = intrinsic().devNull; + +/** + * Returns the number of logical CPUs available to the current process. + * + * @returns The number of logical CPUs available to the current process + */ +export function availableParallelism(): number { + return intrinsic().availableParallelism() as number; +} + +/** + * Returns the operating system CPU architecture for which the Elide binary was compiled. + * + * Possible values follow the Node API: + * 'arm', 'arm64', 'ia32', 'loong64', 'mips', 'mipsel', 'ppc', 'ppc64', 'riscv64', 's390', 's390x', and 'x64'. + * + * @returns The operating system CPU architecture for which the Elide binary was compiled + */ +export function arch(): string { + return intrinsic().arch() as string; +} + +/** + * Describes CPU info as part of a return value from `cpus`. + */ +export type CpuInfo = { + model: string; + speed: number; + times: { + user: number; + nice: number; + sys: number; + idle: number; + irq: number; + }; +}; + +/** + * Returns an array of objects containing information about each logical CPU core. + * + * @returns An array of objects containing information about each logical CPU core + */ +export function cpus(): CpuInfo[] { + return intrinsic().cpus() as CpuInfo[]; +} + +/** + * Returns the endianness of the CPU for which the Elide binary was compiled. + * + * Possible values are 'BE' for big endian and 'LE' for little endian. + * + * @returns The endianness of the CPU for which the Elide binary was compiled + */ +export function endianness(): "BE" | "LE" { + return intrinsic().endianness() as "BE" | "LE"; +} + +/** + * Returns the amount of free system memory in bytes. + * + * @returns The amount of free system memory in bytes + */ +export function freemem(): number { + return intrinsic().freemem() as number; +} + +/** + * Returns the priority value for the provided process ID, or the current process, if no ID is given. + * + * @param pid Process ID to get the priority for + * @returns The priority value for the provided process ID + */ +export function getPriority(pid?: number): number { + return intrinsic().getPriority(pid) as number; +} + +/** + * Returns the home directory of the current user. + * + * @returns The home directory of the current user + */ +export function homedir(): string { + return intrinsic().homedir() as string; +} + +/** + * Returns the hostname of the operating system. + * + * @returns The hostname of the operating system + */ +export function hostname(): string { + return intrinsic().hostname() as string; +} + +/** + * Returns the load average of the system. + * + * @returns Load averages + */ +export function loadavg(): number[] { + return intrinsic().loadavg() as number[]; +} + +/** + * Returns the machine type. + * + * @returns The machine type + */ +export function machine(): string { + return intrinsic().machine() as string; +} + +/** + * Object shape of a network interface info payload returned by `networkInterfaces`. + */ +export type NetworkInterfaceInfo = { + address: string; + netmask: string; + family: string; + mac: string; + internal: boolean; + cidr: string; + scopeid?: number; +} + +/** + * Returns network interfaces. + * + * @returns Network interfaces + */ +export function networkInterfaces(): { [key: string]: NetworkInterfaceInfo[] } { + return intrinsic().networkInterfaces() as { [key: string]: NetworkInterfaceInfo[] }; +} + +/** + * Returns the operating system platform. + * + * Possible values are 'aix', 'android', 'darwin', 'freebsd', 'linux', 'openbsd', 'sunos', and 'win32'. + * + * @returns The operating system platform + */ +export function platform(): string { + return intrinsic().platform() as string; +} + +/** + * Returns the operating system as a string. + * + * @returns The operating system as a string + */ +export function release(): string { + return intrinsic().release() as string; +} + +/** + * Attempts to set the priority of the provided process ID, or the current process, if no ID is given. + * + * @param pidOrPriority Process ID to set the priority for, or the priority value to set if the value + * should be set for the current process. Default is `0`. + * @param priority Priority value to set for the provided process ID + */ +export function setPriority(pidOrPriority: number, priority?: number): void { + intrinsic().setPriority(pidOrPriority, priority); +} + +/** + * Returns the operating system's default directory for temporary files as a string. + * + * @returns The operating system's default directory for temporary files + */ +export function tmpdir(): string { + return intrinsic().tmpdir() as string; +} + +/** + * Returns the total amount of system memory in bytes. + * + * @returns The total amount of system memory in bytes + */ +export function totalmem(): number { + return intrinsic().totalmem() as number; +} + +/** + * Returns the operating system type as a string. + * + * @returns The operating system type as a string + */ +export function type(): string { + return intrinsic().type() as string; +} + +/** + * Returns the system uptime in seconds. + * + * @returns The system uptime in seconds + */ +export function uptime(): number { + return intrinsic().uptime() as number; +} + +/** + * Options which can be passed into the `userInfo` method. + */ +export type UserInfoOptions = { + encoding?: BufferEncoding; +} + +/** + * Object shape of a user info payload returned by `userInfo`. + */ +export type UserInfo = { + username: string; + uid: number; + gid: number; + shell: string; + homedir: string; +} + +/** + * Returns information about the currently effective user. + * + * @param options Options for retrieving user information + * @returns Information about the currently effective user + */ +export function userInfo(options?: UserInfoOptions): UserInfo { + return intrinsic().userInfo(options) as UserInfo; +} + +/** + * Returns the operating system version as a string. + * + * @returns The operating system version as a string + */ +export function version(): string { + return intrinsic().version() as string; +} diff --git a/elide/runtime/js/modules/process/process.ts b/elide/runtime/js/modules/process/process.ts index b4a63fe..4c1fe6b 100644 --- a/elide/runtime/js/modules/process/process.ts +++ b/elide/runtime/js/modules/process/process.ts @@ -4,7 +4,14 @@ * Provides a shim which offers a `process` module implementation that is compatible with Node.js-style imports. */ -const internals: any = globalThis['__Elide_node_process__']; +function intrinsic(): any { + // @ts-expect-error intrinsic symbol + const api = __Elide_node_process__; + if (!api) { + throw new Error(`The 'process' module failed to load the intrinsic API.`); + } + return api || {}; +} /** * Intrinsic: Process. @@ -84,7 +91,7 @@ export interface NodeProcessAPI { } function getProcessAPI(): NodeProcessAPI { - return internals as NodeProcessAPI || {}; + return intrinsic() as NodeProcessAPI || {}; } /** From e3bd69fd51d68e2b4a863e24e9bc139a6e1410f3 Mon Sep 17 00:00:00 2001 From: Sam Gammon Date: Thu, 25 Apr 2024 17:54:35 -0700 Subject: [PATCH 4/4] chore: support latest dev bin for testing Signed-off-by: Sam Gammon --- tools/defs/elide/bindist.bzl | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/tools/defs/elide/bindist.bzl b/tools/defs/elide/bindist.bzl index 7eee05f..a46cd54 100644 --- a/tools/defs/elide/bindist.bzl +++ b/tools/defs/elide/bindist.bzl @@ -1,6 +1,6 @@ """Defines binary distribution endpoints for the Elide CLI.""" -_latest_version = "1.0-v3-alpha3-b1" +_latest_version = "1.0-dev-19838" _download_domain = "dl.elide.dev" @@ -23,6 +23,14 @@ _elide_version_configs = { "linux-amd64": "f1fe32812fc1fa13c48e7ef1d96dfd0698e788767363cdd50edf332a2e1e688b", }, }, + "1.0-dev-19838": { + "urls": ["https://static.elide.dev/{version}/{platform}/elide-1.0.0-alpha8-{platform}.zip"], + "trim_prefix": "elide-1.0.0-alpha8-{platform}", + "sha256": { + "darwin-aarch64": "b4323e9f8d954ce5d70ca696948f98cf09a03cbace65b0c4b90ab17e9a8d12fa", + "linux-amd64": "79b248d03cc65f95c8140278ab4bb3b0b0fa6d9d74319ca0955c534be124b4f5", + }, + }, } def _get_platform(ctx): @@ -57,17 +65,26 @@ def _elide_bindist_repository_impl(ctx): config = _elide_version_configs[version] sha = config["sha256"][platform] urls = [url.format(**format_args) for url in config["urls"]] + trim_prefix = config.get("trim_prefix", None) + trim_prefix = trim_prefix.format(**format_args) if trim_prefix else None ctx.download_and_extract( url = urls, sha256 = sha, + stripPrefix = trim_prefix, ) ctx.file("WORKSPACE", "workspace(name = \"{name}\")".format(name = ctx.name)) ctx.file("BUILD", """ package(default_visibility = ["//visibility:public"]) + exports_files(["elide"]) -filegroup(name = "elide_cli", srcs = ["elide"]) + +filegroup(name = "libs", srcs = glob(["*"], exclude = ["elide"])) +filegroup(name = "resources", srcs = glob(["resources/*"])) + +filegroup(name = "cli", srcs = ["elide", ":libs", ":resources"]) +alias(name = "elide_cli", actual = ":elide") """) elide_bindist_repository = repository_rule(