importmap-plus 2.0.0 → 2.0.1

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: a89ba9297f9e04e1f3a892ea319d509ce9300789f648327c3b4b1cf72c0951d7
4
- data.tar.gz: c088e5ab5732c9f132bfa63a60d2a7d9243ac85690670e1a040e3658fc1fb091
3
+ metadata.gz: 1d0e4b656e7047b77938a8b92681f72ec7af73034c2c94517ef2ff37d725b76d
4
+ data.tar.gz: 9a83c644e76fab4892e074ea8f254877b4351c55b071294f4edd3c965167054f
5
5
  SHA512:
6
- metadata.gz: 81f2d28ee6c2d724917e338451d4a0f2baef86feb1bcc5477feca8114f13213787d9164b6befc8e7e02cf24e5ec0f810e13d05195be4e27c689bc70db5fd9311
7
- data.tar.gz: 3cbd693b24887409203b8f75294798c3ee4b08dec82a90eb404de2a24e7ba7f273d4babd48a72c3a1608f8f263b4804b27dcfbf624edbb69252ea8ea6b346d63
6
+ metadata.gz: 39c36fbd0719746f4ca30e21632731f1d91ebc4e524132c2592b2d0a1432315cf8e2d3e7b70fffa935cbc4d3135a14d561dd86d52c6de3a18fd41030f91209a1
7
+ data.tar.gz: c9eaf6a0f2b07be30f46487d07ffa17ebaa538efd443d23d677fd8e95509b4d50b193162a136dec8d9d6888b5414cbfe7b832d0929b0d0f9d6373de35ad2f0bf
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## Unreleased
4
+
5
+ ### Fixed
6
+
7
+ - **`bin/importmap doctor` no longer reports comments and prose as unpinned
8
+ imports.** The import scanner read `from "…"` and `import "…"` wherever the
9
+ text appeared, so a line comment such as `// e.g. 'en' from 'en-US'` and
10
+ strings such as turbo_power's `` `…provided in the "from" attribute for
11
+ the "…` `` came back as `error … imports " attribute for the ", which isn't
12
+ pinned`. Line comments are now discounted before scanning (strings and
13
+ regex literals are still consumed whole, so `"https://…"` is safe), and a
14
+ quoted text that can't be a module specifier — whitespace, a backtick, a
15
+ `${` interpolation, or no word character at all — is not reported.
16
+
3
17
  ## 1.2.0
4
18
 
5
19
  ### Added
@@ -6,16 +6,19 @@ require "importmap/module_inspector"
6
6
  #
7
7
  # Like Importmap::ModuleInspector and Importmap::PackageGraph::IMPORT_REGEXP
8
8
  # this reads the source with regexes rather than parsing JavaScript, so an
9
- # import statement spelled out inside a string literal is reported too, and a
10
- # form the regexp can't read — a magic comment between the keyword and the
9
+ # import statement spelled out inside a string literal is reported too — unless
10
+ # what it names can't be a module specifier at all — and a form the regexp
11
+ # can't read — a magic comment between the keyword and the
11
12
  # specifier, a specifier built at runtime — isn't reported at all. Both
12
13
  # mistakes are the cautious direction for the one caller: a specifier reported
13
14
  # that the browser never asks for costs a pin, and a specifier missed leaves
14
15
  # the app exactly as broken as it was before anyone ran the check.
15
16
  #
16
- # Block comments are discounted first, through ModuleInspector#code, because a
17
- # published bundle is full of `/** @typedef {import('./slide.js').Slide} */` —
18
- # type annotations naming files the package never loads.
17
+ # Comments are discounted first, through
18
+ # ModuleInspector#code_without_line_comments, because a published bundle is
19
+ # full of `/** @typedef {import('./slide.js').Slide} */` — type annotations
20
+ # naming files the package never loads — and app code is full of prose like
21
+ # `// 'en' from 'en-US'`.
19
22
  class Importmap::ImportScanner
20
23
  Import = Struct.new(:specifier, :kind, keyword_init: true) # :nodoc:
21
24
 
@@ -38,6 +41,12 @@ class Importmap::ImportScanner
38
41
  (?:from|import)\s*(["'])([^"'\n]*)\3
39
42
  )
40
43
  /x.freeze # :nodoc:
44
+ # What a quoted `from "…"` or `import "…"` has to hold to name a module
45
+ # rather than be prose the regexp above found in a string: something, no
46
+ # whitespace or backtick, no `${` interpolation, and at least one word
47
+ # character — `"from" attribute for the "`, `["import",","]` and
48
+ # `` `import("${url}")` `` are all strings, never specifiers.
49
+ SPECIFIER_REGEXP = /\A(?!.*\$\{)[^\s`]*\w[^\s`]*\z/.freeze # :nodoc:
41
50
 
42
51
  attr_reader :source
43
52
 
@@ -46,7 +55,9 @@ class Importmap::ImportScanner
46
55
  end
47
56
 
48
57
  def imports
49
- @imports ||= Importmap::ModuleInspector.new(source).code.scan(IMPORT_REGEXP).map do |_, dynamic, _, static|
58
+ @imports ||= Importmap::ModuleInspector.new(source).code_without_line_comments.scan(IMPORT_REGEXP).filter_map do |_, dynamic, _, static|
59
+ next unless (dynamic || static).match?(SPECIFIER_REGEXP)
60
+
50
61
  dynamic ? Import.new(specifier: dynamic, kind: :dynamic) : Import.new(specifier: static, kind: :static)
51
62
  end
52
63
  end
@@ -144,6 +144,14 @@ class Importmap::ModuleInspector
144
144
  @code ||= without_block_comments
145
145
  end
146
146
 
147
+ # #code with its line comments discounted too, for Importmap::ImportScanner:
148
+ # prose such as `// e.g. 'en' from 'en-US'` reads as an import otherwise.
149
+ # Strings and regex literals are still consumed whole first, so the `//` of
150
+ # `"https://…"` or `/\/\//` never opens a comment.
151
+ def code_without_line_comments
152
+ @code_without_line_comments ||= without_block_comments(line_comments: true)
153
+ end
154
+
147
155
  private
148
156
  def statements
149
157
  @statements ||= without_block_comments(statements_only: true)
@@ -170,12 +178,15 @@ class Importmap::ModuleInspector
170
178
  # reads as an import statement while the text inside it stops being read
171
179
  # as code at all. That mode feeds only the ES-module check, where the
172
180
  # `[//]` trap above can at worst send a package on to the next CDN.
173
- def without_block_comments(statements_only: false)
181
+ # With +line_comments+ alone they go but literals are kept, for the import
182
+ # scanner: the trap there can at worst miss an import in the doctor's
183
+ # report, where prose in a comment is a false error on every run.
184
+ def without_block_comments(statements_only: false, line_comments: statements_only)
174
185
  scanner = StringScanner.new(source)
175
186
  kept = +""
176
187
 
177
188
  until scanner.eos?
178
- if scanner.skip(BLOCK_COMMENT_REGEXP) || (statements_only && scanner.skip(LINE_COMMENT_REGEXP))
189
+ if scanner.skip(BLOCK_COMMENT_REGEXP) || (line_comments && scanner.skip(LINE_COMMENT_REGEXP))
179
190
  next
180
191
  elsif (literal = scanner.scan(STRING_REGEXP)) ||
181
192
  (regexp_literal_next?(kept, scanner) && (literal = scanner.scan(REGEXP_LITERAL_REGEXP)))
@@ -1,6 +1,6 @@
1
1
  module Importmap
2
2
  # importmap-plus versions independently of the importmap-rails it forks.
3
3
  # UPSTREAM_VERSION is the importmap-rails release this tracks.
4
- VERSION = "2.0.0"
4
+ VERSION = "2.0.1"
5
5
  UPSTREAM_VERSION = "2.2.3"
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: importmap-plus
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson