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.js CHANGED
@@ -1,115 +1,290 @@
1
- import { readFileSync, writeFileSync } from 'node:fs';
2
- import { createServer } from 'node:http';
3
- import { dirname, resolve } from 'node:path';
4
- import { fileURLToPath } from 'node:url';
5
- import websocket from 'websocket';
6
- import { banner } from './banner.js';
7
- import { grants, regexpScripts, template } from './constants.js';
8
- import css from './css.js';
9
- import { defineGrants, removeDuplicates, transform } from './helpers.js';
10
- function UserscriptPlugin(config) {
11
- let pluginConfig;
12
- let isBuildWatch;
13
- let socketConnection = null;
14
- const port = config.server?.port || 8000;
15
- const server = createServer();
16
- server.listen(port);
17
- const WebSocketServer = websocket.server;
18
- const ws = new WebSocketServer({ httpServer: server });
19
- ws.on('request', (request) => {
20
- socketConnection = request.accept(null, request.origin);
1
+ // src/index.ts
2
+ import { readFileSync, writeFileSync } from "fs";
3
+ import { createServer } from "http";
4
+ import { dirname, resolve } from "path";
5
+ import { fileURLToPath } from "url";
6
+ import { server } from "websocket";
7
+
8
+ // src/banner.ts
9
+ function banner(config) {
10
+ const metadata = [];
11
+ const configKeys = Object.keys(config);
12
+ const maxKeyLength = Math.max(...configKeys.map((key) => key.length)) + 1;
13
+ const addSpaces = (str) => {
14
+ return " ".repeat(maxKeyLength - str.length);
15
+ };
16
+ const addMetadata = (key, value) => {
17
+ const isBoolean = typeof value === "boolean";
18
+ if (isBoolean && !value)
19
+ return;
20
+ value = !isBoolean ? addSpaces(key) + value.toString() : "";
21
+ metadata.push(`// @${key}${value}`);
22
+ };
23
+ for (const [key, value] of Object.entries(config)) {
24
+ if (Array.isArray(value)) {
25
+ value.forEach((value2) => addMetadata(key, value2));
26
+ } else {
27
+ if (value === void 0)
28
+ continue;
29
+ addMetadata(key, value);
30
+ }
31
+ }
32
+ return [
33
+ "// ==UserScript==",
34
+ ...metadata,
35
+ "// ==/UserScript=="
36
+ ].join("\n");
37
+ }
38
+
39
+ // src/constants.ts
40
+ var regexpScripts = new RegExp(/\.(tsx?|jsx?)$/);
41
+ var regexpStyles = new RegExp(/\.(s?css|sass)$/);
42
+ var template = `console.warn("__TEMPLATE__")`;
43
+ var grants = [
44
+ "unsafeWindow",
45
+ "window.onurlchange",
46
+ "window.focus",
47
+ "window.close",
48
+ "GM_setValue",
49
+ "GM_getValue",
50
+ "GM_deleteValue",
51
+ "GM_listValues",
52
+ "GM_setClipboard",
53
+ "GM_addStyle",
54
+ "GM_addElement",
55
+ "GM_addValueChangeListener",
56
+ "GM_removeValueChangeListener",
57
+ "GM_registerMenuCommand",
58
+ "GM_unregisterMenuCommand",
59
+ "GM_download",
60
+ "GM_getTab",
61
+ "GM_getTabs",
62
+ "GM_saveTab",
63
+ "GM_openInTab",
64
+ "GM_notification",
65
+ "GM_getResourceURL",
66
+ "GM_getResourceText",
67
+ "GM_xmlhttpRequest",
68
+ "GM_webRequest",
69
+ "GM_log",
70
+ "GM_info"
71
+ ];
72
+
73
+ // src/helpers.ts
74
+ import { transformWithEsbuild } from "vite";
75
+ function removeDuplicates(arr) {
76
+ return [...new Set(Array.isArray(arr) ? arr : arr ? [arr] : [])];
77
+ }
78
+ async function transform({
79
+ file,
80
+ name,
81
+ loader
82
+ }) {
83
+ const { code } = await transformWithEsbuild(file, name, {
84
+ loader,
85
+ minify: true
86
+ });
87
+ return code;
88
+ }
89
+ function defineGrants(code) {
90
+ const definedGrants = [];
91
+ for (const grant of grants) {
92
+ if (code.indexOf(grant) !== -1) {
93
+ definedGrants.push(grant);
94
+ }
95
+ }
96
+ return definedGrants;
97
+ }
98
+
99
+ // src/css.ts
100
+ var CSS = class {
101
+ styles = /* @__PURE__ */ new Map();
102
+ add(entry, code, path) {
103
+ if (regexpStyles.test(path)) {
104
+ this.styles.set(path, code);
105
+ return {
106
+ code: ""
107
+ };
108
+ }
109
+ if (path.includes(entry)) {
110
+ return {
111
+ code: code + template
112
+ };
113
+ }
114
+ return null;
115
+ }
116
+ async minify(file, name) {
117
+ return await transform({
118
+ file,
119
+ name,
120
+ loader: "css"
21
121
  });
22
- return {
23
- name: 'vite-userscript-plugin',
24
- apply: 'build',
25
- config() {
26
- return {
27
- build: {
28
- lib: {
29
- entry: config.entry,
30
- name: config.metadata.name,
31
- formats: ['iife'],
32
- fileName: () => `${config.metadata.name}.js`
33
- },
34
- rollupOptions: {
35
- output: {
36
- extend: true
37
- }
38
- }
39
- }
40
- };
41
- },
42
- configResolved(cfg) {
43
- pluginConfig = cfg;
44
- isBuildWatch = (cfg.build.watch ?? false);
45
- const { match, require, include, exclude, resource, connect } = config.metadata;
46
- config.metadata.match = removeDuplicates(match);
47
- config.metadata.require = removeDuplicates(require);
48
- config.metadata.include = removeDuplicates(include);
49
- config.metadata.exclude = removeDuplicates(exclude);
50
- config.metadata.resource = removeDuplicates(resource);
51
- config.metadata.connect = removeDuplicates(connect);
52
- },
53
- async transform(code, path) {
54
- const style = await css.minify(code, path);
55
- return css.add(config.entry, style.replace('\n', ''), path);
56
- },
57
- generateBundle(_, bundle) {
58
- for (const [_, file] of Object.entries(bundle)) {
59
- const styleModules = Object.keys(file.modules);
60
- css.merge(styleModules);
61
- }
62
- },
63
- async writeBundle(_, bundle) {
64
- for (const [fileName] of Object.entries(bundle)) {
65
- if (regexpScripts.test(fileName)) {
66
- const rootDir = pluginConfig.root;
67
- const outDir = pluginConfig.build.outDir;
68
- const filePath = resolve(rootDir, outDir, fileName);
69
- const proxyFilePath = resolve(rootDir, outDir, `${config.metadata.name}.proxy.user.js`);
70
- const userFilePath = resolve(rootDir, outDir, `${config.metadata.name}.user.js`);
71
- try {
72
- let file = readFileSync(filePath, {
73
- encoding: 'utf8'
74
- });
75
- const hmrScript = readFileSync(resolve(dirname(fileURLToPath(import.meta.url)), 'hmr.js'), 'utf-8');
76
- file = file.replace(template, `
77
- ${css.inject()}
78
- const port = ${port}
79
- ${hmrScript}
80
- `);
81
- file = await transform({ file, name: fileName, loader: 'js' });
82
- // prettier-ignore
83
- config.metadata.grant = removeDuplicates(isBuildWatch
84
- ? grants
85
- : config.autoGrants ?? true
86
- ? defineGrants(file)
87
- : [...(config.metadata.grant ?? []), 'GM_addStyle', 'GM_info']);
88
- // prettier-ignore-end
89
- // source
90
- writeFileSync(filePath, file);
91
- // production
92
- writeFileSync(userFilePath, `${banner(config.metadata)}\n\n${file}`);
93
- // development
94
- writeFileSync(proxyFilePath, banner({
95
- ...config.metadata,
96
- require: [...config.metadata.require, 'file://' + filePath]
97
- }));
98
- }
99
- catch (err) {
100
- console.log(err);
101
- }
102
- }
122
+ }
123
+ inject() {
124
+ const styles = [...this.styles.values()].join("");
125
+ if (!styles)
126
+ return;
127
+ return `GM_addStyle(\`${styles}\`)`;
128
+ }
129
+ merge(modules) {
130
+ const styleModules = [];
131
+ for (const module of modules) {
132
+ const style = this.styles.get(module);
133
+ if (!style)
134
+ continue;
135
+ styleModules.push([module, style]);
136
+ }
137
+ this.styles.clear();
138
+ styleModules.forEach((value) => this.styles.set(...value));
139
+ }
140
+ };
141
+ var css_default = new CSS();
142
+
143
+ // src/index.ts
144
+ function UserscriptPlugin(config) {
145
+ var _a;
146
+ let pluginConfig;
147
+ let isBuildWatch;
148
+ let socketConnection = null;
149
+ const port = ((_a = config.server) == null ? void 0 : _a.port) || 8e3;
150
+ const httpServer = createServer();
151
+ httpServer.listen(port);
152
+ const WebSocketServer = server;
153
+ const ws = new WebSocketServer({ httpServer });
154
+ ws.on("request", (request) => {
155
+ socketConnection = request.accept(null, request.origin);
156
+ });
157
+ return {
158
+ name: "vite-userscript-plugin",
159
+ apply: "build",
160
+ config() {
161
+ return {
162
+ build: {
163
+ lib: {
164
+ entry: config.entry,
165
+ name: config.metadata.name,
166
+ formats: ["iife"],
167
+ fileName: () => `${config.metadata.name}.js`
168
+ },
169
+ rollupOptions: {
170
+ output: {
171
+ exports: "none",
172
+ extend: true
103
173
  }
104
- },
105
- buildEnd() {
106
- if (socketConnection) {
107
- socketConnection.sendUTF(JSON.stringify({
108
- message: 'reload'
109
- }));
174
+ }
175
+ }
176
+ };
177
+ },
178
+ configResolved(cfg) {
179
+ pluginConfig = cfg;
180
+ isBuildWatch = cfg.build.watch ?? false;
181
+ const { match, require: require2, include, exclude, resource, connect } = config.metadata;
182
+ config.metadata.match = removeDuplicates(match);
183
+ config.metadata.require = removeDuplicates(require2);
184
+ config.metadata.include = removeDuplicates(include);
185
+ config.metadata.exclude = removeDuplicates(exclude);
186
+ config.metadata.resource = removeDuplicates(resource);
187
+ config.metadata.connect = removeDuplicates(connect);
188
+ },
189
+ async transform(code, path) {
190
+ const style = await css_default.minify(code, path);
191
+ return css_default.add(config.entry, style.replace("\n", ""), path);
192
+ },
193
+ generateBundle(_, bundle) {
194
+ for (const [_2, file] of Object.entries(bundle)) {
195
+ const styleModules = Object.keys(
196
+ file.modules
197
+ );
198
+ css_default.merge(styleModules);
199
+ }
200
+ },
201
+ async writeBundle(_, bundle) {
202
+ for (const [fileName] of Object.entries(bundle)) {
203
+ if (regexpScripts.test(fileName)) {
204
+ const rootDir = pluginConfig.root;
205
+ const outDir = pluginConfig.build.outDir || "dist";
206
+ const outPath = resolve(rootDir, outDir, fileName);
207
+ const hotReloadPath = resolve(
208
+ dirname(fileURLToPath(import.meta.url)),
209
+ "__hot-reload__.js"
210
+ );
211
+ const proxyFilePath = resolve(
212
+ rootDir,
213
+ outDir,
214
+ `${config.metadata.name}.proxy.user.js`
215
+ );
216
+ const userFilePath = resolve(
217
+ rootDir,
218
+ outDir,
219
+ `${config.metadata.name}.user.js`
220
+ );
221
+ try {
222
+ let source = readFileSync(outPath, "utf8");
223
+ config.metadata.grant = removeDuplicates(
224
+ isBuildWatch ? grants : config.autoGrants ?? true ? defineGrants(source) : [...config.metadata.grant ?? [], "GM_addStyle", "GM_info"]
225
+ );
226
+ if (isBuildWatch) {
227
+ const hotReloadFile = readFileSync(
228
+ resolve(
229
+ dirname(fileURLToPath(import.meta.url)),
230
+ "hot-reload.js"
231
+ ),
232
+ "utf8"
233
+ );
234
+ const hotReloadScript = await transform({
235
+ file: hotReloadFile.replace("__PORT__", port.toString()),
236
+ name: hotReloadPath,
237
+ loader: "js"
238
+ });
239
+ writeFileSync(hotReloadPath, hotReloadScript);
240
+ writeFileSync(
241
+ proxyFilePath,
242
+ banner({
243
+ ...config.metadata,
244
+ require: [
245
+ ...config.metadata.require,
246
+ "file://" + hotReloadPath,
247
+ "file://" + outPath
248
+ ]
249
+ })
250
+ );
110
251
  }
252
+ source = source.replace(template, `${css_default.inject()}`);
253
+ source = await transform({
254
+ file: source,
255
+ name: fileName,
256
+ loader: "js"
257
+ });
258
+ writeFileSync(outPath, source);
259
+ writeFileSync(
260
+ userFilePath,
261
+ `${banner(config.metadata)}
262
+
263
+ ${source}`
264
+ );
265
+ } catch (err) {
266
+ console.log(err);
267
+ }
111
268
  }
112
- };
269
+ }
270
+ if (!isBuildWatch) {
271
+ httpServer.close();
272
+ process.exit(0);
273
+ }
274
+ },
275
+ buildEnd() {
276
+ if (socketConnection) {
277
+ socketConnection.sendUTF(
278
+ JSON.stringify({
279
+ message: "reload"
280
+ })
281
+ );
282
+ }
283
+ }
284
+ };
113
285
  }
114
- export { UserscriptPlugin };
115
- export default UserscriptPlugin;
286
+ var src_default = UserscriptPlugin;
287
+ export {
288
+ UserscriptPlugin,
289
+ src_default as default
290
+ };
package/package.json CHANGED
@@ -1,9 +1,14 @@
1
1
  {
2
2
  "name": "vite-userscript-plugin",
3
- "version": "0.3.0",
3
+ "version": "0.6.1",
4
4
  "type": "module",
5
5
  "types": "./dist/index.d.ts",
6
- "main": "./dist/index.js",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "exports": {
9
+ "require": "./dist/index.cjs",
10
+ "import": "./dist/index.js"
11
+ },
7
12
  "files": [
8
13
  "dist"
9
14
  ],
@@ -33,11 +38,10 @@
33
38
  "@types/websocket": "^1.0.5"
34
39
  },
35
40
  "peerDependencies": {
36
- "@types/tampermonkey": "latest"
41
+ "@types/tampermonkey": ">=4.0.0"
37
42
  },
38
43
  "scripts": {
39
- "dev": "tsc --build --watch",
40
- "build": "pnpm clean && tsc --build",
41
- "clean": "del-cli dist"
44
+ "dev": "tsup --watch",
45
+ "build": "tsup"
42
46
  }
43
47
  }
package/dist/banner.d.ts DELETED
@@ -1,2 +0,0 @@
1
- import type { MetadataConfig } from './types.js';
2
- export declare function banner(config: MetadataConfig): string;
package/dist/banner.js DELETED
@@ -1,30 +0,0 @@
1
- export function banner(config) {
2
- const metadata = [];
3
- const configKeys = Object.keys(config);
4
- const maxKeyLength = Math.max(...configKeys.map((key) => key.length)) + 1;
5
- const addSpaces = (str) => {
6
- return ' '.repeat(maxKeyLength - str.length);
7
- };
8
- const addMetadata = (key, value) => {
9
- const isBoolean = typeof value === 'boolean';
10
- if (isBoolean && !value)
11
- return;
12
- value = !isBoolean ? addSpaces(key) + value.toString() : '';
13
- metadata.push(`// @${key}${value}`);
14
- };
15
- for (const [key, value] of Object.entries(config)) {
16
- if (Array.isArray(value)) {
17
- value.forEach((value) => addMetadata(key, value));
18
- }
19
- else {
20
- if (value === undefined)
21
- continue;
22
- addMetadata(key, value);
23
- }
24
- }
25
- return [
26
- '// ==UserScript==',
27
- ...metadata,
28
- '// ==/UserScript=='
29
- ].join('\n');
30
- }
@@ -1,4 +0,0 @@
1
- export declare const regexpScripts: RegExp;
2
- export declare const regexpStyles: RegExp;
3
- export declare const template = "console.warn(\"__TEMPLATE__\")";
4
- export 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"];
package/dist/constants.js DELETED
@@ -1,32 +0,0 @@
1
- export const regexpScripts = new RegExp(/\.js/);
2
- export const regexpStyles = new RegExp(/\.css|\.sass|\.scss|\.less/);
3
- export const template = `console.warn("__TEMPLATE__")`;
4
- export const grants = [
5
- 'unsafeWindow',
6
- 'window.onurlchange',
7
- 'window.focus',
8
- 'window.close',
9
- 'GM_setValue',
10
- 'GM_getValue',
11
- 'GM_deleteValue',
12
- 'GM_listValues',
13
- 'GM_setClipboard',
14
- 'GM_addStyle',
15
- 'GM_addElement',
16
- 'GM_addValueChangeListener',
17
- 'GM_removeValueChangeListener',
18
- 'GM_registerMenuCommand',
19
- 'GM_unregisterMenuCommand',
20
- 'GM_download',
21
- 'GM_getTab',
22
- 'GM_getTabs',
23
- 'GM_saveTab',
24
- 'GM_openInTab',
25
- 'GM_notification',
26
- 'GM_getResourceURL',
27
- 'GM_getResourceText',
28
- 'GM_xmlhttpRequest',
29
- 'GM_webRequest',
30
- 'GM_log',
31
- 'GM_info'
32
- ];
package/dist/css.d.ts DELETED
@@ -1,11 +0,0 @@
1
- declare class CSS {
2
- private readonly styles;
3
- add(entry: string, code: string, path: string): {
4
- code: string;
5
- } | null;
6
- minify(file: string, name: string): Promise<string>;
7
- inject(): string | void;
8
- merge(modules: string[]): void;
9
- }
10
- declare const _default: CSS;
11
- export default _default;
package/dist/css.js DELETED
@@ -1,51 +0,0 @@
1
- import { regexpStyles, template } from './constants.js';
2
- import { transform } from './helpers.js';
3
- class CSS {
4
- constructor() {
5
- Object.defineProperty(this, "styles", {
6
- enumerable: true,
7
- configurable: true,
8
- writable: true,
9
- value: new Map()
10
- });
11
- }
12
- add(entry, code, path) {
13
- if (regexpStyles.test(path)) {
14
- this.styles.set(path, code);
15
- return {
16
- code: ''
17
- };
18
- }
19
- if (path.includes(entry)) {
20
- return {
21
- code: code + template
22
- };
23
- }
24
- return null;
25
- }
26
- async minify(file, name) {
27
- return await transform({
28
- file,
29
- name,
30
- loader: 'css'
31
- });
32
- }
33
- inject() {
34
- const styles = [...this.styles.values()].join('');
35
- if (!styles)
36
- return;
37
- return `GM_addStyle(\`${styles}\`)`;
38
- }
39
- merge(modules) {
40
- const styleModules = [];
41
- for (const module of modules) {
42
- const style = this.styles.get(module);
43
- if (!style)
44
- continue;
45
- styleModules.push([module, style]);
46
- }
47
- this.styles.clear();
48
- styleModules.forEach((value) => this.styles.set(...value));
49
- }
50
- }
51
- export default new CSS();
package/dist/helpers.d.ts DELETED
@@ -1,4 +0,0 @@
1
- import { Grants, Transform } from './types.js';
2
- export declare function removeDuplicates(arr: string | string[] | readonly string[] | undefined): any[];
3
- export declare function transform({ file, name, loader }: Transform): Promise<string>;
4
- export declare function defineGrants(code: string): Grants[];
package/dist/helpers.js DELETED
@@ -1,21 +0,0 @@
1
- import { transformWithEsbuild } from 'vite';
2
- import { grants } from './constants.js';
3
- export function removeDuplicates(arr) {
4
- return [...new Set(Array.isArray(arr) ? arr : arr ? [arr] : [])];
5
- }
6
- export async function transform({ file, name, loader }) {
7
- const { code } = await transformWithEsbuild(file, name, {
8
- loader,
9
- minify: true
10
- });
11
- return code;
12
- }
13
- export function defineGrants(code) {
14
- const definedGrants = [];
15
- for (const grant of grants) {
16
- if (code.indexOf(grant) !== -1) {
17
- definedGrants.push(grant);
18
- }
19
- }
20
- return definedGrants;
21
- }
package/dist/hmr.d.ts DELETED
@@ -1,2 +0,0 @@
1
- declare const port: number;
2
- declare const ws: WebSocket;
package/dist/hmr.js DELETED
@@ -1,11 +0,0 @@
1
- "use strict";
2
- const ws = new WebSocket(`ws://localhost:${port}`);
3
- ws.addEventListener('message', () => {
4
- location.reload();
5
- });
6
- ws.addEventListener('open', () => {
7
- const { script } = GM_info;
8
- console.group(`${script.name} / ${script.version}`);
9
- console.log(GM_info);
10
- console.groupEnd();
11
- });