proscenium 0.16.0

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 (59) hide show
  1. checksums.yaml +7 -0
  2. data/CODE_OF_CONDUCT.md +84 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +908 -0
  5. data/lib/proscenium/builder.rb +189 -0
  6. data/lib/proscenium/core_ext/object/css_module_ivars.rb +19 -0
  7. data/lib/proscenium/css_module/path.rb +31 -0
  8. data/lib/proscenium/css_module/rewriter.rb +44 -0
  9. data/lib/proscenium/css_module/transformer.rb +84 -0
  10. data/lib/proscenium/css_module.rb +57 -0
  11. data/lib/proscenium/ensure_loaded.rb +27 -0
  12. data/lib/proscenium/ext/proscenium +0 -0
  13. data/lib/proscenium/ext/proscenium.h +131 -0
  14. data/lib/proscenium/helper.rb +70 -0
  15. data/lib/proscenium/importer.rb +134 -0
  16. data/lib/proscenium/libs/custom_element.js +54 -0
  17. data/lib/proscenium/libs/react-manager/index.jsx +121 -0
  18. data/lib/proscenium/libs/react-manager/react.js +2 -0
  19. data/lib/proscenium/libs/stimulus-loading.js +65 -0
  20. data/lib/proscenium/libs/test.js +1 -0
  21. data/lib/proscenium/libs/ujs/class.js +15 -0
  22. data/lib/proscenium/libs/ujs/data_confirm.js +23 -0
  23. data/lib/proscenium/libs/ujs/data_disable_with.js +68 -0
  24. data/lib/proscenium/libs/ujs/index.js +9 -0
  25. data/lib/proscenium/log_subscriber.rb +37 -0
  26. data/lib/proscenium/middleware/base.rb +103 -0
  27. data/lib/proscenium/middleware/engines.rb +45 -0
  28. data/lib/proscenium/middleware/esbuild.rb +30 -0
  29. data/lib/proscenium/middleware/runtime.rb +18 -0
  30. data/lib/proscenium/middleware/url.rb +16 -0
  31. data/lib/proscenium/middleware.rb +76 -0
  32. data/lib/proscenium/monkey.rb +95 -0
  33. data/lib/proscenium/phlex/asset_inclusions.rb +17 -0
  34. data/lib/proscenium/phlex/css_modules.rb +79 -0
  35. data/lib/proscenium/phlex/react_component.rb +32 -0
  36. data/lib/proscenium/phlex.rb +42 -0
  37. data/lib/proscenium/railtie.rb +106 -0
  38. data/lib/proscenium/react_componentable.rb +95 -0
  39. data/lib/proscenium/resolver.rb +39 -0
  40. data/lib/proscenium/side_load.rb +155 -0
  41. data/lib/proscenium/source_path.rb +15 -0
  42. data/lib/proscenium/templates/rescues/build_error.html.erb +30 -0
  43. data/lib/proscenium/ui/breadcrumbs/component.module.css +14 -0
  44. data/lib/proscenium/ui/breadcrumbs/component.rb +79 -0
  45. data/lib/proscenium/ui/breadcrumbs/computed_element.rb +69 -0
  46. data/lib/proscenium/ui/breadcrumbs/control.rb +95 -0
  47. data/lib/proscenium/ui/breadcrumbs/mixins.css +83 -0
  48. data/lib/proscenium/ui/breadcrumbs.rb +72 -0
  49. data/lib/proscenium/ui/component.rb +11 -0
  50. data/lib/proscenium/ui/test.js +1 -0
  51. data/lib/proscenium/ui.rb +14 -0
  52. data/lib/proscenium/utils.rb +13 -0
  53. data/lib/proscenium/version.rb +5 -0
  54. data/lib/proscenium/view_component/css_modules.rb +11 -0
  55. data/lib/proscenium/view_component/react_component.rb +22 -0
  56. data/lib/proscenium/view_component/sideload.rb +4 -0
  57. data/lib/proscenium/view_component.rb +38 -0
  58. data/lib/proscenium.rb +70 -0
  59. metadata +228 -0
@@ -0,0 +1,189 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ffi'
4
+ require 'oj'
5
+
6
+ module Proscenium
7
+ class Builder
8
+ class CompileError < StandardError; end
9
+
10
+ class Result < FFI::Struct
11
+ layout :success, :bool,
12
+ :response, :string
13
+ end
14
+
15
+ module Request
16
+ extend FFI::Library
17
+ ffi_lib Pathname.new(__dir__).join('ext/proscenium').to_s
18
+
19
+ enum :environment, [:development, 1, :test, :production]
20
+
21
+ attach_function :build_to_string, [
22
+ :string, # Path or entry point.
23
+ :string, # Base URL of the Rails app. eg. https://example.com
24
+ :string, # Path to import map, relative to root
25
+ :string, # ENV variables as a JSON string
26
+
27
+ # Config
28
+ :string, # Rails application root
29
+ :string, # Proscenium gem root
30
+ :environment, # Rails environment as a Symbol
31
+ :bool, # Code splitting enabled?
32
+ :string, # Engine names and paths as a JSON string
33
+ :bool # Debugging enabled?
34
+ ], Result.by_value
35
+
36
+ attach_function :build_to_path, [
37
+ :string, # Path or entry point. Multiple can be given by separating with a semi-colon
38
+ :string, # Base URL of the Rails app. eg. https://example.com
39
+ :string, # Path to import map, relative to root
40
+ :string, # ENV variables as a JSON string
41
+
42
+ # Config
43
+ :string, # Rails application root
44
+ :string, # Proscenium gem root
45
+ :environment, # Rails environment as a Symbol
46
+ :bool, # Code splitting enabled?
47
+ :string, # Engine names and paths as a JSON string
48
+ :bool # Debugging enabled?
49
+ ], Result.by_value
50
+
51
+ attach_function :resolve, [
52
+ :string, # path or entry point
53
+ :string, # path to import map, relative to root
54
+
55
+ # Config
56
+ :string, # Rails application root
57
+ :string, # Proscenium gem root
58
+ :environment, # Rails environment as a Symbol
59
+ :bool # debugging enabled?
60
+ ], Result.by_value
61
+ end
62
+
63
+ class BuildError < StandardError
64
+ attr_reader :error
65
+
66
+ def initialize(error)
67
+ @error = Oj.load(error, mode: :strict).deep_transform_keys(&:underscore)
68
+
69
+ msg = @error['text']
70
+ if (location = @error['location'])
71
+ msg << " at #{location['file']}:#{location['line']}:#{location['column']}"
72
+ end
73
+
74
+ super(msg)
75
+ end
76
+ end
77
+
78
+ class ResolveError < StandardError
79
+ attr_reader :error_msg, :path
80
+
81
+ def initialize(path, error_msg)
82
+ super("Failed to resolve '#{path}' -- #{error_msg}")
83
+ end
84
+ end
85
+
86
+ def self.build_to_path(path, root: nil, base_url: nil)
87
+ new(root:, base_url:).build_to_path(path)
88
+ end
89
+
90
+ def self.build_to_string(path, root: nil, base_url: nil)
91
+ new(root:, base_url:).build_to_string(path)
92
+ end
93
+
94
+ def self.resolve(path, root: nil)
95
+ new(root:).resolve(path)
96
+ end
97
+
98
+ def initialize(root: nil, base_url: nil)
99
+ @root = root || Rails.root
100
+ @base_url = base_url
101
+ end
102
+
103
+ def build_to_path(path)
104
+ ActiveSupport::Notifications.instrument('build_to_path.proscenium',
105
+ identifier: path,
106
+ cached: Proscenium.cache.exist?(path)) do
107
+ Proscenium.cache.fetch path do
108
+ result = Request.build_to_path(path, @base_url, import_map, env_vars.to_json,
109
+ @root.to_s,
110
+ gem_root,
111
+ Rails.env.to_sym,
112
+ Proscenium.config.code_splitting,
113
+ engines.to_json,
114
+ Proscenium.config.debug)
115
+
116
+ raise BuildError, result[:response] unless result[:success]
117
+
118
+ result[:response]
119
+ end
120
+ end
121
+ end
122
+
123
+ def build_to_string(path)
124
+ ActiveSupport::Notifications.instrument('build_to_string.proscenium', identifier: path) do
125
+ result = Request.build_to_string(path, @base_url, import_map, env_vars.to_json,
126
+ @root.to_s,
127
+ gem_root,
128
+ Rails.env.to_sym,
129
+ Proscenium.config.code_splitting,
130
+ engines.to_json,
131
+ Proscenium.config.debug)
132
+
133
+ raise BuildError, result[:response] unless result[:success]
134
+
135
+ result[:response]
136
+ end
137
+ end
138
+
139
+ def resolve(path)
140
+ ActiveSupport::Notifications.instrument('resolve.proscenium', identifier: path) do
141
+ result = Request.resolve(path, import_map, @root.to_s,
142
+ gem_root,
143
+ Rails.env.to_sym,
144
+ Proscenium.config.debug)
145
+ raise ResolveError.new(path, result[:response]) unless result[:success]
146
+
147
+ result[:response]
148
+ end
149
+ end
150
+
151
+ private
152
+
153
+ # Build the ENV variables as determined by `Proscenium.config.env_vars` and
154
+ # `Proscenium::DEFAULT_ENV_VARS` to pass to esbuild.
155
+ def env_vars
156
+ ENV['NODE_ENV'] = ENV.fetch('RAILS_ENV', nil)
157
+ ENV.slice(*Proscenium.config.env_vars + Proscenium::DEFAULT_ENV_VARS)
158
+ end
159
+
160
+ def cache_query_string
161
+ q = Proscenium.config.cache_query_string
162
+ q ? "--cache-query-string #{q}" : nil
163
+ end
164
+
165
+ def engines
166
+ Proscenium.config.engines.to_h { |e| [e.engine_name, e.root.to_s] }.tap do |x|
167
+ x['proscenium/ui'] = Proscenium.ui_path.to_s
168
+ end
169
+ end
170
+
171
+ def import_map
172
+ return unless (path = Rails.root&.join('config'))
173
+
174
+ if (json = path.join('import_map.json')).exist?
175
+ return json.relative_path_from(@root).to_s
176
+ end
177
+
178
+ if (js = path.join('import_map.js')).exist?
179
+ return js.relative_path_from(@root).to_s
180
+ end
181
+
182
+ nil
183
+ end
184
+
185
+ def gem_root
186
+ Pathname.new(__dir__).join('..', '..').to_s
187
+ end
188
+ end
189
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Object
4
+ def instance_variable_get(name)
5
+ name.is_a?(::Proscenium::CssModule::Name) ? super(name.to_sym) : super
6
+ end
7
+
8
+ def instance_variable_set(name, obj)
9
+ name.is_a?(::Proscenium::CssModule::Name) ? super(name.to_sym, obj) : super
10
+ end
11
+
12
+ def instance_variable_defined?(name)
13
+ name.is_a?(::Proscenium::CssModule::Name) ? super(name.to_sym) : super
14
+ end
15
+
16
+ def remove_instance_variable(name)
17
+ name.is_a?(::Proscenium::CssModule::Name) ? super(name.to_sym) : super
18
+ end
19
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Proscenium
4
+ module CssModule::Path
5
+ # Returns the path to the CSS module file for this class, where the file is located alongside
6
+ # the class file, and has the same name as the class file, but with a `.module.css` extension.
7
+ #
8
+ # If the CSS module file does not exist, it's ancestry is checked, returning the first that
9
+ # exists. Then finally `nil` is returned if never found.
10
+ #
11
+ # @return [Pathname]
12
+ def css_module_path
13
+ return @css_module_path if instance_variable_defined?(:@css_module_path)
14
+
15
+ path = source_path.sub_ext('.module.css')
16
+ @css_module_path = path.exist? ? path : nil
17
+
18
+ unless @css_module_path
19
+ klass = superclass
20
+
21
+ while klass.respond_to?(:css_module_path) && !klass.abstract_class
22
+ break if (@css_module_path = klass.css_module_path)
23
+
24
+ klass = klass.superclass
25
+ end
26
+ end
27
+
28
+ @css_module_path
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ruby-next/language'
4
+ require 'proscenium/core_ext/object/css_module_ivars'
5
+
6
+ module Proscenium
7
+ module CssModule
8
+ class Rewriter < RubyNext::Language::Rewriters::Text
9
+ NAME = 'proscenium-css-module'
10
+
11
+ def rewrite(source)
12
+ source = source.gsub(/%i\[((@[\w@ ]+)|([\w@ ]+ @[\w@ ]+))\]/) do |_|
13
+ arr = ::Regexp.last_match(1).split.map do |x|
14
+ x.start_with?('@') ? css_module_string(x[1..]) : ":#{x}"
15
+ end
16
+ "[#{arr.join(',')}]"
17
+ end
18
+
19
+ source.gsub(/:@([\w]+)/) do |_|
20
+ context.track!(self)
21
+ css_module_string(::Regexp.last_match(1))
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def css_module_string(name)
28
+ if (path = Pathname.new(context.path).sub_ext('.module.css')).exist?
29
+ tname = Transformer.new(path).class_name!(name, name.dup).first
30
+ "Proscenium::CssModule::Name.new(:@#{name}, '#{tname}')"
31
+ else
32
+ "Proscenium::CssModule::Name.new(:@#{name}, css_module(:#{name}))"
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ RubyNext::Language.send :include_patterns=, []
40
+ RubyNext::Language.include_patterns << "#{Rails.root.join('app', 'components')}/*.rb"
41
+ RubyNext::Language.include_patterns << "#{Rails.root.join('app', 'views')}/*.rb"
42
+ RubyNext::Language.rewriters = [Proscenium::CssModule::Rewriter]
43
+
44
+ require 'ruby-next/language/runtime'
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Proscenium
4
+ class CssModule::Transformer
5
+ FILE_EXT = '.module.css'
6
+
7
+ def self.class_names(path, *names)
8
+ new(path).class_names(*names)
9
+ end
10
+
11
+ def initialize(source_path)
12
+ return unless (@source_path = source_path)
13
+
14
+ @source_path = Pathname.new(@source_path) unless @source_path.is_a?(Pathname)
15
+ @source_path = @source_path.sub_ext(FILE_EXT) unless @source_path.to_s.end_with?(FILE_EXT)
16
+ end
17
+
18
+ # Transform each of the given class `names` to their respective CSS module name, which consist
19
+ # of the name, and suffixed with the digest of the resolved source path.
20
+ #
21
+ # Any name beginning with '@' will be transformed to a CSS module name. If `require_prefix` is
22
+ # false, then all names will be transformed to a CSS module name regardless of whether or not
23
+ # they begin with '@'.
24
+ #
25
+ # class_names :@my_module_name, :my_class_name
26
+ #
27
+ # Note that the generated digest is based on the resolved (URL) path, not the original path.
28
+ #
29
+ # You can also provide a path specifier and class name. The path will be the URL path to a
30
+ # stylesheet. The class name will be the name of the class to transform.
31
+ #
32
+ # class_names "/lib/button@default"
33
+ # class_names "mypackage/button@large"
34
+ # class_names "@scoped/package/button@small"
35
+ #
36
+ # @param names [String,Symbol,Array<String,Symbol>]
37
+ # @param require_prefix: [Boolean] whether or not to require the `@` prefix.
38
+ # @return [Array<String>] the transformed CSS module names.
39
+ def class_names(*names, require_prefix: true)
40
+ names.map do |name|
41
+ original_name = name.dup
42
+ name = name.to_s if name.is_a?(Symbol)
43
+
44
+ if name.include?('/')
45
+ if name.start_with?('@')
46
+ # Scoped bare specifier (eg. "@scoped/package/lib/button@default").
47
+ _, path, name = name.split('@')
48
+ path = "@#{path}"
49
+ else
50
+ # Local path (eg. /some/path/to/button@default") or bare specifier (eg.
51
+ # "mypackage/lib/button@default").
52
+ path, name = name.split('@')
53
+ end
54
+
55
+ class_name! name, original_name, path: "#{path}#{FILE_EXT}"
56
+ elsif name.start_with?('@')
57
+ class_name! name[1..], original_name
58
+ else
59
+ require_prefix ? name : class_name!(name, original_name)
60
+ end
61
+ end
62
+ end
63
+
64
+ def class_name!(name, original_name, path: @source_path)
65
+ unless path
66
+ raise Proscenium::CssModule::TransformError.new(original_name, 'CSS module path not given')
67
+ end
68
+
69
+ resolved_path = Resolver.resolve(path.to_s)
70
+ digest = Importer.import(resolved_path)
71
+
72
+ transformed_path = ''
73
+ transformed_path = "__#{resolved_path[1..].gsub(%r{[/\.]}, '-')}" if Rails.env.development?
74
+ transformed_name = name.to_s
75
+ transformed_name = if transformed_name.start_with?('_')
76
+ "_#{transformed_name[1..]}-#{digest}#{transformed_path}"
77
+ else
78
+ "#{transformed_name}-#{digest}#{transformed_path}"
79
+ end
80
+
81
+ [transformed_name, resolved_path]
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Proscenium::CssModule
4
+ extend ActiveSupport::Autoload
5
+
6
+ autoload :Path
7
+ autoload :Transformer
8
+
9
+ class TransformError < StandardError
10
+ def initialize(name, additional_msg = nil)
11
+ msg = "Failed to transform CSS module `#{name}`"
12
+ msg << ' - ' << additional_msg if additional_msg
13
+
14
+ super(msg)
15
+ end
16
+ end
17
+
18
+ class Name
19
+ def initialize(name, transform)
20
+ @name = name
21
+ @transform = transform
22
+ end
23
+
24
+ def to_s
25
+ @transform
26
+ end
27
+
28
+ def to_sym
29
+ @name
30
+ end
31
+ end
32
+
33
+ # Accepts one or more CSS class names, and transforms them into CSS module names.
34
+ #
35
+ # @param name [String,Symbol,Array<String,Symbol>]
36
+ # @param path [Pathname] the path to the CSS module file to use for the transformation.
37
+ # @return [String] the transformed CSS module names concatenated as a string.
38
+ def css_module(*names, path: nil)
39
+ transformer = path.nil? ? cssm : Transformer.new(path)
40
+ transformer.class_names(*names, require_prefix: false).map { |name, _| name }.join(' ')
41
+ end
42
+
43
+ # @param name [String,Symbol,Array<String,Symbol>]
44
+ # @param path [Pathname] the path to the CSS file to use for the transformation.
45
+ # @return [String] the transformed CSS module names concatenated as a string.
46
+ def class_names(*names, path: nil)
47
+ names = names.flatten.compact
48
+ transformer = path.nil? ? cssm : Transformer.new(path)
49
+ transformer.class_names(*names).map { |name, _| name }.join(' ') unless names.empty?
50
+ end
51
+
52
+ private
53
+
54
+ def cssm
55
+ @cssm ||= Transformer.new(self.class.css_module_path)
56
+ end
57
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Proscenium
4
+ NotIncludedError = Class.new(StandardError)
5
+
6
+ module EnsureLoaded
7
+ def self.included(child)
8
+ child.class_eval do
9
+ append_after_action do
10
+ if request.format.html? && Importer.imported?
11
+ if Importer.js_imported?
12
+ raise NotIncludedError, 'There are side loaded javascripts to be included, but ' \
13
+ 'they have not been included in the page. Did you forget ' \
14
+ 'to add the `#include_assets` helper in your views?'
15
+ end
16
+
17
+ if Importer.css_imported?
18
+ raise NotIncludedError, 'There are side loaded stylesheets to be included, but ' \
19
+ 'they have not been included in the page. Did you forget ' \
20
+ 'to add the `#include_assets` helper in your views?'
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
Binary file
@@ -0,0 +1,131 @@
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
+ // BuildOptions
89
+ // - path - The path to build relative to `root`.
90
+ // - baseUrl - base URL of the Rails app. eg. https://example.com
91
+ // - importMap - Path to the import map relative to `root`.
92
+ // - envVars - JSON string of environment variables.
93
+ // Config:
94
+ // - root - The working directory.
95
+ // - env - The environment (1 = development, 2 = test, 3 = production)
96
+ // - codeSpitting?
97
+ // - debug?
98
+ //
99
+ extern struct Result build_to_string(char* filepath, char* baseUrl, char* importMap, char* envVars, char* appRoot, char* gemPath, unsigned int env, GoUint8 codeSplitting, char* engines, GoUint8 debug);
100
+
101
+ // Build the given `path` in the `root`.
102
+ //
103
+ // BuildOptions
104
+ // - path - The path to build relative to `root`. Multiple paths can be given by separating them
105
+ // with a semi-colon.
106
+ // - baseUrl - base URL of the Rails app. eg. https://example.com
107
+ // - importMap - Path to the import map relative to `root`.
108
+ // - envVars - JSON string of environment variables.
109
+ // Config:
110
+ // - root - The working directory.
111
+ // - env - The environment (1 = development, 2 = test, 3 = production)
112
+ // - codeSpitting?
113
+ // - debug?
114
+ //
115
+ extern struct Result build_to_path(char* filepath, char* baseUrl, char* importMap, char* envVars, char* appRoot, char* gemPath, unsigned int env, GoUint8 codeSplitting, char* engines, GoUint8 debug);
116
+
117
+ // Resolve the given `path` relative to the `root`.
118
+ //
119
+ // ResolveOptions
120
+ // - path - The path to build relative to `root`.
121
+ // - importMap - Path to the import map relative to `root`.
122
+ // Config
123
+ // - root - The working directory.
124
+ // - env - The environment (1 = development, 2 = test, 3 = production)
125
+ // - debug?
126
+ //
127
+ extern struct Result resolve(char* path, char* importMap, char* appRoot, char* gemPath, unsigned int env, GoUint8 debug);
128
+
129
+ #ifdef __cplusplus
130
+ }
131
+ #endif
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Proscenium
4
+ module Helper
5
+ def sideload_assets(value)
6
+ if value.nil?
7
+ @current_template.instance_variable_defined?(:@sideload_assets_options) &&
8
+ @current_template.remove_instance_variable(:@sideload_assets_options)
9
+ else
10
+ @current_template.instance_variable_set :@sideload_assets_options, value
11
+ end
12
+ end
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
+ def compute_asset_path(path, options = {})
19
+ if %i[javascript stylesheet].include?(options[:type])
20
+ result = Proscenium::Builder.build_to_path(path, base_url: request.base_url)
21
+ return result.split('::').last.delete_prefix 'public'
22
+ end
23
+
24
+ super
25
+ end
26
+
27
+ # Accepts one or more CSS class names, and transforms them into CSS module names.
28
+ #
29
+ # @see CssModule::Transformer#class_names
30
+ # @param name [String,Symbol,Array<String,Symbol>]
31
+ # @param path [Pathname] the path to the CSS module file to use for the transformation.
32
+ # @return [String] the transformed CSS module names concatenated as a string.
33
+ def css_module(*names, path: nil)
34
+ path ||= Pathname.new(@lookup_context.find(@virtual_path).identifier).sub_ext('')
35
+ CssModule::Transformer.new(path).class_names(*names, require_prefix: false)
36
+ .map { |name, _| name }.join(' ')
37
+ end
38
+
39
+ # @param name [String,Symbol,Array<String,Symbol>]
40
+ # @param path [Pathname] the path to the CSS file to use for the transformation.
41
+ # @return [String] the transformed CSS module names concatenated as a string.
42
+ def class_names(*names, path: nil)
43
+ names = names.flatten.compact
44
+
45
+ return if names.empty?
46
+
47
+ path ||= Pathname.new(@lookup_context.find(@virtual_path).identifier).sub_ext('')
48
+ CssModule::Transformer.new(path).class_names(*names).map { |name, _| name }.join(' ')
49
+ end
50
+
51
+ def include_assets
52
+ include_stylesheets + include_javascripts
53
+ end
54
+
55
+ def include_stylesheets
56
+ '<!-- [PROSCENIUM_STYLESHEETS] -->'.html_safe
57
+ end
58
+ alias side_load_stylesheets include_stylesheets
59
+ deprecate side_load_stylesheets: 'Use `include_stylesheets` instead', deprecator: Deprecator.new
60
+
61
+ # Includes all javascripts that have been imported and side loaded.
62
+ #
63
+ # @return [String] the HTML tags for the javascripts.
64
+ def include_javascripts
65
+ '<!-- [PROSCENIUM_LAZY_SCRIPTS] --><!-- [PROSCENIUM_JAVASCRIPTS] -->'.html_safe
66
+ end
67
+ alias side_load_javascripts include_javascripts
68
+ deprecate side_load_javascripts: 'Use `include_javascripts` instead', deprecator: Deprecator.new
69
+ end
70
+ end