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.
Files changed (53) hide show
  1. package/README.md +104 -48
  2. package/bin/addonova.js +4 -0
  3. package/bin/index.js +1 -60
  4. package/package.json +18 -28
  5. package/src/build/browser.js +143 -0
  6. package/{workspace/tasks → src/build}/build.js +16 -2
  7. package/{workspace/tasks → src/build}/cli.js +4 -2
  8. package/src/cli/help.js +15 -0
  9. package/src/cli/index.js +54 -0
  10. package/src/commands/init.js +14 -18
  11. package/src/index.js +0 -1
  12. package/{workspace → src}/tools/translate.js +1 -1
  13. package/templates/extension/package.json.tpl +35 -0
  14. package/src/commands/update.js +0 -67
  15. package/template/package.json.tpl +0 -54
  16. /package/{workspace/tasks → src/build}/bundle-css.js +0 -0
  17. /package/{workspace/tasks → src/build}/bundle-html.js +0 -0
  18. /package/{workspace/tasks → src/build}/bundle-js.js +0 -0
  19. /package/{workspace/tasks → src/build}/bundle-locales.js +0 -0
  20. /package/{workspace/tasks → src/build}/bundle-manifest.js +0 -0
  21. /package/{workspace/tasks → src/build}/copy.js +0 -0
  22. /package/{workspace/tasks → src/build}/folder.js +0 -0
  23. /package/{workspace/tasks → src/build}/paths.js +0 -0
  24. /package/{workspace/tasks → src/build}/task.js +0 -0
  25. /package/{workspace/tasks → src/build}/utils.js +0 -0
  26. /package/{workspace/tasks → src/build}/watch.js +0 -0
  27. /package/{workspace/tasks → src/build}/zip.js +0 -0
  28. /package/{workspace → src}/tools/json2i18n.js +0 -0
  29. /package/{template → templates/extension}/config/chrome.js +0 -0
  30. /package/{template → templates/extension}/config/edge.js +0 -0
  31. /package/{template → templates/extension}/config/firefox.js +0 -0
  32. /package/{template → templates/extension}/config/naver.js +0 -0
  33. /package/{template → templates/extension}/config/opera.js +0 -0
  34. /package/{template → templates/extension}/config/thunderbird.js +0 -0
  35. /package/{template → templates/extension}/platform/chrome/platform.js +0 -0
  36. /package/{template → templates/extension}/platform/edge/platform.js +0 -0
  37. /package/{template → templates/extension}/platform/naver/platform.js +0 -0
  38. /package/{template → templates/extension}/platform/opera/platform.js +0 -0
  39. /package/{template → templates/extension}/src/_locales/en.i18n +0 -0
  40. /package/{template → templates/extension}/src/assets/icons/128.png +0 -0
  41. /package/{template → templates/extension}/src/assets/icons/32.png +0 -0
  42. /package/{template → templates/extension}/src/assets/icons/48.png +0 -0
  43. /package/{template → templates/extension}/src/assets/icons/64.png +0 -0
  44. /package/{template → templates/extension}/src/css/popup.css +0 -0
  45. /package/{template → templates/extension}/src/html/popup.html +0 -0
  46. /package/{template → templates/extension}/src/js/background.js +0 -0
  47. /package/{template → templates/extension}/src/js/lib/browser.js +0 -0
  48. /package/{template → templates/extension}/src/js/lib/common.js +0 -0
  49. /package/{template → templates/extension}/src/js/lib/config.js +0 -0
  50. /package/{template → templates/extension}/src/js/lib/runtime.js +0 -0
  51. /package/{template → templates/extension}/src/js/popup.js +0 -0
  52. /package/{template → templates/extension}/src/manifest/manifest-firefox.json +0 -0
  53. /package/{template → templates/extension}/src/manifest/manifest.json +0 -0
package/README.md CHANGED
@@ -1,10 +1,8 @@
1
1
  # addonova
2
2
 
3
- Next-Generation Web Extension Framework
3
+ WebExtension framework for scaffolding and building cross-browser browser extensions.
4
4
 
5
- Scaffold and build cross-browser WebExtensions with a modern toolchain.
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
- | `update` | Update workspace/build files to latest template |
30
- | `build [options]` | Build the extension |
31
- | `zip` | Create zip bundles |
32
- | `--help` | Show help |
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
- ## Build Targets
33
+ Generated projects include these npm scripts:
35
34
 
36
35
  ```bash
37
- npm run release # Release build (all)
38
- npm run release:chrome # Release (Chrome)
39
- npm run release:firefox # Release (Firefox)
40
- npm run debug # Debug build (all)
41
- npm run debug:chrome # Debug (Chrome)
42
- npm run watch # Watch mode
43
- npm run zip # Create zip bundles
44
- npx addonova build --all --release # Build via CLI
45
- npx addonova --help # Show help
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
- ## Project Structure
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
- ├── config/ # Build config per browser
53
- │ ├── chrome.js
54
- │ ├── firefox.js
55
- └── ...
56
- ├── platform/ # Platform-specific polyfills
57
- ├── chrome/platform.js
58
- ├── firefox/platform.js
59
- └── ...
60
- ├── src/
61
- │ ├── _locales/ # i18n locale files (.i18n)
62
- │ ├── assets/ # Static assets (icons, etc.)
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
- node node_modules/addonova/workspace/tools/translate.js
145
+ npm pack --dry-run
91
146
  ```
92
147
 
93
148
  ## Requirements
94
149
 
95
- - Node.js >= 18
150
+ - Addonova package: Node.js >= 20.19
151
+ - Generated extension template: Node.js >= 22 and npm >= 11
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { runCli } from '../src/cli/index.js';
3
+
4
+ await runCli(process.argv.slice(2));
package/bin/index.js CHANGED
@@ -1,61 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import { fork } from 'node:child_process';
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.2",
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/index.js"
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
- "workspace/",
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
- "webextension",
45
- "browser-extension",
46
- "chrome-extension",
47
- "firefox-addon",
48
- "scaffold",
49
- "cli"
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": ">=18"
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
- standardTask.forEach((task) => task.watch(settings.platforms, settings.isDebug));
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 rebuild automatically',
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(' No target platform specified.');
87
+ log.error('[X] No target platform specified.');
86
88
  printHelp();
87
89
  process.exit(130);
88
90
  }
@@ -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
+ }
@@ -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
+ }
@@ -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, '../../template');
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
- │ [+] "${projectName}" is ready!
96
-
97
- │ ${projectDir} │
98
-
99
- │ Next steps:
100
- cd ${projectName}
101
- npm run release
102
- │ npm run debug:chrome
103
- │ npm run watch
104
-
105
- Targets: ${selected.join(', ').padEnd(30)}
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,2 +1 @@
1
1
  export { init } from './commands/init.js';
2
- export { update } from './commands/update.js';
@@ -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 '../tasks/utils.js';
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
+ }
@@ -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