proscenium 0.1.0.alpha2-arm64-darwin → 0.1.0.alpha4-arm64-darwin

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +216 -32
  3. data/app/components/react_component.rb +10 -3
  4. data/bin/esbuild +0 -0
  5. data/bin/{parcel_css → lightningcss} +0 -0
  6. data/config/routes.rb +1 -1
  7. data/lib/proscenium/compilers/esbuild/compile_error.js +147 -0
  8. data/lib/proscenium/compilers/esbuild/css/postcss.js +67 -0
  9. data/lib/proscenium/compilers/esbuild/css_plugin.js +176 -0
  10. data/lib/proscenium/compilers/esbuild/env_plugin.js +6 -4
  11. data/lib/proscenium/compilers/esbuild/http_bundle_plugin.js +53 -0
  12. data/lib/proscenium/compilers/esbuild/import_map.js +59 -0
  13. data/lib/proscenium/compilers/esbuild/resolve_plugin.js +67 -65
  14. data/lib/proscenium/compilers/esbuild/setup_plugin.js +32 -22
  15. data/lib/proscenium/{cli → compilers}/esbuild/solidjs_plugin.js +5 -4
  16. data/lib/proscenium/compilers/esbuild.bench.js +7 -3
  17. data/lib/proscenium/compilers/esbuild.js +93 -19
  18. data/lib/proscenium/css_module.rb +48 -6
  19. data/lib/proscenium/helper.rb +5 -3
  20. data/lib/proscenium/middleware/base.rb +13 -3
  21. data/lib/proscenium/middleware/esbuild.rb +17 -1
  22. data/lib/proscenium/middleware/lightningcss.rb +64 -0
  23. data/lib/proscenium/middleware.rb +7 -19
  24. data/lib/proscenium/phlex.rb +36 -0
  25. data/lib/proscenium/railtie.rb +10 -20
  26. data/lib/proscenium/runtime/auto_reload.js +3 -3
  27. data/lib/proscenium/side_load.rb +14 -40
  28. data/lib/proscenium/utils.js +8 -0
  29. data/lib/proscenium/version.rb +1 -1
  30. data/lib/proscenium/view_component.rb +5 -1
  31. data/lib/proscenium.rb +1 -0
  32. metadata +27 -19
  33. data/lib/proscenium/cli/argument_error.js +0 -24
  34. data/lib/proscenium/cli/builders/index.js +0 -1
  35. data/lib/proscenium/cli/builders/javascript.js +0 -45
  36. data/lib/proscenium/cli/builders/react.js +0 -60
  37. data/lib/proscenium/cli/builders/solid.js +0 -46
  38. data/lib/proscenium/cli/esbuild/env_plugin.js +0 -21
  39. data/lib/proscenium/cli/esbuild/resolve_plugin.js +0 -136
  40. data/lib/proscenium/cli/js_builder.js +0 -194
  41. data/lib/proscenium/cli/solid.js +0 -15
  42. data/lib/proscenium/cli/utils.js +0 -93
  43. data/lib/proscenium/middleware/parcel_css.rb +0 -37
  44. data/lib/proscenium/runtime/component_manager/index.js +0 -27
  45. data/lib/proscenium/runtime/component_manager/render_component.js +0 -40
  46. data/lib/proscenium/runtime/import_css.js +0 -46
@@ -0,0 +1,176 @@
1
+ import { crypto } from 'std/crypto/mod.ts'
2
+ import { join, resolve, dirname, basename, fromFileUrl } 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 => {
9
+ const cwd = build.initialOptions.absWorkingDir
10
+ const lightningcssBin = resolve(
11
+ dirname(fromFileUrl(import.meta.url)),
12
+ '../../../../bin/lightningcss'
13
+ )
14
+
15
+ let customMedia
16
+ try {
17
+ customMedia = await Deno.readTextFile(join(cwd, 'config', 'custom_media_queries.css'))
18
+ } catch {
19
+ // do nothing, as we don't require custom media.
20
+ }
21
+
22
+ return [
23
+ {
24
+ type: 'onLoad',
25
+ filter: /\.css$/,
26
+ namespace: 'file',
27
+ async callback(args) {
28
+ const hash = await digest(args.path.slice(cwd.length))
29
+ const isCssModule = args.path.endsWith('.module.css')
30
+
31
+ // If path is a CSS module, imported from JS, and a side-loaded ViewComponent stylesheet,
32
+ // simply return a JS proxy of the class names. The stylesheet itself will have already been
33
+ // side loaded. This avoids compiling the CSS all over again.
34
+ if (isCssModule && args.pluginData?.importedFromJs && (await isViewComponent(args.path))) {
35
+ return {
36
+ resolveDir: cwd,
37
+ loader: 'js',
38
+ contents: cssModulesProxyTemplate(hash)
39
+ }
40
+ }
41
+
42
+ let cmd = [
43
+ lightningcssBin,
44
+ '--nesting',
45
+ '--error-recovery',
46
+ args.pluginData?.importedFromJs && '--minify',
47
+ '--targets',
48
+ '>= 0.25%'
49
+ ].filter(Boolean)
50
+
51
+ // This will process the CSS with Postcss only if it needs to.
52
+ let [tmpFile, contents] = await postcss(cwd, args.path)
53
+
54
+ // As custom media are defined in their own file, we have to append the file contents to our
55
+ // stylesheet, so that the custom media can be used.
56
+ if (customMedia) {
57
+ cmd.push('--custom-media')
58
+
59
+ if (!tmpFile && !contents) {
60
+ tmpFile = await Deno.makeTempFile()
61
+ contents = await Deno.readTextFile(args.path)
62
+ }
63
+
64
+ contents += customMedia
65
+ }
66
+
67
+ if (tmpFile && contents) {
68
+ await Deno.writeTextFile(tmpFile, contents)
69
+ }
70
+
71
+ if (isCssModule) {
72
+ cmd = cmd.concat(['--css-modules', '--css-modules-pattern', `[local]${hash}`])
73
+ }
74
+
75
+ const p = Deno.run({
76
+ cmd: [...cmd, tmpFile || args.path],
77
+ stdout: 'piped',
78
+ stderr: 'piped'
79
+ })
80
+
81
+ const { code } = await p.status()
82
+ const rawOutput = await p.output()
83
+ const rawError = await p.stderrOutput()
84
+
85
+ // Even though Deno docs say that reading the outputs (above) closes their pipes, warnings
86
+ // are raised during tests that the child process have not been closed. So we manually close
87
+ // here.
88
+ p.close()
89
+
90
+ // Success!
91
+ if (code === 0) {
92
+ let contents = new TextDecoder().decode(rawOutput)
93
+ if (isCssModule) {
94
+ contents = JSON.parse(contents)
95
+ }
96
+
97
+ // If stylesheet is imported from JS, then we return JS code that appends the stylesheet
98
+ // in a <style> in the <head> of the page, and if the stylesheet is a CSS module, it
99
+ // exports a plain object of class names.
100
+ if (args.pluginData?.importedFromJs) {
101
+ const code = isCssModule ? contents.code : contents
102
+ const mod = [
103
+ `let e = document.querySelector('#_${hash}');`,
104
+ 'if (!e) {',
105
+ "e = document.createElement('style');",
106
+ `e.id = '_${hash}';`,
107
+ 'document.head.appendChild(e);',
108
+ `e.appendChild(document.createTextNode(\`${code}\`));`,
109
+ '}'
110
+ ]
111
+
112
+ if (isCssModule) {
113
+ const classes = {}
114
+ for (const key in contents.exports) {
115
+ if (Object.hasOwnProperty.call(contents.exports, key)) {
116
+ classes[key] = contents.exports[key].name
117
+ }
118
+ }
119
+ mod.push(`export default ${JSON.stringify(classes)};`)
120
+ }
121
+
122
+ // We are importing from JS, so return the entire result from LightningCSS via the js
123
+ // loader.
124
+ return {
125
+ resolveDir: cwd,
126
+ loader: 'js',
127
+ contents: mod.join('')
128
+ }
129
+ }
130
+
131
+ return { loader: 'css', contents: isCssModule ? contents.code : contents }
132
+ } else {
133
+ const errorString = new TextDecoder().decode(rawError)
134
+ throw errorString
135
+ }
136
+ }
137
+ }
138
+ ]
139
+ })
140
+
141
+ async function digest(value) {
142
+ value = new TextEncoder().encode(value)
143
+ const view = new DataView(await crypto.subtle.digest('SHA-1', value))
144
+
145
+ let hexCodes = ''
146
+ for (let index = 0; index < view.byteLength; index += 4) {
147
+ hexCodes += view.getUint32(index).toString(16).padStart(8, '0')
148
+ }
149
+
150
+ return hexCodes.slice(0, 8)
151
+ }
152
+
153
+ async function isViewComponent(path) {
154
+ const fileName = basename(path)
155
+ const dirName = dirname(path)
156
+
157
+ return (
158
+ (fileName === 'component.module.css' && (await fileExists(join(dirName, 'component.rb')))) ||
159
+ (fileName.endsWith('_component.module.css') &&
160
+ (await fileExists(join(dirName, fileName.replace(/\.module\.css$/, '.rb')))))
161
+ )
162
+ }
163
+
164
+ function cssModulesProxyTemplate(hash) {
165
+ return [
166
+ `export default new Proxy( {}, {`,
167
+ ` get(target, prop, receiver) {`,
168
+ ` if (prop in target || typeof prop === 'symbol') {`,
169
+ ` return Reflect.get(target, prop, receiver)`,
170
+ ` } else {`,
171
+ ` return prop + '${hash}'`,
172
+ ` }`,
173
+ ` }`,
174
+ `})`
175
+ ].join('')
176
+ }
@@ -1,15 +1,17 @@
1
1
  import setup from './setup_plugin.js'
2
2
 
3
3
  export default setup('env', () => {
4
- return {
5
- onResolve: {
4
+ return [
5
+ {
6
+ type: 'onResolve',
6
7
  filter: /^env$/,
7
8
  callback({ path }) {
8
9
  return { path, namespace: 'env' }
9
10
  }
10
11
  },
11
12
 
12
- onLoad: {
13
+ {
14
+ type: 'onLoad',
13
15
  filter: /.*/,
14
16
  namespace: 'env',
15
17
  callback() {
@@ -17,5 +19,5 @@ export default setup('env', () => {
17
19
  return { loader: 'json', contents: JSON.stringify(env) }
18
20
  }
19
21
  }
20
- }
22
+ ]
21
23
  })
@@ -0,0 +1,53 @@
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
+ })
@@ -0,0 +1,59 @@
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,36 +1,50 @@
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
- const baseURL = new URL('file://')
10
- const importKinds = ['import-statement', 'dynamic-import', 'require-call']
6
+ const importKinds = ['import-statement', 'dynamic-import', 'require-call', 'import-rule']
11
7
 
12
8
  export default setup('resolve', (build, options) => {
13
- const { runtimeDir } = options
9
+ const { runtimeDir, importMap } = options
14
10
  const cwd = build.initialOptions.absWorkingDir
15
11
  const runtimeCwdAlias = `${cwd}/proscenium-runtime`
16
- const importMap = readImportMap()
12
+ let bundled = false
17
13
 
18
- return {
19
- onResolve: {
14
+ return [
15
+ {
16
+ type: 'onResolve',
20
17
  filter: /.*/,
21
18
  async callback(args) {
22
- // Remote modules
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
- if (args.path.startsWith('@proscenium/')) {
29
- return {
30
- path: `${args.path.replace(/^@proscenium/, '/proscenium-runtime')}/index.js`,
31
- external: true
32
- }
33
- }
32
+ // if (args.path.startsWith('@proscenium/')) {
33
+ // const result = { suffix: args.suffix }
34
+
35
+ // if (args.queryParams?.has('bundle-all')) {
36
+ // bundled = true
37
+ // }
38
+
39
+ // if (bundled || args.queryParams?.has('bundle')) {
40
+ // result.path = join(runtimeDir, `${args.path.replace(/^@proscenium/, '')}/index.js`)
41
+ // } else {
42
+ // result.path = `${args.path.replace(/^@proscenium/, '/proscenium-runtime')}/index.js`
43
+ // result.external = true
44
+ // }
45
+
46
+ // return result
47
+ // }
34
48
 
35
49
  if (args.path.startsWith(runtimeCwdAlias)) {
36
50
  return { path: join(runtimeDir, args.path.slice(runtimeCwdAlias.length)) }
@@ -41,33 +55,8 @@ export default setup('resolve', (build, options) => {
41
55
  return await unbundleImport(args)
42
56
  }
43
57
  }
44
- },
45
-
46
- onLoad: {
47
- filter: /.*/,
48
- namespace: 'importStylesheet',
49
- callback(args) {
50
- const result = {
51
- resolveDir: cwd,
52
- loader: 'js'
53
- }
54
-
55
- if (args.path.endsWith('.module.css')) {
56
- result.contents = `
57
- import { importCssModule } from '/proscenium-runtime/import_css.js'
58
- export default await importCssModule('${args.path}')
59
- `
60
- } else {
61
- result.contents = `
62
- import { appendStylesheet } from '/proscenium-runtime/import_css.js'
63
- appendStylesheet('${args.path}')
64
- `
65
- }
66
-
67
- return result
68
- }
69
58
  }
70
- }
59
+ ]
71
60
 
72
61
  // Resolve the given `params.path` to a path relative to the Rails root.
73
62
  //
@@ -76,10 +65,12 @@ export default setup('resolve', (build, options) => {
76
65
  // './my_module' -> '/.../app/my_module.js'
77
66
  // '/app/my_module' -> '/.../app/my_module.js'
78
67
  async function unbundleImport(params) {
79
- const result = { path: params.path }
68
+ const result = { path: params.path, suffix: params.suffix }
80
69
 
81
70
  if (importMap) {
71
+ const baseURL = new URL(params.importer.slice(cwd.length), 'file://')
82
72
  const { matched, resolvedImport } = resolveFromImportMap(params.path, importMap, baseURL)
73
+
83
74
  if (matched) {
84
75
  if (resolvedImport.protocol === 'file:') {
85
76
  params.path = resolvedImport.pathname
@@ -97,18 +88,43 @@ export default setup('resolve', (build, options) => {
97
88
  // Resolve the path using esbuild's internal resolution. This allows us to import node packages
98
89
  // and extension-less paths without custom code, as esbuild with resolve them for us.
99
90
  const resolveResult = await build.resolve(result.path, {
100
- resolveDir: params.resolveDir,
91
+ // If path is a bare module (node_modules), and resolveDir is the Proscenium runtime dir, then
92
+ // use `cwd` as the `resolveDir`, otherwise pass it through as is. This ensures that nested
93
+ // node_modules are resolved correctly.
94
+ resolveDir:
95
+ isBareModule(result.path) && params.resolveDir.startsWith(runtimeDir)
96
+ ? cwd
97
+ : params.resolveDir,
101
98
  pluginData: {
102
99
  // We use this property later on, as we should ignore this resolution call.
103
100
  isResolvingPath: true
104
101
  }
105
102
  })
106
103
 
107
- if (resolveResult.errors.length > 0) {
108
- // throw `${resolveResult.errors[0].text} (resolveDir: ${cwd})`
104
+ // Simple return the resolved result if we have an error. Usually happens when module is not
105
+ // found.
106
+ if (resolveResult.errors.length > 0) return resolveResult
107
+
108
+ // If 'bundle-all' queryParam is defined, return the resolveResult.
109
+ if (bundled || params.queryParams?.has('bundle-all')) {
110
+ bundled = true
111
+ return { ...resolveResult, suffix: '?bundle-all' }
112
+ }
113
+
114
+ // If 'bundle' queryParam is defined, return the resolveResult.
115
+ if (params.queryParams?.has('bundle')) {
116
+ return { ...resolveResult, suffix: '?bundle' }
117
+ }
118
+
119
+ if (resolveResult.path.startsWith(runtimeDir)) {
120
+ result.path = '/proscenium-runtime' + resolveResult.path.slice(runtimeDir.length)
121
+ } else if (!resolveResult.path.startsWith(cwd)) {
122
+ // If resolved path does not start with cwd, then it is most likely linked, so bundle it.
123
+ return { ...resolveResult, suffix: '?bundle' }
124
+ } else {
125
+ result.path = resolveResult.path.slice(cwd.length)
109
126
  }
110
127
 
111
- result.path = resolveResult.path.slice(cwd.length)
112
128
  result.sideEffects = resolveResult.sideEffects
113
129
 
114
130
  if (
@@ -117,29 +133,15 @@ export default setup('resolve', (build, options) => {
117
133
  /\.jsx?$/.test(params.importer)
118
134
  ) {
119
135
  // We're importing a CSS file from JS(X).
120
- result.namespace = 'importStylesheet'
136
+ return { ...resolveResult, pluginData: { importedFromJs: true } }
121
137
  } else {
122
- // Requested path is a bare module.
123
138
  result.external = true
124
139
  }
125
140
 
126
141
  return result
127
142
  }
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
143
  })
142
144
 
143
- function isBareModule(path) {
144
- return !path.startsWith('.') && !path.startsWith('/')
145
+ function isBareModule(mod) {
146
+ return !mod.startsWith('/') && !mod.startsWith('.')
145
147
  }
@@ -1,35 +1,45 @@
1
1
  export default (pluginName, pluginFn) => {
2
2
  return (options = {}) => ({
3
3
  name: pluginName,
4
- setup(build) {
5
- const plugin = pluginFn(build, options)
4
+ async setup(build) {
5
+ const callbacks = await pluginFn(build, options)
6
6
 
7
- if (plugin.onResolve) {
8
- const { callback, ...onResolve } = plugin.onResolve
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
- build.onResolve(onResolve, async params => {
11
- if (params.pluginData?.isResolvingPath) return
12
+ let results
12
13
 
13
- options.debug && console.debug(`plugin(${pluginName}):onResolve`, params.path, { params })
14
- const results = await callback(params)
15
- options.debug &&
16
- console.debug(`plugin(${pluginName}):onResolve`, params.path, { results })
14
+ if (options.debug) {
15
+ console.debug()
16
+ console.group(`plugin(${pluginName}):onResolve`, { filter, namespace })
17
+ console.debug('params:', params)
17
18
 
18
- return results
19
- })
20
- }
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
+ }
21
28
 
22
- if (plugin.onLoad) {
23
- const { callback, ...onLoad } = plugin.onLoad
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 })
24
34
 
25
- build.onLoad(onLoad, params => {
26
- options.debug && console.debug(`plugin(${pluginName}):onLoad`, { params })
27
- const results = callback(params)
28
- options.debug && console.debug(`plugin(${pluginName}):onLoad`, { results })
35
+ const results = await callback(params)
29
36
 
30
- return results
31
- })
32
- }
37
+ options.debug && console.debug(`plugin(${pluginName}):onLoad`, { results })
38
+
39
+ return results
40
+ })
41
+ }
42
+ })
33
43
  }
34
44
  })
35
45
  }
@@ -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 '../utils.js'
5
+ import { setup } from './setup_plugin.js'
6
6
 
7
7
  export default setup('solidjs', () => {
8
- return {
9
- onLoad: {
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 cwd = join(Deno.cwd(), 'test', 'internal')
5
+ const root = join(Deno.cwd(), 'test', 'internal')
6
6
 
7
- Deno.bench('esbuild', async () => {
8
- await compile(cwd, 'lib/foo.js')
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
  })