vite-userscript-plugin 0.3.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,316 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ UserscriptPlugin: () => UserscriptPlugin,
24
+ default: () => src_default
25
+ });
26
+ module.exports = __toCommonJS(src_exports);
27
+ var import_node_fs = require("fs");
28
+ var import_node_http = require("http");
29
+ var import_node_path = require("path");
30
+ var import_node_url = require("url");
31
+ var import_websocket = require("websocket");
32
+
33
+ // src/banner.ts
34
+ function banner(config) {
35
+ const metadata = [];
36
+ const configKeys = Object.keys(config);
37
+ const maxKeyLength = Math.max(...configKeys.map((key) => key.length)) + 1;
38
+ const addSpaces = (str) => {
39
+ return " ".repeat(maxKeyLength - str.length);
40
+ };
41
+ const addMetadata = (key, value) => {
42
+ const isBoolean = typeof value === "boolean";
43
+ if (isBoolean && !value)
44
+ return;
45
+ value = !isBoolean ? addSpaces(key) + value.toString() : "";
46
+ metadata.push(`// @${key}${value}`);
47
+ };
48
+ for (const [key, value] of Object.entries(config)) {
49
+ if (Array.isArray(value)) {
50
+ value.forEach((value2) => addMetadata(key, value2));
51
+ } else {
52
+ if (value === void 0)
53
+ continue;
54
+ addMetadata(key, value);
55
+ }
56
+ }
57
+ return [
58
+ "// ==UserScript==",
59
+ ...metadata,
60
+ "// ==/UserScript=="
61
+ ].join("\n");
62
+ }
63
+
64
+ // src/constants.ts
65
+ var regexpScripts = new RegExp(/\.(tsx?|jsx?)$/);
66
+ var regexpStyles = new RegExp(/\.(s?css|sass)$/);
67
+ var template = `console.warn("__TEMPLATE__")`;
68
+ var grants = [
69
+ "unsafeWindow",
70
+ "window.onurlchange",
71
+ "window.focus",
72
+ "window.close",
73
+ "GM_setValue",
74
+ "GM_getValue",
75
+ "GM_deleteValue",
76
+ "GM_listValues",
77
+ "GM_setClipboard",
78
+ "GM_addStyle",
79
+ "GM_addElement",
80
+ "GM_addValueChangeListener",
81
+ "GM_removeValueChangeListener",
82
+ "GM_registerMenuCommand",
83
+ "GM_unregisterMenuCommand",
84
+ "GM_download",
85
+ "GM_getTab",
86
+ "GM_getTabs",
87
+ "GM_saveTab",
88
+ "GM_openInTab",
89
+ "GM_notification",
90
+ "GM_getResourceURL",
91
+ "GM_getResourceText",
92
+ "GM_xmlhttpRequest",
93
+ "GM_webRequest",
94
+ "GM_log",
95
+ "GM_info"
96
+ ];
97
+
98
+ // src/helpers.ts
99
+ var import_vite = require("vite");
100
+ function removeDuplicates(arr) {
101
+ return [...new Set(Array.isArray(arr) ? arr : arr ? [arr] : [])];
102
+ }
103
+ async function transform({
104
+ file,
105
+ name,
106
+ loader
107
+ }) {
108
+ const { code } = await (0, import_vite.transformWithEsbuild)(file, name, {
109
+ loader,
110
+ minify: true
111
+ });
112
+ return code;
113
+ }
114
+ function defineGrants(code) {
115
+ const definedGrants = [];
116
+ for (const grant of grants) {
117
+ if (code.indexOf(grant) !== -1) {
118
+ definedGrants.push(grant);
119
+ }
120
+ }
121
+ return definedGrants;
122
+ }
123
+
124
+ // src/css.ts
125
+ var CSS = class {
126
+ styles = /* @__PURE__ */ new Map();
127
+ add(entry, code, path) {
128
+ if (regexpStyles.test(path)) {
129
+ this.styles.set(path, code);
130
+ return {
131
+ code: ""
132
+ };
133
+ }
134
+ if (path.includes(entry)) {
135
+ return {
136
+ code: code + template
137
+ };
138
+ }
139
+ return null;
140
+ }
141
+ async minify(file, name) {
142
+ return await transform({
143
+ file,
144
+ name,
145
+ loader: "css"
146
+ });
147
+ }
148
+ inject() {
149
+ const styles = [...this.styles.values()].join("");
150
+ if (!styles)
151
+ return;
152
+ return `GM_addStyle(\`${styles}\`)`;
153
+ }
154
+ merge(modules) {
155
+ const styleModules = [];
156
+ for (const module2 of modules) {
157
+ const style = this.styles.get(module2);
158
+ if (!style)
159
+ continue;
160
+ styleModules.push([module2, style]);
161
+ }
162
+ this.styles.clear();
163
+ styleModules.forEach((value) => this.styles.set(...value));
164
+ }
165
+ };
166
+ var css_default = new CSS();
167
+
168
+ // src/index.ts
169
+ var import_meta = {};
170
+ function UserscriptPlugin(config) {
171
+ var _a;
172
+ let pluginConfig;
173
+ let isBuildWatch;
174
+ let socketConnection = null;
175
+ const port = ((_a = config.server) == null ? void 0 : _a.port) || 8e3;
176
+ const httpServer = (0, import_node_http.createServer)();
177
+ httpServer.listen(port);
178
+ const WebSocketServer = import_websocket.server;
179
+ const ws = new WebSocketServer({ httpServer });
180
+ ws.on("request", (request) => {
181
+ socketConnection = request.accept(null, request.origin);
182
+ });
183
+ return {
184
+ name: "vite-userscript-plugin",
185
+ apply: "build",
186
+ config() {
187
+ return {
188
+ build: {
189
+ lib: {
190
+ entry: config.entry,
191
+ name: config.metadata.name,
192
+ formats: ["iife"],
193
+ fileName: () => `${config.metadata.name}.js`
194
+ },
195
+ rollupOptions: {
196
+ output: {
197
+ exports: "none",
198
+ extend: true
199
+ }
200
+ }
201
+ }
202
+ };
203
+ },
204
+ configResolved(cfg) {
205
+ pluginConfig = cfg;
206
+ isBuildWatch = cfg.build.watch ?? false;
207
+ const { match, require: require2, include, exclude, resource, connect } = config.metadata;
208
+ config.metadata.match = removeDuplicates(match);
209
+ config.metadata.require = removeDuplicates(require2);
210
+ config.metadata.include = removeDuplicates(include);
211
+ config.metadata.exclude = removeDuplicates(exclude);
212
+ config.metadata.resource = removeDuplicates(resource);
213
+ config.metadata.connect = removeDuplicates(connect);
214
+ },
215
+ async transform(code, path) {
216
+ const style = await css_default.minify(code, path);
217
+ return css_default.add(config.entry, style.replace("\n", ""), path);
218
+ },
219
+ generateBundle(_, bundle) {
220
+ for (const [_2, file] of Object.entries(bundle)) {
221
+ const styleModules = Object.keys(
222
+ file.modules
223
+ );
224
+ css_default.merge(styleModules);
225
+ }
226
+ },
227
+ async writeBundle(_, bundle) {
228
+ for (const [fileName] of Object.entries(bundle)) {
229
+ if (regexpScripts.test(fileName)) {
230
+ const rootDir = pluginConfig.root;
231
+ const outDir = pluginConfig.build.outDir || "dist";
232
+ const outPath = (0, import_node_path.resolve)(rootDir, outDir, fileName);
233
+ const hotReloadPath = (0, import_node_path.resolve)(
234
+ (0, import_node_path.dirname)((0, import_node_url.fileURLToPath)(import_meta.url)),
235
+ "__hot-reload__.js"
236
+ );
237
+ const proxyFilePath = (0, import_node_path.resolve)(
238
+ rootDir,
239
+ outDir,
240
+ `${config.metadata.name}.proxy.user.js`
241
+ );
242
+ const userFilePath = (0, import_node_path.resolve)(
243
+ rootDir,
244
+ outDir,
245
+ `${config.metadata.name}.user.js`
246
+ );
247
+ try {
248
+ let source = (0, import_node_fs.readFileSync)(outPath, "utf8");
249
+ config.metadata.grant = removeDuplicates(
250
+ isBuildWatch ? grants : config.autoGrants ?? true ? defineGrants(source) : [...config.metadata.grant ?? [], "GM_addStyle", "GM_info"]
251
+ );
252
+ if (isBuildWatch) {
253
+ const hotReloadFile = (0, import_node_fs.readFileSync)(
254
+ (0, import_node_path.resolve)(
255
+ (0, import_node_path.dirname)((0, import_node_url.fileURLToPath)(import_meta.url)),
256
+ "hot-reload.js"
257
+ ),
258
+ "utf8"
259
+ );
260
+ const hotReloadScript = await transform({
261
+ file: hotReloadFile.replace("__PORT__", port.toString()),
262
+ name: hotReloadPath,
263
+ loader: "js"
264
+ });
265
+ (0, import_node_fs.writeFileSync)(hotReloadPath, hotReloadScript);
266
+ (0, import_node_fs.writeFileSync)(
267
+ proxyFilePath,
268
+ banner({
269
+ ...config.metadata,
270
+ require: [
271
+ ...config.metadata.require,
272
+ "file://" + hotReloadPath,
273
+ "file://" + outPath
274
+ ]
275
+ })
276
+ );
277
+ }
278
+ source = source.replace(template, `${css_default.inject()}`);
279
+ source = await transform({
280
+ file: source,
281
+ name: fileName,
282
+ loader: "js"
283
+ });
284
+ (0, import_node_fs.writeFileSync)(outPath, source);
285
+ (0, import_node_fs.writeFileSync)(
286
+ userFilePath,
287
+ `${banner(config.metadata)}
288
+
289
+ ${source}`
290
+ );
291
+ } catch (err) {
292
+ console.log(err);
293
+ }
294
+ }
295
+ }
296
+ if (!isBuildWatch) {
297
+ httpServer.close();
298
+ process.exit(0);
299
+ }
300
+ },
301
+ buildEnd() {
302
+ if (socketConnection) {
303
+ socketConnection.sendUTF(
304
+ JSON.stringify({
305
+ message: "reload"
306
+ })
307
+ );
308
+ }
309
+ }
310
+ };
311
+ }
312
+ var src_default = UserscriptPlugin;
313
+ // Annotate the CommonJS export names for ESM import in node:
314
+ 0 && (module.exports = {
315
+ UserscriptPlugin
316
+ });
package/dist/index.d.ts CHANGED
@@ -1,6 +1,219 @@
1
1
  import { PluginOption } from 'vite';
2
- import type { UserscriptPluginConfig } from './types.js';
2
+
3
+ declare const grants: readonly ["unsafeWindow", "window.onurlchange", "window.focus", "window.close", "GM_setValue", "GM_getValue", "GM_deleteValue", "GM_listValues", "GM_setClipboard", "GM_addStyle", "GM_addElement", "GM_addValueChangeListener", "GM_removeValueChangeListener", "GM_registerMenuCommand", "GM_unregisterMenuCommand", "GM_download", "GM_getTab", "GM_getTabs", "GM_saveTab", "GM_openInTab", "GM_notification", "GM_getResourceURL", "GM_getResourceText", "GM_xmlhttpRequest", "GM_webRequest", "GM_log", "GM_info"];
4
+
5
+ declare type RunAt = 'document-start' | 'document-body' | 'document-end' | 'document-idle' | 'context-menu';
6
+ declare type Grants = typeof grants[number];
7
+ declare type MetadataConfig = {
8
+ [property: string]: string | boolean | number | string[] | undefined;
9
+ /**
10
+ * The name of the script.
11
+ * Internationalization is done by adding an appendix naming the locale.
12
+ */
13
+ name: string;
14
+ /**
15
+ * Version of the script,
16
+ * it can be used to check if a script has new versions. It is composed
17
+ * of several parts, joined by `.` Each part must start with numbers,
18
+ * and can be followed by alphabetic characters.
19
+ */
20
+ version: string;
21
+ /**
22
+ * A brief summary to describe the script.
23
+ */
24
+ description?: string;
25
+ /**
26
+ * The scripts author.
27
+ */
28
+ author?: string;
29
+ /**
30
+ * The script license.
31
+ */
32
+ license?: string;
33
+ /**
34
+ * The combination of `@namespace` and `@name` is the unique identifier for
35
+ * a userscript. `@namespace` can be any string, for example the homepage
36
+ * of a group of userscripts by the same author. If not provided
37
+ * the `@namespace` falls back to an empty string ('').
38
+ */
39
+ namespace?: string;
40
+ /**
41
+ * The authors homepage that is used at the options page to link from
42
+ * the scripts name to the given page. Please note that if the `@namespace`
43
+ * tag starts with `https://` its content will be used for this too.
44
+ */
45
+ homepage?: string;
46
+ /**
47
+ * An update URL for the userscript.
48
+ * Note:
49
+ * - a `@version` tag is required to make update checks work.
50
+ */
51
+ updateURL?: string;
52
+ /**
53
+ * Defines the URL where the script will be downloaded from when an update was
54
+ * detected. If the value none is used, then no update check will be done.
55
+ */
56
+ downloadURL?: string;
57
+ /**
58
+ * Defines the URL where the user can report issues and get personal support.
59
+ */
60
+ supportURL?: string;
61
+ /**
62
+ * Specify an icon for the script. Almost any image will work,
63
+ * but a 32x32 pixel size is best. This value may be specified relative
64
+ * to the URL the script itself is downloaded from.
65
+ */
66
+ icon?: string;
67
+ icon64?: string;
68
+ /**
69
+ * Each `@include` and `@exclude` rule can be one of the following:
70
+ * - a normal string
71
+ * > If the string does not start or end with a slash (/),
72
+ * > it will be used as a normal string.
73
+ * > If there are wildcards (*), each of them matches any characters.
74
+ * > e.g. https://www.google.com/* matches the following:
75
+ * - https://www.google.com/
76
+ * - https://www.google.com/any/subview
77
+ * > but not the following:
78
+ * - http://www.google.com/
79
+ * - https://www.google.com.hk/
80
+ * > If there is no wildcard in the string, the rule matches the entire URL.
81
+ * > e.g. https://www.google.com/ matches only https://www.google.com/
82
+ * > but not https://www.google.com/any/subview.
83
+ * > The host part accepts .tld to match top level domain suffix.
84
+ * > e.g. https://www.google.tld/ matches both https://www.google.com/
85
+ * > and https://www.google.co.jp/.
86
+ *
87
+ * - a regular expression
88
+ * > If the string starts and ends with a slash (/),
89
+ * > it will be compiled as a regular expression.
90
+ * > e.g. /\.google\.com[\.\/]/ matches the following:
91
+ * - https://www.google.com/,
92
+ * - https://www.google.com/any/subview
93
+ * - http://www.google.com/
94
+ * - https://www.google.com.hk/
95
+ */
96
+ include?: string[] | string;
97
+ exclude?: string[] | string;
98
+ /**
99
+ * More or less equal to the `@include` tag.
100
+ * You can get more information
101
+ * [here](https://developer.chrome.com/docs/extensions/mv2/match_patterns/).
102
+ *
103
+ * Note:
104
+ * - The `<all_urls>` statement is not yet supported and the scheme part also
105
+ * accepts `http*://`.
106
+ */
107
+ match: string[] | string;
108
+ /**
109
+ * Points to a JavaScript file that is loaded and executed before the script
110
+ * itself starts running.
111
+ *
112
+ * Note:
113
+ * - The scripts loaded via `@require` and their "use strict" statements
114
+ * might influence the userscript's strict mode!
115
+ */
116
+ require?: string[] | string;
117
+ /**
118
+ * Preloads resources that can by accessed
119
+ * via `GM_getResourceURL` and `GM_getResourceText` by the script.
120
+ */
121
+ resource?: string[] | string;
122
+ /**
123
+ * This tag defines the domains (no top-level domains) including subdomains
124
+ * which are allowed to be retrieved by `GM_xmlhttpRequest`
125
+ *
126
+ * - domains like tampermonkey.net (this will also allow all sub-domains)
127
+ * - sub-domains i.e. safari.tampermonkey.net
128
+ * - self to whitelist the domain the script is currently running at
129
+ * - localhost to access the localhost
130
+ * - 1.2.3.4 to connect to an IP address
131
+ *
132
+ * If it's not possible to declare all domains a userscript might connect
133
+ * to then it's a good practice to do the following: ***Declare all known or
134
+ * at least all common domains*** that might be connected by the script.
135
+ * This way the confirmation dialog can be avoided for most of the users.
136
+ *
137
+ * Additionally add `@connect *` to the script. By doing so Tampermonkey will
138
+ * still ask the user whether the next connection to a not mentioned domain
139
+ * is allowed, but also ***offer a "Always allow all domains" button***.
140
+ * If the user clicks at this button then all future requestswill
141
+ * be permitted automatically.
142
+ */
143
+ connect?: string[] | string;
144
+ /**
145
+ * This tag makes the script running on the main pages, but not at iframes.
146
+ * @default false
147
+ */
148
+ noframes?: boolean;
149
+ /**
150
+ * `@grant` is used to whitelist GM_* functions, the `unsafeWindow` object and
151
+ * some powerful window functions. If no `@grant` tag is given TM guesses
152
+ * the scripts needs.
153
+ */
154
+ grant?: Exclude<Grants, 'GM_addStyle' | 'GM_info'>[];
155
+ /**
156
+ * Defines the moment the script is injected. In opposition to other script
157
+ * handlers, `@run-at` defines the first possible moment a script wants to
158
+ * run. This means it may happen, that a script that uses the `@require` tag
159
+ * may be executed after the document is already loaded, cause fetching the
160
+ * required script took that long. Anyhow, all `DOMNodeInserted` and
161
+ * `DOMContentLoaded` events that happended after the given injection
162
+ * moment are cached and delivered to the script when it is injected.
163
+ *
164
+ * - `document-start`
165
+ * The script will be injected as fast as possible.
166
+ *
167
+ * - `document-body`
168
+ * The script will be injected if the body element exists.
169
+ *
170
+ * - `document-end`
171
+ * The script will be injected when or after the `DOMContentLoaded` event
172
+ * was dispatched.
173
+ *
174
+ * - `document-idle`
175
+ * The script will be injected after the DOMContentLoaded event was
176
+ * dispatched. This is the default value if no `@run-at` tag is given.
177
+ *
178
+ * - `context-menu`
179
+ * The script will be injected if it is clicked at the browser context menu
180
+ * (desktop Chrome-based browsers only).
181
+ *
182
+ * Note:
183
+ * - all `@include` and `@exclude` statements will be ignored if this value
184
+ * is used, but this may change in the future.
185
+ *
186
+ * @default 'document-idle'
187
+ */
188
+ 'run-at'?: RunAt;
189
+ };
190
+ interface ServerConfig {
191
+ /**
192
+ * Server port.
193
+ * @default 8000
194
+ */
195
+ port?: number;
196
+ }
197
+ interface UserscriptPluginConfig {
198
+ /**
199
+ * Path of userscript entry.
200
+ */
201
+ entry: string;
202
+ /**
203
+ * Userscript header config.
204
+ */
205
+ metadata: MetadataConfig;
206
+ /**
207
+ * Import all `@grant` in development mode.
208
+ * @default true
209
+ */
210
+ autoGrants?: boolean;
211
+ /**
212
+ * Server config (used in development mode for hot reloading).
213
+ */
214
+ server?: ServerConfig;
215
+ }
216
+
3
217
  declare function UserscriptPlugin(config: UserscriptPluginConfig): PluginOption;
4
- export { UserscriptPlugin };
5
- export default UserscriptPlugin;
6
- export type { UserscriptPluginConfig };
218
+
219
+ export { UserscriptPlugin, UserscriptPluginConfig, UserscriptPlugin as default };