proscenium 0.5.1-x86_64-linux → 0.7.0-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.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +128 -92
  3. data/lib/proscenium/css_module/class_names_resolver.rb +66 -0
  4. data/lib/proscenium/css_module/resolver.rb +76 -0
  5. data/lib/proscenium/css_module.rb +18 -39
  6. data/lib/proscenium/esbuild/golib.rb +97 -0
  7. data/lib/proscenium/esbuild.rb +32 -0
  8. data/lib/proscenium/ext/proscenium +0 -0
  9. data/lib/proscenium/ext/proscenium.h +109 -0
  10. data/lib/proscenium/helper.rb +0 -23
  11. data/lib/proscenium/log_subscriber.rb +26 -0
  12. data/lib/proscenium/middleware/base.rb +28 -36
  13. data/lib/proscenium/middleware/esbuild.rb +18 -44
  14. data/lib/proscenium/middleware/url.rb +1 -6
  15. data/lib/proscenium/middleware.rb +12 -16
  16. data/lib/proscenium/phlex/component_concerns.rb +27 -0
  17. data/lib/proscenium/phlex/page.rb +62 -0
  18. data/lib/proscenium/phlex/react_component.rb +52 -8
  19. data/lib/proscenium/phlex/resolve_css_modules.rb +67 -0
  20. data/lib/proscenium/phlex.rb +34 -33
  21. data/lib/proscenium/railtie.rb +41 -67
  22. data/lib/proscenium/side_load/ensure_loaded.rb +25 -0
  23. data/lib/proscenium/side_load/helper.rb +25 -0
  24. data/lib/proscenium/side_load/monkey.rb +48 -0
  25. data/lib/proscenium/side_load.rb +58 -52
  26. data/lib/proscenium/version.rb +1 -1
  27. data/lib/proscenium/view_component/react_component.rb +14 -0
  28. data/lib/proscenium/view_component.rb +28 -18
  29. data/lib/proscenium.rb +79 -2
  30. metadata +35 -74
  31. data/app/channels/proscenium/connection.rb +0 -13
  32. data/app/channels/proscenium/reload_channel.rb +0 -9
  33. data/bin/esbuild +0 -0
  34. data/bin/lightningcss +0 -0
  35. data/config/routes.rb +0 -7
  36. data/lib/proscenium/compiler.js +0 -84
  37. data/lib/proscenium/compilers/esbuild/argument_error.js +0 -24
  38. data/lib/proscenium/compilers/esbuild/compile_error.js +0 -148
  39. data/lib/proscenium/compilers/esbuild/css/postcss.js +0 -67
  40. data/lib/proscenium/compilers/esbuild/css_plugin.js +0 -172
  41. data/lib/proscenium/compilers/esbuild/env_plugin.js +0 -46
  42. data/lib/proscenium/compilers/esbuild/http_bundle_plugin.js +0 -53
  43. data/lib/proscenium/compilers/esbuild/import_map.js +0 -59
  44. data/lib/proscenium/compilers/esbuild/resolve_plugin.js +0 -205
  45. data/lib/proscenium/compilers/esbuild/setup_plugin.js +0 -45
  46. data/lib/proscenium/compilers/esbuild/solidjs_plugin.js +0 -24
  47. data/lib/proscenium/compilers/esbuild.bench.js +0 -14
  48. data/lib/proscenium/compilers/esbuild.js +0 -179
  49. data/lib/proscenium/link_to_helper.rb +0 -40
  50. data/lib/proscenium/middleware/lightningcss.rb +0 -64
  51. data/lib/proscenium/middleware/outside_root.rb +0 -26
  52. data/lib/proscenium/middleware/runtime.rb +0 -22
  53. data/lib/proscenium/middleware/static.rb +0 -14
  54. data/lib/proscenium/phlex/component.rb +0 -9
  55. data/lib/proscenium/precompile.rb +0 -31
  56. data/lib/proscenium/runtime/auto_reload.js +0 -40
  57. data/lib/proscenium/runtime/react_shim/index.js +0 -1
  58. data/lib/proscenium/runtime/react_shim/package.json +0 -5
  59. data/lib/proscenium/utils.js +0 -8
  60. data/lib/tasks/assets.rake +0 -19
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ffi'
4
+ require 'oj'
5
+
6
+ module Proscenium
7
+ class Esbuild::Golib
8
+ class Result < FFI::Struct
9
+ layout :success, :bool,
10
+ :response, :string
11
+ end
12
+
13
+ module Request
14
+ extend FFI::Library
15
+ ffi_lib Pathname.new(__dir__).join('../ext/proscenium').to_s
16
+
17
+ enum :environment, [:development, 1, :test, :production]
18
+
19
+ attach_function :build, [
20
+ :string, # path or entry point
21
+ :string, # root
22
+ :string, # base URL of the Rails app. eg. https://example.com
23
+ :environment, # Rails environment as a Symbol
24
+ :string, # path to import map, relative to root
25
+ :bool # debugging enabled?
26
+ ], Result.by_value
27
+
28
+ attach_function :resolve, [
29
+ :string, # path or entry point
30
+ :string, # root
31
+ :environment, # Rails environment as a Symbol
32
+ :string # path to import map, relative to root
33
+ ], Result.by_value
34
+ end
35
+
36
+ class BuildError < StandardError
37
+ attr_reader :error, :path
38
+
39
+ def initialize(path, error)
40
+ error = Oj.load(error, mode: :strict).deep_transform_keys(&:underscore)
41
+
42
+ super "Failed to build '#{path}' -- #{error['text']}"
43
+ end
44
+ end
45
+
46
+ class ResolveError < StandardError
47
+ attr_reader :error_msg, :path
48
+
49
+ def initialize(path, error_msg)
50
+ super "Failed to resolve '#{path}' -- #{error_msg}"
51
+ end
52
+ end
53
+
54
+ def initialize(root: nil, base_url: nil)
55
+ @root = root || Rails.root
56
+ @base_url = base_url
57
+ end
58
+
59
+ def self.resolve(path)
60
+ new.resolve(path)
61
+ end
62
+
63
+ def self.build(path)
64
+ new.build(path)
65
+ end
66
+
67
+ def build(path)
68
+ result = Request.build(path, @root.to_s, @base_url, Rails.env.to_sym, import_map,
69
+ Rails.env.development?)
70
+ raise BuildError.new(path, result[:response]) unless result[:success]
71
+
72
+ result[:response]
73
+ end
74
+
75
+ def resolve(path)
76
+ result = Request.resolve(path, @root.to_s, Rails.env.to_sym, import_map)
77
+ raise ResolveError.new(path, result[:response]) unless result[:success]
78
+
79
+ result[:response]
80
+ end
81
+
82
+ private
83
+
84
+ def import_map
85
+ return unless (path = Rails.root&.join('config'))
86
+
87
+ if (json = path.join('import_map.json')).exist?
88
+ return json.relative_path_from(@root).to_s
89
+ end
90
+ if (js = path.join('import_map.js')).exist?
91
+ return js.relative_path_from(@root).to_s
92
+ end
93
+
94
+ nil
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Proscenium
4
+ class Esbuild
5
+ class CompileError < StandardError; end
6
+
7
+ extend ActiveSupport::Autoload
8
+
9
+ autoload :Golib
10
+
11
+ def self.build(...)
12
+ new(...).build
13
+ end
14
+
15
+ def initialize(path, root:, base_url:)
16
+ @path = path
17
+ @root = root
18
+ @base_url = base_url
19
+ end
20
+
21
+ def build
22
+ Proscenium::Esbuild::Golib.new(root: @root, base_url: @base_url).build(@path)
23
+ end
24
+
25
+ private
26
+
27
+ def cache_query_string
28
+ q = Proscenium.config.cache_query_string
29
+ q ? "--cache-query-string #{q}" : nil
30
+ end
31
+ end
32
+ end
Binary file
@@ -0,0 +1,109 @@
1
+ /* Code generated by cmd/cgo; DO NOT EDIT. */
2
+
3
+ /* package joelmoss/proscenium */
4
+
5
+
6
+ #line 1 "cgo-builtin-export-prolog"
7
+
8
+ #include <stddef.h>
9
+
10
+ #ifndef GO_CGO_EXPORT_PROLOGUE_H
11
+ #define GO_CGO_EXPORT_PROLOGUE_H
12
+
13
+ #ifndef GO_CGO_GOSTRING_TYPEDEF
14
+ typedef struct { const char *p; ptrdiff_t n; } _GoString_;
15
+ #endif
16
+
17
+ #endif
18
+
19
+ /* Start of preamble from import "C" comments. */
20
+
21
+
22
+ #line 3 "main.go"
23
+
24
+ struct Result {
25
+ int success;
26
+ char* response;
27
+ };
28
+
29
+ #line 1 "cgo-generated-wrapper"
30
+
31
+
32
+ /* End of preamble from import "C" comments. */
33
+
34
+
35
+ /* Start of boilerplate cgo prologue. */
36
+ #line 1 "cgo-gcc-export-header-prolog"
37
+
38
+ #ifndef GO_CGO_PROLOGUE_H
39
+ #define GO_CGO_PROLOGUE_H
40
+
41
+ typedef signed char GoInt8;
42
+ typedef unsigned char GoUint8;
43
+ typedef short GoInt16;
44
+ typedef unsigned short GoUint16;
45
+ typedef int GoInt32;
46
+ typedef unsigned int GoUint32;
47
+ typedef long long GoInt64;
48
+ typedef unsigned long long GoUint64;
49
+ typedef GoInt64 GoInt;
50
+ typedef GoUint64 GoUint;
51
+ typedef size_t GoUintptr;
52
+ typedef float GoFloat32;
53
+ typedef double GoFloat64;
54
+ #ifdef _MSC_VER
55
+ #include <complex.h>
56
+ typedef _Fcomplex GoComplex64;
57
+ typedef _Dcomplex GoComplex128;
58
+ #else
59
+ typedef float _Complex GoComplex64;
60
+ typedef double _Complex GoComplex128;
61
+ #endif
62
+
63
+ /*
64
+ static assertion to make sure the file is being used on architecture
65
+ at least with matching size of GoInt.
66
+ */
67
+ typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];
68
+
69
+ #ifndef GO_CGO_GOSTRING_TYPEDEF
70
+ typedef _GoString_ GoString;
71
+ #endif
72
+ typedef void *GoMap;
73
+ typedef void *GoChan;
74
+ typedef struct { void *t; void *v; } GoInterface;
75
+ typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
76
+
77
+ #endif
78
+
79
+ /* End of boilerplate cgo prologue. */
80
+
81
+ #ifdef __cplusplus
82
+ extern "C" {
83
+ #endif
84
+
85
+
86
+ // Build the given `path` in the `root`.
87
+ //
88
+ // - path - The path to build relative to `root`.
89
+ // - root - The working directory.
90
+ // - baseUrl - base URL of the Rails app. eg. https://example.com
91
+ // - env - The environment (1 = development, 2 = test, 3 = production)
92
+ // - importMap - Path to the import map relative to `root`.
93
+ // - bundle
94
+ // - debug
95
+ //
96
+ extern struct Result build(char* filepath, char* root, char* baseUrl, unsigned int env, char* importMap, GoUint8 bundle, GoUint8 debug);
97
+
98
+ // Resolve the given `path` relative to the `root`.
99
+ //
100
+ // - path - The path to build relative to `root`.
101
+ // - root - The working directory.
102
+ // - env - The environment (1 = development, 2 = test, 3 = production)
103
+ // - importMap - Path to the import map relative to `root`.
104
+ //
105
+ extern struct Result resolve(char* path, char* root, unsigned int env, char* importMap);
106
+
107
+ #ifdef __cplusplus
108
+ }
109
+ #endif
@@ -15,28 +15,5 @@ module Proscenium
15
15
 
16
16
  super
17
17
  end
18
-
19
- def side_load_stylesheets
20
- return unless Proscenium::Current.loaded
21
-
22
- Proscenium::Current.loaded[:css].map do |sheet|
23
- stylesheet_link_tag(sheet, id: "_#{Digest::SHA1.hexdigest("/#{sheet}")[..7]}")
24
- end.join("\n").html_safe
25
- end
26
-
27
- def side_load_javascripts(**options)
28
- return unless Proscenium::Current.loaded
29
-
30
- javascript_include_tag(*Proscenium::Current.loaded[:js], options)
31
- end
32
-
33
- def proscenium_dev
34
- return unless Proscenium.config.auto_reload
35
-
36
- javascript_tag %(
37
- import autoReload from '/proscenium-runtime/auto_reload.js';
38
- autoReload('#{Proscenium::Railtie.websocket_mount_path}');
39
- ), type: 'module', defer: true
40
- end
41
18
  end
42
19
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/log_subscriber'
4
+
5
+ module Proscenium
6
+ class LogSubscriber < ActiveSupport::LogSubscriber
7
+ def sideload(event)
8
+ info do
9
+ " [Proscenium] Side loaded #{event.payload[:identifier]}"
10
+ end
11
+ end
12
+
13
+ def build(event)
14
+ path = event.payload[:identifier]
15
+ path = path.start_with?(/https?%3A%2F%2F/) ? CGI.unescape(path) : path
16
+
17
+ info do
18
+ message = +"[Proscenium] Building #{path}"
19
+ message << " (Duration: #{event.duration.round(1)}ms | Allocations: #{event.allocations})"
20
+ message << "\n" if defined?(Rails.env) && Rails.env.development?
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ Proscenium::LogSubscriber.attach_to :proscenium
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'open3'
4
+ require 'oj'
4
5
 
5
6
  module Proscenium
6
7
  class Middleware
@@ -9,7 +10,15 @@ module Proscenium
9
10
 
10
11
  # Error when the result of the build returns an error. For example, when esbuild returns
11
12
  # errors.
12
- class CompileError < StandardError; end
13
+ class CompileError < StandardError
14
+ attr_reader :detail, :file
15
+
16
+ def initialize(args)
17
+ @detail = args[:detail]
18
+ @file = args[:file]
19
+ super "Failed to build '#{args[:file]}' -- #{detail}"
20
+ end
21
+ end
13
22
 
14
23
  def self.attempt(request)
15
24
  new(request).renderable!&.attempt
@@ -25,12 +34,25 @@ module Proscenium
25
34
 
26
35
  private
27
36
 
37
+ def real_path
38
+ @request.path
39
+ end
40
+
41
+ # @return [String] the path to the file without the leading slash which will be built.
42
+ def path_to_build
43
+ @request.path[1..]
44
+ end
45
+
46
+ def sourcemap?
47
+ @request.path.ends_with?('.map')
48
+ end
49
+
28
50
  def renderable?
29
51
  file_readable?
30
52
  end
31
53
 
32
- def file_readable?(file = @request.path_info)
33
- return unless (path = clean_path(file))
54
+ def file_readable?
55
+ return unless (path = clean_path(sourcemap? ? real_path[0...-4] : real_path))
34
56
 
35
57
  file_stat = File.stat(Pathname(root).join(path.delete_prefix('/').b).to_s)
36
58
  rescue SystemCallError
@@ -50,7 +72,7 @@ module Proscenium
50
72
 
51
73
  def content_type
52
74
  @content_type ||
53
- ::Rack::Mime.mime_type(::File.extname(@request.path_info), nil) ||
75
+ ::Rack::Mime.mime_type(::File.extname(path_to_build), nil) ||
54
76
  'application/javascript'
55
77
  end
56
78
 
@@ -59,9 +81,10 @@ module Proscenium
59
81
  response.write content
60
82
  response.content_type = content_type
61
83
  response['X-Proscenium-Middleware'] = name
84
+ response.set_header 'SourceMap', "#{@request.path_info}.map"
62
85
 
63
86
  if Proscenium.config.cache_query_string && Proscenium.config.cache_max_age
64
- response['Cache-Control'] = "public, max-age=#{Proscenium.config.cache_max_age}"
87
+ response.cache! Proscenium.config.cache_max_age
65
88
  end
66
89
 
67
90
  yield response if block_given?
@@ -69,37 +92,6 @@ module Proscenium
69
92
  response.finish
70
93
  end
71
94
 
72
- def build(cmd)
73
- stdout, stderr, status = Open3.capture3(cmd)
74
-
75
- unless status.success?
76
- raise self.class::CompileError, stderr if status.exitstatus == 2
77
-
78
- raise BuildError, stderr
79
- end
80
-
81
- unless stderr.empty?
82
- raise BuildError, "Proscenium build of #{name}:'#{@request.fullpath}' failed -- #{stderr}"
83
- end
84
-
85
- stdout
86
- end
87
-
88
- def benchmark(type)
89
- super logging_message(type)
90
- end
91
-
92
- # rubocop:disable Style/FormatStringToken
93
- def logging_message(type)
94
- format '[Proscenium] Request (%s) %s for %s at %s',
95
- type, @request.fullpath, @request.ip, Time.now.to_default_s
96
- end
97
- # rubocop:enable Style/FormatStringToken
98
-
99
- def logger
100
- Rails.logger
101
- end
102
-
103
95
  def name
104
96
  @name ||= self.class.name.split('::').last.downcase
105
97
  end
@@ -3,55 +3,29 @@
3
3
  module Proscenium
4
4
  class Middleware
5
5
  class Esbuild < Base
6
- class CompileError < StandardError
7
- attr_reader :detail
8
-
9
- def initialize(detail)
10
- @detail = ActiveSupport::HashWithIndifferentAccess.new(Oj.load(detail, mode: :strict))
11
-
12
- super "#{@detail[:text]} in #{@detail[:location][:file]}:#{@detail[:location][:line]}"
6
+ class CompileError < Base::CompileError
7
+ def initialize(args)
8
+ detail = args[:detail]
9
+ detail = ActiveSupport::HashWithIndifferentAccess.new(Oj.load(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 args
13
19
  end
14
20
  end
15
21
 
16
22
  def attempt
17
- benchmark :esbuild do
18
- render_response build([
19
- "#{cli} --root #{root}",
20
- cache_query_string,
21
- "--lightningcss-bin #{lightningcss_cli} #{path}"
22
- ].compact.join(' '))
23
- end
24
- rescue CompileError => e
25
- render_response "export default #{e.detail.to_json}" do |response|
26
- response['X-Proscenium-Middleware'] = 'Esbuild::CompileError'
27
- end
28
- end
29
-
30
- private
31
-
32
- def path
33
- @request.path[1..]
34
- end
35
-
36
- def cli
37
- if ENV['PROSCENIUM_TEST']
38
- 'deno run -q --import-map import_map.json -A lib/proscenium/compilers/esbuild.js'
39
- else
40
- Gem.bin_path 'proscenium', 'esbuild'
23
+ ActiveSupport::Notifications.instrument('build.proscenium', identifier: path_to_build) do
24
+ render_response Proscenium::Esbuild.build(path_to_build, root: root,
25
+ base_url: @request.base_url)
41
26
  end
42
- end
43
-
44
- def lightningcss_cli
45
- if ENV['PROSCENIUM_TEST']
46
- 'bin/lightningcss'
47
- else
48
- Gem.bin_path 'proscenium', 'lightningcss'
49
- end
50
- end
51
-
52
- def cache_query_string
53
- q = Proscenium.config.cache_query_string
54
- q ? "--cache-query-string #{q}" : nil
27
+ rescue Proscenium::Esbuild::CompileError => e
28
+ raise self.class::CompileError, { file: @request.fullpath, detail: e.message }, caller
55
29
  end
56
30
  end
57
31
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Proscenium
4
4
  class Middleware
5
- # Handles requests prefixed with "url:https://"; downloading, caching, and compiling them.
5
+ # Handles requests for URL encoded URL's.
6
6
  class Url < Esbuild
7
7
  private
8
8
 
@@ -11,11 +11,6 @@ module Proscenium
11
11
  def renderable?
12
12
  true
13
13
  end
14
-
15
- # @override [Esbuild]
16
- def path
17
- CGI.unescape(@request.path)[1..]
18
- end
19
14
  end
20
15
  end
21
16
  end
@@ -9,9 +9,7 @@ module Proscenium
9
9
 
10
10
  autoload :Base
11
11
  autoload :Esbuild
12
- autoload :Runtime
13
12
  autoload :Url
14
- autoload :OutsideRoot
15
13
 
16
14
  def initialize(app)
17
15
  @app = app
@@ -27,37 +25,35 @@ module Proscenium
27
25
 
28
26
  private
29
27
 
30
- # Look for the precompiled file in public/assets first, then fallback to the Proscenium
31
- # middleware that matches the type of file requested, ie: .js => esbuild.
32
- # See Rails.application.config.proscenium.glob_types.
33
28
  def attempt(request)
34
29
  return unless (type = find_type(request))
35
30
 
36
- file_handler.attempt(request.env) || type.attempt(request)
31
+ # file_handler.attempt(request.env) || type.attempt(request)
32
+
33
+ type.attempt(request)
37
34
  end
38
35
 
39
- # Returns the type of file being requested using Rails.application.config.proscenium.glob_types.
36
+ # Returns the type of file being requested using Proscenium::MIDDLEWARE_GLOB_TYPES.
40
37
  def find_type(request)
41
38
  path = Pathname.new(request.path)
42
39
 
43
- # Non-production only!
44
- if request.query_string == 'outsideRoot'
45
- return if Rails.env.production?
46
- return OutsideRoot if path.fnmatch?(glob_types[:outsideRoot], File::FNM_EXTGLOB)
47
- end
48
-
49
40
  return Url if request.path.match?(glob_types[:url])
50
- return Runtime if path.fnmatch?(glob_types[:runtime], File::FNM_EXTGLOB)
51
- return Esbuild if path.fnmatch?(glob_types[:esbuild], File::FNM_EXTGLOB)
41
+ return Esbuild if path.fnmatch?(application_glob_type, File::FNM_EXTGLOB)
52
42
  end
53
43
 
44
+ # TODO: handle precompiled assets
54
45
  def file_handler
55
46
  ::ActionDispatch::FileHandler.new Rails.public_path.join('assets').to_s,
56
47
  headers: { 'X-Proscenium-Middleware' => 'precompiled' }
57
48
  end
58
49
 
59
50
  def glob_types
60
- Rails.application.config.proscenium.glob_types
51
+ @glob_types ||= Proscenium::MIDDLEWARE_GLOB_TYPES
52
+ end
53
+
54
+ def application_glob_type
55
+ paths = Rails.application.config.proscenium.include_paths.join(',')
56
+ "/{#{paths}}#{glob_types[:application]}"
61
57
  end
62
58
  end
63
59
  end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Proscenium::Phlex::ComponentConcerns
4
+ module CssModules
5
+ extend ActiveSupport::Concern
6
+ include Proscenium::CssModule
7
+ include Proscenium::Phlex::ResolveCssModules
8
+
9
+ # class_methods do
10
+ # # FIXME: Still needed?
11
+ # def path
12
+ # pp name, super
13
+ # pp Module.const_source_location(name).first
14
+
15
+ # name && Pathname.new(Module.const_source_location(name).first)
16
+ # rescue NameError
17
+ # nil
18
+ # end
19
+ # end
20
+
21
+ private
22
+
23
+ def path
24
+ self.class.path
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'phlex/rails'
4
+
5
+ # Include this in your view for additional logic for rendering a full HTML page, usually from a
6
+ # controller.
7
+ module Proscenium::Phlex::Page
8
+ include Phlex::Rails::Helpers::CSPMetaTag
9
+ include Phlex::Rails::Helpers::CSRFMetaTags
10
+ include Phlex::Rails::Helpers::FaviconLinkTag
11
+ include Phlex::Rails::Helpers::PreloadLinkTag
12
+ include Phlex::Rails::Helpers::StyleSheetLinkTag
13
+ include Phlex::Rails::Helpers::ActionCableMetaTag
14
+ include Phlex::Rails::Helpers::AutoDiscoveryLinkTag
15
+ include Phlex::Rails::Helpers::JavaScriptIncludeTag
16
+ include Phlex::Rails::Helpers::JavaScriptImportMapTags
17
+ include Phlex::Rails::Helpers::JavaScriptImportModuleTag
18
+
19
+ def self.included(klass)
20
+ klass.extend(Phlex::Rails::Layout::Interface)
21
+ end
22
+
23
+ def template(&block)
24
+ doctype
25
+ html do
26
+ head
27
+ body(&block)
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def after_template
34
+ super
35
+ @_buffer.gsub!('<!-- [SIDE_LOAD_STYLESHEETS] -->', capture { side_load_stylesheets })
36
+ end
37
+
38
+ def page_title
39
+ Rails.application.class.name.deconstantize
40
+ end
41
+
42
+ def head
43
+ super do
44
+ title { page_title }
45
+
46
+ yield if block_given?
47
+
48
+ csp_meta_tag
49
+ csrf_meta_tags
50
+
51
+ comment { '[SIDE_LOAD_STYLESHEETS]' }
52
+ end
53
+ end
54
+
55
+ def body
56
+ super do
57
+ yield if block_given?
58
+
59
+ side_load_javascripts defer: true, type: :module
60
+ end
61
+ end
62
+ end