extension 4.0.24 → 4.0.25

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,158 @@ 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: 'one language or framework, sidebar UI',
6225
+ templates: [
6226
+ "javascript",
6227
+ "typescript",
6228
+ 'react',
6229
+ 'preact',
6230
+ 'vue',
6231
+ 'svelte'
6232
+ ]
6233
+ },
6234
+ {
6235
+ title: 'Sidebar',
6236
+ summary: 'side panel on Chromium, sidebar action on Firefox',
6237
+ templates: [
6238
+ 'sidebar',
6239
+ 'sidebar-antd',
6240
+ 'sidebar-shadcn',
6241
+ 'sidebar-monorepo-turbopack',
6242
+ 'ai-chatgpt',
6243
+ 'ai-claude',
6244
+ 'ai-gemini',
6245
+ 'ai-perplexity',
6246
+ 'playwright',
6247
+ 'transformers-js'
6248
+ ]
6249
+ },
6250
+ {
6251
+ title: "Content scripts",
6252
+ summary: 'code injected into the pages you browse',
6253
+ templates: [
6254
+ 'content',
6255
+ 'content-css-modules',
6256
+ 'content-custom-font',
6257
+ 'content-env',
6258
+ 'content-less',
6259
+ 'content-less-modules',
6260
+ 'content-main-world',
6261
+ 'content-multi-one-entry',
6262
+ 'content-multi-three-entries',
6263
+ 'content-preact',
6264
+ 'content-react',
6265
+ 'content-sass',
6266
+ 'content-sass-modules',
6267
+ 'content-svelte',
6268
+ "content-typescript",
6269
+ 'content-vue'
6270
+ ]
6271
+ },
6272
+ {
6273
+ title: 'New tab',
6274
+ summary: 'replaces the browser new tab page',
6275
+ templates: [
6276
+ 'new',
6277
+ 'new-browser-flags',
6278
+ 'new-config-eslint',
6279
+ 'new-config-prettier',
6280
+ 'new-config-stylelint',
6281
+ 'new-crypto',
6282
+ 'new-env',
6283
+ 'new-less',
6284
+ 'new-preact',
6285
+ 'new-react',
6286
+ 'new-react-router',
6287
+ 'new-sass',
6288
+ 'new-svelte',
6289
+ "new-typescript",
6290
+ 'new-vue'
6291
+ ]
6292
+ },
6293
+ {
6294
+ title: 'Toolbar action',
6295
+ summary: 'popup opened from the toolbar button',
6296
+ templates: [
6297
+ 'action',
6298
+ 'action-locales'
6299
+ ]
6300
+ },
6301
+ {
6302
+ title: 'Special folders',
6303
+ summary: "pages/ and scripts/ entrypoints",
6304
+ templates: [
6305
+ 'special-folders-pages',
6306
+ "special-folders-scripts"
6307
+ ]
6308
+ }
6309
+ ];
6310
+ const TEMPLATE_ALIASES = [
6311
+ {
6312
+ name: 'init',
6313
+ resolvesTo: DEFAULT_TEMPLATE,
6314
+ note: `scaffolds ${DEFAULT_TEMPLATE}, not the catalog folder of the same name`
6315
+ }
6316
+ ];
6317
+ function listTemplates() {
6318
+ return TEMPLATE_GROUPS.flatMap((group)=>group.templates);
6319
+ }
6320
+ function wrapSlugs(slugs, indent, width) {
6321
+ const lines = [];
6322
+ let current = '';
6323
+ for (const slug of slugs){
6324
+ const candidate = current ? `${current}, ${slug}` : slug;
6325
+ if (current && indent.length + candidate.length + 1 > width) {
6326
+ lines.push(`${indent}${current},`);
6327
+ current = slug;
6328
+ continue;
6329
+ }
6330
+ current = candidate;
6331
+ }
6332
+ if (current) lines.push(indent + current);
6333
+ return lines;
6334
+ }
6335
+ function renderTemplateList({ color = true, width = 78 } = {}) {
6336
+ const title = (text)=>color ? external_pintor_default().green(text) : text;
6337
+ const dim = (text)=>color ? external_pintor_default().gray(text) : text;
6338
+ const slug = (text)=>color ? external_pintor_default().blue(text) : text;
6339
+ const lines = [];
6340
+ for (const group of TEMPLATE_GROUPS){
6341
+ lines.push(` ${title(group.title)} ${dim(`(${group.summary})`)}`);
6342
+ for (const line of wrapSlugs(group.templates, ' ', width))lines.push(slug(line));
6343
+ lines.push('');
6344
+ }
6345
+ lines.push(` ${title('Aliases')}`);
6346
+ for (const alias of TEMPLATE_ALIASES){
6347
+ lines.push(` ${slug(alias.name)} ${dim(alias.note)}`);
6348
+ lines.push(dim(' pass its URL to scaffold the catalog folder instead'));
6349
+ }
6350
+ return lines.join('\n');
6351
+ }
6352
+ function renderCreateTemplateHelp() {
6353
+ const total = listTemplates().length;
6354
+ const dim = (text)=>external_pintor_default().gray(text);
6355
+ return [
6356
+ '',
6357
+ external_pintor_default().underline(external_pintor_default().blue(`Templates (${total})`)),
6358
+ renderTemplateList(),
6359
+ '',
6360
+ ` ${external_pintor_default().green('Default')}`,
6361
+ ` ${external_pintor_default().blue(DEFAULT_TEMPLATE)} ${dim('is used when --template is omitted. It ships inside the CLI,')}`,
6362
+ dim(' so it scaffolds with no network call.'),
6363
+ '',
6364
+ ` ${external_pintor_default().green('Everything else')}`,
6365
+ dim(' downloads the catalog archive at create time, so it needs the'),
6366
+ dim(' network and takes longer. A GitHub URL or a ZIP URL also works.'),
6367
+ ` ${external_pintor_default().blue(TEMPLATE_CATALOG_URL)}`,
6368
+ ''
6369
+ ].join('\n');
6370
+ }
6219
6371
  function getLoggingPrefix(type) {
6220
6372
  return (0, messaging.Pl)(type);
6221
6373
  }
@@ -6638,11 +6790,11 @@ Environment variables
6638
6790
  - Example: ${messages_code(messages_arg('EXTENSION_PUBLIC_API_KEY=your_key'))}
6639
6791
 
6640
6792
  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'))}
6793
+ ${TEMPLATE_GROUPS.map((group)=>`- ${external_pintor_default().green(group.title)} ${messages_arg(`(${group.summary})`)}: ${group.templates.map((template)=>messages_code(template)).join(', ')}`).join('\n')}
6794
+ - ${external_pintor_default().green('Alias')}: ${TEMPLATE_ALIASES.map((alias)=>`${messages_code(alias.name)} ${messages_arg(alias.note)}`).join(', ')}
6795
+ - ${messages_code(DEFAULT_TEMPLATE)} is the default when ${messages_code('--template')} is omitted. It ships inside the CLI and needs no network.
6796
+ - 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.
6797
+ - A name that is not on this list fails with ${messages_code('TemplateNotFoundError')}. Run ${messages_code('extension create --help')} for the same list.
6646
6798
 
6647
6799
  Webpack/Rspack configuration
6648
6800
  - Create ${external_pintor_default().underline(messages_code(messages_arg('extension.config.js')))} for custom webpack configuration
@@ -6774,6 +6926,31 @@ Cross-browser compatibility
6774
6926
  description: 'Connectable host the browser dials for HMR + the reload bridge when it differs from the bind host (remote/devcontainer)'
6775
6927
  }
6776
6928
  ],
6929
+ templates: {
6930
+ default: DEFAULT_TEMPLATE,
6931
+ bundled: [
6932
+ DEFAULT_TEMPLATE
6933
+ ],
6934
+ catalogUrl: TEMPLATE_CATALOG_URL,
6935
+ names: listTemplates(),
6936
+ groups: TEMPLATE_GROUPS.map((group)=>({
6937
+ title: group.title,
6938
+ summary: group.summary,
6939
+ templates: [
6940
+ ...group.templates
6941
+ ]
6942
+ })),
6943
+ aliases: TEMPLATE_ALIASES.map((alias)=>({
6944
+ ...alias
6945
+ })),
6946
+ notes: [
6947
+ `extension create <name> with no --template scaffolds ${DEFAULT_TEMPLATE}`,
6948
+ `${DEFAULT_TEMPLATE} is bundled with the CLI and scaffolds offline. Every other name downloads the examples archive at create time`,
6949
+ 'a GitHub URL or a ZIP URL is accepted in place of a catalog name',
6950
+ 'a name outside names[] fails with TemplateNotFoundError and creates nothing',
6951
+ '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'
6952
+ ]
6953
+ },
6777
6954
  capabilities: {
6778
6955
  logger: {
6779
6956
  levels: [
@@ -7444,7 +7621,7 @@ Cross-browser compatibility
7444
7621
  return messaging.Lp.E_INTERNAL;
7445
7622
  }
7446
7623
  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 })=>{
7624
+ 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
7625
  const asJson = 'json' === output;
7449
7626
  if (!process.env.EXTENSION_CREATE_DEVELOP_ROOT) try {
7450
7627
  process.env.EXTENSION_CREATE_DEVELOP_ROOT = resolveExtensionDevelopRoot();
@@ -8633,9 +8810,10 @@ Cross-browser compatibility
8633
8810
  });
8634
8811
  }
8635
8812
  const DEFAULT_API = 'https://www.extension.dev';
8813
+ const PUBLISH_DOCS_URL = 'https://docs.extension.dev/tools/publish';
8636
8814
  function buildPublishRequest(opts) {
8637
8815
  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.");
8816
+ 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
8817
  const base = String(opts.api || process.env.EXTENSION_DEV_API_URL || DEFAULT_API).replace(/\/+$/, '');
8640
8818
  const body = {};
8641
8819
  if (null != opts.ttl && '' !== opts.ttl) body.ttlHours = Number(opts.ttl);
@@ -8667,7 +8845,7 @@ Cross-browser compatibility
8667
8845
  await failWith('denied', {
8668
8846
  code: messaging.Lp.E_AUTH_REQUIRED,
8669
8847
  message
8670
- }, message, 'Set EXTENSION_DEV_TOKEN or pass --token.');
8848
+ }, message, `Get a token at ${PUBLISH_DOCS_URL}, then set EXTENSION_DEV_TOKEN or pass --token.`);
8671
8849
  return;
8672
8850
  }
8673
8851
  let res;
@@ -8852,6 +9030,7 @@ Cross-browser compatibility
8852
9030
  else obj[key] = value;
8853
9031
  return obj;
8854
9032
  }
9033
+ const VERSION_MAX_LENGTH = 64;
8855
9034
  function sanitizeTag(value) {
8856
9035
  return String(value).trim().replace(/[^a-zA-Z0-9._-]/g, '').slice(0, 64);
8857
9036
  }
@@ -8870,6 +9049,17 @@ Cross-browser compatibility
8870
9049
  const v = process.env;
8871
9050
  return Boolean(v.CI || v.GITHUB_ACTIONS || v.GITLAB_CI || v.BUILDKITE || v.CIRCLECI || v.TRAVIS);
8872
9051
  }
9052
+ const INSTALL_DIRECTORY_MARKERS = [
9053
+ 'node_modules',
9054
+ '.pnpm',
9055
+ '.yarn',
9056
+ '.npm',
9057
+ '.bun'
9058
+ ];
9059
+ function isSourceCheckout(startDir = __dirname) {
9060
+ const segments = String(startDir ?? '').split(/[\\/]+/);
9061
+ return !segments.some((segment)=>INSTALL_DIRECTORY_MARKERS.includes(segment));
9062
+ }
8873
9063
  function configDir() {
8874
9064
  const xdg = process.env.XDG_CONFIG_HOME;
8875
9065
  if (xdg) return external_node_path_default().join(xdg, 'extensionjs');
@@ -8973,21 +9163,19 @@ Cross-browser compatibility
8973
9163
  source: 'env'
8974
9164
  };
8975
9165
  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
- }
9166
+ const stored = storage ? readConsentFile(storage.consentFile) : null;
9167
+ if ('disabled' === stored) return {
9168
+ enabled: false,
9169
+ source: 'config'
9170
+ };
8987
9171
  if (isCI() && !process.stdout.isTTY) return {
8988
9172
  enabled: false,
8989
9173
  source: 'ci'
8990
9174
  };
9175
+ if ('enabled' === stored) return {
9176
+ enabled: true,
9177
+ source: 'config'
9178
+ };
8991
9179
  return {
8992
9180
  enabled: true,
8993
9181
  source: 'default'
@@ -9019,7 +9207,7 @@ Cross-browser compatibility
9019
9207
  const enforcedProps = {
9020
9208
  command: String(props.command ?? 'unknown').slice(0, 32),
9021
9209
  success: Boolean(props.success),
9022
- version: String(props.version ?? this.version).slice(0, 32)
9210
+ version: String(props.version ?? this.version).slice(0, VERSION_MAX_LENGTH)
9023
9211
  };
9024
9212
  if (props.template) enforcedProps.template = sanitizeTag(props.template);
9025
9213
  if (props.source) enforcedProps.source = sanitizeTag(props.source);
@@ -9122,7 +9310,8 @@ Cross-browser compatibility
9122
9310
  os: process.platform,
9123
9311
  arch: process.arch,
9124
9312
  node_major: Number(String(process.versions.node).split('.')[0]) || 0,
9125
- is_ci: isCI()
9313
+ is_ci: isCI(),
9314
+ is_source_build: isSourceCheckout()
9126
9315
  };
9127
9316
  if (!this.disabled) {
9128
9317
  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.25",
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.25",
110
+ "extension-develop": "4.0.25",
111
+ "extension-install": "4.0.25",
112
112
  "commander": "^15.0.0",
113
113
  "pintor": "0.3.0",
114
114
  "semver": "^7.7.3",