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.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +128 -92
  3. data/bin/proscenium +0 -0
  4. data/bin/proscenium.h +109 -0
  5. data/config/routes.rb +0 -3
  6. data/lib/proscenium/css_module/class_names_resolver.rb +66 -0
  7. data/lib/proscenium/css_module/resolver.rb +76 -0
  8. data/lib/proscenium/css_module.rb +18 -39
  9. data/lib/proscenium/esbuild/golib.rb +97 -0
  10. data/lib/proscenium/esbuild.rb +32 -0
  11. data/lib/proscenium/helper.rb +0 -23
  12. data/lib/proscenium/log_subscriber.rb +26 -0
  13. data/lib/proscenium/middleware/base.rb +28 -36
  14. data/lib/proscenium/middleware/esbuild.rb +18 -44
  15. data/lib/proscenium/middleware/url.rb +1 -6
  16. data/lib/proscenium/middleware.rb +12 -16
  17. data/lib/proscenium/phlex/component_concerns.rb +27 -0
  18. data/lib/proscenium/phlex/page.rb +62 -0
  19. data/lib/proscenium/phlex/react_component.rb +52 -8
  20. data/lib/proscenium/phlex/resolve_css_modules.rb +67 -0
  21. data/lib/proscenium/phlex.rb +34 -33
  22. data/lib/proscenium/railtie.rb +41 -67
  23. data/lib/proscenium/side_load/ensure_loaded.rb +25 -0
  24. data/lib/proscenium/side_load/helper.rb +25 -0
  25. data/lib/proscenium/side_load/monkey.rb +48 -0
  26. data/lib/proscenium/side_load.rb +58 -52
  27. data/lib/proscenium/version.rb +1 -1
  28. data/lib/proscenium/view_component/react_component.rb +14 -0
  29. data/lib/proscenium/view_component.rb +28 -18
  30. data/lib/proscenium.rb +79 -2
  31. metadata +35 -72
  32. data/app/channels/proscenium/connection.rb +0 -13
  33. data/app/channels/proscenium/reload_channel.rb +0 -9
  34. data/bin/esbuild +0 -0
  35. data/bin/lightningcss +0 -0
  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
@@ -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,9 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Proscenium::Phlex::Component < Proscenium::Phlex
4
- private
5
-
6
- def virtual_path
7
- "/#{self.class.name.underscore}"
8
- end
9
- 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'
@@ -1,5 +0,0 @@
1
- {
2
- "name": "react_shim",
3
- "private": true,
4
- "sideEffects": false
5
- }
@@ -1,8 +0,0 @@
1
- export async function fileExists(path) {
2
- try {
3
- const fileInfo = await Deno.stat(path)
4
- return fileInfo.isFile
5
- } catch {
6
- return false
7
- }
8
- }
@@ -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