utopia 3.0.5 → 3.0.6

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: '0314485e770b9085839422a7484c179a39e447d81b15b211d70fbb4361fdc8b2'
4
- data.tar.gz: 6e583362bc02ce48424f8e3adf7b9528bfd0990ea4ff946879d4bfbc95a52624
3
+ metadata.gz: 29332f03b21ff72e75b4f372deb3b317469ff3a5f2991ad363214b144d68ee4d
4
+ data.tar.gz: 3e3405b9bc409eeaf96e575bbb4f61fceb16298630b63f6069c2f443c2264025
5
5
  SHA512:
6
- metadata.gz: 4a81a495790a61faea6d2ab3873fd4043807134a4dec40b50177318802eefc14484e8dc218373fbf32b8bfe441ff96038ccaa21093acda9610d1ebc3ad6e87bb
7
- data.tar.gz: 35d548a73022d0307b8a631d3a8c755b1eef9fcb0edb3f7b224ac2ef57b2334018928f3c8a47e4fea6812882ce794973a639af563b5a941627f3cafcce8ca971
6
+ metadata.gz: 4f376ca1d057b6a91002e1abcab1ef98c0754772203c8d44392eca14e41df72b59e79bdb37a81e29624a6e0dd696e71ae63623a3ede9afec76b400825a8063fd
7
+ data.tar.gz: 4c20f0613d97496fd0fe1c4584384a1022677378b84504173817cdb6f6eb1a5405c440319ee482222e7c1fe7c8eea9401e1f7df89871607be8eeb023c9bf6487
checksums.yaml.gz.sig CHANGED
Binary file
@@ -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 `bake-node.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
+ "bake-node": {
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 node:install
38
+ $ bundle exec bake node:packages:static
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 node:install frozen=true
47
+ $ bundle exec bake node: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 node:importmap:show` when debugging package resolution.
100
+
101
+ See the [Bake Node documentation](https://socketry.github.io/bake-node/) for workspace packages, package selection, and alternative package managers.
76
102
 
77
103
  ### Advanced Import Map Features
78
104
 
@@ -6,6 +6,7 @@
6
6
  require "json"
7
7
  require "xrb"
8
8
  require "protocol/url"
9
+ require "bake/node/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 Bake Node static package 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 = Bake::Node::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
@@ -5,5 +5,5 @@
5
5
 
6
6
  # @namespace
7
7
  module Utopia
8
- VERSION = "3.0.5"
8
+ VERSION = "3.0.6"
9
9
  end
data/readme.md CHANGED
@@ -31,6 +31,10 @@ 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.6
35
+
36
+ - [JavaScript Packages](https://socketry.github.io/utopia/releases/index#javascript-packages)
37
+
34
38
  ### v3.0.5
35
39
 
36
40
  - **Breaking** Remove support for JavaScript packages installed in `lib/components`; use `node_modules` instead.
data/releases.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Releases
2
2
 
3
+ ## v3.0.6
4
+
5
+ ### JavaScript Packages
6
+
7
+ 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`.
8
+
9
+ Use `Utopia::ImportMap.load_manifest("public/_components")` to load the generated browser import mappings directly from the Bake Node manifest.
10
+
3
11
  ## v3.0.5
4
12
 
5
13
  - **Breaking** Remove support for JavaScript packages installed in `lib/components`; use `node_modules` instead.
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.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
@@ -57,6 +57,20 @@ dependencies:
57
57
  - - "~>"
58
58
  - !ruby/object:Gem::Version
59
59
  version: '0.20'
60
+ - !ruby/object:Gem::Dependency
61
+ name: bake-node
62
+ requirement: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ type: :runtime
68
+ prerelease: false
69
+ version_requirements: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
60
74
  - !ruby/object:Gem::Dependency
61
75
  name: concurrent-ruby
62
76
  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
metadata.gz.sig CHANGED
Binary file
@@ -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