proscenium 0.1.0.alpha2-x86_64-darwin → 0.1.0.alpha3-x86_64-darwin
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +158 -29
- data/bin/esbuild +0 -0
- data/bin/parcel_css +0 -0
- data/config/routes.rb +1 -1
- data/lib/proscenium/compilers/esbuild/compile_error.js +145 -0
- data/lib/proscenium/compilers/esbuild/css_plugin.js +76 -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 +38 -35
- data/lib/proscenium/compilers/esbuild/setup_plugin.js +23 -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 +60 -19
- data/lib/proscenium/helper.rb +2 -2
- data/lib/proscenium/middleware/base.rb +13 -3
- data/lib/proscenium/middleware/esbuild.rb +14 -0
- data/lib/proscenium/middleware/parcel_css.rb +30 -3
- data/lib/proscenium/middleware.rb +3 -0
- data/lib/proscenium/railtie.rb +16 -18
- data/lib/proscenium/runtime/component_manager/index.js +7 -7
- data/lib/proscenium/runtime/component_manager/{render_component.js → render_component.jsx} +9 -13
- data/lib/proscenium/version.rb +1 -1
- metadata +8 -14
- 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
@@ -1,35 +1,45 @@
|
|
1
1
|
import { join, resolve } from 'std/path/mod.ts'
|
2
|
-
import {
|
3
|
-
parseFromString as parseImportMap,
|
4
|
-
resolve as resolveFromImportMap
|
5
|
-
} from 'import-maps/resolve'
|
2
|
+
import { resolve as resolveFromImportMap } from 'import-maps/resolve'
|
6
3
|
|
7
4
|
import setup from './setup_plugin.js'
|
8
5
|
|
9
6
|
const baseURL = new URL('file://')
|
10
|
-
const importKinds = ['import-statement', 'dynamic-import', 'require-call']
|
7
|
+
const importKinds = ['import-statement', 'dynamic-import', 'require-call', 'import-rule']
|
11
8
|
|
12
9
|
export default setup('resolve', (build, options) => {
|
13
|
-
const { runtimeDir } = options
|
10
|
+
const { runtimeDir, importMap } = options
|
14
11
|
const cwd = build.initialOptions.absWorkingDir
|
15
12
|
const runtimeCwdAlias = `${cwd}/proscenium-runtime`
|
16
|
-
const importMap = readImportMap()
|
17
13
|
|
18
|
-
return
|
19
|
-
|
14
|
+
return [
|
15
|
+
{
|
16
|
+
type: 'onResolve',
|
20
17
|
filter: /.*/,
|
21
18
|
async callback(args) {
|
22
|
-
|
19
|
+
if (args.path.includes('?')) {
|
20
|
+
const [path, query] = args.path.split('?')
|
21
|
+
args.path = path
|
22
|
+
args.suffix = `?${query}`
|
23
|
+
args.queryParams = new URLSearchParams(query)
|
24
|
+
}
|
25
|
+
|
26
|
+
// Mark remote modules as external.
|
23
27
|
if (args.path.startsWith('http://') || args.path.startsWith('https://')) {
|
24
28
|
return { external: true }
|
25
29
|
}
|
26
30
|
|
27
31
|
// Proscenium runtime
|
28
32
|
if (args.path.startsWith('@proscenium/')) {
|
29
|
-
|
30
|
-
|
31
|
-
|
33
|
+
const result = { suffix: args.suffix }
|
34
|
+
|
35
|
+
if (args.queryParams?.has('bundle')) {
|
36
|
+
result.path = join(runtimeDir, `${args.path.replace(/^@proscenium/, '')}/index.js`)
|
37
|
+
} else {
|
38
|
+
result.path = `${args.path.replace(/^@proscenium/, '/proscenium-runtime')}/index.js`
|
39
|
+
result.external = true
|
32
40
|
}
|
41
|
+
|
42
|
+
return result
|
33
43
|
}
|
34
44
|
|
35
45
|
if (args.path.startsWith(runtimeCwdAlias)) {
|
@@ -43,7 +53,8 @@ export default setup('resolve', (build, options) => {
|
|
43
53
|
}
|
44
54
|
},
|
45
55
|
|
46
|
-
|
56
|
+
{
|
57
|
+
type: 'onLoad',
|
47
58
|
filter: /.*/,
|
48
59
|
namespace: 'importStylesheet',
|
49
60
|
callback(args) {
|
@@ -67,7 +78,7 @@ export default setup('resolve', (build, options) => {
|
|
67
78
|
return result
|
68
79
|
}
|
69
80
|
}
|
70
|
-
|
81
|
+
]
|
71
82
|
|
72
83
|
// Resolve the given `params.path` to a path relative to the Rails root.
|
73
84
|
//
|
@@ -76,7 +87,7 @@ export default setup('resolve', (build, options) => {
|
|
76
87
|
// './my_module' -> '/.../app/my_module.js'
|
77
88
|
// '/app/my_module' -> '/.../app/my_module.js'
|
78
89
|
async function unbundleImport(params) {
|
79
|
-
const result = { path: params.path }
|
90
|
+
const result = { path: params.path, suffix: params.suffix }
|
80
91
|
|
81
92
|
if (importMap) {
|
82
93
|
const { matched, resolvedImport } = resolveFromImportMap(params.path, importMap, baseURL)
|
@@ -108,7 +119,17 @@ export default setup('resolve', (build, options) => {
|
|
108
119
|
// throw `${resolveResult.errors[0].text} (resolveDir: ${cwd})`
|
109
120
|
}
|
110
121
|
|
111
|
-
|
122
|
+
// If bundle queryParam is defined, return the resolveResult.
|
123
|
+
if (params.queryParams?.has('bundle')) {
|
124
|
+
return { ...resolveResult, suffix: result.suffix }
|
125
|
+
}
|
126
|
+
|
127
|
+
if (resolveResult.path.startsWith(runtimeDir)) {
|
128
|
+
result.path = '/proscenium-runtime' + resolveResult.path.slice(runtimeDir.length)
|
129
|
+
} else {
|
130
|
+
result.path = resolveResult.path.slice(cwd.length)
|
131
|
+
}
|
132
|
+
|
112
133
|
result.sideEffects = resolveResult.sideEffects
|
113
134
|
|
114
135
|
if (
|
@@ -119,27 +140,9 @@ export default setup('resolve', (build, options) => {
|
|
119
140
|
// We're importing a CSS file from JS(X).
|
120
141
|
result.namespace = 'importStylesheet'
|
121
142
|
} else {
|
122
|
-
// Requested path is a bare module.
|
123
143
|
result.external = true
|
124
144
|
}
|
125
145
|
|
126
146
|
return result
|
127
147
|
}
|
128
|
-
|
129
|
-
function readImportMap() {
|
130
|
-
const file = join(cwd, 'config', 'import_map.json')
|
131
|
-
let source
|
132
|
-
|
133
|
-
try {
|
134
|
-
source = Deno.readTextFileSync(file)
|
135
|
-
} catch {
|
136
|
-
return null
|
137
|
-
}
|
138
|
-
|
139
|
-
return parseImportMap(source, baseURL)
|
140
|
-
}
|
141
148
|
})
|
142
|
-
|
143
|
-
function isBareModule(path) {
|
144
|
-
return !path.startsWith('.') && !path.startsWith('/')
|
145
|
-
}
|
@@ -1,35 +1,36 @@
|
|
1
1
|
export default (pluginName, pluginFn) => {
|
2
2
|
return (options = {}) => ({
|
3
3
|
name: pluginName,
|
4
|
-
setup(build) {
|
5
|
-
const
|
4
|
+
async setup(build) {
|
5
|
+
const callbacks = await pluginFn(build, options)
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
callbacks.forEach(({ type, callback, filter, namespace }) => {
|
8
|
+
if (type === 'onResolve') {
|
9
|
+
build.onResolve({ filter, namespace }, async params => {
|
10
|
+
if (params.pluginData?.isResolvingPath) return
|
9
11
|
|
10
|
-
|
11
|
-
|
12
|
+
options.debug &&
|
13
|
+
console.debug(`plugin(${pluginName}):onResolve`, params.path, { params })
|
12
14
|
|
13
|
-
|
14
|
-
const results = await callback(params)
|
15
|
-
options.debug &&
|
16
|
-
console.debug(`plugin(${pluginName}):onResolve`, params.path, { results })
|
15
|
+
const results = await callback(params)
|
17
16
|
|
18
|
-
|
19
|
-
|
20
|
-
}
|
17
|
+
options.debug &&
|
18
|
+
console.debug(`plugin(${pluginName}):onResolve`, params.path, { results })
|
21
19
|
|
22
|
-
|
23
|
-
|
20
|
+
return results
|
21
|
+
})
|
22
|
+
} else if (type === 'onLoad') {
|
23
|
+
build.onLoad({ filter, namespace }, async params => {
|
24
|
+
options.debug && console.debug(`plugin(${pluginName}):onLoad`, { params })
|
24
25
|
|
25
|
-
|
26
|
-
options.debug && console.debug(`plugin(${pluginName}):onLoad`, { params })
|
27
|
-
const results = callback(params)
|
28
|
-
options.debug && console.debug(`plugin(${pluginName}):onLoad`, { results })
|
26
|
+
const results = await callback(params)
|
29
27
|
|
30
|
-
|
31
|
-
|
32
|
-
|
28
|
+
options.debug && console.debug(`plugin(${pluginName}):onLoad`, { results })
|
29
|
+
|
30
|
+
return results
|
31
|
+
})
|
32
|
+
}
|
33
|
+
})
|
33
34
|
}
|
34
35
|
})
|
35
36
|
}
|
@@ -2,11 +2,12 @@ import { basename } from 'std/path/mod.ts'
|
|
2
2
|
import { transformAsync } from '@babel/core'
|
3
3
|
import solid from 'babel-preset-solid'
|
4
4
|
|
5
|
-
import { setup } from '
|
5
|
+
import { setup } from './setup_plugin.js'
|
6
6
|
|
7
7
|
export default setup('solidjs', () => {
|
8
|
-
return
|
9
|
-
|
8
|
+
return [
|
9
|
+
{
|
10
|
+
type: 'onLoad',
|
10
11
|
filter: /\.jsx$/,
|
11
12
|
async callback(args) {
|
12
13
|
const source = await Deno.readTextFile(args.path)
|
@@ -19,5 +20,5 @@ export default setup('solidjs', () => {
|
|
19
20
|
return { contents: code, loader: 'js' }
|
20
21
|
}
|
21
22
|
}
|
22
|
-
|
23
|
+
]
|
23
24
|
})
|
@@ -2,8 +2,12 @@ import { join } from 'std/path/mod.ts'
|
|
2
2
|
|
3
3
|
import compile from './esbuild.js'
|
4
4
|
|
5
|
-
const
|
5
|
+
const root = join(Deno.cwd(), 'test', 'internal')
|
6
6
|
|
7
|
-
Deno.bench('esbuild', async () => {
|
8
|
-
await compile(
|
7
|
+
Deno.bench('esbuild js', async () => {
|
8
|
+
await compile(['lib/foo.js'], { root })
|
9
|
+
})
|
10
|
+
|
11
|
+
Deno.bench('esbuild css', async () => {
|
12
|
+
await compile(['lib/foo.css'], { root })
|
9
13
|
})
|
@@ -4,21 +4,37 @@ 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
|
|
11
14
|
if (import.meta.main) {
|
15
|
+
!Deno.env.get('RAILS_ENV') && Deno.env.set('RAILS_ENV', 'development')
|
16
|
+
|
12
17
|
const { _: paths, ...options } = parseArgs(Deno.args, {
|
13
|
-
string: ['root', 'runtime-dir'],
|
14
|
-
boolean: ['write'],
|
15
|
-
alias: {
|
18
|
+
string: ['root', 'runtime-dir', 'import-map'],
|
19
|
+
boolean: ['write', 'debug'],
|
20
|
+
alias: {
|
21
|
+
'runtime-dir': 'runtimeDir',
|
22
|
+
'import-map': 'importMap'
|
23
|
+
}
|
16
24
|
})
|
17
|
-
|
25
|
+
|
26
|
+
let result = await main(paths, options)
|
27
|
+
|
28
|
+
// `result` is an error object, so return to stderr as JSON, and an exit code of 1.
|
29
|
+
if (isPlainObject(result)) {
|
30
|
+
result = new TextEncoder().encode(`(${throwCompileError()})(${JSON.stringify(result)})`)
|
31
|
+
}
|
32
|
+
|
33
|
+
await writeAll(Deno.stdout, result)
|
18
34
|
}
|
19
35
|
|
20
36
|
async function main(paths = [], options = {}) {
|
21
|
-
const { root, write } = { write: false, ...options }
|
37
|
+
const { root, write, debug } = { write: false, ...options }
|
22
38
|
|
23
39
|
if (!Array.isArray(paths) || paths.length < 1) throw new ArgumentError('pathsRequired')
|
24
40
|
if (!root) throw new ArgumentError('rootRequired')
|
@@ -31,7 +47,9 @@ async function main(paths = [], options = {}) {
|
|
31
47
|
throw new ArgumentError('rootUnknown', { root })
|
32
48
|
}
|
33
49
|
|
34
|
-
const
|
50
|
+
const env = Deno.env.get('RAILS_ENV')
|
51
|
+
const isProd = env === 'production'
|
52
|
+
const isTest = env === 'test'
|
35
53
|
|
36
54
|
const entryPoints = new Set()
|
37
55
|
for (let i = 0; i < paths.length; i++) {
|
@@ -46,37 +64,60 @@ async function main(paths = [], options = {}) {
|
|
46
64
|
}
|
47
65
|
}
|
48
66
|
|
67
|
+
let importMap
|
68
|
+
try {
|
69
|
+
importMap = readImportMap(options.importMap, root)
|
70
|
+
} catch (error) {
|
71
|
+
return {
|
72
|
+
detail: error.stack,
|
73
|
+
text: `Cannot read/parse import map: ${error.message}`,
|
74
|
+
location: {
|
75
|
+
file: error.file
|
76
|
+
}
|
77
|
+
}
|
78
|
+
}
|
79
|
+
|
49
80
|
const runtimeDir = resolve(dirname(fromFileUrl(import.meta.url)), '../runtime')
|
50
81
|
|
51
82
|
const params = {
|
52
83
|
entryPoints: Array.from(entryPoints),
|
53
84
|
absWorkingDir: root,
|
54
|
-
logLevel: '
|
55
|
-
|
85
|
+
logLevel: 'silent',
|
86
|
+
logLimit: 1,
|
87
|
+
sourcemap: isTest ? false : isProd ? 'linked' : 'inline',
|
56
88
|
outdir: 'public/assets',
|
57
89
|
outbase: './',
|
58
90
|
format: 'esm',
|
59
|
-
|
60
|
-
|
91
|
+
jsx: 'automatic',
|
92
|
+
jsxDev: !isTest && !isProd,
|
61
93
|
minify: isProd,
|
62
94
|
bundle: true,
|
63
|
-
plugins: [envPlugin(), resolvePlugin({ runtimeDir, debug
|
64
|
-
inject: [join(runtimeDir, 'react_shim/index.js')],
|
95
|
+
plugins: [envPlugin(), resolvePlugin({ runtimeDir, importMap, debug }), cssPlugin({ debug })],
|
65
96
|
metafile: write,
|
66
97
|
write
|
67
98
|
}
|
68
99
|
|
100
|
+
let result
|
69
101
|
try {
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
return new TextEncoder().encode(JSON.stringify(result))
|
74
|
-
} else {
|
75
|
-
return result.outputFiles[0].contents
|
76
|
-
}
|
102
|
+
result = await build(params)
|
103
|
+
} catch (error) {
|
104
|
+
return { ...error.errors[0] }
|
77
105
|
} finally {
|
78
106
|
stop()
|
79
107
|
}
|
108
|
+
|
109
|
+
if (write) {
|
110
|
+
return new TextEncoder().encode(JSON.stringify(result))
|
111
|
+
} else {
|
112
|
+
return result.outputFiles[0].contents
|
113
|
+
}
|
114
|
+
}
|
115
|
+
|
116
|
+
export function isPlainObject(value) {
|
117
|
+
if (Object.prototype.toString.call(value) !== '[object Object]') return false
|
118
|
+
|
119
|
+
const prototype = Object.getPrototypeOf(value)
|
120
|
+
return prototype === null || prototype === Object.prototype
|
80
121
|
}
|
81
122
|
|
82
123
|
export default main
|
data/lib/proscenium/helper.rb
CHANGED
@@ -21,12 +21,12 @@ module Proscenium
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def proscenium_dev
|
24
|
-
return
|
24
|
+
return unless Proscenium.config.auto_reload
|
25
25
|
|
26
26
|
javascript_tag %(
|
27
27
|
import autoReload from '/proscenium-runtime/auto_reload.js';
|
28
28
|
autoReload('#{Proscenium::Railtie.websocket_mount_path}');
|
29
|
-
), type: 'module'
|
29
|
+
), type: 'module', defer: true
|
30
30
|
end
|
31
31
|
end
|
32
32
|
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
|
@@ -7,19 +7,46 @@ module Proscenium
|
|
7
7
|
class ParcelCss < Base
|
8
8
|
def attempt
|
9
9
|
benchmark :parcelcss do
|
10
|
-
|
11
|
-
render_response css_module? ? Oj.load(results, mode: :strict)['code'] : results
|
10
|
+
with_custom_media { |path| build path }
|
12
11
|
end
|
13
12
|
end
|
14
13
|
|
15
14
|
private
|
16
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
|
+
|
17
43
|
def cli
|
18
44
|
Gem.bin_path 'proscenium', 'parcel_css'
|
19
45
|
end
|
20
46
|
|
21
47
|
def cli_options
|
22
|
-
options = ['--nesting', '--
|
48
|
+
options = ['--nesting', '--targets', "'>= 0.25%'"]
|
49
|
+
options << '--custom-media' if custom_media?
|
23
50
|
|
24
51
|
if css_module?
|
25
52
|
hash = Digest::SHA1.hexdigest(@request.path)[..7]
|
data/lib/proscenium/railtie.rb
CHANGED
@@ -13,15 +13,15 @@ module Proscenium
|
|
13
13
|
# See https://doc.deno.land/https://deno.land/std@0.145.0/path/mod.ts/~/globToRegExp
|
14
14
|
DEFAULT_GLOB_TYPES = {
|
15
15
|
esbuild: [
|
16
|
-
'
|
17
|
-
'
|
18
|
-
'app
|
19
|
-
],
|
20
|
-
parcelcss: [
|
21
|
-
'lib/**/*.css',
|
22
|
-
'app/components/**/*.css',
|
23
|
-
'app/views/**/*.css'
|
16
|
+
'config/**/*.{js,jsx,css}',
|
17
|
+
'lib/**/*.{js,jsx,css}',
|
18
|
+
'app/**/*.{js,jsx,css}'
|
24
19
|
]
|
20
|
+
# parcelcss: [
|
21
|
+
# 'config/**/*.css',
|
22
|
+
# 'lib/**/*.css',
|
23
|
+
# 'app/**/*.css'
|
24
|
+
# ]
|
25
25
|
}.freeze
|
26
26
|
|
27
27
|
class << self
|
@@ -34,18 +34,16 @@ module Proscenium
|
|
34
34
|
isolate_namespace Proscenium
|
35
35
|
|
36
36
|
config.proscenium = ActiveSupport::OrderedOptions.new
|
37
|
-
config.proscenium.listen_paths ||= %w[lib app]
|
38
|
-
config.proscenium.listen_extensions ||= /\.(css|jsx?)$/
|
39
37
|
config.proscenium.side_load = true
|
38
|
+
config.proscenium.auto_reload = Rails.env.development?
|
39
|
+
config.proscenium.auto_reload_paths ||= %w[lib app config]
|
40
|
+
config.proscenium.auto_reload_extensions ||= /\.(css|jsx?)$/
|
40
41
|
|
41
42
|
initializer 'proscenium.configuration' do |app|
|
42
43
|
options = app.config.proscenium
|
43
44
|
|
44
45
|
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 }
|
46
|
+
options.auto_reload_paths.filter! { |path| Dir.exist? path }
|
49
47
|
options.cable_mount_path ||= '/proscenium-cable'
|
50
48
|
options.cable_logger ||= Rails.logger
|
51
49
|
end
|
@@ -73,10 +71,10 @@ module Proscenium
|
|
73
71
|
end
|
74
72
|
|
75
73
|
config.after_initialize do
|
76
|
-
next unless config.proscenium.
|
74
|
+
next unless config.proscenium.auto_reload
|
77
75
|
|
78
|
-
@listener = Listen.to(*config.proscenium.
|
79
|
-
only: config.proscenium.
|
76
|
+
@listener = Listen.to(*config.proscenium.auto_reload_paths,
|
77
|
+
only: config.proscenium.auto_reload_extensions) do |mod, add, rem|
|
80
78
|
Proscenium::Railtie.websocket&.broadcast('reload', {
|
81
79
|
modified: mod,
|
82
80
|
removed: rem,
|
@@ -94,7 +92,7 @@ module Proscenium
|
|
94
92
|
class << self
|
95
93
|
def websocket
|
96
94
|
return @websocket unless @websocket.nil?
|
97
|
-
return unless config.proscenium.
|
95
|
+
return unless config.proscenium.auto_reload
|
98
96
|
|
99
97
|
cable = ActionCable::Server::Configuration.new
|
100
98
|
cable.cable = { adapter: 'async' }.with_indifferent_access
|
@@ -1,18 +1,18 @@
|
|
1
1
|
/* eslint-disable no-console */
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
document.addEventListener('DOMContentLoaded', () => {
|
3
|
+
export async function init() {
|
6
4
|
const elements = document.querySelectorAll('[data-component]')
|
7
5
|
|
8
6
|
if (elements.length < 1) return
|
9
7
|
|
10
|
-
|
8
|
+
const { default: renderComponent } = await import(`./render_component`)
|
9
|
+
|
10
|
+
Array.from(elements, ele => {
|
11
11
|
const data = JSON.parse(ele.getAttribute('data-component'))
|
12
12
|
|
13
13
|
let isVisible = false
|
14
|
-
const observer = new IntersectionObserver(
|
15
|
-
entries.forEach(
|
14
|
+
const observer = new IntersectionObserver(entries => {
|
15
|
+
entries.forEach(entry => {
|
16
16
|
if (!isVisible && entry.isIntersecting) {
|
17
17
|
isVisible = true
|
18
18
|
observer.unobserve(ele)
|
@@ -24,4 +24,4 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
24
24
|
|
25
25
|
observer.observe(ele)
|
26
26
|
})
|
27
|
-
}
|
27
|
+
}
|
@@ -18,22 +18,18 @@ export default async function (ele, data) {
|
|
18
18
|
|
19
19
|
if (!contentLoader) return null
|
20
20
|
|
21
|
-
return
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
return (
|
22
|
+
<div
|
23
|
+
style={{ height: '100%' }}
|
24
|
+
dangerouslySetInnerHTML={{ __html: contentLoader.outerHTML }}
|
25
|
+
></div>
|
26
|
+
)
|
25
27
|
}
|
26
28
|
|
27
29
|
createRoot(ele).render(
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
fallback: /* @__PURE__ */ createElement(Fallback, {
|
32
|
-
contentLoader
|
33
|
-
})
|
34
|
-
},
|
35
|
-
createElement(component, data.props)
|
36
|
-
)
|
30
|
+
<Suspense fallback={<Fallback contentLoader={contentLoader} />}>
|
31
|
+
{createElement(component, data.props)}
|
32
|
+
</Suspense>
|
37
33
|
)
|
38
34
|
|
39
35
|
RAILS_ENV === 'development' && console.debug(`[REACT]`, `Rendered ${data.path.slice(1)}`)
|
data/lib/proscenium/version.rb
CHANGED