utopia 3.0.5 → 3.0.7

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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/bake/utopia/server.rb +1 -1
  4. data/bake/utopia/static.rb +1 -1
  5. data/bake/utopia.rb +1 -1
  6. data/context/index.yaml +2 -0
  7. data/context/integrating-with-javascript.md +39 -13
  8. data/lib/utopia/content/builder.rb +1 -1
  9. data/lib/utopia/content/document.rb +1 -1
  10. data/lib/utopia/content/link.rb +1 -1
  11. data/lib/utopia/content/response.rb +1 -1
  12. data/lib/utopia/content.rb +1 -1
  13. data/lib/utopia/controller/actions.rb +1 -1
  14. data/lib/utopia/controller/base.rb +1 -1
  15. data/lib/utopia/controller/respond.rb +1 -1
  16. data/lib/utopia/controller/responder.rb +1 -1
  17. data/lib/utopia/controller/result.rb +1 -1
  18. data/lib/utopia/controller/variables.rb +1 -1
  19. data/lib/utopia/controller.rb +1 -1
  20. data/lib/utopia/extensions/array_split.rb +1 -1
  21. data/lib/utopia/extensions/date_comparisons.rb +1 -1
  22. data/lib/utopia/import_map.rb +15 -1
  23. data/lib/utopia/localization/resolver.rb +1 -1
  24. data/lib/utopia/localization.rb +1 -1
  25. data/lib/utopia/path/matcher.rb +1 -1
  26. data/lib/utopia/session/serialization.rb +1 -1
  27. data/lib/utopia/session.rb +1 -1
  28. data/lib/utopia/shell.rb +1 -1
  29. data/lib/utopia/static.rb +1 -1
  30. data/lib/utopia/version.rb +1 -1
  31. data/lib/utopia.rb +1 -1
  32. data/readme.md +15 -7
  33. data/releases.md +14 -0
  34. data/setup/site/bake.rb +1 -1
  35. data/setup/site/config/application.rb +4 -0
  36. data/setup/site/config/serve.rb +1 -1
  37. data/setup/site/fixtures/website.rb +1 -1
  38. data/setup/site/gems.rb +1 -1
  39. data.tar.gz.sig +0 -0
  40. metadata +17 -3
  41. metadata.gz.sig +2 -3
  42. data/bake/utopia/components.rb +0 -44
  43. data/lib/utopia/components.rb +0 -174
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '0314485e770b9085839422a7484c179a39e447d81b15b211d70fbb4361fdc8b2'
4
- data.tar.gz: 6e583362bc02ce48424f8e3adf7b9528bfd0990ea4ff946879d4bfbc95a52624
3
+ metadata.gz: dad6b7c394da35c51fd5041dd39f5caae69b81b2f847777ceee2f4685f7190ec
4
+ data.tar.gz: 0243d583b140c45fa0d954ce7579d6538872cf77b744909039bba4bb6a91b462
5
5
  SHA512:
6
- metadata.gz: 4a81a495790a61faea6d2ab3873fd4043807134a4dec40b50177318802eefc14484e8dc218373fbf32b8bfe441ff96038ccaa21093acda9610d1ebc3ad6e87bb
7
- data.tar.gz: 35d548a73022d0307b8a631d3a8c755b1eef9fcb0edb3f7b224ac2ef57b2334018928f3c8a47e4fea6812882ce794973a639af563b5a941627f3cafcce8ca971
6
+ metadata.gz: d448efb2345156f1bf0a748418b7f4fafac043c0ec23abafc17e415ae3b3fc2d5828f87c1317955c1cabfc07516b9390273e8963b20fd50fa39073e6ab283155
7
+ data.tar.gz: 6a1085fcdd2286734d12c915d7459d738a26b0ba7ae3470573a7e9356977dfc8439a8d3d368b8aaf4b137eda3401035fd71ad20f6c238b15f530b886e05ad734
checksums.yaml.gz.sig CHANGED
Binary file
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2017-2025, by Samuel Williams.
4
+ # Copyright, 2017-2026, by Samuel Williams.
5
5
 
6
6
  def initialize(...)
7
7
  super
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2017-2025, by Samuel Williams.
4
+ # Copyright, 2017-2026, by Samuel Williams.
5
5
 
6
6
  # Generate a static copy of the application.
7
7
  # @parameter output_path [String] The output path for the generated site.
data/bake/utopia.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2017-2025, by Samuel Williams.
4
+ # Copyright, 2017-2026, by Samuel Williams.
5
5
 
6
6
  def environment(name: nil)
7
7
  if name
data/context/index.yaml CHANGED
@@ -3,6 +3,8 @@
3
3
  ---
4
4
  description: Utopia is a framework for building dynamic content-driven websites.
5
5
  metadata:
6
+ bug_tracker_uri: https://github.com/socketry/utopia/issues
7
+ changelog_uri: https://github.com/socketry/utopia/blob/main/releases.md
6
8
  documentation_uri: https://socketry.github.io/utopia/
7
9
  funding_uri: https://github.com/sponsors/ioquatix/
8
10
  source_code_uri: https://github.com/socketry/utopia.git
@@ -8,20 +8,45 @@ Import maps provide a modern way to manage JavaScript module dependencies. Utopi
8
8
 
9
9
  ### Installing JavaScript Libraries
10
10
 
11
- First, install the library using npm:
11
+ Declare browser libraries as production dependencies in `package.json`. The `web-packages.packages` section selects the files that should be served and assigns their browser import names:
12
+
13
+ ```json
14
+ {
15
+ "private": true,
16
+ "dependencies": {
17
+ "@socketry/syntax": "^0.6.1"
18
+ },
19
+ "web-packages": {
20
+ "packages": {
21
+ "@socketry/syntax": {
22
+ "include": [
23
+ "Syntax.js"
24
+ ],
25
+ "imports": {
26
+ "@socketry/syntax": "Syntax.js"
27
+ }
28
+ }
29
+ }
30
+ }
31
+ }
32
+ ```
33
+
34
+ Install the dependencies using the configured package manager, then generate the browser-facing package projection:
12
35
 
13
36
  ```bash
14
- $ npm install jquery
37
+ $ bundle exec bake web:packages:install
38
+ $ bundle exec bake web:packages:update
15
39
  ```
16
40
 
17
- Copy the distribution files to `public/_components`:
41
+ This installs dependencies into `node_modules/` and copies only the selected browser files into `public/_components/`. Treat both directories as generated projections rather than authored source.
42
+
43
+ Use an immutable installation and verify the checked-in projection in CI:
18
44
 
19
45
  ```bash
20
- $ bundle exec bake utopia:components:update
46
+ $ bundle exec bake web:packages:install frozen=true
47
+ $ bundle exec bake web:packages:check
21
48
  ```
22
49
 
23
- This will copy the library's distribution files (typically from `node_modules/*/dist/`) to your `public/_components/` directory, making them available for local serving.
24
-
25
50
  ### Creating the Import Map
26
51
 
27
52
  Create a global import map in `lib/my_website/import_map.rb`:
@@ -30,12 +55,12 @@ Create a global import map in `lib/my_website/import_map.rb`:
30
55
  require "utopia/import_map"
31
56
 
32
57
  module MyWebsite
33
- IMPORT_MAP = Utopia::ImportMap.build(base: "/_components/") do |map|
34
- map.import("jquery", "./jquery/jquery.js")
35
- end
58
+ IMPORT_MAP = Utopia::ImportMap.load_manifest("public/_components")
36
59
  end
37
60
  ```
38
61
 
62
+ This loads the generated package mappings directly, so the browser import map remains synchronized with `package.json`.
63
+
39
64
  Then load this in `lib/my_website.rb`:
40
65
 
41
66
  ```ruby
@@ -64,15 +89,16 @@ Once the import map is set up, you can import and use the library in your script
64
89
  ```xrb
65
90
  <script type="module">
66
91
  // <![CDATA[
67
- import $ from 'jquery';
92
+ import Syntax from '@socketry/syntax';
68
93
 
69
- $(document).ready(function() {
70
- console.log("jQuery is ready!");
71
- });
94
+ await Syntax.highlight();
72
95
  // ]]>
73
96
  </script>
74
97
  ```
75
98
 
99
+ Inspect the generated import map with `bundle exec bake web:packages:import_map:show` when debugging package resolution.
100
+
101
+ See the [Web Packages documentation](https://socketry.github.io/web-packages/) for workspace packages, package selection, and alternative package managers.
76
102
 
77
103
  ### Advanced Import Map Features
78
104
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2025, by Samuel Williams.
4
+ # Copyright, 2025-2026, by Samuel Williams.
5
5
 
6
6
  require "xrb/builder"
7
7
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2015-2025, by Samuel Williams.
4
+ # Copyright, 2015-2026, by Samuel Williams.
5
5
 
6
6
  require_relative "links"
7
7
  require_relative "response"
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2009-2025, by Samuel Williams.
4
+ # Copyright, 2009-2026, by Samuel Williams.
5
5
  # Copyright, 2017, by Huba Nagy.
6
6
  # Copyright, 2020, by Michael Adams.
7
7
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2010-2025, by Samuel Williams.
4
+ # Copyright, 2010-2026, by Samuel Williams.
5
5
 
6
6
  require_relative "../response"
7
7
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2009-2025, by Samuel Williams.
4
+ # Copyright, 2009-2026, by Samuel Williams.
5
5
 
6
6
  require_relative "content/middleware"
7
7
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2014-2025, by Samuel Williams.
4
+ # Copyright, 2014-2026, by Samuel Williams.
5
5
 
6
6
  require_relative "../http"
7
7
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2014-2025, by Samuel Williams.
4
+ # Copyright, 2014-2026, by Samuel Williams.
5
5
 
6
6
  require_relative "../http"
7
7
  require_relative "../path"
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2016-2025, by Samuel Williams.
4
+ # Copyright, 2016-2026, by Samuel Williams.
5
5
 
6
6
  require_relative "../http"
7
7
  require_relative "../response"
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2020-2025, by Samuel Williams.
4
+ # Copyright, 2020-2026, by Samuel Williams.
5
5
 
6
6
  require_relative "middleware"
7
7
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2026, by Samuel Williams.
4
+ # Copyright, 2016-2026, by Samuel Williams.
5
5
 
6
6
  module Utopia
7
7
  module Controller
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2014-2025, by Samuel Williams.
4
+ # Copyright, 2014-2026, by Samuel Williams.
5
5
 
6
6
  module Utopia
7
7
  module Controller
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2009-2025, by Samuel Williams.
4
+ # Copyright, 2009-2026, by Samuel Williams.
5
5
 
6
6
  require_relative "controller/middleware"
7
7
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2009-2025, by Samuel Williams.
4
+ # Copyright, 2009-2026, by Samuel Williams.
5
5
 
6
6
  module Utopia
7
7
  # @namespace
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2010-2025, by Samuel Williams.
4
+ # Copyright, 2010-2026, by Samuel Williams.
5
5
 
6
6
  require "date"
7
7
 
@@ -1,11 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2025, by Samuel Williams.
4
+ # Copyright, 2025-2026, by Samuel Williams.
5
5
 
6
6
  require "json"
7
7
  require "xrb"
8
8
  require "protocol/url"
9
+ require "web/packages/manifest"
9
10
 
10
11
  module Utopia
11
12
  # Represents an import map for JavaScript modules with support for URI and relative path resolution.
@@ -55,6 +56,19 @@ module Utopia
55
56
  #
56
57
  # puts page_map.to_html
57
58
  class ImportMap
59
+ # Load the import mappings from a Web Packages static projection manifest.
60
+ # @parameter root [String | Pathname] The static package output directory.
61
+ # @returns [ImportMap] A frozen import map using the manifest's public base URL.
62
+ def self.load_manifest(root)
63
+ manifest = Web::Packages::Manifest.load(root)
64
+ base = Protocol::URL[manifest.data.fetch("base")]
65
+ imports = manifest.import_map.fetch("imports").transform_values do |value|
66
+ Protocol::URL[value].relative_to(base).to_s
67
+ end
68
+
69
+ return self.new(imports, base: base).freeze
70
+ end
71
+
58
72
  # Builder class for constructing import maps with scoped base URIs.
59
73
  #
60
74
  # The builder supports nested `with(base:)` blocks where each base is resolved
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2026, by Samuel Williams.
4
+ # Copyright, 2015-2026, by Samuel Williams.
5
5
 
6
6
  require_relative "../response"
7
7
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2009-2025, by Samuel Williams.
4
+ # Copyright, 2009-2026, by Samuel Williams.
5
5
 
6
6
  require_relative "localization/preferences"
7
7
  require_relative "localization/locales"
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2015-2025, by Samuel Williams.
4
+ # Copyright, 2015-2026, by Samuel Williams.
5
5
 
6
6
  require_relative "../path"
7
7
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2019-2025, by Samuel Williams.
4
+ # Copyright, 2019-2026, by Samuel Williams.
5
5
 
6
6
  require "msgpack"
7
7
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2014-2025, by Samuel Williams.
4
+ # Copyright, 2014-2026, by Samuel Williams.
5
5
  # Copyright, 2019, by Huba Nagy.
6
6
 
7
7
  require_relative "session/middleware"
data/lib/utopia/shell.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2020-2025, by Samuel Williams.
4
+ # Copyright, 2020-2026, by Samuel Williams.
5
5
 
6
6
  require "protocol/http/request"
7
7
  require_relative "application"
data/lib/utopia/static.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2009-2025, by Samuel Williams.
4
+ # Copyright, 2009-2026, by Samuel Williams.
5
5
 
6
6
  require_relative "static/middleware"
7
7
 
@@ -5,5 +5,5 @@
5
5
 
6
6
  # @namespace
7
7
  module Utopia
8
- VERSION = "3.0.5"
8
+ VERSION = "3.0.7"
9
9
  end
data/lib/utopia.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2009-2025, by Samuel Williams.
4
+ # Copyright, 2009-2026, by Samuel Williams.
5
5
 
6
6
  require_relative "utopia/version"
7
7
 
data/readme.md CHANGED
@@ -31,6 +31,14 @@ Please see the [project documentation](https://socketry.github.io/utopia/) for m
31
31
 
32
32
  Please see the [project releases](https://socketry.github.io/utopia/releases/index) for all releases.
33
33
 
34
+ ### v3.0.7
35
+
36
+ - [Web Packages](https://socketry.github.io/utopia/releases/index#web-packages)
37
+
38
+ ### v3.0.6
39
+
40
+ - [JavaScript Packages](https://socketry.github.io/utopia/releases/index#javascript-packages)
41
+
34
42
  ### v3.0.5
35
43
 
36
44
  - **Breaking** Remove support for JavaScript packages installed in `lib/components`; use `node_modules` instead.
@@ -90,26 +98,26 @@ The 3.0.x series is considered a development release while the protocol HTTP app
90
98
 
91
99
  We welcome contributions to this project.
92
100
 
93
- 1. Fork it.
101
+ 1. Fork the repository.
94
102
  2. Create your feature branch (`git checkout -b my-new-feature`).
95
- 3. Commit your changes (`git commit -am 'Add some feature'`).
103
+ 3. Commit your changes (`git commit -am 'Add some feature.'`).
96
104
  4. Push to the branch (`git push origin my-new-feature`).
97
- 5. Create new Pull Request.
105
+ 5. Create a new pull request.
98
106
 
99
107
  ### Running Tests
100
108
 
101
109
  To run the test suite:
102
110
 
103
- ``` shell
104
- bundle exec sus
111
+ ``` bash
112
+ $ bundle exec sus
105
113
  ```
106
114
 
107
115
  ### Making Releases
108
116
 
109
117
  To make a new release:
110
118
 
111
- ``` shell
112
- bundle exec bake gem:release:patch # or minor or major
119
+ ``` bash
120
+ $ bundle exec bake gem:release:patch # or minor or major
113
121
  ```
114
122
 
115
123
  ### Developer Certificate of Origin
data/releases.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Releases
2
2
 
3
+ ## v3.0.7
4
+
5
+ ### Web Packages
6
+
7
+ Utopia now uses `web-packages` v0.2 after the project was renamed from `bake-node`. Applications should use the `web-packages` key in `package.json` and invoke package management through the `web:packages` Bake namespace. The generated `.manifest.json` format remains compatible.
8
+
9
+ ## v3.0.6
10
+
11
+ ### JavaScript Packages
12
+
13
+ Utopia now depends on `bake-node` for JavaScript dependency installation and static package projection. `Utopia::Components` and `utopia:components:update` have been removed. Replace the old task with `bundle exec bake node:packages:static`, and migrate package selection from `utopia.components` to `bake-node.packages` in `package.json`.
14
+
15
+ Use `Utopia::ImportMap.load_manifest("public/_components")` to load the generated browser import mappings directly from the Bake Node manifest.
16
+
3
17
  ## v3.0.5
4
18
 
5
19
  - **Breaking** Remove support for JavaScript packages installed in `lib/components`; use `node_modules` instead.
data/setup/site/bake.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2020-2025, by Samuel Williams.
4
+ # Copyright, 2020-2026, by Samuel Williams.
5
5
 
6
6
  def deploy
7
7
  # This task is typiclly run after the site is updated but before the server is restarted.
@@ -1,5 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Released under the MIT License.
4
+ # Copyright, 2010-2026, by Samuel Williams.
5
+ # Copyright, 2020, by Michael Adams.
6
+
3
7
  require_relative "environment"
4
8
 
5
9
  require "utopia/application"
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2026, by Samuel Williams.
4
+ # Copyright, 2019-2026, by Samuel Williams.
5
5
 
6
6
  require "utopia/application"
7
7
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2016-2025, by Samuel Williams.
4
+ # Copyright, 2016-2026, by Samuel Williams.
5
5
 
6
6
  require "protocol/http/request"
7
7
  require "sus/fixtures/async/http"
data/setup/site/gems.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2012-2025, by Samuel Williams.
4
+ # Copyright, 2012-2026, by Samuel Williams.
5
5
 
6
6
  source "https://rubygems.org"
7
7
 
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: utopia
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.5
4
+ version: 3.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
@@ -253,6 +253,20 @@ dependencies:
253
253
  - - "~>"
254
254
  - !ruby/object:Gem::Version
255
255
  version: '0.1'
256
+ - !ruby/object:Gem::Dependency
257
+ name: web-packages
258
+ requirement: !ruby/object:Gem::Requirement
259
+ requirements:
260
+ - - "~>"
261
+ - !ruby/object:Gem::Version
262
+ version: '0.2'
263
+ type: :runtime
264
+ prerelease: false
265
+ version_requirements: !ruby/object:Gem::Requirement
266
+ requirements:
267
+ - - "~>"
268
+ - !ruby/object:Gem::Version
269
+ version: '0.2'
256
270
  - !ruby/object:Gem::Dependency
257
271
  name: xrb
258
272
  requirement: !ruby/object:Gem::Requirement
@@ -272,7 +286,6 @@ extensions: []
272
286
  extra_rdoc_files: []
273
287
  files:
274
288
  - bake/utopia.rb
275
- - bake/utopia/components.rb
276
289
  - bake/utopia/environment.rb
277
290
  - bake/utopia/server.rb
278
291
  - bake/utopia/shell.rb
@@ -290,7 +303,6 @@ files:
290
303
  - lib/traces/provider/utopia/static/middleware.rb
291
304
  - lib/utopia.rb
292
305
  - lib/utopia/application.rb
293
- - lib/utopia/components.rb
294
306
  - lib/utopia/content.rb
295
307
  - lib/utopia/content/builder.rb
296
308
  - lib/utopia/content/document.rb
@@ -381,6 +393,8 @@ homepage: https://github.com/socketry/utopia
381
393
  licenses:
382
394
  - MIT
383
395
  metadata:
396
+ bug_tracker_uri: https://github.com/socketry/utopia/issues
397
+ changelog_uri: https://github.com/socketry/utopia/blob/main/releases.md
384
398
  documentation_uri: https://socketry.github.io/utopia/
385
399
  funding_uri: https://github.com/sponsors/ioquatix/
386
400
  source_code_uri: https://github.com/socketry/utopia.git
metadata.gz.sig CHANGED
@@ -1,3 +1,2 @@
1
- ~2�DQ���g�y]R�X�@8����v���e:覴�������M�~:r��)�^�E��x ����~��nF6ڌm�;J3D��Vb��A���m��{�<~7��7wڔc��"32��A�X���������8���puC3rZP�=U@;oAqTQv;���´��Pjy>�jj/3MB ���`�2���j����U\F
2
- Ol�c���F��(��a���3���C�����È123Eo>�p�T8���}ns>5
3
- ��rN��_���RmJ�S�&z��ǹ0()b���ʾ���
1
+ Rj�!�q�� �*���vڀw��:,{���x��(F��Z uoO'hI�Ǣ���Rz��gtB)�*����dulFDu���v��ӁTO��*"ro���Gs/�:����d�i����ET�"
2
+ �����26����ݫ����b��"��Ղ��0y��좯&T�����Ԓam
@@ -1,44 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Released under the MIT License.
4
- # Copyright, 2026, by Samuel Williams.
5
-
6
- NPM = ENV["NPM"] || "npm"
7
-
8
- # Update public components from production JavaScript packages.
9
- #
10
- # Packages are copied from their `dist` directory when present, or otherwise
11
- # from the package root. The `utopia.components` section of `package.json` can
12
- # specify per-package `include` patterns to select only required files.
13
- #
14
- # @parameter root [String] The project root directory.
15
- def update(root: context.root)
16
- require "json"
17
- require "open3"
18
- require "utopia/components"
19
-
20
- components = Utopia::Components.new(root)
21
- production_packages = fetch_production_packages(components.package_root)
22
-
23
- components.update(production_packages)
24
- end
25
-
26
- private
27
-
28
- def fetch_production_packages(package_root)
29
- stdout, _status = Open3.capture2(NPM, "ls", "--production", "--json", chdir: package_root.to_s)
30
- json = JSON.parse(stdout)
31
-
32
- flatten_package_dependencies(json).sort.uniq
33
- end
34
-
35
- def flatten_package_dependencies(json, into = [])
36
- if json["dependencies"]
37
- json["dependencies"].each do |name, details|
38
- into << name
39
- flatten_package_dependencies(details, into)
40
- end
41
- end
42
-
43
- return into
44
- end
@@ -1,174 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Released under the MIT License.
4
- # Copyright, 2026, by Samuel Williams.
5
-
6
- require "fileutils"
7
- require "json"
8
- require "pathname"
9
-
10
- module Utopia
11
- # Installs JavaScript packages from `node_modules` into the public components directory. Package contents are copied from `dist` when it exists, otherwise from the package root.
12
- #
13
- # By default, the complete source directory is installed. Projects can limit an individual package to a set of files using `utopia.components` in their `package.json` file.
14
- class Components
15
- # Initialize a component installer for the given project root.
16
- #
17
- # @parameter root [String | Pathname] The project root directory.
18
- def initialize(root)
19
- @root = Pathname.new(root)
20
- @package_root = @root + "node_modules"
21
-
22
- @install_root = @root + "public/_components"
23
- @configuration = load_configuration
24
- end
25
-
26
- # @attribute [Pathname] The directory containing the installed JavaScript packages.
27
- attr :package_root
28
-
29
- # Update the specified packages in the public components directory.
30
- #
31
- # @parameter package_names [Array(String)] The production package names to install.
32
- def update(package_names)
33
- expand_package_paths(@package_root).each do |package_path|
34
- package_name = package_path.relative_path_from(@package_root).to_s
35
-
36
- if package_names.include?(package_name)
37
- install(package_name, package_path)
38
- end
39
- end
40
- end
41
-
42
- private
43
-
44
- # Load the optional per-package installation rules. A missing `package.json`, or a file without `utopia.components`, preserves the default behaviour of copying complete packages.
45
- # @returns [Hash] The per-package installation rules.
46
- def load_configuration
47
- package_path = @root + "package.json"
48
-
49
- unless package_path.file?
50
- return {}
51
- end
52
-
53
- configuration = JSON.parse(package_path.read).dig("utopia", "components") || {}
54
-
55
- unless configuration.is_a?(Hash)
56
- raise ArgumentError, "utopia.components must be an object!"
57
- end
58
-
59
- return configuration
60
- end
61
-
62
- # Install one package. Distribution directories are preferred because they generally contain the browser-ready form of a package.
63
- # @parameter package_name [String] The package name relative to `node_modules`.
64
- # @parameter package_path [Pathname] The package source directory.
65
- def install(package_name, package_path)
66
- install_path = @install_root + package_name
67
- dist_path = package_path + "dist"
68
-
69
- if dist_path.directory?
70
- source_path = dist_path
71
- else
72
- source_path = package_path
73
- end
74
-
75
- configuration = @configuration[package_name]
76
-
77
- if configuration
78
- install_selected(package_name, source_path, install_path, configuration)
79
- else
80
- FileUtils::Verbose.rm_rf(install_path)
81
- FileUtils::Verbose.mkpath(install_path.dirname)
82
- FileUtils::Verbose.cp_r(source_path, install_path)
83
- end
84
- end
85
-
86
- # Install only the files matched by the configured include patterns. Every pattern is resolved before removing the existing installation, so an invalid configuration cannot leave a package partially installed or remove a previously working copy.
87
- # @parameter package_name [String] The package name relative to `node_modules`.
88
- # @parameter source_path [Pathname] The package source directory.
89
- # @parameter install_path [Pathname] The destination directory.
90
- # @parameter configuration [Hash] The package installation rules.
91
- def install_selected(package_name, source_path, install_path, configuration)
92
- unless configuration.is_a?(Hash)
93
- raise ArgumentError, "utopia.components.#{package_name}.include must be a non-empty array!"
94
- end
95
-
96
- include_patterns = configuration["include"]
97
-
98
- unless include_patterns.is_a?(Array) && include_patterns.any?
99
- raise ArgumentError, "utopia.components.#{package_name}.include must be a non-empty array!"
100
- end
101
-
102
- paths = include_patterns.flat_map do |pattern|
103
- included_paths(package_name, source_path, pattern)
104
- end.uniq.sort
105
-
106
- FileUtils::Verbose.rm_rf(install_path)
107
-
108
- paths.each do |relative_path|
109
- source_file = source_path + relative_path
110
- install_file = install_path + relative_path
111
-
112
- FileUtils::Verbose.mkpath(install_file.dirname)
113
- FileUtils::Verbose.cp(source_file, install_file)
114
- end
115
- end
116
-
117
- # Expand one include pattern into files relative to the package source. Directories are excluded so each result can be copied independently.
118
- # @parameter package_name [String] The package name used in validation errors.
119
- # @parameter source_path [Pathname] The package source directory.
120
- # @parameter pattern [String] The include pattern to expand.
121
- # @returns [Array(String)] The matching file paths relative to the package source.
122
- def included_paths(package_name, source_path, pattern)
123
- unless pattern.is_a?(String) && relative_pattern?(pattern)
124
- raise ArgumentError, "Invalid include pattern for #{package_name}: #{pattern.inspect}"
125
- end
126
-
127
- paths = Dir.glob(pattern, base: source_path.to_s).select do |relative_path|
128
- (source_path + relative_path).file?
129
- end
130
-
131
- if paths.empty?
132
- raise ArgumentError, "Include pattern for #{package_name} matched no files: #{pattern.inspect}"
133
- end
134
-
135
- return paths
136
- end
137
-
138
- # Determine whether the pattern is contained within the package source. Absolute paths and parent traversal are rejected because they could otherwise copy arbitrary files from outside the package.
139
- # @parameter pattern [String] The include pattern to validate.
140
- # @returns [Boolean] Whether the pattern is relative and does not contain parent traversal.
141
- def relative_pattern?(pattern)
142
- path = Pathname.new(pattern)
143
-
144
- if path.absolute?
145
- return false
146
- end
147
-
148
- if path.each_filename.any?{|component| component == ".."}
149
- return false
150
- end
151
-
152
- return true
153
- end
154
-
155
- # Enumerate packages in `node_modules`, descending through scoped package directories such as `@socketry` while preserving their scoped names.
156
- # @parameter root [Pathname] The directory to enumerate.
157
- # @parameter into [Array(Pathname)] The array into which package paths are appended.
158
- # @returns [Array(Pathname)] The discovered package directories.
159
- def expand_package_paths(root, into = [])
160
- root.children.select(&:directory?).each do |path|
161
- basename = path.basename.to_s
162
-
163
- # Handle organisation sub-directories which start with an '@' symbol:
164
- if basename.start_with?("@")
165
- expand_package_paths(path, into)
166
- else
167
- into << path
168
- end
169
- end
170
-
171
- return into
172
- end
173
- end
174
- end