proscenium 0.1.0.alpha2-x86_64-linux → 0.1.0.alpha4-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 +4 -4
- data/README.md +216 -32
- data/app/components/react_component.rb +10 -3
- data/bin/esbuild +0 -0
- data/bin/lightningcss +0 -0
- data/config/routes.rb +1 -1
- data/lib/proscenium/compilers/esbuild/compile_error.js +147 -0
- data/lib/proscenium/compilers/esbuild/css/postcss.js +67 -0
- data/lib/proscenium/compilers/esbuild/css_plugin.js +176 -0
- data/lib/proscenium/compilers/esbuild/env_plugin.js +6 -4
- data/lib/proscenium/compilers/esbuild/http_bundle_plugin.js +53 -0
- data/lib/proscenium/compilers/esbuild/import_map.js +59 -0
- data/lib/proscenium/compilers/esbuild/resolve_plugin.js +67 -65
- data/lib/proscenium/compilers/esbuild/setup_plugin.js +32 -22
- data/lib/proscenium/{cli → compilers}/esbuild/solidjs_plugin.js +5 -4
- data/lib/proscenium/compilers/esbuild.bench.js +7 -3
- data/lib/proscenium/compilers/esbuild.js +93 -19
- data/lib/proscenium/css_module.rb +48 -6
- data/lib/proscenium/helper.rb +5 -3
- data/lib/proscenium/middleware/base.rb +13 -3
- data/lib/proscenium/middleware/esbuild.rb +17 -1
- data/lib/proscenium/middleware/lightningcss.rb +64 -0
- data/lib/proscenium/middleware.rb +7 -19
- data/lib/proscenium/phlex.rb +36 -0
- data/lib/proscenium/railtie.rb +10 -20
- data/lib/proscenium/runtime/auto_reload.js +3 -3
- data/lib/proscenium/side_load.rb +14 -40
- data/lib/proscenium/utils.js +8 -0
- data/lib/proscenium/version.rb +1 -1
- data/lib/proscenium/view_component.rb +5 -1
- data/lib/proscenium.rb +1 -0
- metadata +27 -19
- data/bin/parcel_css +0 -0
- data/lib/proscenium/cli/argument_error.js +0 -24
- data/lib/proscenium/cli/builders/index.js +0 -1
- data/lib/proscenium/cli/builders/javascript.js +0 -45
- data/lib/proscenium/cli/builders/react.js +0 -60
- data/lib/proscenium/cli/builders/solid.js +0 -46
- data/lib/proscenium/cli/esbuild/env_plugin.js +0 -21
- data/lib/proscenium/cli/esbuild/resolve_plugin.js +0 -136
- data/lib/proscenium/cli/js_builder.js +0 -194
- data/lib/proscenium/cli/solid.js +0 -15
- data/lib/proscenium/cli/utils.js +0 -93
- data/lib/proscenium/middleware/parcel_css.rb +0 -37
- data/lib/proscenium/runtime/component_manager/index.js +0 -27
- data/lib/proscenium/runtime/component_manager/render_component.js +0 -40
- data/lib/proscenium/runtime/import_css.js +0 -46
@@ -4,24 +4,66 @@ import { expandGlob } from 'std/fs/mod.ts'
|
|
4
4
|
import { join, isGlob, resolve, dirname, fromFileUrl } from 'std/path/mod.ts'
|
5
5
|
import { build, stop } from 'esbuild'
|
6
6
|
|
7
|
+
import { readImportMap } from './esbuild/import_map.js'
|
7
8
|
import envPlugin from './esbuild/env_plugin.js'
|
9
|
+
import cssPlugin from './esbuild/css_plugin.js'
|
8
10
|
import resolvePlugin from './esbuild/resolve_plugin.js'
|
9
11
|
import ArgumentError from './esbuild/argument_error.js'
|
12
|
+
import throwCompileError from './esbuild/compile_error.js'
|
10
13
|
|
14
|
+
/**
|
15
|
+
* Compile the given paths, outputting the result to stdout. This is designed to be called as a CLI:
|
16
|
+
*
|
17
|
+
* Example with Deno run (dev and test):
|
18
|
+
* deno run -A lib/proscenium/compilers/esbuild.js --root ./test/internal lib/foo.js
|
19
|
+
* Example with Deno compiled binary:
|
20
|
+
* bin/esbuild lib/proscenium/compilers/esbuild.js --root ./test/internal lib/foo.js
|
21
|
+
*
|
22
|
+
* USAGE:
|
23
|
+
* esbuild [OPTIONS] <PATHS_ARG>...
|
24
|
+
*
|
25
|
+
* ARGS:
|
26
|
+
* <PATHS_ARG>... One or more file paths to compile.
|
27
|
+
*
|
28
|
+
* OPTIONS:
|
29
|
+
* --root
|
30
|
+
* Relative or absolute path to the root or current working directory when compilation will
|
31
|
+
* take place.
|
32
|
+
* --import-map
|
33
|
+
* Path to an import map, relative to the <root>.
|
34
|
+
* --write
|
35
|
+
* Write output to the filesystem according to esbuild logic.
|
36
|
+
* --debug
|
37
|
+
* Debug output,
|
38
|
+
*/
|
11
39
|
if (import.meta.main) {
|
40
|
+
!Deno.env.get('RAILS_ENV') && Deno.env.set('RAILS_ENV', 'development')
|
41
|
+
|
12
42
|
const { _: paths, ...options } = parseArgs(Deno.args, {
|
13
|
-
string: ['root', '
|
14
|
-
boolean: ['write'],
|
15
|
-
alias: {
|
43
|
+
string: ['root', 'import-map'],
|
44
|
+
boolean: ['write', 'debug'],
|
45
|
+
alias: {
|
46
|
+
'import-map': 'importMap'
|
47
|
+
}
|
16
48
|
})
|
17
|
-
|
49
|
+
|
50
|
+
let result = await main(paths, options)
|
51
|
+
|
52
|
+
// `result` is an error object, so return to stderr as JSON, and an exit code of 1.
|
53
|
+
if (isPlainObject(result)) {
|
54
|
+
result = new TextEncoder().encode(`(${throwCompileError()})(${JSON.stringify(result)})`)
|
55
|
+
}
|
56
|
+
|
57
|
+
await writeAll(Deno.stdout, result)
|
18
58
|
}
|
19
59
|
|
20
60
|
async function main(paths = [], options = {}) {
|
21
|
-
const {
|
61
|
+
const { write, debug } = { write: false, ...options }
|
22
62
|
|
23
63
|
if (!Array.isArray(paths) || paths.length < 1) throw new ArgumentError('pathsRequired')
|
24
|
-
if (!root) throw new ArgumentError('rootRequired')
|
64
|
+
if (!options.root) throw new ArgumentError('rootRequired')
|
65
|
+
|
66
|
+
const root = resolve(options.root)
|
25
67
|
|
26
68
|
// Make sure that `root` is a valid directory.
|
27
69
|
try {
|
@@ -31,7 +73,9 @@ async function main(paths = [], options = {}) {
|
|
31
73
|
throw new ArgumentError('rootUnknown', { root })
|
32
74
|
}
|
33
75
|
|
34
|
-
const
|
76
|
+
const env = Deno.env.get('RAILS_ENV')
|
77
|
+
const isProd = env === 'production'
|
78
|
+
const isTest = env === 'test'
|
35
79
|
|
36
80
|
const entryPoints = new Set()
|
37
81
|
for (let i = 0; i < paths.length; i++) {
|
@@ -46,37 +90,67 @@ async function main(paths = [], options = {}) {
|
|
46
90
|
}
|
47
91
|
}
|
48
92
|
|
93
|
+
let importMap
|
94
|
+
try {
|
95
|
+
importMap = readImportMap(options.importMap, root)
|
96
|
+
} catch (error) {
|
97
|
+
return {
|
98
|
+
detail: error.stack,
|
99
|
+
text: `Cannot read/parse import map: ${error.message}`,
|
100
|
+
location: {
|
101
|
+
file: error.file
|
102
|
+
}
|
103
|
+
}
|
104
|
+
}
|
105
|
+
|
49
106
|
const runtimeDir = resolve(dirname(fromFileUrl(import.meta.url)), '../runtime')
|
50
107
|
|
51
108
|
const params = {
|
52
109
|
entryPoints: Array.from(entryPoints),
|
53
110
|
absWorkingDir: root,
|
54
|
-
logLevel: '
|
55
|
-
|
111
|
+
logLevel: 'silent',
|
112
|
+
logLimit: 1,
|
56
113
|
outdir: 'public/assets',
|
57
114
|
outbase: './',
|
58
115
|
format: 'esm',
|
59
|
-
|
60
|
-
|
116
|
+
jsx: 'automatic',
|
117
|
+
jsxDev: !isTest && !isProd,
|
61
118
|
minify: isProd,
|
62
119
|
bundle: true,
|
63
|
-
plugins: [envPlugin(), resolvePlugin({ runtimeDir, debug
|
64
|
-
inject: [join(runtimeDir, 'react_shim/index.js')],
|
120
|
+
plugins: [envPlugin(), resolvePlugin({ runtimeDir, importMap, debug }), cssPlugin({ debug })],
|
65
121
|
metafile: write,
|
66
122
|
write
|
67
123
|
}
|
68
124
|
|
69
|
-
|
70
|
-
|
125
|
+
if (!debug) {
|
126
|
+
params.sourcemap = isTest ? false : isProd ? 'linked' : 'inline'
|
127
|
+
}
|
71
128
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
129
|
+
let result
|
130
|
+
try {
|
131
|
+
result = await build(params)
|
132
|
+
} catch (error) {
|
133
|
+
if (debug) {
|
134
|
+
throw error
|
76
135
|
}
|
136
|
+
|
137
|
+
return { ...error.errors[0] }
|
77
138
|
} finally {
|
78
139
|
stop()
|
79
140
|
}
|
141
|
+
|
142
|
+
if (write) {
|
143
|
+
return new TextEncoder().encode(JSON.stringify(result))
|
144
|
+
} else {
|
145
|
+
return result.outputFiles[0].contents
|
146
|
+
}
|
147
|
+
}
|
148
|
+
|
149
|
+
export function isPlainObject(value) {
|
150
|
+
if (Object.prototype.toString.call(value) !== '[object Object]') return false
|
151
|
+
|
152
|
+
const prototype = Object.getPrototypeOf(value)
|
153
|
+
return prototype === null || prototype === Object.prototype
|
80
154
|
}
|
81
155
|
|
82
156
|
export default main
|
@@ -1,22 +1,64 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Proscenium::CssModule
|
4
|
+
class NotFound < StandardError
|
5
|
+
def initialize(pathname)
|
6
|
+
@pathname = pathname
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
def message
|
11
|
+
"Stylesheet is required, but does not exist: #{@pathname}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
4
15
|
def initialize(path)
|
5
|
-
@path =
|
16
|
+
@path = path
|
17
|
+
@css_module_path = "#{path}.module.css"
|
18
|
+
end
|
6
19
|
|
7
|
-
|
20
|
+
# Parses the given `content` for CSS modules names ('class' attributes beginning with '@'), and
|
21
|
+
# returns the content with said CSS Modules replaced with the compiled class names.
|
22
|
+
#
|
23
|
+
# Example:
|
24
|
+
# <div class="@my_css_module_name"></div>
|
25
|
+
def compile_class_names(content)
|
26
|
+
doc = Nokogiri::HTML::DocumentFragment.parse(content)
|
27
|
+
|
28
|
+
return content if (modules = doc.css('[class*="@"]')).empty?
|
29
|
+
|
30
|
+
modules.each do |ele|
|
31
|
+
classes = ele.classes.map { |cls| cls.starts_with?('@') ? class_names!(cls[1..]) : cls }
|
32
|
+
ele['class'] = classes.join(' ')
|
33
|
+
end
|
8
34
|
|
9
|
-
|
35
|
+
doc.to_html.html_safe
|
10
36
|
end
|
11
37
|
|
12
|
-
#
|
38
|
+
# @returns [Array] of class names generated from the given CSS module `names`.
|
13
39
|
def class_names(*names)
|
14
|
-
|
40
|
+
side_load_css_module
|
41
|
+
names.flatten.compact.map { |name| "#{name.to_s.camelize(:lower)}#{hash}" }
|
42
|
+
end
|
43
|
+
|
44
|
+
# Like #class_names, but requires that the stylesheet exists.
|
45
|
+
#
|
46
|
+
# @raises Proscenium::CssModule::NotFound if stylesheet does not exists.
|
47
|
+
def class_names!(...)
|
48
|
+
raise NotFound, @css_module_path unless Rails.root.join(@css_module_path).exist?
|
49
|
+
|
50
|
+
class_names(...)
|
15
51
|
end
|
16
52
|
|
17
53
|
private
|
18
54
|
|
19
55
|
def hash
|
20
|
-
@hash ||= Digest::SHA1.hexdigest("/#{@
|
56
|
+
@hash ||= Digest::SHA1.hexdigest("/#{@css_module_path}")[..7]
|
57
|
+
end
|
58
|
+
|
59
|
+
def side_load_css_module
|
60
|
+
return unless Rails.application.config.proscenium.side_load
|
61
|
+
|
62
|
+
Proscenium::SideLoad.append "#{@path}.module", :css
|
21
63
|
end
|
22
64
|
end
|
data/lib/proscenium/helper.rb
CHANGED
@@ -11,7 +11,9 @@ module Proscenium
|
|
11
11
|
def side_load_stylesheets
|
12
12
|
return unless Proscenium::Current.loaded
|
13
13
|
|
14
|
-
|
14
|
+
Proscenium::Current.loaded[:css].map do |sheet|
|
15
|
+
stylesheet_link_tag(sheet, id: "_#{Digest::SHA1.hexdigest("/#{sheet}")[..7]}")
|
16
|
+
end.join("\n").html_safe
|
15
17
|
end
|
16
18
|
|
17
19
|
def side_load_javascripts(**options)
|
@@ -21,12 +23,12 @@ module Proscenium
|
|
21
23
|
end
|
22
24
|
|
23
25
|
def proscenium_dev
|
24
|
-
return
|
26
|
+
return unless Proscenium.config.auto_reload
|
25
27
|
|
26
28
|
javascript_tag %(
|
27
29
|
import autoReload from '/proscenium-runtime/auto_reload.js';
|
28
30
|
autoReload('#{Proscenium::Railtie.websocket_mount_path}');
|
29
|
-
), type: 'module'
|
31
|
+
), type: 'module', defer: true
|
30
32
|
end
|
31
33
|
end
|
32
34
|
end
|
@@ -7,7 +7,9 @@ module Proscenium
|
|
7
7
|
class Base
|
8
8
|
include ActiveSupport::Benchmarkable
|
9
9
|
|
10
|
-
|
10
|
+
# Error when the result of the build returns an error. For example, when esbuild returns
|
11
|
+
# errors.
|
12
|
+
class CompileError < StandardError; end
|
11
13
|
|
12
14
|
def self.attempt(request)
|
13
15
|
new(request).renderable!&.attempt
|
@@ -57,15 +59,23 @@ module Proscenium
|
|
57
59
|
response.write content
|
58
60
|
response.content_type = content_type
|
59
61
|
response['X-Proscenium-Middleware'] = name
|
62
|
+
|
63
|
+
yield response if block_given?
|
64
|
+
|
60
65
|
response.finish
|
61
66
|
end
|
62
67
|
|
63
68
|
def build(cmd)
|
64
69
|
stdout, stderr, status = Open3.capture3(cmd)
|
65
70
|
|
66
|
-
|
71
|
+
unless status.success?
|
72
|
+
raise self.class::CompileError, stderr if status.exitstatus == 2
|
73
|
+
|
74
|
+
raise BuildError, stderr
|
75
|
+
end
|
76
|
+
|
67
77
|
unless stderr.empty?
|
68
|
-
raise "Proscenium build of #{name}:'#{@request.fullpath}' failed -- #{stderr}"
|
78
|
+
raise BuildError, "Proscenium build of #{name}:'#{@request.fullpath}' failed -- #{stderr}"
|
69
79
|
end
|
70
80
|
|
71
81
|
stdout
|
@@ -3,10 +3,24 @@
|
|
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]}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
6
16
|
def attempt
|
7
17
|
benchmark :esbuild do
|
8
18
|
render_response build("#{cli} --root #{root} #{path}")
|
9
19
|
end
|
20
|
+
rescue CompileError => e
|
21
|
+
render_response "export default #{e.detail.to_json}" do |response|
|
22
|
+
response['X-Proscenium-Middleware'] = 'Esbuild::CompileError'
|
23
|
+
end
|
10
24
|
end
|
11
25
|
|
12
26
|
private
|
@@ -17,7 +31,9 @@ module Proscenium
|
|
17
31
|
|
18
32
|
def cli
|
19
33
|
if ENV['PROSCENIUM_TEST']
|
20
|
-
|
34
|
+
[
|
35
|
+
'deno run -q --import-map import_map.json -A', 'lib/proscenium/compilers/esbuild.js'
|
36
|
+
].join(' ')
|
21
37
|
else
|
22
38
|
Gem.bin_path 'proscenium', 'esbuild'
|
23
39
|
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'oj'
|
4
|
+
|
5
|
+
module Proscenium
|
6
|
+
class Middleware
|
7
|
+
class Lightningcss < Base
|
8
|
+
def attempt
|
9
|
+
benchmark :lightningcss do
|
10
|
+
with_custom_media { |path| build path }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def with_custom_media
|
17
|
+
if custom_media?
|
18
|
+
Tempfile.create do |f|
|
19
|
+
contents = Pathname.new("#{root}#{@request.path}").read
|
20
|
+
f.write contents, "\n", custom_media_path.read
|
21
|
+
f.rewind
|
22
|
+
|
23
|
+
yield f.path
|
24
|
+
end
|
25
|
+
else
|
26
|
+
yield "#{root}#{@request.path}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def build(path)
|
31
|
+
results = super("#{cli} #{cli_options.join ' '} #{path}")
|
32
|
+
render_response css_module? ? Oj.load(results, mode: :strict)['code'] : results
|
33
|
+
end
|
34
|
+
|
35
|
+
def custom_media?
|
36
|
+
@custom_media ||= custom_media_path.exist?
|
37
|
+
end
|
38
|
+
|
39
|
+
def custom_media_path
|
40
|
+
@custom_media_path ||= Rails.root.join('lib', 'custom_media_queries.css')
|
41
|
+
end
|
42
|
+
|
43
|
+
def cli
|
44
|
+
Gem.bin_path 'proscenium', 'lightningcss'
|
45
|
+
end
|
46
|
+
|
47
|
+
def cli_options
|
48
|
+
options = ['--nesting', '--targets', "'>= 0.25%'"]
|
49
|
+
options << '--custom-media' if custom_media?
|
50
|
+
|
51
|
+
if css_module?
|
52
|
+
hash = Digest::SHA1.hexdigest(@request.path)[..7]
|
53
|
+
options += ['--css-modules', '--css-modules-pattern', "'[local]#{hash}'"]
|
54
|
+
end
|
55
|
+
|
56
|
+
Rails.env.production? ? options << '-m' : options
|
57
|
+
end
|
58
|
+
|
59
|
+
def css_module?
|
60
|
+
@css_module ||= /\.module\.css$/i.match?(@request.path_info)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -4,17 +4,13 @@ module Proscenium
|
|
4
4
|
class Middleware
|
5
5
|
extend ActiveSupport::Autoload
|
6
6
|
|
7
|
+
# Error when the build command fails.
|
8
|
+
class BuildError < StandardError; end
|
9
|
+
|
7
10
|
autoload :Base
|
8
11
|
autoload :Esbuild
|
9
|
-
autoload :ParcelCss
|
10
12
|
autoload :Runtime
|
11
13
|
|
12
|
-
MIDDLEWARE_CLASSES = {
|
13
|
-
esbuild: Esbuild,
|
14
|
-
parcelcss: ParcelCss,
|
15
|
-
runtime: Runtime
|
16
|
-
}.freeze
|
17
|
-
|
18
14
|
def initialize(app)
|
19
15
|
@app = app
|
20
16
|
end
|
@@ -35,23 +31,15 @@ module Proscenium
|
|
35
31
|
def attempt(request)
|
36
32
|
return unless (type = find_type(request))
|
37
33
|
|
38
|
-
file_handler.attempt(request.env) ||
|
34
|
+
file_handler.attempt(request.env) || type.attempt(request)
|
39
35
|
end
|
40
36
|
|
41
37
|
# Returns the type of file being requested using Rails.application.config.proscenium.glob_types.
|
42
38
|
def find_type(request)
|
43
|
-
|
44
|
-
|
45
|
-
path = Rails.root.join(request.path[1..])
|
46
|
-
|
47
|
-
type, = glob_types.find do |_, globs|
|
48
|
-
# TODO: Look for the precompiled file in public/assets first
|
49
|
-
# globs.any? { |glob| Rails.public_path.join('assets').glob(glob).any?(path) }
|
50
|
-
|
51
|
-
globs.any? { |glob| Rails.root.glob(glob).any?(path) }
|
52
|
-
end
|
39
|
+
path = Pathname.new(request.path)
|
53
40
|
|
54
|
-
|
41
|
+
return Runtime if path.fnmatch?(glob_types[:runtime], File::FNM_EXTGLOB)
|
42
|
+
return Esbuild if path.fnmatch?(glob_types[:esbuild], File::FNM_EXTGLOB)
|
55
43
|
end
|
56
44
|
|
57
45
|
def file_handler
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Proscenium
|
4
|
+
class Phlex < ::Phlex::View
|
5
|
+
module Sideload
|
6
|
+
def template(...)
|
7
|
+
Proscenium::SideLoad.append self.class.path if Rails.application.config.proscenium.side_load
|
8
|
+
|
9
|
+
super
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class << self
|
14
|
+
attr_accessor :path
|
15
|
+
|
16
|
+
def inherited(child)
|
17
|
+
path = caller_locations(1, 1)[0].path
|
18
|
+
child.path = path.delete_prefix(::Rails.root.to_s).delete_suffix('.rb')[1..]
|
19
|
+
|
20
|
+
child.prepend Sideload
|
21
|
+
|
22
|
+
super
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def css_module(name)
|
27
|
+
cssm.class_names!(name).join ' '
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def cssm
|
33
|
+
@cssm ||= Proscenium::CssModule.new(self.class.path)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/proscenium/railtie.rb
CHANGED
@@ -12,16 +12,8 @@ module Proscenium
|
|
12
12
|
#
|
13
13
|
# See https://doc.deno.land/https://deno.land/std@0.145.0/path/mod.ts/~/globToRegExp
|
14
14
|
DEFAULT_GLOB_TYPES = {
|
15
|
-
esbuild:
|
16
|
-
|
17
|
-
'app/components/**/*.{js,jsx}',
|
18
|
-
'app/views/**/*.{js,jsx}'
|
19
|
-
],
|
20
|
-
parcelcss: [
|
21
|
-
'lib/**/*.css',
|
22
|
-
'app/components/**/*.css',
|
23
|
-
'app/views/**/*.css'
|
24
|
-
]
|
15
|
+
esbuild: '/{config,app,lib,node_modules}/**.{js,mjs,jsx,css}',
|
16
|
+
runtime: '/proscenium-runtime/**.{js,jsx}'
|
25
17
|
}.freeze
|
26
18
|
|
27
19
|
class << self
|
@@ -34,18 +26,16 @@ module Proscenium
|
|
34
26
|
isolate_namespace Proscenium
|
35
27
|
|
36
28
|
config.proscenium = ActiveSupport::OrderedOptions.new
|
37
|
-
config.proscenium.listen_paths ||= %w[lib app]
|
38
|
-
config.proscenium.listen_extensions ||= /\.(css|jsx?)$/
|
39
29
|
config.proscenium.side_load = true
|
30
|
+
config.proscenium.auto_reload = Rails.env.development?
|
31
|
+
config.proscenium.auto_reload_paths ||= %w[lib app config]
|
32
|
+
config.proscenium.auto_reload_extensions ||= /\.(css|jsx?)$/
|
40
33
|
|
41
34
|
initializer 'proscenium.configuration' do |app|
|
42
35
|
options = app.config.proscenium
|
43
36
|
|
44
37
|
options.glob_types = DEFAULT_GLOB_TYPES if options.glob_types.blank?
|
45
|
-
|
46
|
-
options.auto_refresh = true if options.auto_refresh.nil?
|
47
|
-
options.listen = Rails.env.development? if options.listen.nil?
|
48
|
-
options.listen_paths.filter! { |path| Dir.exist? path }
|
38
|
+
options.auto_reload_paths.filter! { |path| Dir.exist? path }
|
49
39
|
options.cable_mount_path ||= '/proscenium-cable'
|
50
40
|
options.cable_logger ||= Rails.logger
|
51
41
|
end
|
@@ -73,10 +63,10 @@ module Proscenium
|
|
73
63
|
end
|
74
64
|
|
75
65
|
config.after_initialize do
|
76
|
-
next unless config.proscenium.
|
66
|
+
next unless config.proscenium.auto_reload
|
77
67
|
|
78
|
-
@listener = Listen.to(*config.proscenium.
|
79
|
-
only: config.proscenium.
|
68
|
+
@listener = Listen.to(*config.proscenium.auto_reload_paths,
|
69
|
+
only: config.proscenium.auto_reload_extensions) do |mod, add, rem|
|
80
70
|
Proscenium::Railtie.websocket&.broadcast('reload', {
|
81
71
|
modified: mod,
|
82
72
|
removed: rem,
|
@@ -94,7 +84,7 @@ module Proscenium
|
|
94
84
|
class << self
|
95
85
|
def websocket
|
96
86
|
return @websocket unless @websocket.nil?
|
97
|
-
return unless config.proscenium.
|
87
|
+
return unless config.proscenium.auto_reload
|
98
88
|
|
99
89
|
cable = ActionCable::Server::Configuration.new
|
100
90
|
cable.cable = { adapter: 'async' }.with_indifferent_access
|
@@ -7,16 +7,16 @@ export default socketPath => {
|
|
7
7
|
|
8
8
|
consumer.subscriptions.create('Proscenium::ReloadChannel', {
|
9
9
|
received: debounce(() => {
|
10
|
-
console.log('Proscenium
|
10
|
+
console.log('[Proscenium] Files changed; reloading...')
|
11
11
|
location.reload()
|
12
12
|
}, 200),
|
13
13
|
|
14
14
|
connected() {
|
15
|
-
console.log('Proscenium
|
15
|
+
console.log('[Proscenium] Auto-reload websocket connected')
|
16
16
|
},
|
17
17
|
|
18
18
|
disconnected() {
|
19
|
-
console.log('Proscenium
|
19
|
+
console.log('[Proscenium] Auto-reload websocket disconnected')
|
20
20
|
}
|
21
21
|
})
|
22
22
|
}
|
data/lib/proscenium/side_load.rb
CHANGED
@@ -5,17 +5,6 @@ module Proscenium
|
|
5
5
|
DEFAULT_EXTENSIONS = %i[js css].freeze
|
6
6
|
EXTENSIONS = %i[js css].freeze
|
7
7
|
|
8
|
-
class NotFound < StandardError
|
9
|
-
def initialize(pathname)
|
10
|
-
@pathname = pathname
|
11
|
-
super
|
12
|
-
end
|
13
|
-
|
14
|
-
def message
|
15
|
-
"#{@pathname} does not exist"
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
8
|
module_function
|
20
9
|
|
21
10
|
# Side load the given asset `path`, by appending it to `Proscenium::Current.loaded`, which is a
|
@@ -46,40 +35,25 @@ module Proscenium
|
|
46
35
|
end
|
47
36
|
end
|
48
37
|
|
49
|
-
# Like #append, but only accepts a single `path` argument, which must be a Pathname. Raises
|
50
|
-
# `NotFound` if path does not exist,
|
51
|
-
def append!(pathname)
|
52
|
-
Proscenium::Current.loaded ||= EXTENSIONS.to_h { |e| [e, Set[]] }
|
53
|
-
|
54
|
-
unless pathname.is_a?(Pathname)
|
55
|
-
raise ArgumentError, "Argument `pathname` (#{pathname}) must be a Pathname"
|
56
|
-
end
|
57
|
-
|
58
|
-
ext = pathname.extname.sub('.', '').to_sym
|
59
|
-
path = pathname.relative_path_from(Rails.root).to_s
|
60
|
-
|
61
|
-
raise ArgumentError, "unsupported extension: #{ext}" unless EXTENSIONS.include?(ext)
|
62
|
-
|
63
|
-
return if Proscenium::Current.loaded[ext].include?(path)
|
64
|
-
|
65
|
-
raise NotFound, path unless pathname.exist?
|
66
|
-
|
67
|
-
Proscenium::Current.loaded[ext] << path
|
68
|
-
|
69
|
-
Rails.logger.debug "[Proscenium] Side loaded /#{path}"
|
70
|
-
end
|
71
|
-
|
72
38
|
module Monkey
|
73
39
|
module TemplateRenderer
|
74
40
|
private
|
75
41
|
|
76
42
|
def render_template(view, template, layout_name, locals)
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
43
|
+
layout = find_layout(layout_name, locals.keys, [formats.first])
|
44
|
+
renderable = template.instance_variable_get(:@renderable)
|
45
|
+
|
46
|
+
if template.is_a?(ActionView::Template::Renderable) &&
|
47
|
+
renderable.class < ::ViewComponent::Base && renderable.class.format == :html
|
48
|
+
# Side load controller rendered ViewComponent
|
49
|
+
Proscenium::SideLoad.append "app/views/#{layout.virtual_path}" if layout
|
50
|
+
Proscenium::SideLoad.append "app/views/#{renderable.virtual_path}"
|
51
|
+
elsif template.respond_to?(:virtual_path) &&
|
52
|
+
template.respond_to?(:type) && template.type == :html
|
53
|
+
# Side load regular view template.
|
54
|
+
Proscenium::SideLoad.append "app/views/#{layout.virtual_path}" if layout
|
55
|
+
|
56
|
+
# Try side loading the variant template
|
83
57
|
if template.respond_to?(:variant) && template.variant
|
84
58
|
Proscenium::SideLoad.append "app/views/#{template.virtual_path}+#{template.variant}"
|
85
59
|
end
|
data/lib/proscenium/version.rb
CHANGED
@@ -5,12 +5,16 @@ module Proscenium::ViewComponent
|
|
5
5
|
|
6
6
|
autoload :TagBuilder
|
7
7
|
|
8
|
+
def render_in(...)
|
9
|
+
cssm.compile_class_names(super)
|
10
|
+
end
|
11
|
+
|
8
12
|
def before_render
|
9
13
|
side_load_assets unless self.class < ReactComponent
|
10
14
|
end
|
11
15
|
|
12
16
|
def css_module(name)
|
13
|
-
cssm.class_names(name
|
17
|
+
cssm.class_names!(name).join ' '
|
14
18
|
end
|
15
19
|
|
16
20
|
private
|