proscenium 0.7.0-aarch64-linux

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: da3515fcd067a9196ab1efa8b0317f529ad3d8d0ba01ed81fe3d6c3a00f27788
4
+ data.tar.gz: 3ca2bfd945a9919fb51a16b884b888e85766ad71fedbe67341043737c7fe2e58
5
+ SHA512:
6
+ metadata.gz: 82021604ea289ac9f9ff3a873c355a4e800c65b3a67efcc2245a77a53c69e5f38b50205164e86efebbbb2ebeb896e6dbd3082b9b445940141f21e00b3b5fc9fc
7
+ data.tar.gz: 25736d64cff191fac8fb1e842f09a5bbc9707a6700ace76cf1ed13985a07be9aaa4e419bf44779dc5a880409c9f3ea840d57fd2ab1f89a3a07e48b4438344101
@@ -0,0 +1,84 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
6
+
7
+ We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
8
+
9
+ ## Our Standards
10
+
11
+ Examples of behavior that contributes to a positive environment for our community include:
12
+
13
+ * Demonstrating empathy and kindness toward other people
14
+ * Being respectful of differing opinions, viewpoints, and experiences
15
+ * Giving and gracefully accepting constructive feedback
16
+ * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
17
+ * Focusing on what is best not just for us as individuals, but for the overall community
18
+
19
+ Examples of unacceptable behavior include:
20
+
21
+ * The use of sexualized language or imagery, and sexual attention or
22
+ advances of any kind
23
+ * Trolling, insulting or derogatory comments, and personal or political attacks
24
+ * Public or private harassment
25
+ * Publishing others' private information, such as a physical or email
26
+ address, without their explicit permission
27
+ * Other conduct which could reasonably be considered inappropriate in a
28
+ professional setting
29
+
30
+ ## Enforcement Responsibilities
31
+
32
+ Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
33
+
34
+ Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
35
+
36
+ ## Scope
37
+
38
+ This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
39
+
40
+ ## Enforcement
41
+
42
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at joel@developwithstyle.com. All complaints will be reviewed and investigated promptly and fairly.
43
+
44
+ All community leaders are obligated to respect the privacy and security of the reporter of any incident.
45
+
46
+ ## Enforcement Guidelines
47
+
48
+ Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:
49
+
50
+ ### 1. Correction
51
+
52
+ **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
53
+
54
+ **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
55
+
56
+ ### 2. Warning
57
+
58
+ **Community Impact**: A violation through a single incident or series of actions.
59
+
60
+ **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
61
+
62
+ ### 3. Temporary Ban
63
+
64
+ **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.
65
+
66
+ **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
67
+
68
+ ### 4. Permanent Ban
69
+
70
+ **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
71
+
72
+ **Consequence**: A permanent ban from any sort of public interaction within the community.
73
+
74
+ ## Attribution
75
+
76
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0,
77
+ available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
78
+
79
+ Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
80
+
81
+ [homepage]: https://www.contributor-covenant.org
82
+
83
+ For answers to common questions about this code of conduct, see the FAQ at
84
+ https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations.
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 Joel Moss
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,336 @@
1
+ # Proscenium - Modern Client-Side Tooling for Rails
2
+
3
+ Proscenium treats your client-side code as first class citizens of your Rails app, and assumes a
4
+ "fast by default" internet. It bundles your JS, JSX and CSS in real time, on demand, and with zero
5
+ configuration.
6
+
7
+ - Zero configuration.
8
+ - Fast real-time bundling, tree-shaking and minification.
9
+ - NO JavaScript runtime - just the browser!
10
+ - Deep integration with Rails.
11
+ - No additional process or server - Just run Rails!
12
+ - 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.
16
+ - Server-side import map support.
17
+ - CSS Modules.
18
+ - CSS mixins.
19
+ - Source maps.
20
+ - Phlex and ViewComponent integration.
21
+
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
+ ## Installation
27
+
28
+ Add this line to your Rails application's Gemfile, and you're good to go:
29
+
30
+ ```ruby
31
+ gem 'proscenium'
32
+ ```
33
+
34
+ Please note that Proscenium is designed solely for use with Rails, so will not work - at least out of the box - anywhere else.
35
+
36
+ ## Client-Side Code Anywhere
37
+
38
+ Proscenium believes that your frontend code is just as important as your backend code, and is not an afterthought - they should be first class citizens of your Rails app. So instead of having to throw all your JS and CSS into a "app/assets" directory, and then requiring a separate process to compile or bundle, just put them wherever you want within your app, and just run Rails!
39
+
40
+ For example, if you have some JS that is required by your `app/views/users/index.html.erb` view, just create a JS file alongside it at `app/views/users/index.js`. Or if you have some CSS that is used by your entire application, put it in `app/views/layouts/application.css` and load it alongside your layout. Maybe you have a few JS utility functions, so put them in `lib/utils.js`.
41
+
42
+ Simply put your JS(X) and CSS anywhere you want, and they will be served by your Rails app from the location where you placed them.
43
+
44
+ Using the examples above...
45
+
46
+ - `app/views/users/index.js` => `https://yourapp.com/app/views/users/index.js`
47
+ - `app/views/layouts/application.css` => `https://yourapp.com/app/views/layouts/application.css`
48
+ - `lib/utils.js` => `https://yourapp.com/lib/utils.js`
49
+ - `app/components/menu_component.jsx` => `https://yourapp.com/app/components/menu_component.jsx`
50
+ - `config/properties.css` => `https://yourapp.com/config/properties.css`
51
+
52
+ ## Importing
53
+
54
+ Proscenium supports importing JS, JSX, TS and CSS from NPM, by URL, your local app, and even from Ruby Gems.
55
+
56
+ Imported files are bundled together in real time. So no build step or pre-compilation is needed.
57
+
58
+ Imports are assumed to be JS files, so there is no need to specify the file extesnion in such cases. But you can if you like. All other file types must be specified using their full file name and extension.
59
+
60
+ ### URL Imports
61
+
62
+ Any import beginning with `http://` or `https://` will be fetched from the URL provided. For example:
63
+
64
+ ```js
65
+ import React from 'https://esm.sh/react'
66
+ ```
67
+
68
+ ```css
69
+ @import 'https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css';
70
+ ```
71
+
72
+ URL imports are cached, so that each import is only fetched once per server restart.
73
+
74
+ ### Import from NPM (`node_modules`)
75
+
76
+ Bare imports (imports not beginning with `./`, `/`, `https://`, `http://`) are fully supported, and will use your package manager of choice (eg, NPM, Yarn, pnpm) via the `package.json` file:
77
+
78
+ ```js
79
+ import React from 'react'
80
+ ```
81
+
82
+ ### Local Imports
83
+
84
+ And of course you can import your own code, using relative or absolute paths (file extension is optional):
85
+
86
+ ```js /app/views/layouts/application.js
87
+ import utils from '/lib/utils'
88
+ ```
89
+
90
+ ```js /lib/utils.js
91
+ import constants from './constants'
92
+ ```
93
+
94
+ ## Import Map [WIP]
95
+
96
+ [Import map](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap) for both JS and CSS is supported out of the box, and works with no regard to the browser being used. This is because the import map is parsed and resolved by Proscenium on the server. If you are not familiar with import maps, think of it as a way to define aliases.
97
+
98
+ Just create `config/import_map.json` and specify the imports you want to use. For example:
99
+
100
+ ```json
101
+ {
102
+ "imports": {
103
+ "react": "https://esm.sh/react@18.2.0",
104
+ "start": "/lib/start.js",
105
+ "common": "/lib/common.css",
106
+ "@radix-ui/colors/": "https://esm.sh/@radix-ui/colors@0.1.8/",
107
+ }
108
+ }
109
+ ```
110
+
111
+ Using the above import map, we can do...
112
+
113
+ ```js
114
+ import { useCallback } from 'react'
115
+ import startHere from 'start'
116
+ import styles from 'common'
117
+ ```
118
+
119
+ and for CSS...
120
+
121
+ ```css
122
+ @import 'common';
123
+ @import '@radix-ui/colors/blue.css';
124
+ ```
125
+
126
+ You can also write your import map in Javascript instead of JSON. So instead of `config/import_map.json`, create `config/import_map.js`, and define an anonymous function. This function accepts a single `environment` argument.
127
+
128
+ ```js
129
+ env => ({
130
+ imports: {
131
+ react: env === 'development' ? 'https://esm.sh/react@18.2.0?dev' : 'https://esm.sh/react@18.2.0'
132
+ }
133
+ })
134
+ ```
135
+
136
+ ## Side Loading
137
+
138
+ Proscenium has built in support for automatically side loading JS and CSS with your views and
139
+ layouts.
140
+
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:
144
+
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>
157
+ ```
158
+
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.
161
+
162
+ Side loading is enabled by default, but you can disable it by setting `config.proscenium.side_load`
163
+ to `false`.
164
+
165
+ ## CSS Modules
166
+
167
+ 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
+
169
+ Give any CSS file a `.module.css` extension, and Proscenium will load it as a CSS Module...
170
+
171
+ ```css
172
+ .header {
173
+ background-color: #00f;
174
+ }
175
+ ```
176
+
177
+ The above input produces:
178
+
179
+ ```css
180
+ .header5564cdbb {
181
+ background-color: #00f;
182
+ }
183
+ ```
184
+
185
+ Importing a CSS file from JS will automatically append the stylesheet to the document's head. And the results of the import will be an object of CSS class to module names.
186
+
187
+ ```js
188
+ import styles from './styles.module.css'
189
+ // styles == { header: 'header5564cdbb' }
190
+ ```
191
+
192
+ It is important to note that the exported object of CSS module names is actually a Proxy object. So destructuring the object will not work. Instead, you must access the properties directly.
193
+
194
+ Also, importing a CSS module from another CSS module will result in the same digest string for all classes.
195
+
196
+ ## CSS Mixins
197
+
198
+ 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
+
200
+ CSS mixins are supported using the `@define-mixin` and `@mixin` at-rules.
201
+
202
+ A mixin is defined using the `@define-mixin` at-rule. Pass it a name, which should adhere to class name semantics, and declare your rules:
203
+
204
+ ```css
205
+ // /lib/mixins.css
206
+ @define-mixin bigText {
207
+ font-size: 50px;
208
+ }
209
+ ```
210
+
211
+ Use a mixin using the `@mixin` at-rule. Pass it the name of the mixin you want to use, and the url where the mixin is declared. The url is used to resolve the mixin, and can be relative, absolute, a URL, or even from an NPM packacge.
212
+
213
+ ```css
214
+ // /app/views/layouts/application.css
215
+ p {
216
+ @mixin bigText from url('/lib/mixins.css');
217
+ color: red;
218
+ }
219
+ ```
220
+
221
+ The above produce this output:
222
+
223
+ ```css
224
+ p {
225
+ font-size: 50px;
226
+ color: red;
227
+ }
228
+ ```
229
+
230
+ Mixins can be declared in any CSS file. They do not need to be declared in the same file as where they are used. however, if you declare and use a mixin in the same file, you don't need to specify the URL of where the mixin is declared.
231
+
232
+ ```css
233
+ @define-mixin bigText {
234
+ font-size: 50px;
235
+ }
236
+
237
+ p {
238
+ @mixin bigText;
239
+ color: red;
240
+ }
241
+ ```
242
+
243
+ CSS modules and Mixins works perfectly together. You can include a mixin in a CSS module.
244
+
245
+ ## Importing SVG from JS(X)
246
+
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.
248
+
249
+ ## Environment Variables
250
+
251
+ Import any environment variables into your JS(X) code.
252
+
253
+ ```js
254
+ import RAILS_ENV from '@proscenium/env/RAILS_ENV'
255
+ ```
256
+
257
+ You can only access environment variables that are explicitly named. It will export `undefined` if the env variable does not exist.
258
+
259
+ ## Importing i18n
260
+
261
+ Basic support is provided for importing your Rails locale files from `config/locales/*.yml`, exporting them as JSON.
262
+
263
+ ```js
264
+ import translations from '@proscenium/i18n'
265
+ // translations.en.*
266
+ ```
267
+
268
+ ## Phlex Support
269
+
270
+ *docs needed*
271
+
272
+ ## ViewComponent Support
273
+
274
+ *docs needed*
275
+
276
+ ## Cache Busting [*COMING SOON*]
277
+
278
+ By default, all assets are not cached by the browser. But if in production, you populate the `REVISION` env variable, all CSS and JS URL's will be appended with its value as a query string, and the `Cache-Control` response header will be set to `public` and a max-age of 30 days.
279
+
280
+ For example, if you set `REVISION=v1`, URL's will be appended with `?v1`: `/my/imported/file.js?v1`.
281
+
282
+ It is assumed that the `REVISION` env var will be unique between deploys. If it isn't, then assets will continue to be cached as the same version between deploys. I recommend you assign a version number or to use the Git commit hash of the deploy. Just make sure it is unique for each deploy.
283
+
284
+ You can set the `cache_query_string` config option directly to define any query string you wish:
285
+
286
+ ```ruby
287
+ Rails.application.config.proscenium.cache_query_string = 'my-cache-busting-version-string'
288
+ ```
289
+
290
+ The cache is set with a `max-age` of 30 days. You can customise this with the `cache_max_age` config option:
291
+
292
+ ```ruby
293
+ Rails.application.config.proscenium.cache_max_age = 12.months.to_i
294
+ ```
295
+
296
+ ## Include Paths
297
+
298
+ By default, Proscenium will serve files ending with any of these extension: `js,mjs,css,jsx`, and only from `config`, `app/views`, `lib` and `node_modules` directories.
299
+
300
+ However, you can customise these paths with the `include_path` config option...
301
+
302
+ ```ruby
303
+ Rails.application.config.proscenium.include_paths << 'app/components'
304
+ ```
305
+
306
+ ## rjs is back!
307
+
308
+ Proscenium brings back RJS! Any path ending in .rjs will be served from your Rails app. This allows you to import server rendered javascript.
309
+
310
+ ## Serving from Ruby Gem
311
+
312
+ *docs needed*
313
+
314
+ ## Development
315
+
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.
317
+
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).
319
+
320
+ ### Running Go benchmarks
321
+
322
+ ```bash
323
+ go test ./internal/builder -bench=. -run="^$" -count=10 -benchmem
324
+ ```
325
+
326
+ ## Contributing
327
+
328
+ 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).
329
+
330
+ ## License
331
+
332
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
333
+
334
+ ## Code of Conduct
335
+
336
+ Everyone interacting in the Proscenium project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/joelmoss/proscenium/blob/master/CODE_OF_CONDUCT.md).
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Proscenium
4
+ class CssModule::ClassNamesResolver
5
+ def initialize(class_names, phlex_path)
6
+ @class_names = class_names.split
7
+ @stylesheets = {}
8
+ @phlex_path = phlex_path.sub_ext('.module.css')
9
+
10
+ resolve_class_names
11
+ end
12
+
13
+ def class_names
14
+ @class_names.join(' ')
15
+ end
16
+
17
+ def stylesheets
18
+ @stylesheets.map { |_, values| values[:resolved_path] }
19
+ end
20
+
21
+ private
22
+
23
+ def resolve_class_names
24
+ @class_names.map! do |class_name|
25
+ if class_name.include?('/')
26
+ if class_name.starts_with?('@')
27
+ # Scoped bare specifier (eg. "@scoped/package/lib/button@default").
28
+ _, path, name = class_name.split('@')
29
+ path = "@#{path}"
30
+ elsif class_name.starts_with?('/')
31
+ # Local path with leading slash.
32
+ path, name = class_name[1..].split('@')
33
+ else
34
+ # Bare specifier (eg. "mypackage/lib/button@default").
35
+ path, name = class_name.split('@')
36
+ end
37
+
38
+ path += '.module.css'
39
+
40
+ Utils.css_modularise_class_name name, digest: add_stylesheet(path)[:digest]
41
+ elsif class_name.starts_with?('@')
42
+ Utils.css_modularise_class_name class_name[1..],
43
+ digest: add_stylesheet(@phlex_path)[:digest]
44
+ else
45
+ class_name
46
+ end
47
+ end
48
+ end
49
+
50
+ def add_stylesheet(path)
51
+ return @stylesheets[path] if @stylesheets.key?(path)
52
+
53
+ resolved_path = Utils.resolve_path(path.to_s)
54
+
55
+ unless Rails.root.join(resolved_path[1..]).exist?
56
+ raise CssModule::StylesheetNotFound, resolved_path
57
+ end
58
+
59
+ # Note that the digest is based on the resolved (URL) path, not the original path.
60
+ @stylesheets[path] = {
61
+ resolved_path: resolved_path,
62
+ digest: Utils.digest(resolved_path)
63
+ }
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Proscenium
4
+ class CssModule::Resolver
5
+ attr_reader :side_loaded_paths
6
+
7
+ # @param path [Pathname] Absolute file system path to the Ruby file that will be side loaded.
8
+ def initialize(path, side_load: true, hash: nil)
9
+ raise ArgumentError, "'#{path}' must be a `Pathname`" unless path.is_a?(Pathname)
10
+
11
+ @path = path
12
+ @hash = hash
13
+ @css_module_path = path.sub_ext('.module.css')
14
+ @side_load = side_load
15
+ @side_loaded_paths = nil
16
+ end
17
+
18
+ # Parses the given `content` for CSS modules names ('class' attributes beginning with '@'), and
19
+ # returns the content with said CSS Modules replaced with the compiled class names.
20
+ #
21
+ # Example:
22
+ # <div class="@my_css_module_name"></div>
23
+ def compile_class_names(content)
24
+ doc = Nokogiri::HTML::DocumentFragment.parse(content)
25
+
26
+ return content if (modules = doc.css('[class*="@"]')).empty?
27
+
28
+ modules.each do |ele|
29
+ classes = ele.classes.map { |cls| cls.starts_with?('@') ? class_names!(cls[1..]) : cls }
30
+ ele['class'] = classes.join(' ')
31
+ end
32
+
33
+ doc.to_html.html_safe
34
+ end
35
+
36
+ # Resolves the given CSS class names to CSS modules. This will also side load the stylesheet if
37
+ # it exists.
38
+ #
39
+ # @param names [String, Array]
40
+ # @returns [Array] of class names generated from the given CSS module `names`.
41
+ def class_names(*names)
42
+ side_load_css_module
43
+ Utils.css_modularise_class_names names, digest: @hash
44
+ end
45
+
46
+ # Like #class_names, but requires that the stylesheet exists.
47
+ #
48
+ # @param names [String, Array]
49
+ # @raises Proscenium::CssModule::NotFound if stylesheet does not exists.
50
+ # @see #class_names
51
+ def class_names!(...)
52
+ raise CssModule::StylesheetNotFound, @css_module_path unless @css_module_path.exist?
53
+
54
+ class_names(...)
55
+ end
56
+
57
+ def side_loaded?
58
+ @side_loaded_paths.present?
59
+ end
60
+
61
+ private
62
+
63
+ def side_load_css_module
64
+ return if !@side_load || !Rails.application.config.proscenium.side_load
65
+
66
+ paths = SideLoad.append @path, { '.module.css' => :css }
67
+
68
+ @side_loaded_paths = if paths.empty?
69
+ nil
70
+ else
71
+ @hash = Utils.digest(paths[0])
72
+ paths
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Proscenium::CssModule
4
+ extend ActiveSupport::Autoload
5
+
6
+ class StylesheetNotFound < StandardError
7
+ def initialize(pathname)
8
+ @pathname = pathname
9
+ super
10
+ end
11
+
12
+ def message
13
+ "Stylesheet is required, but does not exist: #{@pathname}"
14
+ end
15
+ end
16
+
17
+ autoload :ClassNamesResolver
18
+ autoload :Resolver # deprecated
19
+
20
+ # Like `css_modules`, but will raise if the stylesheet cannot be found.
21
+ #
22
+ # @param name [Array, String]
23
+ def css_module!(names)
24
+ cssm.class_names!(names).join ' '
25
+ end
26
+
27
+ # Accepts one or more CSS class names, and transforms them into CSS module names.
28
+ #
29
+ # @param name [Array, String]
30
+ def css_module(names)
31
+ cssm.class_names(names).join ' '
32
+ end
33
+
34
+ private
35
+
36
+ def path
37
+ self.class.path
38
+ end
39
+
40
+ def cssm
41
+ @cssm ||= Resolver.new(path)
42
+ end
43
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/current_attributes'
4
+
5
+ module Proscenium
6
+ class Current < ActiveSupport::CurrentAttributes
7
+ attribute :loaded
8
+ end
9
+ end