addonova 1.0.2 → 1.0.4

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 (59) hide show
  1. package/README.md +117 -48
  2. package/bin/addonova.js +4 -0
  3. package/bin/index.js +1 -60
  4. package/package.json +20 -29
  5. package/src/build/browser.js +143 -0
  6. package/{workspace/tasks → src/build}/build.js +16 -2
  7. package/src/build/bundle-html.js +120 -0
  8. package/{workspace/tasks → src/build}/cli.js +4 -2
  9. package/src/cli/help.js +16 -0
  10. package/src/cli/index.js +58 -0
  11. package/src/commands/init.js +14 -18
  12. package/src/commands/tool.js +32 -0
  13. package/src/index.js +0 -1
  14. package/src/tools/tools-server.js +287 -0
  15. package/src/tools/tools.html +388 -0
  16. package/templates/extension/config/firefox.js +30 -0
  17. package/templates/extension/config/thunderbird.js +30 -0
  18. package/templates/extension/package.json.tpl +36 -0
  19. package/{template → templates/extension}/platform/naver/platform.js +0 -15
  20. package/src/commands/update.js +0 -67
  21. package/template/config/firefox.js +0 -29
  22. package/template/config/thunderbird.js +0 -29
  23. package/template/package.json.tpl +0 -54
  24. package/workspace/tasks/bundle-html.js +0 -91
  25. package/workspace/tools/json2i18n.js +0 -37
  26. package/workspace/tools/translate.js +0 -299
  27. /package/{workspace/tasks → src/build}/bundle-css.js +0 -0
  28. /package/{workspace/tasks → src/build}/bundle-js.js +0 -0
  29. /package/{workspace/tasks → src/build}/bundle-locales.js +0 -0
  30. /package/{workspace/tasks → src/build}/bundle-manifest.js +0 -0
  31. /package/{workspace/tasks → src/build}/copy.js +0 -0
  32. /package/{workspace/tasks → src/build}/folder.js +0 -0
  33. /package/{workspace/tasks → src/build}/paths.js +0 -0
  34. /package/{workspace/tasks → src/build}/task.js +0 -0
  35. /package/{workspace/tasks → src/build}/utils.js +0 -0
  36. /package/{workspace/tasks → src/build}/watch.js +0 -0
  37. /package/{workspace/tasks → src/build}/zip.js +0 -0
  38. /package/{template → templates/extension}/config/chrome.js +0 -0
  39. /package/{template → templates/extension}/config/edge.js +0 -0
  40. /package/{template → templates/extension}/config/naver.js +0 -0
  41. /package/{template → templates/extension}/config/opera.js +0 -0
  42. /package/{template → templates/extension}/platform/chrome/platform.js +0 -0
  43. /package/{template → templates/extension}/platform/edge/platform.js +0 -0
  44. /package/{template → templates/extension}/platform/opera/platform.js +0 -0
  45. /package/{template → templates/extension}/src/_locales/en.i18n +0 -0
  46. /package/{template → templates/extension}/src/assets/icons/128.png +0 -0
  47. /package/{template → templates/extension}/src/assets/icons/32.png +0 -0
  48. /package/{template → templates/extension}/src/assets/icons/48.png +0 -0
  49. /package/{template → templates/extension}/src/assets/icons/64.png +0 -0
  50. /package/{template → templates/extension}/src/css/popup.css +0 -0
  51. /package/{template → templates/extension}/src/html/popup.html +0 -0
  52. /package/{template → templates/extension}/src/js/background.js +0 -0
  53. /package/{template → templates/extension}/src/js/lib/browser.js +0 -0
  54. /package/{template → templates/extension}/src/js/lib/common.js +0 -0
  55. /package/{template → templates/extension}/src/js/lib/config.js +0 -0
  56. /package/{template → templates/extension}/src/js/lib/runtime.js +0 -0
  57. /package/{template → templates/extension}/src/js/popup.js +0 -0
  58. /package/{template → templates/extension}/src/manifest/manifest-firefox.json +0 -0
  59. /package/{template → templates/extension}/src/manifest/manifest.json +0 -0
@@ -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
- }
@@ -1,91 +0,0 @@
1
- import path from 'node:path';
2
- import { build } from 'esbuild';
3
- import { htmlPlugin } from '@craftamap/esbuild-plugin-html';
4
- import {getDestDir} from './paths.js';
5
- import {readFile, writeFile, getConfig, getAllFiles, log, fileExistsInConfig} from './utils.js';
6
- import {createTask} from './task.js';
7
-
8
- const srcHTMLDir = 'src/html';
9
-
10
- async function removeTopSourceComment(filePath) {
11
- const code = await readFile(filePath, 'utf8');
12
- const newCode = code.replace(/^\s*<!--[\s\S]*?-->\s*/, '');
13
- await writeFile(filePath, newCode);
14
- }
15
-
16
- async function esbuildHTML(config, isDebug, platform){
17
- let buildResult, outputs;
18
- const dir = getDestDir({isDebug, platform});
19
- for (const [dest, src] of Object.entries(config.entry)) {
20
- buildResult = await build({
21
- entryPoints: src,
22
- outdir: path.join(dir, dest),
23
- plugins: [htmlPlugin()],
24
- loader: {
25
- '.html': 'file' // 🔥 Important
26
- },
27
- entryNames: config.filename,
28
- assetNames: config.filename,
29
- chunkNames: config.filename,
30
- metafile: true,
31
- write: true
32
- });
33
-
34
- if (!isDebug) {
35
- outputs = Object.keys(buildResult.metafile.outputs);
36
- for (const file of outputs) {
37
- if (file.endsWith('.html')) {
38
- removeTopSourceComment(file);
39
- }
40
- }
41
- }
42
- }
43
-
44
- }
45
-
46
- export function createBundleHTMLTask(srcHTMLDir){
47
- let currentWatchFiles;
48
- const bundleHTML = async ({platforms, isDebug, logInfo, logWarn}) => {
49
- for(const platform of platforms){
50
- const config = (await getConfig(platform));
51
- if(config.html){
52
- await esbuildHTML(config.html, isDebug, platform);
53
- if (logInfo) log.ok(`Bundling HTML for ${platform}...`);
54
- } else{
55
- if(logWarn) log.warn(`No HTML config found for ${platform}, skipping HTML bundling.`);
56
- }
57
- }
58
- }
59
-
60
- const onChange = async (changedFiles, watcher, platforms, isDebug) => {
61
- for(const platform of platforms){
62
- const config = (await getConfig(platform));
63
-
64
- if (!config.html) continue;
65
- const exists = await fileExistsInConfig(
66
- config.html.entry,
67
- changedFiles[0]
68
- );
69
-
70
- if (exists){
71
- const newConfig = {
72
- html: {
73
- entry: exists,
74
- filename: config.html.filename
75
- }
76
- };
77
- await esbuildHTML(newConfig.html, isDebug, platform);
78
- }
79
- }
80
- }
81
- return createTask(
82
- 'bundle HTML',
83
- bundleHTML,
84
- ).addWatcher(
85
- async () => {
86
- currentWatchFiles = await getAllFiles(srcHTMLDir);
87
- return currentWatchFiles;
88
- }, onChange);
89
- }
90
-
91
- export default createBundleHTMLTask(srcHTMLDir);
@@ -1,37 +0,0 @@
1
- import { mkdirSync, readdirSync, existsSync, readFileSync, writeFileSync } from "fs";
2
- import { join, dirname } from "path";
3
- import { fileURLToPath } from "url";
4
-
5
- const __filename = fileURLToPath(import.meta.url);
6
- const __dirname = dirname(__filename);
7
-
8
- const localesDir = join(__dirname, "../", "_locales");
9
- const outputDir = join(__dirname, "../", "output", "_locales");
10
-
11
- mkdirSync(outputDir, { recursive: true });
12
-
13
- console.log("Output directory ready.");
14
-
15
- const languages = readdirSync(localesDir);
16
-
17
- languages.forEach((lang) => {
18
- const messagesPath = join(localesDir, lang, "messages.json");
19
-
20
- if (!existsSync(messagesPath)) return;
21
-
22
- const input = JSON.parse(readFileSync(messagesPath, "utf8"));
23
-
24
- let output = "";
25
-
26
- for (const key in input) {
27
- output += `@${key}\n`;
28
- output += `${input[key].message}\n\n`;
29
- }
30
-
31
- const outputPath = join(outputDir, `${lang}.i18n`);
32
- writeFileSync(outputPath, output);
33
-
34
- console.log(`${lang}.i18n created`);
35
- });
36
-
37
- console.log("All conversions completed.");
@@ -1,299 +0,0 @@
1
- import fs from 'node:fs/promises';
2
- import * as readline from 'node:readline/promises';
3
- import { stdin as input, stdout as output } from 'node:process';
4
- import {readFile, writeFile, httpsRequest, timeout} from '../tasks/utils.js';
5
-
6
-
7
- const LOCALES_ROOT = 'src/_locales';
8
-
9
- function toMessageId(message) {
10
- if (typeof message !== 'string') return '';
11
-
12
- return message
13
- .trim()
14
- .split(/\s+/) // split by any whitespace
15
- .slice(0, 3) // only first 3 words
16
- .map(word =>
17
- word
18
- .replace(/[^\w]/g, '') // remove special characters
19
- .toLowerCase()
20
- )
21
- .filter(Boolean) // remove empty results
22
- .join('_');
23
- }
24
-
25
- async function getSupportedLocales() {
26
- const fileList = await fs.readdir(LOCALES_ROOT);
27
-
28
- const locales = [];
29
-
30
- for (const file of fileList) {
31
- if (file.endsWith('.i18n')) {
32
- const locale = file.substring(0, file.lastIndexOf('.i18n'));
33
- locales.push(locale);
34
- }
35
- }
36
-
37
- return locales;
38
- }
39
-
40
- function stringifyLocale(messages) {
41
- const lines = [];
42
- messages.forEach((message, id) => {
43
- lines.push(`@${id}`);
44
- const hasDoubleNewLines = /\n\n/.test(message);
45
- message.split('\n')
46
- .filter((line) => line.trim())
47
- .forEach((line, index, filtered) => {
48
- lines.push(line);
49
- if (hasDoubleNewLines && index < filtered.length - 1) {
50
- lines.push('');
51
- }
52
- });
53
- lines.push('');
54
- });
55
- return lines.join('\n');
56
- }
57
-
58
- function parseLocale(content) {
59
- const messages = new Map();
60
- const lines = content.split('\n');
61
- let id = '';
62
- for (let i = 0; i < lines.length; i++) {
63
- const line = lines[i];
64
- if (line.startsWith('@')) {
65
- id = line.substring(1);
66
- } else if (line.startsWith('#')) {
67
- // Ignore
68
- } else if (messages.has(id)) {
69
- const message = messages.get(id);
70
- messages.set(id, `${message}\n${line}`);
71
- } else {
72
- messages.set(id, line);
73
- }
74
- }
75
- messages.forEach((value, id) => {
76
- messages.set(id, value.trim());
77
- });
78
- return messages;
79
- }
80
-
81
- async function translate(text, lang) {
82
- const url = new URL('https://translate.googleapis.com/translate_a/single');
83
- url.search = (new URLSearchParams({
84
- client: 'gtx',
85
- sl: 'en-US',
86
- tl: lang,
87
- dt: 't',
88
- dj: '1',
89
- q: text,
90
- })).toString();
91
- const response = await httpsRequest(url.toString());
92
- const translation = JSON.parse(response.text());
93
- return translation.sentences.map((s) => s.trans).join('\n').replaceAll(/\n+/g, '\n');
94
- }
95
-
96
- async function deleteMessage(messageId, targetLocale = null) {
97
- const supportedLocales = await getSupportedLocales();
98
-
99
- if (!messageId || !messageId.trim()) {
100
- console.log('⚠ Message key cannot be empty.');
101
- return;
102
- }
103
-
104
- const id = messageId.trim();
105
- let foundAnywhere = false;
106
-
107
- // 🎯 Decide scope
108
- const localesToProcess = targetLocale
109
- ? [targetLocale]
110
- : supportedLocales;
111
-
112
- for (const locale of localesToProcess) {
113
- const locFile = `${LOCALES_ROOT}/${locale}.i18n`;
114
-
115
- try {
116
- const locContent = await readFile(locFile);
117
- const locMessages = parseLocale(locContent);
118
-
119
- if (!locMessages.has(id)) {
120
- continue;
121
- }
122
-
123
- locMessages.delete(id);
124
-
125
- const output = stringifyLocale(locMessages);
126
- await writeFile(locFile, output);
127
-
128
- console.log(`✔ Removed from ${locale}.i18n`);
129
- foundAnywhere = true;
130
-
131
- } catch (err) {
132
- console.log(`⚠ Failed to process ${locale}: ${err.message}`);
133
- }
134
- }
135
-
136
- // 🧾 Final status
137
- if (!foundAnywhere) {
138
- console.log(
139
- targetLocale
140
- ? `⚠ Message key not found in ${targetLocale}.`
141
- : '⚠ Message key not found in any locale.'
142
- );
143
- } else {
144
- console.log('🗑 Deletion completed.');
145
- }
146
- }
147
-
148
-
149
- async function translateEnMessage(message, customId) {
150
- console.log(`Translating message: ${message}`);
151
-
152
- const supportedLocales = await getSupportedLocales();
153
- const messageId = customId && customId.trim()
154
- ? customId.trim()
155
- : toMessageId(message);
156
-
157
- // 1️⃣ Update English first
158
- const enFile = `${LOCALES_ROOT}/en.i18n`;
159
- const enContent = await readFile(enFile);
160
- const enMessages = parseLocale(enContent);
161
-
162
- if (!enMessages.has(messageId)) {
163
- enMessages.set(messageId, message);
164
- const output = stringifyLocale(enMessages);
165
- await writeFile(enFile, output);
166
- console.log(`en: ${message}`);
167
- } else {
168
- console.log('Message already exists in en.i18n');
169
- }
170
-
171
- // 2️⃣ Translate other locales
172
- for (const locale of supportedLocales) {
173
- if (locale === 'en') continue;
174
-
175
- await timeout(1000);
176
-
177
- const locFile = `${LOCALES_ROOT}/${locale}.i18n`;
178
- const locContent = await readFile(locFile);
179
- const locMessages = parseLocale(locContent);
180
-
181
- if (locMessages.has(messageId)) {
182
- console.log(`Already exists in: ${locFile}`);
183
- continue;
184
- }
185
-
186
- const translatedDefault = await translate(message, locale);
187
-
188
- console.log(`✅ Output: ${locale}: ${translatedDefault}`);
189
-
190
- locMessages.set(messageId, translatedDefault);
191
-
192
- const outputs = stringifyLocale(locMessages);
193
- await writeFile(locFile, outputs);
194
- }
195
- }
196
-
197
- async function main() {
198
- const rl = readline.createInterface({ input, output });
199
-
200
- try {
201
- while (true) {
202
- console.log('\n=== Message Manager ===');
203
- console.log('[1] Add new message');
204
- console.log('[2] Delete message');
205
- console.log('[0] Exit');
206
-
207
- const choice = (await rl.question('Select option: '))
208
- .trim()
209
- .toLowerCase();
210
-
211
- switch (choice) {
212
- case '1': {
213
- const newMessage = (await rl.question('Enter new message: '))
214
- .trim();
215
-
216
- if (!newMessage) {
217
- console.log('⚠ Message cannot be empty.');
218
- break;
219
- }
220
-
221
- const newMessageId = (await rl.question(
222
- 'Enter new message key (leave empty for auto-generation): '
223
- )).trim();
224
-
225
- await translateEnMessage(
226
- newMessage,
227
- newMessageId || undefined
228
- );
229
-
230
- console.log('✔ Message processed successfully.');
231
- break;
232
- }
233
-
234
- case '2': {
235
- const messageId = (await rl.question(
236
- 'Enter message key to delete: '
237
- )).trim();
238
-
239
- if (!messageId) {
240
- console.log('⚠ Message key cannot be empty.');
241
- break;
242
- }
243
-
244
- // 🌍 Ask scope
245
- const scope = (await rl.question(
246
- 'Delete from (a)ll locales or specific locale? (a/s): '
247
- )).trim().toLowerCase();
248
-
249
- let targetLocale = null;
250
-
251
- if (scope === 's') {
252
- targetLocale = (await rl.question(
253
- 'Enter locale (e.g., en, fa, ru): '
254
- )).trim();
255
-
256
- if (!targetLocale) {
257
- console.log('⚠ Locale cannot be empty.');
258
- break;
259
- }
260
- }
261
-
262
- // ✅ Confirmation
263
- const confirm = (await rl.question(
264
- targetLocale
265
- ? `Are you sure you want to delete "${messageId}" from "${targetLocale}"? (y/n): `
266
- : `Are you sure you want to delete "${messageId}" from ALL locales? (y/n): `
267
- )).trim().toLowerCase();
268
-
269
- if (confirm !== 'y') {
270
- console.log('Deletion cancelled.');
271
- break;
272
- }
273
-
274
- // 🔥 Delete logic
275
- if (targetLocale) {
276
- await deleteMessage(messageId, targetLocale);
277
- } else {
278
- await deleteMessage(messageId);
279
- }
280
-
281
- console.log('Deleted ✅');
282
- break;
283
- }
284
-
285
- case '0':
286
- case 'exit': {
287
- console.log('Exiting...');
288
- return;
289
- }
290
-
291
- default: {
292
- console.log('Invalid selection. Please try again.');
293
- }
294
- }
295
- }
296
- } finally {
297
- rl.close();
298
- }
299
- }main();
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