@menapo/icons 0.0.0-stage → 0.1.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.
- package/LICENSE +21 -0
- package/README.md +35 -2
- package/dist/index.d.ts +52 -0
- package/dist/index.js +6 -0
- package/dist/vite.d.ts +69 -0
- package/dist/vite.js +2624 -0
- package/package.json +62 -3
package/dist/vite.js
ADDED
|
@@ -0,0 +1,2624 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { parseSync } from "vite";
|
|
5
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
6
|
+
//#region ../glue/src/detect.ts
|
|
7
|
+
/**
|
|
8
|
+
* Which framework a module renders with (machine PLAN §5.5), the way vicss
|
|
9
|
+
* decides it: `.vue` and `.svelte` (with `.svelte.js`/`.svelte.ts` rune
|
|
10
|
+
* modules) by extension; JSX by its `@jsxImportSource` pragma, else the
|
|
11
|
+
* project's JSX framework ({@link jsxFrameworkOf}); plain scripts by the
|
|
12
|
+
* plugin's `framework` option, else the framework the root `package.json`
|
|
13
|
+
* depends on, else none (vanilla). Plain data in and out, so it is
|
|
14
|
+
* unit-tested without Vite.
|
|
15
|
+
*
|
|
16
|
+
* vi18n has a detection of its own (its `vite/glue/detect.ts`, by plugin
|
|
17
|
+
* names); it is published, so moving it onto this module waits for its
|
|
18
|
+
* next minor (machine PLAN §11).
|
|
19
|
+
*/
|
|
20
|
+
/** Root dependencies that name a project's framework, the first found winning. */
|
|
21
|
+
const DEPENDENCIES = [
|
|
22
|
+
["vue", "vue"],
|
|
23
|
+
["svelte", "svelte"],
|
|
24
|
+
["solid-js", "solid"],
|
|
25
|
+
["preact", "preact"],
|
|
26
|
+
["react", "react"]
|
|
27
|
+
];
|
|
28
|
+
/** `preact`, `solid-js` and `vue` (and their subpaths) by name; any other JSX source takes React's. */
|
|
29
|
+
function jsxFramework(source) {
|
|
30
|
+
const name = source.split("/")[0];
|
|
31
|
+
if (name === "preact") return "preact";
|
|
32
|
+
if (name === "solid-js") return "solid";
|
|
33
|
+
if (name === "vue") return "vue";
|
|
34
|
+
return "react";
|
|
35
|
+
}
|
|
36
|
+
/** The framework of module `id` with source `code`. */
|
|
37
|
+
function frameworkOf(id, code, settings) {
|
|
38
|
+
const file = id.replace(/[?#].*$/, "").toLowerCase();
|
|
39
|
+
if (file.endsWith(".vue")) return "vue";
|
|
40
|
+
if (file.endsWith(".svelte") || /\.svelte\.[cm]?[jt]s$/.test(file)) return "svelte";
|
|
41
|
+
if (/\.[jt]sx$/.test(file)) {
|
|
42
|
+
const pragma = /@jsxImportSource\s+([^\s*]+)/.exec(code)?.[1];
|
|
43
|
+
return pragma ? jsxFramework(pragma) : settings.jsx;
|
|
44
|
+
}
|
|
45
|
+
return settings.scripts;
|
|
46
|
+
}
|
|
47
|
+
/** The names a `package.json` depends on, if it can be read. */
|
|
48
|
+
function dependencies(root) {
|
|
49
|
+
const file = join(root, "package.json");
|
|
50
|
+
if (!existsSync(file)) return void 0;
|
|
51
|
+
try {
|
|
52
|
+
const pkg = JSON.parse(readFileSync(file, "utf8"));
|
|
53
|
+
return /* @__PURE__ */ new Set([...Object.keys(pkg.dependencies ?? {}), ...Object.keys(pkg.devDependencies ?? {})]);
|
|
54
|
+
} catch {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/** The framework the `package.json` in `root` depends on, if a known one. */
|
|
59
|
+
function rootFramework(root) {
|
|
60
|
+
const names = dependencies(root);
|
|
61
|
+
return names && DEPENDENCIES.find(([name]) => names.has(name))?.[1];
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* JSX frameworks by the names of the Vite plugins that compile them, as
|
|
65
|
+
* vi18n recognises them: a prefix up to a separator, or the exact name.
|
|
66
|
+
*/
|
|
67
|
+
const JSX_PLUGINS = [
|
|
68
|
+
["react", /^(?:vite:react|@vitejs\/plugin-react)(?:[:/-]|$)/],
|
|
69
|
+
["preact", /^(?:preact:|vite:preact(?:[:/-]|$)|@preact\/|prefresh(?:[:/-]|$)|preact$)/],
|
|
70
|
+
["solid", /^(?:solid:|vite-plugin-solid(?:[:/-]|$)|solid-start(?:[:/-]|$)|solid$)/],
|
|
71
|
+
["vue", /^(?:vite:vue-jsx|@vitejs\/plugin-vue-jsx)(?:[:/-]|$)/]
|
|
72
|
+
];
|
|
73
|
+
/** The one JSX framework whose Vite plugin is in `plugins`; none when there are none or several. */
|
|
74
|
+
function pluginFramework(plugins) {
|
|
75
|
+
const found = /* @__PURE__ */ new Set();
|
|
76
|
+
for (const plugin of plugins) {
|
|
77
|
+
const name = plugin?.name;
|
|
78
|
+
if (typeof name !== "string") continue;
|
|
79
|
+
for (const [framework, pattern] of JSX_PLUGINS) if (pattern.test(name)) found.add(framework);
|
|
80
|
+
}
|
|
81
|
+
return found.size === 1 ? [...found][0] : void 0;
|
|
82
|
+
}
|
|
83
|
+
/** tsconfig's JSON with comments and trailing commas, as TypeScript reads it. */
|
|
84
|
+
function parseJsonc(text) {
|
|
85
|
+
let out = "";
|
|
86
|
+
for (let i = 0; i < text.length; i++) {
|
|
87
|
+
const c = text[i];
|
|
88
|
+
if (c === "\"") {
|
|
89
|
+
const end = /"(?:[^"\\]|\\.)*"/y;
|
|
90
|
+
end.lastIndex = i;
|
|
91
|
+
const string = end.exec(text)?.[0] ?? c;
|
|
92
|
+
out += string;
|
|
93
|
+
i += string.length - 1;
|
|
94
|
+
} else if (c === "/" && text[i + 1] === "/") {
|
|
95
|
+
while (i < text.length && text[i] !== "\n") i++;
|
|
96
|
+
out += "\n";
|
|
97
|
+
} else if (c === "/" && text[i + 1] === "*") {
|
|
98
|
+
const end = text.indexOf("*/", i + 2);
|
|
99
|
+
i = end < 0 ? text.length : end + 1;
|
|
100
|
+
} else out += c;
|
|
101
|
+
}
|
|
102
|
+
return JSON.parse(out.replace(/,(\s*[}\]])/g, "$1"));
|
|
103
|
+
}
|
|
104
|
+
/** A tsconfig named by `extends` or a reference: a file, a directory's `tsconfig.json`, or a package's. */
|
|
105
|
+
function tsconfigPath(from, name) {
|
|
106
|
+
const local = name.startsWith(".") || name.startsWith("/");
|
|
107
|
+
let path;
|
|
108
|
+
try {
|
|
109
|
+
path = local ? resolve(dirname(from), name) : createRequire(from).resolve(name);
|
|
110
|
+
} catch {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (existsSync(path) && statSync(path).isDirectory()) path = join(path, "tsconfig.json");
|
|
114
|
+
else if (!existsSync(path) && existsSync(`${path}.json`)) path = `${path}.json`;
|
|
115
|
+
return existsSync(path) ? path : void 0;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* `compilerOptions.jsxImportSource` as TypeScript resolves it for `file`:
|
|
119
|
+
* its own, else its `extends`' (the last one first); then, for a solution
|
|
120
|
+
* file (`references`, as create-vite writes), the first referenced project
|
|
121
|
+
* that sets one.
|
|
122
|
+
*/
|
|
123
|
+
function tsconfigJsxSource(file, seen = /* @__PURE__ */ new Set()) {
|
|
124
|
+
if (seen.has(file) || !existsSync(file)) return void 0;
|
|
125
|
+
seen.add(file);
|
|
126
|
+
let config;
|
|
127
|
+
try {
|
|
128
|
+
config = parseJsonc(readFileSync(file, "utf8"));
|
|
129
|
+
} catch {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
const own = config.compilerOptions?.jsxImportSource;
|
|
133
|
+
if (typeof own === "string") return own;
|
|
134
|
+
const bases = typeof config.extends === "string" ? [config.extends] : config.extends ?? [];
|
|
135
|
+
for (const base of [...bases].reverse()) {
|
|
136
|
+
const path = tsconfigPath(file, base);
|
|
137
|
+
const found = path && tsconfigJsxSource(path, seen);
|
|
138
|
+
if (found) return found;
|
|
139
|
+
}
|
|
140
|
+
for (const reference of config.references ?? []) {
|
|
141
|
+
const path = tsconfigPath(file, reference.path);
|
|
142
|
+
const found = path && tsconfigJsxSource(path, seen);
|
|
143
|
+
if (found) return found;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* The framework of JSX without a pragma: the JSX import source the Vite
|
|
148
|
+
* config gives Oxc or esbuild; else the one JSX framework whose Vite plugin
|
|
149
|
+
* is installed (`vite-plugin-solid`, the Preact preset); else the root
|
|
150
|
+
* tsconfig's `jsxImportSource`, through `extends` and `references`; else
|
|
151
|
+
* Preact, Solid or Vue when the root `package.json` depends on it; else React.
|
|
152
|
+
*/
|
|
153
|
+
function jsxFrameworkOf(config, root) {
|
|
154
|
+
const oxc = config.oxc;
|
|
155
|
+
const esbuild = config.esbuild;
|
|
156
|
+
const fromVite = (oxc && oxc.jsx?.importSource) ?? (esbuild && esbuild.jsxImportSource);
|
|
157
|
+
if (typeof fromVite === "string") return jsxFramework(fromVite);
|
|
158
|
+
const plugin = pluginFramework(config.plugins ?? []);
|
|
159
|
+
if (plugin) return plugin;
|
|
160
|
+
const fromTsconfig = tsconfigJsxSource(join(root, "tsconfig.json"));
|
|
161
|
+
if (fromTsconfig) return jsxFramework(fromTsconfig);
|
|
162
|
+
const names = dependencies(root);
|
|
163
|
+
if (names?.has("preact")) return "preact";
|
|
164
|
+
if (names?.has("solid-js")) return "solid";
|
|
165
|
+
if (names?.has("vue") && !names.has("react")) return "vue";
|
|
166
|
+
return "react";
|
|
167
|
+
}
|
|
168
|
+
//#endregion
|
|
169
|
+
//#region ../glue/src/vite.ts
|
|
170
|
+
/**
|
|
171
|
+
* The build side of the shared glue (machine PLAN §5.1, §5.5): a Vite
|
|
172
|
+
* plugin that resolves a package's hooks (`useMachine` from
|
|
173
|
+
* `@menapo/machine`) per module to that module's framework, so one import
|
|
174
|
+
* works in React, Preact, Vue, Svelte, Solid and plain scripts, and no
|
|
175
|
+
* package has a `/react` or `/vue` entry (repo rule: entries by role).
|
|
176
|
+
*
|
|
177
|
+
* - **Per module:** each module importing a hook gets the hook from
|
|
178
|
+
* `virtual:<package>/glue/<framework>`, which resolves to the package's
|
|
179
|
+
* glue module for that framework; the rest of the import stays. Every
|
|
180
|
+
* form that reaches a hook is rewritten: named imports (string names
|
|
181
|
+
* too), a namespace import's members, re-exports, `export *` and
|
|
182
|
+
* `import()`.
|
|
183
|
+
* - **The app's framework:** a glue module and the adapters it uses import
|
|
184
|
+
* `react`, `vue`… by name. Those imports resolve from the project root, as
|
|
185
|
+
* the app's own do, so the app's single copy is used wherever the glue
|
|
186
|
+
* module lives (a workspace package, `node_modules`); one that does not
|
|
187
|
+
* resolve there fails the build, naming the fix.
|
|
188
|
+
*/
|
|
189
|
+
/**
|
|
190
|
+
* A path as Vite writes ids, with forward slashes on every platform:
|
|
191
|
+
* `fileURLToPath` gives backslashes on Windows, and a prefix test of one
|
|
192
|
+
* against the other never matches.
|
|
193
|
+
*/
|
|
194
|
+
const posix = (path) => path.replace(/\\/g, "/");
|
|
195
|
+
/** Whether `id` is inside one of `dirs`, both compared as Vite writes them. */
|
|
196
|
+
const within = (dirs, id) => dirs.some((dir) => posix(id).startsWith(posix(dir)));
|
|
197
|
+
dirname(fileURLToPath(import.meta.url));
|
|
198
|
+
//#endregion
|
|
199
|
+
//#region ../../node_modules/.pnpm/@jridgewell+sourcemap-codec@1.6.0/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.mjs
|
|
200
|
+
var comma = ",".charCodeAt(0);
|
|
201
|
+
var semicolon = ";".charCodeAt(0);
|
|
202
|
+
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
203
|
+
var intToChar$1 = /* @__PURE__ */ new Uint8Array(64);
|
|
204
|
+
var charToInt = /* @__PURE__ */ new Uint8Array(128);
|
|
205
|
+
for (let i = 0; i < chars.length; i++) {
|
|
206
|
+
const c = chars.charCodeAt(i);
|
|
207
|
+
intToChar$1[i] = c;
|
|
208
|
+
charToInt[c] = i;
|
|
209
|
+
}
|
|
210
|
+
function encodeInteger(builder, num) {
|
|
211
|
+
do {
|
|
212
|
+
let clamped = num & 31;
|
|
213
|
+
num >>>= 5;
|
|
214
|
+
if (num > 0) clamped |= 32;
|
|
215
|
+
builder.write(intToChar$1[clamped]);
|
|
216
|
+
} while (num > 0);
|
|
217
|
+
}
|
|
218
|
+
function encodeSign(num) {
|
|
219
|
+
return num < 0 ? -num << 1 | 1 : num << 1;
|
|
220
|
+
}
|
|
221
|
+
var bufLength = 16384;
|
|
222
|
+
var td = typeof TextDecoder !== "undefined" ? /* @__PURE__ */ new TextDecoder() : typeof Buffer !== "undefined" ? { decode(buf) {
|
|
223
|
+
return Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength).toString();
|
|
224
|
+
} } : { decode(buf) {
|
|
225
|
+
let out = "";
|
|
226
|
+
for (let i = 0; i < buf.length; i++) out += String.fromCharCode(buf[i]);
|
|
227
|
+
return out;
|
|
228
|
+
} };
|
|
229
|
+
var StringWriter = class {
|
|
230
|
+
constructor() {
|
|
231
|
+
this.pos = 0;
|
|
232
|
+
this.out = "";
|
|
233
|
+
this.buffer = new Uint8Array(bufLength);
|
|
234
|
+
}
|
|
235
|
+
write(v) {
|
|
236
|
+
const { buffer } = this;
|
|
237
|
+
buffer[this.pos++] = v;
|
|
238
|
+
if (this.pos === bufLength) {
|
|
239
|
+
this.out += td.decode(buffer);
|
|
240
|
+
this.pos = 0;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
flush() {
|
|
244
|
+
const { buffer, out, pos } = this;
|
|
245
|
+
return pos > 0 ? out + td.decode(buffer.subarray(0, pos)) : out;
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
function encodeRangeMappings(decoded) {
|
|
249
|
+
if (decoded.length === 0) return "";
|
|
250
|
+
const writer = new StringWriter();
|
|
251
|
+
for (let i = 0; i < decoded.length; i++) {
|
|
252
|
+
const indices = decoded[i];
|
|
253
|
+
if (i > 0) writer.write(semicolon);
|
|
254
|
+
let index = 0;
|
|
255
|
+
for (let j = 0; j < indices.length; j++) {
|
|
256
|
+
const offset = indices[j];
|
|
257
|
+
encodeInteger(writer, offset - index);
|
|
258
|
+
index = offset;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return writer.flush();
|
|
262
|
+
}
|
|
263
|
+
function encode(decoded) {
|
|
264
|
+
const writer = new StringWriter();
|
|
265
|
+
let sourcesIndex = 0;
|
|
266
|
+
let sourceLine = 0;
|
|
267
|
+
let sourceColumn = 0;
|
|
268
|
+
let namesIndex = 0;
|
|
269
|
+
for (let i = 0; i < decoded.length; i++) {
|
|
270
|
+
const line = decoded[i];
|
|
271
|
+
if (i > 0) writer.write(semicolon);
|
|
272
|
+
if (line.length === 0) continue;
|
|
273
|
+
let genColumn = 0;
|
|
274
|
+
for (let j = 0; j < line.length; j++) {
|
|
275
|
+
const segment = line[j];
|
|
276
|
+
if (j > 0) writer.write(comma);
|
|
277
|
+
encodeInteger(writer, encodeSign(segment[0] - genColumn));
|
|
278
|
+
genColumn = segment[0];
|
|
279
|
+
if (segment.length === 1) continue;
|
|
280
|
+
encodeInteger(writer, encodeSign(segment[1] - sourcesIndex));
|
|
281
|
+
encodeInteger(writer, encodeSign(segment[2] - sourceLine));
|
|
282
|
+
encodeInteger(writer, encodeSign(segment[3] - sourceColumn));
|
|
283
|
+
sourcesIndex = segment[1];
|
|
284
|
+
sourceLine = segment[2];
|
|
285
|
+
sourceColumn = segment[3];
|
|
286
|
+
if (segment.length === 4) continue;
|
|
287
|
+
encodeInteger(writer, encodeSign(segment[4] - namesIndex));
|
|
288
|
+
namesIndex = segment[4];
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return writer.flush();
|
|
292
|
+
}
|
|
293
|
+
//#endregion
|
|
294
|
+
//#region ../../node_modules/.pnpm/magic-string@1.4.3/node_modules/magic-string/dist/index.mjs
|
|
295
|
+
var BitSet = class BitSet {
|
|
296
|
+
constructor(arg) {
|
|
297
|
+
this.bits = arg instanceof BitSet ? arg.bits.slice() : [];
|
|
298
|
+
}
|
|
299
|
+
add(n) {
|
|
300
|
+
this.bits[n >> 5] |= 1 << (n & 31);
|
|
301
|
+
}
|
|
302
|
+
has(n) {
|
|
303
|
+
return !!(this.bits[n >> 5] & 1 << (n & 31));
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
var Chunk = class Chunk {
|
|
307
|
+
constructor(start, end, content) {
|
|
308
|
+
this.start = start;
|
|
309
|
+
this.end = end;
|
|
310
|
+
this.original = content;
|
|
311
|
+
this.intro = "";
|
|
312
|
+
this.outro = "";
|
|
313
|
+
this.content = content;
|
|
314
|
+
this.storeName = false;
|
|
315
|
+
this.edited = false;
|
|
316
|
+
this.previous = null;
|
|
317
|
+
this.next = null;
|
|
318
|
+
}
|
|
319
|
+
appendLeft(content) {
|
|
320
|
+
this.outro += content;
|
|
321
|
+
}
|
|
322
|
+
appendRight(content) {
|
|
323
|
+
this.intro = this.intro + content;
|
|
324
|
+
}
|
|
325
|
+
clone() {
|
|
326
|
+
const chunk = new Chunk(this.start, this.end, this.original);
|
|
327
|
+
chunk.intro = this.intro;
|
|
328
|
+
chunk.outro = this.outro;
|
|
329
|
+
chunk.content = this.content;
|
|
330
|
+
chunk.storeName = this.storeName;
|
|
331
|
+
chunk.edited = this.edited;
|
|
332
|
+
return chunk;
|
|
333
|
+
}
|
|
334
|
+
contains(index) {
|
|
335
|
+
return this.start < index && index < this.end;
|
|
336
|
+
}
|
|
337
|
+
eachNext(fn) {
|
|
338
|
+
fn(this);
|
|
339
|
+
let chunk = this.next;
|
|
340
|
+
while (chunk) {
|
|
341
|
+
fn(chunk);
|
|
342
|
+
chunk = chunk.next;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
eachPrevious(fn) {
|
|
346
|
+
fn(this);
|
|
347
|
+
let chunk = this.previous;
|
|
348
|
+
while (chunk) {
|
|
349
|
+
fn(chunk);
|
|
350
|
+
chunk = chunk.previous;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
edit(content, storeName, contentOnly) {
|
|
354
|
+
this.content = content;
|
|
355
|
+
if (!contentOnly) {
|
|
356
|
+
this.intro = "";
|
|
357
|
+
this.outro = "";
|
|
358
|
+
}
|
|
359
|
+
this.storeName = storeName;
|
|
360
|
+
this.edited = true;
|
|
361
|
+
return this;
|
|
362
|
+
}
|
|
363
|
+
prependLeft(content) {
|
|
364
|
+
this.outro = content + this.outro;
|
|
365
|
+
}
|
|
366
|
+
prependRight(content) {
|
|
367
|
+
this.intro = content + this.intro;
|
|
368
|
+
}
|
|
369
|
+
reset() {
|
|
370
|
+
this.intro = "";
|
|
371
|
+
this.outro = "";
|
|
372
|
+
if (this.edited) {
|
|
373
|
+
this.content = this.original;
|
|
374
|
+
this.storeName = false;
|
|
375
|
+
this.edited = false;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
split(index) {
|
|
379
|
+
const sliceIndex = index - this.start;
|
|
380
|
+
const originalBefore = this.original.slice(0, sliceIndex);
|
|
381
|
+
const originalAfter = this.original.slice(sliceIndex);
|
|
382
|
+
this.original = originalBefore;
|
|
383
|
+
const newChunk = new Chunk(index, this.end, originalAfter);
|
|
384
|
+
newChunk.outro = this.outro;
|
|
385
|
+
this.outro = "";
|
|
386
|
+
this.end = index;
|
|
387
|
+
if (this.edited) {
|
|
388
|
+
newChunk.edit("", false, true);
|
|
389
|
+
this.content = "";
|
|
390
|
+
} else this.content = originalBefore;
|
|
391
|
+
newChunk.next = this.next;
|
|
392
|
+
if (newChunk.next) newChunk.next.previous = newChunk;
|
|
393
|
+
newChunk.previous = this;
|
|
394
|
+
this.next = newChunk;
|
|
395
|
+
return newChunk;
|
|
396
|
+
}
|
|
397
|
+
toString() {
|
|
398
|
+
return this.intro + this.content + this.outro;
|
|
399
|
+
}
|
|
400
|
+
trimEnd(rx) {
|
|
401
|
+
this.outro = this.outro.replace(rx, "");
|
|
402
|
+
if (this.outro.length) return true;
|
|
403
|
+
const trimmed = this.content.replace(rx, "");
|
|
404
|
+
if (trimmed.length) {
|
|
405
|
+
if (trimmed !== this.content) {
|
|
406
|
+
if (this.edited) this.edit(trimmed, this.storeName, true);
|
|
407
|
+
else this.split(this.start + trimmed.length).edit("", void 0, true);
|
|
408
|
+
}
|
|
409
|
+
return true;
|
|
410
|
+
} else {
|
|
411
|
+
this.edit("", void 0, true);
|
|
412
|
+
this.intro = this.intro.replace(rx, "");
|
|
413
|
+
if (this.intro.length) return true;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
trimStart(rx) {
|
|
417
|
+
this.intro = this.intro.replace(rx, "");
|
|
418
|
+
if (this.intro.length) return true;
|
|
419
|
+
const trimmed = this.content.replace(rx, "");
|
|
420
|
+
if (trimmed.length) {
|
|
421
|
+
if (trimmed !== this.content) {
|
|
422
|
+
if (this.edited) this.edit(trimmed, this.storeName, true);
|
|
423
|
+
else {
|
|
424
|
+
this.split(this.end - trimmed.length);
|
|
425
|
+
this.edit("", void 0, true);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
return true;
|
|
429
|
+
} else {
|
|
430
|
+
this.edit("", void 0, true);
|
|
431
|
+
this.outro = this.outro.replace(rx, "");
|
|
432
|
+
if (this.outro.length) return true;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
/**
|
|
437
|
+
* The single error type thrown by MagicString.
|
|
438
|
+
*
|
|
439
|
+
* Every message is prefixed with `[MagicString]` so its source is obvious at a
|
|
440
|
+
* glance, and is kept short and consistent in tone.
|
|
441
|
+
*/
|
|
442
|
+
var MagicStringError = class extends Error {
|
|
443
|
+
name = "MagicStringError";
|
|
444
|
+
constructor(message, options) {
|
|
445
|
+
super(`[MagicString] ${message}`, options);
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
function getBtoa() {
|
|
449
|
+
/* v8 ignore next -- environment fallback, unreachable when `btoa` is present */
|
|
450
|
+
if (typeof globalThis !== "undefined" && typeof globalThis.btoa === "function") return (str) => globalThis.btoa(unescape(encodeURIComponent(str)));
|
|
451
|
+
const buffer = globalThis["Buffer"];
|
|
452
|
+
if (buffer) return (str) => buffer.from(str, "utf-8").toString("base64");
|
|
453
|
+
return () => {
|
|
454
|
+
throw new MagicStringError("unsupported environment: `btoa` or `Buffer` is required");
|
|
455
|
+
};
|
|
456
|
+
/* v8 ignore stop */
|
|
457
|
+
}
|
|
458
|
+
const btoa = /* #__PURE__ */ getBtoa();
|
|
459
|
+
var SourceMap = class {
|
|
460
|
+
constructor(properties) {
|
|
461
|
+
this.version = 3;
|
|
462
|
+
this.file = properties.file;
|
|
463
|
+
this.sources = properties.sources;
|
|
464
|
+
this.sourcesContent = properties.sourcesContent;
|
|
465
|
+
this.names = properties.names;
|
|
466
|
+
this.mappings = typeof properties.mappings === "string" ? properties.mappings : encode(properties.mappings);
|
|
467
|
+
if (typeof properties.x_google_ignoreList !== "undefined") this.x_google_ignoreList = properties.x_google_ignoreList;
|
|
468
|
+
if (typeof properties.debugId !== "undefined") this.debugId = properties.debugId;
|
|
469
|
+
if (typeof properties.rangeMappings !== "undefined") {
|
|
470
|
+
let shouldOutputRangeMapping = false;
|
|
471
|
+
for (const line of properties.rangeMappings) if (line.length !== 0) {
|
|
472
|
+
shouldOutputRangeMapping = true;
|
|
473
|
+
break;
|
|
474
|
+
}
|
|
475
|
+
if (shouldOutputRangeMapping) this.rangeMappings = encodeRangeMappings(properties.rangeMappings);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Returns the equivalent of `JSON.stringify(map)`
|
|
480
|
+
*/
|
|
481
|
+
toString() {
|
|
482
|
+
return JSON.stringify(this);
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Returns a DataURI containing the sourcemap. Useful for doing this sort of thing:
|
|
486
|
+
* `generateMap(options?: SourceMapOptions): SourceMap;`
|
|
487
|
+
*/
|
|
488
|
+
toUrl() {
|
|
489
|
+
return `data:application/json;charset=utf-8;base64,${btoa(this.toString())}`;
|
|
490
|
+
}
|
|
491
|
+
};
|
|
492
|
+
function getLocator(source) {
|
|
493
|
+
const lineOffsets = [0];
|
|
494
|
+
for (let i = source.indexOf("\n"); i !== -1; i = source.indexOf("\n", i + 1)) lineOffsets.push(i + 1);
|
|
495
|
+
return function locate(index) {
|
|
496
|
+
let i = 0;
|
|
497
|
+
let j = lineOffsets.length;
|
|
498
|
+
while (i < j) {
|
|
499
|
+
const m = i + j >> 1;
|
|
500
|
+
if (index < lineOffsets[m]) j = m;
|
|
501
|
+
else i = m + 1;
|
|
502
|
+
}
|
|
503
|
+
const line = i - 1;
|
|
504
|
+
return {
|
|
505
|
+
line,
|
|
506
|
+
column: index - lineOffsets[line]
|
|
507
|
+
};
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
function getRelativePath(from, to) {
|
|
511
|
+
const fromParts = from.split(/[/\\]/);
|
|
512
|
+
const toParts = to.split(/[/\\]/);
|
|
513
|
+
fromParts.pop();
|
|
514
|
+
while (fromParts[0] === toParts[0]) {
|
|
515
|
+
fromParts.shift();
|
|
516
|
+
toParts.shift();
|
|
517
|
+
}
|
|
518
|
+
if (fromParts.length) {
|
|
519
|
+
let i = fromParts.length;
|
|
520
|
+
while (i--) fromParts[i] = "..";
|
|
521
|
+
}
|
|
522
|
+
return fromParts.concat(toParts).join("/");
|
|
523
|
+
}
|
|
524
|
+
function guessIndent(code) {
|
|
525
|
+
const lines = code.split("\n");
|
|
526
|
+
const tabbed = lines.filter((line) => /^\t+/.test(line));
|
|
527
|
+
const spaced = lines.filter((line) => /^ {2,}/.test(line));
|
|
528
|
+
if (tabbed.length === 0 && spaced.length === 0) return null;
|
|
529
|
+
if (tabbed.length >= spaced.length) return " ";
|
|
530
|
+
const min = spaced.reduce((previous, current) => {
|
|
531
|
+
const numSpaces = /^ +/.exec(current)[0].length;
|
|
532
|
+
return Math.min(numSpaces, previous);
|
|
533
|
+
}, Infinity);
|
|
534
|
+
return " ".repeat(min);
|
|
535
|
+
}
|
|
536
|
+
const toString = Object.prototype.toString;
|
|
537
|
+
function isObject(thing) {
|
|
538
|
+
return toString.call(thing) === "[object Object]";
|
|
539
|
+
}
|
|
540
|
+
const BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
541
|
+
const intToChar = /* #__PURE__ */ (() => {
|
|
542
|
+
const chars = /* @__PURE__ */ new Uint8Array(64);
|
|
543
|
+
for (let i = 0; i < 64; i++) chars[i] = BASE64_CHARS.charCodeAt(i);
|
|
544
|
+
return chars;
|
|
545
|
+
})();
|
|
546
|
+
const COMMA = 44;
|
|
547
|
+
const SEMICOLON = 59;
|
|
548
|
+
const BUFFER_SIZE = 16384;
|
|
549
|
+
const FLUSH_THRESHOLD = 16348;
|
|
550
|
+
const scratch = /* #__PURE__ */ new Uint8Array(BUFFER_SIZE);
|
|
551
|
+
function writeVlq(pos, num) {
|
|
552
|
+
num = num < 0 ? -num << 1 | 1 : num << 1;
|
|
553
|
+
do {
|
|
554
|
+
let clamped = num & 31;
|
|
555
|
+
num >>>= 5;
|
|
556
|
+
if (num > 0) clamped |= 32;
|
|
557
|
+
scratch[pos++] = intToChar[clamped];
|
|
558
|
+
} while (num > 0);
|
|
559
|
+
return pos;
|
|
560
|
+
}
|
|
561
|
+
const decoder = /* #__PURE__ */ new TextDecoder();
|
|
562
|
+
var MappingsEncoder = class {
|
|
563
|
+
constructor() {
|
|
564
|
+
this.out = "";
|
|
565
|
+
this.pos = 0;
|
|
566
|
+
this.lines = [];
|
|
567
|
+
this.buffered = 0;
|
|
568
|
+
this.needsComma = false;
|
|
569
|
+
this.prevGenColumn = 0;
|
|
570
|
+
this.prevSourceIndex = 0;
|
|
571
|
+
this.prevSourceLine = 0;
|
|
572
|
+
this.prevSourceColumn = 0;
|
|
573
|
+
this.prevNameIndex = 0;
|
|
574
|
+
}
|
|
575
|
+
endLine(segments) {
|
|
576
|
+
this.lines.push(segments);
|
|
577
|
+
this.buffered += segments.length + 1;
|
|
578
|
+
if (this.buffered >= 4096) this.drain(null);
|
|
579
|
+
}
|
|
580
|
+
segments(segments) {
|
|
581
|
+
this.drain(segments);
|
|
582
|
+
}
|
|
583
|
+
finish(segments) {
|
|
584
|
+
this.drain(segments);
|
|
585
|
+
this.flush();
|
|
586
|
+
return this.out;
|
|
587
|
+
}
|
|
588
|
+
drain(trailing) {
|
|
589
|
+
const lines = this.lines;
|
|
590
|
+
const lineCount = lines.length;
|
|
591
|
+
for (let l = 0; l <= lineCount; l++) {
|
|
592
|
+
const line = l < lineCount ? lines[l] : trailing;
|
|
593
|
+
if (line === null) break;
|
|
594
|
+
for (let i = 0; i < line.length; i++) {
|
|
595
|
+
if (this.pos > FLUSH_THRESHOLD) this.flush();
|
|
596
|
+
const segment = line[i];
|
|
597
|
+
if (this.needsComma) scratch[this.pos++] = COMMA;
|
|
598
|
+
this.needsComma = true;
|
|
599
|
+
this.pos = writeVlq(this.pos, segment[0] - this.prevGenColumn);
|
|
600
|
+
this.prevGenColumn = segment[0];
|
|
601
|
+
this.pos = writeVlq(this.pos, segment[1] - this.prevSourceIndex);
|
|
602
|
+
this.prevSourceIndex = segment[1];
|
|
603
|
+
this.pos = writeVlq(this.pos, segment[2] - this.prevSourceLine);
|
|
604
|
+
this.prevSourceLine = segment[2];
|
|
605
|
+
this.pos = writeVlq(this.pos, segment[3] - this.prevSourceColumn);
|
|
606
|
+
this.prevSourceColumn = segment[3];
|
|
607
|
+
if (segment.length === 5) {
|
|
608
|
+
this.pos = writeVlq(this.pos, segment[4] - this.prevNameIndex);
|
|
609
|
+
this.prevNameIndex = segment[4];
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
if (l < lineCount) {
|
|
613
|
+
if (this.pos > FLUSH_THRESHOLD) this.flush();
|
|
614
|
+
scratch[this.pos++] = SEMICOLON;
|
|
615
|
+
this.needsComma = false;
|
|
616
|
+
this.prevGenColumn = 0;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
lines.length = 0;
|
|
620
|
+
this.buffered = 0;
|
|
621
|
+
this.flush();
|
|
622
|
+
}
|
|
623
|
+
flush() {
|
|
624
|
+
this.out += decoder.decode(scratch.subarray(0, this.pos));
|
|
625
|
+
this.pos = 0;
|
|
626
|
+
}
|
|
627
|
+
};
|
|
628
|
+
const NEWLINE_CHAR$1 = 10;
|
|
629
|
+
function isWordCode(code) {
|
|
630
|
+
return code >= 97 && code <= 122 || code >= 65 && code <= 90 || code >= 48 && code <= 57 || code === 95;
|
|
631
|
+
}
|
|
632
|
+
var Mappings = class {
|
|
633
|
+
constructor(hires, encoder = null) {
|
|
634
|
+
this.hires = hires;
|
|
635
|
+
this.generatedCodeLine = 0;
|
|
636
|
+
this.generatedCodeColumn = 0;
|
|
637
|
+
this.raw = [];
|
|
638
|
+
this.rawSegments = this.raw[this.generatedCodeLine] = [];
|
|
639
|
+
this.rawRangeMappings = [];
|
|
640
|
+
this.rawRangeMappingsIndices = this.rawRangeMappings[this.generatedCodeLine] = [];
|
|
641
|
+
this.encoder = encoder;
|
|
642
|
+
}
|
|
643
|
+
nextLine() {
|
|
644
|
+
if (this.encoder === null) {
|
|
645
|
+
this.generatedCodeLine += 1;
|
|
646
|
+
this.raw[this.generatedCodeLine] = this.rawSegments = [];
|
|
647
|
+
} else {
|
|
648
|
+
this.encoder.endLine(this.rawSegments);
|
|
649
|
+
this.rawSegments = [];
|
|
650
|
+
this.generatedCodeLine += 1;
|
|
651
|
+
}
|
|
652
|
+
this.generatedCodeColumn = 0;
|
|
653
|
+
this.rawRangeMappings[this.generatedCodeLine] = this.rawRangeMappingsIndices = [];
|
|
654
|
+
}
|
|
655
|
+
addEdit(sourceIndex, content, loc, nameIndex) {
|
|
656
|
+
if (content.length) {
|
|
657
|
+
const contentLengthMinusOne = content.length - 1;
|
|
658
|
+
let contentLineEnd = content.indexOf("\n", 0);
|
|
659
|
+
let previousContentLineEnd = -1;
|
|
660
|
+
while (contentLineEnd >= 0 && contentLengthMinusOne > contentLineEnd) {
|
|
661
|
+
const segment = [
|
|
662
|
+
this.generatedCodeColumn,
|
|
663
|
+
sourceIndex,
|
|
664
|
+
loc.line,
|
|
665
|
+
loc.column
|
|
666
|
+
];
|
|
667
|
+
if (nameIndex >= 0) segment.push(nameIndex);
|
|
668
|
+
this.rawSegments.push(segment);
|
|
669
|
+
this.nextLine();
|
|
670
|
+
previousContentLineEnd = contentLineEnd;
|
|
671
|
+
contentLineEnd = content.indexOf("\n", contentLineEnd + 1);
|
|
672
|
+
}
|
|
673
|
+
const segment = [
|
|
674
|
+
this.generatedCodeColumn,
|
|
675
|
+
sourceIndex,
|
|
676
|
+
loc.line,
|
|
677
|
+
loc.column
|
|
678
|
+
];
|
|
679
|
+
if (nameIndex >= 0) segment.push(nameIndex);
|
|
680
|
+
this.rawSegments.push(segment);
|
|
681
|
+
this.advance(content.slice(previousContentLineEnd + 1));
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
addUneditedChunk(sourceIndex, chunk, original, loc, sourcemapLocations) {
|
|
685
|
+
const end = chunk.end;
|
|
686
|
+
let i = chunk.start;
|
|
687
|
+
if (this.hires) {
|
|
688
|
+
const boundary = this.hires === "boundary";
|
|
689
|
+
const experimentalRange = this.hires === "experimental-range";
|
|
690
|
+
const encoder = experimentalRange ? null : this.encoder;
|
|
691
|
+
let charInHiresBoundary = false;
|
|
692
|
+
while (i < end) {
|
|
693
|
+
if (encoder !== null && this.rawSegments.length >= 4096) {
|
|
694
|
+
encoder.segments(this.rawSegments);
|
|
695
|
+
this.rawSegments.length = 0;
|
|
696
|
+
}
|
|
697
|
+
if (experimentalRange && i + 1 >= end) this.rawSegments.push([
|
|
698
|
+
this.generatedCodeColumn,
|
|
699
|
+
sourceIndex,
|
|
700
|
+
loc.line,
|
|
701
|
+
loc.column
|
|
702
|
+
]);
|
|
703
|
+
const code = original.charCodeAt(i);
|
|
704
|
+
if (code === NEWLINE_CHAR$1) {
|
|
705
|
+
loc.line += 1;
|
|
706
|
+
loc.column = 0;
|
|
707
|
+
this.nextLine();
|
|
708
|
+
charInHiresBoundary = false;
|
|
709
|
+
} else {
|
|
710
|
+
if (boundary) {
|
|
711
|
+
if (isWordCode(code)) {
|
|
712
|
+
if (!charInHiresBoundary) {
|
|
713
|
+
this.rawSegments.push([
|
|
714
|
+
this.generatedCodeColumn,
|
|
715
|
+
sourceIndex,
|
|
716
|
+
loc.line,
|
|
717
|
+
loc.column
|
|
718
|
+
]);
|
|
719
|
+
charInHiresBoundary = true;
|
|
720
|
+
}
|
|
721
|
+
} else {
|
|
722
|
+
this.rawSegments.push([
|
|
723
|
+
this.generatedCodeColumn,
|
|
724
|
+
sourceIndex,
|
|
725
|
+
loc.line,
|
|
726
|
+
loc.column
|
|
727
|
+
]);
|
|
728
|
+
charInHiresBoundary = false;
|
|
729
|
+
}
|
|
730
|
+
} else if (experimentalRange) {
|
|
731
|
+
if (i === chunk.start) {
|
|
732
|
+
this.rawRangeMappingsIndices.push(this.rawSegments.length);
|
|
733
|
+
this.rawSegments.push([
|
|
734
|
+
this.generatedCodeColumn,
|
|
735
|
+
sourceIndex,
|
|
736
|
+
loc.line,
|
|
737
|
+
loc.column
|
|
738
|
+
]);
|
|
739
|
+
}
|
|
740
|
+
} else this.rawSegments.push([
|
|
741
|
+
this.generatedCodeColumn,
|
|
742
|
+
sourceIndex,
|
|
743
|
+
loc.line,
|
|
744
|
+
loc.column
|
|
745
|
+
]);
|
|
746
|
+
loc.column += 1;
|
|
747
|
+
this.generatedCodeColumn += 1;
|
|
748
|
+
}
|
|
749
|
+
i += 1;
|
|
750
|
+
}
|
|
751
|
+
} else {
|
|
752
|
+
const bits = sourcemapLocations.bits;
|
|
753
|
+
while (i < end) {
|
|
754
|
+
let newline = original.indexOf("\n", i);
|
|
755
|
+
if (newline === -1 || newline > end) newline = end;
|
|
756
|
+
if (newline > i) {
|
|
757
|
+
this.rawSegments.push([
|
|
758
|
+
this.generatedCodeColumn,
|
|
759
|
+
sourceIndex,
|
|
760
|
+
loc.line,
|
|
761
|
+
loc.column
|
|
762
|
+
]);
|
|
763
|
+
for (let w = i + 1 >> 5, last = newline - 1 >> 5; w <= last; w++) {
|
|
764
|
+
let word = bits[w];
|
|
765
|
+
if (!word) continue;
|
|
766
|
+
const base = w << 5;
|
|
767
|
+
while (word) {
|
|
768
|
+
const lowest = word & -word;
|
|
769
|
+
const index = base + 31 - Math.clz32(lowest);
|
|
770
|
+
if (index > i && index < newline) {
|
|
771
|
+
const offset = index - i;
|
|
772
|
+
this.rawSegments.push([
|
|
773
|
+
this.generatedCodeColumn + offset,
|
|
774
|
+
sourceIndex,
|
|
775
|
+
loc.line,
|
|
776
|
+
loc.column + offset
|
|
777
|
+
]);
|
|
778
|
+
}
|
|
779
|
+
word ^= lowest;
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
loc.column += newline - i;
|
|
783
|
+
this.generatedCodeColumn += newline - i;
|
|
784
|
+
}
|
|
785
|
+
if (newline === end) break;
|
|
786
|
+
loc.line += 1;
|
|
787
|
+
loc.column = 0;
|
|
788
|
+
this.nextLine();
|
|
789
|
+
i = newline + 1;
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
advance(str) {
|
|
794
|
+
if (!str) return;
|
|
795
|
+
const lastNewline = str.lastIndexOf("\n");
|
|
796
|
+
for (let i = str.indexOf("\n"); i !== -1; i = str.indexOf("\n", i + 1)) this.nextLine();
|
|
797
|
+
this.generatedCodeColumn += str.length - lastNewline - 1;
|
|
798
|
+
}
|
|
799
|
+
};
|
|
800
|
+
const n = "\n";
|
|
801
|
+
const NEWLINE_CHAR = "\n".charCodeAt(0);
|
|
802
|
+
const CR_CHAR = "\r".charCodeAt(0);
|
|
803
|
+
const warned = {
|
|
804
|
+
insertLeft: false,
|
|
805
|
+
insertRight: false,
|
|
806
|
+
storeName: false
|
|
807
|
+
};
|
|
808
|
+
/**
|
|
809
|
+
* Expands the `$` substitution patterns that `String.prototype.replace` accepts
|
|
810
|
+
* in a string replacement.
|
|
811
|
+
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#specifying_a_string_as_a_parameter
|
|
812
|
+
*
|
|
813
|
+
* `captures` holds the capture groups in order, so `captures[0]` is `$1`.
|
|
814
|
+
* `namedCaptures` is `undefined` when the pattern has no named groups, and
|
|
815
|
+
* `$<name>` is then literal text - which is also the case for a string search
|
|
816
|
+
* value, where there are no groups of either kind.
|
|
817
|
+
*/
|
|
818
|
+
function expandReplacement(replacement, matched, position, str, captures, namedCaptures) {
|
|
819
|
+
if (!replacement.includes("$")) return replacement;
|
|
820
|
+
let result = "";
|
|
821
|
+
let index = 0;
|
|
822
|
+
while (index < replacement.length) {
|
|
823
|
+
const dollar = replacement.indexOf("$", index);
|
|
824
|
+
if (dollar === -1) {
|
|
825
|
+
result += replacement.slice(index);
|
|
826
|
+
break;
|
|
827
|
+
}
|
|
828
|
+
result += replacement.slice(index, dollar);
|
|
829
|
+
const char = replacement[dollar + 1];
|
|
830
|
+
let expansion = "$";
|
|
831
|
+
let consumed = 1;
|
|
832
|
+
if (char === "$") consumed = 2;
|
|
833
|
+
else if (char === "&") {
|
|
834
|
+
expansion = matched;
|
|
835
|
+
consumed = 2;
|
|
836
|
+
} else if (char === "`") {
|
|
837
|
+
expansion = str.slice(0, position);
|
|
838
|
+
consumed = 2;
|
|
839
|
+
} else if (char === "'") {
|
|
840
|
+
expansion = str.slice(position + matched.length);
|
|
841
|
+
consumed = 2;
|
|
842
|
+
} else if (char === "<" && namedCaptures !== void 0) {
|
|
843
|
+
const close = replacement.indexOf(">", dollar + 2);
|
|
844
|
+
if (close !== -1) {
|
|
845
|
+
expansion = namedCaptures[replacement.slice(dollar + 2, close)] ?? "";
|
|
846
|
+
consumed = close + 1 - dollar;
|
|
847
|
+
}
|
|
848
|
+
} else if (char >= "0" && char <= "9") {
|
|
849
|
+
const second = replacement[dollar + 2];
|
|
850
|
+
const double = second >= "0" && second <= "9" ? Number(char + second) : NaN;
|
|
851
|
+
const single = Number(char);
|
|
852
|
+
if (double >= 1 && double <= captures.length) {
|
|
853
|
+
expansion = captures[double - 1] ?? "";
|
|
854
|
+
consumed = 3;
|
|
855
|
+
} else if (single >= 1 && single <= captures.length) {
|
|
856
|
+
expansion = captures[single - 1] ?? "";
|
|
857
|
+
consumed = 2;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
result += expansion;
|
|
861
|
+
index = dollar + consumed;
|
|
862
|
+
}
|
|
863
|
+
return result;
|
|
864
|
+
}
|
|
865
|
+
var MagicString = class MagicString {
|
|
866
|
+
constructor(string, options = {}) {
|
|
867
|
+
const chunk = new Chunk(0, string.length, string);
|
|
868
|
+
Object.defineProperties(this, {
|
|
869
|
+
original: {
|
|
870
|
+
writable: true,
|
|
871
|
+
value: string
|
|
872
|
+
},
|
|
873
|
+
outro: {
|
|
874
|
+
writable: true,
|
|
875
|
+
value: ""
|
|
876
|
+
},
|
|
877
|
+
intro: {
|
|
878
|
+
writable: true,
|
|
879
|
+
value: ""
|
|
880
|
+
},
|
|
881
|
+
firstChunk: {
|
|
882
|
+
writable: true,
|
|
883
|
+
value: chunk
|
|
884
|
+
},
|
|
885
|
+
lastChunk: {
|
|
886
|
+
writable: true,
|
|
887
|
+
value: chunk
|
|
888
|
+
},
|
|
889
|
+
lastSearchedChunk: {
|
|
890
|
+
writable: true,
|
|
891
|
+
value: chunk
|
|
892
|
+
},
|
|
893
|
+
byStart: {
|
|
894
|
+
writable: true,
|
|
895
|
+
value: /* @__PURE__ */ new Map([[0, chunk]])
|
|
896
|
+
},
|
|
897
|
+
byEnd: {
|
|
898
|
+
writable: true,
|
|
899
|
+
value: /* @__PURE__ */ new Map([[string.length, chunk]])
|
|
900
|
+
},
|
|
901
|
+
filename: {
|
|
902
|
+
writable: true,
|
|
903
|
+
value: options.filename
|
|
904
|
+
},
|
|
905
|
+
indentExclusionRanges: {
|
|
906
|
+
writable: true,
|
|
907
|
+
value: options.indentExclusionRanges
|
|
908
|
+
},
|
|
909
|
+
sourcemapLocations: {
|
|
910
|
+
writable: true,
|
|
911
|
+
value: new BitSet()
|
|
912
|
+
},
|
|
913
|
+
storedNames: {
|
|
914
|
+
writable: true,
|
|
915
|
+
value: {}
|
|
916
|
+
},
|
|
917
|
+
indentStr: {
|
|
918
|
+
writable: true,
|
|
919
|
+
value: void 0
|
|
920
|
+
},
|
|
921
|
+
ignoreList: {
|
|
922
|
+
writable: true,
|
|
923
|
+
value: options.ignoreList
|
|
924
|
+
},
|
|
925
|
+
offset: {
|
|
926
|
+
writable: true,
|
|
927
|
+
value: options.offset || 0
|
|
928
|
+
},
|
|
929
|
+
hasMovedChunks: {
|
|
930
|
+
writable: true,
|
|
931
|
+
value: false
|
|
932
|
+
}
|
|
933
|
+
});
|
|
934
|
+
}
|
|
935
|
+
/**
|
|
936
|
+
* Adds the specified character index (with respect to the original string) to sourcemap mappings, if `hires` is false.
|
|
937
|
+
*/
|
|
938
|
+
addSourcemapLocation(char) {
|
|
939
|
+
this.sourcemapLocations.add(char);
|
|
940
|
+
}
|
|
941
|
+
/**
|
|
942
|
+
* Appends the specified content to the end of the string.
|
|
943
|
+
*/
|
|
944
|
+
append(content) {
|
|
945
|
+
if (typeof content !== "string") throw new MagicStringError(`content must be a string, got ${typeof content}`);
|
|
946
|
+
this.outro += content;
|
|
947
|
+
return this;
|
|
948
|
+
}
|
|
949
|
+
/**
|
|
950
|
+
* Appends the specified content at the index in the original string.
|
|
951
|
+
* If a range *ending* with index is subsequently moved, the insert will be moved with it.
|
|
952
|
+
* See also `s.prependLeft(...)`.
|
|
953
|
+
*/
|
|
954
|
+
appendLeft(index, content) {
|
|
955
|
+
index = index + this.offset;
|
|
956
|
+
if (typeof content !== "string") throw new MagicStringError(`content must be a string, got ${typeof content}`);
|
|
957
|
+
this._split(index);
|
|
958
|
+
const chunk = this.byEnd.get(index);
|
|
959
|
+
if (chunk) chunk.appendLeft(content);
|
|
960
|
+
else this.intro += content;
|
|
961
|
+
return this;
|
|
962
|
+
}
|
|
963
|
+
/**
|
|
964
|
+
* Appends the specified content at the index in the original string.
|
|
965
|
+
* If a range *starting* with index is subsequently moved, the insert will be moved with it.
|
|
966
|
+
* See also `s.prependRight(...)`.
|
|
967
|
+
*/
|
|
968
|
+
appendRight(index, content) {
|
|
969
|
+
index = index + this.offset;
|
|
970
|
+
if (typeof content !== "string") throw new MagicStringError(`content must be a string, got ${typeof content}`);
|
|
971
|
+
this._split(index);
|
|
972
|
+
const chunk = this.byStart.get(index);
|
|
973
|
+
if (chunk) chunk.appendRight(content);
|
|
974
|
+
else this.outro += content;
|
|
975
|
+
return this;
|
|
976
|
+
}
|
|
977
|
+
/**
|
|
978
|
+
* Does what you'd expect.
|
|
979
|
+
*/
|
|
980
|
+
clone() {
|
|
981
|
+
const cloned = new MagicString(this.original, {
|
|
982
|
+
filename: this.filename,
|
|
983
|
+
ignoreList: this.ignoreList,
|
|
984
|
+
offset: this.offset
|
|
985
|
+
});
|
|
986
|
+
let originalChunk = this.firstChunk;
|
|
987
|
+
let clonedChunk = cloned.firstChunk = cloned.lastSearchedChunk = originalChunk.clone();
|
|
988
|
+
while (originalChunk) {
|
|
989
|
+
cloned.byStart.set(clonedChunk.start, clonedChunk);
|
|
990
|
+
cloned.byEnd.set(clonedChunk.end, clonedChunk);
|
|
991
|
+
const nextOriginalChunk = originalChunk.next;
|
|
992
|
+
const nextClonedChunk = nextOriginalChunk && nextOriginalChunk.clone();
|
|
993
|
+
if (nextClonedChunk) {
|
|
994
|
+
clonedChunk.next = nextClonedChunk;
|
|
995
|
+
nextClonedChunk.previous = clonedChunk;
|
|
996
|
+
clonedChunk = nextClonedChunk;
|
|
997
|
+
}
|
|
998
|
+
originalChunk = nextOriginalChunk;
|
|
999
|
+
}
|
|
1000
|
+
cloned.lastChunk = clonedChunk;
|
|
1001
|
+
if (this.indentExclusionRanges) cloned.indentExclusionRanges = this.indentExclusionRanges.slice();
|
|
1002
|
+
cloned.sourcemapLocations = new BitSet(this.sourcemapLocations);
|
|
1003
|
+
cloned.storedNames = { ...this.storedNames };
|
|
1004
|
+
cloned.intro = this.intro;
|
|
1005
|
+
cloned.outro = this.outro;
|
|
1006
|
+
cloned.hasMovedChunks = this.hasMovedChunks;
|
|
1007
|
+
return cloned;
|
|
1008
|
+
}
|
|
1009
|
+
/**
|
|
1010
|
+
* Generates a sourcemap object with raw mappings in array form, rather than encoded as a string.
|
|
1011
|
+
* Useful if you need to manipulate the sourcemap further, but most of the time you will use `generateMap` instead.
|
|
1012
|
+
*/
|
|
1013
|
+
generateDecodedMap(options) {
|
|
1014
|
+
options = options || {};
|
|
1015
|
+
const mappings = new Mappings(options.hires);
|
|
1016
|
+
const names = this._generateMappings(mappings);
|
|
1017
|
+
return {
|
|
1018
|
+
...this._mapProperties(options, names),
|
|
1019
|
+
mappings: mappings.raw,
|
|
1020
|
+
rangeMappings: mappings.rawRangeMappings
|
|
1021
|
+
};
|
|
1022
|
+
}
|
|
1023
|
+
/**
|
|
1024
|
+
* Generates a version 3 sourcemap.
|
|
1025
|
+
*/
|
|
1026
|
+
generateMap(options) {
|
|
1027
|
+
options = options || {};
|
|
1028
|
+
const encoder = new MappingsEncoder();
|
|
1029
|
+
const mappings = new Mappings(options.hires, encoder);
|
|
1030
|
+
const names = this._generateMappings(mappings);
|
|
1031
|
+
return new SourceMap({
|
|
1032
|
+
...this._mapProperties(options, names),
|
|
1033
|
+
mappings: encoder.finish(mappings.rawSegments),
|
|
1034
|
+
rangeMappings: mappings.rawRangeMappings
|
|
1035
|
+
});
|
|
1036
|
+
}
|
|
1037
|
+
/** @internal */
|
|
1038
|
+
_generateMappings(mappings) {
|
|
1039
|
+
const sourceIndex = 0;
|
|
1040
|
+
const names = Object.keys(this.storedNames);
|
|
1041
|
+
const locate = getLocator(this.original);
|
|
1042
|
+
if (this.intro) mappings.advance(this.intro);
|
|
1043
|
+
this.firstChunk.eachNext((chunk) => {
|
|
1044
|
+
const loc = locate(chunk.start);
|
|
1045
|
+
if (chunk.intro.length) mappings.advance(chunk.intro);
|
|
1046
|
+
if (chunk.edited) mappings.addEdit(sourceIndex, chunk.content, loc, chunk.storeName ? names.indexOf(chunk.original) : -1);
|
|
1047
|
+
else mappings.addUneditedChunk(sourceIndex, chunk, this.original, loc, this.sourcemapLocations);
|
|
1048
|
+
if (chunk.outro.length) mappings.advance(chunk.outro);
|
|
1049
|
+
});
|
|
1050
|
+
if (this.outro) mappings.advance(this.outro);
|
|
1051
|
+
return names;
|
|
1052
|
+
}
|
|
1053
|
+
/** @internal */
|
|
1054
|
+
_mapProperties(options, names) {
|
|
1055
|
+
return {
|
|
1056
|
+
file: options.file ? options.file.split(/[/\\]/).pop() : void 0,
|
|
1057
|
+
sources: [options.source ? getRelativePath(options.file || "", options.source) : options.file || ""],
|
|
1058
|
+
sourcesContent: options.includeContent ? [this.original] : void 0,
|
|
1059
|
+
names,
|
|
1060
|
+
x_google_ignoreList: this.ignoreList ? [0] : void 0
|
|
1061
|
+
};
|
|
1062
|
+
}
|
|
1063
|
+
/** @internal */
|
|
1064
|
+
_ensureindentStr() {
|
|
1065
|
+
if (this.indentStr === void 0) this.indentStr = guessIndent(this.original);
|
|
1066
|
+
}
|
|
1067
|
+
/** @internal */
|
|
1068
|
+
_getRawIndentString() {
|
|
1069
|
+
this._ensureindentStr();
|
|
1070
|
+
return this.indentStr;
|
|
1071
|
+
}
|
|
1072
|
+
getIndentString() {
|
|
1073
|
+
this._ensureindentStr();
|
|
1074
|
+
return this.indentStr === null ? " " : this.indentStr;
|
|
1075
|
+
}
|
|
1076
|
+
indent(indentStr, options) {
|
|
1077
|
+
const pattern = /^[^\r\n]/gm;
|
|
1078
|
+
if (isObject(indentStr)) {
|
|
1079
|
+
options = indentStr;
|
|
1080
|
+
indentStr = void 0;
|
|
1081
|
+
}
|
|
1082
|
+
if (indentStr === void 0) {
|
|
1083
|
+
this._ensureindentStr();
|
|
1084
|
+
indentStr = this.indentStr || " ";
|
|
1085
|
+
}
|
|
1086
|
+
if (indentStr === "") return this;
|
|
1087
|
+
const resolvedIndentStr = indentStr;
|
|
1088
|
+
options = options || {};
|
|
1089
|
+
const isExcluded = {};
|
|
1090
|
+
if (options.exclude) (typeof options.exclude[0] === "number" ? [options.exclude] : options.exclude).forEach((exclusion) => {
|
|
1091
|
+
for (let i = exclusion[0]; i < exclusion[1]; i += 1) isExcluded[i] = true;
|
|
1092
|
+
});
|
|
1093
|
+
let shouldIndentNextCharacter = options.indentStart !== false;
|
|
1094
|
+
const indentPiece = (str) => {
|
|
1095
|
+
if (str === "") return str;
|
|
1096
|
+
const indented = str.replace(pattern, (match, offset) => offset > 0 || shouldIndentNextCharacter ? `${resolvedIndentStr}${match}` : match);
|
|
1097
|
+
shouldIndentNextCharacter = str[str.length - 1] === "\n";
|
|
1098
|
+
return indented;
|
|
1099
|
+
};
|
|
1100
|
+
this.intro = indentPiece(this.intro);
|
|
1101
|
+
let charIndex = 0;
|
|
1102
|
+
let chunk = this.firstChunk;
|
|
1103
|
+
const indentAt = (index) => {
|
|
1104
|
+
shouldIndentNextCharacter = false;
|
|
1105
|
+
if (index === chunk.start) chunk.appendRight(resolvedIndentStr);
|
|
1106
|
+
else {
|
|
1107
|
+
this._splitChunk(chunk, index);
|
|
1108
|
+
chunk = chunk.next;
|
|
1109
|
+
chunk.prependRight(resolvedIndentStr);
|
|
1110
|
+
}
|
|
1111
|
+
};
|
|
1112
|
+
while (chunk) {
|
|
1113
|
+
const end = chunk.end;
|
|
1114
|
+
if (!isExcluded[chunk.start]) chunk.intro = indentPiece(chunk.intro);
|
|
1115
|
+
if (chunk.edited) {
|
|
1116
|
+
if (!isExcluded[charIndex]) chunk.content = indentPiece(chunk.content);
|
|
1117
|
+
} else if (options.exclude) {
|
|
1118
|
+
charIndex = chunk.start;
|
|
1119
|
+
while (charIndex < end) {
|
|
1120
|
+
if (!isExcluded[charIndex]) {
|
|
1121
|
+
const char = this.original.charCodeAt(charIndex);
|
|
1122
|
+
if (char === NEWLINE_CHAR) shouldIndentNextCharacter = true;
|
|
1123
|
+
else if (char !== CR_CHAR && shouldIndentNextCharacter) indentAt(charIndex);
|
|
1124
|
+
}
|
|
1125
|
+
charIndex += 1;
|
|
1126
|
+
}
|
|
1127
|
+
} else {
|
|
1128
|
+
charIndex = chunk.start;
|
|
1129
|
+
while (charIndex < end) {
|
|
1130
|
+
if (!shouldIndentNextCharacter) {
|
|
1131
|
+
const nextLine = this.original.indexOf(n, charIndex);
|
|
1132
|
+
if (nextLine === -1 || nextLine >= end) break;
|
|
1133
|
+
shouldIndentNextCharacter = true;
|
|
1134
|
+
charIndex = nextLine + 1;
|
|
1135
|
+
continue;
|
|
1136
|
+
}
|
|
1137
|
+
const char = this.original.charCodeAt(charIndex);
|
|
1138
|
+
if (char === NEWLINE_CHAR || char === CR_CHAR) {
|
|
1139
|
+
charIndex += 1;
|
|
1140
|
+
continue;
|
|
1141
|
+
}
|
|
1142
|
+
indentAt(charIndex);
|
|
1143
|
+
charIndex += 1;
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
if (!isExcluded[chunk.end - 1]) chunk.outro = indentPiece(chunk.outro);
|
|
1147
|
+
charIndex = chunk.end;
|
|
1148
|
+
chunk = chunk.next;
|
|
1149
|
+
}
|
|
1150
|
+
this.outro = indentPiece(this.outro);
|
|
1151
|
+
return this;
|
|
1152
|
+
}
|
|
1153
|
+
/** @internal */
|
|
1154
|
+
insert() {
|
|
1155
|
+
throw new MagicStringError("insert() is deprecated, use appendLeft() or prependRight()");
|
|
1156
|
+
}
|
|
1157
|
+
/** @internal */
|
|
1158
|
+
insertLeft(index, content) {
|
|
1159
|
+
if (!warned.insertLeft) {
|
|
1160
|
+
console.warn("magicString.insertLeft(...) is deprecated. Use magicString.appendLeft(...) instead");
|
|
1161
|
+
warned.insertLeft = true;
|
|
1162
|
+
}
|
|
1163
|
+
return this.appendLeft(index, content);
|
|
1164
|
+
}
|
|
1165
|
+
/** @internal */
|
|
1166
|
+
insertRight(index, content) {
|
|
1167
|
+
if (!warned.insertRight) {
|
|
1168
|
+
console.warn("magicString.insertRight(...) is deprecated. Use magicString.prependRight(...) instead");
|
|
1169
|
+
warned.insertRight = true;
|
|
1170
|
+
}
|
|
1171
|
+
return this.prependRight(index, content);
|
|
1172
|
+
}
|
|
1173
|
+
/**
|
|
1174
|
+
* Moves the characters from `start` and `end` to `index`.
|
|
1175
|
+
*
|
|
1176
|
+
* `affinity` controls where the range is anchored at `index`. With the
|
|
1177
|
+
* default `'right'`, it is inserted before the content that starts at `index`;
|
|
1178
|
+
* with `'left'`, it is inserted after the content that ends at `index`. The
|
|
1179
|
+
* two differ only when other content has already been moved to that boundary,
|
|
1180
|
+
* mirroring the `appendLeft`/`appendRight` distinction.
|
|
1181
|
+
*/
|
|
1182
|
+
move(start, end, index, affinity = "right") {
|
|
1183
|
+
start = start + this.offset;
|
|
1184
|
+
end = end + this.offset;
|
|
1185
|
+
index = index + this.offset;
|
|
1186
|
+
if (start === end) return this;
|
|
1187
|
+
if (index >= start && index <= end) throw new MagicStringError("cannot move a selection inside itself");
|
|
1188
|
+
this._split(start);
|
|
1189
|
+
this._split(end);
|
|
1190
|
+
this._split(index);
|
|
1191
|
+
const first = this.byStart.get(start);
|
|
1192
|
+
const last = this.byEnd.get(end);
|
|
1193
|
+
if (this.hasMovedChunks) {
|
|
1194
|
+
let cursor = first;
|
|
1195
|
+
while (cursor !== last) {
|
|
1196
|
+
cursor = cursor.next;
|
|
1197
|
+
if (!cursor || cursor.start < start || cursor.end > end) throw new MagicStringError(`cannot move ${start} to ${end} because an earlier move split that range`);
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
const oldLeft = first.previous;
|
|
1201
|
+
const oldRight = last.next;
|
|
1202
|
+
let newLeft;
|
|
1203
|
+
let newRight;
|
|
1204
|
+
if (affinity === "left") {
|
|
1205
|
+
newLeft = this.byEnd.get(index) ?? null;
|
|
1206
|
+
if (!newLeft) {
|
|
1207
|
+
if (first === this.firstChunk) return this;
|
|
1208
|
+
newRight = this.firstChunk;
|
|
1209
|
+
} else {
|
|
1210
|
+
if (newLeft.next === first) return this;
|
|
1211
|
+
newRight = newLeft.next;
|
|
1212
|
+
}
|
|
1213
|
+
} else {
|
|
1214
|
+
newRight = this.byStart.get(index) ?? null;
|
|
1215
|
+
if (!newRight) {
|
|
1216
|
+
if (last === this.lastChunk) return this;
|
|
1217
|
+
newLeft = this.lastChunk;
|
|
1218
|
+
} else {
|
|
1219
|
+
if (newRight.previous === last) return this;
|
|
1220
|
+
newLeft = newRight.previous;
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
if (oldLeft) oldLeft.next = oldRight;
|
|
1224
|
+
if (oldRight) oldRight.previous = oldLeft;
|
|
1225
|
+
if (newLeft) newLeft.next = first;
|
|
1226
|
+
if (newRight) newRight.previous = last;
|
|
1227
|
+
if (!first.previous) this.firstChunk = last.next;
|
|
1228
|
+
if (!last.next) {
|
|
1229
|
+
this.lastChunk = first.previous;
|
|
1230
|
+
this.lastChunk.next = null;
|
|
1231
|
+
}
|
|
1232
|
+
first.previous = newLeft;
|
|
1233
|
+
last.next = newRight || null;
|
|
1234
|
+
if (!newLeft) this.firstChunk = first;
|
|
1235
|
+
if (!newRight) this.lastChunk = last;
|
|
1236
|
+
this.hasMovedChunks = true;
|
|
1237
|
+
return this;
|
|
1238
|
+
}
|
|
1239
|
+
/**
|
|
1240
|
+
* Replaces the characters from `start` to `end` with `content`, along with the appended/prepended content in
|
|
1241
|
+
* that range. The same restrictions as `s.remove()` apply.
|
|
1242
|
+
*
|
|
1243
|
+
* The fourth argument is optional. It can have a storeName property - if true, the original name will be stored
|
|
1244
|
+
* for later inclusion in a sourcemap's names array - and a contentOnly property which determines whether only
|
|
1245
|
+
* the content is overwritten, or anything that was appended/prepended to the range as well.
|
|
1246
|
+
*
|
|
1247
|
+
* It may be preferred to use `s.update(...)` instead if you wish to avoid overwriting the appended/prepended content.
|
|
1248
|
+
*/
|
|
1249
|
+
overwrite(start, end, content, options) {
|
|
1250
|
+
const optionObject = typeof options === "object" && options ? options : {};
|
|
1251
|
+
return this.update(start, end, content, {
|
|
1252
|
+
...optionObject,
|
|
1253
|
+
overwrite: !optionObject.contentOnly
|
|
1254
|
+
});
|
|
1255
|
+
}
|
|
1256
|
+
/**
|
|
1257
|
+
* Replaces the characters from `start` to `end` with `content`. The same restrictions as `s.remove()` apply.
|
|
1258
|
+
*
|
|
1259
|
+
* The fourth argument is optional. It can have a storeName property - if true, the original name will be stored
|
|
1260
|
+
* for later inclusion in a sourcemap's names array - and an overwrite property which determines whether only
|
|
1261
|
+
* the content is overwritten, or anything that was appended/prepended to the range as well.
|
|
1262
|
+
*/
|
|
1263
|
+
update(start, end, content, options) {
|
|
1264
|
+
start = start + this.offset;
|
|
1265
|
+
end = end + this.offset;
|
|
1266
|
+
if (typeof content !== "string") throw new MagicStringError(`content must be a string, got ${typeof content}`);
|
|
1267
|
+
if (this.original.length !== 0) {
|
|
1268
|
+
if (start < 0) start = Math.max(0, start + this.original.length);
|
|
1269
|
+
if (end < 0) end = Math.max(0, end + this.original.length);
|
|
1270
|
+
}
|
|
1271
|
+
if (start < 0) throw new MagicStringError(`start ${start} is out of bounds`);
|
|
1272
|
+
if (end > this.original.length) throw new MagicStringError(`end ${end} is out of bounds`);
|
|
1273
|
+
if (start === end) throw new MagicStringError(`cannot overwrite a zero-length range at ${start}, use appendLeft() or prependRight()`);
|
|
1274
|
+
if (start > end) throw new MagicStringError(`end must be greater than start (start: ${start}, end: ${end})`);
|
|
1275
|
+
this._split(start);
|
|
1276
|
+
this._split(end);
|
|
1277
|
+
if (options === true) {
|
|
1278
|
+
if (!warned.storeName) {
|
|
1279
|
+
console.warn("The final argument to magicString.overwrite(...) should be an options object. See https://github.com/rich-harris/magic-string");
|
|
1280
|
+
warned.storeName = true;
|
|
1281
|
+
}
|
|
1282
|
+
options = { storeName: true };
|
|
1283
|
+
}
|
|
1284
|
+
const optionObject = typeof options === "object" && options ? options : {};
|
|
1285
|
+
const storeName = optionObject.storeName || false;
|
|
1286
|
+
const overwrite = optionObject.overwrite || false;
|
|
1287
|
+
if (storeName) {
|
|
1288
|
+
const original = this.original.slice(start, end);
|
|
1289
|
+
Object.defineProperty(this.storedNames, original, {
|
|
1290
|
+
writable: true,
|
|
1291
|
+
value: true,
|
|
1292
|
+
enumerable: true
|
|
1293
|
+
});
|
|
1294
|
+
}
|
|
1295
|
+
const first = this.byStart.get(start);
|
|
1296
|
+
const last = this.byEnd.get(end);
|
|
1297
|
+
/* v8 ignore else -- unreachable: a valid start/end always yields a `first` chunk */
|
|
1298
|
+
if (first) {
|
|
1299
|
+
let chunk = first;
|
|
1300
|
+
while (chunk !== last) {
|
|
1301
|
+
if (chunk.next !== this.byStart.get(chunk.end)) throw new MagicStringError("cannot overwrite across a split point");
|
|
1302
|
+
chunk = chunk.next;
|
|
1303
|
+
chunk.edit("", false, !overwrite);
|
|
1304
|
+
}
|
|
1305
|
+
first.edit(content, storeName, !overwrite);
|
|
1306
|
+
} else {
|
|
1307
|
+
const newChunk = new Chunk(start, end, "").edit(content, storeName);
|
|
1308
|
+
last.next = newChunk;
|
|
1309
|
+
newChunk.previous = last;
|
|
1310
|
+
}
|
|
1311
|
+
return this;
|
|
1312
|
+
}
|
|
1313
|
+
/**
|
|
1314
|
+
* Prepends the string with the specified content.
|
|
1315
|
+
*/
|
|
1316
|
+
prepend(content) {
|
|
1317
|
+
if (typeof content !== "string") throw new MagicStringError(`content must be a string, got ${typeof content}`);
|
|
1318
|
+
this.intro = content + this.intro;
|
|
1319
|
+
return this;
|
|
1320
|
+
}
|
|
1321
|
+
/**
|
|
1322
|
+
* Same as `s.appendLeft(...)`, except that the inserted content will go *before* any previous appends or prepends at index
|
|
1323
|
+
*/
|
|
1324
|
+
prependLeft(index, content) {
|
|
1325
|
+
index = index + this.offset;
|
|
1326
|
+
if (typeof content !== "string") throw new MagicStringError(`content must be a string, got ${typeof content}`);
|
|
1327
|
+
this._split(index);
|
|
1328
|
+
const chunk = this.byEnd.get(index);
|
|
1329
|
+
if (chunk) chunk.prependLeft(content);
|
|
1330
|
+
else this.intro = content + this.intro;
|
|
1331
|
+
return this;
|
|
1332
|
+
}
|
|
1333
|
+
/**
|
|
1334
|
+
* Same as `s.appendRight(...)`, except that the inserted content will go *before* any previous appends or prepends at `index`
|
|
1335
|
+
*/
|
|
1336
|
+
prependRight(index, content) {
|
|
1337
|
+
index = index + this.offset;
|
|
1338
|
+
if (typeof content !== "string") throw new MagicStringError(`content must be a string, got ${typeof content}`);
|
|
1339
|
+
this._split(index);
|
|
1340
|
+
const chunk = this.byStart.get(index);
|
|
1341
|
+
if (chunk) chunk.prependRight(content);
|
|
1342
|
+
else this.outro = content + this.outro;
|
|
1343
|
+
return this;
|
|
1344
|
+
}
|
|
1345
|
+
/**
|
|
1346
|
+
* Removes the characters from `start` to `end` (of the original string, **not** the generated string).
|
|
1347
|
+
* Content appended or prepended at positions strictly inside the range is removed with it, while
|
|
1348
|
+
* content attached at `start` or `end` is preserved — use `s.overwrite(start, end, '')` to remove
|
|
1349
|
+
* the range including its edge inserts.
|
|
1350
|
+
* Repeated removals and removals with partially overlapping ranges are allowed.
|
|
1351
|
+
*/
|
|
1352
|
+
remove(start, end) {
|
|
1353
|
+
start = start + this.offset;
|
|
1354
|
+
end = end + this.offset;
|
|
1355
|
+
if (this.original.length !== 0) {
|
|
1356
|
+
if (start < 0) start = Math.max(0, start + this.original.length);
|
|
1357
|
+
if (end < 0) end = Math.max(0, end + this.original.length);
|
|
1358
|
+
}
|
|
1359
|
+
if (start === end) return this;
|
|
1360
|
+
if (start < 0 || end > this.original.length) throw new MagicStringError(`range ${start}–${end} is out of bounds`);
|
|
1361
|
+
if (start > end) throw new MagicStringError(`end must be greater than start (start: ${start}, end: ${end})`);
|
|
1362
|
+
this._split(start);
|
|
1363
|
+
this._split(end);
|
|
1364
|
+
let chunk = this.byStart.get(start);
|
|
1365
|
+
while (chunk) {
|
|
1366
|
+
if (chunk.start > start) chunk.intro = "";
|
|
1367
|
+
if (chunk.end < end) chunk.outro = "";
|
|
1368
|
+
chunk.edit("", false, true);
|
|
1369
|
+
chunk = end > chunk.end ? this.byStart.get(chunk.end) : null;
|
|
1370
|
+
}
|
|
1371
|
+
return this;
|
|
1372
|
+
}
|
|
1373
|
+
/**
|
|
1374
|
+
* Reset the modified characters from `start` to `end` (of the original string, **not** the generated string).
|
|
1375
|
+
*/
|
|
1376
|
+
reset(start, end) {
|
|
1377
|
+
start = start + this.offset;
|
|
1378
|
+
end = end + this.offset;
|
|
1379
|
+
if (this.original.length !== 0) {
|
|
1380
|
+
if (start < 0) start = Math.max(0, start + this.original.length);
|
|
1381
|
+
if (end < 0) end = Math.max(0, end + this.original.length);
|
|
1382
|
+
}
|
|
1383
|
+
if (start === end) return this;
|
|
1384
|
+
if (start < 0 || end > this.original.length) throw new MagicStringError(`range ${start}–${end} is out of bounds`);
|
|
1385
|
+
if (start > end) throw new MagicStringError(`end must be greater than start (start: ${start}, end: ${end})`);
|
|
1386
|
+
this._split(start);
|
|
1387
|
+
this._split(end);
|
|
1388
|
+
let chunk = this.byStart.get(start);
|
|
1389
|
+
while (chunk) {
|
|
1390
|
+
chunk.reset();
|
|
1391
|
+
chunk = end > chunk.end ? this.byStart.get(chunk.end) : null;
|
|
1392
|
+
}
|
|
1393
|
+
return this;
|
|
1394
|
+
}
|
|
1395
|
+
lastChar() {
|
|
1396
|
+
if (this.outro.length) return this.outro[this.outro.length - 1];
|
|
1397
|
+
let chunk = this.lastChunk;
|
|
1398
|
+
while (chunk) {
|
|
1399
|
+
if (chunk.outro.length) return chunk.outro[chunk.outro.length - 1];
|
|
1400
|
+
if (chunk.content.length) return chunk.content[chunk.content.length - 1];
|
|
1401
|
+
if (chunk.intro.length) return chunk.intro[chunk.intro.length - 1];
|
|
1402
|
+
chunk = chunk.previous;
|
|
1403
|
+
}
|
|
1404
|
+
if (this.intro.length) return this.intro[this.intro.length - 1];
|
|
1405
|
+
return "";
|
|
1406
|
+
}
|
|
1407
|
+
lastLine() {
|
|
1408
|
+
let lineIndex = this.outro.lastIndexOf(n);
|
|
1409
|
+
if (lineIndex !== -1) return this.outro.substr(lineIndex + 1);
|
|
1410
|
+
let lineStr = this.outro;
|
|
1411
|
+
let chunk = this.lastChunk;
|
|
1412
|
+
while (chunk) {
|
|
1413
|
+
if (chunk.outro.length > 0) {
|
|
1414
|
+
lineIndex = chunk.outro.lastIndexOf(n);
|
|
1415
|
+
if (lineIndex !== -1) return chunk.outro.substr(lineIndex + 1) + lineStr;
|
|
1416
|
+
lineStr = chunk.outro + lineStr;
|
|
1417
|
+
}
|
|
1418
|
+
if (chunk.content.length > 0) {
|
|
1419
|
+
lineIndex = chunk.content.lastIndexOf(n);
|
|
1420
|
+
if (lineIndex !== -1) return chunk.content.substr(lineIndex + 1) + lineStr;
|
|
1421
|
+
lineStr = chunk.content + lineStr;
|
|
1422
|
+
}
|
|
1423
|
+
if (chunk.intro.length > 0) {
|
|
1424
|
+
lineIndex = chunk.intro.lastIndexOf(n);
|
|
1425
|
+
if (lineIndex !== -1) return chunk.intro.substr(lineIndex + 1) + lineStr;
|
|
1426
|
+
lineStr = chunk.intro + lineStr;
|
|
1427
|
+
}
|
|
1428
|
+
chunk = chunk.previous;
|
|
1429
|
+
}
|
|
1430
|
+
lineIndex = this.intro.lastIndexOf(n);
|
|
1431
|
+
if (lineIndex !== -1) return this.intro.substr(lineIndex + 1) + lineStr;
|
|
1432
|
+
return this.intro + lineStr;
|
|
1433
|
+
}
|
|
1434
|
+
/**
|
|
1435
|
+
* Returns the content of the generated string that corresponds to the slice between `start` and `end` of the original string.
|
|
1436
|
+
* Throws error if the indices are for characters that were already removed.
|
|
1437
|
+
*/
|
|
1438
|
+
slice(start = 0, end = this.original.length - this.offset) {
|
|
1439
|
+
start = start + this.offset;
|
|
1440
|
+
end = end + this.offset;
|
|
1441
|
+
if (this.original.length !== 0) {
|
|
1442
|
+
if (start < 0) start = Math.max(0, start + this.original.length);
|
|
1443
|
+
if (end < 0) end = Math.max(0, end + this.original.length);
|
|
1444
|
+
}
|
|
1445
|
+
let result = "";
|
|
1446
|
+
let chunk = this.firstChunk;
|
|
1447
|
+
while (chunk && (chunk.start > start || chunk.end <= start)) {
|
|
1448
|
+
if (chunk.start < end && chunk.end >= end || end === 0 && chunk.start === 0) return result;
|
|
1449
|
+
chunk = chunk.next;
|
|
1450
|
+
}
|
|
1451
|
+
if (chunk && chunk.edited && chunk.start !== start) throw new MagicStringError(`cannot use edited character ${start} as slice start anchor`);
|
|
1452
|
+
const startChunk = chunk;
|
|
1453
|
+
while (chunk) {
|
|
1454
|
+
if (end === 0 && chunk.start === 0) break;
|
|
1455
|
+
if (chunk.intro && (startChunk !== chunk || chunk.start === start)) result += chunk.intro;
|
|
1456
|
+
const containsEnd = chunk.start < end && chunk.end >= end;
|
|
1457
|
+
if (containsEnd && chunk.edited && chunk.end !== end) throw new MagicStringError(`cannot use edited character ${end} as slice end anchor`);
|
|
1458
|
+
const sliceStart = startChunk === chunk ? start - chunk.start : 0;
|
|
1459
|
+
const sliceEnd = containsEnd ? chunk.content.length + end - chunk.end : chunk.content.length;
|
|
1460
|
+
result += chunk.content.slice(sliceStart, sliceEnd);
|
|
1461
|
+
if (chunk.outro && (!containsEnd || chunk.end === end)) result += chunk.outro;
|
|
1462
|
+
if (containsEnd) break;
|
|
1463
|
+
chunk = chunk.next;
|
|
1464
|
+
}
|
|
1465
|
+
return result;
|
|
1466
|
+
}
|
|
1467
|
+
/**
|
|
1468
|
+
* Returns a clone of `s`, with all content before the `start` and `end` characters of the original string removed.
|
|
1469
|
+
*/
|
|
1470
|
+
snip(start, end) {
|
|
1471
|
+
const clone = this.clone();
|
|
1472
|
+
clone.remove(0, start);
|
|
1473
|
+
clone.remove(end, clone.original.length);
|
|
1474
|
+
return clone;
|
|
1475
|
+
}
|
|
1476
|
+
/** @internal */
|
|
1477
|
+
_split(index) {
|
|
1478
|
+
if (this.byStart.get(index) || this.byEnd.get(index)) return;
|
|
1479
|
+
let chunk = this.lastSearchedChunk;
|
|
1480
|
+
let previousChunk = chunk;
|
|
1481
|
+
const searchForward = index > chunk.end;
|
|
1482
|
+
while (chunk) {
|
|
1483
|
+
if (chunk.contains(index)) return this._splitChunk(chunk, index);
|
|
1484
|
+
chunk = searchForward ? this.byStart.get(chunk.end) : this.byEnd.get(chunk.start);
|
|
1485
|
+
if (chunk === previousChunk) return;
|
|
1486
|
+
previousChunk = chunk;
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
/** @internal */
|
|
1490
|
+
_splitChunk(chunk, index) {
|
|
1491
|
+
if (chunk.edited && chunk.content.length) {
|
|
1492
|
+
const loc = getLocator(this.original)(index);
|
|
1493
|
+
throw new MagicStringError(`cannot split a chunk that has already been edited (${loc.line}:${loc.column} – "${chunk.original}")`);
|
|
1494
|
+
}
|
|
1495
|
+
const newChunk = chunk.split(index);
|
|
1496
|
+
this.byEnd.set(index, chunk);
|
|
1497
|
+
this.byStart.set(index, newChunk);
|
|
1498
|
+
this.byEnd.set(newChunk.end, newChunk);
|
|
1499
|
+
if (chunk === this.lastChunk) this.lastChunk = newChunk;
|
|
1500
|
+
this.lastSearchedChunk = chunk;
|
|
1501
|
+
return true;
|
|
1502
|
+
}
|
|
1503
|
+
/**
|
|
1504
|
+
* Returns the generated string.
|
|
1505
|
+
*/
|
|
1506
|
+
toString() {
|
|
1507
|
+
let str = this.intro;
|
|
1508
|
+
let chunk = this.firstChunk;
|
|
1509
|
+
while (chunk) {
|
|
1510
|
+
str += chunk.toString();
|
|
1511
|
+
chunk = chunk.next;
|
|
1512
|
+
}
|
|
1513
|
+
return str + this.outro;
|
|
1514
|
+
}
|
|
1515
|
+
/**
|
|
1516
|
+
* Returns true if the resulting source is empty (disregarding white space).
|
|
1517
|
+
*/
|
|
1518
|
+
isEmpty() {
|
|
1519
|
+
if (this.intro.length && this.intro.trim()) return false;
|
|
1520
|
+
let chunk = this.firstChunk;
|
|
1521
|
+
while (chunk) {
|
|
1522
|
+
if (chunk.intro.length && chunk.intro.trim() || chunk.content.length && chunk.content.trim() || chunk.outro.length && chunk.outro.trim()) return false;
|
|
1523
|
+
chunk = chunk.next;
|
|
1524
|
+
}
|
|
1525
|
+
if (this.outro.length && this.outro.trim()) return false;
|
|
1526
|
+
return true;
|
|
1527
|
+
}
|
|
1528
|
+
length() {
|
|
1529
|
+
let chunk = this.firstChunk;
|
|
1530
|
+
let length = 0;
|
|
1531
|
+
while (chunk) {
|
|
1532
|
+
length += chunk.intro.length + chunk.content.length + chunk.outro.length;
|
|
1533
|
+
chunk = chunk.next;
|
|
1534
|
+
}
|
|
1535
|
+
return length;
|
|
1536
|
+
}
|
|
1537
|
+
/**
|
|
1538
|
+
* Removes empty lines from the start and end.
|
|
1539
|
+
*/
|
|
1540
|
+
trimLines() {
|
|
1541
|
+
return this.trim("[\\r\\n]");
|
|
1542
|
+
}
|
|
1543
|
+
/**
|
|
1544
|
+
* Trims content matching `charType` (defaults to `\s`, i.e. whitespace) from the start and end.
|
|
1545
|
+
*/
|
|
1546
|
+
trim(charType) {
|
|
1547
|
+
return this.trimStart(charType).trimEnd(charType);
|
|
1548
|
+
}
|
|
1549
|
+
/** @internal */
|
|
1550
|
+
trimEndAborted(charType) {
|
|
1551
|
+
const rx = new RegExp(`${charType || "\\s"}+$`);
|
|
1552
|
+
this.outro = this.outro.replace(rx, "");
|
|
1553
|
+
if (this.outro.length) return true;
|
|
1554
|
+
let chunk = this.lastChunk;
|
|
1555
|
+
do {
|
|
1556
|
+
const end = chunk.end;
|
|
1557
|
+
const aborted = chunk.trimEnd(rx);
|
|
1558
|
+
if (chunk.end !== end) {
|
|
1559
|
+
if (this.lastChunk === chunk) this.lastChunk = chunk.next;
|
|
1560
|
+
this.byEnd.set(chunk.end, chunk);
|
|
1561
|
+
this.byStart.set(chunk.next.start, chunk.next);
|
|
1562
|
+
this.byEnd.set(chunk.next.end, chunk.next);
|
|
1563
|
+
}
|
|
1564
|
+
if (aborted) return true;
|
|
1565
|
+
chunk = chunk.previous;
|
|
1566
|
+
} while (chunk);
|
|
1567
|
+
this.intro = this.intro.replace(rx, "");
|
|
1568
|
+
return this.intro.length > 0;
|
|
1569
|
+
}
|
|
1570
|
+
/**
|
|
1571
|
+
* Trims content matching `charType` (defaults to `\s`, i.e. whitespace) from the end.
|
|
1572
|
+
*/
|
|
1573
|
+
trimEnd(charType) {
|
|
1574
|
+
this.trimEndAborted(charType);
|
|
1575
|
+
return this;
|
|
1576
|
+
}
|
|
1577
|
+
/** @internal */
|
|
1578
|
+
trimStartAborted(charType) {
|
|
1579
|
+
const rx = new RegExp(`^${charType || "\\s"}+`);
|
|
1580
|
+
this.intro = this.intro.replace(rx, "");
|
|
1581
|
+
if (this.intro.length) return true;
|
|
1582
|
+
let chunk = this.firstChunk;
|
|
1583
|
+
do {
|
|
1584
|
+
const end = chunk.end;
|
|
1585
|
+
const aborted = chunk.trimStart(rx);
|
|
1586
|
+
if (chunk.end !== end) {
|
|
1587
|
+
if (chunk === this.lastChunk) this.lastChunk = chunk.next;
|
|
1588
|
+
this.byEnd.set(chunk.end, chunk);
|
|
1589
|
+
this.byStart.set(chunk.next.start, chunk.next);
|
|
1590
|
+
this.byEnd.set(chunk.next.end, chunk.next);
|
|
1591
|
+
}
|
|
1592
|
+
if (aborted) return true;
|
|
1593
|
+
chunk = chunk.next;
|
|
1594
|
+
} while (chunk);
|
|
1595
|
+
this.outro = this.outro.replace(rx, "");
|
|
1596
|
+
return this.outro.length > 0;
|
|
1597
|
+
}
|
|
1598
|
+
/**
|
|
1599
|
+
* Trims content matching `charType` (defaults to `\s`, i.e. whitespace) from the start.
|
|
1600
|
+
*/
|
|
1601
|
+
trimStart(charType) {
|
|
1602
|
+
this.trimStartAborted(charType);
|
|
1603
|
+
return this;
|
|
1604
|
+
}
|
|
1605
|
+
/**
|
|
1606
|
+
* Indicates if the string has been changed.
|
|
1607
|
+
*/
|
|
1608
|
+
hasChanged() {
|
|
1609
|
+
if (this.intro || this.outro) return true;
|
|
1610
|
+
let outputIndex = 0;
|
|
1611
|
+
let chunk = this.firstChunk;
|
|
1612
|
+
while (chunk) {
|
|
1613
|
+
if (chunk.intro || chunk.outro) return true;
|
|
1614
|
+
if (!chunk.edited && chunk.start === outputIndex) outputIndex = chunk.end;
|
|
1615
|
+
else {
|
|
1616
|
+
if (!this.original.startsWith(chunk.content, outputIndex)) return true;
|
|
1617
|
+
outputIndex += chunk.content.length;
|
|
1618
|
+
}
|
|
1619
|
+
chunk = chunk.next;
|
|
1620
|
+
}
|
|
1621
|
+
return outputIndex !== this.original.length;
|
|
1622
|
+
}
|
|
1623
|
+
/**
|
|
1624
|
+
* Whether the original range [start, end) has had any of its characters
|
|
1625
|
+
* removed. `replace`/`replaceAll` search `original`, so a match can land on
|
|
1626
|
+
* text that is no longer in the output - overwriting it would resurrect the
|
|
1627
|
+
* removed characters, so such matches are skipped instead.
|
|
1628
|
+
*
|
|
1629
|
+
* @internal
|
|
1630
|
+
*/
|
|
1631
|
+
_hasRemovedContent(start, end) {
|
|
1632
|
+
let chunk = this.byStart.get(start);
|
|
1633
|
+
if (!chunk) {
|
|
1634
|
+
chunk = this.lastSearchedChunk;
|
|
1635
|
+
const searchForward = start >= chunk.end;
|
|
1636
|
+
while (chunk && !chunk.contains(start)) chunk = searchForward ? this.byStart.get(chunk.end) : this.byEnd.get(chunk.start);
|
|
1637
|
+
if (chunk) this.lastSearchedChunk = chunk;
|
|
1638
|
+
}
|
|
1639
|
+
while (chunk && chunk.start < end) {
|
|
1640
|
+
if (chunk.content === "") return true;
|
|
1641
|
+
chunk = this.byStart.get(chunk.end);
|
|
1642
|
+
}
|
|
1643
|
+
return false;
|
|
1644
|
+
}
|
|
1645
|
+
/** @internal */
|
|
1646
|
+
_replaceRegexp(searchValue, replacement) {
|
|
1647
|
+
function getReplacement(match, str) {
|
|
1648
|
+
if (typeof replacement === "string") return expandReplacement(replacement, match[0], match.index, str, match.slice(1), match.groups);
|
|
1649
|
+
else return match.groups === void 0 ? replacement(match[0], ...match.slice(1), match.index, str) : replacement(match[0], ...match.slice(1), match.index, str, match.groups);
|
|
1650
|
+
}
|
|
1651
|
+
const replaceMatch = (match) => {
|
|
1652
|
+
/* v8 ignore next 2 -- `match.index` is always defined for matches from `matchAll` */
|
|
1653
|
+
if (match.index == null) return false;
|
|
1654
|
+
if (this._hasRemovedContent(match.index, match.index + match[0].length)) return false;
|
|
1655
|
+
const replacement = getReplacement(match, this.original);
|
|
1656
|
+
if (replacement === match[0]) return true;
|
|
1657
|
+
if (match[0].length === 0) this.appendRight(match.index - this.offset, replacement);
|
|
1658
|
+
else this.overwrite(match.index - this.offset, match.index + match[0].length - this.offset, replacement);
|
|
1659
|
+
return true;
|
|
1660
|
+
};
|
|
1661
|
+
if (searchValue.global) {
|
|
1662
|
+
searchValue.lastIndex = 0;
|
|
1663
|
+
for (const match of this.original.matchAll(searchValue)) replaceMatch(match);
|
|
1664
|
+
} else {
|
|
1665
|
+
const match = this.original.match(searchValue);
|
|
1666
|
+
if (match && !replaceMatch(match)) {
|
|
1667
|
+
const global = new RegExp(searchValue.source, `${searchValue.flags}g`);
|
|
1668
|
+
for (const next of this.original.matchAll(global)) if (replaceMatch(next)) break;
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
return this;
|
|
1672
|
+
}
|
|
1673
|
+
/** @internal */
|
|
1674
|
+
_replaceString(string, replacement) {
|
|
1675
|
+
const { original } = this;
|
|
1676
|
+
let index = original.indexOf(string);
|
|
1677
|
+
while (index !== -1) {
|
|
1678
|
+
if (this._hasRemovedContent(index, index + string.length)) {
|
|
1679
|
+
index = original.indexOf(string, index + string.length);
|
|
1680
|
+
continue;
|
|
1681
|
+
}
|
|
1682
|
+
if (typeof replacement === "function") replacement = replacement(string, index, original);
|
|
1683
|
+
else replacement = expandReplacement(replacement, string, index, original, [], void 0);
|
|
1684
|
+
if (string !== replacement) {
|
|
1685
|
+
if (string.length === 0) this.appendRight(index - this.offset, replacement);
|
|
1686
|
+
else this.overwrite(index - this.offset, index + string.length - this.offset, replacement);
|
|
1687
|
+
}
|
|
1688
|
+
break;
|
|
1689
|
+
}
|
|
1690
|
+
return this;
|
|
1691
|
+
}
|
|
1692
|
+
/**
|
|
1693
|
+
* String replacement with RegExp or string.
|
|
1694
|
+
*/
|
|
1695
|
+
replace(searchValue, replacement) {
|
|
1696
|
+
if (typeof searchValue === "string") return this._replaceString(searchValue, replacement);
|
|
1697
|
+
return this._replaceRegexp(searchValue, replacement);
|
|
1698
|
+
}
|
|
1699
|
+
/** @internal */
|
|
1700
|
+
_replaceAllString(string, replacement) {
|
|
1701
|
+
const { original } = this;
|
|
1702
|
+
const stringLength = string.length;
|
|
1703
|
+
if (stringLength === 0) {
|
|
1704
|
+
for (let index = 0; index <= original.length; index += 1) {
|
|
1705
|
+
if (this._hasRemovedContent(index, index)) continue;
|
|
1706
|
+
const _replacement = typeof replacement === "function" ? replacement("", index, original) : expandReplacement(replacement, "", index, original, [], void 0);
|
|
1707
|
+
if (_replacement !== "") this.appendRight(index - this.offset, _replacement);
|
|
1708
|
+
}
|
|
1709
|
+
return this;
|
|
1710
|
+
}
|
|
1711
|
+
for (let index = original.indexOf(string); index !== -1; index = original.indexOf(string, index + stringLength)) {
|
|
1712
|
+
if (this._hasRemovedContent(index, index + stringLength)) continue;
|
|
1713
|
+
const previous = original.slice(index, index + stringLength);
|
|
1714
|
+
const _replacement = typeof replacement === "function" ? replacement(previous, index, original) : expandReplacement(replacement, previous, index, original, [], void 0);
|
|
1715
|
+
if (previous !== _replacement) this.overwrite(index - this.offset, index + stringLength - this.offset, _replacement);
|
|
1716
|
+
}
|
|
1717
|
+
return this;
|
|
1718
|
+
}
|
|
1719
|
+
/**
|
|
1720
|
+
* Same as `s.replace`, but replace all matched strings instead of just one.
|
|
1721
|
+
*/
|
|
1722
|
+
replaceAll(searchValue, replacement) {
|
|
1723
|
+
if (typeof searchValue === "string") return this._replaceAllString(searchValue, replacement);
|
|
1724
|
+
if (!searchValue.global) throw new MagicStringError("replaceAll() requires a global RegExp");
|
|
1725
|
+
return this._replaceRegexp(searchValue, replacement);
|
|
1726
|
+
}
|
|
1727
|
+
};
|
|
1728
|
+
//#endregion
|
|
1729
|
+
//#region ../../node_modules/.pnpm/@iconify+utils@3.1.7/node_modules/@iconify/utils/lib/svg/url.js
|
|
1730
|
+
/**
|
|
1731
|
+
* Encode SVG for use in url()
|
|
1732
|
+
*
|
|
1733
|
+
* Short alternative to encodeURIComponent() that encodes only stuff used in SVG, generating
|
|
1734
|
+
* smaller code.
|
|
1735
|
+
*/
|
|
1736
|
+
function encodeSVGforURL(svg) {
|
|
1737
|
+
return svg.replace(/"/g, "'").replace(/%/g, "%25").replace(/#/g, "%23").replace(/</g, "%3C").replace(/>/g, "%3E").replace(/\s+/g, " ");
|
|
1738
|
+
}
|
|
1739
|
+
//#endregion
|
|
1740
|
+
//#region ../../node_modules/.pnpm/@iconify+utils@3.1.7/node_modules/@iconify/utils/lib/svg/encode-svg-for-css.js
|
|
1741
|
+
/**
|
|
1742
|
+
* Encode the `SVG` to be used on `CSS`: https://bl.ocks.org/jennyknuth/222825e315d45a738ed9d6e04c7a88d0.
|
|
1743
|
+
*
|
|
1744
|
+
* @param svg The `SVG` source.
|
|
1745
|
+
*/
|
|
1746
|
+
function encodeSvgForCss(svg) {
|
|
1747
|
+
let useSvg = svg.startsWith("<svg>") ? svg.replace("<svg>", "<svg >") : svg;
|
|
1748
|
+
if (!useSvg.includes(" xmlns:xlink=") && useSvg.includes(" xlink:")) useSvg = useSvg.replace("<svg ", "<svg xmlns:xlink=\"http://www.w3.org/1999/xlink\" ");
|
|
1749
|
+
if (!useSvg.includes(" xmlns=")) useSvg = useSvg.replace("<svg ", "<svg xmlns=\"http://www.w3.org/2000/svg\" ");
|
|
1750
|
+
return encodeSVGforURL(useSvg);
|
|
1751
|
+
}
|
|
1752
|
+
/** `lucide:check` → `mi-lucide--check`: `--` never occurs in a name, so the class reads back (D4). */
|
|
1753
|
+
function iconClass(name) {
|
|
1754
|
+
return `mi-${name.replace(":", "--")}`;
|
|
1755
|
+
}
|
|
1756
|
+
/** `mi-lucide--check` (among other classes) → `lucide:check`, the inverse of `iconClass`. */
|
|
1757
|
+
function nameOfClass(classes) {
|
|
1758
|
+
for (const c of classes.split(/\s+/)) {
|
|
1759
|
+
const match = /^mi-([a-z0-9-]+?)--([a-z0-9-]+)$/.exec(c);
|
|
1760
|
+
if (match) return `${match[1]}:${match[2]}`;
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
const FILL = "50%/100% 100% no-repeat";
|
|
1764
|
+
function baseCss(layer) {
|
|
1765
|
+
return `@layer ${layer}{.mi{display:inline-block;width:1em;height:1em;flex-shrink:0;vertical-align:-.125em;background:currentColor;mask:var(--mi) ${FILL};forced-color-adjust:none}}\n`;
|
|
1766
|
+
}
|
|
1767
|
+
/** One icon's rule. A wider or taller icon keeps its ratio at `1em` high. */
|
|
1768
|
+
function iconCss(name, icon, layer) {
|
|
1769
|
+
const body = icon.mono ? icon.body.replaceAll("currentColor", "black") : icon.body;
|
|
1770
|
+
let rule = `--mi:url("data:image/svg+xml,${encodeSvgForCss(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${icon.width} ${icon.height}">${body}</svg>`)}");`;
|
|
1771
|
+
if (!icon.mono) rule += `background:var(--mi) ${FILL};mask:none;`;
|
|
1772
|
+
if (icon.width !== icon.height) rule += `width:${round(icon.width / icon.height)}em;`;
|
|
1773
|
+
return `@layer ${layer}{${rule.indexOf(";") === rule.length - 1 ? `.${iconClass(name)}` : `.mi.${iconClass(name)}`}{${rule.slice(0, -1)}}}\n`;
|
|
1774
|
+
}
|
|
1775
|
+
function round(n) {
|
|
1776
|
+
return Math.round(n * 1e3) / 1e3;
|
|
1777
|
+
}
|
|
1778
|
+
//#endregion
|
|
1779
|
+
//#region src/vite/compile.ts
|
|
1780
|
+
/**
|
|
1781
|
+
* The transform (D2, D3, D6): every `icon("set:name", { label, class })`
|
|
1782
|
+
* of a module becomes a literal attribute object, and the module imports
|
|
1783
|
+
* the base CSS and one CSS module per icon it names. It runs on JavaScript,
|
|
1784
|
+
* after the framework compilers, so `.vue`, `.svelte`, `.astro` and JSX
|
|
1785
|
+
* are one case; Vue's templates call the import through `$setup` or `_unref`.
|
|
1786
|
+
*/
|
|
1787
|
+
const PACKAGE$1 = "@menapo/icons";
|
|
1788
|
+
/** The virtual CSS modules, `virtual:menapo-icons/base.css` and `…/lucide--check.css`. */
|
|
1789
|
+
const VIRTUAL = "virtual:menapo-icons/";
|
|
1790
|
+
/** A message for a call the plugin cannot compile, at `start` in the module. */
|
|
1791
|
+
var CompileError = class extends Error {
|
|
1792
|
+
name = "CompileError";
|
|
1793
|
+
start;
|
|
1794
|
+
constructor(message, start) {
|
|
1795
|
+
super(message);
|
|
1796
|
+
this.start = start;
|
|
1797
|
+
}
|
|
1798
|
+
};
|
|
1799
|
+
const RUNTIME_NAME = `icon() takes the name as a string literal: icon("lucide:check"). For a name chosen at runtime, pick from compiled calls: { pdf: icon("lucide:file-text"), other: icon("lucide:file") }`;
|
|
1800
|
+
/** `null` when the module calls no `icon()`. */
|
|
1801
|
+
function compile(code, id, options) {
|
|
1802
|
+
const { program, errors } = parseSync(`${id.replace(/[?#].*$/, "")}.js`, code);
|
|
1803
|
+
if (errors.length) return null;
|
|
1804
|
+
const locals = /* @__PURE__ */ new Set();
|
|
1805
|
+
for (const node of program.body) {
|
|
1806
|
+
if (node.type !== "ImportDeclaration" || node.source.value !== "@menapo/icons") continue;
|
|
1807
|
+
for (const specifier of node.specifiers) if (specifier.type === "ImportSpecifier" && specifier.imported.type === "Identifier" && specifier.imported.name === "icon") locals.add(specifier.local.name);
|
|
1808
|
+
}
|
|
1809
|
+
if (!locals.size) return null;
|
|
1810
|
+
const s = new MagicString(code);
|
|
1811
|
+
const icons = [];
|
|
1812
|
+
walk(program, (node) => {
|
|
1813
|
+
if (node.type !== "CallExpression" || !isIconCallee(node.callee, locals)) return;
|
|
1814
|
+
const [nameArg, optionsArg, extra] = node.arguments;
|
|
1815
|
+
const name = literal(nameArg);
|
|
1816
|
+
if (name === void 0 || extra) throw new CompileError(RUNTIME_NAME, node.start);
|
|
1817
|
+
options.check(name, nameArg.start);
|
|
1818
|
+
if (!icons.includes(name)) icons.push(name);
|
|
1819
|
+
const { label, extraClass } = readOptions(code, optionsArg);
|
|
1820
|
+
const classes = JSON.stringify(`mi ${iconClass(name)}`);
|
|
1821
|
+
const classValue = extraClass ? `${classes.slice(0, -1)} "+((${extraClass})??"")` : classes;
|
|
1822
|
+
const rest = label ? `role:"img","aria-label":(${label})` : `"aria-hidden":"true"`;
|
|
1823
|
+
s.overwrite(node.start, node.end, `({${options.classAttribute}:${classValue},${rest}})`);
|
|
1824
|
+
});
|
|
1825
|
+
if (!icons.length) return null;
|
|
1826
|
+
const imports = [`${VIRTUAL}base.css`, ...icons.map((n) => `${VIRTUAL}${n.replace(":", "--")}.css`)].map((source) => `import ${JSON.stringify(source)};`).join("");
|
|
1827
|
+
let at = 0;
|
|
1828
|
+
for (const node of program.body) if (node.type === "ExpressionStatement" && "directive" in node && node.directive) at = node.end;
|
|
1829
|
+
else break;
|
|
1830
|
+
if (at) s.appendLeft(at, `\n${imports}`);
|
|
1831
|
+
else s.prepend(`${imports}\n`);
|
|
1832
|
+
return {
|
|
1833
|
+
code: s.toString(),
|
|
1834
|
+
map: s.generateMap({
|
|
1835
|
+
hires: "boundary",
|
|
1836
|
+
source: id
|
|
1837
|
+
}),
|
|
1838
|
+
icons
|
|
1839
|
+
};
|
|
1840
|
+
}
|
|
1841
|
+
/** `icon(…)`, or Vue's `$setup.icon(…)` / `$setup["icon"](…)` (dev) and `_unref(icon)(…)` (build) in a template. */
|
|
1842
|
+
function isIconCallee(callee, locals) {
|
|
1843
|
+
if (callee.type === "Identifier") return locals.has(callee.name);
|
|
1844
|
+
if (callee.type === "CallExpression") {
|
|
1845
|
+
const [arg] = callee.arguments;
|
|
1846
|
+
return callee.callee.type === "Identifier" && callee.callee.name === "_unref" && callee.arguments.length === 1 && arg?.type === "Identifier" && locals.has(arg.name);
|
|
1847
|
+
}
|
|
1848
|
+
if (callee.type !== "MemberExpression" || callee.object.type !== "Identifier") return false;
|
|
1849
|
+
if (callee.object.name !== "$setup") return false;
|
|
1850
|
+
const property = callee.property;
|
|
1851
|
+
const key = callee.computed ? literal(property) : property.type === "Identifier" ? property.name : void 0;
|
|
1852
|
+
return key !== void 0 && locals.has(key);
|
|
1853
|
+
}
|
|
1854
|
+
/** A string literal's value, or an expression-free template literal's. */
|
|
1855
|
+
function literal(node) {
|
|
1856
|
+
if (!node) return void 0;
|
|
1857
|
+
if (node.type === "Literal" && typeof node.value === "string") return node.value;
|
|
1858
|
+
if (node.type === "TemplateLiteral" && node.expressions.length === 0) return node.quasis[0]?.value.cooked ?? void 0;
|
|
1859
|
+
}
|
|
1860
|
+
/** The source text of `label` and `class`, kept as written. */
|
|
1861
|
+
function readOptions(code, node) {
|
|
1862
|
+
if (!node) return {};
|
|
1863
|
+
if (node.type !== "ObjectExpression") throw new CompileError("icon()'s options are an object literal: icon(\"lucide:x\", { label: t(\"Close\") })", node.start);
|
|
1864
|
+
const out = {};
|
|
1865
|
+
for (const property of node.properties) {
|
|
1866
|
+
const key = property.type === "Property" && !property.computed ? property.key.type === "Identifier" ? property.key.name : literal(property.key) : void 0;
|
|
1867
|
+
if (property.type !== "Property" || key !== "label" && key !== "class") throw new CompileError("icon() takes two options, label and class: icon(name, { label, class })", property.start);
|
|
1868
|
+
const text = code.slice(property.value.start, property.value.end);
|
|
1869
|
+
if (key === "label") out.label = text;
|
|
1870
|
+
else out.extraClass = text;
|
|
1871
|
+
}
|
|
1872
|
+
return out;
|
|
1873
|
+
}
|
|
1874
|
+
/** Visits every node, parents first. */
|
|
1875
|
+
function walk(node, visit) {
|
|
1876
|
+
if (Array.isArray(node)) {
|
|
1877
|
+
for (const child of node) walk(child, visit);
|
|
1878
|
+
return;
|
|
1879
|
+
}
|
|
1880
|
+
if (node === null || typeof node !== "object") return;
|
|
1881
|
+
const n = node;
|
|
1882
|
+
if (typeof n.type === "string") visit(n);
|
|
1883
|
+
for (const key in n) {
|
|
1884
|
+
if (key === "type" || key === "start" || key === "end" || key === "range" || key === "loc") continue;
|
|
1885
|
+
const value = n[key];
|
|
1886
|
+
if (value !== null && typeof value === "object") walk(value, visit);
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
//#endregion
|
|
1890
|
+
//#region ../../node_modules/.pnpm/@iconify+utils@3.1.7/node_modules/@iconify/utils/lib/icon/defaults.js
|
|
1891
|
+
/** Default values for dimensions */
|
|
1892
|
+
const defaultIconDimensions = Object.freeze({
|
|
1893
|
+
left: 0,
|
|
1894
|
+
top: 0,
|
|
1895
|
+
width: 16,
|
|
1896
|
+
height: 16
|
|
1897
|
+
});
|
|
1898
|
+
/** Default values for transformations */
|
|
1899
|
+
const defaultIconTransformations = Object.freeze({
|
|
1900
|
+
rotate: 0,
|
|
1901
|
+
vFlip: false,
|
|
1902
|
+
hFlip: false
|
|
1903
|
+
});
|
|
1904
|
+
/** Default values for all optional IconifyIcon properties */
|
|
1905
|
+
const defaultIconProps = Object.freeze({
|
|
1906
|
+
...defaultIconDimensions,
|
|
1907
|
+
...defaultIconTransformations
|
|
1908
|
+
});
|
|
1909
|
+
/** Default values for all properties used in ExtendedIconifyIcon */
|
|
1910
|
+
const defaultExtendedIconProps = Object.freeze({
|
|
1911
|
+
...defaultIconProps,
|
|
1912
|
+
body: "",
|
|
1913
|
+
hidden: false
|
|
1914
|
+
});
|
|
1915
|
+
//#endregion
|
|
1916
|
+
//#region ../../node_modules/.pnpm/@iconify+utils@3.1.7/node_modules/@iconify/utils/lib/icon/transformations.js
|
|
1917
|
+
/**
|
|
1918
|
+
* Merge transformations
|
|
1919
|
+
*/
|
|
1920
|
+
function mergeIconTransformations(obj1, obj2) {
|
|
1921
|
+
const result = {};
|
|
1922
|
+
if (!obj1.hFlip !== !obj2.hFlip) result.hFlip = true;
|
|
1923
|
+
if (!obj1.vFlip !== !obj2.vFlip) result.vFlip = true;
|
|
1924
|
+
const rotate = ((obj1.rotate || 0) + (obj2.rotate || 0)) % 4;
|
|
1925
|
+
if (rotate) result.rotate = rotate;
|
|
1926
|
+
return result;
|
|
1927
|
+
}
|
|
1928
|
+
//#endregion
|
|
1929
|
+
//#region ../../node_modules/.pnpm/@iconify+utils@3.1.7/node_modules/@iconify/utils/lib/icon/merge.js
|
|
1930
|
+
/**
|
|
1931
|
+
* Merge icon and alias
|
|
1932
|
+
*
|
|
1933
|
+
* Can also be used to merge default values and icon
|
|
1934
|
+
*/
|
|
1935
|
+
function mergeIconData(parent, child) {
|
|
1936
|
+
const result = mergeIconTransformations(parent, child);
|
|
1937
|
+
for (const key in defaultExtendedIconProps) if (key in defaultIconTransformations) {
|
|
1938
|
+
if (key in parent && !(key in result)) result[key] = defaultIconTransformations[key];
|
|
1939
|
+
} else if (key in child) result[key] = child[key];
|
|
1940
|
+
else if (key in parent) result[key] = parent[key];
|
|
1941
|
+
return result;
|
|
1942
|
+
}
|
|
1943
|
+
//#endregion
|
|
1944
|
+
//#region ../../node_modules/.pnpm/@iconify+utils@3.1.7/node_modules/@iconify/utils/lib/icon-set/tree.js
|
|
1945
|
+
/**
|
|
1946
|
+
* Resolve icon set icons
|
|
1947
|
+
*
|
|
1948
|
+
* Returns parent icon for each icon
|
|
1949
|
+
*/
|
|
1950
|
+
function getIconsTree(data, names) {
|
|
1951
|
+
const icons = data.icons;
|
|
1952
|
+
const aliases = data.aliases || Object.create(null);
|
|
1953
|
+
const resolved = Object.create(null);
|
|
1954
|
+
function resolve(name) {
|
|
1955
|
+
if (icons[name]) return resolved[name] = [];
|
|
1956
|
+
if (!(name in resolved)) {
|
|
1957
|
+
resolved[name] = null;
|
|
1958
|
+
const parent = aliases[name] && aliases[name].parent;
|
|
1959
|
+
const value = parent && resolve(parent);
|
|
1960
|
+
if (value) resolved[name] = [parent].concat(value);
|
|
1961
|
+
}
|
|
1962
|
+
return resolved[name];
|
|
1963
|
+
}
|
|
1964
|
+
(names || Object.keys(icons).concat(Object.keys(aliases))).forEach(resolve);
|
|
1965
|
+
return resolved;
|
|
1966
|
+
}
|
|
1967
|
+
//#endregion
|
|
1968
|
+
//#region ../../node_modules/.pnpm/@iconify+utils@3.1.7/node_modules/@iconify/utils/lib/icon-set/get-icon.js
|
|
1969
|
+
/**
|
|
1970
|
+
* Get icon data, using prepared aliases tree
|
|
1971
|
+
*/
|
|
1972
|
+
function internalGetIconData(data, name, tree) {
|
|
1973
|
+
const icons = data.icons;
|
|
1974
|
+
const aliases = data.aliases || Object.create(null);
|
|
1975
|
+
let currentProps = {};
|
|
1976
|
+
function parse(name) {
|
|
1977
|
+
currentProps = mergeIconData(icons[name] || aliases[name], currentProps);
|
|
1978
|
+
}
|
|
1979
|
+
parse(name);
|
|
1980
|
+
tree.forEach(parse);
|
|
1981
|
+
return mergeIconData(data, currentProps);
|
|
1982
|
+
}
|
|
1983
|
+
/**
|
|
1984
|
+
* Get data for icon
|
|
1985
|
+
*/
|
|
1986
|
+
function getIconData(data, name) {
|
|
1987
|
+
if (data.icons[name]) return internalGetIconData(data, name, []);
|
|
1988
|
+
const tree = getIconsTree(data, [name])[name];
|
|
1989
|
+
return tree ? internalGetIconData(data, name, tree) : null;
|
|
1990
|
+
}
|
|
1991
|
+
//#endregion
|
|
1992
|
+
//#region ../../node_modules/.pnpm/@iconify+utils@3.1.7/node_modules/@iconify/utils/lib/customisations/defaults.js
|
|
1993
|
+
/**
|
|
1994
|
+
* Default icon customisations values
|
|
1995
|
+
*/
|
|
1996
|
+
const defaultIconSizeCustomisations = Object.freeze({
|
|
1997
|
+
width: null,
|
|
1998
|
+
height: null
|
|
1999
|
+
});
|
|
2000
|
+
const defaultIconCustomisations = Object.freeze({
|
|
2001
|
+
...defaultIconSizeCustomisations,
|
|
2002
|
+
...defaultIconTransformations
|
|
2003
|
+
});
|
|
2004
|
+
//#endregion
|
|
2005
|
+
//#region ../../node_modules/.pnpm/@iconify+utils@3.1.7/node_modules/@iconify/utils/lib/svg/size.js
|
|
2006
|
+
/**
|
|
2007
|
+
* Regular expressions for calculating dimensions
|
|
2008
|
+
*/
|
|
2009
|
+
const unitsSplit = /(-?[0-9.]*[0-9]+[0-9.]*)/g;
|
|
2010
|
+
const unitsTest = /^-?[0-9.]*[0-9]+[0-9.]*$/g;
|
|
2011
|
+
function calculateSize(size, ratio, precision) {
|
|
2012
|
+
if (ratio === 1) return size;
|
|
2013
|
+
precision = precision || 100;
|
|
2014
|
+
if (typeof size === "number") return Math.ceil(size * ratio * precision) / precision;
|
|
2015
|
+
if (typeof size !== "string") return size;
|
|
2016
|
+
const oldParts = size.split(unitsSplit);
|
|
2017
|
+
if (oldParts === null || !oldParts.length) return size;
|
|
2018
|
+
const newParts = [];
|
|
2019
|
+
let code = oldParts.shift();
|
|
2020
|
+
let isNumber = unitsTest.test(code);
|
|
2021
|
+
while (true) {
|
|
2022
|
+
if (isNumber) {
|
|
2023
|
+
const num = parseFloat(code);
|
|
2024
|
+
if (isNaN(num)) newParts.push(code);
|
|
2025
|
+
else newParts.push(Math.ceil(num * ratio * precision) / precision);
|
|
2026
|
+
} else newParts.push(code);
|
|
2027
|
+
code = oldParts.shift();
|
|
2028
|
+
if (code === void 0) return newParts.join("");
|
|
2029
|
+
isNumber = !isNumber;
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
//#endregion
|
|
2033
|
+
//#region ../../node_modules/.pnpm/@iconify+utils@3.1.7/node_modules/@iconify/utils/lib/svg/defs.js
|
|
2034
|
+
function splitSVGDefs(content, tag = "defs") {
|
|
2035
|
+
let defs = "";
|
|
2036
|
+
const index = content.indexOf("<" + tag);
|
|
2037
|
+
while (index >= 0) {
|
|
2038
|
+
const start = content.indexOf(">", index);
|
|
2039
|
+
const end = content.indexOf("</" + tag);
|
|
2040
|
+
if (start === -1 || end === -1) break;
|
|
2041
|
+
const endEnd = content.indexOf(">", end);
|
|
2042
|
+
if (endEnd === -1) break;
|
|
2043
|
+
defs += content.slice(start + 1, end).trim();
|
|
2044
|
+
content = content.slice(0, index).trim() + content.slice(endEnd + 1);
|
|
2045
|
+
}
|
|
2046
|
+
return {
|
|
2047
|
+
defs,
|
|
2048
|
+
content
|
|
2049
|
+
};
|
|
2050
|
+
}
|
|
2051
|
+
/**
|
|
2052
|
+
* Merge defs and content
|
|
2053
|
+
*/
|
|
2054
|
+
function mergeDefsAndContent(defs, content) {
|
|
2055
|
+
return defs ? "<defs>" + defs + "</defs>" + content : content;
|
|
2056
|
+
}
|
|
2057
|
+
/**
|
|
2058
|
+
* Wrap SVG content, without wrapping definitions
|
|
2059
|
+
*/
|
|
2060
|
+
function wrapSVGContent(body, start, end) {
|
|
2061
|
+
const split = splitSVGDefs(body);
|
|
2062
|
+
return mergeDefsAndContent(split.defs, start + split.content + end);
|
|
2063
|
+
}
|
|
2064
|
+
//#endregion
|
|
2065
|
+
//#region ../../node_modules/.pnpm/@iconify+utils@3.1.7/node_modules/@iconify/utils/lib/svg/build.js
|
|
2066
|
+
/**
|
|
2067
|
+
* Check if value should be unset. Allows multiple keywords
|
|
2068
|
+
*/
|
|
2069
|
+
const isUnsetKeyword = (value) => value === "unset" || value === "undefined" || value === "none";
|
|
2070
|
+
/**
|
|
2071
|
+
* Get SVG attributes and content from icon + customisations
|
|
2072
|
+
*
|
|
2073
|
+
* Does not generate style to make it compatible with frameworks that use objects for style, such as React.
|
|
2074
|
+
* Instead, it generates 'inline' value. If true, rendering engine should add verticalAlign: -0.125em to icon.
|
|
2075
|
+
*
|
|
2076
|
+
* Customisations should be normalised by platform specific parser.
|
|
2077
|
+
* Result should be converted to <svg> by platform specific parser.
|
|
2078
|
+
* Use replaceIDs to generate unique IDs for body.
|
|
2079
|
+
*/
|
|
2080
|
+
function iconToSVG(icon, customisations) {
|
|
2081
|
+
const fullIcon = {
|
|
2082
|
+
...defaultIconProps,
|
|
2083
|
+
...icon
|
|
2084
|
+
};
|
|
2085
|
+
const fullCustomisations = {
|
|
2086
|
+
...defaultIconCustomisations,
|
|
2087
|
+
...customisations
|
|
2088
|
+
};
|
|
2089
|
+
const box = {
|
|
2090
|
+
left: fullIcon.left,
|
|
2091
|
+
top: fullIcon.top,
|
|
2092
|
+
width: fullIcon.width,
|
|
2093
|
+
height: fullIcon.height
|
|
2094
|
+
};
|
|
2095
|
+
let body = fullIcon.body;
|
|
2096
|
+
[fullIcon, fullCustomisations].forEach((props) => {
|
|
2097
|
+
const transformations = [];
|
|
2098
|
+
const hFlip = props.hFlip;
|
|
2099
|
+
const vFlip = props.vFlip;
|
|
2100
|
+
let rotation = props.rotate;
|
|
2101
|
+
if (hFlip) {
|
|
2102
|
+
if (vFlip) rotation += 2;
|
|
2103
|
+
else {
|
|
2104
|
+
transformations.push("translate(" + (box.width + box.left).toString() + " " + (0 - box.top).toString() + ")");
|
|
2105
|
+
transformations.push("scale(-1 1)");
|
|
2106
|
+
box.top = box.left = 0;
|
|
2107
|
+
}
|
|
2108
|
+
} else if (vFlip) {
|
|
2109
|
+
transformations.push("translate(" + (0 - box.left).toString() + " " + (box.height + box.top).toString() + ")");
|
|
2110
|
+
transformations.push("scale(1 -1)");
|
|
2111
|
+
box.top = box.left = 0;
|
|
2112
|
+
}
|
|
2113
|
+
let tempValue;
|
|
2114
|
+
if (rotation < 0) rotation -= Math.floor(rotation / 4) * 4;
|
|
2115
|
+
rotation = rotation % 4;
|
|
2116
|
+
switch (rotation) {
|
|
2117
|
+
case 1:
|
|
2118
|
+
tempValue = box.height / 2 + box.top;
|
|
2119
|
+
transformations.unshift("rotate(90 " + tempValue.toString() + " " + tempValue.toString() + ")");
|
|
2120
|
+
break;
|
|
2121
|
+
case 2:
|
|
2122
|
+
transformations.unshift("rotate(180 " + (box.width / 2 + box.left).toString() + " " + (box.height / 2 + box.top).toString() + ")");
|
|
2123
|
+
break;
|
|
2124
|
+
case 3:
|
|
2125
|
+
tempValue = box.width / 2 + box.left;
|
|
2126
|
+
transformations.unshift("rotate(-90 " + tempValue.toString() + " " + tempValue.toString() + ")");
|
|
2127
|
+
}
|
|
2128
|
+
if (rotation % 2 === 1) {
|
|
2129
|
+
if (box.left !== box.top) {
|
|
2130
|
+
tempValue = box.left;
|
|
2131
|
+
box.left = box.top;
|
|
2132
|
+
box.top = tempValue;
|
|
2133
|
+
}
|
|
2134
|
+
if (box.width !== box.height) {
|
|
2135
|
+
tempValue = box.width;
|
|
2136
|
+
box.width = box.height;
|
|
2137
|
+
box.height = tempValue;
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
2140
|
+
if (transformations.length) body = wrapSVGContent(body, "<g transform=\"" + transformations.join(" ") + "\">", "</g>");
|
|
2141
|
+
});
|
|
2142
|
+
const customisationsWidth = fullCustomisations.width;
|
|
2143
|
+
const customisationsHeight = fullCustomisations.height;
|
|
2144
|
+
const boxWidth = box.width;
|
|
2145
|
+
const boxHeight = box.height;
|
|
2146
|
+
let width;
|
|
2147
|
+
let height;
|
|
2148
|
+
if (customisationsWidth === null) {
|
|
2149
|
+
height = customisationsHeight === null ? "1em" : customisationsHeight === "auto" ? boxHeight : customisationsHeight;
|
|
2150
|
+
width = calculateSize(height, boxWidth / boxHeight);
|
|
2151
|
+
} else {
|
|
2152
|
+
width = customisationsWidth === "auto" ? boxWidth : customisationsWidth;
|
|
2153
|
+
height = customisationsHeight === null ? calculateSize(width, boxHeight / boxWidth) : customisationsHeight === "auto" ? boxHeight : customisationsHeight;
|
|
2154
|
+
}
|
|
2155
|
+
const attributes = {};
|
|
2156
|
+
const setAttr = (prop, value) => {
|
|
2157
|
+
if (!isUnsetKeyword(value)) attributes[prop] = value.toString();
|
|
2158
|
+
};
|
|
2159
|
+
setAttr("width", width);
|
|
2160
|
+
setAttr("height", height);
|
|
2161
|
+
const viewBox = [
|
|
2162
|
+
box.left,
|
|
2163
|
+
box.top,
|
|
2164
|
+
boxWidth,
|
|
2165
|
+
boxHeight
|
|
2166
|
+
];
|
|
2167
|
+
attributes.viewBox = viewBox.join(" ");
|
|
2168
|
+
return {
|
|
2169
|
+
attributes,
|
|
2170
|
+
viewBox,
|
|
2171
|
+
body
|
|
2172
|
+
};
|
|
2173
|
+
}
|
|
2174
|
+
//#endregion
|
|
2175
|
+
//#region src/vite/suggest.ts
|
|
2176
|
+
/**
|
|
2177
|
+
* "Did you mean": the closest names by edit distance, as vicss and vi18n
|
|
2178
|
+
* suggest theirs. A set holds a few thousand names, which this scans in
|
|
2179
|
+
* well under a millisecond.
|
|
2180
|
+
*/
|
|
2181
|
+
/** Up to three names within a third of `name`'s length (at least two edits). */
|
|
2182
|
+
function closest(name, names) {
|
|
2183
|
+
const limit = Math.max(2, Math.floor(name.length / 3));
|
|
2184
|
+
const scored = [];
|
|
2185
|
+
for (const candidate of names) {
|
|
2186
|
+
if (Math.abs(candidate.length - name.length) > limit) continue;
|
|
2187
|
+
const d = distance(name, candidate, limit);
|
|
2188
|
+
if (d <= limit) scored.push([d, candidate]);
|
|
2189
|
+
}
|
|
2190
|
+
scored.sort((a, b) => a[0] - b[0] || a[1].localeCompare(b[1]));
|
|
2191
|
+
return scored.slice(0, 3).map(([, candidate]) => candidate);
|
|
2192
|
+
}
|
|
2193
|
+
/** Levenshtein distance, stopping once every path exceeds `limit`. */
|
|
2194
|
+
function distance(a, b, limit) {
|
|
2195
|
+
let previous = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
2196
|
+
for (let i = 1; i <= a.length; i++) {
|
|
2197
|
+
const row = [i];
|
|
2198
|
+
let best = i;
|
|
2199
|
+
for (let j = 1; j <= b.length; j++) {
|
|
2200
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
2201
|
+
const value = Math.min(previous[j] + 1, row[j - 1] + 1, previous[j - 1] + cost);
|
|
2202
|
+
row.push(value);
|
|
2203
|
+
if (value < best) best = value;
|
|
2204
|
+
}
|
|
2205
|
+
if (best > limit) return limit + 1;
|
|
2206
|
+
previous = row;
|
|
2207
|
+
}
|
|
2208
|
+
return previous[b.length];
|
|
2209
|
+
}
|
|
2210
|
+
//#endregion
|
|
2211
|
+
//#region src/vite/svg.ts
|
|
2212
|
+
/** The whole `<svg>` element: what a data URL holds and what og inlines. */
|
|
2213
|
+
function svgElement(icon) {
|
|
2214
|
+
const { width, height, body } = icon;
|
|
2215
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${width} ${height}" width="${width}" height="${height}">${body}</svg>`;
|
|
2216
|
+
}
|
|
2217
|
+
const BLACK = /^(?:black|#000|#000000|#000f|#000000ff|rgb\(0,\s*0,\s*0\))$/i;
|
|
2218
|
+
const NO_PAINT = /^(?:none|transparent|currentcolor|inherit|unset|initial|context-fill|context-stroke)$/i;
|
|
2219
|
+
const PAINT_ATTRIBUTE = /\b(?:fill|stroke|stop-color|flood-color|lighting-color|color)\s*[=:]\s*["']?([^"';)>\s]+(?:\([^)]*\))?)/gi;
|
|
2220
|
+
/**
|
|
2221
|
+
* Whether `body` paints in one colour: only `currentColor`, black (the
|
|
2222
|
+
* default fill, and what Figma exports hard-code) or no paint. A gradient
|
|
2223
|
+
* or a `url(#…)` paint counts as a colour.
|
|
2224
|
+
*/
|
|
2225
|
+
function isMonochrome(body) {
|
|
2226
|
+
for (const match of body.matchAll(PAINT_ATTRIBUTE)) {
|
|
2227
|
+
const value = match[1];
|
|
2228
|
+
if (!NO_PAINT.test(value) && !BLACK.test(value)) return false;
|
|
2229
|
+
}
|
|
2230
|
+
return !/<(?:linear|radial)Gradient\b|<pattern\b|<image\b/i.test(body);
|
|
2231
|
+
}
|
|
2232
|
+
/** Black paints become `currentColor`, so an inlined monochrome icon follows `color` like the mask does. */
|
|
2233
|
+
function paintCurrentColor(body) {
|
|
2234
|
+
return body.replace(PAINT_ATTRIBUTE, (all, value) => BLACK.test(value) ? all.replace(value, "currentColor") : all);
|
|
2235
|
+
}
|
|
2236
|
+
/**
|
|
2237
|
+
* A local SVG file as an icon. The clean-up removes what a CSS image never
|
|
2238
|
+
* uses and what would be active content inlined (D13); it is not an
|
|
2239
|
+
* optimiser. Throws a message for a file that is not an SVG or has no size.
|
|
2240
|
+
*/
|
|
2241
|
+
function parseSvg(source) {
|
|
2242
|
+
const text = source.replace(/<\?xml[\s\S]*?\?>/gi, "").replace(/<!DOCTYPE[\s\S]*?>/gi, "").replace(/<!--[\s\S]*?-->/g, "").replace(/<(script|metadata|foreignObject|title|desc)\b[\s\S]*?<\/\1\s*>/gi, "").replace(/<(script|metadata|foreignObject)\b[^>]*\/>/gi, "").replace(/\s+on[a-z]+\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/gi, "").replace(/\s+(?:xlink:)?href\s*=\s*(?:"(?!#)[^"]*"|'(?!#)[^']*')/gi, "");
|
|
2243
|
+
const open = /<svg\b([^>]*)>/i.exec(text);
|
|
2244
|
+
const close = text.lastIndexOf("</svg>");
|
|
2245
|
+
if (!open || close < open.index) throw new Error("is not an <svg> element");
|
|
2246
|
+
const attributes = open[1];
|
|
2247
|
+
const body = text.slice(open.index + open[0].length, close).trim();
|
|
2248
|
+
const viewBox = /\bviewBox\s*=\s*["']([^"']+)["']/i.exec(attributes)?.[1];
|
|
2249
|
+
let width;
|
|
2250
|
+
let height;
|
|
2251
|
+
if (viewBox) {
|
|
2252
|
+
const box = viewBox.trim().split(/[\s,]+/).map(Number);
|
|
2253
|
+
if (box.length !== 4 || box.some((n) => !Number.isFinite(n))) throw new Error(`has a viewBox "${viewBox}" that is not four numbers`);
|
|
2254
|
+
const [x, y] = box;
|
|
2255
|
+
width = box[2];
|
|
2256
|
+
height = box[3];
|
|
2257
|
+
return finish(x !== 0 || y !== 0 ? `<g transform="translate(${-x} ${-y})">${body}</g>` : body, width, height);
|
|
2258
|
+
}
|
|
2259
|
+
width = Number(/\bwidth\s*=\s*["']?([\d.]+)/i.exec(attributes)?.[1]);
|
|
2260
|
+
height = Number(/\bheight\s*=\s*["']?([\d.]+)/i.exec(attributes)?.[1]);
|
|
2261
|
+
if (!(width > 0 && height > 0)) throw new Error("has no viewBox and no width and height");
|
|
2262
|
+
return finish(body, width, height);
|
|
2263
|
+
function finish(inner, w, h) {
|
|
2264
|
+
const paint = [...attributes.matchAll(/\s(fill|stroke|stroke-width|stroke-linecap|stroke-linejoin)\s*=\s*("[^"]*"|'[^']*')/gi)].map((m) => ` ${m[1]}=${m[2]}`).join("");
|
|
2265
|
+
const wrapped = paint ? `<g${paint}>${inner}</g>` : inner;
|
|
2266
|
+
const mono = isMonochrome(wrapped);
|
|
2267
|
+
return {
|
|
2268
|
+
body: mono ? paintCurrentColor(wrapped) : wrapped,
|
|
2269
|
+
width: w,
|
|
2270
|
+
height: h,
|
|
2271
|
+
mono
|
|
2272
|
+
};
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
2275
|
+
//#endregion
|
|
2276
|
+
//#region src/vite/resolve.ts
|
|
2277
|
+
/**
|
|
2278
|
+
* Names to SVGs (PLAN §4): the `@iconify-json/*` sets the root
|
|
2279
|
+
* `package.json` lists (D9) and the app's folders. A set is parsed on first
|
|
2280
|
+
* use and kept for the process; a folder file is read on each resolve, so a
|
|
2281
|
+
* changed SVG is picked up when the plugin asks again.
|
|
2282
|
+
*/
|
|
2283
|
+
/** A name that cannot be resolved; the message names the fix. */
|
|
2284
|
+
var IconError = class extends Error {
|
|
2285
|
+
name = "IconError";
|
|
2286
|
+
};
|
|
2287
|
+
/** Iconify's id grammar: lowercase words joined by single dashes. */
|
|
2288
|
+
const PART = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
2289
|
+
const PACKAGE = "@iconify-json/";
|
|
2290
|
+
/** Parsed sets by file, for the process: a dev server restart or a second build reuses them. */
|
|
2291
|
+
const parsed = /* @__PURE__ */ new Map();
|
|
2292
|
+
/** The icons one project can name. */
|
|
2293
|
+
var IconSource = class {
|
|
2294
|
+
root;
|
|
2295
|
+
/** Installed set prefixes, from `package.json`. */
|
|
2296
|
+
sets;
|
|
2297
|
+
/** Folder prefixes to absolute directories. */
|
|
2298
|
+
folders;
|
|
2299
|
+
#require;
|
|
2300
|
+
#built = /* @__PURE__ */ new Map();
|
|
2301
|
+
constructor(options) {
|
|
2302
|
+
this.root = options.root;
|
|
2303
|
+
this.#require = createRequire(join(options.root, "package.json"));
|
|
2304
|
+
this.sets = installedSets(options.root);
|
|
2305
|
+
const folders = /* @__PURE__ */ new Map();
|
|
2306
|
+
for (const [prefix, dir] of Object.entries(options.folders ?? {})) {
|
|
2307
|
+
if (!PART.test(prefix)) throw new IconError(`folders: "${prefix}" is not a set name: use lowercase words joined by dashes, like "brand"`);
|
|
2308
|
+
if (this.sets.includes(prefix)) throw new IconError(`folders: "${prefix}" is also the installed ${PACKAGE}${prefix}: give the folder another name`);
|
|
2309
|
+
const abs = resolve(options.root, dir);
|
|
2310
|
+
if (!existsSync(abs) || !statSync(abs).isDirectory()) throw new IconError(`folders: "${prefix}" points to ${dir}, which is not a directory`);
|
|
2311
|
+
folders.set(prefix, abs);
|
|
2312
|
+
}
|
|
2313
|
+
this.folders = folders;
|
|
2314
|
+
}
|
|
2315
|
+
/** Every name, sorted per set: the `IconName` union (D10). */
|
|
2316
|
+
names(skipped = []) {
|
|
2317
|
+
const out = [];
|
|
2318
|
+
for (const prefix of this.sets) for (const name of this.#set(prefix).names) out.push(`${prefix}:${name}`);
|
|
2319
|
+
for (const [prefix, dir] of this.folders) for (const name of folderNames(dir, skipped)) out.push(`${prefix}:${name}`);
|
|
2320
|
+
return out;
|
|
2321
|
+
}
|
|
2322
|
+
/** `prefix:name` to its SVG; throws an `IconError` naming the closest names. */
|
|
2323
|
+
resolve(id) {
|
|
2324
|
+
const colon = id.indexOf(":");
|
|
2325
|
+
const prefix = id.slice(0, colon);
|
|
2326
|
+
const name = id.slice(colon + 1);
|
|
2327
|
+
if (colon < 1 || !PART.test(prefix) || !PART.test(name)) throw new IconError(`"${id}" is not an icon name: write "set:name" in lowercase, like "lucide:check"`);
|
|
2328
|
+
const dir = this.folders.get(prefix);
|
|
2329
|
+
if (dir) return this.#fromFolder(id, prefix, name, dir);
|
|
2330
|
+
if (this.sets.includes(prefix)) return this.#fromSet(id, prefix, name);
|
|
2331
|
+
const known = [...this.sets, ...this.folders.keys()];
|
|
2332
|
+
throw new IconError(`no icon set "${prefix}" for "${id}". ${known.length ? `Installed: ${this.sets.join(", ") || "none"}; folders: ${[...this.folders.keys()].join(", ") || "none"}.` : "No set is installed and no folder is listed."}${hint(prefix, known)} To add it: vp add -D ${PACKAGE}${prefix}`);
|
|
2333
|
+
}
|
|
2334
|
+
#set(prefix) {
|
|
2335
|
+
const file = this.#require.resolve(`${PACKAGE}${prefix}/icons.json`);
|
|
2336
|
+
let set = parsed.get(file);
|
|
2337
|
+
if (!set) {
|
|
2338
|
+
const json = JSON.parse(readFileSync(file, "utf8"));
|
|
2339
|
+
set = {
|
|
2340
|
+
json,
|
|
2341
|
+
file,
|
|
2342
|
+
names: [...Object.keys(json.icons), ...Object.keys(json.aliases ?? {})].sort(),
|
|
2343
|
+
palette: palette(file.slice(0, -10))
|
|
2344
|
+
};
|
|
2345
|
+
parsed.set(file, set);
|
|
2346
|
+
}
|
|
2347
|
+
return set;
|
|
2348
|
+
}
|
|
2349
|
+
#fromSet(id, prefix, name) {
|
|
2350
|
+
let icon = this.#built.get(id);
|
|
2351
|
+
if (!icon) this.#built.set(id, icon = this.#buildFromSet(id, prefix, name));
|
|
2352
|
+
return icon;
|
|
2353
|
+
}
|
|
2354
|
+
#buildFromSet(id, prefix, name) {
|
|
2355
|
+
const set = this.#set(prefix);
|
|
2356
|
+
const data = getIconData(set.json, name);
|
|
2357
|
+
if (!data) throw new IconError(`icon "${id}" is not in ${PACKAGE}${prefix}.${hint(name, set.names, prefix)}`);
|
|
2358
|
+
const built = iconToSVG(data, { height: "auto" });
|
|
2359
|
+
const [x, y, width, height] = built.viewBox;
|
|
2360
|
+
const body = x || y ? `<g transform="translate(${-x} ${-y})">${built.body}</g>` : built.body;
|
|
2361
|
+
const mono = set.palette === void 0 ? isMonochrome(body) : !set.palette;
|
|
2362
|
+
return {
|
|
2363
|
+
name: id,
|
|
2364
|
+
file: set.file,
|
|
2365
|
+
body: mono ? paintCurrentColor(body) : body,
|
|
2366
|
+
width,
|
|
2367
|
+
height,
|
|
2368
|
+
mono
|
|
2369
|
+
};
|
|
2370
|
+
}
|
|
2371
|
+
#fromFolder(id, prefix, name, dir) {
|
|
2372
|
+
const file = folderFile(dir, name);
|
|
2373
|
+
if (!file) throw new IconError(`icon "${id}" has no file in ${relative(this.root, dir) || "."}.` + hint(name, folderNames(dir), prefix));
|
|
2374
|
+
try {
|
|
2375
|
+
return {
|
|
2376
|
+
name: id,
|
|
2377
|
+
file,
|
|
2378
|
+
...parseSvg(readFileSync(file, "utf8"))
|
|
2379
|
+
};
|
|
2380
|
+
} catch (error) {
|
|
2381
|
+
throw new IconError(`${relative(this.root, file)} ${error.message}`);
|
|
2382
|
+
}
|
|
2383
|
+
}
|
|
2384
|
+
};
|
|
2385
|
+
/** A set's `info.json` palette; the packages ship it beside `icons.json`, which lacks `info`. */
|
|
2386
|
+
function palette(dir) {
|
|
2387
|
+
try {
|
|
2388
|
+
const info = JSON.parse(readFileSync(join(dir, "info.json"), "utf8"));
|
|
2389
|
+
return typeof info.palette === "boolean" ? info.palette : void 0;
|
|
2390
|
+
} catch {
|
|
2391
|
+
return;
|
|
2392
|
+
}
|
|
2393
|
+
}
|
|
2394
|
+
/** ` Did you mean "lucide:check"?`, or nothing. */
|
|
2395
|
+
function hint(name, names, prefix) {
|
|
2396
|
+
const found = closest(name, names);
|
|
2397
|
+
if (!found.length) return "";
|
|
2398
|
+
return ` Did you mean ${found.map((n) => `"${prefix ? `${prefix}:` : ""}${n}"`).join(", ")}?`;
|
|
2399
|
+
}
|
|
2400
|
+
/** The set prefixes of the `@iconify-json/*` packages `root/package.json` depends on. */
|
|
2401
|
+
function installedSets(root) {
|
|
2402
|
+
let pkg;
|
|
2403
|
+
try {
|
|
2404
|
+
pkg = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
|
|
2405
|
+
} catch {
|
|
2406
|
+
return [];
|
|
2407
|
+
}
|
|
2408
|
+
return [...Object.keys(pkg.dependencies ?? {}), ...Object.keys(pkg.devDependencies ?? {})].filter((name) => name.startsWith(PACKAGE)).map((name) => name.slice(14)).sort();
|
|
2409
|
+
}
|
|
2410
|
+
/** A folder's icon names: `arrows/left.svg` is `arrows-left` (D4). */
|
|
2411
|
+
function folderNames(dir, skipped = [], base = "") {
|
|
2412
|
+
const out = [];
|
|
2413
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) if (entry.isDirectory()) out.push(...folderNames(join(dir, entry.name), skipped, `${base}${entry.name}-`));
|
|
2414
|
+
else if (entry.name.endsWith(".svg")) {
|
|
2415
|
+
const name = base + entry.name.slice(0, -4);
|
|
2416
|
+
(PART.test(name) ? out : skipped).push(name);
|
|
2417
|
+
}
|
|
2418
|
+
return out.sort();
|
|
2419
|
+
}
|
|
2420
|
+
/** The file for `name` in `dir`, trying each dash as a folder separator. */
|
|
2421
|
+
function folderFile(dir, name) {
|
|
2422
|
+
const direct = join(dir, `${name}.svg`);
|
|
2423
|
+
if (existsSync(direct)) return direct;
|
|
2424
|
+
const dash = name.indexOf("-");
|
|
2425
|
+
for (let i = dash; i > 0; i = name.indexOf("-", i + 1)) {
|
|
2426
|
+
const sub = join(dir, name.slice(0, i));
|
|
2427
|
+
if (existsSync(sub) && statSync(sub).isDirectory()) {
|
|
2428
|
+
const found = folderFile(sub, name.slice(i + 1));
|
|
2429
|
+
if (found) return found;
|
|
2430
|
+
}
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
//#endregion
|
|
2434
|
+
//#region src/vite/types.ts
|
|
2435
|
+
/**
|
|
2436
|
+
* The generated `icons.d.ts` (D10): the names and the framework, registered
|
|
2437
|
+
* on `@menapo/icons`'s `Register` the way vicss registers its tokens.
|
|
2438
|
+
*/
|
|
2439
|
+
/** `src/icons.d.ts`, else `app/icons.d.ts`, else the root's: where a tsconfig's `include` looks. */
|
|
2440
|
+
function typesFile(root) {
|
|
2441
|
+
const dir = ["src", "app"].map((name) => join(root, name)).find((d) => existsSync(d));
|
|
2442
|
+
return join(dir ?? root, "icons.d.ts");
|
|
2443
|
+
}
|
|
2444
|
+
function typesText(names, framework) {
|
|
2445
|
+
const union = names.length ? names.map((n) => `\n | ${JSON.stringify(n)}`).join("") : " never";
|
|
2446
|
+
return `// Generated by @menapo/icons/vite from the installed sets and folders. Commit it; do not edit.
|
|
2447
|
+
import "@menapo/icons";
|
|
2448
|
+
|
|
2449
|
+
declare module "@menapo/icons" {
|
|
2450
|
+
interface Register {
|
|
2451
|
+
framework: ${JSON.stringify(framework)};\n names:${union};\n }
|
|
2452
|
+
}
|
|
2453
|
+
`;
|
|
2454
|
+
}
|
|
2455
|
+
/** Writes unless unchanged, so a type checker or watcher sees no edit for nothing. */
|
|
2456
|
+
function writeIfChanged(file, text) {
|
|
2457
|
+
try {
|
|
2458
|
+
if (readFileSync(file, "utf8") === text) return false;
|
|
2459
|
+
} catch {}
|
|
2460
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
2461
|
+
writeFileSync(file, text);
|
|
2462
|
+
return true;
|
|
2463
|
+
}
|
|
2464
|
+
//#endregion
|
|
2465
|
+
//#region src/vite/index.ts
|
|
2466
|
+
/**
|
|
2467
|
+
* `@menapo/icons/vite`: `plugins: [icons()]` (PLAN §0). It compiles every
|
|
2468
|
+
* `icon()` call to a literal, serves each icon's CSS as a virtual module the
|
|
2469
|
+
* calling module imports, and writes `icons.d.ts`. `resolveIcon` and
|
|
2470
|
+
* `iconName` are the build-side door og's renderer uses (D11). The only
|
|
2471
|
+
* entry that imports from the build side.
|
|
2472
|
+
*/
|
|
2473
|
+
const RESOLVED = `\0${VIRTUAL}`;
|
|
2474
|
+
const SCRIPT = /\.(?:[cm]?[jt]sx?|vue|svelte|astro)(?:[?#]|$)/;
|
|
2475
|
+
const OWN = posix(fileURLToPath(new URL(import.meta.url.endsWith(".ts") ? "../" : "./", import.meta.url)));
|
|
2476
|
+
/** `plugins: [icons()]` in a Vite config. */
|
|
2477
|
+
function icons(options = {}) {
|
|
2478
|
+
let config;
|
|
2479
|
+
let source;
|
|
2480
|
+
let settings;
|
|
2481
|
+
let layer = "base";
|
|
2482
|
+
/** Folder files to the virtual ids built from them, for dev updates. */
|
|
2483
|
+
const built = /* @__PURE__ */ new Map();
|
|
2484
|
+
function writeTypes() {
|
|
2485
|
+
const skipped = [];
|
|
2486
|
+
const names = source.names(skipped);
|
|
2487
|
+
for (const name of skipped) config.logger.warn(`[icons] ${name}.svg is skipped: name icon files in lowercase words joined by dashes`);
|
|
2488
|
+
const framework = rootFramework(config.root) ?? "vanilla";
|
|
2489
|
+
writeIfChanged(typesFile(config.root), typesText(names, framework));
|
|
2490
|
+
}
|
|
2491
|
+
return {
|
|
2492
|
+
name: "icons",
|
|
2493
|
+
api: { folders: options.folders },
|
|
2494
|
+
enforce: "post",
|
|
2495
|
+
configResolved(resolved) {
|
|
2496
|
+
config = resolved;
|
|
2497
|
+
source = new IconSource({
|
|
2498
|
+
root: resolved.root,
|
|
2499
|
+
folders: options.folders
|
|
2500
|
+
});
|
|
2501
|
+
settings = {
|
|
2502
|
+
scripts: rootFramework(resolved.root) ?? "vanilla",
|
|
2503
|
+
jsx: jsxFrameworkOf(resolved, resolved.root)
|
|
2504
|
+
};
|
|
2505
|
+
if (resolved.plugins.some((plugin) => plugin.name === "vicss")) layer = "vicss.global";
|
|
2506
|
+
writeTypes();
|
|
2507
|
+
},
|
|
2508
|
+
configureServer(server) {
|
|
2509
|
+
const dirs = [...source.folders.values()];
|
|
2510
|
+
if (!dirs.length) return;
|
|
2511
|
+
server.watcher.add(dirs);
|
|
2512
|
+
const changed = (file) => {
|
|
2513
|
+
if (file.endsWith(".svg") && dirs.some((dir) => file.startsWith(dir + sep))) writeTypes();
|
|
2514
|
+
};
|
|
2515
|
+
server.watcher.on("add", changed);
|
|
2516
|
+
server.watcher.on("unlink", changed);
|
|
2517
|
+
},
|
|
2518
|
+
hotUpdate({ file, modules }) {
|
|
2519
|
+
const ids = built.get(file);
|
|
2520
|
+
if (!ids) return;
|
|
2521
|
+
const updated = [...ids].flatMap((id) => {
|
|
2522
|
+
const module = this.environment.moduleGraph.getModuleById(id);
|
|
2523
|
+
if (!module) return [];
|
|
2524
|
+
this.environment.moduleGraph.invalidateModule(module);
|
|
2525
|
+
return [module];
|
|
2526
|
+
});
|
|
2527
|
+
return [...modules, ...updated];
|
|
2528
|
+
},
|
|
2529
|
+
resolveId: {
|
|
2530
|
+
filter: { id: new RegExp(`^${VIRTUAL}`) },
|
|
2531
|
+
handler: (id) => `\0${id}`
|
|
2532
|
+
},
|
|
2533
|
+
load: {
|
|
2534
|
+
filter: { id: new RegExp(`^${RESOLVED.replace("\0", "\\0")}`) },
|
|
2535
|
+
handler(id) {
|
|
2536
|
+
const file = id.slice(RESOLVED.length, -4);
|
|
2537
|
+
if (file === "base") return baseCss(layer);
|
|
2538
|
+
const name = file.replace("--", ":");
|
|
2539
|
+
let icon;
|
|
2540
|
+
try {
|
|
2541
|
+
icon = source.resolve(name);
|
|
2542
|
+
} catch (error) {
|
|
2543
|
+
if (error instanceof IconError) this.error(error.message);
|
|
2544
|
+
throw error;
|
|
2545
|
+
}
|
|
2546
|
+
if (source.folders.has(name.slice(0, name.indexOf(":")))) {
|
|
2547
|
+
this.addWatchFile(icon.file);
|
|
2548
|
+
let ids = built.get(icon.file);
|
|
2549
|
+
if (!ids) built.set(icon.file, ids = /* @__PURE__ */ new Set());
|
|
2550
|
+
ids.add(id);
|
|
2551
|
+
}
|
|
2552
|
+
return iconCss(name, icon, layer);
|
|
2553
|
+
}
|
|
2554
|
+
},
|
|
2555
|
+
transform: {
|
|
2556
|
+
filter: {
|
|
2557
|
+
id: {
|
|
2558
|
+
include: SCRIPT,
|
|
2559
|
+
exclude: /^\0/
|
|
2560
|
+
},
|
|
2561
|
+
code: PACKAGE$1
|
|
2562
|
+
},
|
|
2563
|
+
handler(code, id) {
|
|
2564
|
+
if (within([OWN], id)) return null;
|
|
2565
|
+
const framework = /\.astro(?:[?#]|$)/.test(id) ? "astro" : frameworkOf(id, code, settings);
|
|
2566
|
+
try {
|
|
2567
|
+
const out = compile(code, id, {
|
|
2568
|
+
classAttribute: framework === "react" ? "className" : "class",
|
|
2569
|
+
check: (name, start) => {
|
|
2570
|
+
try {
|
|
2571
|
+
source.resolve(name);
|
|
2572
|
+
} catch (error) {
|
|
2573
|
+
if (error instanceof IconError) throw new CompileError(error.message, start);
|
|
2574
|
+
throw error;
|
|
2575
|
+
}
|
|
2576
|
+
}
|
|
2577
|
+
});
|
|
2578
|
+
return out && {
|
|
2579
|
+
code: out.code,
|
|
2580
|
+
map: out.map
|
|
2581
|
+
};
|
|
2582
|
+
} catch (error) {
|
|
2583
|
+
if (error instanceof CompileError) {
|
|
2584
|
+
const file = posix(relative(config.root, id.replace(/[?#].*$/, "")));
|
|
2585
|
+
this.error({
|
|
2586
|
+
message: `${file}: ${error.message}`,
|
|
2587
|
+
pos: error.start
|
|
2588
|
+
});
|
|
2589
|
+
}
|
|
2590
|
+
throw error;
|
|
2591
|
+
}
|
|
2592
|
+
}
|
|
2593
|
+
}
|
|
2594
|
+
};
|
|
2595
|
+
}
|
|
2596
|
+
const sources = /* @__PURE__ */ new Map();
|
|
2597
|
+
/**
|
|
2598
|
+
* `lucide:check` to its SVG, from the sets and folders of the project at
|
|
2599
|
+
* `root`, as the plugin resolves it. Throws a message naming the fix for an
|
|
2600
|
+
* unknown name. Build side only: og calls it while it renders.
|
|
2601
|
+
*/
|
|
2602
|
+
function resolveIcon(name, options) {
|
|
2603
|
+
const key = JSON.stringify([options.root, options.folders ?? {}]);
|
|
2604
|
+
let icons = sources.get(key);
|
|
2605
|
+
if (!icons) sources.set(key, icons = new IconSource(options));
|
|
2606
|
+
const icon = icons.resolve(name);
|
|
2607
|
+
const svg = svgElement(icon);
|
|
2608
|
+
return {
|
|
2609
|
+
...icon,
|
|
2610
|
+
svg: icon.mono ? svg.replace("<svg ", "<svg fill=\"currentColor\" ") : svg
|
|
2611
|
+
};
|
|
2612
|
+
}
|
|
2613
|
+
/**
|
|
2614
|
+
* The name an `icon()` result stands for, read back from its classes:
|
|
2615
|
+
* `iconName({ class: "mi mi-lucide--x" })` is `"lucide:x"`. A spread in a
|
|
2616
|
+
* markup template is just attributes; this is how a renderer knows it is
|
|
2617
|
+
* an icon. Takes `class` or `className` (D3).
|
|
2618
|
+
*/
|
|
2619
|
+
function iconName(attributes) {
|
|
2620
|
+
const classes = attributes.class ?? attributes.className;
|
|
2621
|
+
return typeof classes === "string" ? nameOfClass(classes) : void 0;
|
|
2622
|
+
}
|
|
2623
|
+
//#endregion
|
|
2624
|
+
export { iconName, icons, resolveIcon };
|