proscenium 0.21.6-x86_64-darwin → 0.22.0.beta1-x86_64-darwin

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: 73bfd46f757a568bc396030d83a700b92b688bd7b1ce202f75801bdb6b1bdaa8
4
- data.tar.gz: eb081a7f9a7fb639f4f6504c3ac7e9e58a8b10d39f14b0c3dedb1b922e6356e4
3
+ metadata.gz: b0de71a6fd50be101ac7603f9cdea66447ee561991d7a4c4acc624fc1b4faf08
4
+ data.tar.gz: accea95f24de39987d31abfeccd2622b6458a5ed36dd605a52635fd2c115498b
5
5
  SHA512:
6
- metadata.gz: 1668d90ca4d1732d42014ba4244ed783d8db930eaee83c627da66b0abaa507bdfb16cdb23b3e3af4fc859441ca17a071ef96e3ddc31bbc38f172b0f13c57a455
7
- data.tar.gz: 8c6ccf8a3bbbfaf4ec480eef08bab1210684e787d69b1519d5d75b5cf31ba61cc549305f31bf0513d455ddee95dc83581eab1f1394b1d6c2815695e5c4a4ced0
6
+ metadata.gz: c52ef4ddcb6108dd24fcdbf4f7fa8ec7f2a1cb39677ece59f94b33bfe804a396d331531f5f4216592010aa1ca0697cb46d124bda91461d2add4b9ca6be1fe2bc
7
+ data.tar.gz: f3e0b30e4427732cd2f9cb0958a06f653700d464e7535b11a05e8d3d5bdac86067962421918692fb57e118823f02964b12262ef5e800ec3f051a3aff42bce833
@@ -4,8 +4,6 @@ require 'ffi'
4
4
 
5
5
  module Proscenium
6
6
  class Builder
7
- class CompileError < StandardError; end
8
-
9
7
  ENVIRONMENTS = { development: 1, test: 2, production: 3 }.freeze
10
8
 
11
9
  class Result < FFI::Struct
@@ -14,6 +12,11 @@ module Proscenium
14
12
  :content_hash, :string
15
13
  end
16
14
 
15
+ class CompileResult < FFI::Struct
16
+ layout :success, :bool,
17
+ :messages, :string
18
+ end
19
+
17
20
  module Request
18
21
  extend FFI::Library
19
22
 
@@ -32,30 +35,36 @@ module Proscenium
32
35
  :pointer # Config as JSON.
33
36
  ], Result.by_value
34
37
 
38
+ attach_function :compile, [
39
+ :pointer # Config as JSON.
40
+ ], CompileResult.by_value
41
+
35
42
  attach_function :reset_config, [], :void
36
43
  end
37
44
 
38
- class BuildError < StandardError
39
- attr_reader :error
45
+ class BuildError < Error
46
+ attr_reader :error, :path
40
47
 
41
- def initialize(error)
42
- @error = JSON.parse(error, strict: true).deep_transform_keys(&:underscore)
48
+ def initialize(path, error)
49
+ @path = path
50
+ @error = JSON.parse(error, strict: true)
43
51
 
44
- msg = @error['text']
45
- msg << ' - ' << @error['detail'] if @error['detail'].is_a?(String)
46
- if (location = @error['location'])
47
- msg << " at #{location['file']}:#{location['line']}:#{location['column']}"
52
+ msg = @error['Text']
53
+ msg << ' - ' << @error['Detail'] if @error['Detail'].is_a?(String)
54
+ if (location = @error['Location'])
55
+ msg << " at #{location['File']}:#{location['Line']}:#{location['Column']}"
48
56
  end
49
57
 
50
- super(msg)
58
+ super("Failed to build #{path} - #{msg}")
51
59
  end
52
60
  end
53
61
 
54
- class ResolveError < StandardError
55
- attr_reader :error_msg, :path
62
+ class ResolveError < Error
63
+ attr_reader :path
56
64
 
57
- def initialize(path, error_msg)
58
- super("Failed to resolve '#{path}' -- #{error_msg}")
65
+ def initialize(path, msg)
66
+ @path = path
67
+ super("Failed to resolve #{path} - #{msg}")
59
68
  end
60
69
  end
61
70
 
@@ -67,6 +76,10 @@ module Proscenium
67
76
  new(root:).resolve(path)
68
77
  end
69
78
 
79
+ def self.compile(root: nil)
80
+ new(root:).compile
81
+ end
82
+
70
83
  # Intended for tests only.
71
84
  def self.reset_config!
72
85
  Request.reset_config
@@ -75,6 +88,7 @@ module Proscenium
75
88
  def initialize(root: nil)
76
89
  @request_config = FFI::MemoryPointer.from_string({
77
90
  RootPath: (root || Rails.root).to_s,
91
+ OutputDir: "public#{Proscenium.config.output_dir}",
78
92
  GemPath: gem_root,
79
93
  Environment: ENVIRONMENTS.fetch(Rails.env.to_sym, 2),
80
94
  EnvVars: env_vars,
@@ -82,6 +96,7 @@ module Proscenium
82
96
  RubyGems: Proscenium::BundledGems.paths,
83
97
  Bundle: Proscenium.config.bundle,
84
98
  Aliases: Proscenium.config.aliases,
99
+ Precompile: Proscenium.config.precompile,
85
100
  QueryString: Proscenium.config.cache_query_string.presence || '',
86
101
  Debug: Proscenium.config.debug
87
102
  }.to_json)
@@ -91,7 +106,7 @@ module Proscenium
91
106
  ActiveSupport::Notifications.instrument('build.proscenium', identifier: path) do
92
107
  result = Request.build_to_string(path, cache_query_string, @request_config)
93
108
 
94
- raise BuildError, result[:response] unless result[:success]
109
+ raise BuildError.new(path, result[:response]) unless result[:success]
95
110
 
96
111
  result
97
112
  end
@@ -107,6 +122,11 @@ module Proscenium
107
122
  end
108
123
  end
109
124
 
125
+ def compile
126
+ result = Request.compile(@request_config)
127
+ result[:success]
128
+ end
129
+
110
130
  private
111
131
 
112
132
  # Build the ENV variables as determined by `Proscenium.config.env_vars` and
@@ -7,7 +7,7 @@ module Proscenium::CssModule
7
7
  autoload :Transformer
8
8
  autoload :Rewriter
9
9
 
10
- class TransformError < StandardError
10
+ class TransformError < Proscenium::Error
11
11
  def initialize(name, additional_msg = nil)
12
12
  msg = "Failed to transform CSS module `#{name}`"
13
13
  msg << ' - ' << additional_msg if additional_msg
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Proscenium
4
- NotIncludedError = Class.new(StandardError)
4
+ NotIncludedError = Class.new(Error)
5
5
 
6
6
  module EnsureLoaded
7
7
  def self.included(child)
Binary file
@@ -25,6 +25,10 @@ struct Result {
25
25
  int success;
26
26
  char* response;
27
27
  char* contentHash;
28
+ };
29
+ struct CompileResult {
30
+ int success;
31
+ char* messages;
28
32
  };
29
33
 
30
34
  #line 1 "cgo-generated-wrapper"
@@ -88,6 +92,7 @@ extern void reset_config();
88
92
  // Build the given `path` using the `config`.
89
93
  //
90
94
  // - path - The path to build relative to `root`.
95
+ // - cache_query_string - The current query string used for cache busting, taken from the URL.
91
96
  // - config
92
97
  //
93
98
  extern struct Result build_to_string(char* filePath, char* cacheQueryString, char* configJson);
@@ -99,6 +104,12 @@ extern struct Result build_to_string(char* filePath, char* cacheQueryString, cha
99
104
  //
100
105
  extern struct Result resolve(char* filePath, char* configJson);
101
106
 
107
+ // Compile assets using the given `config`.
108
+ //
109
+ // - config
110
+ //
111
+ extern struct CompileResult compile(char* configJson);
112
+
102
113
  #ifdef __cplusplus
103
114
  }
104
115
  #endif
@@ -11,18 +11,9 @@ module Proscenium
11
11
  end
12
12
  end
13
13
 
14
- # Overriden to allow regular use of javascript_include_tag and stylesheet_link_tag, while still
15
- # building with Proscenium. It's important to note that `include_assets` will not call this, as
16
- # those asset paths all begin with a slash, which the Rails asset helpers do not pass through to
17
- # here.
18
- #
19
- # If the given `path` is a bare path (does not start with `./` or `../`), then we use
20
- # Rails default conventions, and serve CSS from /app/assets/stylesheets and JS from
21
- # /app/javascript.
22
14
  def compute_asset_path(path, options = {})
23
15
  if %i[javascript stylesheet].include?(options[:type])
24
- path.prepend DEFAULT_RAILS_ASSET_PATHS[options[:type]] if !path.start_with?('./', '../')
25
- return path
16
+ return Proscenium::Manifest[path] || "/#{path}"
26
17
  end
27
18
 
28
19
  super
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Proscenium
4
+ module Manifest
5
+ mattr_accessor :manifest, default: {}
6
+ mattr_accessor :loaded, default: false
7
+
8
+ module_function
9
+
10
+ def loaded?
11
+ loaded
12
+ end
13
+
14
+ def load!
15
+ self.manifest = {}
16
+ self.loaded = false
17
+
18
+ if Proscenium.config.manifest_path.exist?
19
+ self.loaded = true
20
+
21
+ JSON.parse(Proscenium.config.manifest_path.read)['outputs'].each do |output_path, details|
22
+ next if !details.key?('entryPoint')
23
+
24
+ manifest[details['entryPoint']] = "/#{output_path.delete_prefix('public/')}"
25
+ end
26
+ end
27
+
28
+ manifest
29
+ end
30
+
31
+ def reset!
32
+ self.manifest = {}
33
+ self.loaded = false
34
+ end
35
+
36
+ def [](key)
37
+ loaded? ? manifest[key] : "/#{key}"
38
+ end
39
+ end
40
+ end
@@ -5,18 +5,6 @@ module Proscenium
5
5
  class Base
6
6
  include ActiveSupport::Benchmarkable
7
7
 
8
- # Error when the result of the build returns an error. For example, when esbuild returns
9
- # errors.
10
- class CompileError < StandardError
11
- attr_reader :detail, :file
12
-
13
- def initialize(args)
14
- @detail = args[:detail]
15
- @file = args[:file]
16
- super("Failed to build '#{args[:file]}' -- #{detail}")
17
- end
18
- end
19
-
20
8
  def self.attempt(request)
21
9
  new(request).renderable!&.attempt
22
10
  end
@@ -3,26 +3,8 @@
3
3
  module Proscenium
4
4
  class Middleware
5
5
  class Esbuild < Base
6
- class CompileError < Base::CompileError
7
- def initialize(args)
8
- detail = args[:detail]
9
- detail = JSON.parse(detail, mode: :strict)
10
-
11
- args['detail'] = if detail['location']
12
- "#{detail['text']} in #{detail['location']['file']}:" +
13
- detail['location']['line'].to_s
14
- else
15
- detail['text']
16
- end
17
-
18
- super
19
- end
20
- end
21
-
22
6
  def attempt
23
7
  render_response Builder.build_to_string(path_to_build, cache_query_string:)
24
- rescue Builder::CompileError => e
25
- raise self.class::CompileError, { file: @request.fullpath, detail: e.message }, caller
26
8
  end
27
9
  end
28
10
  end
@@ -4,7 +4,7 @@ module Proscenium
4
4
  class Middleware
5
5
  extend ActiveSupport::Autoload
6
6
 
7
- class BuildError < StandardError; end
7
+ class BuildError < Error; end
8
8
 
9
9
  autoload :Base
10
10
  autoload :Esbuild
@@ -24,7 +24,7 @@ module Proscenium
24
24
  # cache lifetime, since these are content-hashed and will never change.
25
25
  if request.path.match?(CHUNKS_PATH)
26
26
  ::ActionDispatch::FileHandler.new(
27
- Rails.public_path.join('assets').to_s,
27
+ Proscenium.config.output_path.to_s,
28
28
  headers: {
29
29
  'Cache-Control' => "public, max-age=#{100.years}, immutable",
30
30
  'etag' => request.path.match(/-\$([a-z0-9]+)\$/i)[1]
@@ -17,8 +17,9 @@ module Proscenium
17
17
  config.proscenium.ensure_loaded = :raise
18
18
  config.proscenium.cache_query_string = Rails.env.production? && ENV.fetch('REVISION', nil)
19
19
  config.proscenium.cache_max_age = 2_592_000 # 30 days
20
-
21
20
  config.proscenium.aliases = {}
21
+ config.proscenium.precompile = Set.new
22
+ config.proscenium.output_dir = '/assets'
22
23
 
23
24
  # List of environment variable names that should be passed to the builder, which will then be
24
25
  # passed to esbuild's `Define` option. Being explicit about which environment variables are
@@ -29,7 +30,13 @@ module Proscenium
29
30
  'Proscenium::Builder::BuildError' => 'build_error'
30
31
  }
31
32
 
32
- config.after_initialize do |_app|
33
+ config.after_initialize do |app|
34
+ config.proscenium.output_path ||=
35
+ Pathname.new(File.join(app.config.paths['public'].first, app.config.proscenium.output_dir))
36
+ config.proscenium.manifest_path = config.proscenium.output_path.join('.manifest.json')
37
+
38
+ Proscenium::Manifest.load!
39
+
33
40
  if config.proscenium.logging
34
41
  require 'proscenium/log_subscriber'
35
42
  Proscenium::LogSubscriber.attach_to :proscenium
@@ -65,5 +72,9 @@ module Proscenium
65
72
  ActionView::PartialRenderer.prepend Monkey::PartialRenderer
66
73
  end
67
74
  end
75
+
76
+ rake_tasks do
77
+ load 'proscenium/railties/assets.rake'
78
+ end
68
79
  end
69
80
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :assets do
4
+ desc 'Compile Proscenium assets'
5
+ task precompile: :environment do
6
+ puts "\nPre-compiling assets..."
7
+
8
+ raise 'Assets pre-compilation failed!' unless Proscenium::Builder.compile
9
+
10
+ puts "\nAssets pre-compiled successfully."
11
+
12
+ if Rails.env.development?
13
+ puts "\nWarning: You are precompiling assets in development. Rails will not " \
14
+ 'serve any changed assets until you delete ' \
15
+ "public#{Rails.application.config.proscenium.output_dir}/.manifest.json"
16
+ end
17
+ end
18
+ end
@@ -43,7 +43,7 @@ module Proscenium
43
43
  if Proscenium.config.cache_query_string.present?
44
44
  path += "?#{Proscenium.config.cache_query_string}"
45
45
  end
46
- out << helpers.stylesheet_link_tag(path, extname: false, **opts)
46
+ out << helpers.stylesheet_link_tag(path.delete_prefix('/'), extname: false, **opts)
47
47
  end
48
48
 
49
49
  if fragments
@@ -74,7 +74,7 @@ module Proscenium
74
74
  if Proscenium.config.cache_query_string.present?
75
75
  path += "?#{Proscenium.config.cache_query_string}"
76
76
  end
77
- out << helpers.javascript_include_tag(path, extname: false, **opts)
77
+ out << helpers.javascript_include_tag(path.delete_prefix('/'), extname: false, **opts)
78
78
  end
79
79
 
80
80
  if fragments
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Proscenium
4
- VERSION = '0.21.6'
4
+ VERSION = '0.22.0.beta1'
5
5
  end
data/lib/proscenium.rb CHANGED
@@ -26,6 +26,7 @@ module Proscenium
26
26
  autoload :Utils
27
27
  autoload :Monkey
28
28
  autoload :Middleware
29
+ autoload :Manifest
29
30
  autoload :EnsureLoaded
30
31
  autoload :SideLoad
31
32
  autoload :CssModule
@@ -44,7 +45,20 @@ module Proscenium
44
45
  end
45
46
  end
46
47
 
47
- class PathResolutionFailed < StandardError
48
+ class Error < StandardError; end
49
+
50
+ class MissingAssetError < Error
51
+ def initialize(path)
52
+ super
53
+ @path = path
54
+ end
55
+
56
+ def message
57
+ "The asset '#{@path}' was not found."
58
+ end
59
+ end
60
+
61
+ class PathResolutionFailed < Error
48
62
  def initialize(path)
49
63
  @path = path
50
64
  super
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: proscenium
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.21.6
4
+ version: 0.22.0.beta1
5
5
  platform: x86_64-darwin
6
6
  authors:
7
7
  - Joel Moss
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-10-24 00:00:00.000000000 Z
11
+ date: 2025-10-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi
@@ -66,6 +66,7 @@ files:
66
66
  - lib/proscenium/helper.rb
67
67
  - lib/proscenium/importer.rb
68
68
  - lib/proscenium/log_subscriber.rb
69
+ - lib/proscenium/manifest.rb
69
70
  - lib/proscenium/middleware.rb
70
71
  - lib/proscenium/middleware/base.rb
71
72
  - lib/proscenium/middleware/esbuild.rb
@@ -73,6 +74,7 @@ files:
73
74
  - lib/proscenium/middleware/silence_request.rb
74
75
  - lib/proscenium/monkey.rb
75
76
  - lib/proscenium/railtie.rb
77
+ - lib/proscenium/railties/assets.rake
76
78
  - lib/proscenium/react-manager/index.jsx
77
79
  - lib/proscenium/react-manager/react.js
78
80
  - lib/proscenium/react_componentable.rb