extension 3.0.0-next.62 → 3.0.0-next.67

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,26 +1,28 @@
1
1
  [npm-version-image]: https://img.shields.io/npm/v/extension.svg?color=0971fe
2
2
  [npm-version-url]: https://www.npmjs.com/package/extension
3
- [action-image]: https://github.com/extension-js/extension.js/actions/workflows/ci.yml/badge.svg?branch=main&color=2ecc40
3
+ [npm-downloads-image]: https://img.shields.io/npm/dm/extension.svg?color=0971fe
4
+ [npm-downloads-url]: https://www.npmjs.com/package/extension
5
+ [action-image]: https://github.com/extension-js/extension.js/actions/workflows/ci.yml/badge.svg?branch=main&color=0971fe
4
6
  [action-url]: https://github.com/extension-js/extension.js/actions
5
- [discord-image]: https://img.shields.io/discord/1253608412890271755?label=Discord&logo=discord&style=flat&color=2ecc40
7
+ [discord-image]: https://img.shields.io/discord/1253608412890271755?label=Discord&logo=discord&style=flat&color=0971fe
6
8
  [discord-url]: https://discord.gg/v9h2RgeTSN
7
- [snyk-image]: https://snyk.io/test/github/extension-js/extension/badge.svg?color=2ecc40
9
+ [snyk-image]: https://snyk.io/test/github/extension-js/extension/badge.svg?color=0971fe
8
10
  [snyk-url]: https://snyk.io/test/github/extension-js/extension
9
11
 
10
- > The cross-browser extension framework
12
+ # Extension.js [![Version][npm-version-image]][npm-version-url] [![Downloads][npm-downloads-image]][npm-downloads-url] [![workflow][action-image]][action-url] [![discord][discord-image]][discord-url]
11
13
 
12
- # Extension.js [![Version][npm-version-image]][npm-version-url] [![workflow][action-image]][action-url] [![discord][discord-image]][discord-url]
14
+ > The cross-browser extension framework
13
15
 
14
16
  <img alt="Logo" align="right" src="https://avatars.githubusercontent.com/u/172809806" width="15.5%" />
15
17
 
16
- - [Create A New Extension](#create-a-new-extension) — How to create a new extension.
17
- - [Get Started Immediately](#get-started-immediately) — Get work done in no time.
18
- - [Start From An Example](https://github.com/extension-js/extension.js/tree/main/examples) — Start with your favorite tool.
19
- - [I have An Extension](#i-have-an-extension) — Use only specific parts of Extension.js.
18
+ - [Create a new extension](#create-a-new-extension) — How to create a new extension.
19
+ - [Watch demo](#watch-demo) — See how creating a new extension works.
20
+ - [Start from an example](https://github.com/extension-js/extension.js/tree/main/examples) — Start from a working baseline.
21
+ - [I have an extension](#i-have-an-extension) — Use only specific parts of Extension.js.
20
22
 
21
- Extension.js makes it very easy to develop cross-browser extensions.<br />Developers prefer it for its fast builds, unified interface, and zero configuration setup.
23
+ Create cross-browser extensions with no build configuration.<br />Use Extension.js to develop, build, and preview across browsers with a unified workflow.
22
24
 
23
- ## Create A New Extension
25
+ ## Create a new extension
24
26
 
25
27
  Use the `create` command to generate a new extension. Also works with pnpm, yarn, and bun.
26
28
 
@@ -30,11 +32,11 @@ cd my-extension
30
32
  npm run dev
31
33
  ```
32
34
 
33
- ### Watch Demo
35
+ ### Watch demo
34
36
 
35
- https://github.com/cezaraugusto/extension/assets/4672033/7263d368-99c4-434f-a60a-72c489672586
37
+ [Watch demo](https://github.com/cezaraugusto/extension/assets/4672033/7263d368-99c4-434f-a60a-72c489672586)
36
38
 
37
- ## Web Standards and Framework Support
39
+ ## Web standards and framework support
38
40
 
39
41
  <!-- For a preview of extensions running these technologies, see the [templates](https://templates.extension.land) website. -->
40
42
 
@@ -46,15 +48,18 @@ https://github.com/cezaraugusto/extension/assets/4672033/7263d368-99c4-434f-a60a
46
48
 
47
49
  </div>
48
50
 
49
- ## Get Started Immediately
51
+ <details>
52
+ <summary>Get started from a sample</summary>
53
+
54
+ ## Get started
50
55
 
51
56
  Start developing an extension using a sample from Chrome Extension Samples
52
57
 
53
58
  See the example below where we request the sample [page-redder](https://github.com/GoogleChrome/chrome-extensions-samples/tree/main/functional-samples/sample.page-redder) from [Google Chrome Extension Samples](https://github.com/GoogleChrome/chrome-extensions-samples).
54
59
 
55
- ### Watch Demo
60
+ ### Watch demo
56
61
 
57
- https://github.com/cezaraugusto/extension/assets/4672033/ee221a94-6ec7-4e04-8553-8812288927f1
62
+ [Watch demo](https://github.com/cezaraugusto/extension/assets/4672033/ee221a94-6ec7-4e04-8553-8812288927f1)
58
63
 
59
64
  ### Try Yourself
60
65
 
@@ -64,13 +69,13 @@ npx extension@latest dev https://github.com/GoogleChrome/chrome-extensions-sampl
64
69
 
65
70
  </details>
66
71
 
67
- ## I have An Extension
72
+ ## I have an extension
68
73
 
69
74
  If you have an existing extension which is using a package manager, you can install the Extension.js package and manually create the scripts used to run your extension.
70
75
 
71
- ### See How It Works
76
+ ### See how it works
72
77
 
73
- https://github.com/cezaraugusto/extension/assets/4672033/48694a23-b7f1-4098-9c5d-eff49983739c
78
+ [See how it works](https://github.com/cezaraugusto/extension/assets/4672033/48694a23-b7f1-4098-9c5d-eff49983739c)
74
79
 
75
80
  **Step 1 - Install extension as a `devDependency`**
76
81
 
@@ -133,7 +138,7 @@ npx extension@latest dev --gecko-binary "/Applications/Firefox.app/Contents/MacO
133
138
  <div align="center">
134
139
  <p>
135
140
  <span style="font-size:21px; color:black;">Browser testing via</span><br>
136
- <a href="https://www.lambdatest.com/?utm_source=extensionjs&utm_medium=sponsor" target="_blank">
141
+ <a href="https://www.lambdatest.com/?utm_source=extensionjs&utm_medium=sponsor" target="_blank" rel="noopener noreferrer">
137
142
  <img src="https://www.lambdatest.com/blue-logo.png" width="250" height="45" alt="LambdaTest Logo" />
138
143
  </a>
139
144
  </p>
@@ -7,6 +7,15 @@ export declare const fmt: {
7
7
  block(title: string, rows: Array<[string, string]>): string;
8
8
  truncate(input: unknown, max?: number): string;
9
9
  };
10
+ export declare const commandDescriptions: {
11
+ readonly create: "Creates a new extension from a template (React, TypeScript, Vue, Svelte, etc.)";
12
+ readonly dev: "Starts the development server with hot reloading";
13
+ readonly start: "Builds and starts the extension in production mode";
14
+ readonly preview: "Previews the extension in production mode without building";
15
+ readonly build: "Builds the extension for packaging/distribution";
16
+ readonly cleanup: "Cleans up orphaned instances and frees unused ports";
17
+ };
18
+ export declare function unhandledError(err: unknown): string;
10
19
  export declare function updateFailed(err: any): string;
11
20
  export declare function checkUpdates(packageJson: Record<string, any>, update: {
12
21
  latest: string;
package/dist/cli.js CHANGED
@@ -40,13 +40,17 @@ function __webpack_require__(moduleId) {
40
40
  var __webpack_exports__ = {};
41
41
  (()=>{
42
42
  const external_commander_namespaceObject = require("commander");
43
- var package_namespaceObject = JSON.parse('{"license":"MIT","repository":{"type":"git","url":"git+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","typesVersions":{"*":{"types":["./types/index.d.ts"],"types/*":["./types/*"]}},"files":["dist","types"],"bin":{"extension":"./dist/cli.js"},"name":"extension","version":"3.0.0-next.62","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"},"scripts":{"prepublishOnly":"pnpm run compile","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":{"extension-develop":"^3.0.0-next.62","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"}}');
43
+ var package_namespaceObject = JSON.parse('{"license":"MIT","repository":{"type":"git","url":"git+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","typesVersions":{"*":{"types":["./types/index.d.ts"],"types/*":["./types/*"]}},"files":["dist","types"],"bin":{"extension":"./dist/cli.js"},"name":"extension","version":"3.0.0-next.67","description":"Create cross-browser extensions with no build configuration.","homepage":"https://extension.js.org/","bugs":{"url":"https://github.com/extension-js/extension.js/issues"},"author":{"name":"Cezar Augusto","email":"boss@cezaraugusto.net","url":"https://cezaraugusto.com"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"scripts":{"prepublishOnly":"pnpm run compile","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":{"extension-develop":"^3.0.0-next.67","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"}}');
44
44
  const external_update_check_namespaceObject = require("update-check");
45
45
  var external_update_check_default = /*#__PURE__*/ __webpack_require__.n(external_update_check_namespaceObject);
46
46
  const external_pintor_namespaceObject = require("pintor");
47
47
  var external_pintor_default = /*#__PURE__*/ __webpack_require__.n(external_pintor_namespaceObject);
48
48
  function getLoggingPrefix(type) {
49
- if ('true' === process.env.EXTENSION_AUTHOR_MODE) return external_pintor_default().brightMagenta('error' === type ? 'ERROR' : "\u25BA\u25BA\u25BA");
49
+ const isAuthor = 'true' === process.env.EXTENSION_AUTHOR_MODE;
50
+ if (isAuthor) {
51
+ const base = 'error' === type ? 'ERROR Author says' : "\u25BA\u25BA\u25BA Author says";
52
+ return external_pintor_default().brightMagenta(base);
53
+ }
50
54
  if ('error' === type) return external_pintor_default().red('ERROR');
51
55
  if ('warn' === type) return external_pintor_default().brightYellow("\u25BA\u25BA\u25BA");
52
56
  if ('info' === type) return external_pintor_default().gray("\u25BA\u25BA\u25BA");
@@ -54,11 +58,45 @@ var __webpack_exports__ = {};
54
58
  }
55
59
  const code = (text)=>external_pintor_default().blue(text);
56
60
  const arg = (text)=>external_pintor_default().gray(text);
61
+ const fmt = {
62
+ heading: (title)=>external_pintor_default().underline(external_pintor_default().blue(title)),
63
+ label: (k)=>external_pintor_default().gray(k.toUpperCase()),
64
+ val: (v)=>external_pintor_default().underline(v),
65
+ code: (v)=>external_pintor_default().blue(v),
66
+ bullet: (s)=>`- ${s}`,
67
+ block (title, rows) {
68
+ const head = fmt.heading(title);
69
+ const body = rows.map(([k, v])=>`${fmt.label(k)} ${v}`).join('\n');
70
+ return `${head}\n${body}`;
71
+ },
72
+ truncate (input, max = 800) {
73
+ const s = (()=>{
74
+ try {
75
+ return 'string' == typeof input ? input : JSON.stringify(input);
76
+ } catch {
77
+ return String(input);
78
+ }
79
+ })();
80
+ return s.length > max ? s.slice(0, max) + "\u2026" : s;
81
+ }
82
+ };
83
+ const commandDescriptions = {
84
+ create: 'Creates a new extension from a template (React, TypeScript, Vue, Svelte, etc.)',
85
+ dev: 'Starts the development server with hot reloading',
86
+ start: 'Builds and starts the extension in production mode',
87
+ preview: 'Previews the extension in production mode without building',
88
+ build: 'Builds the extension for packaging/distribution',
89
+ cleanup: 'Cleans up orphaned instances and frees unused ports'
90
+ };
91
+ function unhandledError(err) {
92
+ const message = err instanceof Error ? err.stack || err.message : 'string' == typeof err ? err : fmt.truncate(err);
93
+ return `${getLoggingPrefix('error')} ${external_pintor_default().red(String(message || 'Unknown error'))}`;
94
+ }
57
95
  function updateFailed(err) {
58
96
  return `${getLoggingPrefix('error')} Failed to check for updates.\n${external_pintor_default().red(String((null == err ? void 0 : err.message) || err))}`;
59
97
  }
60
98
  function checkUpdates(packageJson, update) {
61
- 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.`;
99
+ 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))}.\nUpdate to the latest stable to get fixes and new features.`;
62
100
  }
63
101
  function programUserHelp() {
64
102
  return `\n${getLoggingPrefix('info')} ${external_pintor_default().underline('Help center for the Extension.js program')}
@@ -73,22 +111,22 @@ Example
73
111
 
74
112
  Available Commands
75
113
  - ${code('extension create ' + arg('<project-name|project-path>'))}
76
- Creates a new extension from a template (React, TypeScript, Vue, Svelte, etc.)
114
+ ${commandDescriptions.create}
77
115
 
78
116
  - ${code('extension dev ' + arg('[project-path|remote-url]'))}
79
- Starts a development server with hot reloading
117
+ ${commandDescriptions.dev}
80
118
 
81
119
  - ${code('extension start ' + arg('[project-path|remote-url]'))}
82
- Builds and starts the extension in production mode
120
+ ${commandDescriptions.start}
83
121
 
84
122
  - ${code('extension preview ' + arg('[project-path|remote-url]'))}
85
- Previews the extension in production mode without building
123
+ ${commandDescriptions.preview}
86
124
 
87
125
  - ${code('extension build ' + arg('[project-path|remote-url]'))}
88
- Builds the extension for packaging/distribution
126
+ ${commandDescriptions.build}
89
127
 
90
128
  - ${code('extension cleanup')}
91
- Cleans up orphaned instances and frees unused ports
129
+ ${commandDescriptions.cleanup}
92
130
 
93
131
  Common Options
94
132
  - ${code('--browser')} ${arg('<chrome|edge|firefox|chromium|chromium-based|gecko-based|firefox-based>')} Target browser/engine (default: chrome)
@@ -429,7 +467,7 @@ Cross-Browser Compatibility
429
467
  this.track('cli_telemetry_consent', {
430
468
  value: 'implicit_opt_in'
431
469
  });
432
- console.log('[extension] anonymous telemetry helps us improve. Pass --no-telemetry to opt out. Read more in TELEMETRY.md.');
470
+ console.log(`${external_pintor_default().gray("\u25BA\u25BA\u25BA system")} [extension] anonymous telemetry helps us improve. Pass --no-telemetry to opt out. Read more in TELEMETRY.md.`);
433
471
  }
434
472
  }
435
473
  }
@@ -469,6 +507,28 @@ Cross-Browser Compatibility
469
507
  return argv.includes('--no-telemetry');
470
508
  }
471
509
  const telemetryDisabled = isTelemetryDisabledFromArgs(process.argv);
510
+ function findManifestJson(projectRoot) {
511
+ const stack = [
512
+ projectRoot
513
+ ];
514
+ while(stack.length > 0){
515
+ const dir = stack.pop();
516
+ if (!dir) continue;
517
+ let entries;
518
+ try {
519
+ entries = external_fs_default().readdirSync(dir, {
520
+ withFileTypes: true
521
+ });
522
+ } catch {
523
+ continue;
524
+ }
525
+ for (const entry of entries){
526
+ if (entry.isFile() && 'manifest.json' === entry.name) return external_path_default().join(dir, entry.name);
527
+ if (entry.isDirectory() && !entry.name.startsWith('.') && 'node_modules' !== entry.name && 'dist' !== entry.name) stack.push(external_path_default().join(dir, entry.name));
528
+ }
529
+ }
530
+ return null;
531
+ }
472
532
  const telemetry_cli_telemetry = new Telemetry({
473
533
  app: 'extension',
474
534
  version: package_namespaceObject.version,
@@ -488,9 +548,8 @@ Cross-Browser Compatibility
488
548
  telemetry_cli_telemetry.track('cli_boot', {
489
549
  command_guess: invoked
490
550
  });
491
- const cwd = process.cwd();
492
- const manifestPath = external_path_default().join(cwd, 'manifest.json');
493
- if (external_fs_default().existsSync(manifestPath)) {
551
+ const manifestPath = findManifestJson(process.cwd());
552
+ if (manifestPath) {
494
553
  const raw = external_fs_default().readFileSync(manifestPath, 'utf8');
495
554
  const json = JSON.parse(raw);
496
555
  const summary = summarizeManifest(json);
@@ -553,7 +612,7 @@ Cross-Browser Compatibility
553
612
  }
554
613
  }
555
614
  function registerCreateCommand(program, telemetry) {
556
- 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 }) {
615
+ program.command('create').arguments('<project-name|project-path>').usage('create <project-name|project-path> [options]').description(commandDescriptions.create).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 }) {
557
616
  const startedAt = Date.now();
558
617
  telemetry.track('cli_command_start', {
559
618
  command: 'create',
@@ -583,8 +642,30 @@ Cross-Browser Compatibility
583
642
  }
584
643
  });
585
644
  }
645
+ function normalizeSourceOption(source, startingUrl) {
646
+ if (!source) return;
647
+ const hasExplicitSourceString = 'string' == typeof source && 'true' !== String(source).trim().toLowerCase();
648
+ const hasStartingUrl = 'string' == typeof startingUrl && String(startingUrl).trim().length > 0;
649
+ if (!hasExplicitSourceString) return hasStartingUrl ? String(startingUrl) : 'https://example.com';
650
+ return String(source);
651
+ }
652
+ function parseLogContexts(raw) {
653
+ if (!raw || 0 === String(raw).trim().length) return;
654
+ if ('all' === String(raw).trim().toLowerCase()) return;
655
+ const allowed = [
656
+ 'background',
657
+ 'content',
658
+ 'page',
659
+ 'sidebar',
660
+ 'popup',
661
+ 'options',
662
+ 'devtools'
663
+ ];
664
+ const values = String(raw).split(',').map((s)=>s.trim()).filter((s)=>s.length > 0).filter((c)=>allowed.includes(c));
665
+ return values.length > 0 ? values : void 0;
666
+ }
586
667
  function registerDevCommand(program, telemetry) {
587
- 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").option('--author, --author-mode', '[internal] enable maintainer diagnostics (does not affect user runtime logs)').action(async function(pathOrRemoteUrl, { browser = 'chromium', ...devOptions }) {
668
+ program.command('dev').arguments('[project-path|remote-url]').usage('dev [project-path|remote-url] [options]').description(commandDescriptions.dev).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").option('--author, --author-mode', '[internal] enable maintainer diagnostics (does not affect user runtime logs)').action(async function(pathOrRemoteUrl, { browser = 'chromium', ...devOptions }) {
588
669
  var _devOptions_polyfill;
589
670
  if (devOptions.author || devOptions['authorMode']) {
590
671
  process.env.EXTENSION_AUTHOR_MODE = 'true';
@@ -603,10 +684,9 @@ Cross-Browser Compatibility
603
684
  validateVendorsOrExit(list, (invalid, supported)=>{
604
685
  console.error(unsupportedBrowserFlag(invalid, supported));
605
686
  });
606
- if (devOptions.source) {
607
- const hasExplicitSourceString = 'string' == typeof devOptions.source && 'true' !== String(devOptions.source).trim().toLowerCase();
608
- const hasStartingUrl = 'string' == typeof devOptions.startingUrl && String(devOptions.startingUrl).trim().length > 0;
609
- if (!hasExplicitSourceString) devOptions.source = hasStartingUrl ? String(devOptions.startingUrl) : 'https://example.com';
687
+ const normalizedSource = normalizeSourceOption(devOptions.source, devOptions.startingUrl);
688
+ if (normalizedSource) {
689
+ devOptions.source = normalizedSource;
610
690
  devOptions.watchSource = true;
611
691
  }
612
692
  const { extensionDev } = await Promise.resolve().then(__webpack_require__.bind(__webpack_require__, "extension-develop"));
@@ -631,22 +711,7 @@ Cross-Browser Compatibility
631
711
  source: devOptions.source,
632
712
  watchSource: devOptions.watchSource,
633
713
  logLevel: logsOption || devOptions.logLevel || 'off',
634
- logContexts: (()=>{
635
- const raw = logContextOption || devOptions.logContexts;
636
- if (!raw || 0 === String(raw).trim().length) return;
637
- if ('all' === String(raw).trim().toLowerCase()) return;
638
- const allowed = [
639
- 'background',
640
- 'content',
641
- 'page',
642
- 'sidebar',
643
- 'popup',
644
- 'options',
645
- 'devtools'
646
- ];
647
- const values = String(raw).split(',').map((s)=>s.trim()).filter((s)=>s.length > 0).filter((c)=>allowed.includes(c));
648
- return values.length ? values : void 0;
649
- })(),
714
+ logContexts: parseLogContexts(logContextOption),
650
715
  logFormat: devOptions.logFormat || 'pretty',
651
716
  logTimestamps: false !== devOptions.logTimestamps,
652
717
  logColor: false !== devOptions.logColor,
@@ -669,9 +734,9 @@ Cross-Browser Compatibility
669
734
  });
670
735
  }
671
736
  function registerStartCommand(program, telemetry) {
672
- 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").option('--author, --author-mode', '[internal] enable maintainer diagnostics (does not affect user runtime logs)').action(async function(pathOrRemoteUrl, { browser = 'chromium', ...startOptions }) {
737
+ program.command('start').arguments('[project-path|remote-url]').usage('start [project-path|remote-url] [options]').description(commandDescriptions.start).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").option('--author, --author-mode', '[internal] enable maintainer diagnostics (does not affect user runtime logs)').action(async function(pathOrRemoteUrl, { browser = 'chromium', ...startOptions }) {
673
738
  var _startOptions_polyfill;
674
- if (startOptions.author || startOptions['authorMode']) {
739
+ if (startOptions.author || startOptions.authorMode) {
675
740
  process.env.EXTENSION_AUTHOR_MODE = 'true';
676
741
  if (!process.env.EXTENSION_VERBOSE) process.env.EXTENSION_VERBOSE = '1';
677
742
  }
@@ -694,22 +759,7 @@ Cross-Browser Compatibility
694
759
  });
695
760
  const logsOption = startOptions.logs;
696
761
  const logContextOption = startOptions.logContext;
697
- const logContexts = (()=>{
698
- const raw = logContextOption || startOptions.logContexts;
699
- if (!raw || 0 === String(raw).trim().length) return;
700
- if ('all' === String(raw).trim().toLowerCase()) return;
701
- const allowed = [
702
- 'background',
703
- 'content',
704
- 'page',
705
- 'sidebar',
706
- 'popup',
707
- 'options',
708
- 'devtools'
709
- ];
710
- const values = String(raw).split(',').map((s)=>s.trim()).filter((s)=>s.length > 0).filter((c)=>allowed.includes(c));
711
- return values.length ? values : void 0;
712
- })();
762
+ const logContexts = parseLogContexts(logContextOption);
713
763
  await extensionStart(pathOrRemoteUrl, {
714
764
  mode: 'production',
715
765
  profile: startOptions.profile,
@@ -743,7 +793,7 @@ Cross-Browser Compatibility
743
793
  });
744
794
  }
745
795
  function registerPreviewCommand(program, telemetry) {
746
- 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").option('--author, --author-mode', '[internal] enable maintainer diagnostics (does not affect user runtime logs)').action(async function(pathOrRemoteUrl, { browser = 'chromium', ...previewOptions }) {
796
+ program.command('preview').arguments('[project-name]').usage('preview [path-to-remote-extension] [options]').description(commandDescriptions.preview).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").option('--author, --author-mode', '[internal] enable maintainer diagnostics (does not affect user runtime logs)').action(async function(pathOrRemoteUrl, { browser = 'chromium', ...previewOptions }) {
747
797
  if (previewOptions.author || previewOptions['authorMode']) {
748
798
  process.env.EXTENSION_AUTHOR_MODE = 'true';
749
799
  if (!process.env.EXTENSION_VERBOSE) process.env.EXTENSION_VERBOSE = '1';
@@ -770,22 +820,7 @@ Cross-Browser Compatibility
770
820
  });
771
821
  const logsOption = previewOptions.logs;
772
822
  const logContextOption = previewOptions.logContext;
773
- const logContexts = (()=>{
774
- const raw = logContextOption || previewOptions.logContexts;
775
- if (!raw || 0 === String(raw).trim().length) return;
776
- if ('all' === String(raw).trim().toLowerCase()) return;
777
- const allowed = [
778
- 'background',
779
- 'content',
780
- 'page',
781
- 'sidebar',
782
- 'popup',
783
- 'options',
784
- 'devtools'
785
- ];
786
- const values = String(raw).split(',').map((s)=>s.trim()).filter((s)=>s.length > 0).filter((c)=>allowed.includes(c));
787
- return values.length ? values : void 0;
788
- })();
823
+ const logContexts = parseLogContexts(logContextOption);
789
824
  await extensionPreview(pathOrRemoteUrl, {
790
825
  mode: 'production',
791
826
  profile: previewOptions.profile,
@@ -819,7 +854,7 @@ Cross-Browser Compatibility
819
854
  });
820
855
  }
821
856
  function registerBuildCommand(program, telemetry) {
822
- 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`').option('--author, --author-mode', '[internal] enable maintainer diagnostics (does not affect user runtime logs)').action(async function(pathOrRemoteUrl, { browser = 'chromium', ...buildOptions }) {
857
+ program.command('build').arguments('[project-name]').usage('build [path-to-remote-extension] [options]').description(commandDescriptions.build).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`').option('--author, --author-mode', '[internal] enable maintainer diagnostics (does not affect user runtime logs)').action(async function(pathOrRemoteUrl, { browser = 'chromium', ...buildOptions }) {
823
858
  if (buildOptions.author || buildOptions['authorMode']) {
824
859
  process.env.EXTENSION_AUTHOR_MODE = 'true';
825
860
  if (!process.env.EXTENSION_VERBOSE) process.env.EXTENSION_VERBOSE = '1';
@@ -885,7 +920,7 @@ Cross-Browser Compatibility
885
920
  process.exit(0);
886
921
  }
887
922
  extensionJs.parseAsync().catch((err)=>{
888
- console.error(err);
923
+ console.error(unhandledError(err));
889
924
  process.exit(1);
890
925
  });
891
926
  })();
@@ -0,0 +1,2 @@
1
+ export declare function normalizeSourceOption(source: boolean | string | undefined, startingUrl?: string): string | undefined;
2
+ export declare function parseLogContexts(raw: string | undefined): Array<'background' | 'content' | 'page' | 'sidebar' | 'popup' | 'options' | 'devtools'> | undefined;
package/package.json CHANGED
@@ -35,9 +35,12 @@
35
35
  "extension": "./dist/cli.js"
36
36
  },
37
37
  "name": "extension",
38
- "version": "3.0.0-next.62",
38
+ "version": "3.0.0-next.67",
39
39
  "description": "Create cross-browser extensions with no build configuration.",
40
40
  "homepage": "https://extension.js.org/",
41
+ "bugs": {
42
+ "url": "https://github.com/extension-js/extension.js/issues"
43
+ },
41
44
  "author": {
42
45
  "name": "Cezar Augusto",
43
46
  "email": "boss@cezaraugusto.net",
@@ -85,7 +88,7 @@
85
88
  "cli"
86
89
  ],
87
90
  "dependencies": {
88
- "extension-develop": "^3.0.0-next.62",
91
+ "extension-develop": "^3.0.0-next.67",
89
92
  "commander": "^12.1.0",
90
93
  "extension-create": "^2.2.0",
91
94
  "pintor": "0.3.0",
@@ -1,3 +1,11 @@
1
+ // ██████╗██╗ ██╗
2
+ // ██╔════╝██║ ██║
3
+ // ██║ ██║ ██║
4
+ // ██║ ██║ ██║
5
+ // ╚██████╗███████╗██║
6
+ // ╚═════╝╚══════╝╚═╝
7
+ // MIT License (c) 2020–present Cezar Augusto & the Extension.js authors — presence implies inheritance
8
+
1
9
  type CSSContentData = Readonly<Record<string, string>>
2
10
 
3
11
  declare module '*.css' {
@@ -1,3 +1,11 @@
1
+ // ██████╗██╗ ██╗
2
+ // ██╔════╝██║ ██║
3
+ // ██║ ██║ ██║
4
+ // ██║ ██║ ██║
5
+ // ╚██████╗███████╗██║
6
+ // ╚═════╝╚══════╝╚═╝
7
+ // MIT License (c) 2020–present Cezar Augusto & the Extension.js authors — presence implies inheritance
8
+
1
9
  type CSSModuleData = Readonly<Record<string, string>>
2
10
 
3
11
  declare module '*.module.css' {
@@ -1,3 +1,11 @@
1
+ // ██████╗██╗ ██╗
2
+ // ██╔════╝██║ ██║
3
+ // ██║ ██║ ██║
4
+ // ██║ ██║ ██║
5
+ // ╚██████╗███████╗██║
6
+ // ╚═════╝╚══════╝╚═╝
7
+ // MIT License (c) 2020–present Cezar Augusto & the Extension.js authors — presence implies inheritance
8
+
1
9
  declare module 'extension-develop' {
2
10
  // These types mirror the public surface of programs/develop/module.ts,
3
11
  // but are intentionally loose on the CLI side. The real, precise types
package/types/images.d.ts CHANGED
@@ -1,3 +1,11 @@
1
+ // ██████╗██╗ ██╗
2
+ // ██╔════╝██║ ██║
3
+ // ██║ ██║ ██║
4
+ // ██║ ██║ ██║
5
+ // ╚██████╗███████╗██║
6
+ // ╚═════╝╚══════╝╚═╝
7
+ // MIT License (c) 2020–present Cezar Augusto & the Extension.js authors — presence implies inheritance
8
+
1
9
  declare module '*.png' {
2
10
  const content: string
3
11
 
package/types/index.d.ts CHANGED
@@ -1,3 +1,11 @@
1
+ // ██████╗██╗ ██╗
2
+ // ██╔════╝██║ ██║
3
+ // ██║ ██║ ██║
4
+ // ██║ ██║ ██║
5
+ // ╚██████╗███████╗██║
6
+ // ╚═════╝╚══════╝╚═╝
7
+ // MIT License (c) 2020–present Cezar Augusto & the Extension.js authors — presence implies inheritance
8
+
1
9
  /// <reference types="node" />
2
10
  /// <reference types="chrome" />
3
11
  /// <reference types="./js-frameworks.d.ts" />
@@ -20,7 +28,7 @@ interface ExtensionEnv {
20
28
  EXTENSION_PUBLIC_BROWSER: ExtensionBrowser
21
29
  EXTENSION_PUBLIC_MODE: ExtensionMode
22
30
  EXTENSION_PUBLIC_DESCRIPTION_TEXT: string
23
- EXTENSION_PUBLIC_OPENAI_API_KEY: string
31
+ EXTENSION_PUBLIC_LLM_API_KEY: string
24
32
  EXTENSION_AUTHOR_MODE: string
25
33
  EXTENSION_PUBLIC_AUTHOR_MODE: string
26
34
  }
package/types/index.ts CHANGED
@@ -1,3 +1,11 @@
1
+ // ██████╗██╗ ██╗
2
+ // ██╔════╝██║ ██║
3
+ // ██║ ██║ ██║
4
+ // ██║ ██║ ██║
5
+ // ╚██████╗███████╗██║
6
+ // ╚═════╝╚══════╝╚═╝
7
+ // MIT License (c) 2020–present Cezar Augusto & the Extension.js authors — presence implies inheritance
8
+
1
9
  /// <reference types="node" />
2
10
  /// <reference types="react" />
3
11
  /// <reference types="react-dom" />
@@ -1,3 +1,11 @@
1
+ // ██████╗██╗ ██╗
2
+ // ██╔════╝██║ ██║
3
+ // ██║ ██║ ██║
4
+ // ██║ ██║ ██║
5
+ // ╚██████╗███████╗██║
6
+ // ╚═════╝╚══════╝╚═╝
7
+ // MIT License (c) 2020–present Cezar Augusto & the Extension.js authors — presence implies inheritance
8
+
1
9
  /// <reference types="react" />
2
10
  /// <reference types="react-dom" />
3
11
  /// <reference types="svelte" />
@@ -1,3 +1,11 @@
1
+ // ██████╗██╗ ██╗
2
+ // ██╔════╝██║ ██║
3
+ // ██║ ██║ ██║
4
+ // ██║ ██║ ██║
5
+ // ╚██████╗███████╗██║
6
+ // ╚═════╝╚══════╝╚═╝
7
+ // MIT License (c) 2020–present Cezar Augusto & the Extension.js authors — presence implies inheritance
8
+
1
9
  /// <reference types="webextension-polyfill" />
2
10
 
3
11
  declare global {