proscenium 0.11.0.pre.7-x86_64-linux → 0.11.0.pre.8-x86_64-linux

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: 88c960e2cb342e9d27c84cfc7f14545e9c900876d64c7ae27db96e9c470d387f
4
- data.tar.gz: 18c2090d81ccfb1d012d4c8c2a5a72ef66d76555eb3def406dddd17726264238
3
+ metadata.gz: 90ea3174caa7312b84d189b74ec6a5171cf0ee934eec92f3b0bca057aa4aee81
4
+ data.tar.gz: 9c865f250eb246a0e418a0c88cdd53db37b86f907fe61c90bfc8c6bf49b6a8aa
5
5
  SHA512:
6
- metadata.gz: 63ab8a8d80c739cbafaf8f2f5f3b51eb321a856fc5aed3e4aaf5d92b7574b8d0310ce8b476cf7faa09bc104a3365895c67789b70cb86ac8ea61a4cc542e8a644
7
- data.tar.gz: e301f752d5a149daf30c11a4c0de268c37a1a5af69761cb92721c8d1850dd25aa43fa895c3fcda566bea2794c6ce9db0a7b353f63567272ab628498ace7df3ee
6
+ metadata.gz: 9c789cf77502528b0132e29199ae2896a7aa2da6ed438ce7a0f42ffa72b27875da48209edad61dd240d0d24d3797ce0d1d4166c506b569a462a59e733093e527
7
+ data.tar.gz: 8c038b9c6afca87a4b16ea3efd89f6309a270d907b249fa3c33523ba15bee7e8ad1c5acdf29214f49bd3c4fa75c9e743f5d3cbe4adb48478fdbac018e3a8cf85
data/README.md CHANGED
@@ -757,18 +757,35 @@ Rails.application.config.proscenium.cache_max_age = 12.months.to_i
757
757
 
758
758
  Proscenium brings back RJS! Any path ending in .rjs will be served from your Rails app. This allows you to import server rendered javascript.
759
759
 
760
+ ## Resolution
760
761
 
761
- ## Included Paths
762
-
763
- Proscenium will serve files ending with any of these extension: `js,mjs,ts,css,jsx,tsx` from the following directories, and their sub-directories: `/app`, `/lib`, `/config`, `/node_modules`, `/vendor`.
762
+ Proscenium will serve files ending with any of these extension: `js,mjs,ts,css,jsx,tsx` from the following directories, and their sub-directories of your Rails application's root: `/app`, `/lib`, `/config`, `/node_modules`, `/vendor`.
764
763
 
765
764
  So a file at `/app/views/users/index.js` will be served from `https://yourapp.com/app/views/users/index.js`.
766
765
 
767
- You can continue to access any file in the `/public` directory as you normally would, but any file ending with a supported extension (`js,mjs,ts,css,jsx,tsx`) will be processed by Proscenium. For example, `/public/some/file.js` will be served from `https://yourapp.com/some/file.js`.
766
+ You can continue to access any file in the `/public` directory as you normally would. Proscenium will not process files in the `/public` directory.
767
+
768
+ If requesting a file that exists in a root directory and the public directory, the file in the public directory will be served. For example, if you have a file at `/lib/foo.js` and `/public/lib/foo.js`, and you request `/lib/foo.js`, the file in the public directory (`/public/lib/foo.js`) will be served.
769
+
770
+ ### Assets from Rails Engines
771
+
772
+ Proscenium can serve assets from Rails Engines that are installed in your Rails app.
773
+
774
+ An engine that wants to expose its assets via Proscenium to the application must add Proscenium as a dependency, and add itself to the list of engines in the Proscenium config options `Proscenium.config.engines`.
775
+
776
+ For example, we have a gem called `gem1` that has Proscenium as a dependency, and exposes a Rails engine. It has some assets that it wants to expose to the application. To do this, it adds itself to the list of engines in the Proscenium config `engines` option:
777
+
778
+ ```ruby
779
+ class Gem1::Engine < ::Rails::Engine
780
+ config.proscenium.engines << self
781
+ end
782
+ ```
783
+
784
+ When this gem is installed in any Rails application, its assets will be available at the URL `/gem1/...`. For example, if the gem has a file `lib/styles.css`, it can be requested at `/gem1/lib/styles.css`.
768
785
 
769
- ## Serving from Ruby Gems and Rails Engines
786
+ The same directories and file extensions are supported as for the application itself.
770
787
 
771
- Proscenium can serve assets from Ruby Gems and Rails Engines.
788
+ It is important to note that the application takes precedence over the gem. So if the application has a file at `/public/gem1/lib/styles.css`, and the gem also has a file at `/lib/styles.css`, then the file in the application will be served. This is because both files would be accessible at the same URL: `/gem1/lib/styles.css`.
772
789
 
773
790
  ## Thanks
774
791
 
@@ -29,6 +29,7 @@ module Proscenium
29
29
  :string, # Proscenium gem root
30
30
  :environment, # Rails environment as a Symbol
31
31
  :bool, # code splitting enabled?
32
+ :string, # engine names and paths as a JSON string
32
33
  :bool # debugging enabled?
33
34
  ], Result.by_value
34
35
 
@@ -45,12 +46,17 @@ module Proscenium
45
46
  end
46
47
 
47
48
  class BuildError < StandardError
48
- attr_reader :error, :path
49
+ attr_reader :error
49
50
 
50
- def initialize(path, error)
51
- error = Oj.load(error, mode: :strict).deep_transform_keys(&:underscore)
51
+ def initialize(error)
52
+ @error = Oj.load(error, mode: :strict).deep_transform_keys(&:underscore)
52
53
 
53
- super "Failed to build '#{path}' -- #{error['text']}"
54
+ msg = @error['text']
55
+ if (location = @error['location'])
56
+ msg << " at #{location['file']}:#{location['line']}:#{location['column']}"
57
+ end
58
+
59
+ super msg
54
60
  end
55
61
  end
56
62
 
@@ -82,9 +88,10 @@ module Proscenium
82
88
  Pathname.new(__dir__).join('..', '..').to_s,
83
89
  Rails.env.to_sym,
84
90
  Proscenium.config.code_splitting,
91
+ engines.to_json,
85
92
  Proscenium.config.debug)
86
93
 
87
- raise BuildError.new(path, result[:response]) unless result[:success]
94
+ raise BuildError, result[:response] unless result[:success]
88
95
 
89
96
  result[:response]
90
97
  end
@@ -116,6 +123,10 @@ module Proscenium
116
123
  q ? "--cache-query-string #{q}" : nil
117
124
  end
118
125
 
126
+ def engines
127
+ Proscenium.config.engines.to_h { |e| [e.engine_name, e.root.to_s] }
128
+ end
129
+
119
130
  def import_map
120
131
  return unless (path = Rails.root&.join('config'))
121
132
 
Binary file
@@ -97,7 +97,7 @@ extern "C" {
97
97
  // - codeSpitting?
98
98
  // - debug?
99
99
  //
100
- extern struct Result build(char* filepath, char* baseUrl, char* importMap, char* envVars, char* appRoot, char* gemPath, unsigned int env, GoUint8 codeSplitting, GoUint8 debug);
100
+ extern struct Result build(char* filepath, char* baseUrl, char* importMap, char* envVars, char* appRoot, char* gemPath, unsigned int env, GoUint8 codeSplitting, char* engines, GoUint8 debug);
101
101
 
102
102
  // Resolve the given `path` relative to the `root`.
103
103
  //
@@ -21,7 +21,7 @@ module Proscenium
21
21
  class << self
22
22
  # Import the given `filepath`. This is idempotent - it will never include duplicates.
23
23
  #
24
- # @param filepath [String] Absolute path (relative to Rails root) of the file to import.
24
+ # @param filepath [String] Absolute URL path (relative to Rails root) of the file to import.
25
25
  # Should be the actual asset file, eg. app.css, some/component.js.
26
26
  # @param resolve [String] description of the file to resolve and import.
27
27
  # @return [String] the digest of the imported file path if a css module (*.module.css).
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Proscenium
4
+ class Middleware
5
+ # This middleware handles requests for assets in Rails engines. An engine that wants to expose
6
+ # its assets via Proscenium to the application must add itself to the list of engines in the
7
+ # Proscenium config options `Proscenium.config.engines`.
8
+ #
9
+ # For example, we have a gem that exposes a Rails engine.
10
+ #
11
+ # module Gem1
12
+ # class Engine < ::Rails::Engine
13
+ # config.proscenium.engines << self
14
+ # end
15
+ # end
16
+ #
17
+ # When this gem is installed in any Rails application, its assets will be available at the URL
18
+ # `/gem1/...`. For example, if the gem has a file `lib/styles.css`, it can be requested at
19
+ # `/gem1/lib/styles.css`.
20
+ #
21
+ class Engines < Esbuild
22
+ def real_path
23
+ @real_path ||= Pathname.new(@request.path.delete_prefix("/#{engine.engine_name}")).to_s
24
+ end
25
+
26
+ def root_for_readable
27
+ engine.root
28
+ end
29
+
30
+ def engine
31
+ @engine ||= Proscenium.config.engines.find do |engine|
32
+ @request.path.start_with?("/#{engine.engine_name}")
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -20,9 +20,9 @@ module Proscenium
20
20
  end
21
21
 
22
22
  def attempt
23
- render_response Proscenium::Builder.build(path_to_build, root: Rails.root.to_s,
24
- base_url: @request.base_url)
25
- rescue Proscenium::Builder::CompileError => e
23
+ render_response Builder.build(path_to_build, root: Rails.root.to_s,
24
+ base_url: @request.base_url)
25
+ rescue Builder::CompileError => e
26
26
  raise self.class::CompileError, { file: @request.fullpath, detail: e.message }, caller
27
27
  end
28
28
  end
@@ -9,6 +9,7 @@ module Proscenium
9
9
 
10
10
  autoload :Base
11
11
  autoload :Esbuild
12
+ autoload :Engines
12
13
  autoload :Runtime
13
14
  autoload :Url
14
15
 
@@ -42,12 +43,19 @@ module Proscenium
42
43
  def find_type(request)
43
44
  return Url if request.path.match?(%r{^/https?%3A%2F%2F})
44
45
  return Runtime if request.path.match?(%r{^/@proscenium/})
46
+ return Esbuild if Pathname.new(request.path).fnmatch?(app_path_glob, File::FNM_EXTGLOB)
45
47
 
46
- Esbuild if Pathname.new(request.path).fnmatch?(app_path_glob, File::FNM_EXTGLOB)
48
+ Engines if Pathname.new(request.path).fnmatch?(engines_path_glob, File::FNM_EXTGLOB)
47
49
  end
48
50
 
49
51
  def app_path_glob
50
- "/{#{Proscenium::ALLOWED_DIRECTORIES.join(',')}}/**.{#{FILE_EXTENSIONS.join(',')}}"
52
+ "/{#{Proscenium::ALLOWED_DIRECTORIES}}/**.{#{FILE_EXTENSIONS.join(',')}}"
53
+ end
54
+
55
+ def engines_path_glob
56
+ names = Proscenium.config.engines.map(&:engine_name)
57
+
58
+ "/{#{names.join(',')}}/{#{Proscenium::ALLOWED_DIRECTORIES}}/**.{#{FILE_EXTENSIONS.join(',')}}"
51
59
  end
52
60
 
53
61
  # TODO: handle precompiled assets
@@ -3,6 +3,14 @@
3
3
  module Proscenium
4
4
  # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
5
5
  module Monkey
6
+ module DebugView
7
+ def initialize(assigns)
8
+ paths = [RESCUES_TEMPLATE_PATH, Rails.root.join('lib', 'templates').to_s]
9
+ lookup_context = ActionView::LookupContext.new(paths)
10
+ super(lookup_context, assigns, nil)
11
+ end
12
+ end
13
+
6
14
  module TemplateRenderer
7
15
  private
8
16
 
@@ -29,31 +29,29 @@ module Proscenium
29
29
  # defined means a faster build, as esbuild will have less to do.
30
30
  config.proscenium.env_vars = Set.new
31
31
 
32
- # A hash of gems that can be side loaded. Assets from gems listed here can be side loaded.
32
+ # Rails engines to expose and allow Proscenium to serve their assets.
33
33
  #
34
- # Because side loading uses URL paths, any gem dependencies that side load assets will fail,
35
- # because the URL path will be relative to the application's root, and not the gem's root. By
36
- # specifying a list of gems that can be side loaded, Proscenium will be able to resolve the URL
37
- # path to the gem's root, and side load the asset.
38
- #
39
- # Side loading gems rely on NPM and a package.json file in the gem root. This ensures that any
40
- # dependencies are resolved correctly. This is required even if your gem has no package
41
- # dependencies.
34
+ # A Rails engine that has assets, can add Proscenium as a gem dependency, and then add itself
35
+ # to this list. Proscenium will then serve the engine's assets at the URL path beginning with
36
+ # the engine name.
42
37
  #
43
38
  # Example:
44
- # config.proscenium.side_load_gems['mygem'] = {
45
- # root: gem_root,
46
- # package_name: 'mygem'
47
- # }
48
- config.proscenium.side_load_gems = {}
39
+ # class Gem1::Engine < ::Rails::Engine
40
+ # config.proscenium.engines << self
41
+ # end
42
+ config.proscenium.engines = Set.new
43
+
44
+ config.action_dispatch.rescue_templates = {
45
+ 'Proscenium::Builder::BuildError' => 'build_error'
46
+ }
49
47
 
50
48
  initializer 'proscenium.middleware' do |app|
51
49
  app.middleware.insert_after ActionDispatch::Static, Middleware
52
- app.middleware.insert_after ActionDispatch::Static, Rack::ETag, 'no-cache'
53
- app.middleware.insert_after ActionDispatch::Static, Rack::ConditionalGet
50
+ # app.middleware.insert_after ActionDispatch::Static, Rack::ETag, 'no-cache'
51
+ # app.middleware.insert_after ActionDispatch::Static, Rack::ConditionalGet
54
52
  end
55
53
 
56
- initializer 'proscenium.monkey_views' do
54
+ initializer 'proscenium.monkey_patches' do
57
55
  ActiveSupport.on_load(:action_view) do
58
56
  ActionView::TemplateRenderer.prepend Monkey::TemplateRenderer
59
57
  ActionView::PartialRenderer.prepend Monkey::PartialRenderer
@@ -71,3 +69,13 @@ module Proscenium
71
69
  end
72
70
  end
73
71
  end
72
+
73
+ # Monkey path ActionDispatch::DebugView to use our custom error template on BuildError exceptions.
74
+ class ActionDispatch::DebugView
75
+ def initialize(assigns)
76
+ paths = [RESCUES_TEMPLATE_PATH,
77
+ Proscenium::Railtie.root.join('lib', 'proscenium', 'templates').to_s]
78
+ lookup_context = ActionView::LookupContext.new(paths)
79
+ super(lookup_context, assigns, nil)
80
+ end
81
+ end
@@ -11,7 +11,9 @@ module Proscenium
11
11
  #
12
12
  # @param path [String] Can be URL path, file system path, or bare specifier (ie. NPM package).
13
13
  # @return [String] URL path.
14
- def self.resolve(path) # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
14
+ #
15
+ # rubocop:disable Metrics/*
16
+ def self.resolve(path)
15
17
  self.resolved ||= {}
16
18
 
17
19
  self.resolved[path] ||= begin
@@ -21,15 +23,8 @@ module Proscenium
21
23
 
22
24
  if path.start_with?('@proscenium/')
23
25
  "/#{path}"
24
- elsif (gem = Proscenium.config.side_load_gems.find do |_, x|
25
- path.start_with? "#{x[:root]}/"
26
- end)
27
- unless (package_name = gem[1][:package_name] || gem[0])
28
- # TODO: manually resolve the path without esbuild
29
- raise PathResolutionFailed, path
30
- end
31
-
32
- Builder.resolve "#{package_name}/#{path.delete_prefix("#{gem[1][:root]}/")}"
26
+ elsif (engine = Proscenium.config.engines.find { |e| path.start_with? "#{e.root}/" })
27
+ path.sub(/^#{engine.root}/, "/#{engine.engine_name}")
33
28
  elsif path.start_with?("#{Rails.root}/")
34
29
  path.delete_prefix Rails.root.to_s
35
30
  else
@@ -37,5 +32,6 @@ module Proscenium
37
32
  end
38
33
  end
39
34
  end
35
+ # rubocop:enable Metrics/*
40
36
  end
41
37
  end
@@ -0,0 +1,27 @@
1
+ <header>
2
+ <h1>
3
+ <%= @exception.class.to_s %>
4
+ </h1>
5
+ </header>
6
+
7
+ <main role="main" id="container">
8
+ <%= render "rescues/message_and_suggestions", exception: @exception %>
9
+
10
+ <% if @exception.error['location'] %>
11
+ <div class="source">
12
+ <div class="data">
13
+ <pre>
14
+
15
+ <%= @exception.error['location']['file'] %>:<%= @exception.error['location']['line'] %>:<%= @exception.error['location']['column'] %>
16
+
17
+ <%= @exception.error['location']['line'].to_s.rjust 5 %> │ <%= @exception.error['location']['line_text'] %>
18
+ │ <%= (@exception.error['location']['length'] > 1 ? "~" * @exception.error['location']['length'] : "^").rjust(@exception.error['location']['column'] + @exception.error['location']['length']) %>
19
+ <%- if @exception.error['location']['suggestion'].present? -%> + │ <%= @exception.error['location']['suggestion'].rjust(@exception.error['location']['column'] + 1) %>
20
+ <% else %> <%- end -%>
21
+ </pre>
22
+ </div>
23
+ </div>
24
+ <% end %>
25
+
26
+ <%= render template: "rescues/_request_and_response" %>
27
+ </main>
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Proscenium
4
- VERSION = '0.11.0.pre.7'
4
+ VERSION = '0.11.0.pre.8'
5
5
  end
data/lib/proscenium.rb CHANGED
@@ -8,7 +8,7 @@ module Proscenium
8
8
  FILE_EXTENSIONS = ['js', 'mjs', 'ts', 'jsx', 'tsx', 'css', 'js.map', 'mjs.map', 'jsx.map',
9
9
  'ts.map', 'tsx.map', 'css.map'].freeze
10
10
 
11
- ALLOWED_DIRECTORIES = %w[app lib config vendor node_modules].freeze
11
+ ALLOWED_DIRECTORIES = 'app,lib,config,vendor,node_modules'
12
12
 
13
13
  # Environment variables that should always be passed to the builder.
14
14
  DEFAULT_ENV_VARS = Set['RAILS_ENV', 'NODE_ENV'].freeze
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.11.0.pre.7
4
+ version: 0.11.0.pre.8
5
5
  platform: x86_64-linux
6
6
  authors:
7
7
  - Joel Moss
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-09-20 00:00:00.000000000 Z
11
+ date: 2023-09-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -105,6 +105,7 @@ files:
105
105
  - lib/proscenium/log_subscriber.rb
106
106
  - lib/proscenium/middleware.rb
107
107
  - lib/proscenium/middleware/base.rb
108
+ - lib/proscenium/middleware/engines.rb
108
109
  - lib/proscenium/middleware/esbuild.rb
109
110
  - lib/proscenium/middleware/runtime.rb
110
111
  - lib/proscenium/middleware/url.rb
@@ -118,6 +119,7 @@ files:
118
119
  - lib/proscenium/resolver.rb
119
120
  - lib/proscenium/side_load.rb
120
121
  - lib/proscenium/source_path.rb
122
+ - lib/proscenium/templates/rescues/build_error.html.erb
121
123
  - lib/proscenium/utils.rb
122
124
  - lib/proscenium/version.rb
123
125
  - lib/proscenium/view_component.rb