vite-userscript-plugin 2.3.1 โ 2.5.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/README.md +34 -1
- package/dist/index.d.ts +28 -2
- package/dist/index.js +340 -91
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
- ๐ Only used `@grant`s in the production build
|
|
16
16
|
- ๐ฆ Built-in types for Tampermonkey, Greasemonkey and Violentmonkey
|
|
17
17
|
- ๐ Virtual module with script metadata
|
|
18
|
+
- ๐งต Support Web Workers
|
|
18
19
|
|
|
19
20
|
## Getting started
|
|
20
21
|
|
|
@@ -107,10 +108,20 @@ import './style.css'
|
|
|
107
108
|
> [!NOTE]
|
|
108
109
|
> Do not put userscript assets in `public/` โ those URLs hit the host site and 404. Import the file so Vite inlines it.
|
|
109
110
|
|
|
111
|
+
## Web Workers
|
|
112
|
+
|
|
113
|
+
HMR serve rewrites `import Worker from './w?worker'` (and `?worker&inline`) to a data-URI module worker that imports Vite's `?worker_file`. The host page cannot load `/src/w.ts?worker_file` from localhost.
|
|
114
|
+
|
|
115
|
+
`vite build` and `server.file` keep Vite's worker emit. A plain `?worker` is a separate file the match site cannot serve โ use `?worker&inline`. `?worker&url` and `?sharedworker` stay as Vite emits them.
|
|
116
|
+
|
|
117
|
+
See [examples/web-worker](./examples/web-worker).
|
|
118
|
+
|
|
110
119
|
## HTML pages
|
|
111
120
|
|
|
112
121
|
`index.html` is a normal Vite app next to the userscript. `vite` serves it at `/`. `vite build` writes it to `dist/` beside `{fileName}.user.js`.
|
|
113
122
|
|
|
123
|
+
The plugin sets `build.assetsInlineLimit` very high so userscript assets become data URLs. The HTML appโs assets will too, unless you set `build.assetsInlineLimit` yourself.
|
|
124
|
+
|
|
114
125
|
> [!WARNING]
|
|
115
126
|
> Keep the page's `<script>` entries distinct from `entry`.
|
|
116
127
|
|
|
@@ -142,10 +153,30 @@ See [examples/sourcemap](./examples/sourcemap).
|
|
|
142
153
|
| `generate` | โ | Rewrite the generated metablock. |
|
|
143
154
|
| `autoMetaUrls` | `false` | Fill empty `updateURL` / `downloadURL` from `homepage` / `homepageURL` / `website` / `source`. |
|
|
144
155
|
| `metaFile` | `true` | Emit `{fileName}.meta.js`. |
|
|
156
|
+
| `external` | โ | Keep these packages out of the bundle and load them via `@require`. Keys are specifiers (`jquery`, `vue`). A string value is the CDN URL (global name from the specifier). Pass `{ global, url }` for `$` / `Vue`. Install `@types/โฆ` for `tsc`; do not install the runtime package. See [examples/external-cdn](./examples/external-cdn). |
|
|
145
157
|
|
|
146
158
|
Everything else on `header` follows the manager metablock (`@grant`, `@require`, `@connect`, โฆ).
|
|
147
159
|
|
|
148
|
-
|
|
160
|
+
```ts
|
|
161
|
+
userscript({
|
|
162
|
+
entry: 'src/index.ts',
|
|
163
|
+
header: {
|
|
164
|
+
name: pkg.name,
|
|
165
|
+
version: pkg.version,
|
|
166
|
+
match: 'https://example.com/*',
|
|
167
|
+
},
|
|
168
|
+
external: {
|
|
169
|
+
jquery: {
|
|
170
|
+
global: '$',
|
|
171
|
+
url: 'https://cdn.jsdelivr.net/npm/jquery@4.0.0/dist/jquery.min.js',
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
})
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
For types without bundling the package: add `@types/jquery` (not `jquery`) and a `d.ts` that references those types. The import type-checks; the plugin maps it to the CDN global. Full setup: [examples/external-cdn](./examples/external-cdn).
|
|
178
|
+
|
|
179
|
+
In serve mode the header lists every grant. In production the plugin scans the bundle and writes only the grants in use. `window.focus`, `window.close`, and `window.onurlchange` are **not** auto-detected (they collide with DOM APIs) โ list them in `header.grant` when you need them. `grant: "none"` disables GM APIs and is never mixed with the scan.
|
|
149
180
|
|
|
150
181
|
> [!WARNING]
|
|
151
182
|
> Keep `metaFile: true` if you use `autoMetaUrls`. Otherwise `@updateURL` points at a file that is not emitted.
|
|
@@ -161,6 +192,8 @@ In serve mode the header lists every grant. In production the plugin scans the b
|
|
|
161
192
|
| [multiple-entries](./examples/multiple-entries) | Two scripts. |
|
|
162
193
|
| [sourcemap](./examples/sourcemap) | Inline map, HTML page, virtual module. |
|
|
163
194
|
| [serve-file](./examples/serve-file) | `server.file`, install `.user.js` or the proxy from the printed URLs. |
|
|
195
|
+
| [external-cdn](./examples/external-cdn) | `@types/jquery` only; runtime `$` from a CDN `@require`. |
|
|
196
|
+
| [web-worker](./examples/web-worker) | `?worker&inline`, data-URI bridge in HMR. |
|
|
164
197
|
|
|
165
198
|
## FAQ
|
|
166
199
|
|
package/dist/index.d.ts
CHANGED
|
@@ -195,6 +195,22 @@ interface HeaderGenerateContext {
|
|
|
195
195
|
userscript: string;
|
|
196
196
|
mode: HeaderMode;
|
|
197
197
|
}
|
|
198
|
+
interface ExternalRequire {
|
|
199
|
+
/**
|
|
200
|
+
* Global name the `@require` script assigns (e.g. `$`, `Vue`).
|
|
201
|
+
*/
|
|
202
|
+
global: string;
|
|
203
|
+
/**
|
|
204
|
+
* Absolute URL added to `@require`.
|
|
205
|
+
*/
|
|
206
|
+
url: string;
|
|
207
|
+
}
|
|
208
|
+
type UserscriptExternal = Record<string, string | ExternalRequire>;
|
|
209
|
+
interface ResolvedExternal {
|
|
210
|
+
specifier: string;
|
|
211
|
+
global: string;
|
|
212
|
+
url: string;
|
|
213
|
+
}
|
|
198
214
|
/**
|
|
199
215
|
* One userscript. Pass an object, or an array of these, to {@link UserscriptPluginConfig}.
|
|
200
216
|
*/
|
|
@@ -243,12 +259,21 @@ interface UserscriptConfig {
|
|
|
243
259
|
* @default true
|
|
244
260
|
*/
|
|
245
261
|
metaFile?: boolean;
|
|
262
|
+
/**
|
|
263
|
+
* Keep these packages out of the bundle and load them via `@require`.
|
|
264
|
+
* Keys are specifiers (`jquery`, `vue`). A string value is the CDN URL; the
|
|
265
|
+
* global name is derived from the specifier. Pass `{ global, url }` for `$` / `Vue`.
|
|
266
|
+
*
|
|
267
|
+
* Install `@types/โฆ` (or a types-only package) for `tsc`. Do not install the
|
|
268
|
+
* runtime package โ serve maps the specifier to the CDN global, build rewrites
|
|
269
|
+
* the import. See `examples/external-cdn`.
|
|
270
|
+
*/
|
|
271
|
+
external?: UserscriptExternal;
|
|
246
272
|
}
|
|
247
273
|
type UserscriptPluginConfig = UserscriptConfig | UserscriptConfig[];
|
|
248
274
|
interface ResolvedScript {
|
|
249
275
|
entry: string;
|
|
250
276
|
fileName: string;
|
|
251
|
-
iifeName: string;
|
|
252
277
|
header: HeaderConfig;
|
|
253
278
|
server: {
|
|
254
279
|
open: ResolvedServerOpen;
|
|
@@ -259,9 +284,10 @@ interface ResolvedScript {
|
|
|
259
284
|
generate?: (ctx: HeaderGenerateContext) => string;
|
|
260
285
|
autoMetaUrls: boolean;
|
|
261
286
|
metaFile: boolean;
|
|
287
|
+
external: ResolvedExternal[];
|
|
262
288
|
}
|
|
263
289
|
//#endregion
|
|
264
290
|
//#region src/plugin.d.ts
|
|
265
291
|
declare function UserscriptPlugin(config: UserscriptPluginConfig): Plugin[];
|
|
266
292
|
//#endregion
|
|
267
|
-
export { type HeaderConfig, type HeaderGenerateContext, type ResolvedScript, type ResolvedServerOpen, type ServerConfig, type ServerOpen, type UserscriptConfig, type UserscriptPluginConfig, UserscriptPlugin as default };
|
|
293
|
+
export { type ExternalRequire, type HeaderConfig, type HeaderGenerateContext, type ResolvedExternal, type ResolvedScript, type ResolvedServerOpen, type ServerConfig, type ServerOpen, type UserscriptConfig, type UserscriptExternal, type UserscriptPluginConfig, UserscriptPlugin as default };
|
package/dist/index.js
CHANGED
|
@@ -54,7 +54,7 @@ const gmIdentifiers = [
|
|
|
54
54
|
];
|
|
55
55
|
//#endregion
|
|
56
56
|
//#region src/grants/scan.ts
|
|
57
|
-
const grantMatchers = grants.map((grant) => ({
|
|
57
|
+
const grantMatchers = grants.filter((grant) => !grant.startsWith("window.")).map((grant) => ({
|
|
58
58
|
grant,
|
|
59
59
|
pattern: new RegExp(`\\b${grant.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`)
|
|
60
60
|
}));
|
|
@@ -74,15 +74,11 @@ function withServeGrants(header) {
|
|
|
74
74
|
grant: [.../* @__PURE__ */ new Set([...header.grant ?? [], ...grants])]
|
|
75
75
|
};
|
|
76
76
|
}
|
|
77
|
-
function withBuildGrants(header, code
|
|
77
|
+
function withBuildGrants(header, code) {
|
|
78
78
|
if (header.grant === "none") return header;
|
|
79
79
|
return {
|
|
80
80
|
...header,
|
|
81
|
-
grant: removeDuplicates([
|
|
82
|
-
...defineGrants(code),
|
|
83
|
-
...removeDuplicates(header.grant),
|
|
84
|
-
...extraGrants
|
|
85
|
-
])
|
|
81
|
+
grant: removeDuplicates([...defineGrants(code), ...removeDuplicates(header.grant)])
|
|
86
82
|
};
|
|
87
83
|
}
|
|
88
84
|
//#endregion
|
|
@@ -312,21 +308,86 @@ function isAsset(item) {
|
|
|
312
308
|
return item.type === "asset";
|
|
313
309
|
}
|
|
314
310
|
//#endregion
|
|
311
|
+
//#region src/build/graph.ts
|
|
312
|
+
function importedChunkIds(chunk, includeDynamic = true) {
|
|
313
|
+
if (!includeDynamic) return [...chunk.imports];
|
|
314
|
+
return [...chunk.imports, ...chunk.dynamicImports ?? []];
|
|
315
|
+
}
|
|
316
|
+
function walkImportedChunks(chunk, bundle, options = {}) {
|
|
317
|
+
const includeDynamic = options.includeDynamic ?? true;
|
|
318
|
+
const result = [];
|
|
319
|
+
const seen = /* @__PURE__ */ new Set();
|
|
320
|
+
const visit = (current) => {
|
|
321
|
+
for (const fileName of importedChunkIds(current, includeDynamic)) {
|
|
322
|
+
if (seen.has(fileName)) continue;
|
|
323
|
+
const dep = bundle[fileName];
|
|
324
|
+
if (!dep || !isChunk(dep) || dep.isEntry) continue;
|
|
325
|
+
seen.add(fileName);
|
|
326
|
+
visit(dep);
|
|
327
|
+
result.push({
|
|
328
|
+
fileName,
|
|
329
|
+
chunk: dep
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
visit(chunk);
|
|
334
|
+
return result;
|
|
335
|
+
}
|
|
336
|
+
function collectImportedChunkIds(chunk, bundle) {
|
|
337
|
+
return new Set(walkImportedChunks(chunk, bundle).map((item) => item.fileName));
|
|
338
|
+
}
|
|
339
|
+
function inlineImportedChunks(chunk, bundle) {
|
|
340
|
+
return walkImportedChunks(chunk, bundle).map(({ chunk: dep }) => dep.code.endsWith("\n") ? dep.code : `${dep.code}\n`).join("");
|
|
341
|
+
}
|
|
342
|
+
function namespaceLiteral(chunk) {
|
|
343
|
+
return `{ ${(chunk.exports ?? []).map((name) => {
|
|
344
|
+
if (name === "default") return "default: undefined";
|
|
345
|
+
return /^[a-z_$][\w$]*$/i.test(name) ? name : `${JSON.stringify(name)}: undefined`;
|
|
346
|
+
}).join(", ")} }`;
|
|
347
|
+
}
|
|
348
|
+
function replaceDynamicImportCalls(code, fileName, namespace) {
|
|
349
|
+
const replacement = `Promise.resolve(${namespace})`;
|
|
350
|
+
const baseName = fileName.split("/").pop() ?? fileName;
|
|
351
|
+
const candidates = [
|
|
352
|
+
fileName,
|
|
353
|
+
`./${fileName}`,
|
|
354
|
+
baseName,
|
|
355
|
+
`./${baseName}`
|
|
356
|
+
];
|
|
357
|
+
let next = code;
|
|
358
|
+
for (const name of new Set(candidates)) {
|
|
359
|
+
const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
360
|
+
next = next.replace(new RegExp(`\\bimport\\s*\\(\\s*(?:/\\*[\\s\\S]*?\\*/\\s*)?(['"])${escaped}\\1\\s*\\)`, "g"), replacement);
|
|
361
|
+
}
|
|
362
|
+
return next;
|
|
363
|
+
}
|
|
364
|
+
function rewriteInlinedDynamicImports(code, chunk, bundle) {
|
|
365
|
+
let next = code;
|
|
366
|
+
const seen = /* @__PURE__ */ new Set();
|
|
367
|
+
const visit = (current) => {
|
|
368
|
+
for (const fileName of current.dynamicImports ?? []) {
|
|
369
|
+
if (seen.has(fileName)) continue;
|
|
370
|
+
seen.add(fileName);
|
|
371
|
+
const dep = bundle[fileName];
|
|
372
|
+
if (!dep || !isChunk(dep)) continue;
|
|
373
|
+
visit(dep);
|
|
374
|
+
next = replaceDynamicImportCalls(next, fileName, namespaceLiteral(dep));
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
visit(chunk);
|
|
378
|
+
if (next.includes("__vitePreload")) return `function __vitePreload(fn){return fn();}\n${next}`;
|
|
379
|
+
return next;
|
|
380
|
+
}
|
|
381
|
+
//#endregion
|
|
315
382
|
//#region src/build/css.ts
|
|
316
383
|
const defaultCssInjector = `(function (css) {
|
|
317
384
|
var style = document.createElement('style')
|
|
318
385
|
style.textContent = css
|
|
319
386
|
;(document.head || document.documentElement).appendChild(style)
|
|
320
387
|
})`;
|
|
321
|
-
function collectImportedCss(chunk, bundle
|
|
388
|
+
function collectImportedCss(chunk, bundle) {
|
|
322
389
|
const files = [...chunk.viteMetadata?.importedCss ?? []];
|
|
323
|
-
for (const
|
|
324
|
-
if (seen.has(imported)) continue;
|
|
325
|
-
const dep = bundle[imported];
|
|
326
|
-
if (!dep || !isChunk(dep) || dep.isEntry) continue;
|
|
327
|
-
seen.add(imported);
|
|
328
|
-
files.push(...collectImportedCss(dep, bundle, seen));
|
|
329
|
-
}
|
|
390
|
+
for (const { chunk: dep } of walkImportedChunks(chunk, bundle)) files.push(...dep.viteMetadata?.importedCss ?? []);
|
|
330
391
|
return files;
|
|
331
392
|
}
|
|
332
393
|
function collectCss(chunk, bundle) {
|
|
@@ -347,6 +408,94 @@ function createCssInject(css) {
|
|
|
347
408
|
return `${defaultCssInjector}(${JSON.stringify(css)});\n`;
|
|
348
409
|
}
|
|
349
410
|
//#endregion
|
|
411
|
+
//#region src/build/external.ts
|
|
412
|
+
function resolveExternals(external) {
|
|
413
|
+
if (!external) return [];
|
|
414
|
+
return Object.entries(external).map(([specifier, value]) => {
|
|
415
|
+
if (typeof value === "string") return {
|
|
416
|
+
specifier,
|
|
417
|
+
url: value,
|
|
418
|
+
global: toIdentifier(specifier)
|
|
419
|
+
};
|
|
420
|
+
return {
|
|
421
|
+
specifier,
|
|
422
|
+
url: value.url,
|
|
423
|
+
global: value.global
|
|
424
|
+
};
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
function toRequireList$1(value) {
|
|
428
|
+
if (value == null) return [];
|
|
429
|
+
return Array.isArray(value) ? value.map(String) : [String(value)];
|
|
430
|
+
}
|
|
431
|
+
function mergeRequireUrls(header, urls) {
|
|
432
|
+
if (!urls.length) return header;
|
|
433
|
+
const merged = [...toRequireList$1(header.require)];
|
|
434
|
+
for (const url of urls) if (!merged.includes(url)) merged.push(url);
|
|
435
|
+
return {
|
|
436
|
+
...header,
|
|
437
|
+
require: merged
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
function withExternalRequires(header, externals) {
|
|
441
|
+
return mergeRequireUrls(header, externals.map((item) => item.url));
|
|
442
|
+
}
|
|
443
|
+
function normalizeNamedImports(inner) {
|
|
444
|
+
return inner.split(",").map((part) => {
|
|
445
|
+
const trimmed = part.trim();
|
|
446
|
+
if (!trimmed) return trimmed;
|
|
447
|
+
const asMatch = trimmed.match(/^([a-z_$][\w$]*)\s+as\s+([a-z_$][\w$]*)$/i);
|
|
448
|
+
if (asMatch) return `${asMatch[1]}: ${asMatch[2]}`;
|
|
449
|
+
return trimmed;
|
|
450
|
+
}).filter(Boolean).join(", ");
|
|
451
|
+
}
|
|
452
|
+
const IDENT = "([a-z_$][\\w$]*)";
|
|
453
|
+
const IDENT_FLAGS = "i";
|
|
454
|
+
function rewriteImportClause(clause, globalName) {
|
|
455
|
+
const trimmed = clause.trim();
|
|
456
|
+
const star = trimmed.match(new RegExp(`^\\*\\s+as\\s+${IDENT}$`, IDENT_FLAGS));
|
|
457
|
+
if (star?.[1]) return star[1] === globalName ? "" : `const ${star[1]} = ${globalName};`;
|
|
458
|
+
const defaultStar = trimmed.match(new RegExp(`^${IDENT}\\s*,\\s*\\*\\s+as\\s+${IDENT}$`, IDENT_FLAGS));
|
|
459
|
+
if (defaultStar?.[1] && defaultStar[2]) return [defaultStar[1] === globalName ? "" : `const ${defaultStar[1]} = ${globalName};`, defaultStar[2] === globalName ? "" : `const ${defaultStar[2]} = ${globalName};`].filter(Boolean).join(" ");
|
|
460
|
+
const defaultNamed = trimmed.match(new RegExp(`^${IDENT}\\s*,\\s*\\{([^}]+)\\}$`, IDENT_FLAGS));
|
|
461
|
+
if (defaultNamed?.[1] && defaultNamed[2] != null) {
|
|
462
|
+
const named = `const { ${normalizeNamedImports(defaultNamed[2])} } = ${globalName};`;
|
|
463
|
+
if (defaultNamed[1] === globalName) return named;
|
|
464
|
+
return `const ${defaultNamed[1]} = ${globalName}; ${named}`;
|
|
465
|
+
}
|
|
466
|
+
const named = trimmed.match(/^\{([^}]+)\}$/);
|
|
467
|
+
if (named?.[1] != null) return `const { ${normalizeNamedImports(named[1])} } = ${globalName};`;
|
|
468
|
+
if (/^[a-z_$][\w$]*$/i.test(trimmed)) return trimmed === globalName ? "" : `const ${trimmed} = ${globalName};`;
|
|
469
|
+
return `const ${trimmed} = ${globalName};`;
|
|
470
|
+
}
|
|
471
|
+
function rewriteExternalImports(code, externals) {
|
|
472
|
+
let next = code;
|
|
473
|
+
for (const { specifier, global: globalName } of externals) {
|
|
474
|
+
const quoted = `["']${specifier.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}["']`;
|
|
475
|
+
next = next.replace(new RegExp(`(^|\\n)import\\s*${quoted}\\s*;?`, "g"), "$1");
|
|
476
|
+
next = next.replace(new RegExp(`(^|\\n)import\\s+type\\s+[\\s\\S]*?\\s+from\\s*${quoted}\\s*;?`, "g"), "$1");
|
|
477
|
+
next = next.replace(new RegExp(`(^|\\n)import\\s+([\\s\\S]*?)\\s+from\\s*${quoted}\\s*;?`, "g"), (_match, lead, clause) => {
|
|
478
|
+
const rewritten = rewriteImportClause(clause, globalName);
|
|
479
|
+
return rewritten ? `${lead}${rewritten}` : lead;
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
return next;
|
|
483
|
+
}
|
|
484
|
+
function mergeRolldownExternal(user, specifiers) {
|
|
485
|
+
if (!specifiers.length) return user;
|
|
486
|
+
if (typeof user === "function") {
|
|
487
|
+
const specSet = new Set(specifiers);
|
|
488
|
+
return (id, importer, isResolved) => {
|
|
489
|
+
if (specSet.has(id)) return true;
|
|
490
|
+
return user(id, importer, isResolved);
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
let list = [];
|
|
494
|
+
if (Array.isArray(user)) list = [...user];
|
|
495
|
+
else if (user != null) list = [user];
|
|
496
|
+
return [...list, ...specifiers];
|
|
497
|
+
}
|
|
498
|
+
//#endregion
|
|
350
499
|
//#region src/build/iife.ts
|
|
351
500
|
const SOURCE_MAPPING_URL_RE = /\/\/[#@]\s*sourceMappingURL=\S+/g;
|
|
352
501
|
function stripSourceMappingUrl(code) {
|
|
@@ -358,11 +507,15 @@ function stripImports(code) {
|
|
|
358
507
|
function stripExports(code) {
|
|
359
508
|
return code.replace(/^export\s+\{[\s\S]*?\}\s+from\s+["'][^"']+["']\s*;?\s*$/gm, "").replace(/^export\s+\*\s+from\s+["'][^"']+["']\s*;?\s*$/gm, "").replace(/^export\s+\{[\s\S]*?\};?\s*$/gm, "").replace(/^export\s+default\s+/gm, "").replace(/^export\s+async\s+function/gm, "async function").replace(/^export\s+function/gm, "function").replace(/^export\s+class/gm, "class").replace(/^export\s+(const|let|var)/gm, "$1");
|
|
360
509
|
}
|
|
510
|
+
function stripDynamicImports(code) {
|
|
511
|
+
return code.replace(/\bimport\s*\(\s*(?:\/\*[\s\S]*?\*\/\s*)?["'][^"']+["']\s*\)/g, "Promise.resolve({})");
|
|
512
|
+
}
|
|
361
513
|
function stripModuleSyntax(code) {
|
|
362
|
-
return stripExports(stripImports(code));
|
|
514
|
+
return stripExports(stripImports(stripDynamicImports(code)));
|
|
363
515
|
}
|
|
364
516
|
function isAlreadyIife(code) {
|
|
365
|
-
|
|
517
|
+
const trimmed = code.trimStart();
|
|
518
|
+
return !/^\s*(?:import|export)\s/m.test(code) && /^\(\s*(?:async\s+)?function\b/.test(trimmed);
|
|
366
519
|
}
|
|
367
520
|
function ensureIife(code) {
|
|
368
521
|
const withoutMap = stripSourceMappingUrl(code);
|
|
@@ -380,7 +533,7 @@ function toRequireList(value) {
|
|
|
380
533
|
return Array.isArray(value) ? value.map(String) : [String(value)];
|
|
381
534
|
}
|
|
382
535
|
function createWatchProxyHeader(script, jsAbsPath) {
|
|
383
|
-
const header = withServeGrants(
|
|
536
|
+
const header = withServeGrants(withExternalRequires(script.header, script.external));
|
|
384
537
|
return {
|
|
385
538
|
...header,
|
|
386
539
|
require: [...toRequireList(header.require), toFileRequireUrl(jsAbsPath)]
|
|
@@ -403,35 +556,6 @@ function toRequireFileName(fileName) {
|
|
|
403
556
|
}
|
|
404
557
|
//#endregion
|
|
405
558
|
//#region src/build/apply.ts
|
|
406
|
-
function importedChunkIds(chunk) {
|
|
407
|
-
return [...chunk.imports, ...chunk.dynamicImports ?? []];
|
|
408
|
-
}
|
|
409
|
-
function inlineImportedChunks(chunk, bundle, seen = /* @__PURE__ */ new Set()) {
|
|
410
|
-
let prelude = "";
|
|
411
|
-
for (const imported of chunk.imports) {
|
|
412
|
-
if (seen.has(imported)) continue;
|
|
413
|
-
const dep = bundle[imported];
|
|
414
|
-
if (!dep || !isChunk(dep) || dep.isEntry) continue;
|
|
415
|
-
seen.add(imported);
|
|
416
|
-
prelude += inlineImportedChunks(dep, bundle, seen);
|
|
417
|
-
prelude += dep.code.endsWith("\n") ? dep.code : `${dep.code}\n`;
|
|
418
|
-
}
|
|
419
|
-
return prelude;
|
|
420
|
-
}
|
|
421
|
-
function collectImportedChunks(chunk, bundle) {
|
|
422
|
-
const files = /* @__PURE__ */ new Set();
|
|
423
|
-
const walk = (current) => {
|
|
424
|
-
for (const imported of importedChunkIds(current)) {
|
|
425
|
-
if (files.has(imported)) continue;
|
|
426
|
-
const dep = bundle[imported];
|
|
427
|
-
if (!dep || !isChunk(dep) || dep.isEntry) continue;
|
|
428
|
-
files.add(imported);
|
|
429
|
-
walk(dep);
|
|
430
|
-
}
|
|
431
|
-
};
|
|
432
|
-
walk(chunk);
|
|
433
|
-
return files;
|
|
434
|
-
}
|
|
435
559
|
function collectImportedCssFiles(fileNames, bundle) {
|
|
436
560
|
const files = /* @__PURE__ */ new Set();
|
|
437
561
|
for (const fileName of fileNames) {
|
|
@@ -449,8 +573,13 @@ function withSourceMaps(fileNames) {
|
|
|
449
573
|
function findScriptForChunk(chunk, fileName, scripts) {
|
|
450
574
|
return scripts.find((script) => chunk.name === script.fileName || fileName === `${script.fileName}.js` || fileName === `${script.fileName}.user.js`);
|
|
451
575
|
}
|
|
576
|
+
function offsetUserscriptMap(options) {
|
|
577
|
+
const wrapOffset = isAlreadyIife(stripSourceMappingUrl(options.body)) ? 0 : 1;
|
|
578
|
+
const lineOffset = countHeaderLines(options.headerPrefix ?? "") + countHeaderLines(options.cssPrelude) + wrapOffset + countHeaderLines(options.inlined);
|
|
579
|
+
return stripVendorSourcesContent(offsetSourceMap(options.map, lineOffset, options.fileName));
|
|
580
|
+
}
|
|
452
581
|
function createHeadedUserscript(script, options) {
|
|
453
|
-
const headerConfig = withBuildGrants(script.header, options.wrapped);
|
|
582
|
+
const headerConfig = withBuildGrants(withExternalRequires(script.header, script.external), options.wrapped);
|
|
454
583
|
const prefix = `${generateHeader(headerConfig, {
|
|
455
584
|
align: script.headerAlign,
|
|
456
585
|
autoMetaUrls: script.autoMetaUrls,
|
|
@@ -461,9 +590,14 @@ function createHeadedUserscript(script, options) {
|
|
|
461
590
|
const nextFileName = `${script.fileName}.user.js`;
|
|
462
591
|
let nextCode = `${prefix}${options.code}`;
|
|
463
592
|
if (options.map) {
|
|
464
|
-
const
|
|
465
|
-
|
|
466
|
-
|
|
593
|
+
const map = offsetUserscriptMap({
|
|
594
|
+
map: options.map,
|
|
595
|
+
fileName: nextFileName,
|
|
596
|
+
cssPrelude: options.cssPrelude,
|
|
597
|
+
inlined: options.inlined,
|
|
598
|
+
body: options.body,
|
|
599
|
+
headerPrefix: prefix
|
|
600
|
+
});
|
|
467
601
|
nextCode = `${stripSourceMappingUrl(nextCode).replace(/\n+$/g, "\n")}//# sourceMappingURL=${toInlineSourceMappingUrl(map)}\n`;
|
|
468
602
|
}
|
|
469
603
|
return {
|
|
@@ -491,7 +625,7 @@ function applyUserscriptBundle(bundle, config, context) {
|
|
|
491
625
|
const keptChunks = new Set(otherEntryFiles);
|
|
492
626
|
for (const fileName of otherEntryFiles) {
|
|
493
627
|
const chunk = bundle[fileName];
|
|
494
|
-
if (chunk && isChunk(chunk)) for (const dep of
|
|
628
|
+
if (chunk && isChunk(chunk)) for (const dep of collectImportedChunkIds(chunk, bundle)) keptChunks.add(dep);
|
|
495
629
|
}
|
|
496
630
|
const keptCss = collectImportedCssFiles(keptChunks, bundle);
|
|
497
631
|
const leftoverChunks = [];
|
|
@@ -501,7 +635,7 @@ function applyUserscriptBundle(bundle, config, context) {
|
|
|
501
635
|
const { css, files: cssFiles } = collectCss(chunk, bundle);
|
|
502
636
|
for (const cssFile of cssFiles) if (!keptCss.has(cssFile)) leftoverAssets.push(...withSourceMaps([cssFile]));
|
|
503
637
|
const cssPrelude = css ? createCssInject(css) : "";
|
|
504
|
-
const body = `${inlined}${chunk.code}
|
|
638
|
+
const body = rewriteExternalImports(rewriteInlinedDynamicImports(`${inlined}${chunk.code}`, chunk, bundle), script.external);
|
|
505
639
|
const wrapped = ensureIife(body);
|
|
506
640
|
const code = `${cssPrelude}${wrapped}`;
|
|
507
641
|
const emitFileProxy = Boolean(context.emitProxy && context.outDir && script.server.file);
|
|
@@ -518,9 +652,13 @@ function applyUserscriptBundle(bundle, config, context) {
|
|
|
518
652
|
const requireName = toRequireFileName(script.fileName);
|
|
519
653
|
let nextCode = code.endsWith("\n") ? code : `${code}\n`;
|
|
520
654
|
if (chunk.map) {
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
655
|
+
chunk.map = offsetUserscriptMap({
|
|
656
|
+
map: chunk.map,
|
|
657
|
+
fileName: requireName,
|
|
658
|
+
cssPrelude,
|
|
659
|
+
inlined,
|
|
660
|
+
body
|
|
661
|
+
});
|
|
524
662
|
nextCode = `${stripSourceMappingUrl(nextCode).replace(/\n+$/g, "\n")}//# sourceMappingURL=${toInlineSourceMappingUrl(chunk.map)}\n`;
|
|
525
663
|
}
|
|
526
664
|
chunk.code = nextCode;
|
|
@@ -652,7 +790,6 @@ function toResolvedScript(config) {
|
|
|
652
790
|
return {
|
|
653
791
|
entry: config.entry,
|
|
654
792
|
fileName,
|
|
655
|
-
iifeName: toIdentifier(fileName),
|
|
656
793
|
header: config.header,
|
|
657
794
|
server: {
|
|
658
795
|
open: resolveServerOpen(config.server?.open, file),
|
|
@@ -662,7 +799,8 @@ function toResolvedScript(config) {
|
|
|
662
799
|
headerAlign: config.headerAlign ?? 1,
|
|
663
800
|
generate: config.generate,
|
|
664
801
|
autoMetaUrls: config.autoMetaUrls ?? false,
|
|
665
|
-
metaFile: config.metaFile ?? true
|
|
802
|
+
metaFile: config.metaFile ?? true,
|
|
803
|
+
external: resolveExternals(config.external)
|
|
666
804
|
};
|
|
667
805
|
}
|
|
668
806
|
function collectAutoMetaUrlsWarnings(config) {
|
|
@@ -714,6 +852,28 @@ function resolvePluginConfig(config) {
|
|
|
714
852
|
return { scripts };
|
|
715
853
|
}
|
|
716
854
|
//#endregion
|
|
855
|
+
//#region src/serve/external.ts
|
|
856
|
+
const EXTERNAL_MODULE_PREFIX = `\0${PLUGIN_NAME}:external:`;
|
|
857
|
+
function toExternalModuleId(specifier) {
|
|
858
|
+
return `${EXTERNAL_MODULE_PREFIX}${specifier}`;
|
|
859
|
+
}
|
|
860
|
+
function matchExternalModuleId(id) {
|
|
861
|
+
if (!id.startsWith(EXTERNAL_MODULE_PREFIX)) return;
|
|
862
|
+
return id.slice(EXTERNAL_MODULE_PREFIX.length);
|
|
863
|
+
}
|
|
864
|
+
function findResolvedExternal(scripts, specifier) {
|
|
865
|
+
for (const script of scripts) {
|
|
866
|
+
const hit = script.external.find((item) => item.specifier === specifier);
|
|
867
|
+
if (hit) return hit;
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
function listExternalSpecifiers(scripts) {
|
|
871
|
+
return [...new Set(scripts.flatMap((script) => script.external.map((item) => item.specifier)))];
|
|
872
|
+
}
|
|
873
|
+
function renderExternalModule(globalName) {
|
|
874
|
+
return `const api = globalThis[${JSON.stringify(globalName)}];\nexport default api;\n`;
|
|
875
|
+
}
|
|
876
|
+
//#endregion
|
|
717
877
|
//#region src/serve/gm-shim.ts
|
|
718
878
|
function shouldShimModule(id) {
|
|
719
879
|
const cleanId = id.split("\0").pop() ?? id;
|
|
@@ -721,6 +881,7 @@ function shouldShimModule(id) {
|
|
|
721
881
|
if (/\.(?:css|scss|sass|less|styl|stylus|pcss)(?:$|\?)/i.test(cleanId)) return false;
|
|
722
882
|
if (/[?&](?:vue|svelte)&type=style/.test(cleanId)) return false;
|
|
723
883
|
if (/[?&](?:raw|url)(?:&|$)/.test(cleanId)) return false;
|
|
884
|
+
if (/[?&](?:worker_file|sharedworker|worker)(?:&|$)/.test(cleanId)) return false;
|
|
724
885
|
if (/\.(?:m|c)?[jt]sx?(?:$|\?)/.test(cleanId)) return true;
|
|
725
886
|
if (/[?&]vue&type=script/.test(cleanId) || cleanId.endsWith(".vue")) return true;
|
|
726
887
|
if (/[?&]svelte&type=script/.test(cleanId) || cleanId.endsWith(".svelte")) return true;
|
|
@@ -813,14 +974,17 @@ import ${JSON.stringify(entryPath)};
|
|
|
813
974
|
}
|
|
814
975
|
//#endregion
|
|
815
976
|
//#region src/serve/wrapper.ts
|
|
977
|
+
function matchUserscriptPath(url, fileName, suffix) {
|
|
978
|
+
return (url.split("?")[0] ?? "") === `/${fileName}${suffix}`;
|
|
979
|
+
}
|
|
816
980
|
function matchDevUserscript(url, fileName) {
|
|
817
|
-
return (url
|
|
981
|
+
return matchUserscriptPath(url, fileName, ".dev.user.js");
|
|
818
982
|
}
|
|
819
983
|
function matchProxyUserscript(url, fileName) {
|
|
820
|
-
return (url
|
|
984
|
+
return matchUserscriptPath(url, fileName, ".proxy.user.js");
|
|
821
985
|
}
|
|
822
986
|
function matchFileUserscript(url, fileName) {
|
|
823
|
-
return (url
|
|
987
|
+
return matchUserscriptPath(url, fileName, ".user.js");
|
|
824
988
|
}
|
|
825
989
|
function toInstallFileName(fileName, kind) {
|
|
826
990
|
return {
|
|
@@ -839,17 +1003,17 @@ function toServeEntryPath(root, entry) {
|
|
|
839
1003
|
const absolute = resolve(root, entry);
|
|
840
1004
|
return `/${relative(root, absolute).split(sep).join(posix.sep)}`;
|
|
841
1005
|
}
|
|
842
|
-
function applyServeHeader(header, prefix) {
|
|
843
|
-
return withServeGrants({
|
|
1006
|
+
function applyServeHeader(header, prefix, externals = []) {
|
|
1007
|
+
return withServeGrants(withExternalRequires({
|
|
844
1008
|
...header,
|
|
845
1009
|
name: prefix === false ? header.name : `${prefix}${header.name}`
|
|
846
|
-
});
|
|
1010
|
+
}, externals));
|
|
847
1011
|
}
|
|
848
1012
|
function generateDevWrapper(options) {
|
|
849
1013
|
const clientUrl = `${options.origin}/@vite/client`;
|
|
850
1014
|
const entryUrl = `${options.origin}${options.entryPath}`;
|
|
851
1015
|
const bootstrapUrl = `${options.origin}${REACT_BOOTSTRAP_PATH}?entry=${encodeURIComponent(options.entryPath)}`;
|
|
852
|
-
const copies = gmIdentifiers.map((id) => `if (typeof ${id} !== 'undefined') gm.${id} = ${id};`).join("\n ");
|
|
1016
|
+
const copies = [...gmIdentifiers.map((id) => `if (typeof ${id} !== 'undefined') gm.${id} = ${id};`), ...(options.externals ?? []).map((item) => `if (typeof ${item.global} !== 'undefined') root.${item.global} = ${item.global};`)].join("\n ");
|
|
853
1017
|
const injectTarget = options.reactPreamble ? bootstrapUrl : entryUrl;
|
|
854
1018
|
const clientInject = options.reactPreamble ? "" : `
|
|
855
1019
|
if (!root.${VITE_CLIENT_FLAG}) {
|
|
@@ -874,14 +1038,15 @@ ${clientInject}
|
|
|
874
1038
|
`;
|
|
875
1039
|
}
|
|
876
1040
|
function generateDevUserscript(options) {
|
|
877
|
-
return `${generateHeader(applyServeHeader(options.script.header, options.prefix), {
|
|
1041
|
+
return `${generateHeader(applyServeHeader(options.script.header, options.prefix, options.script.external), {
|
|
878
1042
|
...options.headerOptions,
|
|
879
1043
|
fileName: options.script.fileName,
|
|
880
1044
|
mode: "serve"
|
|
881
1045
|
})}\n\n${generateDevWrapper({
|
|
882
1046
|
origin: options.origin,
|
|
883
1047
|
entryPath: toServeEntryPath(options.root, options.script.entry),
|
|
884
|
-
reactPreamble: options.reactPreamble
|
|
1048
|
+
reactPreamble: options.reactPreamble,
|
|
1049
|
+
externals: options.script.external
|
|
885
1050
|
})}`;
|
|
886
1051
|
}
|
|
887
1052
|
function findDevScript(url, scripts) {
|
|
@@ -995,6 +1160,69 @@ function configureDevServer(server, resolved, reactPreamble) {
|
|
|
995
1160
|
};
|
|
996
1161
|
}
|
|
997
1162
|
//#endregion
|
|
1163
|
+
//#region src/serve/watch-queue.ts
|
|
1164
|
+
function createDebouncedSingleFlight(task, delay, onError) {
|
|
1165
|
+
let timer;
|
|
1166
|
+
let running = false;
|
|
1167
|
+
let queued = false;
|
|
1168
|
+
const run = async () => {
|
|
1169
|
+
if (running) {
|
|
1170
|
+
queued = true;
|
|
1171
|
+
return;
|
|
1172
|
+
}
|
|
1173
|
+
running = true;
|
|
1174
|
+
try {
|
|
1175
|
+
while (true) {
|
|
1176
|
+
queued = false;
|
|
1177
|
+
await task();
|
|
1178
|
+
if (!queued) break;
|
|
1179
|
+
}
|
|
1180
|
+
} catch (error) {
|
|
1181
|
+
onError(error);
|
|
1182
|
+
} finally {
|
|
1183
|
+
running = false;
|
|
1184
|
+
if (queued) {
|
|
1185
|
+
queued = false;
|
|
1186
|
+
run();
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
};
|
|
1190
|
+
return {
|
|
1191
|
+
schedule: () => {
|
|
1192
|
+
clearTimeout(timer);
|
|
1193
|
+
timer = setTimeout(() => {
|
|
1194
|
+
timer = void 0;
|
|
1195
|
+
run();
|
|
1196
|
+
}, delay);
|
|
1197
|
+
},
|
|
1198
|
+
cancel: () => {
|
|
1199
|
+
clearTimeout(timer);
|
|
1200
|
+
timer = void 0;
|
|
1201
|
+
},
|
|
1202
|
+
pending: () => running || queued || timer != null
|
|
1203
|
+
};
|
|
1204
|
+
}
|
|
1205
|
+
//#endregion
|
|
1206
|
+
//#region src/serve/web-worker.ts
|
|
1207
|
+
function isWebWorkerRequest(id) {
|
|
1208
|
+
const queryIndex = id.indexOf("?");
|
|
1209
|
+
if (queryIndex === -1) return false;
|
|
1210
|
+
const query = new URLSearchParams(id.slice(queryIndex + 1));
|
|
1211
|
+
return query.has("worker") && !query.has("url");
|
|
1212
|
+
}
|
|
1213
|
+
const webWorkerWrapper = `const dataUri = \`data:text/javascript;charset=utf-8,\${encodeURIComponent(
|
|
1214
|
+
\`import \${JSON.stringify(
|
|
1215
|
+
new URL('?worker_file&type=module', import.meta['url']).href,
|
|
1216
|
+
)};\`,
|
|
1217
|
+
)}\`;
|
|
1218
|
+
|
|
1219
|
+
export default function WorkerWrapper(options) {
|
|
1220
|
+
return new Worker(dataUri, {
|
|
1221
|
+
type: 'module',
|
|
1222
|
+
name: options?.name,
|
|
1223
|
+
});
|
|
1224
|
+
}`;
|
|
1225
|
+
//#endregion
|
|
998
1226
|
//#region src/plugin.ts
|
|
999
1227
|
function absolutizeEntries(config, root) {
|
|
1000
1228
|
return { scripts: config.scripts.map((script) => ({
|
|
@@ -1048,30 +1276,34 @@ function UserscriptPlugin(config) {
|
|
|
1048
1276
|
server.config.logger.error(String(error));
|
|
1049
1277
|
return;
|
|
1050
1278
|
}
|
|
1051
|
-
|
|
1279
|
+
const scheduler = createDebouncedSingleFlight(run, 80, (error) => {
|
|
1280
|
+
server.config.logger.error(String(error));
|
|
1281
|
+
});
|
|
1052
1282
|
const onChange = (file) => {
|
|
1053
1283
|
if (file.startsWith(outDirAbs)) return;
|
|
1054
|
-
|
|
1055
|
-
timer = setTimeout(() => {
|
|
1056
|
-
run().catch((error) => {
|
|
1057
|
-
server.config.logger.error(String(error));
|
|
1058
|
-
});
|
|
1059
|
-
}, 80);
|
|
1284
|
+
scheduler.schedule();
|
|
1060
1285
|
};
|
|
1061
1286
|
server.watcher.on("change", onChange);
|
|
1062
1287
|
server.watcher.on("add", onChange);
|
|
1063
|
-
|
|
1064
|
-
|
|
1288
|
+
let cleaned = false;
|
|
1289
|
+
const cleanup = () => {
|
|
1290
|
+
if (cleaned) return;
|
|
1291
|
+
cleaned = true;
|
|
1292
|
+
scheduler.cancel();
|
|
1065
1293
|
server.watcher.off("change", onChange);
|
|
1066
1294
|
server.watcher.off("add", onChange);
|
|
1067
|
-
|
|
1295
|
+
};
|
|
1296
|
+
server.httpServer?.once("close", cleanup);
|
|
1297
|
+
const closeServer = server.close.bind(server);
|
|
1298
|
+
server.close = async () => {
|
|
1299
|
+
cleanup();
|
|
1068
1300
|
return closeServer();
|
|
1069
1301
|
};
|
|
1070
1302
|
};
|
|
1071
1303
|
return [
|
|
1072
1304
|
{
|
|
1073
1305
|
name: `${PLUGIN_NAME}:config`,
|
|
1074
|
-
config(userConfig) {
|
|
1306
|
+
config(userConfig, env) {
|
|
1075
1307
|
const { input, hasHtml } = resolvePluginBuildInput(userConfig, resolved.scripts);
|
|
1076
1308
|
const scriptNames = new Set(resolved.scripts.map((script) => script.fileName));
|
|
1077
1309
|
userConfig.build ??= {};
|
|
@@ -1081,11 +1313,25 @@ function UserscriptPlugin(config) {
|
|
|
1081
1313
|
const userEntryFileNames = userOutput && !Array.isArray(userOutput) ? userOutput.entryFileNames : void 0;
|
|
1082
1314
|
const openScript = resolved.scripts.find((script) => script.server.open);
|
|
1083
1315
|
const openPath = openScript?.server.open ? toInstallPath(openScript.fileName, openScript.server.open) : void 0;
|
|
1316
|
+
const specifiers = listExternalSpecifiers(resolved.scripts);
|
|
1317
|
+
const rolldownOptions = {
|
|
1318
|
+
input,
|
|
1319
|
+
output: {
|
|
1320
|
+
format: "es",
|
|
1321
|
+
entryFileNames: (chunkInfo) => {
|
|
1322
|
+
if (scriptNames.has(chunkInfo.name)) return "[name].js";
|
|
1323
|
+
if (typeof userEntryFileNames === "function") return userEntryFileNames(chunkInfo);
|
|
1324
|
+
if (typeof userEntryFileNames === "string") return userEntryFileNames;
|
|
1325
|
+
return "assets/[name]-[hash].js";
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
};
|
|
1329
|
+
if (env.command === "build" && specifiers.length) rolldownOptions.external = mergeRolldownExternal(userConfig.build.rolldownOptions.external, specifiers);
|
|
1084
1330
|
return {
|
|
1085
1331
|
appType: userConfig.appType ?? (hasHtml ? "spa" : "custom"),
|
|
1086
1332
|
optimizeDeps: {
|
|
1087
1333
|
entries: Object.values(input),
|
|
1088
|
-
exclude: [VIRTUAL_MODULE_ID]
|
|
1334
|
+
exclude: [VIRTUAL_MODULE_ID, ...specifiers]
|
|
1089
1335
|
},
|
|
1090
1336
|
server: {
|
|
1091
1337
|
cors: userConfig.server?.cors ?? true,
|
|
@@ -1094,18 +1340,7 @@ function UserscriptPlugin(config) {
|
|
|
1094
1340
|
build: {
|
|
1095
1341
|
minify: userConfig.build?.minify ?? false,
|
|
1096
1342
|
assetsInlineLimit: userConfig.build?.assetsInlineLimit ?? Number.MAX_SAFE_INTEGER,
|
|
1097
|
-
rolldownOptions
|
|
1098
|
-
input,
|
|
1099
|
-
output: {
|
|
1100
|
-
format: "es",
|
|
1101
|
-
entryFileNames: (chunkInfo) => {
|
|
1102
|
-
if (scriptNames.has(chunkInfo.name)) return "[name].js";
|
|
1103
|
-
if (typeof userEntryFileNames === "function") return userEntryFileNames(chunkInfo);
|
|
1104
|
-
if (typeof userEntryFileNames === "string") return userEntryFileNames;
|
|
1105
|
-
return "assets/[name]-[hash].js";
|
|
1106
|
-
}
|
|
1107
|
-
}
|
|
1108
|
-
}
|
|
1343
|
+
rolldownOptions
|
|
1109
1344
|
}
|
|
1110
1345
|
};
|
|
1111
1346
|
},
|
|
@@ -1120,9 +1355,23 @@ function UserscriptPlugin(config) {
|
|
|
1120
1355
|
name: `${PLUGIN_NAME}:virtual`,
|
|
1121
1356
|
resolveId: (id) => {
|
|
1122
1357
|
if (id === "virtual:vite-userscript-plugin") return RESOLVED_VIRTUAL_MODULE_ID;
|
|
1358
|
+
if (command === "serve" && findResolvedExternal(resolved.scripts, id)) return toExternalModuleId(id);
|
|
1123
1359
|
},
|
|
1124
1360
|
load: (id) => {
|
|
1125
1361
|
if (id === RESOLVED_VIRTUAL_MODULE_ID) return renderVirtualModule(createClientSnapshot(resolved.scripts, command));
|
|
1362
|
+
const specifier = matchExternalModuleId(id);
|
|
1363
|
+
if (!specifier) return;
|
|
1364
|
+
const external = findResolvedExternal(resolved.scripts, specifier);
|
|
1365
|
+
if (!external) return;
|
|
1366
|
+
return renderExternalModule(external.global);
|
|
1367
|
+
}
|
|
1368
|
+
},
|
|
1369
|
+
{
|
|
1370
|
+
name: `${PLUGIN_NAME}:web-worker`,
|
|
1371
|
+
enforce: "pre",
|
|
1372
|
+
apply: "serve",
|
|
1373
|
+
load(id) {
|
|
1374
|
+
if (isWebWorkerRequest(id)) return webWorkerWrapper;
|
|
1126
1375
|
}
|
|
1127
1376
|
},
|
|
1128
1377
|
{
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vite-userscript-plugin",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.5.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Vitalij Ryndin",
|
|
7
7
|
"url": "https://github.com/crashmax-dev"
|
|
@@ -52,7 +52,8 @@
|
|
|
52
52
|
"eslint-plugin-format": "2.0.1",
|
|
53
53
|
"prettier-plugin-css-order": "2.2.0",
|
|
54
54
|
"svelte": "5.57.0",
|
|
55
|
-
"
|
|
55
|
+
"taze": "21.1.0",
|
|
56
|
+
"tsdown": "0.23.0",
|
|
56
57
|
"turbo": "2.10.12",
|
|
57
58
|
"typescript": "npm:@typescript/typescript6@6.0.2",
|
|
58
59
|
"vite": "8.2.2",
|
|
@@ -66,7 +67,8 @@
|
|
|
66
67
|
"build:examples": "turbo run build --filter=./examples/*",
|
|
67
68
|
"test": "vitest",
|
|
68
69
|
"test:ui": "vitest --ui --watch",
|
|
69
|
-
"type-check": "tsc --noEmit -p tsconfig.json &&
|
|
70
|
+
"type-check": "tsc --noEmit -p tsconfig.json && turbo run type-check --filter=./examples/*",
|
|
71
|
+
"check-update": "taze major -Irl",
|
|
70
72
|
"sync-types": "node scripts/sync-types.ts",
|
|
71
73
|
"lint": "eslint . --cache",
|
|
72
74
|
"lint:fix": "eslint . --fix --cache"
|