extension-create 2.1.2 → 2.2.1

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
@@ -9,45 +9,57 @@
9
9
 
10
10
  # extension-create
11
11
 
12
- > Scaffold a new [Extension.js](https://extension.js.org) project from a template.
13
-
14
- This package implements the logic Extension.js uses to scaffold a new extension project from a selected template.
15
- It performs, in order:
16
-
17
- - Create or reuse the target directory (and fail on conflicting files)
18
- - Import the selected template (local in dev, remote via Git in prod)
19
- - Write `package.json` metadata and add Extension.js scripts
20
- - Write `manifest.json` metadata
21
- - Initialize a Git repository
22
- - Write a `.gitignore`
23
- - Remove template-only test files
24
- - If the template is TypeScript-based, generate `extension-env.d.ts`
12
+ The standalone extension creation engine from [Extension.js](https://github.com/extension-js/extension.js). It provides an intuitive API for programmatically creating browser extensions with support for multiple frameworks and templates.
25
13
 
26
14
  ## Installation
27
15
 
28
- ```
16
+ Install the package using your preferred package manager:
17
+
18
+ ```bash
19
+ npm install extension-create
20
+ # or
29
21
  pnpm add extension-create
22
+ # or
23
+ yarn add extension-create
30
24
  ```
31
25
 
32
- ## Usage
26
+ ## Quick Start
33
27
 
34
- ```js
28
+ Create a new extension with a single function call:
29
+
30
+ ```javascript
35
31
  import {extensionCreate} from 'extension-create'
36
32
 
37
- async function createNewExtension () {
38
- await extensionCreate(
39
- projectName: /* string (required) */,
40
- {
41
- template: 'init', // or any template name (see /examples)
42
- install: false, // optionally run the package manager install step
43
- cliVersion: '2.x' // used to pin the CLI when not in dev mode
44
- }
45
- )
46
- }
47
-
48
- createNewExtension()
33
+ // Create a basic extension
34
+ await extensionCreate('my-extension', {
35
+ template: 'init'
36
+ })
37
+
38
+ // Create a React extension and install its dependencies
39
+ await extensionCreate('my-react-extension', {
40
+ template: 'react',
41
+ install: true
42
+ })
49
43
  ```
50
44
 
45
+ ## API Reference
46
+
47
+ ### `extensionCreate(projectName, options)`
48
+
49
+ Creates a new extension project with the specified configuration.
50
+
51
+ #### Parameters
52
+
53
+ - `projectName` (string, required) - The name of your extension project
54
+ - `options` (object) - Configuration options
55
+ - `template` (string, optional) - Template name or URL. Defaults to `'init'`
56
+ - `install` (boolean, optional) - Whether to install dependencies. Defaults to `true`
57
+ - `cliVersion` (string, optional) - CLI version for package.json
58
+
59
+ ## Templates
60
+
61
+ Templates are sourced from the public Examples repository. See the catalog at `https://github.com/extension-js/examples` and reference templates by name, e.g. `content`, `content-react`, `content-vue`, etc.
62
+
51
63
  ## License
52
64
 
53
65
  MIT (c) Cezar Augusto.
package/dist/module.js CHANGED
@@ -49,12 +49,12 @@ function destinationNotWriteable(workingDir) {
49
49
  }
50
50
  async function directoryHasConflicts(projectPath, conflictingFiles) {
51
51
  const projectName = external_path_namespaceObject.basename(projectPath);
52
- let message = `Conflict! Path to ${external_pintor_default().yellow(projectName)} includes conflicting files.\n`;
52
+ let message = `Conflict! Path to ${external_pintor_default().blue(projectName)} includes conflicting files.\n\n`;
53
53
  for (const file of conflictingFiles){
54
54
  const stats = await external_fs_namespaceObject.promises.lstat(external_path_namespaceObject.join(projectPath, file));
55
- message += stats.isDirectory() ? `${external_pintor_default().gray('-')} ${external_pintor_default().yellow(file)}\n` : `${external_pintor_default().gray('-')} ${external_pintor_default().yellow(file)}\n`;
55
+ message += stats.isDirectory() ? ` ${external_pintor_default().yellow('-')} ${external_pintor_default().yellow(file)}\n` : ` ${external_pintor_default().yellow('-')} ${external_pintor_default().yellow(file)}\n`;
56
56
  }
57
- message += `${external_pintor_default().red('You need to either rename/remove the files listed above, or choose a new directory name for your extension.')}\nPath to conflicting directory: ${external_pintor_default().underline(projectPath)}`;
57
+ message += `\n${external_pintor_default().red('You need to either rename/remove the files listed above, or choose a new directory name for your extension.')}\n\nPath to conflicting directory: ${external_pintor_default().underline(projectPath)}`;
58
58
  return message;
59
59
  }
60
60
  function noProjectName() {
@@ -87,10 +87,10 @@ async function successfullInstall(projectPath, projectName) {
87
87
  installCmd = 'pnpm install';
88
88
  }
89
89
  }
90
- return `\u{1F9E9} - ${external_pintor_default().green('Success!')} Extension ${external_pintor_default().yellow(projectName)} created.\nNow ${external_pintor_default().blue('cd')} ${external_pintor_default().underline(relativePath)}\n${external_pintor_default().blue(installCmd)} to install dependencies\n${external_pintor_default().blue(command)} to open a new browser instance\nwith your extension installed, loaded, and enabled for development.\n${external_pintor_default().green('You are ready')}. Time to hack on your extension!`;
90
+ return `\u{1F9E9} - ${external_pintor_default().green('Success!')} Extension ${external_pintor_default().blue(projectName)} created.\n\nTo get started developing your extension, do the following:\n\n 1. ${external_pintor_default().blue('cd')} ${external_pintor_default().underline(relativePath)}\n 2. ${external_pintor_default().blue(installCmd)} to install dependencies\n 3. ${external_pintor_default().blue(command)} to open a new browser instance with your extension loaded\n\n${external_pintor_default().green('You are ready')}. Time to hack on your extension!\n`;
91
91
  }
92
92
  function startingNewExtension(projectName) {
93
- return `\u{1F423} - Starting a new browser extension named ${external_pintor_default().yellow(projectName)}...`;
93
+ return `\u{1F423} - Starting a new browser extension named ${external_pintor_default().blue(projectName)}...`;
94
94
  }
95
95
  function checkingIfPathIsWriteable() {
96
96
  return `\u{1F91E} - Checking if destination path is writeable...`;
@@ -99,56 +99,56 @@ function scanningPossiblyConflictingFiles() {
99
99
  return "\uD83D\uDD0E - Scanning for potential conflicting files...";
100
100
  }
101
101
  function createDirectoryError(projectName, error) {
102
- return `${external_pintor_default().red('ERROR')} Can't create directory ${external_pintor_default().yellow(projectName)}.\n${external_pintor_default().red(String(error))}`;
102
+ return `${external_pintor_default().red('ERROR')} Can't create directory ${external_pintor_default().blue(projectName)}.\n${external_pintor_default().red(String(error))}`;
103
103
  }
104
104
  function writingTypeDefinitions(projectName) {
105
- return `\u{1F537} - Writing type definitions for ${external_pintor_default().yellow(projectName)}...`;
105
+ return `\u{1F537} - Writing type definitions for ${external_pintor_default().blue(projectName)}...`;
106
106
  }
107
107
  function writingTypeDefinitionsError(error) {
108
108
  return `${external_pintor_default().red('ERROR')} Failed to write the extension type definition.\n${external_pintor_default().red(String(error))}`;
109
109
  }
110
110
  function installingFromTemplate(projectName, templateName) {
111
- if ('init' === templateName) return `\u{1F9F0} - Installing ${external_pintor_default().yellow(projectName)}...`;
112
- return `\u{1F9F0} - Installing ${external_pintor_default().yellow(projectName)} from template ${external_pintor_default().yellow(templateName)}...`;
111
+ if ('init' === templateName) return `\u{1F9F0} - Installing ${external_pintor_default().blue(projectName)}...`;
112
+ return `\u{1F9F0} - Installing ${external_pintor_default().blue(projectName)} from template ${external_pintor_default().yellow(templateName)}...`;
113
113
  }
114
114
  function installingFromTemplateError(projectName, template, error) {
115
- return `${external_pintor_default().red('ERROR')} Can't find template ${external_pintor_default().yellow(template)} for ${external_pintor_default().yellow(projectName)}.\n${external_pintor_default().red(String(error))}`;
115
+ return `${external_pintor_default().red('ERROR')} Can't find template ${external_pintor_default().yellow(template)} for ${external_pintor_default().blue(projectName)}.\n${external_pintor_default().red(String(error))}`;
116
116
  }
117
117
  function initializingGitForRepository(projectName) {
118
- return `\u{1F332} - Initializing git repository for ${external_pintor_default().yellow(projectName)}...`;
118
+ return `\u{1F332} - Initializing git repository for ${external_pintor_default().blue(projectName)}...`;
119
119
  }
120
120
  function initializingGitForRepositoryFailed(gitCommand, gitArgs, code) {
121
- return `${external_pintor_default().red('ERROR')} Command ${external_pintor_default().gray(gitCommand)} ${external_pintor_default().gray(gitArgs.join(' '))} failed.\n${external_pintor_default().red(`exit code ${external_pintor_default().gray(String(code))}`)}`;
121
+ return `${external_pintor_default().red('ERROR')} Command ${external_pintor_default().yellow(gitCommand)} ${external_pintor_default().yellow(gitArgs.join(' '))} failed.\n${external_pintor_default().red(`exit code ${external_pintor_default().yellow(String(code))}`)}`;
122
122
  }
123
123
  function initializingGitForRepositoryProcessError(projectName, error) {
124
- return `${external_pintor_default().red('ERROR')} Child process error: Can't initialize ${external_pintor_default().gray('git')} for ${external_pintor_default().yellow(projectName)}.\n${external_pintor_default().red(String((null == error ? void 0 : error.message) || error))}`;
124
+ return `${external_pintor_default().red('ERROR')} Child process error: Can't initialize ${external_pintor_default().yellow('git')} for ${external_pintor_default().blue(projectName)}.\n${external_pintor_default().red(String((null == error ? void 0 : error.message) || error))}`;
125
125
  }
126
126
  function initializingGitForRepositoryError(projectName, error) {
127
- return `${external_pintor_default().red('ERROR')} Can't initialize ${external_pintor_default().gray('git')} for ${external_pintor_default().yellow(projectName)}.\n${external_pintor_default().red(String((null == error ? void 0 : error.message) || error))}`;
127
+ return `${external_pintor_default().red('ERROR')} Can't initialize ${external_pintor_default().yellow('git')} for ${external_pintor_default().blue(projectName)}.\n${external_pintor_default().red(String((null == error ? void 0 : error.message) || error))}`;
128
128
  }
129
129
  function installingDependencies() {
130
130
  return "\uD83D\uDEE0 - Installing dependencies... (takes a moment)";
131
131
  }
132
132
  function installingDependenciesFailed(gitCommand, gitArgs, code) {
133
- return `${external_pintor_default().red('ERROR')} Command ${external_pintor_default().gray(gitCommand)} ${external_pintor_default().gray(gitArgs.join(' '))} failed.\n${external_pintor_default().red(`exit code ${external_pintor_default().gray(String(code))}`)}`;
133
+ return `${external_pintor_default().red('ERROR')} Command ${external_pintor_default().yellow(gitCommand)} ${external_pintor_default().yellow(gitArgs.join(' '))} failed.\n${external_pintor_default().red(`exit code ${external_pintor_default().yellow(String(code))}`)}`;
134
134
  }
135
135
  function installingDependenciesProcessError(projectName, error) {
136
- return `${external_pintor_default().red('ERROR')} Child process error: Can't install dependencies for ${external_pintor_default().yellow(projectName)}.\n${external_pintor_default().red(String(error))}`;
136
+ return `${external_pintor_default().red('ERROR')} Child process error: Can't install dependencies for ${external_pintor_default().blue(projectName)}.\n${external_pintor_default().red(String(error))}`;
137
137
  }
138
138
  function cantInstallDependencies(projectName, error) {
139
- return `${external_pintor_default().red('ERROR')} Can't install dependencies for ${external_pintor_default().yellow(projectName)}.\n${external_pintor_default().red(String((null == error ? void 0 : error.message) || error))}`;
139
+ return `${external_pintor_default().red('ERROR')} Can't install dependencies for ${external_pintor_default().blue(projectName)}.\n${external_pintor_default().red(String((null == error ? void 0 : error.message) || error))}`;
140
140
  }
141
141
  function writingPackageJsonMetadata() {
142
142
  return `\u{1F4DD} - Writing ${external_pintor_default().yellow('package.json')} metadata...`;
143
143
  }
144
144
  function writingPackageJsonMetadataError(projectName, error) {
145
- return `${external_pintor_default().red('ERROR')} Can't write ${external_pintor_default().yellow('package.json')} for ${external_pintor_default().yellow(projectName)}.\n${external_pintor_default().red(String(error))}`;
145
+ return `${external_pintor_default().red('ERROR')} Can't write ${external_pintor_default().yellow('package.json')} for ${external_pintor_default().blue(projectName)}.\n${external_pintor_default().red(String(error))}`;
146
146
  }
147
147
  function writingManifestJsonMetadata() {
148
148
  return `\u{1F4DC} - Writing ${external_pintor_default().yellow('manifest.json')} metadata...`;
149
149
  }
150
150
  function writingManifestJsonMetadataError(projectName, error) {
151
- return `${external_pintor_default().red('ERROR')} Can't write ${external_pintor_default().yellow('manifest.json')} for ${external_pintor_default().yellow(projectName)}.\n${external_pintor_default().red(String(error))}`;
151
+ return `${external_pintor_default().red('ERROR')} Can't write ${external_pintor_default().yellow('manifest.json')} for ${external_pintor_default().blue(projectName)}.\n${external_pintor_default().red(String(error))}`;
152
152
  }
153
153
  function writingReadmeMetaData() {
154
154
  return `\u{1F4C4} - Writing ${external_pintor_default().yellow('README.md')} metadata...`;
@@ -157,10 +157,10 @@ function writingGitIgnore() {
157
157
  return `\u{1F648} - Writing ${external_pintor_default().yellow('.gitignore')} lines...`;
158
158
  }
159
159
  function writingReadmeMetaDataEError(projectName, error) {
160
- return `${external_pintor_default().red('ERROR')} Can't write the ${external_pintor_default().yellow('README.md')} file for ${external_pintor_default().yellow(projectName)}.\n${external_pintor_default().red(String(error))}`;
160
+ return `${external_pintor_default().red('ERROR')} Can't write the ${external_pintor_default().yellow('README.md')} file for ${external_pintor_default().blue(projectName)}.\n${external_pintor_default().red(String(error))}`;
161
161
  }
162
162
  function folderExists(projectName) {
163
- return `\u{1F91D} - Ensuring ${external_pintor_default().yellow(projectName)} folder exists...`;
163
+ return `\u{1F91D} - Ensuring ${external_pintor_default().blue(projectName)} folder exists...`;
164
164
  }
165
165
  function writingDirectoryError(error) {
166
166
  return `${external_pintor_default().red('ERROR')} Error while checking directory writability.\n${external_pintor_default().red(String(error))}`;
@@ -203,7 +203,16 @@ async function moveDirectoryContents(source, destination) {
203
203
  else if (entry.isSymbolicLink()) {
204
204
  const target = await promises_namespaceObject.readlink(sourcePath);
205
205
  await promises_namespaceObject.symlink(target, destPath);
206
- } else await promises_namespaceObject.rename(sourcePath, destPath);
206
+ } else try {
207
+ await promises_namespaceObject.rename(sourcePath, destPath);
208
+ } catch (err) {
209
+ if (err && ('EXDEV' === err.code || 'EINVAL' === err.code)) {
210
+ await promises_namespaceObject.copyFile(sourcePath, destPath);
211
+ await promises_namespaceObject.rm(sourcePath, {
212
+ force: true
213
+ });
214
+ } else throw err;
215
+ }
207
216
  }
208
217
  await promises_namespaceObject.rm(source, {
209
218
  recursive: true,
@@ -274,8 +283,7 @@ async function createDirectory(projectPath, projectName) {
274
283
  throw new Error(conflictMessage);
275
284
  }
276
285
  } catch (error) {
277
- console.error(createDirectoryError(projectName, error));
278
- throw error;
286
+ throw new Error(createDirectoryError(projectName, error));
279
287
  }
280
288
  }
281
289
  const external_os_namespaceObject = require("os");
@@ -308,8 +316,32 @@ async function importExternalTemplate(projectPath, projectName, template) {
308
316
  });
309
317
  const isHttp = /^https?:\/\//i.test(template);
310
318
  const isGithub = /^https?:\/\/github.com\//i.test(template);
319
+ async function withFilteredOutput(fn) {
320
+ const originalStdoutWrite = process.stdout.write.bind(process.stdout);
321
+ const originalStderrWrite = process.stderr.write.bind(process.stderr);
322
+ const shouldFilter = (chunk)=>{
323
+ var _chunk_toString;
324
+ const s = 'string' == typeof chunk ? chunk : (null == chunk ? void 0 : null == (_chunk_toString = chunk.toString) ? void 0 : _chunk_toString.call(chunk)) ?? '';
325
+ if (!s) return false;
326
+ return /Using git version/i.test(s) || /GitHub API rate limit reached, continuing without connectivity check/i.test(s);
327
+ };
328
+ process.stdout.write = (chunk, ...args)=>{
329
+ if (shouldFilter(chunk)) return true;
330
+ return originalStdoutWrite(chunk, ...args);
331
+ };
332
+ process.stderr.write = (chunk, ...args)=>{
333
+ if (shouldFilter(chunk)) return true;
334
+ return originalStderrWrite(chunk, ...args);
335
+ };
336
+ try {
337
+ return await fn();
338
+ } finally{
339
+ process.stdout.write = originalStdoutWrite;
340
+ process.stderr.write = originalStderrWrite;
341
+ }
342
+ }
311
343
  if (isGithub) {
312
- await external_go_git_it_default()(template, tempPath, installingFromTemplate(projectName, templateName));
344
+ await withFilteredOutput(()=>external_go_git_it_default()(template, tempPath, installingFromTemplate(projectName, templateName)));
313
345
  const candidates = await promises_namespaceObject.readdir(tempPath, {
314
346
  withFileTypes: true
315
347
  });
@@ -328,7 +360,7 @@ async function importExternalTemplate(projectPath, projectName, template) {
328
360
  zip.extractAllTo(tempPath, true);
329
361
  await moveDirectoryContents(tempPath, projectPath);
330
362
  } else {
331
- await external_go_git_it_default()(templateUrl, tempPath, installingFromTemplate(projectName, templateName));
363
+ await withFilteredOutput(()=>external_go_git_it_default()(templateUrl, tempPath, installingFromTemplate(projectName, templateName)));
332
364
  const srcPath = external_path_namespaceObject.join(tempPath, templateName);
333
365
  await moveDirectoryContents(srcPath, projectPath);
334
366
  }
@@ -0,0 +1,47 @@
1
+ <a href="https://extension.js.org" target="_blank"><img src="https://img.shields.io/badge/Powered%20by%20%7C%20Extension.js-0971fe" alt="Powered by Extension.js" align="right" /></a>
2
+
3
+ # init
4
+
5
+ > A basic browser extension example built with Extension.js. Perfect starting point for developers learning browser extension development with modern tooling.
6
+
7
+ What this example does in the scope of a browser extension. The description should
8
+ describe for an audience of developers looking to use the example. Avoid jargon and
9
+ use simple language.
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ npx extension@latest create <project-name> --template init
15
+ cd <project-name>
16
+ npm install
17
+ ```
18
+
19
+ ## Commands
20
+
21
+ ### dev
22
+
23
+ Run the extension in development mode.
24
+
25
+ ```bash
26
+ npx extension@latest dev
27
+ ```
28
+
29
+ ### build
30
+
31
+ Build the extension for production.
32
+
33
+ ```bash
34
+ npx extension@latest build
35
+ ```
36
+
37
+ ### Preview
38
+
39
+ Preview the extension in the browser.
40
+
41
+ ```bash
42
+ npx extension@latest preview
43
+ ```
44
+
45
+ ## Learn more
46
+
47
+ Learn more about this and other examples at @https://extension.js.org/
@@ -0,0 +1 @@
1
+ console.log('test')
@@ -0,0 +1,11 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/chrome-manifest.json",
3
+ "manifest_version": 3,
4
+ "version": "0.0.1",
5
+ "name": "test-template-init",
6
+ "description": "A basic browser extension example built with Extension.js. Perfect starting point for developers learning browser extension development with modern tooling.",
7
+ "icons": {
8
+ "48": "images/extension_48.png"
9
+ },
10
+ "author": "Your Name"
11
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "private": true,
3
+ "name": "test-template-init",
4
+ "description": "A basic browser extension example built with Extension.js. Perfect starting point for developers learning browser extension development with modern tooling.",
5
+ "version": "0.0.1",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "author": {
9
+ "name": "Your Name",
10
+ "email": "your@email.com",
11
+ "url": "https://yourwebsite.com"
12
+ },
13
+ "scripts": {
14
+ "dev": "extension dev",
15
+ "start": "extension start",
16
+ "build": "extension build"
17
+ },
18
+ "dependencies": {},
19
+ "devDependencies": {
20
+ "extension": "^undefined"
21
+ }
22
+ }
package/package.json CHANGED
@@ -2,8 +2,7 @@
2
2
  "license": "MIT",
3
3
  "repository": {
4
4
  "type": "git",
5
- "url": "https://github.com/cezaraugusto/extension.git",
6
- "directory": "programs/create"
5
+ "url": "https://github.com/extension-js/create.git"
7
6
  },
8
7
  "engines": {
9
8
  "node": ">=18"
@@ -21,8 +20,8 @@
21
20
  "dist"
22
21
  ],
23
22
  "name": "extension-create",
24
- "version": "2.1.2",
25
- "description": "The create step of Extension.js",
23
+ "version": "2.2.1",
24
+ "description": "The standalone extension creation engine for Extension.js",
26
25
  "author": {
27
26
  "name": "Cezar Augusto",
28
27
  "email": "boss@cezaraugusto.net",
@@ -57,7 +56,7 @@
57
56
  ],
58
57
  "dependencies": {
59
58
  "adm-zip": "^0.5.12",
60
- "axios": "^1.7.2",
59
+ "axios": "^1.12.0",
61
60
  "cross-spawn": "^7.0.6",
62
61
  "go-git-it": "^5.0.0",
63
62
  "package-manager-detector": "^0.2.7",
@@ -65,19 +64,29 @@
65
64
  "tiny-glob": "^0.2.9"
66
65
  },
67
66
  "devDependencies": {
67
+ "@changesets/cli": "^2.29.6",
68
+ "@eslint/js": "^9.16.0",
68
69
  "@rslib/core": "^0.6.9",
70
+ "@types/adm-zip": "^0.5.6",
71
+ "@types/chrome": "^0.0.287",
69
72
  "@types/cross-spawn": "^6.0.6",
70
73
  "@types/node": "^22.10.1",
71
- "@vitest/coverage-v8": "3.2.2",
74
+ "@types/webextension-polyfill": "0.12.3",
75
+ "@vitest/coverage-v8": "^3.2.4",
76
+ "eslint": "^9.16.0",
72
77
  "globals": "^15.13.0",
78
+ "prettier": "^3.4.2",
73
79
  "tsconfig": "*",
74
80
  "typescript": "5.7.2",
75
- "vitest": "3.2.2"
81
+ "vitest": "^3.2.4",
82
+ "webextension-polyfill": "^0.12.0"
76
83
  },
77
84
  "scripts": {
78
85
  "clean": "rm -rf dist",
79
86
  "watch": "rslib build --watch",
80
87
  "compile": "rslib build",
88
+ "format": "prettier --write \"**/*.{ts,tsx,md,js,json}\"",
89
+ "lint": "eslint .",
81
90
  "pretest:create": "pnpm compile",
82
91
  "test:create": "vitest run",
83
92
  "test:coverage": "vitest run --coverage"