extension 4.0.24 → 4.0.26

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.cjs CHANGED
@@ -6216,6 +6216,153 @@ var __webpack_modules__ = {
6216
6216
  var external_pintor_ = __webpack_require__("pintor");
6217
6217
  var external_pintor_default = /*#__PURE__*/ __webpack_require__.n(external_pintor_);
6218
6218
  var messaging = __webpack_require__("./helpers/messaging.ts");
6219
+ const DEFAULT_TEMPLATE = "javascript";
6220
+ const TEMPLATE_CATALOG_URL = 'https://github.com/extension-js/examples/tree/main/examples';
6221
+ const TEMPLATE_GROUPS = [
6222
+ {
6223
+ title: 'Starters',
6224
+ summary: 'a bare manifest, or one language or framework with sidebar UI',
6225
+ templates: [
6226
+ 'init',
6227
+ "javascript",
6228
+ "typescript",
6229
+ 'react',
6230
+ 'preact',
6231
+ 'vue',
6232
+ 'svelte'
6233
+ ]
6234
+ },
6235
+ {
6236
+ title: 'Sidebar',
6237
+ summary: 'side panel on Chromium, sidebar action on Firefox',
6238
+ templates: [
6239
+ 'sidebar',
6240
+ 'sidebar-antd',
6241
+ 'sidebar-shadcn',
6242
+ 'sidebar-monorepo-turbopack',
6243
+ 'ai-chatgpt',
6244
+ 'ai-claude',
6245
+ 'ai-gemini',
6246
+ 'ai-perplexity',
6247
+ 'playwright',
6248
+ 'transformers-js'
6249
+ ]
6250
+ },
6251
+ {
6252
+ title: "Content scripts",
6253
+ summary: 'code injected into the pages you browse',
6254
+ templates: [
6255
+ 'content',
6256
+ 'content-css-modules',
6257
+ 'content-custom-font',
6258
+ 'content-env',
6259
+ 'content-less',
6260
+ 'content-less-modules',
6261
+ 'content-main-world',
6262
+ 'content-multi-one-entry',
6263
+ 'content-multi-three-entries',
6264
+ 'content-preact',
6265
+ 'content-react',
6266
+ 'content-sass',
6267
+ 'content-sass-modules',
6268
+ 'content-svelte',
6269
+ "content-typescript",
6270
+ 'content-vue'
6271
+ ]
6272
+ },
6273
+ {
6274
+ title: 'New tab',
6275
+ summary: 'replaces the browser new tab page',
6276
+ templates: [
6277
+ 'new',
6278
+ 'new-browser-flags',
6279
+ 'new-config-eslint',
6280
+ 'new-config-prettier',
6281
+ 'new-config-stylelint',
6282
+ 'new-crypto',
6283
+ 'new-env',
6284
+ 'new-less',
6285
+ 'new-preact',
6286
+ 'new-react',
6287
+ 'new-react-router',
6288
+ 'new-sass',
6289
+ 'new-svelte',
6290
+ "new-typescript",
6291
+ 'new-vue'
6292
+ ]
6293
+ },
6294
+ {
6295
+ title: 'Toolbar action',
6296
+ summary: 'popup opened from the toolbar button',
6297
+ templates: [
6298
+ 'action',
6299
+ 'action-locales'
6300
+ ]
6301
+ },
6302
+ {
6303
+ title: 'Special folders',
6304
+ summary: "pages/ and scripts/ entrypoints",
6305
+ templates: [
6306
+ 'special-folders-pages',
6307
+ "special-folders-scripts"
6308
+ ]
6309
+ }
6310
+ ];
6311
+ const TEMPLATE_ALIASES = [];
6312
+ function listTemplates() {
6313
+ return TEMPLATE_GROUPS.flatMap((group)=>group.templates);
6314
+ }
6315
+ function wrapSlugs(slugs, indent, width) {
6316
+ const lines = [];
6317
+ let current = '';
6318
+ for (const slug of slugs){
6319
+ const candidate = current ? `${current}, ${slug}` : slug;
6320
+ if (current && indent.length + candidate.length + 1 > width) {
6321
+ lines.push(`${indent}${current},`);
6322
+ current = slug;
6323
+ continue;
6324
+ }
6325
+ current = candidate;
6326
+ }
6327
+ if (current) lines.push(indent + current);
6328
+ return lines;
6329
+ }
6330
+ function renderTemplateList({ color = true, width = 78 } = {}) {
6331
+ const title = (text)=>color ? external_pintor_default().green(text) : text;
6332
+ const dim = (text)=>color ? external_pintor_default().gray(text) : text;
6333
+ const slug = (text)=>color ? external_pintor_default().blue(text) : text;
6334
+ const lines = [];
6335
+ for (const group of TEMPLATE_GROUPS){
6336
+ lines.push(` ${title(group.title)} ${dim(`(${group.summary})`)}`);
6337
+ for (const line of wrapSlugs(group.templates, ' ', width))lines.push(slug(line));
6338
+ lines.push('');
6339
+ }
6340
+ lines.push(` ${title('Aliases')}`);
6341
+ for (const alias of TEMPLATE_ALIASES){
6342
+ lines.push(` ${slug(alias.name)} ${dim(alias.note)}`);
6343
+ lines.push(dim(' pass its URL to scaffold the catalog folder instead'));
6344
+ }
6345
+ return lines.join('\n');
6346
+ }
6347
+ function renderCreateTemplateHelp() {
6348
+ const total = listTemplates().length;
6349
+ const dim = (text)=>external_pintor_default().gray(text);
6350
+ return [
6351
+ '',
6352
+ external_pintor_default().underline(external_pintor_default().blue(`Templates (${total})`)),
6353
+ renderTemplateList(),
6354
+ '',
6355
+ ` ${external_pintor_default().green('Default')}`,
6356
+ ` ${external_pintor_default().blue(DEFAULT_TEMPLATE)} ${dim('is used when --template is omitted. It ships inside the CLI,')}`,
6357
+ dim(' so it scaffolds with no network call.'),
6358
+ '',
6359
+ ` ${external_pintor_default().green('Everything else')}`,
6360
+ dim(' downloads the catalog archive at create time, so it needs the'),
6361
+ dim(' network and takes longer. A GitHub URL or a ZIP URL also works.'),
6362
+ ` ${external_pintor_default().blue(TEMPLATE_CATALOG_URL)}`,
6363
+ ''
6364
+ ].join('\n');
6365
+ }
6219
6366
  function getLoggingPrefix(type) {
6220
6367
  return (0, messaging.Pl)(type);
6221
6368
  }
@@ -6638,11 +6785,11 @@ Environment variables
6638
6785
  - Example: ${messages_code(messages_arg('EXTENSION_PUBLIC_API_KEY=your_key'))}
6639
6786
 
6640
6787
  Available templates
6641
- - ${external_pintor_default().green('Frameworks')}: ${messages_code(messages_arg('react'))}, ${messages_code(messages_arg('preact'))}, ${messages_code(messages_arg('vue'))}, ${messages_code(messages_arg('svelte'))}
6642
- - ${external_pintor_default().green('Languages')}: ${messages_code(messages_arg("javascript"))}, ${messages_code(messages_arg("typescript"))}
6643
- - ${external_pintor_default().green('Contexts')}: ${messages_code(messages_arg('content'))} (content scripts), ${messages_code(messages_arg('new'))} (new tab), ${messages_code(messages_arg('action'))} (popup)
6644
- - ${external_pintor_default().green('Styling')}: ${messages_code(messages_arg('tailwind'))}, ${messages_code(messages_arg('sass'))}, ${messages_code(messages_arg('less'))}
6645
- - ${external_pintor_default().green('Configs')}: ${messages_code(messages_arg('eslint'))}, ${messages_code(messages_arg('prettier'))}, ${messages_code(messages_arg('stylelint'))}
6788
+ ${TEMPLATE_GROUPS.map((group)=>`- ${external_pintor_default().green(group.title)} ${messages_arg(`(${group.summary})`)}: ${group.templates.map((template)=>messages_code(template)).join(', ')}`).join('\n')}
6789
+ - ${external_pintor_default().green('Alias')}: ${TEMPLATE_ALIASES.map((alias)=>`${messages_code(alias.name)} ${messages_arg(alias.note)}`).join(', ')}
6790
+ - ${messages_code(DEFAULT_TEMPLATE)} is the default when ${messages_code('--template')} is omitted. It ships inside the CLI and needs no network.
6791
+ - Every other name is downloaded from ${messages_code(TEMPLATE_CATALOG_URL)} at create time. A GitHub or ZIP URL works in place of a name.
6792
+ - A name that is not on this list fails with ${messages_code('TemplateNotFoundError')}. Run ${messages_code('extension create --help')} for the same list.
6646
6793
 
6647
6794
  Webpack/Rspack configuration
6648
6795
  - Create ${external_pintor_default().underline(messages_code(messages_arg('extension.config.js')))} for custom webpack configuration
@@ -6774,6 +6921,31 @@ Cross-browser compatibility
6774
6921
  description: 'Connectable host the browser dials for HMR + the reload bridge when it differs from the bind host (remote/devcontainer)'
6775
6922
  }
6776
6923
  ],
6924
+ templates: {
6925
+ default: DEFAULT_TEMPLATE,
6926
+ bundled: [
6927
+ DEFAULT_TEMPLATE
6928
+ ],
6929
+ catalogUrl: TEMPLATE_CATALOG_URL,
6930
+ names: listTemplates(),
6931
+ groups: TEMPLATE_GROUPS.map((group)=>({
6932
+ title: group.title,
6933
+ summary: group.summary,
6934
+ templates: [
6935
+ ...group.templates
6936
+ ]
6937
+ })),
6938
+ aliases: TEMPLATE_ALIASES.map((alias)=>({
6939
+ ...alias
6940
+ })),
6941
+ notes: [
6942
+ `extension create <name> with no --template scaffolds ${DEFAULT_TEMPLATE}`,
6943
+ `${DEFAULT_TEMPLATE} is bundled with the CLI and scaffolds offline. Every other name downloads the examples archive at create time`,
6944
+ 'a GitHub URL or a ZIP URL is accepted in place of a catalog name',
6945
+ 'a name outside names[] fails with TemplateNotFoundError and creates nothing',
6946
+ 'this list ships with the CLI, so it is the list this CLI version can scaffold, not necessarily the current contents of the catalog repository'
6947
+ ]
6948
+ },
6777
6949
  capabilities: {
6778
6950
  logger: {
6779
6951
  levels: [
@@ -7444,7 +7616,7 @@ Cross-browser compatibility
7444
7616
  return messaging.Lp.E_INTERNAL;
7445
7617
  }
7446
7618
  function registerCreateCommand(program) {
7447
- program.command('create').arguments('<project-name|project-path>').usage('<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, pass --install to opt in)', parseOptionalBoolean, false).option('--source <source>', 'attribution tag for where this create was initiated (e.g. cli, templates); recorded in anonymous telemetry only').option('--output <pretty|json>', 'result format. Use json for a schema-1 envelope on stdout').action(async (pathOrRemoteUrl, { template, install, output })=>{
7619
+ program.command('create').arguments('<project-name|project-path>').usage('<project-name|project-path> [options]').description(commandDescriptions.create).option('-t, --template <template-name>', `catalog name, GitHub URL, or ZIP URL to scaffold from; every catalog name is listed below (default: ${DEFAULT_TEMPLATE})`).option('--install [boolean]', 'whether or not to install the dependencies after creating the project (disabled by default, pass --install to opt in)', parseOptionalBoolean, false).option('--source <source>', 'attribution tag for where this create was initiated (e.g. cli, templates); recorded in anonymous telemetry only').option('--output <pretty|json>', 'result format. Use json for a schema-1 envelope on stdout').addHelpText('after', renderCreateTemplateHelp()).action(async (pathOrRemoteUrl, { template, install, output })=>{
7448
7620
  const asJson = 'json' === output;
7449
7621
  if (!process.env.EXTENSION_CREATE_DEVELOP_ROOT) try {
7450
7622
  process.env.EXTENSION_CREATE_DEVELOP_ROOT = resolveExtensionDevelopRoot();
@@ -8633,9 +8805,10 @@ Cross-browser compatibility
8633
8805
  });
8634
8806
  }
8635
8807
  const DEFAULT_API = 'https://www.extension.dev';
8808
+ const PUBLISH_DOCS_URL = 'https://docs.extension.dev/tools/publish';
8636
8809
  function buildPublishRequest(opts) {
8637
8810
  const token = String(opts.token || process.env.EXTENSION_DEV_TOKEN || '').trim();
8638
- if (!token) throw new Error("No token. Set EXTENSION_DEV_TOKEN (create one in the extension.dev dashboard, or via the project access-tokens API) or pass --token.");
8811
+ if (!token) throw new Error(`No token. Publishing needs an extension.dev access token.\nGet one: ${PUBLISH_DOCS_URL}\nThen set EXTENSION_DEV_TOKEN, or pass --token.`);
8639
8812
  const base = String(opts.api || process.env.EXTENSION_DEV_API_URL || DEFAULT_API).replace(/\/+$/, '');
8640
8813
  const body = {};
8641
8814
  if (null != opts.ttl && '' !== opts.ttl) body.ttlHours = Number(opts.ttl);
@@ -8667,7 +8840,7 @@ Cross-browser compatibility
8667
8840
  await failWith('denied', {
8668
8841
  code: messaging.Lp.E_AUTH_REQUIRED,
8669
8842
  message
8670
- }, message, 'Set EXTENSION_DEV_TOKEN or pass --token.');
8843
+ }, message, `Get a token at ${PUBLISH_DOCS_URL}, then set EXTENSION_DEV_TOKEN or pass --token.`);
8671
8844
  return;
8672
8845
  }
8673
8846
  let res;
@@ -8852,6 +9025,7 @@ Cross-browser compatibility
8852
9025
  else obj[key] = value;
8853
9026
  return obj;
8854
9027
  }
9028
+ const VERSION_MAX_LENGTH = 64;
8855
9029
  function sanitizeTag(value) {
8856
9030
  return String(value).trim().replace(/[^a-zA-Z0-9._-]/g, '').slice(0, 64);
8857
9031
  }
@@ -8870,6 +9044,17 @@ Cross-browser compatibility
8870
9044
  const v = process.env;
8871
9045
  return Boolean(v.CI || v.GITHUB_ACTIONS || v.GITLAB_CI || v.BUILDKITE || v.CIRCLECI || v.TRAVIS);
8872
9046
  }
9047
+ const INSTALL_DIRECTORY_MARKERS = [
9048
+ 'node_modules',
9049
+ '.pnpm',
9050
+ '.yarn',
9051
+ '.npm',
9052
+ '.bun'
9053
+ ];
9054
+ function isSourceCheckout(startDir = __dirname) {
9055
+ const segments = String(startDir ?? '').split(/[\\/]+/);
9056
+ return !segments.some((segment)=>INSTALL_DIRECTORY_MARKERS.includes(segment));
9057
+ }
8873
9058
  function configDir() {
8874
9059
  const xdg = process.env.XDG_CONFIG_HOME;
8875
9060
  if (xdg) return external_node_path_default().join(xdg, 'extensionjs');
@@ -8973,21 +9158,19 @@ Cross-browser compatibility
8973
9158
  source: 'env'
8974
9159
  };
8975
9160
  const storage = resolveTelemetryStorage();
8976
- if (storage) {
8977
- const stored = readConsentFile(storage.consentFile);
8978
- if ('enabled' === stored) return {
8979
- enabled: true,
8980
- source: 'config'
8981
- };
8982
- if ('disabled' === stored) return {
8983
- enabled: false,
8984
- source: 'config'
8985
- };
8986
- }
9161
+ const stored = storage ? readConsentFile(storage.consentFile) : null;
9162
+ if ('disabled' === stored) return {
9163
+ enabled: false,
9164
+ source: 'config'
9165
+ };
8987
9166
  if (isCI() && !process.stdout.isTTY) return {
8988
9167
  enabled: false,
8989
9168
  source: 'ci'
8990
9169
  };
9170
+ if ('enabled' === stored) return {
9171
+ enabled: true,
9172
+ source: 'config'
9173
+ };
8991
9174
  return {
8992
9175
  enabled: true,
8993
9176
  source: 'default'
@@ -9019,7 +9202,7 @@ Cross-browser compatibility
9019
9202
  const enforcedProps = {
9020
9203
  command: String(props.command ?? 'unknown').slice(0, 32),
9021
9204
  success: Boolean(props.success),
9022
- version: String(props.version ?? this.version).slice(0, 32)
9205
+ version: String(props.version ?? this.version).slice(0, VERSION_MAX_LENGTH)
9023
9206
  };
9024
9207
  if (props.template) enforcedProps.template = sanitizeTag(props.template);
9025
9208
  if (props.source) enforcedProps.source = sanitizeTag(props.source);
@@ -9122,7 +9305,8 @@ Cross-browser compatibility
9122
9305
  os: process.platform,
9123
9306
  arch: process.arch,
9124
9307
  node_major: Number(String(process.versions.node).split('.')[0]) || 0,
9125
- is_ci: isCI()
9308
+ is_ci: isCI(),
9309
+ is_source_build: isSourceCheckout()
9126
9310
  };
9127
9311
  if (!this.disabled) {
9128
9312
  this.storage = resolveTelemetryStorage();
@@ -221,6 +221,23 @@ export type ProgramAIHelpJSON = {
221
221
  default?: string;
222
222
  description: string;
223
223
  }>;
224
+ templates: {
225
+ default: string;
226
+ bundled: string[];
227
+ catalogUrl: string;
228
+ names: string[];
229
+ groups: Array<{
230
+ title: string;
231
+ summary: string;
232
+ templates: string[];
233
+ }>;
234
+ aliases: Array<{
235
+ name: string;
236
+ resolvesTo: string;
237
+ note: string;
238
+ }>;
239
+ notes: string[];
240
+ };
224
241
  capabilities: {
225
242
  logger: {
226
243
  levels: string[];
@@ -24,6 +24,7 @@ type TelemetryStorage = {
24
24
  consentFile: string;
25
25
  };
26
26
  export declare const DEFAULT_POSTHOG_KEY: string;
27
+ export declare function isSourceCheckout(startDir?: string): boolean;
27
28
  export declare function resolveTelemetryStorage(): TelemetryStorage | null;
28
29
  export declare function resolveTelemetryConsent(argv?: string[]): {
29
30
  enabled: boolean;
@@ -0,0 +1,22 @@
1
+ export declare const DEFAULT_TEMPLATE = "javascript";
2
+ export declare const TEMPLATE_CATALOG_URL = "https://github.com/extension-js/examples/tree/main/examples";
3
+ export interface TemplateGroup {
4
+ title: string;
5
+ summary: string;
6
+ templates: string[];
7
+ }
8
+ export interface TemplateAlias {
9
+ name: string;
10
+ resolvesTo: string;
11
+ note: string;
12
+ }
13
+ export declare const TEMPLATE_GROUPS: readonly TemplateGroup[];
14
+ export declare const TEMPLATE_ALIASES: readonly TemplateAlias[];
15
+ export declare function listTemplates(): string[];
16
+ export declare function templateAliasFor(name: string): TemplateAlias | undefined;
17
+ export interface RenderTemplateListOptions {
18
+ color?: boolean;
19
+ width?: number;
20
+ }
21
+ export declare function renderTemplateList({ color, width }?: RenderTemplateListOptions): string;
22
+ export declare function renderCreateTemplateHelp(): string;
package/package.json CHANGED
@@ -38,7 +38,7 @@
38
38
  "extension": "./bin/extension.cjs"
39
39
  },
40
40
  "name": "extension",
41
- "version": "4.0.24",
41
+ "version": "4.0.26",
42
42
  "description": "The cross-browser extension framework. Build Chrome, Edge, Firefox, and Safari extensions with no build configuration.",
43
43
  "homepage": "https://extension.js.org/",
44
44
  "bugs": {
@@ -106,9 +106,9 @@
106
106
  "vivaldi-location2": "2.1.0",
107
107
  "waterfox-location": "2.1.0",
108
108
  "yandex-location": "2.1.0",
109
- "extension-create": "4.0.24",
110
- "extension-develop": "4.0.24",
111
- "extension-install": "4.0.24",
109
+ "extension-create": "4.0.26",
110
+ "extension-develop": "4.0.26",
111
+ "extension-install": "4.0.26",
112
112
  "commander": "^15.0.0",
113
113
  "pintor": "0.3.0",
114
114
  "semver": "^7.7.3",