isorun 0.1.7-x86_64-darwin → 0.1.8-x86_64-darwin

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f0a94fe4b9a33a91a13ec9c0f6a266f639a5f801529ae1dae8d85d3ea42b1df0
4
- data.tar.gz: af2ac08336a9e5bc8fe54fe73d3ea692b84a01e02d98924f2043106c221affcc
3
+ metadata.gz: a611e71b2fb8ba1f62c3eb3ba1d62914e4cad152a0c404d308ead15fbcf5ed8b
4
+ data.tar.gz: 6a0c9b323c9e9676ddd1215e2f8299f1cee3cbd70c31e73598bd497cd8ee6e2d
5
5
  SHA512:
6
- metadata.gz: d459628aab2cb4a0e2940fc71b8e570578bbc19c21292668de88342887793332ef646056fc0b2086d6a34a0c83d45503eb283eefffe02014f695082af16740ea
7
- data.tar.gz: 76427407e9083bd6c50e9a9f952e49158dc8e7dc64701e59ab3040fd807969015e0c79ec5bda153b31493e7fd5af840283795cf36b62d51f9d27a2fda83a7e1c
6
+ metadata.gz: c8556803ee6aa905c1e713fd4940143cf96c220dce608c5d46f975172f0b035bac4916bcfb99d5c68c5bd77724dc5928cecd94b78ac58d1c258d445421d42ab5
7
+ data.tar.gz: da661918f416b7892131f4fa0f1cc16dd710d57d3c661441bd898680e4726c45c68bcb4165e656494440663af4fc8895b3cb0b3c8704c2a765f1d05437a466dd
data/Rakefile CHANGED
@@ -6,7 +6,7 @@ require "rake/testtask"
6
6
  require "rake/extensiontask"
7
7
  require "rb_sys"
8
8
 
9
- cross_rubies = %w[3.1.0 3.0.0 2.7.0]
9
+ cross_rubies = %w[3.2.0 3.1.0 3.0.0 2.7.0]
10
10
  cross_platforms = %w[
11
11
  arm64-darwin
12
12
  x86_64-darwin
@@ -72,7 +72,7 @@ begin
72
72
 
73
73
  YARD::Rake::YardocTask.new
74
74
 
75
- task docs: :environment do
75
+ task :docs do
76
76
  `yard server --reload`
77
77
  end
78
78
  rescue LoadError
@@ -2,10 +2,27 @@
2
2
 
3
3
  module Isorun
4
4
  module AppHelper
5
+ # The isorun_app helper is the most convenient way to server-side render
6
+ # a JavaScript application, including state extraction and automatic
7
+ # rehydration. The helper tries to render the application and will also
8
+ # inject the client-side code immediately after the rendered result.
9
+ #
10
+ # @example Renders a JavaScript application
11
+ # <html>
12
+ # <body>
13
+ # <%= isorun_app("my_app") %>
14
+ # </body>
15
+ # </html>
16
+ #
17
+ # @param id [String] An ID representing both, the asset bundle, and by
18
+ # convention, the target node (e.g. `<div id="my_app">`)
19
+ # @return [String]
5
20
  def isorun_app(id) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
21
+ ActiveSupport::Notifications.instrument "start.render.isorun", { ts: Time.current }
22
+
6
23
  module_path = Isorun.config.module_resolver.call(id)
7
24
 
8
- ssr_html = Isorun::Context.create do |context|
25
+ ssr_html = Isorun::Context.create(receiver: Isorun.config.receiver) do |context|
9
26
  render_context = { environment: Rails.env.to_s }
10
27
  render_function = context.import.from(module_path)
11
28
 
@@ -17,13 +34,10 @@ module Isorun
17
34
  return ""
18
35
  end
19
36
 
20
- # set receiver to allow calling into Ruby from JavaScript
21
- context.receiver = Isorun.config.receiver
22
-
23
37
  html = render_function.call(render_context)
24
38
 
25
- # reset receiver
26
- context.receiver = nil
39
+ ActiveSupport::Notifications.instrument "finish.render.isorun", { ts: Time.current }
40
+ ActiveSupport::Notifications.instrument "stats.isorun", Isorun.stats
27
41
 
28
42
  html
29
43
  end
@@ -1,26 +1,26 @@
1
1
  [package]
2
2
  name = "isorun"
3
- version = "0.1.7"
3
+ version = "0.1.8"
4
4
  edition = "2021"
5
5
  authors = ["Hannes Moser <box@hannesmoser.at>"]
6
6
  homepage = "https://github.com/eliias/isorun"
7
7
  repository = "https://github.com/eliias/isorun"
8
8
 
9
9
  [dependencies]
10
- deno_console = "0.82.0"
11
- deno_core = "0.164.0"
12
- deno_fetch = "0.106.0"
13
- deno_runtime = "0.90.0"
14
- deno_url = "0.82.0"
15
- deno_web = "0.113.0"
16
- deno_webidl = "0.82.0"
10
+ deno_console = "0.83.0"
11
+ deno_core = "0.165.0"
12
+ deno_fetch = "0.107.0"
13
+ deno_runtime = "0.91.0"
14
+ deno_url = "0.83.0"
15
+ deno_web = "0.114.0"
16
+ deno_webidl = "0.83.0"
17
17
  lazy_static = "1.4.0"
18
- magnus = { git = "https://github.com/eliias/magnus", rev = "3289eeda7ea63831c0277b153666f1efab3866bb" }
18
+ magnus = { git = "https://github.com/eliias/magnus", rev = "5220756ca0ab4f2bc32d38310c26f7dda2be9bd6" }
19
19
  tokio = { version = "1.21.1", features = ["full"] }
20
20
  v8 = "0.60.0"
21
21
 
22
22
  [dev-dependencies]
23
- magnus = { git = "https://github.com/eliias/magnus", rev = "3289eeda7ea63831c0277b153666f1efab3866bb", features = ["embed"] }
23
+ magnus = { git = "https://github.com/eliias/magnus", rev = "5220756ca0ab4f2bc32d38310c26f7dda2be9bd6", features = ["embed"] }
24
24
 
25
25
  [lib]
26
26
  name = "isorun"
@@ -3,7 +3,7 @@ use crate::js::module::Module;
3
3
  use crate::js::worker::WORKER;
4
4
  use deno_core::JsRealm;
5
5
  use magnus::block::Proc;
6
- use magnus::Error;
6
+ use magnus::{exception, Error};
7
7
  use std::cell::RefCell;
8
8
  use std::rc::Rc;
9
9
 
@@ -19,7 +19,10 @@ impl Context {
19
19
  .with(|worker| worker.create_realm())
20
20
  .map(|realm| Context(Rc::new(RefCell::from(realm))))
21
21
  .map_err(|_error| {
22
- Error::runtime_error("cannot create JavaScript context")
22
+ Error::new(
23
+ exception::runtime_error(),
24
+ "cannot create JavaScript context",
25
+ )
23
26
  })
24
27
  }
25
28
 
@@ -41,11 +44,14 @@ impl Context {
41
44
  })
42
45
  .map(|module| isorun::Module(RefCell::from(module)))
43
46
  .map_err(|error| {
44
- Error::runtime_error(format!(
45
- "cannot load module: `{}`: {}",
46
- path.as_str(),
47
- error
48
- ))
47
+ Error::new(
48
+ exception::runtime_error(),
49
+ format!(
50
+ "cannot load module: `{}`: {}",
51
+ path.as_str(),
52
+ error
53
+ ),
54
+ )
49
55
  })
50
56
  }
51
57
  }
@@ -1,6 +1,6 @@
1
1
  use crate::js;
2
2
  use crate::js::worker::WORKER;
3
- use magnus::Error;
3
+ use magnus::{exception, Error};
4
4
  use std::cell::RefCell;
5
5
  use std::ops::Deref;
6
6
  use v8::{Global, Value};
@@ -28,10 +28,10 @@ impl Function {
28
28
 
29
29
  let result =
30
30
  self.0.borrow().call(v8_args.as_slice()).map_err(|error| {
31
- Error::runtime_error(format!(
32
- "cannot call function: {}",
33
- error
34
- ))
31
+ Error::new(
32
+ exception::runtime_error(),
33
+ format!("cannot call function: {}", error),
34
+ )
35
35
  });
36
36
 
37
37
  result
@@ -1,6 +1,6 @@
1
1
  use crate::isorun::function::Function;
2
2
  use crate::js;
3
- use magnus::Error;
3
+ use magnus::{exception, Error};
4
4
  use std::cell::RefCell;
5
5
 
6
6
  #[magnus::wrap(class = "Isorun::Module")]
@@ -19,9 +19,10 @@ impl Module {
19
19
  export_name: String,
20
20
  ) -> Result<magnus::Value, Error> {
21
21
  let module = self.0.borrow();
22
- let module_item = module
23
- .import(export_name.as_str())
24
- .map_err(|error| Error::runtime_error(format!("{}", error)))?;
22
+ let module_item =
23
+ module.import(export_name.as_str()).map_err(|error| {
24
+ Error::new(exception::runtime_error(), format!("{}", error))
25
+ })?;
25
26
 
26
27
  match module_item {
27
28
  js::module_item::ModuleItem::Value(v) => Ok(v.to_ruby().unwrap()),
@@ -1,3 +1,4 @@
1
+ use crate::js::worker::WORKER;
1
2
  use deno_core::error::AnyError;
2
3
  use magnus::r_hash::ForEach;
3
4
  use magnus::value::{Qfalse, Qtrue};
@@ -5,6 +6,7 @@ use magnus::{
5
6
  Integer, RArray, RFloat, RHash, RString, RStruct, Symbol, Value, QFALSE,
6
7
  QNIL, QTRUE,
7
8
  };
9
+ use std::collections::HashMap;
8
10
  use v8::{Array, GetPropertyNamesArgs, Global, HandleScope, Local, Object};
9
11
 
10
12
  pub fn convert_v8_to_ruby(
@@ -157,3 +159,67 @@ pub fn convert_ruby_to_v8<'s>(
157
159
 
158
160
  Ok(v8::null(scope).into())
159
161
  }
162
+
163
+ pub(crate) fn low_memory_notification() {
164
+ WORKER.with(|worker| {
165
+ let mut worker = worker.worker.borrow_mut();
166
+ worker.js_runtime.v8_isolate().low_memory_notification();
167
+ });
168
+ }
169
+
170
+ pub(crate) fn stats() -> RHash {
171
+ WORKER.with(|worker| {
172
+ let mut worker = worker.worker.borrow_mut();
173
+
174
+ let heap_stats = &mut Default::default();
175
+ worker
176
+ .js_runtime
177
+ .v8_isolate()
178
+ .get_heap_statistics(heap_stats);
179
+
180
+ let current_thread = std::thread::current();
181
+ let thread = HashMap::from([(
182
+ "thread_id",
183
+ format!("{:?}", current_thread.id()),
184
+ )]);
185
+
186
+ let heap = HashMap::from([
187
+ ("external_memory", heap_stats.external_memory()),
188
+ ("heap_size_limit", heap_stats.heap_size_limit()),
189
+ ("malloced_memory", heap_stats.malloced_memory()),
190
+ (
191
+ "number_of_detached_contexts",
192
+ heap_stats.number_of_detached_contexts(),
193
+ ),
194
+ (
195
+ "number_of_native_contexts",
196
+ heap_stats.number_of_native_contexts(),
197
+ ),
198
+ ("peak_malloced_memory", heap_stats.peak_malloced_memory()),
199
+ ("total_available_size", heap_stats.total_available_size()),
200
+ (
201
+ "total_global_handles_size",
202
+ heap_stats.total_global_handles_size(),
203
+ ),
204
+ ("total_heap_size", heap_stats.total_heap_size()),
205
+ (
206
+ "total_heap_size_executable",
207
+ heap_stats.total_heap_size_executable(),
208
+ ),
209
+ ("total_physical_size", heap_stats.total_physical_size()),
210
+ (
211
+ "used_global_handles_size",
212
+ heap_stats.used_global_handles_size(),
213
+ ),
214
+ ("used_heap_size", heap_stats.used_heap_size()),
215
+ ]);
216
+
217
+ let h = RHash::new();
218
+ h.aset("current_thread", RHash::from_iter(thread))
219
+ .expect("cannot set stats for current_thread");
220
+ h.aset("heap", RHash::from_iter(heap))
221
+ .expect("cannot set stats for heap");
222
+
223
+ h
224
+ })
225
+ }
@@ -229,6 +229,7 @@ impl Default for Worker {
229
229
  inspect: false,
230
230
  },
231
231
  extensions: std::mem::take(&mut extensions),
232
+ extensions_with_js: Default::default(),
232
233
  startup_snapshot: None,
233
234
  unsafely_ignore_certificate_errors: None,
234
235
  root_cert_store: None,
@@ -1,4 +1,6 @@
1
+ use self::isorun::utils::stats;
1
2
  use crate::isorun::context::Context;
3
+ use crate::isorun::utils::low_memory_notification;
2
4
  use isorun::function::Function;
3
5
  use isorun::module::Module;
4
6
  use magnus::{define_module, function, method, Error, Module as M, Object};
@@ -10,6 +12,15 @@ mod js;
10
12
  fn init() -> Result<(), Error> {
11
13
  let root = define_module("Isorun").expect("cannot define module: Isorun");
12
14
 
15
+ root.define_module_function("stats", function!(stats, 0))
16
+ .expect("cannot define module function: stats");
17
+
18
+ root.define_module_function(
19
+ "low_memory_notification",
20
+ function!(low_memory_notification, 0),
21
+ )
22
+ .expect("cannot define module function: low_memory_notification");
23
+
13
24
  let context = root
14
25
  .define_class("Context", Default::default())
15
26
  .expect("cannot define class: Isorun::Context");
Binary file
Binary file
Binary file
@@ -10,7 +10,10 @@ module Isorun
10
10
  @export_names = export_names
11
11
  end
12
12
 
13
+ # Specify the module to import from
13
14
  def from(module_path)
15
+ module_path = module_path.to_s if module_path.is_a? Pathname
16
+
14
17
  mod = load(module_path)
15
18
  imports = export_names.map { |export_name| mod.import(export_name) }
16
19
  return imports.first if imports.size == 1
@@ -23,7 +26,7 @@ module Isorun
23
26
  CACHE_KEY = "isorun_module_path_mtime"
24
27
  private_constant :CACHE_KEY
25
28
 
26
- def load(module_path)
29
+ def load(module_path) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
27
30
  return context.load(module_path) if Rails.env.production?
28
31
 
29
32
  key = module_path.parameterize
@@ -41,10 +44,14 @@ module Isorun
41
44
  # map to URI scheme to allow adding a timestamp to bust module cache
42
45
  module_path = "file://#{module_path}?t=#{mtime}"
43
46
 
44
- return context.load(module_path) if cache_miss
47
+ if cache_miss
48
+ ActiveSupport::Notifications.instrument "load.module.isorun", { path: module_path }
49
+ return context.load(module_path)
50
+ end
45
51
 
46
52
  Rails.cache.write("#{CACHE_KEY}:#{key}", mtime) if prev_mtime < mtime
47
53
 
54
+ ActiveSupport::Notifications.instrument "load.module.isorun", { path: module_path }
48
55
  context.load(module_path)
49
56
  end
50
57
  end
@@ -52,11 +59,30 @@ module Isorun
52
59
  private_constant :Import
53
60
 
54
61
  class << self
55
- def create(&block)
62
+ # Creates a new context and yields the context as the first argument to
63
+ # the block.
64
+ #
65
+ # @example Creates a new context, imports the default as function
66
+ # result = Isorun::Context.create do |context|
67
+ # func = context.import.from(module_path)
68
+ # func.call("Hello, World!")
69
+ # end
70
+ #
71
+ # @yield [context] The newly created JavaScript context
72
+ # @yieldparam [Isorun::Context] context
73
+ # @yieldreturn [Object, nil] An optional return value from the execution context
74
+ def create(options = default_options, &block)
56
75
  raise "[Isorun::Context] block missing when creating context" unless block
57
76
 
58
77
  context = Isorun::Context.new
59
- yield(context)
78
+
79
+ context.receiver = options[:receiver] if options[:receiver].present?
80
+
81
+ result = yield(context)
82
+
83
+ context.receiver = nil if options[:receiver].present?
84
+
85
+ result
60
86
  end
61
87
 
62
88
  # @!method new()
@@ -66,11 +92,28 @@ module Isorun
66
92
 
67
93
  def default_options
68
94
  {
69
- receiver: Isorun.configuration.receiver
95
+ receiver: nil
70
96
  }
71
97
  end
72
98
  end
73
99
 
100
+ # Specify items you want to import from the module. If none is specified,
101
+ # the default export is taken.
102
+ #
103
+ # @example Import default export
104
+ # result = Isorun::Context.create do |context|
105
+ # item = context.import.from(module_path)
106
+ # end
107
+ #
108
+ # @example Import default export explicitly
109
+ # result = Isorun::Context.create do |context|
110
+ # item = context.import(:default).from(module_path)
111
+ # end
112
+ #
113
+ # @example Import various exports
114
+ # result = Isorun::Context.create do |context|
115
+ # hello, world = context.import(:hello, :world).from(module_path)
116
+ # end
74
117
  def import(*export_names)
75
118
  export_names = [*export_names].map(&:to_s)
76
119
  export_names = [:default.to_s] if export_names.empty?
@@ -78,6 +121,7 @@ module Isorun
78
121
  end
79
122
 
80
123
  # @!method receiver=(receiver)
124
+ #
81
125
  # @param receiver [Proc, nil]
82
126
  # @return [Isorun::Context] the newly created context
83
127
  end
data/lib/isorun/engine.rb CHANGED
@@ -12,7 +12,6 @@ module Isorun
12
12
 
13
13
  initializer "isorun.helpers", before: :load_config_initializers do
14
14
  ActiveSupport.on_load(:action_controller_base) do
15
- Rails.logger.debug Isorun::Engine.helpers
16
15
  helper Isorun::Engine.helpers
17
16
  end
18
17
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Isorun
4
- VERSION = "0.1.7"
4
+ VERSION = "0.1.8"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: isorun
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 0.1.8
5
5
  platform: x86_64-darwin
6
6
  authors:
7
7
  - Hannes Moser
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-12-31 00:00:00.000000000 Z
11
+ date: 2023-01-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: 0.9.46
55
+ - !ruby/object:Gem::Dependency
56
+ name: rails
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: rake-compiler
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +94,20 @@ dependencies:
80
94
  - - ">="
81
95
  - !ruby/object:Gem::Version
82
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
83
111
  - !ruby/object:Gem::Dependency
84
112
  name: rspec-rails
85
113
  requirement: !ruby/object:Gem::Requirement
@@ -94,10 +122,27 @@ dependencies:
94
122
  - - ">="
95
123
  - !ruby/object:Gem::Version
96
124
  version: '0'
97
- description: "Import ECMAScript modules into Ruby and use values and functions like
98
- \nJavaScript is part of Ruby. Enables easy to set up server-side rendering for\nmodern
99
- frontend stacks.\n\nIsorun embeds v8 into Ruby via a native extension built with
100
- Rust and\ndeno_core.\n"
125
+ - !ruby/object:Gem::Dependency
126
+ name: simplecov
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: 0.22.0
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: 0.22.0
139
+ description: |
140
+ Import ECMAScript modules into Ruby and use values and functions like
141
+ JavaScript is part of Ruby. Enables easy to set up server-side rendering for
142
+ modern frontend stacks.
143
+
144
+ Isorun embeds v8 into Ruby via a native extension built with Rust and
145
+ deno_core.
101
146
  email:
102
147
  - box@hannesmoser.at
103
148
  executables: []