Skip to content

Commit

Permalink
Add import helper to load ESM modules
Browse files Browse the repository at this point in the history
  • Loading branch information
kylesmile committed Feb 5, 2025
1 parent 4166a3c commit ea13263
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 13 deletions.
10 changes: 8 additions & 2 deletions lib/nodo/core.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def generate_core_code

def generate_class_code
<<~JS
(() => {
(async () => {
const __nodo_klass__ = { nodo: global.nodo };
#{dependencies.map(&:to_js).join}
#{constants.map(&:to_js).join}
Expand All @@ -109,7 +109,13 @@ def finalize_context(context_id)
def require(*mods)
deps = mods.last.is_a?(Hash) ? mods.pop : {}
mods = mods.map { |m| [m, m] }.to_h
self.dependencies = dependencies + mods.merge(deps).map { |name, package| Dependency.new(name, package) }
self.dependencies = dependencies + mods.merge(deps).map { |name, package| Dependency.new(name, package, type: :cjs) }
end

def import(*mods)
deps = mods.last.is_a?(Hash) ? mods.pop : {}
mods = mods.map { |m| [m, m] }.to_h
self.dependencies = dependencies + mods.merge(deps).map { |name, package| Dependency.new(name, package, type: :esm) }
end

def function(name, _code = nil, timeout: Nodo.timeout, code: nil, &block)
Expand Down
33 changes: 28 additions & 5 deletions lib/nodo/dependency.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
module Nodo
class Dependency
attr_reader :name, :package
def initialize(name, package)
@name, @package = name, package
attr_reader :name, :package, :type

def initialize(name, package, type:)
@name, @package, @type = name, package, type
end

def to_js
case type
when :cjs then to_cjs
when :esm then to_esm
else raise "Unknown dependency type: #{type}"
end
end

private

def to_cjs
<<~JS
const #{name} = __nodo_klass__.#{name} = (() => {
try {
Expand All @@ -18,5 +28,18 @@ def to_js
})();
JS
end

def to_esm
<<~JS
const #{name} = __nodo_klass__.#{name} = await (async () => {
try {
return await nodo.import(#{package.to_json});
} catch(e) {
e.nodo_dependency = #{package.to_json};
throw e;
}
})();
JS
end
end
end
8 changes: 6 additions & 2 deletions lib/nodo/nodo.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,12 @@ module.exports = (function() {

try {
if (DEFINE_METHOD == method) {
classes[class_name] = vm.runInThisContext(input, class_name);
respond_with_data(res, class_name, start);
Promise.resolve(vm.runInThisContext(input, class_name)).then((result) => {
classes[class_name] = result;
respond_with_data(res, class_name, start);
}).catch((error) => {
respond_with_error(res, 500, error);
})
} else if (EVALUATE_METHOD == method) {
Promise.resolve(vm.runInNewContext(input, context)).then((result) => {
respond_with_data(res, result, start);
Expand Down
43 changes: 39 additions & 4 deletions test/nodo_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,8 @@ def test_logging
assert_match /Nodo::JavaScriptError/, Nodo.logger.errors.first
end
end
def test_dependency_error

def test_require_dependency_error
with_logger nil do
nodo = Class.new(Nodo::Core) do
require 'foobarfoo'
Expand Down Expand Up @@ -260,9 +260,44 @@ def test_dynamic_imports_in_evaluation
uuid = nodo.new.evaluate("nodo.import('uuid').then((uuid) => uuid.v4()).catch((e) => null)")
assert_uuid uuid
end


def test_import
nodo = Class.new(Nodo::Core) do
import :fs
function :exists_file, "(file) => fs.existsSync(file)"
end
assert_equal true, nodo.instance.exists_file(__FILE__)
assert_equal false, nodo.instance.exists_file('FOOBARFOO')
end

def test_import_npm
nodo = Class.new(Nodo::Core) do
import :uuid
function :v4, "() => uuid.v4()"
end
assert uuid = nodo.new.v4
assert_equal 36, uuid.size
end

def test_import_dependency_error
with_logger nil do
nodo = Class.new(Nodo::Core) do
import 'foobarfoo'
end
assert_raises Nodo::DependencyError do
nodo.new
end
end
end

def test_evaluation_can_access_imports
nodo = Class.new(Nodo::Core) { import :uuid }
uuid = nodo.new.evaluate('uuid.v4()')
assert_uuid uuid
end

private

def test_logger
Object.new.instance_exec do
def errors; @errors ||= []; end
Expand Down

0 comments on commit ea13263

Please sign in to comment.