proscenium 0.5.1-x86_64-darwin → 0.7.0-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 +4 -4
- data/README.md +128 -92
- data/bin/proscenium +0 -0
- data/bin/proscenium.h +109 -0
- data/config/routes.rb +0 -3
- data/lib/proscenium/css_module/class_names_resolver.rb +66 -0
- data/lib/proscenium/css_module/resolver.rb +76 -0
- data/lib/proscenium/css_module.rb +18 -39
- data/lib/proscenium/esbuild/golib.rb +97 -0
- data/lib/proscenium/esbuild.rb +32 -0
- data/lib/proscenium/helper.rb +0 -23
- data/lib/proscenium/log_subscriber.rb +26 -0
- data/lib/proscenium/middleware/base.rb +28 -36
- data/lib/proscenium/middleware/esbuild.rb +18 -44
- data/lib/proscenium/middleware/url.rb +1 -6
- data/lib/proscenium/middleware.rb +12 -16
- data/lib/proscenium/phlex/component_concerns.rb +27 -0
- data/lib/proscenium/phlex/page.rb +62 -0
- data/lib/proscenium/phlex/react_component.rb +52 -8
- data/lib/proscenium/phlex/resolve_css_modules.rb +67 -0
- data/lib/proscenium/phlex.rb +34 -33
- data/lib/proscenium/railtie.rb +41 -67
- data/lib/proscenium/side_load/ensure_loaded.rb +25 -0
- data/lib/proscenium/side_load/helper.rb +25 -0
- data/lib/proscenium/side_load/monkey.rb +48 -0
- data/lib/proscenium/side_load.rb +58 -52
- data/lib/proscenium/version.rb +1 -1
- data/lib/proscenium/view_component/react_component.rb +14 -0
- data/lib/proscenium/view_component.rb +28 -18
- data/lib/proscenium.rb +79 -2
- metadata +35 -72
- data/app/channels/proscenium/connection.rb +0 -13
- data/app/channels/proscenium/reload_channel.rb +0 -9
- data/bin/esbuild +0 -0
- data/bin/lightningcss +0 -0
- data/lib/proscenium/compiler.js +0 -84
- data/lib/proscenium/compilers/esbuild/argument_error.js +0 -24
- data/lib/proscenium/compilers/esbuild/compile_error.js +0 -148
- data/lib/proscenium/compilers/esbuild/css/postcss.js +0 -67
- data/lib/proscenium/compilers/esbuild/css_plugin.js +0 -172
- data/lib/proscenium/compilers/esbuild/env_plugin.js +0 -46
- data/lib/proscenium/compilers/esbuild/http_bundle_plugin.js +0 -53
- data/lib/proscenium/compilers/esbuild/import_map.js +0 -59
- data/lib/proscenium/compilers/esbuild/resolve_plugin.js +0 -205
- data/lib/proscenium/compilers/esbuild/setup_plugin.js +0 -45
- data/lib/proscenium/compilers/esbuild/solidjs_plugin.js +0 -24
- data/lib/proscenium/compilers/esbuild.bench.js +0 -14
- data/lib/proscenium/compilers/esbuild.js +0 -179
- data/lib/proscenium/link_to_helper.rb +0 -40
- data/lib/proscenium/middleware/lightningcss.rb +0 -64
- data/lib/proscenium/middleware/outside_root.rb +0 -26
- data/lib/proscenium/middleware/runtime.rb +0 -22
- data/lib/proscenium/middleware/static.rb +0 -14
- data/lib/proscenium/phlex/component.rb +0 -9
- data/lib/proscenium/precompile.rb +0 -31
- data/lib/proscenium/runtime/auto_reload.js +0 -40
- data/lib/proscenium/runtime/react_shim/index.js +0 -1
- data/lib/proscenium/runtime/react_shim/package.json +0 -5
- data/lib/proscenium/utils.js +0 -8
- data/lib/tasks/assets.rake +0 -19
@@ -1,179 +0,0 @@
|
|
1
|
-
import { writeAll } from 'std/streams/mod.ts'
|
2
|
-
import { parse as parseArgs } from 'std/flags/mod.ts'
|
3
|
-
import { expandGlob } from 'std/fs/mod.ts'
|
4
|
-
import { join, isGlob, resolve, dirname, fromFileUrl } from 'std/path/mod.ts'
|
5
|
-
import { build, stop } from 'esbuild'
|
6
|
-
|
7
|
-
import { readImportMap } from './esbuild/import_map.js'
|
8
|
-
import envPlugin from './esbuild/env_plugin.js'
|
9
|
-
import cssPlugin from './esbuild/css_plugin.js'
|
10
|
-
import resolvePlugin from './esbuild/resolve_plugin.js'
|
11
|
-
import ArgumentError from './esbuild/argument_error.js'
|
12
|
-
import throwCompileError from './esbuild/compile_error.js'
|
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 or globs to compile.
|
27
|
-
*
|
28
|
-
* OPTIONS:
|
29
|
-
* --root <PATH>
|
30
|
-
* Relative or absolute path to the root or current working directory when compilation will
|
31
|
-
* take place.
|
32
|
-
* --import-map <PATH>
|
33
|
-
* Path to an import map, relative to the <root>.
|
34
|
-
* --lightningcss-bin <PATH>
|
35
|
-
* Path to the lightningcss CLI binary.
|
36
|
-
* --write
|
37
|
-
* Write output to the filesystem according to esbuild logic.
|
38
|
-
* --cache-query-string <STRING>
|
39
|
-
* Query string to append to all imports as a cache buster. Example: `v1`.
|
40
|
-
* --debug
|
41
|
-
* Debug output,
|
42
|
-
*/
|
43
|
-
if (import.meta.main) {
|
44
|
-
!Deno.env.get('RAILS_ENV') && Deno.env.set('RAILS_ENV', 'development')
|
45
|
-
|
46
|
-
const { _: paths, ...options } = parseArgs(Deno.args, {
|
47
|
-
string: ['root', 'import-map', 'lightningcss-bin', 'cache-query-string'],
|
48
|
-
boolean: ['write', 'debug'],
|
49
|
-
alias: {
|
50
|
-
'import-map': 'importMap',
|
51
|
-
'cache-query-string': 'cacheQueryString',
|
52
|
-
'lightningcss-bin': 'lightningcssBin'
|
53
|
-
}
|
54
|
-
})
|
55
|
-
|
56
|
-
let result = await main(paths, options)
|
57
|
-
|
58
|
-
// `result` is an error object, so return to stderr as JSON, and an exit code of 1.
|
59
|
-
if (isPlainObject(result)) {
|
60
|
-
result = new TextEncoder().encode(`(${throwCompileError()})(${JSON.stringify(result)})`)
|
61
|
-
}
|
62
|
-
|
63
|
-
await writeAll(Deno.stdout, result)
|
64
|
-
}
|
65
|
-
|
66
|
-
async function main(paths = [], options = {}) {
|
67
|
-
const { write, debug } = { write: false, ...options }
|
68
|
-
|
69
|
-
if (!Array.isArray(paths) || paths.length < 1) throw new ArgumentError('pathsRequired')
|
70
|
-
if (!options.root) throw new ArgumentError('rootRequired')
|
71
|
-
if (!options.lightningcssBin) throw new ArgumentError('lightningcssBinRequired')
|
72
|
-
|
73
|
-
const root = resolve(options.root)
|
74
|
-
|
75
|
-
// Make sure that `root` is a valid directory.
|
76
|
-
try {
|
77
|
-
const stat = Deno.lstatSync(root)
|
78
|
-
if (!stat.isDirectory) throw new ArgumentError('rootUnknown', { root })
|
79
|
-
} catch {
|
80
|
-
throw new ArgumentError('rootUnknown', { root })
|
81
|
-
}
|
82
|
-
|
83
|
-
const env = Deno.env.get('RAILS_ENV')
|
84
|
-
const isProd = env === 'production'
|
85
|
-
const isTest = env === 'test'
|
86
|
-
const entryPoints = new Set()
|
87
|
-
|
88
|
-
for (let i = 0; i < paths.length; i++) {
|
89
|
-
const path = paths[i]
|
90
|
-
|
91
|
-
if (isGlob(path)) {
|
92
|
-
for await (const file of expandGlob(path, { root })) {
|
93
|
-
file.isFile && entryPoints.add(file.path)
|
94
|
-
}
|
95
|
-
} else if (path.startsWith('/') || /^url:https?:\/\//.test(path)) {
|
96
|
-
// Path is absolute, or is prefixed with 'url:', so it must be outsideRoot, or Url. Don't
|
97
|
-
// prefix the root.
|
98
|
-
// See Proscenium::Middleware::[OutsideRoot|Url].
|
99
|
-
entryPoints.add(path)
|
100
|
-
} else {
|
101
|
-
entryPoints.add(join(root, path))
|
102
|
-
}
|
103
|
-
}
|
104
|
-
|
105
|
-
let importMap
|
106
|
-
try {
|
107
|
-
importMap = readImportMap(options.importMap, root)
|
108
|
-
} catch (error) {
|
109
|
-
return {
|
110
|
-
detail: error.stack,
|
111
|
-
text: `Cannot read/parse import map: ${error.message}`,
|
112
|
-
location: {
|
113
|
-
file: error.file
|
114
|
-
}
|
115
|
-
}
|
116
|
-
}
|
117
|
-
|
118
|
-
const runtimeDir = resolve(dirname(fromFileUrl(import.meta.url)), '../runtime')
|
119
|
-
|
120
|
-
const params = {
|
121
|
-
entryPoints: Array.from(entryPoints),
|
122
|
-
absWorkingDir: root,
|
123
|
-
logLevel: 'silent',
|
124
|
-
logLimit: 1,
|
125
|
-
outdir: 'public/assets',
|
126
|
-
outbase: './',
|
127
|
-
format: 'esm',
|
128
|
-
jsx: 'automatic',
|
129
|
-
jsxDev: !isTest && !isProd,
|
130
|
-
minify: isProd,
|
131
|
-
bundle: true,
|
132
|
-
|
133
|
-
// The Esbuild default places browser before module, but we're building for modern browsers
|
134
|
-
// which support esm. So we prioritise that. Some libraries export a "browser" build that still
|
135
|
-
// uses CJS.
|
136
|
-
mainFields: ['module', 'browser', 'main'],
|
137
|
-
|
138
|
-
plugins: [
|
139
|
-
envPlugin(),
|
140
|
-
resolvePlugin({ runtimeDir, importMap, debug, cacheQueryString: options.cacheQueryString }),
|
141
|
-
cssPlugin({ lightningcssBin: options.lightningcssBin, debug })
|
142
|
-
],
|
143
|
-
metafile: write,
|
144
|
-
write
|
145
|
-
}
|
146
|
-
|
147
|
-
if (!debug) {
|
148
|
-
params.sourcemap = isProd || isTest ? false : 'inline'
|
149
|
-
}
|
150
|
-
|
151
|
-
let result
|
152
|
-
try {
|
153
|
-
result = await build(params)
|
154
|
-
} catch (error) {
|
155
|
-
if (debug) {
|
156
|
-
throw error
|
157
|
-
}
|
158
|
-
|
159
|
-
return { ...error.errors[0] }
|
160
|
-
} finally {
|
161
|
-
stop()
|
162
|
-
}
|
163
|
-
|
164
|
-
if (write) {
|
165
|
-
return new TextEncoder().encode(JSON.stringify(result))
|
166
|
-
} else {
|
167
|
-
const fileIndex = params.sourcemap === 'linked' ? 1 : 0
|
168
|
-
return result.outputFiles[fileIndex].contents
|
169
|
-
}
|
170
|
-
}
|
171
|
-
|
172
|
-
export function isPlainObject(value) {
|
173
|
-
if (Object.prototype.toString.call(value) !== '[object Object]') return false
|
174
|
-
|
175
|
-
const prototype = Object.getPrototypeOf(value)
|
176
|
-
return prototype === null || prototype === Object.prototype
|
177
|
-
}
|
178
|
-
|
179
|
-
export default main
|
@@ -1,40 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Proscenium
|
4
|
-
module LinkToHelper
|
5
|
-
# Overrides ActionView::Helpers::UrlHelper#link_to to allow passing a component instance as the
|
6
|
-
# URL, which will build the URL from the component path, eg. `/components/my_component`. The
|
7
|
-
# resulting link tag will also populate the `data` attribute with the component props.
|
8
|
-
#
|
9
|
-
# Example:
|
10
|
-
# link_to 'Go to', MyComponent
|
11
|
-
#
|
12
|
-
# TODO: ummm, todo it! ;)
|
13
|
-
end
|
14
|
-
|
15
|
-
# Component handling for the `link_to` helper.
|
16
|
-
class LinkToComponentArguments
|
17
|
-
def initialize(options, name_argument_index, context)
|
18
|
-
@options = options
|
19
|
-
@name_argument_index = name_argument_index
|
20
|
-
@component = @options[@name_argument_index]
|
21
|
-
|
22
|
-
# We have to render the component, and then extract the props from the component. Rendering
|
23
|
-
# first ensures that we have all the correct props.
|
24
|
-
context.render @component
|
25
|
-
end
|
26
|
-
|
27
|
-
def helper_options
|
28
|
-
@options[@name_argument_index] = "/components#{@component.virtual_path}"
|
29
|
-
@options[@name_argument_index += 1] ||= {}
|
30
|
-
@options[@name_argument_index][:rel] = 'nofollow'
|
31
|
-
@options[@name_argument_index][:data] ||= {}
|
32
|
-
@options[@name_argument_index][:data][:component] = {
|
33
|
-
path: @component.virtual_path,
|
34
|
-
props: @component.props
|
35
|
-
}
|
36
|
-
|
37
|
-
@options
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
@@ -1,64 +0,0 @@
|
|
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
|
@@ -1,26 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Proscenium
|
4
|
-
class Middleware
|
5
|
-
# Provides a way to render files outside of the Rails root during non-production. This is
|
6
|
-
# primarily to support linked NPM modules, for example when using `pnpm link ...`.
|
7
|
-
class OutsideRoot < Esbuild
|
8
|
-
private
|
9
|
-
|
10
|
-
# @override [Esbuild] reassigns root to '/'.
|
11
|
-
def renderable?
|
12
|
-
old_root = root
|
13
|
-
@root = Pathname.new('/')
|
14
|
-
|
15
|
-
super
|
16
|
-
ensure
|
17
|
-
@root = old_root
|
18
|
-
end
|
19
|
-
|
20
|
-
# @override [Esbuild] does not remove leading slash, ensuring it is an absolute path.
|
21
|
-
def path
|
22
|
-
@request.path
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Proscenium
|
4
|
-
class Middleware
|
5
|
-
class Runtime < Esbuild
|
6
|
-
private
|
7
|
-
|
8
|
-
def renderable?
|
9
|
-
old_root = root
|
10
|
-
old_path_info = @request.path_info
|
11
|
-
|
12
|
-
@root = Pathname.new(__dir__).join('../')
|
13
|
-
@request.path_info = @request.path_info.sub(%r{^/proscenium-runtime/}, 'runtime/')
|
14
|
-
|
15
|
-
super
|
16
|
-
ensure
|
17
|
-
@request.path_info = old_path_info
|
18
|
-
@root = old_root
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
@@ -1,14 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Proscenium
|
4
|
-
module Middleware
|
5
|
-
# Serves static files from disk that end with .js or .css.
|
6
|
-
class Static < Base
|
7
|
-
def attempt
|
8
|
-
benchmark :static do
|
9
|
-
Rack::File.new(root, { 'X-Proscenium-Middleware' => 'static' }).call(@request.env)
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
@@ -1,31 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'open3'
|
4
|
-
|
5
|
-
module Proscenium
|
6
|
-
class Precompile
|
7
|
-
def self.call
|
8
|
-
new.call
|
9
|
-
end
|
10
|
-
|
11
|
-
def call
|
12
|
-
Rails.application.config.proscenium.glob_types.find do |type, globs|
|
13
|
-
cmd = "#{cli type} --root #{Rails.root} '#{globs.join "' '"}' --write"
|
14
|
-
_, stderr, status = Open3.capture3(cmd)
|
15
|
-
|
16
|
-
raise stderr unless status.success?
|
17
|
-
raise "#{type} compiliation failed -- #{stderr}" unless stderr.empty?
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
private
|
22
|
-
|
23
|
-
def cli(type)
|
24
|
-
if ENV['PROSCENIUM_TEST']
|
25
|
-
"deno run -q --import-map import_map.json -A lib/proscenium/compilers/#{type}.js"
|
26
|
-
else
|
27
|
-
Gem.bin_path 'proscenium', type.to_s
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
@@ -1,40 +0,0 @@
|
|
1
|
-
import { createConsumer } from 'https://esm.sh/v96/@rails/actioncable@7.0.4/es2022/actioncable.js'
|
2
|
-
|
3
|
-
export default socketPath => {
|
4
|
-
const uid = (Date.now() + ((Math.random() * 100) | 0)).toString()
|
5
|
-
const consumer = createConsumer(`${socketPath}?uid=${uid}`)
|
6
|
-
|
7
|
-
consumer.subscriptions.create('Proscenium::ReloadChannel', {
|
8
|
-
received: debounce(() => {
|
9
|
-
console.log('[Proscenium] Files changed; reloading...')
|
10
|
-
location.reload()
|
11
|
-
}, 200, true),
|
12
|
-
|
13
|
-
connected() {
|
14
|
-
console.log('[Proscenium] Auto-reload websocket connected')
|
15
|
-
},
|
16
|
-
|
17
|
-
disconnected() {
|
18
|
-
console.log('[Proscenium] Auto-reload websocket disconnected')
|
19
|
-
}
|
20
|
-
})
|
21
|
-
}
|
22
|
-
|
23
|
-
function debounce(func, wait, immediate) {
|
24
|
-
let timeout
|
25
|
-
|
26
|
-
return function () {
|
27
|
-
const args = arguments
|
28
|
-
|
29
|
-
const later = () => {
|
30
|
-
timeout = null
|
31
|
-
!immediate && func.apply(this, args)
|
32
|
-
}
|
33
|
-
|
34
|
-
const callNow = immediate && !timeout
|
35
|
-
clearTimeout(timeout)
|
36
|
-
timeout = setTimeout(later, wait)
|
37
|
-
|
38
|
-
callNow && func.apply(this, args)
|
39
|
-
}
|
40
|
-
}
|
@@ -1 +0,0 @@
|
|
1
|
-
export { createElement as reactCreateElement, Fragment as ReactFragment } from 'react'
|
data/lib/proscenium/utils.js
DELETED
data/lib/tasks/assets.rake
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
namespace :proscenium do
|
4
|
-
desc 'Compile all your assets with Proscenium'
|
5
|
-
task precompile: :environment do
|
6
|
-
puts 'Precompiling assets with Proscenium...'
|
7
|
-
Proscenium::Precompile.call
|
8
|
-
puts 'Proscenium successfully precompiled your assets 🎉'
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
if Rake::Task.task_defined?('assets:precompile')
|
13
|
-
Rake::Task['assets:precompile'].enhance do
|
14
|
-
Rake::Task['proscenium:precompile'].invoke
|
15
|
-
end
|
16
|
-
else
|
17
|
-
Rake::Task.define_task('assets:precompile' => ['proscenium:precompile'])
|
18
|
-
Rake::Task.define_task('assets:clean') # null task just so Heroku builds don't fail
|
19
|
-
end
|