addonova 1.0.2 → 1.0.3
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 +104 -48
- package/bin/addonova.js +4 -0
- package/bin/index.js +1 -60
- package/package.json +18 -28
- package/src/build/browser.js +143 -0
- package/{workspace/tasks → src/build}/build.js +16 -2
- package/{workspace/tasks → src/build}/cli.js +4 -2
- package/src/cli/help.js +15 -0
- package/src/cli/index.js +54 -0
- package/src/commands/init.js +14 -18
- package/src/index.js +0 -1
- package/{workspace → src}/tools/translate.js +1 -1
- package/templates/extension/package.json.tpl +35 -0
- package/src/commands/update.js +0 -67
- package/template/package.json.tpl +0 -54
- /package/{workspace/tasks → src/build}/bundle-css.js +0 -0
- /package/{workspace/tasks → src/build}/bundle-html.js +0 -0
- /package/{workspace/tasks → src/build}/bundle-js.js +0 -0
- /package/{workspace/tasks → src/build}/bundle-locales.js +0 -0
- /package/{workspace/tasks → src/build}/bundle-manifest.js +0 -0
- /package/{workspace/tasks → src/build}/copy.js +0 -0
- /package/{workspace/tasks → src/build}/folder.js +0 -0
- /package/{workspace/tasks → src/build}/paths.js +0 -0
- /package/{workspace/tasks → src/build}/task.js +0 -0
- /package/{workspace/tasks → src/build}/utils.js +0 -0
- /package/{workspace/tasks → src/build}/watch.js +0 -0
- /package/{workspace/tasks → src/build}/zip.js +0 -0
- /package/{workspace → src}/tools/json2i18n.js +0 -0
- /package/{template → templates/extension}/config/chrome.js +0 -0
- /package/{template → templates/extension}/config/edge.js +0 -0
- /package/{template → templates/extension}/config/firefox.js +0 -0
- /package/{template → templates/extension}/config/naver.js +0 -0
- /package/{template → templates/extension}/config/opera.js +0 -0
- /package/{template → templates/extension}/config/thunderbird.js +0 -0
- /package/{template → templates/extension}/platform/chrome/platform.js +0 -0
- /package/{template → templates/extension}/platform/edge/platform.js +0 -0
- /package/{template → templates/extension}/platform/naver/platform.js +0 -0
- /package/{template → templates/extension}/platform/opera/platform.js +0 -0
- /package/{template → templates/extension}/src/_locales/en.i18n +0 -0
- /package/{template → templates/extension}/src/assets/icons/128.png +0 -0
- /package/{template → templates/extension}/src/assets/icons/32.png +0 -0
- /package/{template → templates/extension}/src/assets/icons/48.png +0 -0
- /package/{template → templates/extension}/src/assets/icons/64.png +0 -0
- /package/{template → templates/extension}/src/css/popup.css +0 -0
- /package/{template → templates/extension}/src/html/popup.html +0 -0
- /package/{template → templates/extension}/src/js/background.js +0 -0
- /package/{template → templates/extension}/src/js/lib/browser.js +0 -0
- /package/{template → templates/extension}/src/js/lib/common.js +0 -0
- /package/{template → templates/extension}/src/js/lib/config.js +0 -0
- /package/{template → templates/extension}/src/js/lib/runtime.js +0 -0
- /package/{template → templates/extension}/src/js/popup.js +0 -0
- /package/{template → templates/extension}/src/manifest/manifest-firefox.json +0 -0
- /package/{template → templates/extension}/src/manifest/manifest.json +0 -0
package/README.md
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
# addonova
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
WebExtension framework for scaffolding and building cross-browser browser extensions.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
## Usage
|
|
5
|
+
## Quick Start
|
|
8
6
|
|
|
9
7
|
```bash
|
|
10
8
|
npx addonova init my-extension
|
|
@@ -21,75 +19,133 @@ npm run release
|
|
|
21
19
|
- Naver Whale
|
|
22
20
|
- Thunderbird
|
|
23
21
|
|
|
24
|
-
## Commands
|
|
22
|
+
## CLI Commands
|
|
25
23
|
|
|
26
24
|
| Command | Description |
|
|
27
|
-
|
|
28
|
-
| `init <name>` | Scaffold a new extension project |
|
|
29
|
-
| `
|
|
30
|
-
| `
|
|
31
|
-
| `
|
|
32
|
-
|
|
25
|
+
| --- | --- |
|
|
26
|
+
| `addonova init <name>` | Scaffold a new extension project |
|
|
27
|
+
| `addonova build [options]` | Build the current extension project |
|
|
28
|
+
| `addonova zip` | Create release zip bundles |
|
|
29
|
+
| `addonova --help` | Show help |
|
|
30
|
+
|
|
31
|
+
## Build Scripts
|
|
33
32
|
|
|
34
|
-
|
|
33
|
+
Generated projects include these npm scripts:
|
|
35
34
|
|
|
36
35
|
```bash
|
|
37
|
-
npm run release
|
|
38
|
-
npm run release:chrome
|
|
39
|
-
npm run release:
|
|
40
|
-
npm run
|
|
41
|
-
npm run
|
|
42
|
-
npm run
|
|
43
|
-
npm run
|
|
44
|
-
|
|
45
|
-
|
|
36
|
+
npm run release
|
|
37
|
+
npm run release:chrome
|
|
38
|
+
npm run release:edge
|
|
39
|
+
npm run release:opera
|
|
40
|
+
npm run release:firefox
|
|
41
|
+
npm run release:thunderbird
|
|
42
|
+
npm run release:naver
|
|
43
|
+
|
|
44
|
+
npm run debug
|
|
45
|
+
npm run debug:chrome
|
|
46
|
+
npm run debug:edge
|
|
47
|
+
npm run debug:opera
|
|
48
|
+
npm run debug:firefox
|
|
49
|
+
npm run debug:thunderbird
|
|
50
|
+
npm run debug:naver
|
|
51
|
+
|
|
52
|
+
npm run dev
|
|
53
|
+
npm run watch
|
|
54
|
+
npm run zip
|
|
55
|
+
npm test
|
|
46
56
|
```
|
|
47
57
|
|
|
48
|
-
|
|
58
|
+
You can also call the CLI directly:
|
|
49
59
|
|
|
60
|
+
```bash
|
|
61
|
+
npx addonova build --all --release
|
|
62
|
+
npx addonova build --chrome --debug
|
|
63
|
+
npx addonova build --all --debug --watch
|
|
64
|
+
npx addonova zip
|
|
50
65
|
```
|
|
66
|
+
|
|
67
|
+
## Build Options
|
|
68
|
+
|
|
69
|
+
| Option | Description |
|
|
70
|
+
| --- | --- |
|
|
71
|
+
| `--all` | Build all configured browsers |
|
|
72
|
+
| `--chrome` | Build Chrome target |
|
|
73
|
+
| `--edge` | Build Microsoft Edge target |
|
|
74
|
+
| `--firefox` | Build Firefox target |
|
|
75
|
+
| `--opera` | Build Opera target |
|
|
76
|
+
| `--naver` | Build Naver Whale target |
|
|
77
|
+
| `--thunderbird` | Build Thunderbird target |
|
|
78
|
+
| `--release` | Create release build |
|
|
79
|
+
| `--debug` | Create debug build |
|
|
80
|
+
| `--watch` | Rebuild when files change and reload opened extensions |
|
|
81
|
+
| `--open` | Open a browser with the debug extension loaded |
|
|
82
|
+
| `--test` | Build test version |
|
|
83
|
+
| `--version=x.x.x` | Append version to output zip names |
|
|
84
|
+
|
|
85
|
+
## Dev Mode
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
npm run dev
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Dev mode runs:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
addonova build --all --debug --watch
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Addonova opens an isolated browser profile, loads the unpacked debug extension from `.output/debug/<browser>`, watches source files, rebuilds changed assets, and reloads the extension when the built files change.
|
|
98
|
+
|
|
99
|
+
## Generated Extension Structure
|
|
100
|
+
|
|
101
|
+
```txt
|
|
51
102
|
my-extension/
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
│ ├── css/ # Stylesheets
|
|
64
|
-
│ ├── html/ # HTML pages
|
|
65
|
-
│ ├── js/ # JavaScript source
|
|
66
|
-
│ │ ├── background.js
|
|
67
|
-
│ │ ├── popup.js
|
|
68
|
-
│ │ └── lib/ # Shared runtime libs
|
|
69
|
-
│ └── manifest/ # Manifest JSON templates
|
|
70
|
-
├── .output/ # Build output
|
|
71
|
-
│ ├── release/ # Release builds
|
|
72
|
-
│ └── debug/ # Debug builds
|
|
73
|
-
└── package.json
|
|
103
|
+
|-- config/
|
|
104
|
+
|-- platform/
|
|
105
|
+
|-- src/
|
|
106
|
+
| |-- _locales/
|
|
107
|
+
| |-- assets/
|
|
108
|
+
| |-- css/
|
|
109
|
+
| |-- html/
|
|
110
|
+
| |-- js/
|
|
111
|
+
| `-- manifest/
|
|
112
|
+
|-- .output/
|
|
113
|
+
`-- package.json
|
|
74
114
|
```
|
|
75
115
|
|
|
76
116
|
## Locales / i18n
|
|
77
117
|
|
|
78
118
|
Locale files use the `.i18n` format:
|
|
79
119
|
|
|
80
|
-
```
|
|
120
|
+
```txt
|
|
81
121
|
@extensionName
|
|
82
122
|
My Extension
|
|
123
|
+
|
|
83
124
|
@extensionDescription
|
|
84
125
|
This is my extension description.
|
|
85
126
|
```
|
|
86
127
|
|
|
87
|
-
Run the interactive message manager:
|
|
128
|
+
Run the interactive message manager from a generated project:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
node node_modules/addonova/src/tools/translate.js
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Development
|
|
135
|
+
|
|
136
|
+
Run the test suite:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
npm test
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Check what will be published:
|
|
88
143
|
|
|
89
144
|
```bash
|
|
90
|
-
|
|
145
|
+
npm pack --dry-run
|
|
91
146
|
```
|
|
92
147
|
|
|
93
148
|
## Requirements
|
|
94
149
|
|
|
95
|
-
- Node.js >=
|
|
150
|
+
- Addonova package: Node.js >= 20.19
|
|
151
|
+
- Generated extension template: Node.js >= 22 and npm >= 11
|
package/bin/addonova.js
ADDED
package/bin/index.js
CHANGED
|
@@ -1,61 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
3
|
-
import process from 'node:process';
|
|
4
|
-
import { dirname, resolve } from 'node:path';
|
|
5
|
-
import { fileURLToPath } from 'node:url';
|
|
6
|
-
import { existsSync } from 'node:fs';
|
|
7
|
-
|
|
8
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
|
-
import { init } from '../src/commands/init.js';
|
|
10
|
-
import { update } from '../src/commands/update.js';
|
|
11
|
-
|
|
12
|
-
const [command, ...args] = process.argv.slice(2);
|
|
13
|
-
|
|
14
|
-
switch (command) {
|
|
15
|
-
case 'init':
|
|
16
|
-
await init(args);
|
|
17
|
-
break;
|
|
18
|
-
case 'update':
|
|
19
|
-
await update();
|
|
20
|
-
break;
|
|
21
|
-
case 'build':
|
|
22
|
-
case 'zip': {
|
|
23
|
-
const cliPath = resolve(__dirname, '../workspace/tasks/cli.js');
|
|
24
|
-
if (!existsSync(cliPath)) {
|
|
25
|
-
console.error('[*] Addonova workspace not found. Reinstall the package.');
|
|
26
|
-
process.exit(1);
|
|
27
|
-
}
|
|
28
|
-
const child = fork(cliPath, [command, ...args], {
|
|
29
|
-
execArgv: ['--max-old-space-size=3072']
|
|
30
|
-
});
|
|
31
|
-
process.on('SIGINT', () => {
|
|
32
|
-
child.kill('SIGKILL');
|
|
33
|
-
process.exit(130);
|
|
34
|
-
});
|
|
35
|
-
await new Promise((resolve, reject) =>
|
|
36
|
-
child.on('error', reject).on('close', (code) => {
|
|
37
|
-
if (code !== 0) process.exit(code);
|
|
38
|
-
resolve();
|
|
39
|
-
})
|
|
40
|
-
);
|
|
41
|
-
break;
|
|
42
|
-
}
|
|
43
|
-
case '--help':
|
|
44
|
-
case '-h':
|
|
45
|
-
default:
|
|
46
|
-
console.log(`
|
|
47
|
-
Addonova — browser extension toolkit
|
|
48
|
-
|
|
49
|
-
Usage:
|
|
50
|
-
npx addonova init <my-extension> Scaffold a new extension project
|
|
51
|
-
npx addonova update Update workspace/build files in existing project
|
|
52
|
-
npx addonova build [options] Build the extension
|
|
53
|
-
npx addonova zip Create zip bundles
|
|
54
|
-
npx addonova --help Show this help
|
|
55
|
-
|
|
56
|
-
Build options:
|
|
57
|
-
--all, --chrome, --firefox, --edge, --opera, --naver, --thunderbird
|
|
58
|
-
--release, --debug, --watch, --test, --version=x.x.x
|
|
59
|
-
`);
|
|
60
|
-
break;
|
|
61
|
-
}
|
|
2
|
+
import './addonova.js';
|
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "addonova",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Web Extension Framework",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "node --test"
|
|
8
|
+
},
|
|
6
9
|
"bin": {
|
|
7
|
-
"addonova": "./bin/
|
|
10
|
+
"addonova": "./bin/addonova.js"
|
|
8
11
|
},
|
|
9
12
|
"exports": {
|
|
10
13
|
".": "./src/index.js",
|
|
@@ -13,40 +16,27 @@
|
|
|
13
16
|
"files": [
|
|
14
17
|
"bin/",
|
|
15
18
|
"src/",
|
|
16
|
-
"
|
|
17
|
-
"template/"
|
|
19
|
+
"templates/"
|
|
18
20
|
],
|
|
19
|
-
"scripts": {
|
|
20
|
-
"addonova-release": "node --max-old-space-size=3072 workspace/tasks/cli.js build --all --release",
|
|
21
|
-
"addonova-release:chrome": "node --max-old-space-size=3072 workspace/tasks/cli.js build --chrome --release",
|
|
22
|
-
"addonova-release:edge": "node --max-old-space-size=3072 workspace/tasks/cli.js build --edge --release",
|
|
23
|
-
"addonova-release:opera": "node --max-old-space-size=3072 workspace/tasks/cli.js build --opera --release",
|
|
24
|
-
"addonova-release:firefox": "node --max-old-space-size=3072 workspace/tasks/cli.js build --firefox --release",
|
|
25
|
-
"addonova-release:thunderbird": "node --max-old-space-size=3072 workspace/tasks/cli.js build --thunderbird --release",
|
|
26
|
-
"addonova-release:naver": "node --max-old-space-size=3072 workspace/tasks/cli.js build --naver --release",
|
|
27
|
-
"addonova-debug": "node --max-old-space-size=3072 workspace/tasks/cli.js build --all --debug",
|
|
28
|
-
"addonova-debug:chrome": "node --max-old-space-size=3072 workspace/tasks/cli.js build --chrome --debug",
|
|
29
|
-
"addonova-debug:edge": "node --max-old-space-size=3072 workspace/tasks/cli.js build --edge --debug",
|
|
30
|
-
"addonova-debug:opera": "node --max-old-space-size=3072 workspace/tasks/cli.js build --opera --debug",
|
|
31
|
-
"addonova-debug:firefox": "node --max-old-space-size=3072 workspace/tasks/cli.js build --firefox --debug",
|
|
32
|
-
"addonova-debug:thunderbird": "node --max-old-space-size=3072 workspace/tasks/cli.js build --thunderbird --debug",
|
|
33
|
-
"addonova-debug:naver": "node --max-old-space-size=3072 workspace/tasks/cli.js build --naver --debug",
|
|
34
|
-
"addonova-watch": "node --max-old-space-size=3072 workspace/tasks/cli.js build --all --debug --watch"
|
|
35
|
-
},
|
|
36
21
|
"dependencies": {
|
|
37
22
|
"@craftamap/esbuild-plugin-html": "^0.9.0",
|
|
38
23
|
"chokidar": "^5.0.0",
|
|
39
24
|
"esbuild": "^0.27.3",
|
|
40
25
|
"globby": "^16.1.0",
|
|
26
|
+
"web-ext": "^10.3.0",
|
|
41
27
|
"yazl": "^3.3.1"
|
|
42
28
|
},
|
|
29
|
+
"overrides": {
|
|
30
|
+
"encoding-sniffer": "^1.0.2",
|
|
31
|
+
"jsdom": "^29.1.1"
|
|
32
|
+
},
|
|
43
33
|
"keywords": [
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
34
|
+
"chrome",
|
|
35
|
+
"web",
|
|
36
|
+
"extension",
|
|
37
|
+
"browser",
|
|
38
|
+
"bundler",
|
|
39
|
+
"framework"
|
|
50
40
|
],
|
|
51
41
|
"license": "ISC",
|
|
52
42
|
"author": "Hemanta gayen",
|
|
@@ -59,6 +49,6 @@
|
|
|
59
49
|
"url": "https://github.com/code-hemu/addonova/issues"
|
|
60
50
|
},
|
|
61
51
|
"engines": {
|
|
62
|
-
"node": ">=
|
|
52
|
+
"node": ">=20.19"
|
|
63
53
|
}
|
|
64
54
|
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { mkdir } from 'node:fs/promises';
|
|
3
|
+
import { dirname, join, resolve } from 'node:path';
|
|
4
|
+
import { spawn } from 'node:child_process';
|
|
5
|
+
import process from 'node:process';
|
|
6
|
+
import { createRequire } from 'node:module';
|
|
7
|
+
|
|
8
|
+
import { getDestDir } from './paths.js';
|
|
9
|
+
import { log } from './utils.js';
|
|
10
|
+
|
|
11
|
+
const WEB_EXT_TARGETS = new Set(['chrome', 'edge', 'firefox', 'opera', 'naver']);
|
|
12
|
+
const require = createRequire(import.meta.url);
|
|
13
|
+
|
|
14
|
+
const chromiumBinaries = {
|
|
15
|
+
edge: {
|
|
16
|
+
win32: [
|
|
17
|
+
'C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe',
|
|
18
|
+
'C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe',
|
|
19
|
+
],
|
|
20
|
+
darwin: ['/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge'],
|
|
21
|
+
linux: ['microsoft-edge', 'microsoft-edge-stable'],
|
|
22
|
+
},
|
|
23
|
+
opera: {
|
|
24
|
+
win32: [
|
|
25
|
+
'C:\\Users\\%USERNAME%\\AppData\\Local\\Programs\\Opera\\opera.exe',
|
|
26
|
+
'C:\\Program Files\\Opera\\opera.exe',
|
|
27
|
+
],
|
|
28
|
+
darwin: ['/Applications/Opera.app/Contents/MacOS/Opera'],
|
|
29
|
+
linux: ['opera'],
|
|
30
|
+
},
|
|
31
|
+
naver: {
|
|
32
|
+
win32: [
|
|
33
|
+
'C:\\Program Files\\Naver\\Naver Whale\\Application\\whale.exe',
|
|
34
|
+
'C:\\Program Files (x86)\\Naver\\Naver Whale\\Application\\whale.exe',
|
|
35
|
+
],
|
|
36
|
+
darwin: ['/Applications/Whale.app/Contents/MacOS/Whale'],
|
|
37
|
+
linux: ['whale'],
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
function expandEnv(value) {
|
|
42
|
+
return value.replace(/%([^%]+)%/g, (_, name) => process.env[name] ?? '');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function resolveBinary(platform) {
|
|
46
|
+
const candidates = chromiumBinaries[platform]?.[process.platform] ?? [];
|
|
47
|
+
|
|
48
|
+
for (const candidate of candidates) {
|
|
49
|
+
const binary = expandEnv(candidate);
|
|
50
|
+
if (process.platform === 'linux' || existsSync(binary)) {
|
|
51
|
+
return binary;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function getWebExtBin() {
|
|
59
|
+
try {
|
|
60
|
+
const webExtIndex = require.resolve('web-ext');
|
|
61
|
+
return join(dirname(webExtIndex), 'bin', 'web-ext.js');
|
|
62
|
+
} catch {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function createWebExtArgs(platform, settings) {
|
|
68
|
+
const sourceDir = resolve(getDestDir({ isDebug: settings.isDebug, platform }));
|
|
69
|
+
const profileDir = resolve(join('.output', 'profiles', platform));
|
|
70
|
+
const args = [
|
|
71
|
+
'run',
|
|
72
|
+
'--source-dir',
|
|
73
|
+
sourceDir,
|
|
74
|
+
'--no-input',
|
|
75
|
+
'--start-url',
|
|
76
|
+
'about:blank',
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
if (platform === 'firefox') {
|
|
80
|
+
args.push('--target', 'firefox-desktop', '--firefox-profile', profileDir);
|
|
81
|
+
return { args, sourceDir, profileDir };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
args.push('--target', 'chromium', '--chromium-profile', profileDir, '--profile-create-if-missing');
|
|
85
|
+
|
|
86
|
+
if (platform !== 'chrome') {
|
|
87
|
+
const binary = resolveBinary(platform);
|
|
88
|
+
if (binary) {
|
|
89
|
+
args.push('--chromium-binary', binary);
|
|
90
|
+
} else {
|
|
91
|
+
log.warn(`[!] ${platform} binary not found. Trying the default Chromium browser.`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return { args, sourceDir, profileDir };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export async function startBrowserRunners(settings) {
|
|
99
|
+
const webExtBin = getWebExtBin();
|
|
100
|
+
const platforms = settings.platforms.filter((platform) => WEB_EXT_TARGETS.has(platform));
|
|
101
|
+
|
|
102
|
+
if (!webExtBin || !existsSync(webExtBin)) {
|
|
103
|
+
log.warn('[!] Browser dev runner not found. Run npm install before dev mode.');
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (platforms.length === 0) {
|
|
108
|
+
log.warn('[!] Browser dev mode supports Chrome, Edge, Firefox, Opera, and Naver Whale.');
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
settings.browserRunners = [];
|
|
113
|
+
|
|
114
|
+
for (const platform of platforms) {
|
|
115
|
+
const { args, sourceDir, profileDir } = createWebExtArgs(platform, settings);
|
|
116
|
+
|
|
117
|
+
if (!existsSync(sourceDir)) {
|
|
118
|
+
log.warn(`[!] Extension output missing for ${platform}: ${sourceDir}`);
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
await mkdir(profileDir, { recursive: true });
|
|
123
|
+
|
|
124
|
+
const child = spawn(process.execPath, [webExtBin, ...args], {
|
|
125
|
+
stdio: 'inherit',
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
child.on('exit', (code) => {
|
|
129
|
+
if (code !== null && code !== 0) {
|
|
130
|
+
log.warn(`[!] Browser runner for ${platform} exited with code ${code}`);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
settings.browserRunners.push(child);
|
|
135
|
+
log.ok(`[+] Started browser runner for ${platform}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function stopBrowserRunners(settings) {
|
|
140
|
+
for (const child of settings.browserRunners ?? []) {
|
|
141
|
+
if (!child.killed) child.kill('SIGTERM');
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -8,6 +8,7 @@ import bundleLocales from './bundle-locales.js';
|
|
|
8
8
|
import bundleManifest from './bundle-manifest.js';
|
|
9
9
|
import assetsCopy from './copy.js';
|
|
10
10
|
import zip from './zip.js';
|
|
11
|
+
import { startBrowserRunners, stopBrowserRunners } from './browser.js';
|
|
11
12
|
import {log} from './utils.js';
|
|
12
13
|
import {runTasks} from './task.js';
|
|
13
14
|
|
|
@@ -53,7 +54,9 @@ const settings = {
|
|
|
53
54
|
isDebug: args.includes('--debug'),
|
|
54
55
|
isTest: args.includes('--test'),
|
|
55
56
|
logInfo: args.includes('--log-info'),
|
|
56
|
-
logWarn: args.includes('--log-warn')
|
|
57
|
+
logWarn: args.includes('--log-warn'),
|
|
58
|
+
shouldRunBrowser: args.includes('--open') || (args.includes('--watch') && args.includes('--debug')),
|
|
59
|
+
args,
|
|
57
60
|
}
|
|
58
61
|
|
|
59
62
|
const standardTask = [
|
|
@@ -77,7 +80,18 @@ const buildTask = [
|
|
|
77
80
|
try {
|
|
78
81
|
await runTasks(settings.isDebug ? standardTask : buildTask, settings);
|
|
79
82
|
if (settings.isWatch) {
|
|
80
|
-
|
|
83
|
+
if (settings.shouldRunBrowser) {
|
|
84
|
+
await startBrowserRunners(settings);
|
|
85
|
+
process.on('exit', () => stopBrowserRunners(settings));
|
|
86
|
+
process.on('SIGINT', () => {
|
|
87
|
+
stopBrowserRunners(settings);
|
|
88
|
+
process.exit(130);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
standardTask.forEach((task) => task.watch(
|
|
92
|
+
settings.platforms,
|
|
93
|
+
settings.isDebug
|
|
94
|
+
));
|
|
81
95
|
log.ok('[^_^] Watching...');
|
|
82
96
|
} else {
|
|
83
97
|
log.ok('MISSION PASSED! RESPECT +');
|
|
@@ -21,10 +21,11 @@ function printHelp() {
|
|
|
21
21
|
'Other parameters:',
|
|
22
22
|
' --release Build release version (default)',
|
|
23
23
|
' --debug Build debug version',
|
|
24
|
-
' --watch Watch for changes and
|
|
24
|
+
' --watch Watch for changes and reload opened extensions',
|
|
25
25
|
' --log-info Log info messages',
|
|
26
26
|
' --log-warn Log warning messages',
|
|
27
27
|
' --test Build test version (for testing in development environment)',
|
|
28
|
+
' --open Open a browser with the debug extension loaded',
|
|
28
29
|
' --version=1.2.3 Append version to output file name (e.g. extension-name-chrome-1.2.3.zip)',
|
|
29
30
|
' -h, --help Show this help message',
|
|
30
31
|
].join('\n'));
|
|
@@ -57,6 +58,7 @@ function validateArguments(args) {
|
|
|
57
58
|
'--log-info',
|
|
58
59
|
'--log-warn',
|
|
59
60
|
'--test',
|
|
61
|
+
'--open',
|
|
60
62
|
'--version=*',
|
|
61
63
|
'--help',
|
|
62
64
|
'-h'
|
|
@@ -82,7 +84,7 @@ async function run() {
|
|
|
82
84
|
const validationErrors = validateArguments(args);
|
|
83
85
|
if (validationErrors.length > 0) {
|
|
84
86
|
validationErrors.forEach(log.error);
|
|
85
|
-
log.error('
|
|
87
|
+
log.error('[X] No target platform specified.');
|
|
86
88
|
printHelp();
|
|
87
89
|
process.exit(130);
|
|
88
90
|
}
|
package/src/cli/help.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function printHelp() {
|
|
2
|
+
console.log(`
|
|
3
|
+
Addonova - browser extension toolkit
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
npx addonova init <my-extension> Scaffold a new extension project
|
|
7
|
+
npx addonova build [options] Build the extension
|
|
8
|
+
npx addonova zip Create release zip bundles
|
|
9
|
+
npx addonova --help Show this help
|
|
10
|
+
|
|
11
|
+
Build options:
|
|
12
|
+
--all, --chrome, --firefox, --edge, --opera, --naver, --thunderbird
|
|
13
|
+
--release, --debug, --watch, --open, --test, --version=x.x.x
|
|
14
|
+
`);
|
|
15
|
+
}
|
package/src/cli/index.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { fork } from 'node:child_process';
|
|
2
|
+
import process from 'node:process';
|
|
3
|
+
import { dirname, resolve } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { existsSync } from 'node:fs';
|
|
6
|
+
|
|
7
|
+
import { init } from '../commands/init.js';
|
|
8
|
+
import { printHelp } from './help.js';
|
|
9
|
+
|
|
10
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
|
|
12
|
+
async function runBuildCommand(command, args) {
|
|
13
|
+
const cliPath = resolve(__dirname, '../build/cli.js');
|
|
14
|
+
|
|
15
|
+
if (!existsSync(cliPath)) {
|
|
16
|
+
console.error('[*] Addonova build CLI not found. Reinstall the package.');
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const child = fork(cliPath, [command, ...args], {
|
|
21
|
+
execArgv: ['--max-old-space-size=3072'],
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
process.on('SIGINT', () => {
|
|
25
|
+
child.kill('SIGKILL');
|
|
26
|
+
process.exit(130);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
await new Promise((resolve, reject) =>
|
|
30
|
+
child.on('error', reject).on('close', (code) => {
|
|
31
|
+
if (code !== 0) process.exit(code);
|
|
32
|
+
resolve();
|
|
33
|
+
})
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function runCli(argv) {
|
|
38
|
+
const [command, ...args] = argv;
|
|
39
|
+
|
|
40
|
+
switch (command) {
|
|
41
|
+
case 'init':
|
|
42
|
+
await init(args);
|
|
43
|
+
break;
|
|
44
|
+
case 'build':
|
|
45
|
+
case 'zip':
|
|
46
|
+
await runBuildCommand(command, args);
|
|
47
|
+
break;
|
|
48
|
+
case '--help':
|
|
49
|
+
case '-h':
|
|
50
|
+
default:
|
|
51
|
+
printHelp();
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
}
|
package/src/commands/init.js
CHANGED
|
@@ -6,10 +6,9 @@ import process from 'node:process';
|
|
|
6
6
|
import { selectBrowsers, askQuestion } from '../utils/prompts.js';
|
|
7
7
|
|
|
8
8
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
-
const TEMPLATE_DIR = path.resolve(__dirname, '../../
|
|
9
|
+
const TEMPLATE_DIR = path.resolve(__dirname, '../../templates/extension');
|
|
10
10
|
|
|
11
11
|
const VARIABLE_RE = /\{\{\s*(\w+)\s*\}\}/g;
|
|
12
|
-
const SKIP_DIRS = new Set(['workspace']);
|
|
13
12
|
|
|
14
13
|
function render(template, vars) {
|
|
15
14
|
return template.replace(VARIABLE_RE, (_, key) => vars[key] ?? `{{${key}}}`);
|
|
@@ -23,8 +22,6 @@ async function copyDir(src, dest, vars, selected = []) {
|
|
|
23
22
|
for (const entry of entries) {
|
|
24
23
|
const entryName = path.basename(entry.name, path.extname(entry.name));
|
|
25
24
|
|
|
26
|
-
if (entry.isDirectory() && SKIP_DIRS.has(entry.name)) continue;
|
|
27
|
-
|
|
28
25
|
if ((dirName === 'config' || dirName === 'platform') && selected.length > 0 && !selected.includes(entryName)) {
|
|
29
26
|
continue;
|
|
30
27
|
}
|
|
@@ -90,19 +87,18 @@ export async function init(args) {
|
|
|
90
87
|
await installDependencies(projectDir);
|
|
91
88
|
}
|
|
92
89
|
|
|
90
|
+
|
|
93
91
|
console.log(`
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
└──────────────────────────────────────────────
|
|
107
|
-
`);
|
|
92
|
+
[+] "${projectName}" is ready!
|
|
93
|
+
|
|
94
|
+
📁 ${projectDir}
|
|
95
|
+
|
|
96
|
+
Next steps:
|
|
97
|
+
cd ${projectName}
|
|
98
|
+
npm install
|
|
99
|
+
npm run watch
|
|
100
|
+
|
|
101
|
+
Targets: ${selected.join(', ').padEnd(30)}
|
|
102
|
+
|
|
103
|
+
`);
|
|
108
104
|
}
|
package/src/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import * as readline from 'node:readline/promises';
|
|
3
3
|
import { stdin as input, stdout as output } from 'node:process';
|
|
4
|
-
import {readFile, writeFile, httpsRequest, timeout} from '../
|
|
4
|
+
import {readFile, writeFile, httpsRequest, timeout} from '../build/utils.js';
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
const LOCALES_ROOT = 'src/_locales';
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{name}}",
|
|
3
|
+
"version": "{{version}}",
|
|
4
|
+
"description": "{{description}}",
|
|
5
|
+
"json.schemaValidation": "off",
|
|
6
|
+
"license": "GPLv3",
|
|
7
|
+
"author": "Addonova",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": ">=22",
|
|
11
|
+
"npm": ">=11"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"release": "addonova build --all --release",
|
|
15
|
+
"release:chrome": "addonova build --chrome --release",
|
|
16
|
+
"release:edge": "addonova build --edge --release",
|
|
17
|
+
"release:opera": "addonova build --opera --release",
|
|
18
|
+
"release:firefox": "addonova build --firefox --release",
|
|
19
|
+
"release:thunderbird": "addonova build --thunderbird --release",
|
|
20
|
+
"release:naver": "addonova build --naver --release",
|
|
21
|
+
"debug": "addonova build --all --debug",
|
|
22
|
+
"debug:chrome": "addonova build --chrome --debug",
|
|
23
|
+
"debug:edge": "addonova build --edge --debug",
|
|
24
|
+
"debug:opera": "addonova build --opera --debug",
|
|
25
|
+
"debug:firefox": "addonova build --firefox --debug",
|
|
26
|
+
"debug:thunderbird": "addonova build --thunderbird --debug",
|
|
27
|
+
"debug:naver": "addonova build --naver --debug",
|
|
28
|
+
"dev": "addonova build --all --debug --watch --port=9222",
|
|
29
|
+
"zip": "addonova zip",
|
|
30
|
+
"test": "node --test"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"addonova": "^1.0.0"
|
|
34
|
+
}
|
|
35
|
+
}
|
package/src/commands/update.js
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { existsSync } from 'node:fs';
|
|
2
|
-
import fs from 'node:fs/promises';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { fileURLToPath } from 'node:url';
|
|
5
|
-
import process from 'node:process';
|
|
6
|
-
|
|
7
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
-
const TEMPLATE_DIR = path.resolve(__dirname, '../../template');
|
|
9
|
-
const VARIABLE_RE = /\{\{\s*(\w+)\s*\}\}/g;
|
|
10
|
-
const SKIP_DIRS = new Set(['config', 'src', 'platform', 'workspace']);
|
|
11
|
-
|
|
12
|
-
function render(template, vars) {
|
|
13
|
-
return template.replace(VARIABLE_RE, (_, key) => vars[key] ?? `{{${key}}}`);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
async function copyDir(src, dest, vars) {
|
|
17
|
-
await fs.mkdir(dest, { recursive: true });
|
|
18
|
-
const entries = await fs.readdir(src, { withFileTypes: true });
|
|
19
|
-
|
|
20
|
-
for (const entry of entries) {
|
|
21
|
-
if (entry.isDirectory() && SKIP_DIRS.has(entry.name)) continue;
|
|
22
|
-
|
|
23
|
-
const srcPath = path.join(src, entry.name);
|
|
24
|
-
const destPath = path.join(dest, entry.name.replace(/\.tpl$/, ''));
|
|
25
|
-
|
|
26
|
-
if (entry.isDirectory()) {
|
|
27
|
-
await copyDir(srcPath, destPath, vars);
|
|
28
|
-
} else if (entry.name.endsWith('.tpl')) {
|
|
29
|
-
const content = await fs.readFile(srcPath, 'utf-8');
|
|
30
|
-
await fs.writeFile(destPath, render(content, vars), 'utf-8');
|
|
31
|
-
} else {
|
|
32
|
-
await fs.copyFile(srcPath, destPath);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export async function update() {
|
|
38
|
-
const cwd = process.cwd();
|
|
39
|
-
const pkgPath = path.join(cwd, 'package.json');
|
|
40
|
-
|
|
41
|
-
if (!existsSync(pkgPath)) {
|
|
42
|
-
console.error('[*] No package.json found. Run this command inside an addonova project.');
|
|
43
|
-
process.exit(1);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
let pkg;
|
|
47
|
-
try {
|
|
48
|
-
pkg = JSON.parse(await fs.readFile(pkgPath, 'utf-8'));
|
|
49
|
-
} catch {
|
|
50
|
-
console.error('[*] Invalid package.json.');
|
|
51
|
-
process.exit(1);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const vars = {
|
|
55
|
-
name: pkg.name || 'extension',
|
|
56
|
-
description: pkg.description || 'A cross-browser WebExtension',
|
|
57
|
-
version: pkg.version || '1.0.0',
|
|
58
|
-
browsers: JSON.stringify([]),
|
|
59
|
-
browsersList: '',
|
|
60
|
-
year: new Date().getFullYear().toString(),
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
console.log(`[+] Updating "${pkg.name}" (keeping config/, src/, platform/)`);
|
|
64
|
-
await copyDir(TEMPLATE_DIR, cwd, vars);
|
|
65
|
-
|
|
66
|
-
console.log('[+] Update complete.');
|
|
67
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "{{name}}",
|
|
3
|
-
"version": "{{version}}",
|
|
4
|
-
"description": "{{description}}",
|
|
5
|
-
"json.schemaValidation": "off",
|
|
6
|
-
"license": "GPLv3",
|
|
7
|
-
"author": "Addonova",
|
|
8
|
-
"type": "module",
|
|
9
|
-
"engines": {
|
|
10
|
-
"node": ">=22",
|
|
11
|
-
"npm": ">=11"
|
|
12
|
-
},
|
|
13
|
-
"scripts": {
|
|
14
|
-
"release": "addonova-release",
|
|
15
|
-
"release:chrome": "addonova-release:chrome",
|
|
16
|
-
"release:edge": "addonova-release:edge",
|
|
17
|
-
"release:opera": "addonova-release:opera",
|
|
18
|
-
"release:firefox": "addonova-release:firefox",
|
|
19
|
-
"release:thunderbird": "addonova-release:thunderbird",
|
|
20
|
-
"release:naver": "addonova-release:naver",
|
|
21
|
-
|
|
22
|
-
"debug": "addonova-debug",
|
|
23
|
-
"debug:chrome": "addonova-debug:chrome",
|
|
24
|
-
"debug:edge": "addonova-debug:edge",
|
|
25
|
-
"debug:opera": "addonova-debug:opera",
|
|
26
|
-
"debug:firefox": "addonova-debug:firefox",
|
|
27
|
-
"debug:thunderbird": "addonova-debug:thunderbird",
|
|
28
|
-
"debug:naver": "addonova-debug:naver",
|
|
29
|
-
|
|
30
|
-
"watch": "addonova-watch",
|
|
31
|
-
"zip": "addonova zip",
|
|
32
|
-
|
|
33
|
-
"addonova-release": "node --max-old-space-size=3072 node_modules/addonova/workspace/tasks/cli.js build --all --release",
|
|
34
|
-
"addonova-release:chrome": "node --max-old-space-size=3072 node_modules/addonova/workspace/tasks/cli.js build --chrome --release",
|
|
35
|
-
"addonova-release:edge": "node --max-old-space-size=3072 node_modules/addonova/workspace/tasks/cli.js build --edge --release",
|
|
36
|
-
"addonova-release:opera": "node --max-old-space-size=3072 node_modules/addonova/workspace/tasks/cli.js build --opera --release",
|
|
37
|
-
"addonova-release:firefox": "node --max-old-space-size=3072 node_modules/addonova/workspace/tasks/cli.js build --firefox --release",
|
|
38
|
-
"addonova-release:thunderbird": "node --max-old-space-size=3072 node_modules/addonova/workspace/tasks/cli.js build --thunderbird --release",
|
|
39
|
-
"addonova-release:naver": "node --max-old-space-size=3072 node_modules/addonova/workspace/tasks/cli.js build --naver --release",
|
|
40
|
-
|
|
41
|
-
"addonova-debug": "node --max-old-space-size=3072 node_modules/addonova/workspace/tasks/cli.js build --all --debug",
|
|
42
|
-
"addonova-debug:chrome": "node --max-old-space-size=3072 node_modules/addonova/workspace/tasks/cli.js build --chrome --debug",
|
|
43
|
-
"addonova-debug:edge": "node --max-old-space-size=3072 node_modules/addonova/workspace/tasks/cli.js build --edge --debug",
|
|
44
|
-
"addonova-debug:opera": "node --max-old-space-size=3072 node_modules/addonova/workspace/tasks/cli.js build --opera --debug",
|
|
45
|
-
"addonova-debug:firefox": "node --max-old-space-size=3072 node_modules/addonova/workspace/tasks/cli.js build --firefox --debug",
|
|
46
|
-
"addonova-debug:thunderbird": "node --max-old-space-size=3072 node_modules/addonova/workspace/tasks/cli.js build --thunderbird --debug",
|
|
47
|
-
"addonova-debug:naver": "node --max-old-space-size=3072 node_modules/addonova/workspace/tasks/cli.js build --naver --debug",
|
|
48
|
-
|
|
49
|
-
"addonova-watch": "node --max-old-space-size=3072 node_modules/addonova/workspace/tasks/cli.js build --all --debug --watch"
|
|
50
|
-
},
|
|
51
|
-
"devDependencies": {
|
|
52
|
-
"addonova": "^1.0.0"
|
|
53
|
-
}
|
|
54
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|