proscenium 0.7.0-x86_64-linux → 0.9.0-x86_64-linux

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2c170e7b81f5a5e8591e8876e9b25cc25c0815cd453b2b14cb840229c471fdc2
4
- data.tar.gz: 75df6068a461a45bd4cdf82b382096a25c6c79b934d27f425ac2ba1e97384b8e
3
+ metadata.gz: e89c958a1a78e610ea61941c833eead456c32f9030c1f880afe5270633158fdb
4
+ data.tar.gz: 6f80d724798b69aa1e9cf8bb92cecd60ec84b99e314f40c36ba44bb7a0b74c8d
5
5
  SHA512:
6
- metadata.gz: e7df563fe5214e951e1d92e024d1f7de61e9a547c13a0442ea9531428b005777007b5e09605a9e88ed6f7f191e6849e2a97fbc7e429a1dfde9bb50c7a2496b59
7
- data.tar.gz: db7286f0871be25d106442a2684ae22271f1632614a9fb0e48f19b5888311cc651ecb0ea5482734b0e7e67dba87f673d05d44d7915a3ea0408a05c24c451b2cc
6
+ metadata.gz: 4241bcc1c617d80496be4f10e373fd15e6cf75227c5fbb0a9f974568276a0c5b490c8cd99efab7818208b595209eb6e068f03942e05073d4e314a7a267d9f70b
7
+ data.tar.gz: bf1e4c6cd00298f4210ac1a6228dd15a0f022c2b33b8b2a01b14753ef7cc0f35c5989e0d7e9e755728492e71699203d958fdef6400b44a2a7750fbe0d306aa7d
data/README.md CHANGED
@@ -1,28 +1,25 @@
1
- # Proscenium - Modern Client-Side Tooling for Rails
1
+ # Proscenium - Modern client-side development for Rails
2
2
 
3
3
  Proscenium treats your client-side code as first class citizens of your Rails app, and assumes a
4
4
  "fast by default" internet. It bundles your JS, JSX and CSS in real time, on demand, and with zero
5
5
  configuration.
6
6
 
7
- - Zero configuration.
8
7
  - Fast real-time bundling, tree-shaking and minification.
8
+ - Real time bundling of Javascript (.js,.jsx), Typescript (.ts,.tsx) and CSS (.css).
9
9
  - NO JavaScript runtime - just the browser!
10
+ - NO build step or pre-compilation.
11
+ - NO additional process or server - Just run Rails!
10
12
  - Deep integration with Rails.
11
- - No additional process or server - Just run Rails!
13
+ - Zero configuration.
12
14
  - Serve assets from anywhere within your Rails root (/app, /config, /lib, etc.).
13
- - Automatically side load JS/CSS for your layouts and views.
14
- - Import JS(X), TS(X) and CSS from NPM, URL, and locally.
15
- - Support for JSX.
15
+ - Automatically side load JS/TS/CSS for your layouts and views.
16
+ - Import from NPM, URLs, and locally.
16
17
  - Server-side import map support.
17
18
  - CSS Modules.
18
19
  - CSS mixins.
19
20
  - Source maps.
20
21
  - Phlex and ViewComponent integration.
21
22
 
22
- ## ⚠️ WORK IN PROGRESS ⚠️
23
-
24
- While my goal is to use Proscenium in production, I recommended that you **DO NOT** use Proscenium in production just yet! It has only been run for local development, and requires several improvements for optimal production use.
25
-
26
23
  ## Installation
27
24
 
28
25
  Add this line to your Rails application's Gemfile, and you're good to go:
@@ -49,6 +46,35 @@ Using the examples above...
49
46
  - `app/components/menu_component.jsx` => `https://yourapp.com/app/components/menu_component.jsx`
50
47
  - `config/properties.css` => `https://yourapp.com/config/properties.css`
51
48
 
49
+ ## Side Loading
50
+
51
+ Proscenium has built in support for automatically side loading JS, TS and CSS with your views and
52
+ layouts.
53
+
54
+ Just create a JS and/or CSS file with the same name as any view or layout, and make sure your
55
+ layouts include `<%= side_load_stylesheets %>` and `<%= side_load_javascripts %>`. Something like
56
+ this:
57
+
58
+ ```html
59
+ <!DOCTYPE html>
60
+ <html>
61
+ <head>
62
+ <title>Hello World</title>
63
+ <%= side_load_stylesheets %>
64
+ </head>
65
+ <body>
66
+ <%= yield %>
67
+ <%= side_load_javascripts defer: true, type: 'module' %>
68
+ </body>
69
+ </html>
70
+ ```
71
+
72
+ On each page request, Proscenium will check if your layout and view has a JS/TS/CSS file of the same
73
+ name, and include them into your layout HTML. Partials are not side loaded.
74
+
75
+ Side loading is enabled by default, but you can disable it by setting `config.proscenium.side_load`
76
+ to `false`.
77
+
52
78
  ## Importing
53
79
 
54
80
  Proscenium supports importing JS, JSX, TS and CSS from NPM, by URL, your local app, and even from Ruby Gems.
@@ -133,36 +159,81 @@ env => ({
133
159
  })
134
160
  ```
135
161
 
136
- ## Side Loading
162
+ ## Importing SVG from JS(X) and TS(X)
137
163
 
138
- Proscenium has built in support for automatically side loading JS and CSS with your views and
139
- layouts.
164
+ Importing SVG from JS(X) will bundle the SVG source code. Additionally, if importing from JSX or TSX, the SVG source code will be rendered as a JSX/TSX component.
140
165
 
141
- Just create a JS and/or CSS file with the same name as any view or layout, and make sure your
142
- layouts include `<%= side_load_stylesheets %>` and `<%= side_load_javascripts %>`. Something like
143
- this:
166
+ ## Environment Variables
144
167
 
145
- ```html
146
- <!DOCTYPE html>
147
- <html>
148
- <head>
149
- <title>Hello World</title>
150
- <%= side_load_stylesheets %>
151
- </head>
152
- <body>
153
- <%= yield %>
154
- <%= side_load_javascripts defer: true, type: 'module' %>
155
- </body>
156
- </html>
168
+ Import any environment variables into your JS(X) code.
169
+
170
+ ```js
171
+ import RAILS_ENV from '@proscenium/env/RAILS_ENV'
157
172
  ```
158
173
 
159
- On each page request, Proscenium will check if your layout and view has a JS/CSS file of the same
160
- name, and include them into your layout HTML. Partials are not side loaded.
174
+ You can only access environment variables that are explicitly named. It will export `undefined` if the env variable does not exist.
161
175
 
162
- Side loading is enabled by default, but you can disable it by setting `config.proscenium.side_load`
163
- to `false`.
176
+ ## Importing i18n
164
177
 
165
- ## CSS Modules
178
+ Basic support is provided for importing your Rails locale files from `config/locales/*.yml`, exporting them as JSON.
179
+
180
+ ```js
181
+ import translations from '@proscenium/i18n'
182
+ // translations.en.*
183
+ ```
184
+
185
+ ## Javascript
186
+
187
+ By default, Proscenium's output will take advantage of all modern JS features. For example, `a !== void 0 && a !== null ? a : b` will become `a ?? b` when minifying (enabled by default in production), which makes use of syntax from the ES2020 version of JavaScript.
188
+
189
+ ### Tree Shaking
190
+
191
+ Tree shaking is the term the JavaScript community uses for dead code elimination, a common compiler optimization that automatically removes unreachable code. Tree shaking is enabled by default in Proascenium.
192
+
193
+ ```javascript
194
+ function one() {
195
+ console.log('one')
196
+ }
197
+ function two() {
198
+ console.log('two')
199
+ }
200
+ one()
201
+ ```
202
+
203
+ The above code will be transformed to the following code, discarding `two()`, as it is never called.
204
+
205
+ ```javascript
206
+ function one() {
207
+ console.log("one");
208
+ }
209
+ one();
210
+ ```
211
+
212
+ ### JavaScript Caveats
213
+
214
+ There are a few important caveats as far as JavaScript is concerned. These are [detailed on the esbuild site](https://esbuild.github.io/content-types/#javascript-caveats).
215
+
216
+ ## CSS
217
+
218
+ CSS is a first-class content type in Proscenium, which means it can bundle CSS files directly without needing to import your CSS from JavaScript code. You can `@import` other CSS files and reference image and font files with `url()` and Proscenium will bundle everything together.
219
+
220
+ Note that by default, Proscenium's output will take advantage of all modern CSS features. For example, `color: rgba(255, 0, 0, 0.4)` will become `color: #f006` after minifying in production, which makes use of syntax from [CSS Color Module Level 4](https://drafts.csswg.org/css-color-4/#changes-from-3).
221
+
222
+ The new CSS nesting syntax is supported, and transformed into non-nested CSS for older browsers.
223
+
224
+ ### Importing from JavaScript
225
+
226
+ You can also import CSS from JavaScript. When you do this, Proscenium will automatically append each stylesheet to the document's head as a `<link>` element.
227
+
228
+ ```jsx
229
+ import './button.css'
230
+
231
+ export let Button = ({ text }) => {
232
+ return <div className="button">{text}</div>
233
+ }
234
+ ```
235
+
236
+ ### CSS Modules
166
237
 
167
238
  Proscenium implements a subset of [CSS Modules](https://github.com/css-modules/css-modules). It supports the `:local` and `:global` keywords, but not the `composes` property. It is recommended that you use mixins instead of `composes`, as they work everywhere.
168
239
 
@@ -193,7 +264,7 @@ It is important to note that the exported object of CSS module names is actually
193
264
 
194
265
  Also, importing a CSS module from another CSS module will result in the same digest string for all classes.
195
266
 
196
- ## CSS Mixins
267
+ ### CSS Mixins
197
268
 
198
269
  Proscenium provides functionality for including or "mixing in" onr or more CSS classes into another. This is similar to the `composes` property of CSS Modules, but works everywhere, and is not limited to CSS Modules.
199
270
 
@@ -242,27 +313,49 @@ p {
242
313
 
243
314
  CSS modules and Mixins works perfectly together. You can include a mixin in a CSS module.
244
315
 
245
- ## Importing SVG from JS(X)
316
+ ### CSS Caveats
246
317
 
247
- Importing SVG from JS(X) will bundle the SVG source code. Additionally, if importing from JSX, the SVG source code will be rendered as a JSX component.
318
+ There are a few important caveats as far as CSS is concerned. These are [detailed on the esbuild site](https://esbuild.github.io/content-types/#css-caveats).
248
319
 
249
- ## Environment Variables
320
+ ## Typescript
250
321
 
251
- Import any environment variables into your JS(X) code.
322
+ Typescript and TSX is supported out of the box, and has built-in support for parsing TypeScript syntax and discarding the type annotations. Just rename your files to `.ts` or `.tsx` and you're good to go.
252
323
 
253
- ```js
254
- import RAILS_ENV from '@proscenium/env/RAILS_ENV'
324
+ Please note that Proscenium does not do any type checking so you will still need to run `tsc -noEmit` in parallel with Proscenium to check types.
325
+
326
+ ### Typescript Caveats
327
+
328
+ There are a few important caveats as far as Typescript is concerned. These are [detailed on the esbuild site](https://esbuild.github.io/content-types/#typescript-caveats).
329
+
330
+ ## JSX
331
+
332
+ Using JSX syntax usually requires you to manually import the JSX library you are using. For example, if you are using React, by default you will need to import React into each JSX file like this:
333
+
334
+ ```javascript
335
+ import * as React from 'react'
336
+ render(<div/>)
255
337
  ```
256
338
 
257
- You can only access environment variables that are explicitly named. It will export `undefined` if the env variable does not exist.
339
+ This is because the JSX transform turns JSX syntax into a call to `React.createElement` but it does not itself import anything, so the React variable is not automatically present.
258
340
 
259
- ## Importing i18n
341
+ Proscenium generates these import statements for you. Keep in mind that this also completely changes how the JSX transform works, so it may break your code if you are using a JSX library that is not React.
260
342
 
261
- Basic support is provided for importing your Rails locale files from `config/locales/*.yml`, exporting them as JSON.
343
+ In the [not too distant] future, you will be able to configure Proscenium to use a different JSX library, or to disable this auto-import completely.
262
344
 
263
- ```js
264
- import translations from '@proscenium/i18n'
265
- // translations.en.*
345
+ ## JSON
346
+
347
+ Importing .json files parses the JSON file into a JavaScript object, and exports the object as the default export. Using it looks something like this:
348
+
349
+ ```javascript
350
+ import object from './example.json'
351
+ console.log(object)
352
+ ```
353
+
354
+ In addition to the default export, there are also named exports for each top-level property in the JSON object. Importing a named export directly means Proscenium can automatically remove unused parts of the JSON file from the bundle, leaving only the named exports that you actually used. For example, this code will only include the version field when bundled:
355
+
356
+ ```javascript
357
+ import { version } from './package.json'
358
+ console.log(version)
266
359
  ```
267
360
 
268
361
  ## Phlex Support
@@ -311,11 +404,33 @@ Proscenium brings back RJS! Any path ending in .rjs will be served from your Rai
311
404
 
312
405
  *docs needed*
313
406
 
407
+ ## Thanks 🙏
408
+
409
+ HUGE thanks go to [Evan Wallace](https://github.com/evanw) and his amazing [esbuild](https://esbuild.github.io/) project. Proscenium would not be possible without it, and it is esbuild that makes this so fast and efficient.
410
+
411
+ Because Proscenium uses esbuild extensively, some of these docs are taken directly from the esbuild docs, with links back to the [esbuild site](https://esbuild.github.io/) where appropriate.
412
+
314
413
  ## Development
315
414
 
316
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
415
+ Before doing anything else, you will need compile a local version of the Go binary. This is because the Go binary is not checked into the repo. To compile the binary, run:
416
+
417
+ ```bash
418
+ bundle exec rake compile:local
419
+ ```
420
+
421
+ ### Running tests
317
422
 
318
- 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).
423
+ We have tests for both Ruby and Go. To run the Ruby tests:
424
+
425
+ ```bash
426
+ bundle exec rake test
427
+ ```
428
+
429
+ To run the Go tests:
430
+
431
+ ```bash
432
+ go test ./test
433
+ ```
319
434
 
320
435
  ### Running Go benchmarks
321
436
 
Binary file
@@ -90,10 +90,9 @@ extern "C" {
90
90
  // - baseUrl - base URL of the Rails app. eg. https://example.com
91
91
  // - env - The environment (1 = development, 2 = test, 3 = production)
92
92
  // - importMap - Path to the import map relative to `root`.
93
- // - bundle
94
93
  // - debug
95
94
  //
96
- extern struct Result build(char* filepath, char* root, char* baseUrl, unsigned int env, char* importMap, GoUint8 bundle, GoUint8 debug);
95
+ extern struct Result build(char* filepath, char* root, char* baseUrl, unsigned int env, char* importMap, GoUint8 debug);
97
96
 
98
97
  // Resolve the given `path` relative to the `root`.
99
98
  //
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'open3'
4
3
  require 'oj'
5
4
 
6
5
  module Proscenium
@@ -71,9 +70,13 @@ module Proscenium
71
70
  end
72
71
 
73
72
  def content_type
74
- @content_type ||
75
- ::Rack::Mime.mime_type(::File.extname(path_to_build), nil) ||
76
- 'application/javascript'
73
+ case ::File.extname(path_to_build)
74
+ when '.js', '.mjs', '.ts', '.tsx', '.jsx' then 'application/javascript'
75
+ when '.css' then 'text/css'
76
+ when '.map' then 'application/json'
77
+ else
78
+ ::Rack::Mime.mime_type(::File.extname(path_to_build), nil) || 'application/javascript'
79
+ end
77
80
  end
78
81
 
79
82
  def render_response(content)
@@ -30,30 +30,23 @@ module Proscenium
30
30
 
31
31
  # file_handler.attempt(request.env) || type.attempt(request)
32
32
 
33
- type.attempt(request)
33
+ type.attempt request
34
34
  end
35
35
 
36
- # Returns the type of file being requested using Proscenium::MIDDLEWARE_GLOB_TYPES.
37
36
  def find_type(request)
38
- path = Pathname.new(request.path)
39
-
40
- return Url if request.path.match?(glob_types[:url])
41
- return Esbuild if path.fnmatch?(application_glob_type, File::FNM_EXTGLOB)
42
- end
43
-
44
- # TODO: handle precompiled assets
45
- def file_handler
46
- ::ActionDispatch::FileHandler.new Rails.public_path.join('assets').to_s,
47
- headers: { 'X-Proscenium-Middleware' => 'precompiled' }
37
+ return Url if request.path.match?(%r{^/https?%3A%2F%2F})
38
+ return Esbuild if Pathname.new(request.path).fnmatch?(path_glob, File::FNM_EXTGLOB)
48
39
  end
49
40
 
50
- def glob_types
51
- @glob_types ||= Proscenium::MIDDLEWARE_GLOB_TYPES
52
- end
53
-
54
- def application_glob_type
41
+ def path_glob
55
42
  paths = Rails.application.config.proscenium.include_paths.join(',')
56
- "/{#{paths}}#{glob_types[:application]}"
43
+ "/{#{paths}}/**.{#{FILE_EXTENSIONS.join(',')}}"
57
44
  end
45
+
46
+ # TODO: handle precompiled assets
47
+ # def file_handler
48
+ # ::ActionDispatch::FileHandler.new Rails.public_path.join('assets').to_s,
49
+ # headers: { 'X-Proscenium-Middleware' => 'precompiled' }
50
+ # end
58
51
  end
59
52
  end
@@ -6,12 +6,8 @@ require 'proscenium/log_subscriber'
6
6
  ENV['RAILS_ENV'] = Rails.env
7
7
 
8
8
  module Proscenium
9
- FILE_EXTENSIONS = ['js', 'mjs', 'jsx', 'css', 'js.map', 'mjs.map', 'jsx.map', 'css.map'].freeze
10
-
11
- MIDDLEWARE_GLOB_TYPES = {
12
- application: "/**.{#{FILE_EXTENSIONS.join(',')}}",
13
- url: %r{^/https?%3A%2F%2F}
14
- }.freeze
9
+ FILE_EXTENSIONS = ['js', 'mjs', 'ts', 'jsx', 'tsx', 'css', 'js.map', 'mjs.map', 'jsx.map',
10
+ 'ts.map', 'tsx.map', 'css.map'].freeze
15
11
 
16
12
  APPLICATION_INCLUDE_PATHS = ['config', 'app/views', 'lib', 'node_modules'].freeze
17
13
 
@@ -7,7 +7,7 @@ module Proscenium
7
7
 
8
8
  out = []
9
9
  Proscenium::Current.loaded[:css].delete_if do |path|
10
- out << stylesheet_link_tag(path)
10
+ out << stylesheet_link_tag(path, extname: false)
11
11
  end
12
12
  out.join("\n").html_safe
13
13
  end
@@ -17,7 +17,7 @@ module Proscenium
17
17
 
18
18
  out = []
19
19
  Proscenium::Current.loaded[:js].delete_if do |path|
20
- out << javascript_include_tag(path, options)
20
+ out << javascript_include_tag(path, extname: false, **options)
21
21
  end
22
22
  out.join("\n").html_safe
23
23
  end
@@ -11,13 +11,19 @@ module Proscenium
11
11
  autoload :EnsureLoaded
12
12
 
13
13
  EXTENSIONS = %i[js css].freeze
14
- EXTENSION_MAP = { '.css' => :css, '.js' => :js }.freeze
14
+ EXTENSION_MAP = {
15
+ '.css' => :css,
16
+ # '.tsx' => :js,
17
+ '.ts' => :js,
18
+ # '.jsx' => :js,
19
+ '.js' => :js
20
+ }.freeze
15
21
 
16
22
  attr_reader :path
17
23
 
18
24
  class << self
19
- # Side load the given asset `path`, by appending it to `Proscenium::Current.loaded`, which is a
20
- # Set of 'js' and 'css' asset paths. This is idempotent, so side loading will never include
25
+ # Side load the given asset `path`, by appending it to `Proscenium::Current.loaded`, which is
26
+ # a Set of 'js' and 'css' asset paths. This is idempotent, so side loading will never include
21
27
  # duplicates.
22
28
  #
23
29
  # @return [Array] appended URL paths
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Proscenium
4
- VERSION = '0.7.0'
4
+ VERSION = '0.9.0'
5
5
  end
data/lib/proscenium.rb CHANGED
@@ -58,8 +58,8 @@ module Proscenium
58
58
  sroot = "#{matched_gem[1][:root]}/"
59
59
  relpath = path.delete_prefix(sroot)
60
60
 
61
- if matched_gem[1][:package_name]
62
- return Esbuild::Golib.resolve("#{matched_gem[1][:package_name]}/#{relpath}")
61
+ if (package_name = matched_gem[1][:package_name] || matched_gem[0])
62
+ return Esbuild::Golib.resolve("#{package_name}/#{relpath}")
63
63
  end
64
64
 
65
65
  # TODO: manually resolve the path without esbuild
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.7.0
4
+ version: 0.9.0
5
5
  platform: x86_64-linux
6
6
  authors:
7
7
  - Joel Moss
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-06-01 00:00:00.000000000 Z
11
+ date: 2023-06-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport