extension 2.1.3 → 3.0.0-next.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,22 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
- var __webpack_modules__ = {
4
- "extension-develop": function(module) {
5
- module.exports = import("extension-develop").then(function(module) {
6
- return module;
7
- });
8
- }
9
- };
10
- var __webpack_module_cache__ = {};
11
- function __webpack_require__(moduleId) {
12
- var cachedModule = __webpack_module_cache__[moduleId];
13
- if (void 0 !== cachedModule) return cachedModule.exports;
14
- var module = __webpack_module_cache__[moduleId] = {
15
- exports: {}
16
- };
17
- __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
18
- return module.exports;
19
- }
3
+ var __webpack_require__ = {};
20
4
  (()=>{
21
5
  __webpack_require__.n = (module)=>{
22
6
  var getter = module && module.__esModule ? ()=>module['default'] : ()=>module;
@@ -38,27 +22,28 @@ function __webpack_require__(moduleId) {
38
22
  __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
39
23
  })();
40
24
  var __webpack_exports__ = {};
41
- (()=>{
42
- const external_commander_namespaceObject = require("commander");
43
- const external_extension_create_namespaceObject = require("extension-create");
44
- const external_pintor_namespaceObject = require("pintor");
45
- var external_pintor_default = /*#__PURE__*/ __webpack_require__.n(external_pintor_namespaceObject);
46
- function getLoggingPrefix(type) {
47
- if ('error' === type) return external_pintor_default().red('ERROR');
48
- if ('warn' === type) return external_pintor_default().brightYellow("\u25BA\u25BA\u25BA");
49
- if ('info' === type) return external_pintor_default().blue("\u25BA\u25BA\u25BA");
50
- return external_pintor_default().green("\u25BA\u25BA\u25BA");
51
- }
52
- const code = (text)=>external_pintor_default().blue(text);
53
- const arg = (text)=>external_pintor_default().gray(text);
54
- function updateFailed(err) {
55
- return `${getLoggingPrefix('error')} Failed to check for updates.\n${external_pintor_default().red(String((null == err ? void 0 : err.message) || err))}`;
56
- }
57
- function checkUpdates(packageJson, update) {
58
- return `${external_pintor_default().blue('Extension.js')} update available.\nYou are currently using version ${external_pintor_default().gray(String(packageJson.version))}. Latest stable is ${external_pintor_default().gray(String(update.latest))}. Please update to enjoy new features and improvements.`;
59
- }
60
- function programUserHelp() {
61
- return `\n${getLoggingPrefix('info')} ${external_pintor_default().underline('Help center for the Extension.js program')}
25
+ const external_commander_namespaceObject = require("commander");
26
+ var package_namespaceObject = JSON.parse('{"license":"MIT","repository":{"type":"git","url":"https://github.com/extension-js/extension.js","directory":"programs/cli"},"engines":{"node":">=18"},"exports":{".":{"types":"./dist/cli.d.ts","import":"./dist/cli.js","require":"./dist/cli.js"}},"main":"./dist/cli.js","types":"./dist/cli.d.ts","typesVersions":{"*":{"types":["./types/index.d.ts"],"types/*":["./types/*"]}},"files":["dist","types"],"bin":{"extension":"./dist/cli.js"},"name":"extension","version":"3.0.0-next.7","description":"Create cross-browser extensions with no build configuration.","homepage":"https://extension.js.org/","author":{"name":"Cezar Augusto","email":"boss@cezaraugusto.net","url":"https://cezaraugusto.com"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org","tag":"latest"},"scripts":{"prepare":"rslib build >/dev/null 2>&1 || true","postinstall":"rslib build >/dev/null 2>&1 || true","compile":"rslib build","watch":"rslib build --watch","test":"vitest run"},"keywords":["zero-config","build","develop","browser","extension","chrome extension","edge extension","firefox extension","safari extension","web","react","typescript","webextension","browser-extension","chrome-extension","firefox-addon","edge-extension","safari-web-extension","manifest-v3","mv3","cross-browser","content-script","background-script","devtools","create-extension","scaffold","starter-template","boilerplate","cli"],"dependencies":{"commander":"^12.1.0","extension-create":"^2.2.0","pintor":"0.3.0","semver":"^7.6.3","update-check":"^1.5.4"},"devDependencies":{"@rslib/core":"^0.6.9","@types/chrome":"^0.0.287","@types/node":"^22.10.1","@types/react":"^19.0.1","@types/react-dom":"^19.0.1","@types/webextension-polyfill":"0.12.3","@types/mock-fs":"^4.13.4","@types/semver":"^7.5.8","mock-fs":"^5.4.1","webextension-polyfill":"^0.12.0","tsconfig":"*","typescript":"5.7.2","vitest":"^3.2.4"}}');
27
+ const external_update_check_namespaceObject = require("update-check");
28
+ var external_update_check_default = /*#__PURE__*/ __webpack_require__.n(external_update_check_namespaceObject);
29
+ const external_pintor_namespaceObject = require("pintor");
30
+ var external_pintor_default = /*#__PURE__*/ __webpack_require__.n(external_pintor_namespaceObject);
31
+ function getLoggingPrefix(type) {
32
+ if ('error' === type) return external_pintor_default().red('ERROR');
33
+ if ('warn' === type) return external_pintor_default().brightYellow("\u25BA\u25BA\u25BA");
34
+ if ('info' === type) return "\u25BA\u25BA\u25BA";
35
+ return external_pintor_default().green("\u25BA\u25BA\u25BA");
36
+ }
37
+ const code = (text)=>external_pintor_default().blue(text);
38
+ const arg = (text)=>external_pintor_default().gray(text);
39
+ function updateFailed(err) {
40
+ return `${getLoggingPrefix('error')} Failed to check for updates.\n${external_pintor_default().red(String((null == err ? void 0 : err.message) || err))}`;
41
+ }
42
+ function checkUpdates(packageJson, update) {
43
+ return `${getLoggingPrefix('info')} \u{1F9E9} ${external_pintor_default().blue('Extension.js')} update available.\n\nYou are currently using version ${external_pintor_default().red(String(packageJson.version))}. Latest stable is ${external_pintor_default().green(String(update.latest))}.\nPlease update to enjoy new features and improvements.`;
44
+ }
45
+ function programUserHelp() {
46
+ return `\n${getLoggingPrefix('info')} ${external_pintor_default().underline('Help center for the Extension.js program')}
62
47
 
63
48
  Usage: extension [command] [options]
64
49
 
@@ -88,52 +73,103 @@ Available Commands
88
73
  Cleans up orphaned instances and frees unused ports
89
74
 
90
75
  Common Options
91
- - ${code('--browser')} ${arg('<chrome|edge|firefox>')} Target browser (default: chrome)
76
+ - ${code('--browser')} ${arg('<chrome|edge|firefox|chromium|chromium-based|gecko-based|firefox-based>')} Target browser/engine (default: chrome)
92
77
  - ${code('--profile')} ${arg('<path|boolean>')} Browser profile configuration
93
78
  - ${code('--polyfill')} ${arg('[boolean]')} Enable/disable cross-browser polyfill
79
+ - ${code('--no-telemetry')} Disable anonymous telemetry for this run
94
80
  - ${code('--port')} ${arg('<number>')} Development server port (default: 8080)
95
81
  - ${code('--starting-url')} ${arg('<url>')} Initial URL to load in browser
96
82
  - ${code('--silent')} ${arg('[boolean]')} Suppress console output during build
97
83
 
98
84
  Source Inspection
99
- - ${code('--source')} ${arg('<url>')} Open URL and print HTML after content scripts inject
100
- - ${code('--watch-source')} Monitor rebuild events and print HTML on reloads
85
+ - ${code('--source')} ${arg('<url|boolean>')} Open URL and print HTML after content scripts inject
86
+ - When provided without a URL, falls back to ${arg('--starting-url')} or ${arg('https://example.com')}
87
+ - Watch mode is enabled by default when ${code('--source')} is present
101
88
 
102
89
  Browser-Specific Options
103
90
  - ${code('--chromium-binary')} ${arg('<path>')} Custom Chromium binary path
104
- - ${code('--gecko-binary')} ${arg('<path>')} Custom Firefox/Gecko binary path
91
+ - ${code('--gecko-binary')}/${code('--firefox-binary')} ${arg('<path>')} Custom Firefox/Gecko binary path
105
92
 
106
93
  Build Options
107
94
  - ${code('--zip')} ${arg('[boolean]')} Create ZIP archive of built extension
108
95
  - ${code('--zip-source')} ${arg('[boolean]')} Include source files in ZIP
109
96
  - ${code('--zip-filename')} ${arg('<name>')} Custom ZIP filename
110
97
 
98
+ ${external_pintor_default().underline('Centralized Logger (terminal output)')}
99
+ - The manager extension embeds a centralized logger that streams events to the CLI.
100
+ - Enable and filter logs directly via ${code('extension dev')} flags:
101
+ - ${code('--logs')} ${arg('<off|error|warn|info|debug|trace>')} Minimum level (default: info)
102
+ - ${code('--log-context')} ${arg('<list|all>')} Contexts: background,content,page,sidebar,popup,options,devtools
103
+ - ${code('--log-format')} ${arg('<pretty|json>')} Output format (default: pretty)
104
+ - ${code('--no-log-timestamps')} Hide ISO timestamps in pretty output
105
+ - ${code('--no-log-color')} Disable color in pretty output
106
+ - ${code('--log-url')} ${arg('<substring|/regex/>')} Filter by event.url
107
+ - ${code('--log-tab')} ${arg('<id>')} Filter by tabId
108
+ - Example: ${code('extension dev ./my-ext --logs=debug --log-context=all --log-format=pretty')}
109
+
111
110
  ${code('extension --help')}
112
111
  This command outputs a help file with key command options.
113
112
 
113
+ ${external_pintor_default().underline('Path Resolution (important)')}
114
+ - Leading ${code('/')} in manifest/HTML means extension root (the directory containing ${code('manifest.json')}).
115
+ - Relative paths resolve from the ${code('manifest.json')} directory.
116
+ - Absolute OS paths are used as-is.
117
+
118
+
114
119
  AI Assistants
115
120
  - For AI-oriented guidance and deep-dive tips, run ${code('extension --ai-help')}
116
121
 
117
122
  Report issues
118
123
  - ${external_pintor_default().underline('https://github.com/cezaraugusto/extension/issues/new')}`;
119
- }
120
- function unsupportedBrowserFlag(value, supported) {
121
- return `${getLoggingPrefix('error')} Unsupported --browser value: ${value}. Supported: ${supported.join(', ')}.`;
122
- }
123
- function programAIHelp() {
124
- return `\n${getLoggingPrefix('info')} ${external_pintor_default().gray('Development tips for extension developers and AI assistants')}
124
+ }
125
+ function unsupportedBrowserFlag(value, supported) {
126
+ return `${getLoggingPrefix('error')} Unsupported --browser value: ${value}. Supported: ${supported.join(', ')}.`;
127
+ }
128
+ function programAIHelp() {
129
+ return `\n${getLoggingPrefix('info')} ${external_pintor_default().gray('Development tips for extension developers and AI assistants')}
125
130
 
126
131
  Browser-Specific Configuration
127
132
  - Use browser prefixes in manifest.json for browser-specific fields:
128
133
  ${code('{"firefox:manifest": 2, "chrome:manifest": 3}')}
129
134
  This applies manifest v2 to Firefox only, v3 to Chrome/Edge.
130
135
 
136
+ Centralized Logger (for AI & CI)
137
+ - Logs from all contexts are centralized by the manager extension and streamed to the CLI.
138
+ - Prefer these flags to control terminal logs during ${code('extension dev')}:
139
+ - ${code('--logs')} ${arg('<off|error|warn|info|debug|trace>')} Minimum level
140
+ - ${code('--log-context')} ${arg('<list|all>')} Contexts to include
141
+ - ${code('--log-format')} ${arg('<pretty|json>')} Pretty for humans; JSON for machines/NDJSON pipelines
142
+ - ${code('--no-log-timestamps')} ${arg(' ')} Disable timestamps (pretty)
143
+ - ${code('--no-log-color')} ${arg(' ')} Disable ANSI colors (pretty)
144
+ - ${code('--log-url')} ${arg('<substring|/regex/>')} Filter by URL
145
+ - ${code('--log-tab')} ${arg('<id>')} Filter by tabId
146
+ - Good CI pattern: ${code('EXTENSION_ENV=development EXTENSION_AUTO_EXIT_MS=6000 extension dev ./ext --logs=info --log-format=json')}
147
+
131
148
  Special Folders for Entrypoints
132
149
  - Use special folders to handle entrypoints and assets not declared in manifest.json:
133
150
  - ${external_pintor_default().underline(code('public/'))} - Static assets automatically copied to build (resolves to output root)
134
151
  - ${external_pintor_default().underline(code('pages/'))} - HTML files not declared in manifest (e.g., welcome pages)
135
152
  - ${external_pintor_default().underline(code("scripts/"))} - JavaScript files not declared in manifest (e.g., executable scripts)
136
153
 
154
+ Predictable Output Paths
155
+ - Core HTML destinations are standardized across browsers so you can reference them safely in code/tests:
156
+ - ${code('devtools_page')} \u{2192} ${code('devtools/index.html')}
157
+ - ${code('sidebar_action.default_panel')} (MV2) and ${code('side_panel.default_path')} (MV3) \u{2192} ${code('sidebar/index.html')}
158
+ - ${code('options_ui.page')} and ${code('options_page')} \u{2192} ${code('options/index.html')}
159
+ - ${code('background.page')} \u{2192} ${code('background/index.html')}
160
+ - ${code('action.default_popup')}, ${code('browser_action.default_popup')}, ${code('page_action.default_popup')} \u{2192} ${code('action/index.html')}
161
+ - Other predictable outputs:
162
+ - ${code('chrome_url_overrides.*')} \u{2192} ${code('chrome_url_overrides/<key>.html')}
163
+ - ${code("content_scripts[n].js/css")} \u{2192} ${code("content_scripts/content-<n>.{js,css}")}
164
+ - ${code('sandbox.pages[]')} \u{2192} ${code('sandbox/page-<n>.html')}
165
+ - ${code("user_scripts.api_script")} \u{2192} ${code("user_scripts/api_script.js")}
166
+ - ${code('icons/*')} \u{2192} ${code('icons/')} (feature-specific icon folders preserved where applicable)
167
+
168
+ Public & Special Folders (Output Behavior)
169
+ - ${external_pintor_default().underline(code('public/'))} is the web root in output. Authors can use ${code('/foo')}, ${code('/public/foo')}, ${code('public/foo')}, or ${code('./public/foo')} and they all emit as ${code('dist/<browser>/foo')}.
170
+ - ${external_pintor_default().underline(code('pages/'))} files emit as ${code('pages/<name>.html')}. Relative assets referenced inside page HTML are emitted under ${code('assets/')} preserving relative structure; public-root URLs are preserved.
171
+ - ${external_pintor_default().underline(code("scripts/"))} files emit as ${code("scripts/<name>.js")} with extracted CSS when applicable.
172
+
137
173
  Shadow DOM for Content Scripts
138
174
  - Add ${code('use shadow-dom')} directive to content scripts for style isolation
139
175
  - Automatically creates ${code('#extension-root')} element with shadow DOM
@@ -191,12 +227,13 @@ Hot Module Replacement (HMR)
191
227
  - Service workers, _locales and manifest changes reload the extension
192
228
 
193
229
  Source Inspection & Real-Time Monitoring
194
- - Use ${code('--source')} ${arg('<url>')} to inspect page HTML after content script injection
195
- - Use ${code('--watch-source')} to monitor real-time changes in stdout
230
+ - Use ${code('--source')} ${arg('<url|boolean>')} to inspect page HTML after content script injection
231
+ - When no URL is provided, falls back to ${arg('--starting-url')} or ${arg('https://example.com')}
232
+ - Watch mode is enabled by default when ${code('--source')} is present
196
233
  - Automatically enables Chrome remote debugging (port 9222) when source inspection is active
197
- - Extracts Shadow DOM content from ${code('#extension-root')} elements
234
+ - Extracts Shadow DOM content from ${code('#extension-root')} or ${code('[data-extension-root=\"true\"]')} elements
198
235
  - Perfect for debugging content script behavior and style injection
199
- - Example: ${code('extension dev --source=' + arg('https://example.com') + ' --watch-source')}
236
+ - Example: ${code('extension dev --source=' + arg('https://example.com'))}
200
237
 
201
238
  Non-Destructive Testing in CI
202
239
  - Prefer ${code('EXTENSION_ENV=development')} to copy local templates and avoid network.
@@ -222,123 +259,680 @@ Cross-Browser Compatibility
222
259
  - Use ${code('--polyfill')} flag to enable webextension-polyfill
223
260
  - Automatically handles browser API differences
224
261
  - Supports Chrome, Edge, Firefox with single codebase`;
262
+ }
263
+ const external_semver_namespaceObject = require("semver");
264
+ function isStableVersion(version) {
265
+ const v = external_semver_namespaceObject.parse(version);
266
+ return Boolean(v && 0 === v.prerelease.length);
267
+ }
268
+ async function check_updates_checkUpdates(packageJson) {
269
+ let update = null;
270
+ try {
271
+ update = await external_update_check_default()(packageJson);
272
+ } catch (err) {
273
+ if ('development' === process.env.EXTENSION_ENV) console.error(updateFailed(err));
225
274
  }
226
- const external_update_check_namespaceObject = require("update-check");
227
- var external_update_check_default = /*#__PURE__*/ __webpack_require__.n(external_update_check_namespaceObject);
228
- function isStableVersion(version) {
229
- return !/[a-zA-Z]/.test(version);
275
+ if (update && isStableVersion(update.latest)) console.log(checkUpdates(packageJson, update));
276
+ }
277
+ const external_fs_namespaceObject = require("fs");
278
+ var external_fs_default = /*#__PURE__*/ __webpack_require__.n(external_fs_namespaceObject);
279
+ const external_path_namespaceObject = require("path");
280
+ var external_path_default = /*#__PURE__*/ __webpack_require__.n(external_path_namespaceObject);
281
+ const external_node_os_namespaceObject = require("node:os");
282
+ var external_node_os_default = /*#__PURE__*/ __webpack_require__.n(external_node_os_namespaceObject);
283
+ const external_node_fs_namespaceObject = require("node:fs");
284
+ var external_node_fs_default = /*#__PURE__*/ __webpack_require__.n(external_node_fs_namespaceObject);
285
+ const external_node_path_namespaceObject = require("node:path");
286
+ var external_node_path_default = /*#__PURE__*/ __webpack_require__.n(external_node_path_namespaceObject);
287
+ const external_node_crypto_namespaceObject = require("node:crypto");
288
+ var external_node_crypto_default = /*#__PURE__*/ __webpack_require__.n(external_node_crypto_namespaceObject);
289
+ function _define_property(obj, key, value) {
290
+ if (key in obj) Object.defineProperty(obj, key, {
291
+ value: value,
292
+ enumerable: true,
293
+ configurable: true,
294
+ writable: true
295
+ });
296
+ else obj[key] = value;
297
+ return obj;
298
+ }
299
+ function isCI() {
300
+ const v = process.env;
301
+ return Boolean(v.CI || v.GITHUB_ACTIONS || v.GITLAB_CI || v.BUILDKITE || v.CIRCLECI || v.TRAVIS);
302
+ }
303
+ function configDir() {
304
+ const xdg = process.env.XDG_CONFIG_HOME;
305
+ if (xdg) return external_node_path_default().join(xdg, 'extensionjs');
306
+ if ('win32' === process.platform && process.env.APPDATA) return external_node_path_default().join(process.env.APPDATA, 'extensionjs');
307
+ return external_node_path_default().join(external_node_os_default().homedir(), '.config', 'extensionjs');
308
+ }
309
+ function ensureDir(p) {
310
+ if (external_node_fs_default().existsSync(p)) return;
311
+ external_node_fs_default().mkdirSync(p, {
312
+ recursive: true
313
+ });
314
+ }
315
+ function loadOrCreateId(file) {
316
+ if (external_node_fs_default().existsSync(file)) return external_node_fs_default().readFileSync(file, 'utf8').trim();
317
+ const id = external_node_crypto_default().randomUUID();
318
+ ensureDir(external_node_path_default().dirname(file));
319
+ external_node_fs_default().writeFileSync(file, id, 'utf8');
320
+ return id;
321
+ }
322
+ function auditFilePath() {
323
+ const dir = external_node_path_default().join(configDir(), 'telemetry');
324
+ ensureDir(dir);
325
+ return external_node_path_default().join(dir, 'events.jsonl');
326
+ }
327
+ const DEFAULT_FLUSH_AT = Number(process.env.EXTENSION_TELEMETRY_FLUSH_AT || 10);
328
+ const DEFAULT_FLUSH_INTERVAL = Number(process.env.EXTENSION_TELEMETRY_FLUSH_INTERVAL || 2000);
329
+ const DEFAULT_TIMEOUT_MS = Number(process.env.EXTENSION_TELEMETRY_TIMEOUT_MS || 200);
330
+ class Telemetry {
331
+ track(event, props = {}) {
332
+ if (this.disabled) return;
333
+ const payload = {
334
+ event,
335
+ properties: {
336
+ ...this.common,
337
+ ...props,
338
+ $ip: null
339
+ },
340
+ distinct_id: this.anonId
341
+ };
342
+ external_node_fs_default().appendFileSync(auditFilePath(), JSON.stringify(payload) + '\n');
343
+ if (this.debug) console.error('[telemetry]', JSON.stringify(payload));
344
+ if (!this.apiKey || !this.host) return;
345
+ this.buffer.push(payload);
346
+ if (this.buffer.length >= DEFAULT_FLUSH_AT) return void this.flush();
347
+ if (!this.timer) this.timer = setTimeout(()=>{
348
+ this.timer = null;
349
+ this.flush();
350
+ }, DEFAULT_FLUSH_INTERVAL);
230
351
  }
231
- async function check_updates_checkUpdates(packageJson) {
232
- let update = null;
352
+ async flush() {
353
+ if (this.disabled || !this.apiKey || !this.host) return;
354
+ if (0 === this.buffer.length) return;
355
+ const batch = this.buffer.splice(0, this.buffer.length);
233
356
  try {
234
- update = await external_update_check_default()(packageJson);
235
- } catch (err) {
236
- if ('development' === process.env.EXTENSION_ENV) console.error(updateFailed(err));
357
+ const ac = new AbortController();
358
+ const t = setTimeout(()=>ac.abort(), DEFAULT_TIMEOUT_MS);
359
+ const url = new URL('/capture/', this.host);
360
+ await fetch(url.toString(), {
361
+ method: 'POST',
362
+ headers: {
363
+ 'content-type': 'application/json'
364
+ },
365
+ body: JSON.stringify({
366
+ api_key: this.apiKey,
367
+ batch: batch.map((e)=>({
368
+ event: e.event,
369
+ properties: e.properties,
370
+ distinct_id: e.distinct_id
371
+ }))
372
+ }),
373
+ signal: ac.signal,
374
+ keepalive: true
375
+ }).catch(()=>{});
376
+ clearTimeout(t);
377
+ } catch {}
378
+ }
379
+ shutdown() {}
380
+ constructor(init){
381
+ _define_property(this, "anonId", void 0);
382
+ _define_property(this, "common", void 0);
383
+ _define_property(this, "debug", void 0);
384
+ _define_property(this, "disabled", void 0);
385
+ _define_property(this, "apiKey", void 0);
386
+ _define_property(this, "host", void 0);
387
+ _define_property(this, "buffer", []);
388
+ _define_property(this, "timer", null);
389
+ this.debug = '1' === process.env.EXTENSION_TELEMETRY_DEBUG;
390
+ this.disabled = Boolean(init.disabled);
391
+ this.anonId = 'disabled';
392
+ if (!this.disabled) {
393
+ const idFile = external_node_path_default().join(configDir(), 'telemetry', 'anonymous-id');
394
+ this.anonId = loadOrCreateId(idFile);
395
+ }
396
+ this.common = {
397
+ app: init.app,
398
+ version: init.version,
399
+ os: process.platform,
400
+ arch: process.arch,
401
+ node: process.versions.node,
402
+ is_ci: isCI(),
403
+ schema_version: 1
404
+ };
405
+ this.apiKey = init.apiKey || process.env.EXTENSION_PUBLIC_POSTHOG_KEY;
406
+ this.host = init.host || process.env.EXTENSION_PUBLIC_POSTHOG_HOST;
407
+ if (!this.disabled) {
408
+ const consentPath = external_node_path_default().join(configDir(), 'telemetry', 'consent');
409
+ if (!external_node_fs_default().existsSync(consentPath)) {
410
+ external_node_fs_default().writeFileSync(consentPath, 'ok', 'utf8');
411
+ this.track('cli_telemetry_consent', {
412
+ value: 'implicit_opt_in'
413
+ });
414
+ console.log('[extension] anonymous telemetry helps us improve. Pass --no-telemetry to opt out. Read more in TELEMETRY.md.');
415
+ }
237
416
  }
238
- if (update && isStableVersion(update.latest)) console.log(checkUpdates(packageJson, update));
239
417
  }
240
- var package_namespaceObject = JSON.parse('{"license":"MIT","repository":{"type":"git","url":"https://github.com/extension-js/extension.js.git","directory":"programs/cli"},"engines":{"node":">=18"},"exports":{".":{"types":"./dist/cli.d.ts","import":"./dist/cli.js","require":"./dist/cli.js"}},"main":"./dist/cli.js","types":"./dist/cli.d.ts","files":["dist","types"],"bin":{"extension":"./dist/cli.js"},"name":"extension","version":"2.1.3","description":"Create cross-browser extensions with no build configuration.","homepage":"https://extension.js.org/","author":{"name":"Cezar Augusto","email":"boss@cezaraugusto.net","url":"https://cezaraugusto.com"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org","tag":"latest"},"scripts":{"watch":"rslib build --watch","compile":"rslib build","clean":"rm -rf dist","test":"echo \\"Note: no test specified\\" && exit 0","test:cli":"vitest run"},"keywords":["zero-config","build","develop","browser","extension","chrome extension","edge extension","firefox extension","safari extension","web","react","typescript","webextension","browser-extension","chrome-extension","firefox-addon","edge-extension","safari-web-extension","manifest-v3","mv3","cross-browser","content-script","background-script","devtools","create-extension","scaffold","starter-template","boilerplate","cli"],"dependencies":{"@types/chrome":"^0.0.287","@types/node":"^22.10.1","@types/react":"^19.0.1","@types/react-dom":"^19.0.1","@types/webextension-polyfill":"0.12.3","commander":"^12.1.0","extension-create":"workspace:*","extension-develop":"workspace:*","pintor":"0.3.0","semver":"^7.6.3","update-check":"^1.5.4","webextension-polyfill":"^0.12.0"},"devDependencies":{"@rslib/core":"^0.6.9","@types/mock-fs":"^4.13.4","@types/semver":"^7.5.8","mock-fs":"^5.4.1","tsconfig":"*","typescript":"5.7.2","vitest":"3.2.2"}}');
241
- function parseOptionalBoolean(value) {
242
- if (void 0 === value) return true;
243
- const normalized = String(value).trim().toLowerCase();
244
- return ![
245
- 'false',
246
- '0',
247
- 'no',
248
- 'off'
249
- ].includes(normalized);
418
+ }
419
+ function summarizeManifest(manifest) {
420
+ var _manifest_action;
421
+ const mv = (null == manifest ? void 0 : manifest.manifest_version) === 2 ? 2 : 3;
422
+ const permissions = Array.isArray(null == manifest ? void 0 : manifest.permissions) ? manifest.permissions : [];
423
+ const optionalPermissions = Array.isArray(null == manifest ? void 0 : manifest.optional_permissions) ? manifest.optional_permissions : [];
424
+ const hostPermissions = Array.isArray(null == manifest ? void 0 : manifest.host_permissions) ? manifest.host_permissions : [];
425
+ const usesAllUrls = [
426
+ ...permissions,
427
+ ...hostPermissions
428
+ ].includes('<all_urls>');
429
+ const usesDeclarativeNetRequest = permissions.includes('declarativeNetRequest') || permissions.includes('declarativeNetRequestWithHostAccess');
430
+ const background = null == manifest ? void 0 : manifest.background;
431
+ let backgroundType = 'none';
432
+ if (3 === mv && (null == background ? void 0 : background.service_worker)) backgroundType = 'service_worker';
433
+ else if (2 === mv && (Array.isArray(null == background ? void 0 : background.scripts) && background.scripts.length > 0 || (null == background ? void 0 : background.page))) backgroundType = 'event_page';
434
+ const contentScriptsCount = Array.isArray(null == manifest ? void 0 : manifest.content_scripts) ? manifest.content_scripts.length : 0;
435
+ const hasDevtoolsPage = Boolean(null == manifest ? void 0 : manifest.devtools_page);
436
+ const hasActionPopup = Boolean(null == manifest ? void 0 : null == (_manifest_action = manifest.action) ? void 0 : _manifest_action.default_popup);
437
+ return {
438
+ mv,
439
+ permissions_count: permissions.length,
440
+ optional_permissions_count: optionalPermissions.length,
441
+ host_permissions_count: hostPermissions.length,
442
+ uses_all_urls: usesAllUrls,
443
+ uses_declarative_net_request: usesDeclarativeNetRequest,
444
+ background_type: backgroundType,
445
+ content_scripts_count: contentScriptsCount,
446
+ has_devtools_page: hasDevtoolsPage,
447
+ has_action_popup: hasActionPopup
448
+ };
449
+ }
450
+ function isTelemetryDisabledFromArgs(argv) {
451
+ return argv.includes('--no-telemetry');
452
+ }
453
+ const telemetryDisabled = isTelemetryDisabledFromArgs(process.argv);
454
+ const telemetry_cli_telemetry = new Telemetry({
455
+ app: 'extension',
456
+ version: package_namespaceObject.version,
457
+ disabled: telemetryDisabled
458
+ });
459
+ if (!telemetryDisabled) {
460
+ const startedAt = Date.now();
461
+ const known = new Set([
462
+ 'create',
463
+ 'dev',
464
+ 'start',
465
+ 'preview',
466
+ 'build',
467
+ 'cleanup'
468
+ ]);
469
+ const invoked = process.argv.slice(2).find((a)=>known.has(a)) || 'unknown';
470
+ telemetry_cli_telemetry.track('cli_boot', {
471
+ command_guess: invoked
472
+ });
473
+ const cwd = process.cwd();
474
+ const manifestPath = external_path_default().join(cwd, 'manifest.json');
475
+ if (external_fs_default().existsSync(manifestPath)) {
476
+ const raw = external_fs_default().readFileSync(manifestPath, 'utf8');
477
+ const json = JSON.parse(raw);
478
+ const summary = summarizeManifest(json);
479
+ telemetry_cli_telemetry.track('manifest_summary', summary);
250
480
  }
251
- check_updates_checkUpdates(package_namespaceObject);
252
- const extensionJs = external_commander_namespaceObject.program;
253
- const vendors = (browser)=>'all' === browser ? 'chrome,edge,firefox'.split(',') : browser.split(',');
254
- function validateVendorsOrExit(vendorsList) {
255
- const supported = [
256
- 'chrome',
257
- 'edge',
258
- 'firefox'
481
+ process.on('beforeExit', async function() {
482
+ telemetry_cli_telemetry.track('cli_shutdown', {
483
+ command_guess: invoked,
484
+ duration_ms: Date.now() - startedAt,
485
+ exit_code: process.exitCode ?? 0
486
+ });
487
+ await telemetry_cli_telemetry.flush();
488
+ });
489
+ process.on('uncaughtException', function(err) {
490
+ telemetry_cli_telemetry.track('cli_error', {
491
+ command_guess: invoked,
492
+ error_name: String((null == err ? void 0 : err.name) || 'Error').slice(0, 64)
493
+ });
494
+ });
495
+ process.on('unhandledRejection', function(reason) {
496
+ telemetry_cli_telemetry.track('cli_error', {
497
+ command_guess: invoked,
498
+ error_name: String((null == reason ? void 0 : reason.name) || 'PromiseRejection').slice(0, 64)
499
+ });
500
+ });
501
+ }
502
+ const external_extension_create_namespaceObject = require("extension-create");
503
+ const external_node_child_process_namespaceObject = require("node:child_process");
504
+ const external_node_url_namespaceObject = require("node:url");
505
+ function parseOptionalBoolean(value) {
506
+ if (void 0 === value) return true;
507
+ const normalized = String(value).trim().toLowerCase();
508
+ return ![
509
+ 'false',
510
+ '0',
511
+ 'no',
512
+ 'off'
513
+ ].includes(normalized);
514
+ }
515
+ async function requireOrDlx(moduleName, versionHint) {
516
+ try {
517
+ return await import(moduleName);
518
+ } catch {}
519
+ const spec = versionHint ? `${moduleName}@${versionHint}` : moduleName;
520
+ const cacheDir = external_node_path_default().join(external_node_os_default().tmpdir(), 'extensionjs-cache', spec);
521
+ const modulePath = external_node_path_default().join(cacheDir, 'node_modules', moduleName);
522
+ const prefer = String(process.env.EXTJS_DLX || '').trim().toLowerCase();
523
+ const isWin = 'win32' === process.platform;
524
+ const npmCmd = isWin ? 'npm.cmd' : 'npm';
525
+ const pnpmCmd = isWin ? 'pnpm.cmd' : 'pnpm';
526
+ const bunCmd = isWin ? 'bun.exe' : 'bun';
527
+ try {
528
+ external_node_fs_default().mkdirSync(cacheDir, {
529
+ recursive: true
530
+ });
531
+ } catch {}
532
+ try {
533
+ var _pkgJson_exports_, _pkgJson_exports, _pkgJson_exports_1, _pkgJson_exports1;
534
+ const pkgJson = JSON.parse(external_node_fs_default().readFileSync(external_node_path_default().join(modulePath, 'package.json'), 'utf8'));
535
+ const main = pkgJson.main || (null == (_pkgJson_exports = pkgJson.exports) ? void 0 : null == (_pkgJson_exports_ = _pkgJson_exports['.']) ? void 0 : _pkgJson_exports_.import) || (null == (_pkgJson_exports1 = pkgJson.exports) ? void 0 : null == (_pkgJson_exports_1 = _pkgJson_exports1['.']) ? void 0 : _pkgJson_exports_1.require);
536
+ if (main) {
537
+ const resolved = (0, external_node_url_namespaceObject.pathToFileURL)(external_node_path_default().join(modulePath, main)).href;
538
+ return await import(resolved);
539
+ }
540
+ } catch {}
541
+ if ('pnpm' === prefer) try {
542
+ external_node_fs_default().writeFileSync(external_node_path_default().join(cacheDir, 'package.json'), JSON.stringify({
543
+ name: 'extensionjs-cache',
544
+ private: true
545
+ }, null, 2));
546
+ } catch {}
547
+ let status = 0;
548
+ if ('pnpm' === prefer) {
549
+ const args = [
550
+ 'add',
551
+ spec,
552
+ '--reporter',
553
+ 'silent',
554
+ '--no-frozen-lockfile'
259
555
  ];
260
- for (const v of vendorsList)if (!supported.includes(v)) {
261
- console.error(unsupportedBrowserFlag(v, supported));
262
- process.exit(1);
556
+ status = (0, external_node_child_process_namespaceObject.spawnSync)(pnpmCmd, args, {
557
+ cwd: cacheDir,
558
+ stdio: 'ignore'
559
+ }).status || 0;
560
+ } else if ('bun' === prefer) {
561
+ const args = [
562
+ 'add',
563
+ spec
564
+ ];
565
+ status = (0, external_node_child_process_namespaceObject.spawnSync)(bunCmd, args, {
566
+ cwd: cacheDir,
567
+ stdio: 'ignore'
568
+ }).status || 0;
569
+ } else {
570
+ const args = [
571
+ 'i',
572
+ spec,
573
+ '--no-fund',
574
+ '--no-audit',
575
+ '--prefer-online',
576
+ '--omit=dev',
577
+ '--no-package-lock'
578
+ ];
579
+ status = (0, external_node_child_process_namespaceObject.spawnSync)(npmCmd, args, {
580
+ cwd: cacheDir,
581
+ stdio: 'ignore'
582
+ }).status || 0;
583
+ }
584
+ if (0 !== status) throw new Error(`Failed to install ${spec}`);
585
+ try {
586
+ var _pkgJson_exports_2, _pkgJson_exports2, _pkgJson_exports_3, _pkgJson_exports3;
587
+ const pkgJson = JSON.parse(external_node_fs_default().readFileSync(external_node_path_default().join(modulePath, 'package.json'), 'utf8'));
588
+ const main = pkgJson.main || (null == (_pkgJson_exports2 = pkgJson.exports) ? void 0 : null == (_pkgJson_exports_2 = _pkgJson_exports2['.']) ? void 0 : _pkgJson_exports_2.import) || (null == (_pkgJson_exports3 = pkgJson.exports) ? void 0 : null == (_pkgJson_exports_3 = _pkgJson_exports3['.']) ? void 0 : _pkgJson_exports_3.require);
589
+ if (main) {
590
+ const resolved = (0, external_node_url_namespaceObject.pathToFileURL)(external_node_path_default().join(modulePath, main)).href;
591
+ return await import(resolved);
263
592
  }
593
+ } catch {}
594
+ return await import((0, external_node_url_namespaceObject.pathToFileURL)(external_node_path_default().join(modulePath, 'dist', 'module.js')).href);
595
+ }
596
+ const vendors = (browser)=>{
597
+ const value = browser ?? 'chromium';
598
+ return 'all' === value ? [
599
+ 'chrome',
600
+ 'edge',
601
+ 'firefox'
602
+ ] : String(value).split(',');
603
+ };
604
+ function validateVendorsOrExit(vendorsList, onInvalid) {
605
+ const supported = [
606
+ 'chrome',
607
+ 'edge',
608
+ 'firefox',
609
+ 'chromium',
610
+ 'chromium-based',
611
+ 'gecko-based',
612
+ 'firefox-based'
613
+ ];
614
+ for (const v of vendorsList)if (!supported.includes(v)) {
615
+ onInvalid(v, supported);
616
+ process.exit(1);
264
617
  }
265
- extensionJs.name(package_namespaceObject.name).description(package_namespaceObject.description).version(package_namespaceObject.version).option('--ai-help', 'show AI-assistant oriented help and tips').addHelpText('after', programUserHelp());
266
- extensionJs.command('create').arguments('<project-name|project-path>').usage('create <project-name|project-path> [options]').description('Creates a new extension.').option('-t, --template <template-name>', 'specify a template for the created project').option('--install [boolean]', 'whether or not to install the dependencies after creating the project (disabled by default)', parseOptionalBoolean, false).action(async function(pathOrRemoteUrl, { template, install }) {
267
- await (0, external_extension_create_namespaceObject.extensionCreate)(pathOrRemoteUrl, {
268
- template,
269
- install,
270
- cliVersion: package_namespaceObject.version
618
+ }
619
+ function registerCreateCommand(program, telemetry) {
620
+ program.command('create').arguments('<project-name|project-path>').usage('create <project-name|project-path> [options]').description('Creates a new extension.').option('-t, --template <template-name>', 'specify a template for the created project').option('--install [boolean]', 'whether or not to install the dependencies after creating the project (disabled by default)', parseOptionalBoolean, false).action(async function(pathOrRemoteUrl, { template, install }) {
621
+ const startedAt = Date.now();
622
+ telemetry.track('cli_command_start', {
623
+ command: 'create',
624
+ template: template || 'default',
625
+ install: Boolean(install)
271
626
  });
627
+ try {
628
+ await (0, external_extension_create_namespaceObject.extensionCreate)(pathOrRemoteUrl, {
629
+ template,
630
+ install,
631
+ cliVersion: package_namespaceObject.version
632
+ });
633
+ telemetry.track('cli_command_finish', {
634
+ command: 'create',
635
+ duration_ms: Date.now() - startedAt,
636
+ success: true,
637
+ exit_code: 0
638
+ });
639
+ } catch (err) {
640
+ telemetry.track('cli_command_finish', {
641
+ command: 'create',
642
+ duration_ms: Date.now() - startedAt,
643
+ success: false,
644
+ exit_code: 1
645
+ });
646
+ throw err;
647
+ }
272
648
  });
273
- extensionJs.command('dev').arguments('[project-path|remote-url]').usage('dev [project-path|remote-url] [options]').description('Starts the development server (development mode)').option('--profile <path-to-file | boolean>', 'what path to use for the browser profile. A boolean value of false sets the profile to the default user profile. Defaults to a fresh profile').option('--browser <chrome | edge | firefox>', 'specify a browser to preview your extension in production mode. Defaults to `chrome`').option('--chromium-binary <path-to-binary>', 'specify a path to the Chromium binary. This option overrides the --browser setting. Defaults to the system default').option('--gecko-binary <path-to-binary>', 'specify a path to the Gecko binary. This option overrides the --browser setting. Defaults to the system default').option('--polyfill [boolean]', 'whether or not to apply the cross-browser polyfill. Defaults to `false`').option('--open [boolean]', 'whether or not to open the browser automatically. Defaults to `true`').option('--starting-url <url>', 'specify the starting URL for the browser. Defaults to `undefined`').option('--port <port>', 'specify the port to use for the development server. Defaults to `8080`').option('--source [url]', "opens the provided URL in Chrome and prints the full, live HTML of the page after content scripts are injected").option('--watch-source', 'continuously monitors rebuild events and prints updated HTML whenever the extension reloads and reinjects into the page').action(async function(pathOrRemoteUrl, { browser = 'chrome', ...devOptions }) {
649
+ }
650
+ function registerDevCommand(program, telemetry) {
651
+ program.command('dev').arguments('[project-path|remote-url]').usage('dev [project-path|remote-url] [options]').description('Starts the development server (development mode)').option('--profile <path-to-file | boolean>', 'what path to use for the browser profile. A boolean value of false sets the profile to the default user profile. Defaults to a fresh profile').option('--browser <chrome | chromium | edge | firefox | chromium-based | gecko-based | firefox-based>', 'specify a browser/engine to run. Defaults to `chromium`').option('--chromium-binary <path-to-binary>', 'specify a path to the Chromium binary. This option overrides the --browser setting. Defaults to the system default').option('--gecko-binary, --firefox-binary <path-to-binary>', 'specify a path to the Gecko binary. This option overrides the --browser setting. Defaults to the system default').option('--polyfill [boolean]', 'whether or not to apply the cross-browser polyfill. Defaults to `false`').option('--no-open', 'do not open the browser automatically (default: open)').option('--starting-url <url>', 'specify the starting URL for the browser. Defaults to `undefined`').option('--port <port>', 'specify the port to use for the development server. Defaults to `8080`').option('--log-context <list>', '[experimental] comma-separated contexts to include (background,content,page,sidebar,popup,options,devtools). Use `all` to include all contexts (default)').option('--logs <off|error|warn|info|debug|trace|all>', '[experimental] minimum centralized logger level to display in terminal (default: off)').option('--log-format <pretty|json>', '[experimental] output format for logger events. Defaults to `pretty`').option('--no-log-timestamps', 'disable ISO timestamps in pretty output').option('--no-log-color', 'disable color in pretty output').option('--log-url <pattern>', '[experimental] only show logs where event.url matches this substring or regex (/re/i)').option('--log-tab <id>', 'only show logs for a specific tabId (number)').option('--source [url]', "[experimental] opens the provided URL in Chrome and prints the full, live HTML of the page after content scripts are injected").action(async function(pathOrRemoteUrl, { browser = 'chromium', ...devOptions }) {
652
+ var _devOptions_polyfill;
653
+ const cmdStart = Date.now();
654
+ telemetry.track('cli_command_start', {
655
+ command: 'dev',
656
+ vendors: vendors(browser),
657
+ polyfill_used: (null == (_devOptions_polyfill = devOptions.polyfill) ? void 0 : _devOptions_polyfill.toString()) !== 'false',
658
+ log_level: devOptions.logLevel || 'off',
659
+ log_format: devOptions.logFormat || 'pretty',
660
+ custom_binary_used: Boolean(devOptions.chromiumBinary || devOptions.geckoBinary)
661
+ });
274
662
  const list = vendors(browser);
275
- validateVendorsOrExit(list);
276
- const { extensionDev } = await Promise.resolve().then(__webpack_require__.bind(__webpack_require__, "extension-develop"));
663
+ validateVendorsOrExit(list, (invalid, supported)=>{
664
+ console.error(unsupportedBrowserFlag(invalid, supported));
665
+ });
666
+ if (devOptions.source) {
667
+ const hasExplicitSourceString = 'string' == typeof devOptions.source && 'true' !== String(devOptions.source).trim().toLowerCase();
668
+ const hasStartingUrl = 'string' == typeof devOptions.startingUrl && String(devOptions.startingUrl).trim().length > 0;
669
+ if (!hasExplicitSourceString) devOptions.source = hasStartingUrl ? String(devOptions.startingUrl) : 'https://example.com';
670
+ devOptions.watchSource = true;
671
+ }
672
+ const major = String(package_namespaceObject.version).split('.')[0] || '2';
673
+ const { extensionDev } = await requireOrDlx('extension-develop', major);
277
674
  for (const vendor of list){
278
- var _devOptions_polyfill;
279
- await extensionDev(pathOrRemoteUrl, {
675
+ var _devOptions_polyfill1;
676
+ const vendorStart = Date.now();
677
+ telemetry.track('cli_vendor_start', {
678
+ command: 'dev',
679
+ vendor
680
+ });
681
+ const logsOption = devOptions.logs;
682
+ const logContextOption = devOptions.logContext;
683
+ const devArgs = {
280
684
  ...devOptions,
281
685
  profile: devOptions.profile,
282
686
  browser: vendor,
283
687
  chromiumBinary: devOptions.chromiumBinary,
284
688
  geckoBinary: devOptions.geckoBinary,
285
- polyfill: (null == (_devOptions_polyfill = devOptions.polyfill) ? void 0 : _devOptions_polyfill.toString()) !== 'false',
689
+ polyfill: (null == (_devOptions_polyfill1 = devOptions.polyfill) ? void 0 : _devOptions_polyfill1.toString()) !== 'false',
286
690
  open: devOptions.open,
287
691
  startingUrl: devOptions.startingUrl,
288
692
  source: devOptions.source,
289
- watchSource: devOptions.watchSource
693
+ watchSource: devOptions.watchSource,
694
+ logLevel: logsOption || devOptions.logLevel || 'off',
695
+ logContexts: (()=>{
696
+ const raw = logContextOption || devOptions.logContexts;
697
+ if (!raw || 0 === String(raw).trim().length) return;
698
+ if ('all' === String(raw).trim().toLowerCase()) return;
699
+ const allowed = [
700
+ 'background',
701
+ 'content',
702
+ 'page',
703
+ 'sidebar',
704
+ 'popup',
705
+ 'options',
706
+ 'devtools'
707
+ ];
708
+ const values = String(raw).split(',').map((s)=>s.trim()).filter((s)=>s.length > 0).filter((c)=>allowed.includes(c));
709
+ return values.length ? values : void 0;
710
+ })(),
711
+ logFormat: devOptions.logFormat || 'pretty',
712
+ logTimestamps: false !== devOptions.logTimestamps,
713
+ logColor: false !== devOptions.logColor,
714
+ logUrl: devOptions.logUrl,
715
+ logTab: devOptions.logTab
716
+ };
717
+ await extensionDev(pathOrRemoteUrl, devArgs);
718
+ telemetry.track('cli_vendor_finish', {
719
+ command: 'dev',
720
+ vendor,
721
+ duration_ms: Date.now() - vendorStart
290
722
  });
291
723
  }
724
+ telemetry.track('cli_command_finish', {
725
+ command: 'dev',
726
+ duration_ms: Date.now() - cmdStart,
727
+ success: 0 === process.exitCode || null == process.exitCode,
728
+ exit_code: process.exitCode ?? 0
729
+ });
292
730
  });
293
- extensionJs.command('start').arguments('[project-path|remote-url]').usage('start [project-path|remote-url] [options]').description('Starts the development server (production mode)').option('--profile <path-to-file | boolean>', 'what path to use for the browser profile. A boolean value of false sets the profile to the default user profile. Defaults to a fresh profile').option('--browser <chrome | edge | firefox>', 'specify a browser to preview your extension in production mode. Defaults to `chrome`').option('--polyfill [boolean]', 'whether or not to apply the cross-browser polyfill. Defaults to `true`').option('--chromium-binary <path-to-binary>', 'specify a path to the Chromium binary. This option overrides the --browser setting. Defaults to the system default').option('--gecko-binary <path-to-binary>', 'specify a path to the Gecko binary. This option overrides the --browser setting. Defaults to the system default').option('--starting-url <url>', 'specify the starting URL for the browser. Defaults to `undefined`').option('--port <port>', 'specify the port to use for the development server. Defaults to `8080`').action(async function(pathOrRemoteUrl, { browser = 'chrome', ...startOptions }) {
731
+ }
732
+ function registerStartCommand(program, telemetry) {
733
+ program.command('start').arguments('[project-path|remote-url]').usage('start [project-path|remote-url] [options]').description('Starts the development server (production mode)').option('--profile <path-to-file | boolean>', 'what path to use for the browser profile. A boolean value of false sets the profile to the default user profile. Defaults to a fresh profile').option('--browser <chrome | chromium | edge | firefox | chromium-based | gecko-based | firefox-based>', 'specify a browser/engine to run. Defaults to `chromium`').option('--polyfill [boolean]', 'whether or not to apply the cross-browser polyfill. Defaults to `true`').option('--chromium-binary <path-to-binary>', 'specify a path to the Chromium binary. This option overrides the --browser setting. Defaults to the system default').option('--gecko-binary, --firefox-binary <path-to-binary>', 'specify a path to the Gecko binary. This option overrides the --browser setting. Defaults to the system default').option('--starting-url <url>', 'specify the starting URL for the browser. Defaults to `undefined`').option('--port <port>', 'specify the port to use for the development server. Defaults to `8080`').option('--log-context <list>', '[experimental] comma-separated contexts to include (background,content,page,sidebar,popup,options,devtools). Use `all` to include all contexts (default)').option('--logs <off|error|warn|info|debug|trace|all>', '[experimental] minimum centralized logger level to display in terminal (default: off)').option('--log-format <pretty|json>', '[experimental] output format for logger events. Defaults to `pretty`').option('--no-log-timestamps', 'disable ISO timestamps in pretty output').option('--no-log-color', 'disable color in pretty output').option('--log-url <pattern>', '[experimental] only show logs where event.url matches this substring or regex (/re/i)').option('--log-tab <id>', 'only show logs for a specific tabId (number)').option('--source [url]', "[experimental] opens the provided URL in Chrome and prints the full, live HTML of the page after content scripts are injected").action(async function(pathOrRemoteUrl, { browser = 'chromium', ...startOptions }) {
734
+ var _startOptions_polyfill;
735
+ const cmdStart = Date.now();
736
+ telemetry.track('cli_command_start', {
737
+ command: 'start',
738
+ vendors: vendors(browser),
739
+ polyfill_used: (null == (_startOptions_polyfill = startOptions.polyfill) ? void 0 : _startOptions_polyfill.toString()) !== 'false'
740
+ });
294
741
  const list = vendors(browser);
295
- validateVendorsOrExit(list);
296
- const { extensionStart } = await Promise.resolve().then(__webpack_require__.bind(__webpack_require__, "extension-develop"));
297
- for (const vendor of list)await extensionStart(pathOrRemoteUrl, {
298
- mode: 'production',
299
- profile: startOptions.profile,
300
- browser: vendor,
301
- chromiumBinary: startOptions.chromiumBinary,
302
- geckoBinary: startOptions.geckoBinary,
303
- startingUrl: startOptions.startingUrl
742
+ validateVendorsOrExit(list, (invalid, supported)=>{
743
+ console.error(unsupportedBrowserFlag(invalid, supported));
744
+ });
745
+ const major = String(package_namespaceObject.version).split('.')[0] || '2';
746
+ const { extensionStart } = await requireOrDlx('extension-develop', major);
747
+ for (const vendor of list){
748
+ const vendorStart = Date.now();
749
+ telemetry.track('cli_vendor_start', {
750
+ command: 'start',
751
+ vendor
752
+ });
753
+ const logsOption = startOptions.logs;
754
+ const logContextOption = startOptions.logContext;
755
+ const logContexts = (()=>{
756
+ const raw = logContextOption || startOptions.logContexts;
757
+ if (!raw || 0 === String(raw).trim().length) return;
758
+ if ('all' === String(raw).trim().toLowerCase()) return;
759
+ const allowed = [
760
+ 'background',
761
+ 'content',
762
+ 'page',
763
+ 'sidebar',
764
+ 'popup',
765
+ 'options',
766
+ 'devtools'
767
+ ];
768
+ const values = String(raw).split(',').map((s)=>s.trim()).filter((s)=>s.length > 0).filter((c)=>allowed.includes(c));
769
+ return values.length ? values : void 0;
770
+ })();
771
+ await extensionStart(pathOrRemoteUrl, {
772
+ mode: 'production',
773
+ profile: startOptions.profile,
774
+ browser: vendor,
775
+ chromiumBinary: startOptions.chromiumBinary,
776
+ geckoBinary: startOptions.geckoBinary,
777
+ startingUrl: startOptions.startingUrl,
778
+ port: startOptions.port,
779
+ source: 'string' == typeof startOptions.source ? startOptions.source : startOptions.source,
780
+ watchSource: startOptions.watchSource,
781
+ logLevel: logsOption || startOptions.logLevel || 'off',
782
+ logContexts,
783
+ logFormat: startOptions.logFormat || 'pretty',
784
+ logTimestamps: false !== startOptions.logTimestamps,
785
+ logColor: false !== startOptions.logColor,
786
+ logUrl: startOptions.logUrl,
787
+ logTab: startOptions.logTab
788
+ });
789
+ telemetry.track('cli_vendor_finish', {
790
+ command: 'start',
791
+ vendor,
792
+ duration_ms: Date.now() - vendorStart
793
+ });
794
+ }
795
+ telemetry.track('cli_command_finish', {
796
+ command: 'start',
797
+ duration_ms: Date.now() - cmdStart,
798
+ success: 0 === process.exitCode || null == process.exitCode,
799
+ exit_code: process.exitCode ?? 0
304
800
  });
305
801
  });
306
- extensionJs.command('preview').arguments('[project-name]').usage('preview [path-to-remote-extension] [options]').description('Preview the extension in production mode').option('--profile <path-to-file | boolean>', 'what path to use for the browser profile. A boolean value of false sets the profile to the default user profile. Defaults to a fresh profile').option('--browser <chrome | edge | firefox>', 'specify a browser to preview your extension in production mode. Defaults to `chrome`').option('--chromium-binary <path-to-binary>', 'specify a path to the Chromium binary. This option overrides the --browser setting. Defaults to the system default').option('--gecko-binary <path-to-binary>', 'specify a path to the Gecko binary. This option overrides the --browser setting. Defaults to the system default').option('--starting-url <url>', 'specify the starting URL for the browser. Defaults to `undefined`').option('--port <port>', 'specify the port to use for the development server. Defaults to `8080`').action(async function(pathOrRemoteUrl, { browser = 'chrome', ...previewOptions }) {
802
+ }
803
+ function registerPreviewCommand(program, telemetry) {
804
+ program.command('preview').arguments('[project-name]').usage('preview [path-to-remote-extension] [options]').description('Preview the extension in production mode').option('--profile <path-to-file | boolean>', 'what path to use for the browser profile. A boolean value of false sets the profile to the default user profile. Defaults to a fresh profile').option('--browser <chrome | chromium | edge | firefox | chromium-based | gecko-based | firefox-based>', 'specify a browser/engine to run. Defaults to `chromium`').option('--chromium-binary <path-to-binary>', 'specify a path to the Chromium binary. This option overrides the --browser setting. Defaults to the system default').option('--gecko-binary, --firefox-binary <path-to-binary>', 'specify a path to the Gecko binary. This option overrides the --browser setting. Defaults to the system default').option('--starting-url <url>', 'specify the starting URL for the browser. Defaults to `undefined`').option('--port <port>', 'specify the port to use for the development server. Defaults to `8080`').option('--log-context <list>', '[experimental] comma-separated contexts to include (background,content,page,sidebar,popup,options,devtools). Use `all` to include all contexts (default)').option('--logs <off|error|warn|info|debug|trace|all>', '[experimental] minimum centralized logger level to display in terminal (default: off)').option('--log-format <pretty|json>', '[experimental] output format for logger events. Defaults to `pretty`').option('--no-log-timestamps', 'disable ISO timestamps in pretty output').option('--no-log-color', 'disable color in pretty output').option('--log-url <pattern>', '[experimental] only show logs where event.url matches this substring or regex (/re/i)').option('--log-tab <id>', 'only show logs for a specific tabId (number)').option('--source [url]', "[experimental] opens the provided URL in Chrome and prints the full, live HTML of the page after content scripts are injected").action(async function(pathOrRemoteUrl, { browser = 'chromium', ...previewOptions }) {
805
+ const cmdStart = Date.now();
806
+ telemetry.track('cli_command_start', {
807
+ command: 'preview',
808
+ vendors: vendors(browser)
809
+ });
307
810
  const list = vendors(browser);
308
- validateVendorsOrExit(list);
309
- const { extensionPreview } = await Promise.resolve().then(__webpack_require__.bind(__webpack_require__, "extension-develop"));
310
- for (const vendor of list)await extensionPreview(pathOrRemoteUrl, {
311
- mode: 'production',
312
- profile: previewOptions.profile,
313
- browser: vendor,
314
- chromiumBinary: previewOptions.chromiumBinary,
315
- geckoBinary: previewOptions.geckoBinary,
316
- startingUrl: previewOptions.startingUrl
811
+ validateVendorsOrExit(list, (invalid, supported)=>{
812
+ console.error(unsupportedBrowserFlag(invalid, supported));
813
+ });
814
+ if (!process.env.EXTJS_LIGHT) {
815
+ const isRemote = 'string' == typeof pathOrRemoteUrl && /^https?:/i.test(pathOrRemoteUrl);
816
+ if (isRemote) process.env.EXTJS_LIGHT = '1';
817
+ }
818
+ const major = String(package_namespaceObject.version).split('.')[0] || '2';
819
+ const { extensionPreview } = await requireOrDlx('extension-develop', major);
820
+ for (const vendor of list){
821
+ const vendorStart = Date.now();
822
+ telemetry.track('cli_vendor_start', {
823
+ command: 'preview',
824
+ vendor
825
+ });
826
+ const logsOption = previewOptions.logs;
827
+ const logContextOption = previewOptions.logContext;
828
+ const logContexts = (()=>{
829
+ const raw = logContextOption || previewOptions.logContexts;
830
+ if (!raw || 0 === String(raw).trim().length) return;
831
+ if ('all' === String(raw).trim().toLowerCase()) return;
832
+ const allowed = [
833
+ 'background',
834
+ 'content',
835
+ 'page',
836
+ 'sidebar',
837
+ 'popup',
838
+ 'options',
839
+ 'devtools'
840
+ ];
841
+ const values = String(raw).split(',').map((s)=>s.trim()).filter((s)=>s.length > 0).filter((c)=>allowed.includes(c));
842
+ return values.length ? values : void 0;
843
+ })();
844
+ await extensionPreview(pathOrRemoteUrl, {
845
+ mode: 'production',
846
+ profile: previewOptions.profile,
847
+ browser: vendor,
848
+ chromiumBinary: previewOptions.chromiumBinary,
849
+ geckoBinary: previewOptions.geckoBinary,
850
+ startingUrl: previewOptions.startingUrl,
851
+ port: previewOptions.port,
852
+ source: 'string' == typeof previewOptions.source ? previewOptions.source : previewOptions.source,
853
+ watchSource: previewOptions.watchSource,
854
+ logLevel: logsOption || previewOptions.logLevel || 'off',
855
+ logContexts,
856
+ logFormat: previewOptions.logFormat || 'pretty',
857
+ logTimestamps: false !== previewOptions.logTimestamps,
858
+ logColor: false !== previewOptions.logColor,
859
+ logUrl: previewOptions.logUrl,
860
+ logTab: previewOptions.logTab
861
+ });
862
+ telemetry.track('cli_vendor_finish', {
863
+ command: 'preview',
864
+ vendor,
865
+ duration_ms: Date.now() - vendorStart
866
+ });
867
+ }
868
+ telemetry.track('cli_command_finish', {
869
+ command: 'preview',
870
+ duration_ms: Date.now() - cmdStart,
871
+ success: 0 === process.exitCode || null == process.exitCode,
872
+ exit_code: process.exitCode ?? 0
317
873
  });
318
874
  });
319
- extensionJs.command('build').arguments('[project-name]').usage('build [path-to-remote-extension] [options]').description('Builds the extension for production').option('--browser <chrome | edge | firefox>', 'specify a browser to preview your extension in production mode. Defaults to `chrome`').option('--polyfill [boolean]', 'whether or not to apply the cross-browser polyfill. Defaults to `false`').option('--zip [boolean]', 'whether or not to compress the extension into a ZIP file. Defaults to `false`').option('--zip-source [boolean]', 'whether or not to include the source files in the ZIP file. Defaults to `false`').option('--zip-filename <string>', 'specify the name of the ZIP file. Defaults to the extension name and version').option('--silent [boolean]', 'whether or not to open the browser automatically. Defaults to `false`').action(async function(pathOrRemoteUrl, { browser = 'chrome', ...buildOptions }) {
875
+ }
876
+ function registerBuildCommand(program, telemetry) {
877
+ program.command('build').arguments('[project-name]').usage('build [path-to-remote-extension] [options]').description('Builds the extension for production').option('--browser <chrome | chromium | edge | firefox | chromium-based | gecko-based | firefox-based>', 'specify a browser/engine to run. Defaults to `chromium`').option('--polyfill [boolean]', 'whether or not to apply the cross-browser polyfill. Defaults to `false`').option('--zip [boolean]', 'whether or not to compress the extension into a ZIP file. Defaults to `false`').option('--zip-source [boolean]', 'whether or not to include the source files in the ZIP file. Defaults to `false`').option('--zip-filename <string>', 'specify the name of the ZIP file. Defaults to the extension name and version').option('--silent [boolean]', 'whether or not to open the browser automatically. Defaults to `false`').action(async function(pathOrRemoteUrl, { browser = 'chromium', ...buildOptions }) {
878
+ const cmdStart = Date.now();
879
+ telemetry.track('cli_command_start', {
880
+ command: 'build',
881
+ vendors: vendors(browser),
882
+ polyfill_used: buildOptions.polyfill || false,
883
+ zip: buildOptions.zip || false,
884
+ zip_source: buildOptions.zipSource || false
885
+ });
320
886
  const list = vendors(browser);
321
- validateVendorsOrExit(list);
322
- const { extensionBuild } = await Promise.resolve().then(__webpack_require__.bind(__webpack_require__, "extension-develop"));
323
- for (const vendor of list)await extensionBuild(pathOrRemoteUrl, {
324
- browser: vendor,
325
- polyfill: buildOptions.polyfill,
326
- zip: buildOptions.zip,
327
- zipSource: buildOptions.zipSource,
328
- zipFilename: buildOptions.zipFilename,
329
- silent: buildOptions.silent
887
+ validateVendorsOrExit(list, (invalid, supported)=>{
888
+ console.error(unsupportedBrowserFlag(invalid, supported));
889
+ });
890
+ const major = String(package_namespaceObject.version).split('.')[0] || '2';
891
+ const { extensionBuild } = await requireOrDlx('extension-develop', major);
892
+ for (const vendor of list){
893
+ const vendorStart = Date.now();
894
+ telemetry.track('cli_vendor_start', {
895
+ command: 'build',
896
+ vendor
897
+ });
898
+ const buildSummary = await extensionBuild(pathOrRemoteUrl, {
899
+ browser: vendor,
900
+ polyfill: buildOptions.polyfill,
901
+ zip: buildOptions.zip,
902
+ zipSource: buildOptions.zipSource,
903
+ zipFilename: buildOptions.zipFilename,
904
+ silent: buildOptions.silent
905
+ });
906
+ telemetry.track('cli_build_summary', {
907
+ ...buildSummary
908
+ });
909
+ telemetry.track('cli_vendor_finish', {
910
+ command: 'build',
911
+ vendor,
912
+ duration_ms: Date.now() - vendorStart
913
+ });
914
+ }
915
+ telemetry.track('cli_command_finish', {
916
+ command: 'build',
917
+ duration_ms: Date.now() - cmdStart,
918
+ success: 0 === process.exitCode || null == process.exitCode,
919
+ exit_code: process.exitCode ?? 0
330
920
  });
331
921
  });
332
- extensionJs.command('cleanup').description('Clean up orphaned instances and free unused ports').action(async function() {
333
- const { cleanupCommand } = await Promise.resolve().then(__webpack_require__.bind(__webpack_require__, "extension-develop"));
334
- await cleanupCommand();
335
- });
336
- extensionJs.on('option:ai-help', function() {
337
- console.log(programAIHelp());
338
- process.exit(0);
339
- });
340
- extensionJs.parse();
341
- })();
922
+ }
923
+ check_updates_checkUpdates(package_namespaceObject);
924
+ const extensionJs = external_commander_namespaceObject.program;
925
+ extensionJs.name(package_namespaceObject.name).description(package_namespaceObject.description).version(package_namespaceObject.version).option('--no-telemetry', 'disable anonymous telemetry for this run').option('--ai-help', 'show AI-assistant oriented help and tips').addHelpText('after', programUserHelp());
926
+ registerCreateCommand(extensionJs, telemetry_cli_telemetry);
927
+ registerDevCommand(extensionJs, telemetry_cli_telemetry);
928
+ registerStartCommand(extensionJs, telemetry_cli_telemetry);
929
+ registerPreviewCommand(extensionJs, telemetry_cli_telemetry);
930
+ registerBuildCommand(extensionJs, telemetry_cli_telemetry);
931
+ extensionJs.on('option:ai-help', function() {
932
+ console.log(programAIHelp());
933
+ process.exit(0);
934
+ });
935
+ extensionJs.parse();
342
936
  for(var __webpack_i__ in __webpack_exports__)exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
343
937
  Object.defineProperty(exports, '__esModule', {
344
938
  value: true