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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/context/integrating-with-javascript.md +39 -13
- data/lib/utopia/import_map.rb +14 -0
- data/lib/utopia/version.rb +1 -1
- data/readme.md +4 -0
- data/releases.md +8 -0
- data.tar.gz.sig +0 -0
- metadata +15 -3
- metadata.gz.sig +0 -0
- data/bake/utopia/components.rb +0 -44
- data/lib/utopia/components.rb +0 -174
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 29332f03b21ff72e75b4f372deb3b317469ff3a5f2991ad363214b144d68ee4d
|
|
4
|
+
data.tar.gz: 3e3405b9bc409eeaf96e575bbb4f61fceb16298630b63f6069c2f443c2264025
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
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
|
-
$
|
|
37
|
+
$ bundle exec bake node:install
|
|
38
|
+
$ bundle exec bake node:packages:static
|
|
15
39
|
```
|
|
16
40
|
|
|
17
|
-
|
|
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
|
|
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.
|
|
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
|
|
92
|
+
import Syntax from '@socketry/syntax';
|
|
68
93
|
|
|
69
|
-
|
|
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
|
|
data/lib/utopia/import_map.rb
CHANGED
|
@@ -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
|
data/lib/utopia/version.rb
CHANGED
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.
|
|
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
|
data/bake/utopia/components.rb
DELETED
|
@@ -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
|
data/lib/utopia/components.rb
DELETED
|
@@ -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
|