1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
//! What: Decide whether source text imports a package or one of its subpaths.
//! Why: A development dependency is bundled, and so carries a bump, only when non-test
//! source imports it. This is the incumbent's specifier scan (`source-imports.ts:59-264`):
//! a quoted specifier equal to the name, or starting with the name and `/`, directly
//! after `from`, `import`, `import(` or `require(`. Every character it inspects is
//! ASCII, so scanning UTF-8 bytes decides exactly what scanning UTF-16 units decides.
//!
//! In TS you'd write (pseudocode):
//! ```ts
//! // importsPackage({ sourceText, packageName });
//! ```
/// What: Keywords that put a string literal directly after them in specifier position.
/// Why: `import x from 'p'`, `export * from 'p'` and `import 'p'`.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// const SPECIFIER_KEYWORDS = ['from', 'import'];
/// ```
const SPECIFIER_KEYWORDS: & = &;
/// What: Callees whose first argument is a module specifier.
/// Why: `import('p')` and `require('p')`.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// const SPECIFIER_CALLEES = ['import', 'require'];
/// ```
const SPECIFIER_CALLEES: & = &;
/// What: Whether a byte can continue a JavaScript identifier, for keyword boundaries.
/// Why: `reimport 'p'` and `myrequire('p')` are not specifiers.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// const isIdentifierCharacter = (c: string) => /[A-Za-z0-9_$]/.test(c);
/// ```
/// What: The exclusive end of the text before `end` once trailing whitespace is dropped.
/// `.rposition(...)` finds the last match.
/// Why: Whitespace, including line breaks, may separate a keyword from its literal.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// function skipWhitespaceBackwards({ text, end }): number;
/// ```
/// What: Whether one byte is something other than a space, tab, carriage return or line feed.
/// Why: `rposition` takes this function by path where a closure would otherwise stand.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// const isNotWhitespaceByte = (byte: number) => ![0x20, 0x09, 0x0d, 0x0a].includes(byte);
/// ```
/// What: Whether one of the keywords ends exactly at `end`, with no identifier byte before
/// it. `.checked_sub` is subtraction that yields nothing instead of going below zero.
/// Why: A keyword must be a whole word.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// function endsWithKeyword({ text, end, keywords }): boolean;
/// ```
/// What: Whether the string literal opening at `quote_index` is in specifier position.
/// Why: `const name = '@scope/b'` mentions a package without importing it.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// function isSpecifierPosition({ text, quoteIndex }): boolean;
/// ```
/// What: Whether source text imports the package or one of its subpaths.
/// `.match_indices(...)` yields each non-overlapping occurrence from the left, the
/// same occurrences the incumbent's `indexOf` loop visits.
/// Why: The character before an occurrence must open a quote, the one after must close it
/// or start a subpath, and the literal must be in specifier position.
/// Gotcha: For an empty name the incumbent's loop never advances and does not terminate;
/// here every position is an occurrence, so the stated rule applies to it too.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// function importsPackage({ sourceText, packageName }): boolean;
/// ```
/// Specifier detection, including every case of the incumbent's unit tests.