proscenium 0.1.2-arm64-darwin → 0.2.1-arm64-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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d20f6189b8ed53c8f8c49fdd34ff7c94e9dd664e4a270d0f19300ce2435868b2
4
- data.tar.gz: 19ca20f0dc49a00ccacddc79459cfa1f9676f3d89d5f276a8c1b9c27637f2212
3
+ metadata.gz: 7d944d36dfe023a4b4362445785ba3474b821abcad06b4937f011ebe4f1ebd84
4
+ data.tar.gz: 63782a1bb192110a39f96aa9ad2ca75897fa10f272076b78263cf850a7d44088
5
5
  SHA512:
6
- metadata.gz: 2703a824ce1acc0e8aa972012fe275b881d8946ff63c8498f8b52de64229faeadc4008e00acb960078c5c9c3d8c02b0219ddaeb75f0b7479f529e7f34495e12a
7
- data.tar.gz: fa206820113a895af26afa184c88045a62ac307e83eaf7337c9feaff202f2d4ac01d16de9b19378f162397bb839f6ca268b0429ec515a39d21899b34b706a8a5
6
+ metadata.gz: 4ac677e20d44ded3f3e49e96265cc31d7263bd35453b86d789b714b58223763cf0e4bcd0ded9ec097d5890793eea5692ad8f68b7038a5cef99634a8782617cc6
7
+ data.tar.gz: f145d102c93d03fee83966982d497f702bc12e066e2cc6d16ce7bab2ca3e330115f2c6f42bac2637666e3e3519e5f6122aaa4750fe6639d242a6a31ab1a7aaa7
data/README.md CHANGED
@@ -110,6 +110,10 @@ resulting in conflicts. For example, esm.sh also supports a `?bundle` param, bun
110
110
  dependencies into a single file. Instead, you should install the module locally using your favourite
111
111
  package manager.
112
112
 
113
+ Note that `?bundle` will only bundle that exact path. It will not bundle any descendant imports. You
114
+ can bundle all imports within a file by using the `?bundle-all` query string. Use this with caution,
115
+ as you could end up swallowing everything, resulting in a very large file.
116
+
113
117
  ## Import Map
114
118
 
115
119
  Import map for both JS and CSS is supported out of the box, and works with no regard to the browser
@@ -246,6 +250,31 @@ p {
246
250
  }
247
251
  ```
248
252
 
253
+ ## Cache Busting
254
+
255
+ By default, all assets are not cached by the browser. But if in production, you populate the
256
+ `REVISION` env variable, all CSS and JS URL's will be appended with its value as a query string, and
257
+ the `Cache-Control` response header will be set to `public` and a max-age of 30 days.
258
+
259
+ For example, if you set `REVISION=v1`, URL's will be appended with `?v1`: `/my/imported/file.js?v1`.
260
+
261
+ It is assumed that the `REVISION` env var will be unique between deploys. If it isn't, then assets
262
+ will continue to be cached as the same version between deploys. I recommend you assign a version
263
+ number or to use the Git commit hash of the deploy. Just make sure it is unique for each deploy.
264
+
265
+ You can set the `cache_query_string` config option directly to define any query string you wish:
266
+
267
+ ```ruby
268
+ Rails.application.config.proscenium.cache_query_string = 'my-cache-busting-version-string'
269
+ ```
270
+
271
+ The cache is set with a `max-age` of 30 days. You can customise this with the `cache_max_age` config
272
+ option:
273
+
274
+ ```ruby
275
+ Rails.application.config.proscenium.cache_max_age = 12.months.to_i
276
+ ```
277
+
249
278
  ## How It Works
250
279
 
251
280
  Proscenium provides a Rails middleware that proxies requests for your frontend code. By default, it will simply search for a file of the same name in your Rails root. For example, a request for '/app/views/layouts/application.js' or '/lib/hooks.js' will return that exact file relative to your Rails root.
@@ -258,10 +287,6 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
258
287
 
259
288
  To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
260
289
 
261
- ### Compile the compilers
262
-
263
- `deno compile --no-config -o bin/compilers/esbuild --import-map import_map.json -A lib/proscenium/compilers/esbuild.js`
264
-
265
290
  ## Contributing
266
291
 
267
292
  Bug reports and pull requests are welcome on GitHub at https://github.com/joelmoss/proscenium. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/joelmoss/proscenium/blob/master/CODE_OF_CONDUCT.md).
data/bin/esbuild CHANGED
Binary file
@@ -11,6 +11,9 @@ export default setup('resolve', (build, options) => {
11
11
  const runtimeCwdAlias = `${cwd}/proscenium-runtime`
12
12
  let bundled = false
13
13
 
14
+ const env = Deno.env.get('RAILS_ENV')
15
+ const isProd = env === 'production'
16
+
14
17
  return [
15
18
  {
16
19
  type: 'onResolve',
@@ -21,6 +24,8 @@ export default setup('resolve', (build, options) => {
21
24
  args.path = path
22
25
  args.suffix = `?${query}`
23
26
  args.queryParams = new URLSearchParams(query)
27
+ } else if (options.cacheQueryString && options.cacheQueryString !== '') {
28
+ args.suffix = `?${options.cacheQueryString}`
24
29
  }
25
30
 
26
31
  // Mark remote modules as external.
@@ -28,24 +33,7 @@ export default setup('resolve', (build, options) => {
28
33
  return { external: true }
29
34
  }
30
35
 
31
- // Proscenium runtime
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
- // }
48
-
36
+ // Rewrite the path to the actual runtime directory.
49
37
  if (args.path.startsWith(runtimeCwdAlias)) {
50
38
  return { path: join(runtimeDir, args.path.slice(runtimeCwdAlias.length)) }
51
39
  }
@@ -88,11 +76,12 @@ export default setup('resolve', (build, options) => {
88
76
  // Resolve the path using esbuild's internal resolution. This allows us to import node packages
89
77
  // and extension-less paths without custom code, as esbuild with resolve them for us.
90
78
  const resolveResult = await build.resolve(result.path, {
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.
79
+ // If path is a bare module (node_modules), and resolveDir is the Proscenium runtime dir, or
80
+ // is the current working dir, then use `cwd` as the `resolveDir`, otherwise pass it through
81
+ // as is. This ensures that nested node_modules are resolved correctly.
94
82
  resolveDir:
95
- isBareModule(result.path) && params.resolveDir.startsWith(runtimeDir)
83
+ isBareModule(result.path) &&
84
+ (!params.resolveDir.startsWith(cwd) || params.resolveDir.startsWith(runtimeDir))
96
85
  ? cwd
97
86
  : params.resolveDir,
98
87
  pluginData: {
@@ -118,9 +107,17 @@ export default setup('resolve', (build, options) => {
118
107
 
119
108
  if (resolveResult.path.startsWith(runtimeDir)) {
120
109
  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' }
110
+ } else if (!resolveResult.path.startsWith(cwd) && !isProd) {
111
+ // Resolved path is not in the current working directory. It could be linked to a file outside
112
+ // the CWD, or it's just invalid. If not in production, return as an outsideRoot namespaced,
113
+ // and externally suffixed path. This lets the Rails Proscenium::Middleware::OutsideRoot
114
+ // handle the import.
115
+ return {
116
+ ...resolveResult,
117
+ namespace: 'outsideRoot',
118
+ path: `${resolveResult.path}?outsideRoot`,
119
+ external: true
120
+ }
124
121
  } else {
125
122
  result.path = resolveResult.path.slice(cwd.length)
126
123
  }
@@ -138,6 +135,10 @@ export default setup('resolve', (build, options) => {
138
135
  result.external = true
139
136
  }
140
137
 
138
+ if (result.suffix && result.suffix !== '') {
139
+ result.path = `${result.path}${result.suffix}`
140
+ }
141
+
141
142
  return result
142
143
  }
143
144
  })
@@ -3,11 +3,12 @@ import { join } from 'std/path/mod.ts'
3
3
  import compile from './esbuild.js'
4
4
 
5
5
  const root = join(Deno.cwd(), 'test', 'internal')
6
+ const lightningcssBin = join(Deno.cwd(), 'bin', 'lightningcss')
6
7
 
7
8
  Deno.bench('esbuild js', async () => {
8
- await compile(['lib/foo.js'], { root })
9
+ await compile(['lib/foo.js'], { root, lightningcssBin })
9
10
  })
10
11
 
11
12
  Deno.bench('esbuild css', async () => {
12
- await compile(['lib/foo.css'], { root })
13
+ await compile(['lib/foo.css'], { root, lightningcssBin })
13
14
  })
@@ -23,18 +23,20 @@ import throwCompileError from './esbuild/compile_error.js'
23
23
  * esbuild [OPTIONS] <PATHS_ARG>...
24
24
  *
25
25
  * ARGS:
26
- * <PATHS_ARG>... One or more file paths to compile.
26
+ * <PATHS_ARG>... One or more file paths or globs to compile.
27
27
  *
28
28
  * OPTIONS:
29
- * --root
29
+ * --root <PATH>
30
30
  * Relative or absolute path to the root or current working directory when compilation will
31
31
  * take place.
32
- * --import-map
32
+ * --import-map <PATH>
33
33
  * Path to an import map, relative to the <root>.
34
- * --lightningcss-bin
34
+ * --lightningcss-bin <PATH>
35
35
  * Path to the lightningcss CLI binary.
36
36
  * --write
37
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`.
38
40
  * --debug
39
41
  * Debug output,
40
42
  */
@@ -42,10 +44,11 @@ if (import.meta.main) {
42
44
  !Deno.env.get('RAILS_ENV') && Deno.env.set('RAILS_ENV', 'development')
43
45
 
44
46
  const { _: paths, ...options } = parseArgs(Deno.args, {
45
- string: ['root', 'import-map', 'lightningcss-bin'],
47
+ string: ['root', 'import-map', 'lightningcss-bin', 'cache-query-string'],
46
48
  boolean: ['write', 'debug'],
47
49
  alias: {
48
50
  'import-map': 'importMap',
51
+ 'cache-query-string': 'cacheQueryString',
49
52
  'lightningcss-bin': 'lightningcssBin'
50
53
  }
51
54
  })
@@ -80,8 +83,8 @@ async function main(paths = [], options = {}) {
80
83
  const env = Deno.env.get('RAILS_ENV')
81
84
  const isProd = env === 'production'
82
85
  const isTest = env === 'test'
83
-
84
86
  const entryPoints = new Set()
87
+
85
88
  for (let i = 0; i < paths.length; i++) {
86
89
  const path = paths[i]
87
90
 
@@ -89,6 +92,10 @@ async function main(paths = [], options = {}) {
89
92
  for await (const file of expandGlob(path, { root })) {
90
93
  file.isFile && entryPoints.add(file.path)
91
94
  }
95
+ } else if (path.startsWith('/')) {
96
+ // Path is absolute, so it must be outsideRoot. Don't prefix the root.
97
+ // See Proscenium::Middleware::OutsideRoot.
98
+ entryPoints.add(path)
92
99
  } else {
93
100
  entryPoints.add(join(root, path))
94
101
  }
@@ -123,7 +130,7 @@ async function main(paths = [], options = {}) {
123
130
  bundle: true,
124
131
  plugins: [
125
132
  envPlugin(),
126
- resolvePlugin({ runtimeDir, importMap, debug }),
133
+ resolvePlugin({ runtimeDir, importMap, debug, cacheQueryString: options.cacheQueryString }),
127
134
  cssPlugin({ lightningcssBin: options.lightningcssBin, debug })
128
135
  ],
129
136
  metafile: write,
@@ -3,7 +3,15 @@
3
3
  module Proscenium
4
4
  module Helper
5
5
  def compute_asset_path(path, options = {})
6
- return "/#{path}" if %i[javascript stylesheet].include?(options[:type])
6
+ if %i[javascript stylesheet].include?(options[:type])
7
+ result = "/#{path}"
8
+
9
+ if (qs = Proscenium.config.cache_query_string)
10
+ result << "?#{qs}"
11
+ end
12
+
13
+ return result
14
+ end
7
15
 
8
16
  super
9
17
  end
@@ -60,6 +60,10 @@ module Proscenium
60
60
  response.content_type = content_type
61
61
  response['X-Proscenium-Middleware'] = name
62
62
 
63
+ if Proscenium.config.cache_query_string && Proscenium.config.cache_max_age
64
+ response['Cache-Control'] = "public, max-age=#{Proscenium.config.cache_max_age}"
65
+ end
66
+
63
67
  yield response if block_given?
64
68
 
65
69
  response.finish
@@ -15,9 +15,11 @@ module Proscenium
15
15
 
16
16
  def attempt
17
17
  benchmark :esbuild do
18
- render_response build(
19
- "#{cli} --root #{root} --lightningcss-bin #{lightningcss_cli} #{path}"
20
- )
18
+ render_response build([
19
+ "#{cli} --root #{root}",
20
+ cache_query_string,
21
+ "--lightningcss-bin #{lightningcss_cli} #{path}"
22
+ ].compact.join(' '))
21
23
  end
22
24
  rescue CompileError => e
23
25
  render_response "export default #{e.detail.to_json}" do |response|
@@ -33,9 +35,7 @@ module Proscenium
33
35
 
34
36
  def cli
35
37
  if ENV['PROSCENIUM_TEST']
36
- [
37
- 'deno run -q --import-map import_map.json -A', 'lib/proscenium/compilers/esbuild.js'
38
- ].join(' ')
38
+ 'deno run -q --import-map import_map.json -A lib/proscenium/compilers/esbuild.js'
39
39
  else
40
40
  Gem.bin_path 'proscenium', 'esbuild'
41
41
  end
@@ -48,6 +48,11 @@ module Proscenium
48
48
  Gem.bin_path 'proscenium', 'lightningcss'
49
49
  end
50
50
  end
51
+
52
+ def cache_query_string
53
+ q = Proscenium.config.cache_query_string
54
+ q ? "--cache-query-string #{q}" : nil
55
+ end
51
56
  end
52
57
  end
53
58
  end
@@ -0,0 +1,26 @@
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
@@ -10,6 +10,7 @@ module Proscenium
10
10
  autoload :Base
11
11
  autoload :Esbuild
12
12
  autoload :Runtime
13
+ autoload :OutsideRoot
13
14
 
14
15
  def initialize(app)
15
16
  @app = app
@@ -38,6 +39,12 @@ module Proscenium
38
39
  def find_type(request)
39
40
  path = Pathname.new(request.path)
40
41
 
42
+ # Non-production only!
43
+ if request.query_string == 'outsideRoot'
44
+ return if Rails.env.production?
45
+ return OutsideRoot if path.fnmatch?(glob_types[:outsideRoot], File::FNM_EXTGLOB)
46
+ end
47
+
41
48
  return Runtime if path.fnmatch?(glob_types[:runtime], File::FNM_EXTGLOB)
42
49
  return Esbuild if path.fnmatch?(glob_types[:esbuild], File::FNM_EXTGLOB)
43
50
  end
@@ -13,7 +13,8 @@ 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: '/{config,app,lib,node_modules}/**.{js,mjs,jsx,css}',
16
- runtime: '/proscenium-runtime/**.{js,jsx}'
16
+ runtime: '/proscenium-runtime/**.{js,jsx}',
17
+ outsideRoot: '/**/*.{js,jsx,mjs,css}'
17
18
  }.freeze
18
19
 
19
20
  class << self
@@ -27,6 +28,8 @@ module Proscenium
27
28
 
28
29
  config.proscenium = ActiveSupport::OrderedOptions.new
29
30
  config.proscenium.side_load = true
31
+ config.proscenium.cache_query_string = Rails.env.production? && ENV.fetch('REVISION', nil)
32
+ config.proscenium.cache_max_age = 2_592_000 # 30 days
30
33
  config.proscenium.auto_reload = Rails.env.development?
31
34
  config.proscenium.auto_reload_paths ||= %w[lib app config]
32
35
  config.proscenium.auto_reload_extensions ||= /\.(css|jsx?)$/
@@ -1,5 +1,4 @@
1
- import { createConsumer } from 'https://esm.sh/@rails/actioncable@6.0.5'
2
- import debounce from 'https://esm.sh/debounce@1.2.1'
1
+ import { createConsumer } from 'https://esm.sh/v96/@rails/actioncable@7.0.4/es2022/actioncable.js'
3
2
 
4
3
  export default socketPath => {
5
4
  const uid = (Date.now() + ((Math.random() * 100) | 0)).toString()
@@ -9,7 +8,7 @@ export default socketPath => {
9
8
  received: debounce(() => {
10
9
  console.log('[Proscenium] Files changed; reloading...')
11
10
  location.reload()
12
- }, 200),
11
+ }, 200, true),
13
12
 
14
13
  connected() {
15
14
  console.log('[Proscenium] Auto-reload websocket connected')
@@ -20,3 +19,22 @@ export default socketPath => {
20
19
  }
21
20
  })
22
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,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Proscenium
4
- VERSION = '0.1.2'
4
+ VERSION = '0.2.1'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: proscenium
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.1
5
5
  platform: arm64-darwin
6
6
  authors:
7
7
  - Joel Moss
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-10-16 00:00:00.000000000 Z
11
+ date: 2022-10-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actioncable
@@ -167,6 +167,7 @@ files:
167
167
  - lib/proscenium/middleware/base.rb
168
168
  - lib/proscenium/middleware/esbuild.rb
169
169
  - lib/proscenium/middleware/lightningcss.rb
170
+ - lib/proscenium/middleware/outside_root.rb
170
171
  - lib/proscenium/middleware/runtime.rb
171
172
  - lib/proscenium/middleware/static.rb
172
173
  - lib/proscenium/phlex.rb