crx-monkey-next 0.0.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.
Files changed (94) hide show
  1. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/dist/client/src/client/api.d.ts +3 -0
  2. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/dist/client/src/client/api.js +4 -0
  3. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/dist/client/src/client/api.js.map +1 -0
  4. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/dist/client/src/client/i18n.d.ts +17 -0
  5. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/dist/client/src/client/i18n.js +38 -0
  6. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/dist/client/src/client/i18n.js.map +1 -0
  7. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/dist/client/src/client/main.d.ts +16 -0
  8. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/dist/client/src/client/main.js +8 -0
  9. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/dist/client/src/client/main.js.map +1 -0
  10. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/dist/client/src/client/message.d.ts +30 -0
  11. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/dist/client/src/client/message.js +75 -0
  12. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/dist/client/src/client/message.js.map +1 -0
  13. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/dist/client/src/client/runtime.d.ts +43 -0
  14. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/dist/client/src/client/runtime.js +89 -0
  15. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/dist/client/src/client/runtime.js.map +1 -0
  16. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/dist/client/src/node/plugins/tsBundler.d.ts +28 -0
  17. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/dist/client/src/node/plugins/tsBundler.js +151 -0
  18. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/dist/client/src/node/plugins/tsBundler.js.map +1 -0
  19. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/dist/client/src/node/typeDefs.d.ts +200 -0
  20. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/dist/client/src/node/typeDefs.js +2 -0
  21. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/dist/client/src/node/typeDefs.js.map +1 -0
  22. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/dist/client/tsconfig.client.tsbuildinfo +1 -0
  23. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/src/client/api.d.ts +3 -0
  24. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/src/client/api.js +4 -0
  25. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/src/client/api.js.map +1 -0
  26. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/src/client/i18n.d.ts +17 -0
  27. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/src/client/i18n.js +38 -0
  28. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/src/client/i18n.js.map +1 -0
  29. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/src/client/main.d.ts +17 -0
  30. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/src/client/main.js +9 -0
  31. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/src/client/main.js.map +1 -0
  32. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/src/client/message.d.ts +30 -0
  33. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/src/client/message.js +75 -0
  34. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/src/client/message.js.map +1 -0
  35. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/src/client/runtime.d.ts +32 -0
  36. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/src/client/runtime.js +58 -0
  37. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/src/client/runtime.js.map +1 -0
  38. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/src/node/typeDefs.d.ts +189 -0
  39. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/src/node/typeDefs.js +2 -0
  40. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/src/node/typeDefs.js.map +1 -0
  41. package/.rollup.cache/home/yakisova41/crx-monkey-next/packages/crx-monkey/tsconfig.client.tsbuildinfo +1 -0
  42. package/README.md +34 -0
  43. package/bin/crx-monkey +2 -0
  44. package/bun.lockb +0 -0
  45. package/dist/client/main.d.ts +187 -0
  46. package/dist/client/main.js +200 -0
  47. package/dist/client/tsconfig.client.tsbuildinfo +1 -0
  48. package/dist/node/exports.d.ts +213 -0
  49. package/dist/node/exports.js +650 -0
  50. package/dist/node/main.js +2759 -0
  51. package/jest.config.js +17 -0
  52. package/package.json +81 -0
  53. package/rollup.config.js +101 -0
  54. package/src/client/i18n.ts +42 -0
  55. package/src/client/main.ts +20 -0
  56. package/src/client/message.ts +118 -0
  57. package/src/client/runtime.ts +109 -0
  58. package/src/node/BundlerRegisterer.ts +311 -0
  59. package/src/node/ConfigLoader.ts +127 -0
  60. package/src/node/CrxmBundler.ts +213 -0
  61. package/src/node/Distributior.ts +295 -0
  62. package/src/node/Logger.ts +77 -0
  63. package/src/node/Watcher.ts +166 -0
  64. package/src/node/__tests__/config.test.ts +157 -0
  65. package/src/node/__tests__/manifest.test.ts +380 -0
  66. package/src/node/build.ts +64 -0
  67. package/src/node/dev.ts +146 -0
  68. package/src/node/development/CreateDevClient.ts +168 -0
  69. package/src/node/development/codes/extension.ts +187 -0
  70. package/src/node/development/codes/sw.ts +41 -0
  71. package/src/node/development/codes/userjs.ts +150 -0
  72. package/src/node/exports.ts +135 -0
  73. package/src/node/file.ts +45 -0
  74. package/src/node/inversify.config.ts +53 -0
  75. package/src/node/main.ts +34 -0
  76. package/src/node/manifest/ManifestFactory.ts +165 -0
  77. package/src/node/manifest/ManifestLoader.ts +71 -0
  78. package/src/node/manifest/ManifestParser.ts +191 -0
  79. package/src/node/manifest/i18n.ts +93 -0
  80. package/src/node/plugins/htmlBundler/HTMLTools.ts +332 -0
  81. package/src/node/plugins/htmlBundler/main.ts +140 -0
  82. package/src/node/plugins/sassBundler.ts +59 -0
  83. package/src/node/plugins/tsBundler.ts +197 -0
  84. package/src/node/server/FileServer.ts +97 -0
  85. package/src/node/server/SockServer.ts +143 -0
  86. package/src/node/typeDefs.ts +221 -0
  87. package/src/node/types.ts +24 -0
  88. package/src/node/userscript/CodeInjector.ts +164 -0
  89. package/src/node/userscript/UserscriptBundler.ts +138 -0
  90. package/src/node/userscript/UserscriptHeader.ts +76 -0
  91. package/src/node/userscript/UserscriptRegisterer.ts +204 -0
  92. package/src/node/utils.ts +25 -0
  93. package/tsconfig.client.json +20 -0
  94. package/tsconfig.json +24 -0
@@ -0,0 +1,168 @@
1
+ import { inject, injectable } from 'inversify';
2
+ import { TYPES } from '../types';
3
+ import { ConfigLoader } from '../ConfigLoader';
4
+ import { developSw } from './codes/sw';
5
+ import { CrxmBundler } from '../CrxmBundler';
6
+ import { UserscriptHeaderFactory } from '../userscript/UserscriptHeader';
7
+ import { userjs } from './codes/userjs';
8
+ import { resolve } from 'path';
9
+ import { developmentContentScript, isolatedConnector } from './codes/extension';
10
+ import fsExtra, { outputFileSync } from 'fs-extra/esm';
11
+ import { ManifestFactory } from '../manifest/ManifestFactory';
12
+
13
+ @injectable()
14
+ export class CreateDevClient {
15
+ constructor(
16
+ @inject(TYPES.ConfigLoader) private readonly configLoader: ConfigLoader,
17
+ @inject(TYPES.CrxmBundler) private readonly bundler: CrxmBundler,
18
+ @inject(TYPES.UserscriptHeaderFactory) private readonly headerFactory: UserscriptHeaderFactory,
19
+ @inject(TYPES.ManifestFactory) private readonly manifestFactory: ManifestFactory,
20
+ @inject(TYPES.BuildID) private readonly buildId: string,
21
+ ) {}
22
+
23
+ /**
24
+ * Make code for service worker on developement.
25
+ */
26
+ public outputDevelomentSw(code: Uint8Array) {
27
+ const {
28
+ server: { host, websocket },
29
+ } = this.configLoader.useConfig();
30
+
31
+ if (host === undefined || websocket === undefined) {
32
+ throw new Error("Server host or websocket's port were not specificated");
33
+ }
34
+
35
+ const decoder = new TextDecoder('utf-8');
36
+ const originalCode = decoder.decode(code);
37
+
38
+ const devCode = stringifyFunction(developSw, [host, websocket]);
39
+
40
+ const result = devCode + '\n\n' + originalCode;
41
+
42
+ return result;
43
+ }
44
+
45
+ public outputDevelopmentUserjs() {
46
+ const {
47
+ server: { disable_sock_in_userjs, host, port, websocket },
48
+ } = this.configLoader.useConfig();
49
+
50
+ if (host === undefined || websocket === undefined || port === undefined) {
51
+ throw new Error("Server host, port or websocket's port were not specificated");
52
+ }
53
+
54
+ const disableSock = disable_sock_in_userjs === undefined ? false : disable_sock_in_userjs;
55
+
56
+ const newFactory = new UserscriptHeaderFactory(this.headerFactory);
57
+
58
+ // for loading code from server in development mode.
59
+ newFactory.push('@grant', 'GM_xmlhttpRequest');
60
+
61
+ const code = [
62
+ newFactory.toString(),
63
+ '',
64
+ stringifyFunction(userjs, [host, port, websocket, false, disableSock, this.buildId]),
65
+ ].join('\n');
66
+
67
+ return code;
68
+ }
69
+
70
+ public outputDevExtension() {
71
+ const {
72
+ output: { chrome },
73
+ server: { host, websocket },
74
+ } = this.configLoader.useConfig();
75
+
76
+ if (chrome === undefined || host === undefined || websocket === undefined) {
77
+ throw new Error('');
78
+ }
79
+
80
+ /**
81
+ * Development contentscript in isolated env
82
+ */
83
+ const devScriptIsolatedFileName = 'crxm-development.js';
84
+ const devScriptIsolatedPath = resolve(chrome, devScriptIsolatedFileName);
85
+
86
+ const devScriptIsolatedCode = stringifyFunction(developmentContentScript, [
87
+ this.buildId,
88
+ host,
89
+ websocket,
90
+ ]);
91
+
92
+ fsExtra.outputFileSync(devScriptIsolatedPath, devScriptIsolatedCode);
93
+
94
+ this.manifestFactory.addContentScript(
95
+ [devScriptIsolatedFileName],
96
+ [],
97
+ ['<all_urls>'],
98
+ 'ISOLATED',
99
+ );
100
+ }
101
+
102
+ /**
103
+ * Include isolated connector
104
+ */
105
+ public outputIsolatedConnector() {
106
+ const {
107
+ output: { chrome },
108
+ server: { host, websocket },
109
+ } = this.configLoader.useConfig();
110
+
111
+ if (chrome === undefined || host === undefined || websocket === undefined) {
112
+ throw new Error('');
113
+ }
114
+
115
+ let includeConnector = false;
116
+ const isolatedmatches: string[] = [];
117
+
118
+ this.manifestFactory.rawManifest.content_scripts.forEach(
119
+ ({ use_isolated_connection, matches }) => {
120
+ if (use_isolated_connection) {
121
+ isolatedmatches.push(...(matches !== undefined ? matches : []));
122
+ includeConnector = true;
123
+ }
124
+ },
125
+ );
126
+
127
+ if (includeConnector) {
128
+ const isoFileName = 'crxm-isolated-connector.js';
129
+ const isoConnectorPath = resolve(chrome, isoFileName);
130
+ outputFileSync(
131
+ isoConnectorPath,
132
+ `${stringifyFunction(isolatedConnector, [this.buildId, JSON.stringify(this.configLoader.useConfig())])}\n`,
133
+ );
134
+ this.manifestFactory.addContentScript(
135
+ [isoFileName],
136
+ [],
137
+ [...isolatedmatches],
138
+ 'ISOLATED',
139
+ 'document_start',
140
+ );
141
+ }
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Stringify a function that binded args.
147
+ * @param target function
148
+ * @param args args
149
+ * @returns A function stringified.
150
+ */
151
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
152
+ export function stringifyFunction<V extends (...args: any[]) => any>(
153
+ target: V,
154
+ args: Parameters<V>,
155
+ ): string {
156
+ const raw = target.toString();
157
+
158
+ const newArgs = args.map((arg) => {
159
+ if (typeof arg === 'string') {
160
+ return `'${arg}'`;
161
+ } else {
162
+ return String(arg);
163
+ }
164
+ });
165
+
166
+ const code = `(${raw})(${newArgs.join(', ')});`;
167
+ return code;
168
+ }
@@ -0,0 +1,187 @@
1
+ import {
2
+ SockServerConsoleRecieved,
3
+ SockServerResponse,
4
+ SockServerResponseReload,
5
+ } from 'src/node/server/SockServer';
6
+
7
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-constraint, @typescript-eslint/no-explicit-any
8
+ export interface IsolatedConnectorEvent<T extends any = any> {
9
+ type: string;
10
+ crxContentBuildId: string;
11
+ detail: T;
12
+ actionId: string;
13
+ }
14
+
15
+ export function developmentContentScript(
16
+ crxContentBuildId: string,
17
+ host: string,
18
+ websocketPort: number,
19
+ ) {
20
+ const websocket = new WebSocket(`ws://${host}:${websocketPort}`);
21
+
22
+ websocket.addEventListener('open', () => {
23
+ console.log('[crxm] A reload server connected..');
24
+ });
25
+
26
+ websocket.addEventListener('close', () => {
27
+ console.log('[crxm] A reload server disconnected..');
28
+ });
29
+
30
+ websocket.addEventListener('message', ({ data }) => {
31
+ const response = JSON.parse(data) as SockServerResponse<SockServerResponseReload>;
32
+
33
+ if (response.type === 'reload') {
34
+ switch (response.content) {
35
+ case 'RELOAD_CSS':
36
+ case 'RELOAD_CONTENT_SCRIPT':
37
+ location.reload();
38
+ break;
39
+ default:
40
+ break;
41
+ }
42
+ }
43
+ });
44
+
45
+ window.addEventListener('message', (e: MessageEvent<IsolatedConnectorEvent>) => {
46
+ const { data } = e;
47
+
48
+ /**
49
+ * Client can listen a result by using of this.
50
+ */
51
+ const handlers: Record<string, () => void> = {
52
+ 'console-log': () => {
53
+ const preload: SockServerConsoleRecieved = {
54
+ type: 'console',
55
+ content: {
56
+ type: 'log',
57
+ contents: data.detail,
58
+ },
59
+ };
60
+ websocket.send(JSON.stringify(preload));
61
+ },
62
+ 'console-error': () => {
63
+ const preload: SockServerConsoleRecieved = {
64
+ type: 'console',
65
+ content: {
66
+ type: 'error',
67
+ contents: data.detail,
68
+ },
69
+ };
70
+ websocket.send(JSON.stringify(preload));
71
+ },
72
+ 'console-warn': () => {
73
+ const preload: SockServerConsoleRecieved = {
74
+ type: 'console',
75
+ content: {
76
+ type: 'warn',
77
+ contents: data.detail,
78
+ },
79
+ };
80
+ websocket.send(JSON.stringify(preload));
81
+ },
82
+ };
83
+
84
+ /**
85
+ * # BuildId Check
86
+ * If a build id of recieved message isn't equiavalent with a buildId of isolatedConnector,
87
+ * wouldn't be runned the handler so It can prevent being runned from other scripts.
88
+ */
89
+ if (data.crxContentBuildId === crxContentBuildId) {
90
+ const handler = handlers[data.type];
91
+ if (handler !== undefined) {
92
+ handler();
93
+ }
94
+ }
95
+ });
96
+ }
97
+
98
+ export function isolatedConnector(crxContentBuildId: string, config: string) {
99
+ const messageListeners: Record<
100
+ string,
101
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
102
+ (request: any, sender: chrome.runtime.MessageSender) => void
103
+ > = {};
104
+
105
+ /**
106
+ * How it works?
107
+ *
108
+ * 1. A client (e.g. contentscripts.ts in MAIN world) : dispatch message to window.
109
+ * 2. This EventListener recieved.
110
+ * 3. This EventListener dispatchs CustomEvent that 'crx-isolated-connector-result' to window.
111
+ * 4. A client recieved CustomEvent as result.
112
+ */
113
+ window.addEventListener('message', (e: MessageEvent<IsolatedConnectorEvent>) => {
114
+ const { data, target } = e;
115
+
116
+ /**
117
+ * Send event of response to target.
118
+ * @param name custom event name.
119
+ * @param type custom event type.
120
+ * @param detail custom event detail.
121
+ */
122
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-constraint, @typescript-eslint/no-explicit-any
123
+ function dispatch<T extends any = any>(name: string, type: string, detail: T) {
124
+ if (target !== null) {
125
+ target.dispatchEvent(
126
+ new CustomEvent(name, {
127
+ detail: {
128
+ type,
129
+ data: detail,
130
+ actionId: data.actionId,
131
+ },
132
+ }),
133
+ );
134
+ }
135
+ }
136
+
137
+ /**
138
+ * Client can listen a result by using of this.
139
+ */
140
+ const responseEventName = 'crx-isolated-connector-result';
141
+
142
+ const handlers: Record<string, () => void> = {
143
+ 'get-id': () => {
144
+ // Send extension runtime id.
145
+ dispatch(responseEventName, 'get-id', chrome.runtime.id);
146
+ },
147
+ 'get-conf': () => {
148
+ // Send crx-monkey config stringified.
149
+ dispatch(responseEventName, 'get-conf', config);
150
+ },
151
+ 'on-message': () => {
152
+ // Append a message listener from client.
153
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
154
+ const handleMessage = (request: any, sender: chrome.runtime.MessageSender) => {
155
+ dispatch(responseEventName, 'on-message', { request, sender });
156
+ };
157
+ messageListeners[data.actionId] = handleMessage;
158
+ chrome.runtime.onMessage.addListener(handleMessage);
159
+ },
160
+ 'remove-on-message': () => {
161
+ // Remove a message listener from client.
162
+ if (messageListeners[data.actionId] !== undefined) {
163
+ chrome.runtime.onMessage.removeListener(messageListeners[data.actionId]);
164
+ }
165
+ },
166
+ 'send-message': () => {
167
+ // Send a message from client.
168
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
169
+ chrome.runtime.sendMessage(data.detail.message, data.detail.options, (response: any) => {
170
+ dispatch(responseEventName, 'send-message', { response });
171
+ });
172
+ },
173
+ };
174
+
175
+ /**
176
+ * # BuildId Check
177
+ * If a build id of recieved message isn't equiavalent with a buildId of isolatedConnector,
178
+ * wouldn't be runned the handler so It can prevent being runned from other scripts.
179
+ */
180
+ if (data.crxContentBuildId === crxContentBuildId) {
181
+ const handler = handlers[data.type];
182
+ if (handler !== undefined) {
183
+ handler();
184
+ }
185
+ }
186
+ });
187
+ }
@@ -0,0 +1,41 @@
1
+ import { SockServerResponse, SockServerResponseReload } from 'src/node/server/SockServer';
2
+
3
+ /**
4
+ * A content to be convined with service_worker of development script generated by crx-monkey.
5
+ * @param host SockServer host
6
+ * @param sockPort SockServer port
7
+ */
8
+ export function developSw(host: string, sockPort: number) {
9
+ function startWebsocket25secLife() {
10
+ const websocket = new WebSocket(`ws://${host}:${sockPort}`);
11
+
12
+ websocket.addEventListener('message', ({ data }) => {
13
+ const response = JSON.parse(data) as SockServerResponse<SockServerResponseReload>;
14
+
15
+ if (response.type === 'reload') {
16
+ switch (response.content) {
17
+ case 'RELOAD_CONTENT_SCRIPT':
18
+ case 'RELOAD_SW':
19
+ case 'RELOAD_CSS':
20
+ case 'RELOAD_POPUP_JS':
21
+ case 'RELOAD_POPUP_HTML':
22
+ case 'ALL':
23
+ chrome.runtime.reload();
24
+ break;
25
+
26
+ default:
27
+ break;
28
+ }
29
+ }
30
+ });
31
+ setTimeout(() => {
32
+ websocket.close();
33
+ }, 25 * 1000);
34
+ }
35
+
36
+ // Keep a service_worker life.
37
+ startWebsocket25secLife();
38
+ setInterval(() => {
39
+ startWebsocket25secLife();
40
+ }, 25 * 1000);
41
+ }
@@ -0,0 +1,150 @@
1
+ import { SockServerConsoleRecieved } from 'src/node/server/SockServer';
2
+ import { IsolatedConnectorEvent } from './extension';
3
+
4
+ export function userjs(
5
+ host: string,
6
+ port: number,
7
+ websocketPort: number,
8
+ bindGM: boolean,
9
+ disableSock: boolean,
10
+ buildId: string,
11
+ ) {
12
+ unsafeWindow.__CRX_CONTENT_BUILD_ID = buildId;
13
+
14
+ if (!disableSock) {
15
+ const websocket = new WebSocket(`ws://${host}:${websocketPort}`);
16
+
17
+ websocket.addEventListener('message', ({ data }) => {
18
+ const { type, content } = JSON.parse(data);
19
+
20
+ if (type === 'reload') {
21
+ switch (content) {
22
+ case 'RELOAD_CSS':
23
+ case 'RELOAD_CONTENT_SCRIPT':
24
+ console.log('[crxm] reloading...');
25
+ location.reload();
26
+
27
+ break;
28
+
29
+ default:
30
+ break;
31
+ }
32
+ }
33
+ });
34
+
35
+ websocket.addEventListener('open', () => {
36
+ console.log('[crxm] A reload server connected..');
37
+
38
+ window.addEventListener('message', (e: MessageEvent<IsolatedConnectorEvent>) => {
39
+ const { data } = e;
40
+ /**
41
+ * Client can listen a result by using of this.
42
+ */
43
+ const handlers: Record<string, () => void> = {
44
+ 'console-log': () => {
45
+ const preload: SockServerConsoleRecieved = {
46
+ type: 'console',
47
+ content: {
48
+ type: 'log',
49
+ contents: data.detail,
50
+ },
51
+ };
52
+ websocket.send(JSON.stringify(preload));
53
+ },
54
+ 'console-error': () => {
55
+ const preload: SockServerConsoleRecieved = {
56
+ type: 'console',
57
+ content: {
58
+ type: 'error',
59
+ contents: data.detail,
60
+ },
61
+ };
62
+ websocket.send(JSON.stringify(preload));
63
+ },
64
+ 'console-warn': () => {
65
+ const preload: SockServerConsoleRecieved = {
66
+ type: 'console',
67
+ content: {
68
+ type: 'warn',
69
+ contents: data.detail,
70
+ },
71
+ };
72
+ websocket.send(JSON.stringify(preload));
73
+ },
74
+ };
75
+
76
+ /**
77
+ * # BuildId Check
78
+ * If a build id of recieved message isn't equiavalent with a buildId of isolatedConnector,
79
+ * wouldn't be runned the handler so It can prevent being runned from other scripts.
80
+ */
81
+ if (data.crxContentBuildId === buildId) {
82
+ const handler = handlers[data.type];
83
+ if (handler !== undefined) {
84
+ handler();
85
+ }
86
+ }
87
+ });
88
+ });
89
+
90
+ websocket.addEventListener('close', () => {
91
+ console.log('[crxm] A reload server disconnected..');
92
+ });
93
+ }
94
+
95
+ function watchScriptDiff(initialCode: string) {
96
+ const scriptContentTmp = initialCode;
97
+
98
+ setInterval(() => {
99
+ getResponse().then((code) => {
100
+ if (scriptContentTmp !== code) {
101
+ location.reload();
102
+ }
103
+ });
104
+ }, 1000);
105
+
106
+ return scriptContentTmp;
107
+ }
108
+
109
+ async function getResponse() {
110
+ return new Promise((resolve) => {
111
+ GM.xmlHttpRequest({
112
+ url: `http://${host}:${port}/userscript`,
113
+ onload: (e) => {
114
+ resolve(e.responseText);
115
+ },
116
+ });
117
+ });
118
+ }
119
+
120
+ let loaded = false;
121
+ document.addEventListener('DOMContentLoaded', () => {
122
+ loaded = true;
123
+ });
124
+
125
+ getResponse().then((code) => {
126
+ if (typeof code === 'string') {
127
+ if (disableSock) {
128
+ watchScriptDiff(code);
129
+ }
130
+
131
+ const injectCode = code;
132
+
133
+ /*
134
+ if (bindGM) {
135
+ const bindGMverName = btoa(crypto.randomUUID()).replaceAll('=', '$');
136
+ unsafeWindow[bindGMverName] = GM;
137
+ injectCode = `const ${bindGMHash} = window["${bindGMverName}"];` + injectCode;
138
+ }
139
+ */
140
+
141
+ const scriptElem = document.createElement('script');
142
+ scriptElem.textContent = injectCode;
143
+ unsafeWindow.document.body.appendChild(scriptElem);
144
+
145
+ if (loaded) {
146
+ document.dispatchEvent(new Event('crxm_DOMContentLoaded'));
147
+ }
148
+ }
149
+ });
150
+ }
@@ -0,0 +1,135 @@
1
+ import {
2
+ CrxmConfig,
3
+ CrxmConfigRequired,
4
+ CrxmManifest,
5
+ CrxmManifestImportantKeyRequired,
6
+ } from './typeDefs';
7
+ import { tsBundler, tsBundlerWatch } from './plugins/tsBundler';
8
+ import { sassBundler, sassBundlerWatch } from './plugins/sassBundler';
9
+ import { htmlBundler, htmlBundlerWatch } from './plugins/htmlBundler/main';
10
+
11
+ /**
12
+ * Define config for crx monkey.
13
+ * @param userConfig
14
+ * @returns
15
+ */
16
+ export function defineConfig(userConfig: CrxmConfig): CrxmConfigRequired {
17
+ const defaultConfig: CrxmConfigRequired = {
18
+ output: {
19
+ chrome: 'dist/chrome',
20
+ userjs: 'dist/bundle.user.js',
21
+ },
22
+ manifest: 'manifest.js',
23
+ server: {
24
+ port: 8080,
25
+ host: 'localhost',
26
+ websocket: 8081,
27
+ disable_sock_in_userjs: false,
28
+ },
29
+ header: [],
30
+ build: {
31
+ '^.*.(ts|js|tsx|jsx)$': tsBundler({
32
+ esbuild: {
33
+ minify: true,
34
+ },
35
+ }),
36
+ '^.*.(css|sass|scss)$': sassBundler(),
37
+ '^.*.(html|htm)$': htmlBundler({
38
+ output: 'dist/chrome',
39
+ plugins: {
40
+ build: { '^.*.(ts|js|tsx|jsx)$': tsBundler(), '^.*.(css|sass|scss)$': sassBundler() },
41
+ watch: {
42
+ '^.*.(ts|js|tsx|jsx)$': tsBundlerWatch(),
43
+ '^.*.(css|sass|scss)$': sassBundlerWatch(),
44
+ },
45
+ },
46
+ }),
47
+ },
48
+ watch: {
49
+ '^.*.(ts|js|tsx|jsx)$': tsBundlerWatch({
50
+ esbuild: {
51
+ sourcemap: 'inline',
52
+ },
53
+ }),
54
+ '^.*.(css|sass|scss)$': sassBundlerWatch(),
55
+ '^.*.(html|htm)$': htmlBundlerWatch({
56
+ output: 'dist/chrome',
57
+ plugins: {
58
+ build: { '^.*.(ts|js|tsx|jsx)$': tsBundler(), '^.*.(css|sass|scss)$': sassBundler() },
59
+ watch: {
60
+ '^.*.(ts|js|tsx|jsx)$': tsBundlerWatch(),
61
+ '^.*.(css|sass|scss)$': sassBundlerWatch(),
62
+ },
63
+ },
64
+ }),
65
+ },
66
+ logLevel: 'error',
67
+ public: '/public',
68
+ define: {
69
+ sw: {},
70
+ contentscripts: {},
71
+ popup: {},
72
+ },
73
+ };
74
+
75
+ return makeDefineFunc(defaultConfig, userConfig);
76
+ }
77
+
78
+ /**
79
+ * Define manifest
80
+ * @param userManifest
81
+ * @returns
82
+ */
83
+ export function defineManifest(userManifest: CrxmManifest): CrxmManifestImportantKeyRequired {
84
+ const defaultManifest: CrxmManifestImportantKeyRequired = {
85
+ description: '',
86
+ manifest_version: 3,
87
+ background: undefined,
88
+ content_scripts: [],
89
+ name: '',
90
+ version: '',
91
+ action: undefined,
92
+ icons: undefined,
93
+ };
94
+
95
+ return makeDefineFunc(defaultManifest, userManifest);
96
+ }
97
+
98
+ function makeDefineFunc<T, U>(defaultConfig: T, userConfig: U) {
99
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
100
+ function isObject(item: any): item is Record<string, any> {
101
+ // null もオブジェクトと判定されるのを防ぐ
102
+ return (
103
+ item &&
104
+ typeof item === 'object' &&
105
+ !Array.isArray(item) &&
106
+ item !== null &&
107
+ !Array.isArray(item) &&
108
+ item !== undefined
109
+ );
110
+ }
111
+
112
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
113
+ function mergeDeep(target: any, source: any): any {
114
+ const output = { ...target };
115
+
116
+ if ((isObject(target) && isObject(source)) || (target === undefined && isObject(source))) {
117
+ Object.keys(source).forEach((key) => {
118
+ if (isObject(source[key])) {
119
+ if (!(key in target)) {
120
+ Object.assign(output, { [key]: source[key] });
121
+ } else {
122
+ output[key] = mergeDeep(target[key], source[key]);
123
+ }
124
+ } else {
125
+ Object.assign(output, { [key]: source[key] });
126
+ }
127
+ });
128
+ }
129
+
130
+ return output;
131
+ }
132
+
133
+ const mergedConfig = mergeDeep(defaultConfig, userConfig);
134
+ return mergedConfig as T;
135
+ }