proscenium 0.5.1-x86_64-linux → 0.7.0-x86_64-linux
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +128 -92
- 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/ext/proscenium +0 -0
- data/lib/proscenium/ext/proscenium.h +109 -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 -74
- 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/config/routes.rb +0 -7
- 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,67 +0,0 @@
|
|
1
|
-
import { expandGlob } from 'std/fs/mod.ts'
|
2
|
-
import postcss from 'postcss'
|
3
|
-
|
4
|
-
export default async (root, path) => {
|
5
|
-
let tmpFile
|
6
|
-
let contents
|
7
|
-
|
8
|
-
const mixinFiles = []
|
9
|
-
for await (const file of expandGlob('lib/**/*.mixin.css', { root, globstar: true })) {
|
10
|
-
mixinFiles.push(file.path)
|
11
|
-
}
|
12
|
-
|
13
|
-
// Only process mixins with PostCSS if there are any 'lib/**/*.mixin.css' files.
|
14
|
-
if (mixinFiles.length > 0) {
|
15
|
-
tmpFile = await Deno.makeTempFile()
|
16
|
-
contents = await Deno.readTextFile(path)
|
17
|
-
|
18
|
-
const result = await postcss([mixinsPlugin({ mixinFiles })]).process(contents, { from: path })
|
19
|
-
contents = result.css
|
20
|
-
}
|
21
|
-
|
22
|
-
return [tmpFile, contents]
|
23
|
-
}
|
24
|
-
|
25
|
-
const mixinsPlugin = (opts = {}) => {
|
26
|
-
return {
|
27
|
-
postcssPlugin: 'mixins',
|
28
|
-
|
29
|
-
prepare() {
|
30
|
-
const mixins = {}
|
31
|
-
|
32
|
-
return {
|
33
|
-
async Once(_, helpers) {
|
34
|
-
for (const path of opts.mixinFiles) {
|
35
|
-
const content = await Deno.readTextFile(path)
|
36
|
-
const root = helpers.parse(content, { from: path })
|
37
|
-
|
38
|
-
root.walkAtRules('define-mixin', atrule => {
|
39
|
-
mixins[atrule.params] = atrule
|
40
|
-
})
|
41
|
-
}
|
42
|
-
},
|
43
|
-
|
44
|
-
AtRule: {
|
45
|
-
mixin: (rule, helpers) => {
|
46
|
-
const mixin = mixins[rule.params]
|
47
|
-
|
48
|
-
if (!mixin) {
|
49
|
-
throw rule.error(`Undefined mixin '${rule.params}'`)
|
50
|
-
}
|
51
|
-
|
52
|
-
const proxy = new helpers.Root()
|
53
|
-
for (let i = 0; i < mixin.nodes.length; i++) {
|
54
|
-
const node = mixin.nodes[i].clone()
|
55
|
-
delete node.raws.before
|
56
|
-
proxy.append(node)
|
57
|
-
}
|
58
|
-
|
59
|
-
rule.parent.insertBefore(rule, proxy)
|
60
|
-
|
61
|
-
if (rule.parent) rule.remove()
|
62
|
-
}
|
63
|
-
}
|
64
|
-
}
|
65
|
-
}
|
66
|
-
}
|
67
|
-
}
|
@@ -1,172 +0,0 @@
|
|
1
|
-
import { crypto } from 'std/crypto/mod.ts'
|
2
|
-
import { join, dirname, basename } from 'std/path/mod.ts'
|
3
|
-
|
4
|
-
import { fileExists } from '../../utils.js'
|
5
|
-
import postcss from './css/postcss.js'
|
6
|
-
import setup from './setup_plugin.js'
|
7
|
-
|
8
|
-
export default setup('css', async (build, options) => {
|
9
|
-
const cwd = build.initialOptions.absWorkingDir
|
10
|
-
|
11
|
-
let customMedia
|
12
|
-
try {
|
13
|
-
customMedia = await Deno.readTextFile(join(cwd, 'config', 'custom_media_queries.css'))
|
14
|
-
} catch {
|
15
|
-
// do nothing, as we don't require custom media.
|
16
|
-
}
|
17
|
-
|
18
|
-
return [
|
19
|
-
{
|
20
|
-
type: 'onLoad',
|
21
|
-
filter: /\.css$/,
|
22
|
-
namespace: 'file',
|
23
|
-
async callback(args) {
|
24
|
-
const hash = await digest(args.path.slice(cwd.length))
|
25
|
-
const isCssModule = args.path.endsWith('.module.css')
|
26
|
-
|
27
|
-
// If path is a CSS module, imported from JS, and a side-loaded ViewComponent stylesheet,
|
28
|
-
// simply return a JS proxy of the class names. The stylesheet itself will have already been
|
29
|
-
// side loaded. This avoids compiling the CSS all over again.
|
30
|
-
if (isCssModule && args.pluginData?.importedFromJs && (await isViewComponent(args.path))) {
|
31
|
-
return {
|
32
|
-
resolveDir: cwd,
|
33
|
-
loader: 'js',
|
34
|
-
contents: cssModulesProxyTemplate(hash)
|
35
|
-
}
|
36
|
-
}
|
37
|
-
|
38
|
-
let cmd = [
|
39
|
-
options.lightningcssBin,
|
40
|
-
'--nesting',
|
41
|
-
'--error-recovery',
|
42
|
-
args.pluginData?.importedFromJs && '--minify',
|
43
|
-
'--targets',
|
44
|
-
'>= 0.25%'
|
45
|
-
].filter(Boolean)
|
46
|
-
|
47
|
-
// This will process the CSS with Postcss only if it needs to.
|
48
|
-
let [tmpFile, contents] = await postcss(cwd, args.path)
|
49
|
-
|
50
|
-
// As custom media are defined in their own file, we have to append the file contents to our
|
51
|
-
// stylesheet, so that the custom media can be used.
|
52
|
-
if (customMedia) {
|
53
|
-
cmd.push('--custom-media')
|
54
|
-
|
55
|
-
if (!tmpFile && !contents) {
|
56
|
-
tmpFile = await Deno.makeTempFile()
|
57
|
-
contents = await Deno.readTextFile(args.path)
|
58
|
-
}
|
59
|
-
|
60
|
-
contents += customMedia
|
61
|
-
}
|
62
|
-
|
63
|
-
if (tmpFile && contents) {
|
64
|
-
await Deno.writeTextFile(tmpFile, contents)
|
65
|
-
}
|
66
|
-
|
67
|
-
if (isCssModule) {
|
68
|
-
cmd = cmd.concat(['--css-modules', '--css-modules-pattern', `[local]${hash}`])
|
69
|
-
}
|
70
|
-
|
71
|
-
const p = Deno.run({
|
72
|
-
cmd: [...cmd, tmpFile || args.path],
|
73
|
-
stdout: 'piped',
|
74
|
-
stderr: 'piped'
|
75
|
-
})
|
76
|
-
|
77
|
-
const { code } = await p.status()
|
78
|
-
const rawOutput = await p.output()
|
79
|
-
const rawError = await p.stderrOutput()
|
80
|
-
|
81
|
-
// Even though Deno docs say that reading the outputs (above) closes their pipes, warnings
|
82
|
-
// are raised during tests that the child process have not been closed. So we manually close
|
83
|
-
// here.
|
84
|
-
p.close()
|
85
|
-
|
86
|
-
// Success!
|
87
|
-
if (code === 0) {
|
88
|
-
let contents = new TextDecoder().decode(rawOutput)
|
89
|
-
if (isCssModule) {
|
90
|
-
contents = JSON.parse(contents)
|
91
|
-
}
|
92
|
-
|
93
|
-
// If stylesheet is imported from JS, then we return JS code that appends the stylesheet
|
94
|
-
// in a <style> in the <head> of the page, and if the stylesheet is a CSS module, it
|
95
|
-
// exports a plain object of class names.
|
96
|
-
if (args.pluginData?.importedFromJs) {
|
97
|
-
const code = isCssModule ? contents.code : contents
|
98
|
-
const mod = [
|
99
|
-
`let e = document.querySelector('#_${hash}');`,
|
100
|
-
'if (!e) {',
|
101
|
-
"e = document.createElement('style');",
|
102
|
-
`e.id = '_${hash}';`,
|
103
|
-
'document.head.appendChild(e);',
|
104
|
-
`e.appendChild(document.createTextNode(\`${code}\`));`,
|
105
|
-
'}'
|
106
|
-
]
|
107
|
-
|
108
|
-
if (isCssModule) {
|
109
|
-
const classes = {}
|
110
|
-
for (const key in contents.exports) {
|
111
|
-
if (Object.hasOwnProperty.call(contents.exports, key)) {
|
112
|
-
classes[key] = contents.exports[key].name
|
113
|
-
}
|
114
|
-
}
|
115
|
-
mod.push(`export default ${JSON.stringify(classes)};`)
|
116
|
-
}
|
117
|
-
|
118
|
-
// We are importing from JS, so return the entire result from LightningCSS via the js
|
119
|
-
// loader.
|
120
|
-
return {
|
121
|
-
resolveDir: cwd,
|
122
|
-
loader: 'js',
|
123
|
-
contents: mod.join('')
|
124
|
-
}
|
125
|
-
}
|
126
|
-
|
127
|
-
return { loader: 'css', contents: isCssModule ? contents.code : contents }
|
128
|
-
} else {
|
129
|
-
const errorString = new TextDecoder().decode(rawError)
|
130
|
-
throw errorString
|
131
|
-
}
|
132
|
-
}
|
133
|
-
}
|
134
|
-
]
|
135
|
-
})
|
136
|
-
|
137
|
-
async function digest(value) {
|
138
|
-
value = new TextEncoder().encode(value)
|
139
|
-
const view = new DataView(await crypto.subtle.digest('SHA-1', value))
|
140
|
-
|
141
|
-
let hexCodes = ''
|
142
|
-
for (let index = 0; index < view.byteLength; index += 4) {
|
143
|
-
hexCodes += view.getUint32(index).toString(16).padStart(8, '0')
|
144
|
-
}
|
145
|
-
|
146
|
-
return hexCodes.slice(0, 8)
|
147
|
-
}
|
148
|
-
|
149
|
-
async function isViewComponent(path) {
|
150
|
-
const fileName = basename(path)
|
151
|
-
const dirName = dirname(path)
|
152
|
-
|
153
|
-
return (
|
154
|
-
(fileName === 'component.module.css' && (await fileExists(join(dirName, 'component.rb')))) ||
|
155
|
-
(fileName.endsWith('_component.module.css') &&
|
156
|
-
(await fileExists(join(dirName, fileName.replace(/\.module\.css$/, '.rb')))))
|
157
|
-
)
|
158
|
-
}
|
159
|
-
|
160
|
-
function cssModulesProxyTemplate(hash) {
|
161
|
-
return [
|
162
|
-
`export default new Proxy( {}, {`,
|
163
|
-
` get(target, prop, receiver) {`,
|
164
|
-
` if (prop in target || typeof prop === 'symbol') {`,
|
165
|
-
` return Reflect.get(target, prop, receiver)`,
|
166
|
-
` } else {`,
|
167
|
-
` return prop + '${hash}'`,
|
168
|
-
` }`,
|
169
|
-
` }`,
|
170
|
-
`})`
|
171
|
-
].join('')
|
172
|
-
}
|
@@ -1,46 +0,0 @@
|
|
1
|
-
import setup from './setup_plugin.js'
|
2
|
-
|
3
|
-
// Export environment variables as named exports only. You can also import from `env:ENV_VAR_NAME`,
|
4
|
-
// which will return the value of the environment variable as the default export. This allows you to
|
5
|
-
// safely import a variable regardless of its existence.
|
6
|
-
export default setup('env', () => {
|
7
|
-
return [
|
8
|
-
{
|
9
|
-
type: 'onResolve',
|
10
|
-
filter: /^env(:.+)?$/,
|
11
|
-
callback({ path }) {
|
12
|
-
return { path, namespace: 'env' }
|
13
|
-
}
|
14
|
-
},
|
15
|
-
|
16
|
-
{
|
17
|
-
type: 'onLoad',
|
18
|
-
filter: /.*/,
|
19
|
-
namespace: 'env',
|
20
|
-
callback({ path }) {
|
21
|
-
if (path.includes(':')) {
|
22
|
-
const name = Deno.env.get(path.split(':')[1])
|
23
|
-
|
24
|
-
return {
|
25
|
-
loader: 'js',
|
26
|
-
contents: name ? `export default '${name}'` : `export default ${name}`
|
27
|
-
}
|
28
|
-
}
|
29
|
-
|
30
|
-
const env = Deno.env.toObject()
|
31
|
-
const contents = []
|
32
|
-
|
33
|
-
for (const key in env) {
|
34
|
-
if (Object.hasOwnProperty.call(env, key)) {
|
35
|
-
contents.push(`export const ${key} = '${env[key]}'`)
|
36
|
-
}
|
37
|
-
}
|
38
|
-
|
39
|
-
return {
|
40
|
-
loader: 'js',
|
41
|
-
contents: contents.join(';')
|
42
|
-
}
|
43
|
-
}
|
44
|
-
}
|
45
|
-
]
|
46
|
-
})
|
@@ -1,53 +0,0 @@
|
|
1
|
-
import setup from './setup_plugin.js'
|
2
|
-
|
3
|
-
export default setup('httpBundle', () => {
|
4
|
-
return [
|
5
|
-
{
|
6
|
-
type: 'onResolve',
|
7
|
-
filter: /^https?:\/\//,
|
8
|
-
callback(args) {
|
9
|
-
let queryParams, suffix
|
10
|
-
if (args.path.includes('?')) {
|
11
|
-
const [path, query] = args.path.split('?')
|
12
|
-
queryParams = new URLSearchParams(query)
|
13
|
-
suffix = `?${query}`
|
14
|
-
args.path = path
|
15
|
-
}
|
16
|
-
|
17
|
-
if (queryParams?.has('bundle')) {
|
18
|
-
return { path: args.path, namespace: 'httpBundle', suffix }
|
19
|
-
} else {
|
20
|
-
return { external: true }
|
21
|
-
}
|
22
|
-
}
|
23
|
-
},
|
24
|
-
|
25
|
-
// Intercept all import paths inside downloaded files and resolve them against the original URL.
|
26
|
-
{
|
27
|
-
type: 'onResolve',
|
28
|
-
filter: /.*/,
|
29
|
-
namespace: 'httpBundle',
|
30
|
-
callback(args) {
|
31
|
-
return {
|
32
|
-
path: new URL(args.path, args.importer).toString(),
|
33
|
-
namespace: 'httpBundle'
|
34
|
-
}
|
35
|
-
}
|
36
|
-
},
|
37
|
-
|
38
|
-
// Download and return the content.
|
39
|
-
//
|
40
|
-
// TODO: cache this!
|
41
|
-
{
|
42
|
-
type: 'onLoad',
|
43
|
-
filter: /.*/,
|
44
|
-
namespace: 'httpBundle',
|
45
|
-
async callback(args) {
|
46
|
-
const textResponse = await fetch(args.path)
|
47
|
-
const contents = await textResponse.text()
|
48
|
-
|
49
|
-
return { contents }
|
50
|
-
}
|
51
|
-
}
|
52
|
-
]
|
53
|
-
})
|
@@ -1,59 +0,0 @@
|
|
1
|
-
import { join } from 'std/path/mod.ts'
|
2
|
-
import { parseFromString } from 'import-maps/resolve'
|
3
|
-
|
4
|
-
const baseURL = new URL('file://')
|
5
|
-
|
6
|
-
class ImportMapError extends Error {
|
7
|
-
constructor(fileName, ...params) {
|
8
|
-
super(...params)
|
9
|
-
|
10
|
-
if (Error.captureStackTrace) {
|
11
|
-
Error.captureStackTrace(this, ImportMapError)
|
12
|
-
}
|
13
|
-
|
14
|
-
this.name = 'ImportMapError'
|
15
|
-
this.file = fileName
|
16
|
-
}
|
17
|
-
}
|
18
|
-
|
19
|
-
export function readImportMap(fileName, rootDir) {
|
20
|
-
let importMap
|
21
|
-
|
22
|
-
if (fileName) {
|
23
|
-
importMap = readFile(fileName, rootDir, true)
|
24
|
-
} else {
|
25
|
-
fileName = ['config/import_map.json', 'config/import_map.js'].find(f => {
|
26
|
-
const result = readFile(f, rootDir)
|
27
|
-
if (result) {
|
28
|
-
importMap = result
|
29
|
-
return true
|
30
|
-
}
|
31
|
-
})
|
32
|
-
}
|
33
|
-
|
34
|
-
return importMap
|
35
|
-
}
|
36
|
-
|
37
|
-
function readFile(file, rootDir, required = false) {
|
38
|
-
let contents = null
|
39
|
-
|
40
|
-
try {
|
41
|
-
contents = Deno.readTextFileSync(join(rootDir, file))
|
42
|
-
} catch (error) {
|
43
|
-
if (required) {
|
44
|
-
throw new ImportMapError(file, error.message, { cause: error })
|
45
|
-
}
|
46
|
-
}
|
47
|
-
|
48
|
-
if (contents === null) return null
|
49
|
-
|
50
|
-
try {
|
51
|
-
if (file.endsWith('.js')) {
|
52
|
-
contents = JSON.stringify(eval(contents)(Deno.env.get('RAILS_ENV')))
|
53
|
-
}
|
54
|
-
|
55
|
-
return parseFromString(contents, baseURL)
|
56
|
-
} catch (error) {
|
57
|
-
throw new ImportMapError(file, error.message, { cause: error })
|
58
|
-
}
|
59
|
-
}
|
@@ -1,205 +0,0 @@
|
|
1
|
-
import { join, resolve } from 'std/path/mod.ts'
|
2
|
-
import { resolve as resolveFromImportMap } from 'import-maps/resolve'
|
3
|
-
import { cache } from 'cache'
|
4
|
-
|
5
|
-
import setup from './setup_plugin.js'
|
6
|
-
|
7
|
-
const importKinds = ['import-statement', 'dynamic-import', 'require-call', 'import-rule']
|
8
|
-
|
9
|
-
export default setup('resolve', (build, options) => {
|
10
|
-
const { runtimeDir, importMap } = options
|
11
|
-
const cwd = build.initialOptions.absWorkingDir
|
12
|
-
const runtimeCwdAlias = `${cwd}/proscenium-runtime`
|
13
|
-
let bundled = false
|
14
|
-
|
15
|
-
const env = Deno.env.get('RAILS_ENV')
|
16
|
-
const isProd = env === 'production'
|
17
|
-
|
18
|
-
return [
|
19
|
-
{
|
20
|
-
// Filters for imports starting with `url:http://` or `url:https://`; returning the path
|
21
|
-
// without the `url:` prefix, and a namespace of 'url`
|
22
|
-
type: 'onResolve',
|
23
|
-
filter: /^url:https?:\/\//,
|
24
|
-
callback(args) {
|
25
|
-
return {
|
26
|
-
path: args.path.slice(4),
|
27
|
-
namespace: 'url'
|
28
|
-
}
|
29
|
-
}
|
30
|
-
},
|
31
|
-
|
32
|
-
{
|
33
|
-
type: 'onResolve',
|
34
|
-
filter: /.*/,
|
35
|
-
namespace: 'url',
|
36
|
-
callback(args) {
|
37
|
-
if (!isBareModule(args.path)) {
|
38
|
-
return {
|
39
|
-
path: new URL(args.path, args.importer).toString(),
|
40
|
-
namespace: 'url'
|
41
|
-
}
|
42
|
-
}
|
43
|
-
}
|
44
|
-
},
|
45
|
-
|
46
|
-
{
|
47
|
-
type: 'onLoad',
|
48
|
-
filter: /.*/,
|
49
|
-
namespace: 'url',
|
50
|
-
async callback(args) {
|
51
|
-
const file = await cache(args.path)
|
52
|
-
const contents = await Deno.readTextFile(file.path)
|
53
|
-
|
54
|
-
return { contents }
|
55
|
-
}
|
56
|
-
},
|
57
|
-
|
58
|
-
{
|
59
|
-
type: 'onResolve',
|
60
|
-
filter: /.*/,
|
61
|
-
async callback(args) {
|
62
|
-
if (args.path.includes('?')) {
|
63
|
-
const [path, query] = args.path.split('?')
|
64
|
-
args.path = path
|
65
|
-
args.suffix = `?${query}`
|
66
|
-
args.queryParams = new URLSearchParams(query)
|
67
|
-
} else if (options.cacheQueryString && options.cacheQueryString !== '') {
|
68
|
-
args.suffix = `?${options.cacheQueryString}`
|
69
|
-
}
|
70
|
-
|
71
|
-
// Mark remote modules as external. If not css, then the path is prefixed with "url:", which
|
72
|
-
// is then handled by the Url Middleware.
|
73
|
-
if (
|
74
|
-
!args.importer.endsWith('.css') &&
|
75
|
-
(args.path.startsWith('http://') || args.path.startsWith('https://'))
|
76
|
-
) {
|
77
|
-
return { path: `/url:${encodeURIComponent(args.path)}`, external: true }
|
78
|
-
}
|
79
|
-
|
80
|
-
// Rewrite the path to the actual runtime directory.
|
81
|
-
if (args.path.startsWith(runtimeCwdAlias)) {
|
82
|
-
return { path: join(runtimeDir, args.path.slice(runtimeCwdAlias.length)) }
|
83
|
-
}
|
84
|
-
|
85
|
-
// Everything else is unbundled.
|
86
|
-
if (importKinds.includes(args.kind)) {
|
87
|
-
return await unbundleImport(args)
|
88
|
-
}
|
89
|
-
}
|
90
|
-
}
|
91
|
-
]
|
92
|
-
|
93
|
-
// Resolve the given `params.path` to a path relative to the Rails root.
|
94
|
-
//
|
95
|
-
// Examples:
|
96
|
-
// 'react' -> '/.../node_modules/react/index.js'
|
97
|
-
// './my_module' -> '/.../app/my_module.js'
|
98
|
-
// '/app/my_module' -> '/.../app/my_module.js'
|
99
|
-
async function unbundleImport(params) {
|
100
|
-
const result = { path: params.path, suffix: params.suffix }
|
101
|
-
|
102
|
-
if (importMap) {
|
103
|
-
let baseURL
|
104
|
-
if (params.importer.startsWith('https://') || params.importer.startsWith('http://')) {
|
105
|
-
baseURL = new URL(params.importer)
|
106
|
-
} else {
|
107
|
-
baseURL = new URL(params.importer.slice(cwd.length), 'file://')
|
108
|
-
}
|
109
|
-
|
110
|
-
const { matched, resolvedImport } = resolveFromImportMap(params.path, importMap, baseURL)
|
111
|
-
|
112
|
-
if (matched) {
|
113
|
-
if (resolvedImport.protocol === 'file:') {
|
114
|
-
params.path = resolvedImport.pathname
|
115
|
-
} else {
|
116
|
-
if (params.importer.endsWith('.css')) {
|
117
|
-
return { path: resolvedImport.href, external: true }
|
118
|
-
}
|
119
|
-
|
120
|
-
return { path: `/url:${encodeURIComponent(resolvedImport.href)}`, external: true }
|
121
|
-
}
|
122
|
-
}
|
123
|
-
}
|
124
|
-
|
125
|
-
// Absolute path - append to current working dir.
|
126
|
-
if (params.path.startsWith('/')) {
|
127
|
-
result.path = resolve(cwd, params.path.slice(1))
|
128
|
-
}
|
129
|
-
|
130
|
-
const resOptions = {
|
131
|
-
// If path is a bare module (node_modules), and resolveDir is the Proscenium runtime dir, or
|
132
|
-
// is the current working dir, then use `cwd` as the `resolveDir`, otherwise pass it through
|
133
|
-
// as is. This ensures that nested node_modules are resolved correctly.
|
134
|
-
resolveDir:
|
135
|
-
isBareModule(result.path) &&
|
136
|
-
(!params.resolveDir.startsWith(cwd) || params.resolveDir.startsWith(runtimeDir))
|
137
|
-
? cwd
|
138
|
-
: params.resolveDir,
|
139
|
-
kind: params.kind,
|
140
|
-
pluginData: {
|
141
|
-
// We use this property later on, as we should ignore this resolution call.
|
142
|
-
isResolvingPath: true
|
143
|
-
}
|
144
|
-
}
|
145
|
-
|
146
|
-
// Resolve the path using esbuild's internal resolution. This allows us to import node packages
|
147
|
-
// and extension-less paths without custom code, as esbuild with resolve them for us.
|
148
|
-
const resolveResult = await build.resolve(result.path, resOptions)
|
149
|
-
|
150
|
-
// Simple return the resolved result if we have an error. Usually happens when module is not
|
151
|
-
// found.
|
152
|
-
if (resolveResult.errors.length > 0) return resolveResult
|
153
|
-
|
154
|
-
// If 'bundle-all' queryParam is defined, return the resolveResult.
|
155
|
-
if (bundled || params.queryParams?.has('bundle-all')) {
|
156
|
-
bundled = true
|
157
|
-
return { ...resolveResult, suffix: '?bundle-all' }
|
158
|
-
}
|
159
|
-
|
160
|
-
// If 'bundle' queryParam is defined, return the resolveResult.
|
161
|
-
if (params.queryParams?.has('bundle')) {
|
162
|
-
return { ...resolveResult, suffix: '?bundle' }
|
163
|
-
}
|
164
|
-
|
165
|
-
if (resolveResult.path.startsWith(runtimeDir)) {
|
166
|
-
result.path = '/proscenium-runtime' + resolveResult.path.slice(runtimeDir.length)
|
167
|
-
} else if (!resolveResult.path.startsWith(cwd) && !isProd) {
|
168
|
-
// Resolved path is not in the current working directory. It could be linked to a file outside
|
169
|
-
// the CWD, or it's just invalid. If not in production, return as an outsideRoot namespaced,
|
170
|
-
// and externally suffixed path. This lets the Rails Proscenium::Middleware::OutsideRoot
|
171
|
-
// handle the import.
|
172
|
-
return {
|
173
|
-
...resolveResult,
|
174
|
-
namespace: 'outsideRoot',
|
175
|
-
path: `${resolveResult.path}?outsideRoot`,
|
176
|
-
external: true
|
177
|
-
}
|
178
|
-
} else {
|
179
|
-
result.path = resolveResult.path.slice(cwd.length)
|
180
|
-
}
|
181
|
-
|
182
|
-
result.sideEffects = resolveResult.sideEffects
|
183
|
-
|
184
|
-
if (
|
185
|
-
params.path.endsWith('.css') &&
|
186
|
-
params.kind === 'import-statement' &&
|
187
|
-
/\.jsx?$/.test(params.importer)
|
188
|
-
) {
|
189
|
-
// We're importing a CSS file from JS(X).
|
190
|
-
return { ...resolveResult, pluginData: { importedFromJs: true } }
|
191
|
-
} else {
|
192
|
-
result.external = true
|
193
|
-
}
|
194
|
-
|
195
|
-
if (result.suffix && result.suffix !== '') {
|
196
|
-
result.path = `${result.path}${result.suffix}`
|
197
|
-
}
|
198
|
-
|
199
|
-
return result
|
200
|
-
}
|
201
|
-
})
|
202
|
-
|
203
|
-
function isBareModule(mod) {
|
204
|
-
return !mod.startsWith('/') && !mod.startsWith('.')
|
205
|
-
}
|
@@ -1,45 +0,0 @@
|
|
1
|
-
export default (pluginName, pluginFn) => {
|
2
|
-
return (options = {}) => ({
|
3
|
-
name: pluginName,
|
4
|
-
async setup(build) {
|
5
|
-
const callbacks = await pluginFn(build, options)
|
6
|
-
|
7
|
-
callbacks.forEach(({ type, callback, filter, namespace }) => {
|
8
|
-
if (type === 'onResolve') {
|
9
|
-
build.onResolve({ filter, namespace }, async params => {
|
10
|
-
if (params.pluginData?.isResolvingPath) return
|
11
|
-
|
12
|
-
let results
|
13
|
-
|
14
|
-
if (options.debug) {
|
15
|
-
console.debug()
|
16
|
-
console.group(`plugin(${pluginName}):onResolve`, { filter, namespace })
|
17
|
-
console.debug('params:', params)
|
18
|
-
|
19
|
-
try {
|
20
|
-
results = await callback(params)
|
21
|
-
console.debug('results:', results)
|
22
|
-
} finally {
|
23
|
-
console.groupEnd()
|
24
|
-
}
|
25
|
-
} else {
|
26
|
-
results = await callback(params)
|
27
|
-
}
|
28
|
-
|
29
|
-
return results
|
30
|
-
})
|
31
|
-
} else if (type === 'onLoad') {
|
32
|
-
build.onLoad({ filter, namespace }, async params => {
|
33
|
-
options.debug && console.debug(`plugin(${pluginName}):onLoad`, { params })
|
34
|
-
|
35
|
-
const results = await callback(params)
|
36
|
-
|
37
|
-
options.debug && console.debug(`plugin(${pluginName}):onLoad`, { results })
|
38
|
-
|
39
|
-
return results
|
40
|
-
})
|
41
|
-
}
|
42
|
-
})
|
43
|
-
}
|
44
|
-
})
|
45
|
-
}
|
@@ -1,24 +0,0 @@
|
|
1
|
-
import { basename } from 'std/path/mod.ts'
|
2
|
-
import { transformAsync } from '@babel/core'
|
3
|
-
import solid from 'babel-preset-solid'
|
4
|
-
|
5
|
-
import { setup } from './setup_plugin.js'
|
6
|
-
|
7
|
-
export default setup('solidjs', () => {
|
8
|
-
return [
|
9
|
-
{
|
10
|
-
type: 'onLoad',
|
11
|
-
filter: /\.jsx$/,
|
12
|
-
async callback(args) {
|
13
|
-
const source = await Deno.readTextFile(args.path)
|
14
|
-
|
15
|
-
const { code } = await transformAsync(source, {
|
16
|
-
presets: [solid],
|
17
|
-
filename: basename(args.path)
|
18
|
-
})
|
19
|
-
|
20
|
-
return { contents: code, loader: 'js' }
|
21
|
-
}
|
22
|
-
}
|
23
|
-
]
|
24
|
-
})
|
@@ -1,14 +0,0 @@
|
|
1
|
-
import { join } from 'std/path/mod.ts'
|
2
|
-
|
3
|
-
import compile from './esbuild.js'
|
4
|
-
|
5
|
-
const root = join(Deno.cwd(), 'test', 'internal')
|
6
|
-
const lightningcssBin = join(Deno.cwd(), 'bin', 'lightningcss')
|
7
|
-
|
8
|
-
Deno.bench('esbuild js', async () => {
|
9
|
-
await compile(['lib/foo.js'], { root, lightningcssBin })
|
10
|
-
})
|
11
|
-
|
12
|
-
Deno.bench('esbuild css', async () => {
|
13
|
-
await compile(['lib/foo.css'], { root, lightningcssBin })
|
14
|
-
})
|