jspm-rails 0.1.0 → 0.3.0
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
- data/CHANGELOG.md +13 -2
- data/README.md +18 -22
- data/lib/jspm/check.rb +4 -17
- data/lib/jspm/install.rb +1 -8
- data/lib/jspm/lockfile.rb +16 -17
- data/lib/jspm/paths.rb +2 -1
- data/lib/jspm/tags.rb +1 -1
- data/lib/jspm/trace.mjs +2 -2
- data/lib/jspm/trace.rb +13 -3
- data/lib/jspm/version.rb +1 -1
- data/lib/jspm/yarn_lock.rb +50 -0
- data/lib/jspm-rails.rb +1 -1
- metadata +2 -2
- data/lib/jspm/assets.rb +0 -46
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2066a105c29bf4cebd19a25c1afa9ac3bfb7530b080cd5f347ed1c69e3cd56b2
|
|
4
|
+
data.tar.gz: 1e89f49cd667a1b7771b90b2df5a8d36918d0cdb78334fe6cdee6d831d470fcc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4253a93277d3368d3e2e34fe023aeee5ac6d74090417941e36056ea65ccffb3f9465909e036ae52a00caa3bb403ef0b3d9ee7e6de26299b0f741487baf11fa30
|
|
7
|
+
data.tar.gz: 330b9d72f4d2b528aa4701d655cc066adf83da4fbfef4f718eb472c624d0398f52542e05ee84b97bfc3b25d4219f227e25ba2f02bc7aab9bd524cefc8ddae6ef
|
data/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.
|
|
3
|
+
## 0.3.0 (unreleased)
|
|
4
4
|
|
|
5
|
-
-
|
|
5
|
+
- Names every module in `app/javascript` in the import map, so stimulus-rails's `eagerLoadControllersFrom` finds controllers.
|
|
6
|
+
|
|
7
|
+
## 0.2.0 (unreleased)
|
|
8
|
+
|
|
9
|
+
- Reads Yarn 1's `yarn.lock` as well as `package-lock.json`.
|
|
10
|
+
- No longer copies npm packages' stylesheets, fonts and images. Put the directories you use on Propshaft's load path instead.
|
|
11
|
+
- A package in the lockfile wins over a gem's file with the same name.
|
|
12
|
+
|
|
13
|
+
## 0.1.0 (2026-11-13)
|
|
14
|
+
|
|
15
|
+
- `bin/rails jspm:install` traces `app/javascript/application.js` with jspm and vendors every module it reaches. `config/importmap.json` locks versions, and `package-lock.json` overrides it for packages installed with npm.
|
|
6
16
|
- `javascript_jspm_tags` renders the import map with Propshaft's digested paths, preloads, and the entry module.
|
|
7
17
|
- Modules on Propshaft's load path, including engines', are importable by asset name, the way importmap-rails pins work.
|
|
18
|
+
- Modules on Propshaft's load path, including engines', are importable by asset name, the way importmap-rails pins work.
|
|
8
19
|
- `bin/rails jspm:check` fails when `config/importmap.json` or `vendor/jspm` is out of date.
|
|
9
20
|
- Warns when the import map can't match `package-lock.json`.
|
data/README.md
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
_ _ _
|
|
3
|
-
(_) ___ _ __ _ __ _ _ __ _ (_)| | ___
|
|
4
|
-
| |(_-<| '_ \| ' \ ___ | '_|/ _` || || |(_-<
|
|
5
|
-
_/ |/__/| .__/|_|_|_||___||_| \__,_||_||_|/__/
|
|
6
|
-
|__/ |_|
|
|
7
|
-
```
|
|
1
|
+
<p align="center"><img src="docs/banner.svg" alt="jspm-rails" width="576"></p>
|
|
8
2
|
|
|
9
3
|
[](https://github.com/ClayShentrup/jspm-rails/actions/workflows/ci.yml)
|
|
10
4
|
|
|
@@ -19,7 +13,6 @@ bin/rails jspm:install
|
|
|
19
13
|
```
|
|
20
14
|
traced ./app/javascript/application.js
|
|
21
15
|
vendored 31 modules from ga.jspm.io
|
|
22
|
-
copied 73 assets from node_modules
|
|
23
16
|
wrote config/importmap.json
|
|
24
17
|
```
|
|
25
18
|
|
|
@@ -96,6 +89,8 @@ import 'controllers' // app/javascript/controllers/index.js
|
|
|
96
89
|
import { helper } from './helpers.js'
|
|
97
90
|
```
|
|
98
91
|
|
|
92
|
+
**Every module in `app/javascript` is in the import map by name,** even ones nothing imports directly, as importmap-rails's `pin_all_from` names them. So stimulus-rails's `eagerLoadControllersFrom("controllers", application)` and `lazyLoadControllersFrom` work unchanged.
|
|
93
|
+
|
|
99
94
|
**Import modules that gems ship the same way.** Every engine's `app/assets` directories are on Propshaft's load path, so a gem's modules are importable by asset name with no setup:
|
|
100
95
|
|
|
101
96
|
```js
|
|
@@ -112,7 +107,7 @@ const { default: katex } = await import('katex')
|
|
|
112
107
|
|
|
113
108
|
**Plain jspm works on its own.** The install resolves each package you import from jspm.io. That's the latest version, or the newest within a range you give in `package.json`'s `dependencies`, as jspm does. `config/importmap.json` is jspm's lockfile: every install keeps the versions it already holds.
|
|
114
109
|
|
|
115
|
-
**Install a package with npm when you want more.** Its version in `package-lock.json` then overrides the import map, and you get:
|
|
110
|
+
**Install a package with npm or Yarn 1 when you want more.** Its version in `package-lock.json` or `yarn.lock` then overrides the import map, and you get:
|
|
116
111
|
|
|
117
112
|
- Dependabot pull requests for it.
|
|
118
113
|
- Its stylesheets, fonts and images in `node_modules`, at the same version as its JavaScript.
|
|
@@ -121,17 +116,19 @@ Mix the two freely. A package's JavaScript always comes from jspm.io either way.
|
|
|
121
116
|
|
|
122
117
|
## Stylesheets, fonts and images
|
|
123
118
|
|
|
124
|
-
jspm.io serves JavaScript. For a package's other files, install it with npm
|
|
119
|
+
jspm.io serves JavaScript. For a package's other files, install it with npm and put the directory that holds them on Propshaft's load path, as Rails's generated `config/initializers/assets.rb` suggests:
|
|
125
120
|
|
|
126
|
-
```
|
|
127
|
-
|
|
121
|
+
```ruby
|
|
122
|
+
Rails.application.config.assets.paths << Rails.root.join('node_modules/katex/dist')
|
|
128
123
|
```
|
|
129
124
|
|
|
130
|
-
|
|
125
|
+
```erb
|
|
126
|
+
<%= stylesheet_link_tag 'katex' %>
|
|
127
|
+
```
|
|
131
128
|
|
|
132
|
-
|
|
129
|
+
Propshaft rewrites the `url()`s in the stylesheet to digested paths, so the fonts load too. The CSS comes from npm and the JavaScript from jspm.io, both at the version in your lockfile.
|
|
133
130
|
|
|
134
|
-
|
|
131
|
+
Add the directory you use, not the whole package or all of `node_modules`. Propshaft checks every file on its load path before each development request and precompiles every one, so `bootstrap-icons/font` (7 files) costs far less than the package (2,089).
|
|
135
132
|
|
|
136
133
|
## Keeping up to date
|
|
137
134
|
|
|
@@ -145,7 +142,7 @@ Why copy, instead of putting `node_modules` on the load path? Propshaft checks e
|
|
|
145
142
|
bin/rails jspm:check
|
|
146
143
|
```
|
|
147
144
|
|
|
148
|
-
It traces again without writing anything. It fails when an install would change `config/importmap.json`, for example after Dependabot bumps `package-lock.json`. It also fails when a module is missing from `vendor/jspm
|
|
145
|
+
It traces again without writing anything. It fails when an install would change `config/importmap.json`, for example after Dependabot bumps `package-lock.json`. It also fails when a module is missing from `vendor/jspm`:
|
|
149
146
|
|
|
150
147
|
```
|
|
151
148
|
stale config/importmap.json
|
|
@@ -165,7 +162,7 @@ npm installs two versions of a package when its dependents' ranges don't overlap
|
|
|
165
162
|
## Deploying
|
|
166
163
|
|
|
167
164
|
- **Commit `config/importmap.json` and `vendor/jspm`.** `assets:precompile` digests the vendored modules like any other asset.
|
|
168
|
-
- **Production needs no
|
|
165
|
+
- **Production needs no jspm.io,** and no Node unless you serve stylesheets from `node_modules`, which needs your packages installed before precompiling.
|
|
169
166
|
- **An `asset_host` on another origin must send `Access-Control-Allow-Origin`.** Browsers load modules and fonts across origins only with CORS.
|
|
170
167
|
- **Content Security Policy works.** Every tag carries the request's nonce.
|
|
171
168
|
- **Turbo reloads the page when the import map changes,** because the map's tag has `data-turbo-track="reload"`.
|
|
@@ -176,8 +173,7 @@ npm installs two versions of a package when its dependents' ranges don't overlap
|
|
|
176
173
|
|
|
177
174
|
1. Boots the app, then traces `app/javascript/application.js` with [`@jspm/generator`](https://github.com/jspm/generator). Every module on Propshaft's load path is importable by asset name. The previous `config/importmap.json` locks versions, and `package-lock.json` pins every package npm installed. Only packages your code reaches end up in the map, so KaTeX's command-line dependency stays out.
|
|
178
175
|
2. Downloads each traced module from jspm.io, with its source map, into `vendor/jspm/ga.jspm.io`.
|
|
179
|
-
3.
|
|
180
|
-
4. Writes jspm's import map to `config/importmap.json`, with the list of modules to preload. Local modules appear as `asset:` plus their logical path, such as `asset:astrology/math_controller.js`, so the map is the same on every machine.
|
|
176
|
+
3. Writes jspm's import map to `config/importmap.json`, with the list of modules to preload. Local modules appear as `asset:` plus their logical path, such as `asset:astrology/math_controller.js`, so the map is the same on every machine.
|
|
181
177
|
|
|
182
178
|
`javascript_jspm_tags` renders that map for Propshaft. Propshaft serves each file under a digested name, so the helper rewrites the map's URLs:
|
|
183
179
|
|
|
@@ -239,15 +235,15 @@ Node printed the reason just above. Usually Node isn't on your `PATH`, or `@jspm
|
|
|
239
235
|
**Modules or fonts fail to load with an asset host**
|
|
240
236
|
The asset host must send `Access-Control-Allow-Origin`. See [Deploying](#deploying).
|
|
241
237
|
|
|
242
|
-
**A package's stylesheet
|
|
243
|
-
|
|
238
|
+
**A package's stylesheet raises `Propshaft::MissingAssetError`**
|
|
239
|
+
Add its directory to `config.assets.paths`. See [Stylesheets, fonts and images](#stylesheets-fonts-and-images).
|
|
244
240
|
|
|
245
241
|
## Development
|
|
246
242
|
|
|
247
243
|
```sh
|
|
248
244
|
bundle install
|
|
249
245
|
npm ci && npx playwright install chromium
|
|
250
|
-
(cd spec/dummy && npm ci) && (cd spec/fixtures/conflicted && npm ci)
|
|
246
|
+
(cd spec/dummy && npm ci) && (cd spec/fixtures/conflicted && npm ci) && (cd spec/fixtures/yarn && yarn install --frozen-lockfile)
|
|
251
247
|
bundle exec rake
|
|
252
248
|
```
|
|
253
249
|
|
data/lib/jspm/check.rb
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'fileutils'
|
|
4
3
|
require 'json'
|
|
5
4
|
|
|
6
5
|
module Jspm
|
|
@@ -10,11 +9,10 @@ module Jspm
|
|
|
10
9
|
def self.call(asset_paths:, output:, root:)
|
|
11
10
|
lockfile = Lockfile.read(root)
|
|
12
11
|
trace = Trace.call(asset_paths:, lockfile:, root:)
|
|
13
|
-
new(
|
|
12
|
+
new(output:, root:, trace:).call
|
|
14
13
|
end
|
|
15
14
|
|
|
16
|
-
def initialize(
|
|
17
|
-
@lockfile = lockfile
|
|
15
|
+
def initialize(output:, root:, trace:)
|
|
18
16
|
@output = output
|
|
19
17
|
@root = root
|
|
20
18
|
@trace = trace
|
|
@@ -23,12 +21,10 @@ module Jspm
|
|
|
23
21
|
def call
|
|
24
22
|
written = written?
|
|
25
23
|
missing = missing_modules
|
|
26
|
-
stale_assets = stale_asset_paths
|
|
27
24
|
report('stale', Paths::IMPORT_MAP) unless written
|
|
28
25
|
missing.each { |path| report('missing', path) }
|
|
29
|
-
stale_assets.each { |path| report('stale', path) }
|
|
30
26
|
|
|
31
|
-
up_to_date = written && missing.empty?
|
|
27
|
+
up_to_date = written && missing.empty?
|
|
32
28
|
if up_to_date
|
|
33
29
|
report('ok', 'nothing to install')
|
|
34
30
|
else
|
|
@@ -39,22 +35,13 @@ module Jspm
|
|
|
39
35
|
|
|
40
36
|
private
|
|
41
37
|
|
|
42
|
-
attr_reader :
|
|
38
|
+
attr_reader :output, :root, :trace
|
|
43
39
|
|
|
44
40
|
def written?
|
|
45
41
|
import_map_file = root.join(Paths::IMPORT_MAP)
|
|
46
42
|
import_map_file.exist? && JSON.parse(import_map_file.read) == trace.import_map
|
|
47
43
|
end
|
|
48
44
|
|
|
49
|
-
def stale_asset_paths
|
|
50
|
-
copies = Assets.copies(lockfile:, root:)
|
|
51
|
-
stale =
|
|
52
|
-
copies.reject do |source, target|
|
|
53
|
-
target.file? && FileUtils.compare_file(source, target)
|
|
54
|
-
end
|
|
55
|
-
stale.map { |_source, target| target.relative_path_from(root).to_s }
|
|
56
|
-
end
|
|
57
|
-
|
|
58
45
|
def missing_modules
|
|
59
46
|
vendored = trace.cdn_urls.map { |url| File.join(Paths::VENDOR, Paths.logical(url)) }
|
|
60
47
|
vendored.reject { |path| root.join(path).file? }
|
data/lib/jspm/install.rb
CHANGED
|
@@ -5,8 +5,7 @@ require 'json'
|
|
|
5
5
|
require 'net/http'
|
|
6
6
|
|
|
7
7
|
module Jspm
|
|
8
|
-
# Vendors every module the trace reaches from jspm.io,
|
|
9
|
-
# dependencies' stylesheets, fonts and images, and writes
|
|
8
|
+
# Vendors every module the trace reaches from jspm.io, and writes
|
|
10
9
|
# config/importmap.json.
|
|
11
10
|
class Install
|
|
12
11
|
SOURCE_MAP = %r{//# sourceMappingURL=(?<path>\S+\.map)\s*\z}
|
|
@@ -32,16 +31,10 @@ module Jspm
|
|
|
32
31
|
cdn = URI(Paths::CDN)
|
|
33
32
|
cdn_urls = trace.cdn_urls
|
|
34
33
|
Net::HTTP.start(cdn.host, cdn.port, use_ssl: true) { |http| download_all(http, cdn_urls, vendor_dir) }
|
|
35
|
-
copies = Assets.copies(lockfile:, root:)
|
|
36
|
-
copies.each do |source, target|
|
|
37
|
-
target.dirname.mkpath
|
|
38
|
-
FileUtils.cp(source, target)
|
|
39
|
-
end
|
|
40
34
|
root.join(Paths::IMPORT_MAP).write("#{JSON.pretty_generate(trace.import_map)}\n")
|
|
41
35
|
|
|
42
36
|
report('traced', Paths::ENTRY_PATH)
|
|
43
37
|
report('vendored', "#{cdn_urls.size} modules from ga.jspm.io")
|
|
44
|
-
report('copied', "#{copies.size} assets from node_modules")
|
|
45
38
|
report('wrote', Paths::IMPORT_MAP)
|
|
46
39
|
warn_about_versions
|
|
47
40
|
end
|
data/lib/jspm/lockfile.rb
CHANGED
|
@@ -3,14 +3,11 @@
|
|
|
3
3
|
require 'json'
|
|
4
4
|
|
|
5
5
|
module Jspm
|
|
6
|
-
# The packages npm installed, as package-lock.json
|
|
6
|
+
# The packages npm or Yarn installed, as package-lock.json or yarn.lock
|
|
7
|
+
# records them.
|
|
7
8
|
class Lockfile
|
|
8
9
|
Package =
|
|
9
|
-
Data.define(:
|
|
10
|
-
def browser?
|
|
11
|
-
top_level && !dev
|
|
12
|
-
end
|
|
13
|
-
|
|
10
|
+
Data.define(:name, :top_level, :version) do
|
|
14
11
|
def resolution
|
|
15
12
|
[name, version]
|
|
16
13
|
end
|
|
@@ -21,18 +18,24 @@ module Jspm
|
|
|
21
18
|
end
|
|
22
19
|
private_constant :Package
|
|
23
20
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
class << self
|
|
22
|
+
def read(root)
|
|
23
|
+
packages = root.join('yarn.lock').exist? ? YarnLock.packages(root) : npm_packages(root)
|
|
24
|
+
new(packages: packages.map { |package| Package.new(**package) })
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def npm_packages(root)
|
|
30
|
+
json = JSON.parse(root.join('package-lock.json').read)
|
|
31
|
+
installed = json.fetch('packages').reject { |path, _package| path.empty? }
|
|
28
32
|
installed.map do |path, package|
|
|
29
33
|
name = path.split('node_modules/').last
|
|
30
34
|
top_level = path.scan('node_modules/').one?
|
|
31
35
|
version = package.fetch('version')
|
|
32
|
-
|
|
33
|
-
Package.new(dev:, name:, path:, top_level:, version:)
|
|
36
|
+
{ name:, top_level:, version: }
|
|
34
37
|
end
|
|
35
|
-
|
|
38
|
+
end
|
|
36
39
|
end
|
|
37
40
|
|
|
38
41
|
def initialize(packages:)
|
|
@@ -43,10 +46,6 @@ module Jspm
|
|
|
43
46
|
packages.select(&:top_level).to_h(&:resolution)
|
|
44
47
|
end
|
|
45
48
|
|
|
46
|
-
def browser_packages
|
|
47
|
-
packages.select(&:browser?)
|
|
48
|
-
end
|
|
49
|
-
|
|
50
49
|
def nested_versions(name)
|
|
51
50
|
nested = packages.select { |package| package.nested?(name) }
|
|
52
51
|
nested.map(&:version)
|
data/lib/jspm/paths.rb
CHANGED
|
@@ -12,7 +12,8 @@ module Jspm
|
|
|
12
12
|
ENTRY_PATH = "#{LOCAL}#{ENTRY}".freeze
|
|
13
13
|
IMPORT_MAP = 'config/importmap.json'
|
|
14
14
|
VENDOR = 'vendor/jspm'
|
|
15
|
-
public_constant :CDN, :ENTRY, :ENTRY_PATH, :IMPORT_MAP, :
|
|
15
|
+
public_constant :CDN, :ENTRY, :ENTRY_PATH, :IMPORT_MAP, :VENDOR
|
|
16
|
+
private_constant :LOCAL
|
|
16
17
|
|
|
17
18
|
def self.logical(url)
|
|
18
19
|
return url.delete_prefix('https://').sub('/npm:', '/npm/') if url.start_with?(CDN)
|
data/lib/jspm/tags.rb
CHANGED
|
@@ -9,7 +9,7 @@ module Jspm
|
|
|
9
9
|
# imports name its siblings undigested. So the rendered map also sends each
|
|
10
10
|
# undigested module URL to its digested one.
|
|
11
11
|
class Tags
|
|
12
|
-
MODULE_EXTENSIONS = %w[.
|
|
12
|
+
MODULE_EXTENSIONS = %w[.js .mjs].freeze
|
|
13
13
|
private_constant :MODULE_EXTENSIONS
|
|
14
14
|
|
|
15
15
|
def self.call(view:)
|
data/lib/jspm/trace.mjs
CHANGED
|
@@ -8,7 +8,7 @@ import { readFileSync } from 'node:fs';
|
|
|
8
8
|
import { createRequire } from 'node:module';
|
|
9
9
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
10
10
|
|
|
11
|
-
const {
|
|
11
|
+
const { entries, inputMap, resolutions, root } = JSON.parse(readFileSync(0, 'utf8'));
|
|
12
12
|
const rootUrl = pathToFileURL(`${root}/`);
|
|
13
13
|
const generatorPath = createRequire(rootUrl).resolve('@jspm/generator');
|
|
14
14
|
const { Generator } = await import(pathToFileURL(generatorPath));
|
|
@@ -37,7 +37,7 @@ const generator = new Generator({
|
|
|
37
37
|
resolutions,
|
|
38
38
|
});
|
|
39
39
|
|
|
40
|
-
const { dynamicDeps, staticDeps } = await generator.link(
|
|
40
|
+
const { dynamicDeps, staticDeps } = await generator.link(entries);
|
|
41
41
|
const map = generator.getMap();
|
|
42
42
|
|
|
43
43
|
process.stdout.write(
|
data/lib/jspm/trace.rb
CHANGED
|
@@ -17,10 +17,20 @@ module Jspm
|
|
|
17
17
|
pathnames = asset_paths.map { |path| Pathname(path) }
|
|
18
18
|
module_paths = pathnames.reject { |path| path == vendor_dir }
|
|
19
19
|
local_modules = LocalModules.find(asset_paths: module_paths)
|
|
20
|
+
resolutions = lockfile.resolutions
|
|
21
|
+
# A package the app installed beats a gem's file of the same name, such
|
|
22
|
+
# as actiontext's trix.js, so the app and its packages share one copy.
|
|
23
|
+
pins = local_modules.pins.reject { |name, _path| resolutions.key?(name) }
|
|
24
|
+
# Every app module is traced and named in the map, as importmap-rails
|
|
25
|
+
# pins them, so code can import one by a name it computes, the way
|
|
26
|
+
# stimulus-rails's eagerLoadControllersFrom does.
|
|
27
|
+
app_dir = root.join('app/javascript').to_s
|
|
28
|
+
app_modules = pins.select { |_name, path| path.start_with?(app_dir) }
|
|
29
|
+
.keys
|
|
20
30
|
input = JSON.generate(
|
|
21
|
-
|
|
22
|
-
inputMap: { imports: locked_versions(root).merge(
|
|
23
|
-
resolutions
|
|
31
|
+
entries: [Paths::ENTRY_PATH, *app_modules],
|
|
32
|
+
inputMap: { imports: locked_versions(root).merge(pins), scopes: {} },
|
|
33
|
+
resolutions:,
|
|
24
34
|
root: root.to_s
|
|
25
35
|
)
|
|
26
36
|
script = File.expand_path('trace.mjs', __dir__)
|
data/lib/jspm/version.rb
CHANGED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
|
|
5
|
+
module Jspm
|
|
6
|
+
# Reads Yarn 1's yarn.lock. Unlike package-lock.json, it doesn't record
|
|
7
|
+
# where each package sits in node_modules. A package locked at one version
|
|
8
|
+
# is the top-level one. When Yarn locked several versions, the one
|
|
9
|
+
# node_modules holds at its top level is.
|
|
10
|
+
module YarnLock
|
|
11
|
+
Entry = Data.define(:name, :version)
|
|
12
|
+
private_constant :Entry
|
|
13
|
+
|
|
14
|
+
class << self
|
|
15
|
+
def packages(root)
|
|
16
|
+
entries = parse(root.join('yarn.lock').read)
|
|
17
|
+
counts = entries.map(&:name).tally
|
|
18
|
+
entries.map do |entry|
|
|
19
|
+
name = entry.name
|
|
20
|
+
version = entry.version
|
|
21
|
+
locked_once = counts.fetch(name) == 1
|
|
22
|
+
installed = installed_version(root.join('node_modules', name)) == version
|
|
23
|
+
{ name:, top_level: locked_once || installed, version: }
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def installed_version(package_dir)
|
|
30
|
+
manifest = package_dir.join('package.json')
|
|
31
|
+
JSON.parse(manifest.read).fetch('version') if manifest.exist?
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def parse(text)
|
|
35
|
+
blocks =
|
|
36
|
+
text.split(/\n{2,}/).reject do |block|
|
|
37
|
+
block.start_with?('#') || block.strip.empty?
|
|
38
|
+
end
|
|
39
|
+
blocks.map { |block| parse_entry(block) }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def parse_entry(block)
|
|
43
|
+
header, *body = block.lines(chomp: true)
|
|
44
|
+
spec = header.delete_suffix(':').split(', ').first.delete('"')
|
|
45
|
+
version = body.find { |line| line.start_with?(' version ') }[/"(.*)"/, 1]
|
|
46
|
+
Entry.new(name: spec.rpartition('@').first, version:)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
data/lib/jspm-rails.rb
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'jspm/assets'
|
|
4
3
|
require 'jspm/check'
|
|
5
4
|
require 'jspm/helper'
|
|
6
5
|
require 'jspm/install'
|
|
@@ -11,3 +10,4 @@ require 'jspm/railtie'
|
|
|
11
10
|
require 'jspm/tags'
|
|
12
11
|
require 'jspm/trace'
|
|
13
12
|
require 'jspm/version'
|
|
13
|
+
require 'jspm/yarn_lock'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: jspm-rails
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Clay Shentrup
|
|
@@ -49,7 +49,6 @@ files:
|
|
|
49
49
|
- LICENSE
|
|
50
50
|
- README.md
|
|
51
51
|
- lib/jspm-rails.rb
|
|
52
|
-
- lib/jspm/assets.rb
|
|
53
52
|
- lib/jspm/check.rb
|
|
54
53
|
- lib/jspm/helper.rb
|
|
55
54
|
- lib/jspm/install.rb
|
|
@@ -61,6 +60,7 @@ files:
|
|
|
61
60
|
- lib/jspm/trace.mjs
|
|
62
61
|
- lib/jspm/trace.rb
|
|
63
62
|
- lib/jspm/version.rb
|
|
63
|
+
- lib/jspm/yarn_lock.rb
|
|
64
64
|
- lib/tasks/jspm.rake
|
|
65
65
|
homepage: https://github.com/ClayShentrup/jspm-rails
|
|
66
66
|
licenses:
|
data/lib/jspm/assets.rb
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Jspm
|
|
4
|
-
# The stylesheets, fonts and images of each npm dependency, and where each
|
|
5
|
-
# one is vendored. devDependencies are build tools, so they stay out.
|
|
6
|
-
module Assets
|
|
7
|
-
EXTENSIONS = %w[
|
|
8
|
-
.avif
|
|
9
|
-
.css
|
|
10
|
-
.eot
|
|
11
|
-
.gif
|
|
12
|
-
.jpeg
|
|
13
|
-
.jpg
|
|
14
|
-
.otf
|
|
15
|
-
.png
|
|
16
|
-
.svg
|
|
17
|
-
.ttf
|
|
18
|
-
.webp
|
|
19
|
-
.woff
|
|
20
|
-
.woff2
|
|
21
|
-
].freeze
|
|
22
|
-
private_constant :EXTENSIONS
|
|
23
|
-
|
|
24
|
-
class << self
|
|
25
|
-
# Each copy is a pair: the file in node_modules, and its vendored path.
|
|
26
|
-
def copies(lockfile:, root:)
|
|
27
|
-
lockfile.browser_packages.flat_map { |package| package_copies(package, root) }
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
private
|
|
31
|
-
|
|
32
|
-
def package_copies(package, root)
|
|
33
|
-
package_dir = root.join(package.path)
|
|
34
|
-
vendor_dir = root.join(Paths::VENDOR, package.name)
|
|
35
|
-
files =
|
|
36
|
-
package_dir.glob('**/*').select do |file|
|
|
37
|
-
EXTENSIONS.include?(file.extname.downcase)
|
|
38
|
-
end
|
|
39
|
-
files.map do |file|
|
|
40
|
-
relative_path = file.relative_path_from(package_dir)
|
|
41
|
-
[file, vendor_dir.join(relative_path)]
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
end
|