isorun 0.1.7-x86_64-linux → 0.1.8-x86_64-linux

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8a4aeda3a39740cabd5ed2a62ca53b3b9908d518990165ddbe1271c97cd09381
4
- data.tar.gz: 1eb2373c0cc3b4287becc08c2b304d7ad9874856ec8ec14a8212be2f764a5dc0
3
+ metadata.gz: 3b7fbac84c4db38f693ab09b2e841b9c8ef840cfeb50d212d1123d147e6bc8ff
4
+ data.tar.gz: bfa169c65285f23465e5016eb8b0e5a8700cad33e966ecaaa8e7bf101ed15bb2
5
5
  SHA512:
6
- metadata.gz: b1425a976b568f920a1c90ad3b47b5007d4ef95a31a319ef9e8cc33fa4f9b27c3d2a76ae98a3d5f051943df62bfa0a3f91557e1fb5e7e0897b09b74d75369f91
7
- data.tar.gz: 421de15c0adc18b0777021be608cac34895bd1ce5c46ba6a233dc45181afba134b98a3a2155002662f2d736b36f80fafb7eedf99419b49f22c5547a6259a25a6
6
+ metadata.gz: 426b9460bfda7af56298427b522808a0816506799895ea4f6adf57836ad259622e056b040d860e4c568a7a01d749496d65255aa45014fe02276b3ba27e32f7c6
7
+ data.tar.gz: 85d72159f954e240c8d68d1c415e5f07e751df504a23310f4305eba9f47c5d90d44b61ed42c92c536fe3b2b40d0ea85e9deba2e4b9811bf150dc67ed83453712
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-linux
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: []