create-vextro 0.0.3 → 0.1.0
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/LICENSE +21 -21
- package/README.md +94 -24
- package/dist/index.d.ts +2 -0
- package/dist/index.js +514 -0
- package/dist/index.js.map +1 -0
- package/package.json +71 -41
- package/src/templates/chrome/src/background/background.ts +30 -0
- package/src/templates/chrome/src/content/content.ts +13 -0
- package/src/templates/chrome/src/manifest.ts +28 -0
- package/{extension-structure → src/templates/chrome}/vite.config.ts +27 -28
- package/src/templates/firefox/src/background/background.ts +29 -0
- package/src/templates/firefox/src/content/content.ts +13 -0
- package/src/templates/firefox/src/manifest.json +35 -0
- package/src/templates/firefox/vite.config.ts +22 -0
- package/src/templates/shared/src/options/App.tsx +70 -0
- package/{extension-structure → src/templates/shared}/src/options/index.tsx +9 -9
- package/src/templates/shared/src/options/options.html +12 -0
- package/src/templates/shared/src/popup/App.tsx +61 -0
- package/{extension-structure → src/templates/shared}/src/popup/index.tsx +9 -9
- package/src/templates/shared/src/popup/popup.html +12 -0
- package/src/templates/shared/src/styles.css +1 -0
- package/src/templates/shared/src/utils/storage.ts +34 -0
- package/bin/cli.js +0 -124
- package/extension-structure/src/background/background.ts +0 -2
- package/extension-structure/src/content/content.ts +0 -2
- package/extension-structure/src/manifest.ts +0 -28
- package/extension-structure/src/options/App.tsx +0 -49
- package/extension-structure/src/options/options.html +0 -12
- package/extension-structure/src/popup/App.tsx +0 -72
- package/extension-structure/src/popup/popup.html +0 -12
- package/extension-structure/src/styles.css +0 -1
- package/extension-structure/src/utils/apiClient.ts +0 -5
- /package/{extension-structure → src/templates/shared}/public/icon.png +0 -0
- /package/{extension-structure → src/templates/shared}/public/icons/icon128.png +0 -0
- /package/{extension-structure → src/templates/shared}/public/icons/icon16.png +0 -0
- /package/{extension-structure → src/templates/shared}/public/icons/icon48.png +0 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli.ts
|
|
4
|
+
import { Command as Command2 } from "commander";
|
|
5
|
+
|
|
6
|
+
// src/commands/create.ts
|
|
7
|
+
import { Command } from "commander";
|
|
8
|
+
import path3 from "path";
|
|
9
|
+
import fs3 from "fs-extra";
|
|
10
|
+
|
|
11
|
+
// src/prompts/index.ts
|
|
12
|
+
import inquirer from "inquirer";
|
|
13
|
+
|
|
14
|
+
// src/constants.ts
|
|
15
|
+
var VALID_PROJECT_NAME = /^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
|
|
16
|
+
var DEFAULT_PROJECT_NAME = "my-extension";
|
|
17
|
+
var FILES_TO_REMOVE = [
|
|
18
|
+
"src/App.tsx",
|
|
19
|
+
"src/main.tsx",
|
|
20
|
+
"src/index.css",
|
|
21
|
+
"src/App.css",
|
|
22
|
+
"src/assets",
|
|
23
|
+
"index.html"
|
|
24
|
+
];
|
|
25
|
+
var BROWSERS = {
|
|
26
|
+
chrome: {
|
|
27
|
+
displayName: "Chrome / Edge",
|
|
28
|
+
description: "Google Chrome & Microsoft Edge (Chromium-based, uses CRXJS)"
|
|
29
|
+
},
|
|
30
|
+
firefox: {
|
|
31
|
+
displayName: "Firefox",
|
|
32
|
+
description: "Mozilla Firefox (uses vite-plugin-web-extension)"
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
var CHROME_DEV_DEPS = [
|
|
36
|
+
"@types/chrome",
|
|
37
|
+
"@types/node",
|
|
38
|
+
"@crxjs/vite-plugin",
|
|
39
|
+
"tailwindcss",
|
|
40
|
+
"@tailwindcss/vite"
|
|
41
|
+
];
|
|
42
|
+
var FIREFOX_DEV_DEPS = [
|
|
43
|
+
"@types/chrome",
|
|
44
|
+
"@types/node",
|
|
45
|
+
"vite-plugin-web-extension",
|
|
46
|
+
"tailwindcss",
|
|
47
|
+
"@tailwindcss/vite"
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
// src/utils/logger.ts
|
|
51
|
+
import chalk from "chalk";
|
|
52
|
+
import ora from "ora";
|
|
53
|
+
var verbose = false;
|
|
54
|
+
function setVerbose(value) {
|
|
55
|
+
verbose = value;
|
|
56
|
+
}
|
|
57
|
+
function success(message) {
|
|
58
|
+
console.log(chalk.green("\u2714"), message);
|
|
59
|
+
}
|
|
60
|
+
function error(message) {
|
|
61
|
+
console.log(chalk.red("\u2716"), message);
|
|
62
|
+
}
|
|
63
|
+
function debug(message) {
|
|
64
|
+
if (verbose) {
|
|
65
|
+
console.log(chalk.gray("\u2B25"), chalk.gray(message));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function spinner(text) {
|
|
69
|
+
return ora({ text, color: "cyan" });
|
|
70
|
+
}
|
|
71
|
+
function newLine() {
|
|
72
|
+
console.log();
|
|
73
|
+
}
|
|
74
|
+
function banner(text) {
|
|
75
|
+
console.log(chalk.bold.cyan(text));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// src/utils/file.ts
|
|
79
|
+
import path from "path";
|
|
80
|
+
import fs from "fs-extra";
|
|
81
|
+
import { fileURLToPath } from "url";
|
|
82
|
+
var __filename2 = fileURLToPath(import.meta.url);
|
|
83
|
+
var __dirname2 = path.dirname(__filename2);
|
|
84
|
+
function getTemplatesDir() {
|
|
85
|
+
const isBundled = __dirname2.replace(/\\/g, "/").endsWith("/dist") || __dirname2.replace(/\\/g, "/").includes("/dist/");
|
|
86
|
+
const projectRoot = isBundled ? path.resolve(__dirname2, "..") : path.resolve(__dirname2, "..", "..");
|
|
87
|
+
return path.resolve(projectRoot, "src", "templates");
|
|
88
|
+
}
|
|
89
|
+
async function directoryExists(dirPath) {
|
|
90
|
+
try {
|
|
91
|
+
const stat = await fs.stat(dirPath);
|
|
92
|
+
return stat.isDirectory();
|
|
93
|
+
} catch {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// src/utils/validator.ts
|
|
99
|
+
function validateProjectName(name) {
|
|
100
|
+
if (!name || name.trim().length === 0) {
|
|
101
|
+
return "Project name is required.";
|
|
102
|
+
}
|
|
103
|
+
if (!VALID_PROJECT_NAME.test(name)) {
|
|
104
|
+
return "Project name must be a valid npm package name (lowercase, no spaces, can use hyphens and dots).";
|
|
105
|
+
}
|
|
106
|
+
if (name.length > 214) {
|
|
107
|
+
return "Project name must be less than 214 characters.";
|
|
108
|
+
}
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
async function validateDirectory(dirPath) {
|
|
112
|
+
if (await directoryExists(dirPath)) {
|
|
113
|
+
return `Directory "${dirPath}" already exists. Use --force to overwrite.`;
|
|
114
|
+
}
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// src/prompts/index.ts
|
|
119
|
+
async function promptProjectName() {
|
|
120
|
+
const { projectName } = await inquirer.prompt([
|
|
121
|
+
{
|
|
122
|
+
name: "projectName",
|
|
123
|
+
message: "Enter your extension project name:",
|
|
124
|
+
default: DEFAULT_PROJECT_NAME,
|
|
125
|
+
validate: (input) => {
|
|
126
|
+
const result = validateProjectName(input);
|
|
127
|
+
return result === true ? true : result;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
]);
|
|
131
|
+
return projectName;
|
|
132
|
+
}
|
|
133
|
+
async function promptBrowser() {
|
|
134
|
+
const { browser } = await inquirer.prompt([
|
|
135
|
+
{
|
|
136
|
+
type: "list",
|
|
137
|
+
name: "browser",
|
|
138
|
+
message: "Select target browser:",
|
|
139
|
+
choices: Object.entries(BROWSERS).map(([value, meta]) => ({
|
|
140
|
+
name: `${meta.displayName} \u2014 ${meta.description}`,
|
|
141
|
+
value
|
|
142
|
+
})),
|
|
143
|
+
default: "chrome"
|
|
144
|
+
}
|
|
145
|
+
]);
|
|
146
|
+
return browser;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// src/generators/extension.ts
|
|
150
|
+
import path2 from "path";
|
|
151
|
+
import { execSync } from "child_process";
|
|
152
|
+
import fs2 from "fs-extra";
|
|
153
|
+
var ExtensionGenerator = class {
|
|
154
|
+
config;
|
|
155
|
+
templatesDir;
|
|
156
|
+
constructor(config) {
|
|
157
|
+
this.config = config;
|
|
158
|
+
this.templatesDir = getTemplatesDir();
|
|
159
|
+
}
|
|
160
|
+
async generate() {
|
|
161
|
+
await this.scaffoldVite();
|
|
162
|
+
await this.installBaseDeps();
|
|
163
|
+
await this.cleanViteDefaults();
|
|
164
|
+
await this.copySharedFiles();
|
|
165
|
+
await this.copyBrowserFiles();
|
|
166
|
+
await this.overrideConfigs();
|
|
167
|
+
await this.patchTsConfig();
|
|
168
|
+
await this.injectProjectName();
|
|
169
|
+
await this.installExtensionDeps();
|
|
170
|
+
}
|
|
171
|
+
async scaffoldVite() {
|
|
172
|
+
const s = spinner("Creating base Vite + React + TS project...");
|
|
173
|
+
s.start();
|
|
174
|
+
try {
|
|
175
|
+
execSync(`npm create vite@latest ${this.config.name} -- --template react-ts`, {
|
|
176
|
+
cwd: path2.dirname(this.config.targetDir),
|
|
177
|
+
stdio: "pipe",
|
|
178
|
+
maxBuffer: 10 * 1024 * 1024
|
|
179
|
+
});
|
|
180
|
+
s.stop();
|
|
181
|
+
success("Created base Vite + React + TS project");
|
|
182
|
+
} catch (err) {
|
|
183
|
+
s.stop();
|
|
184
|
+
throw new Error(
|
|
185
|
+
`Failed to scaffold Vite project: ${err instanceof Error ? err.message : String(err)}`
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
async installBaseDeps() {
|
|
190
|
+
const s = spinner("Installing base dependencies...");
|
|
191
|
+
s.start();
|
|
192
|
+
try {
|
|
193
|
+
execSync("npm install", {
|
|
194
|
+
cwd: this.config.targetDir,
|
|
195
|
+
stdio: "pipe",
|
|
196
|
+
maxBuffer: 10 * 1024 * 1024
|
|
197
|
+
});
|
|
198
|
+
s.stop();
|
|
199
|
+
success("Installed base dependencies");
|
|
200
|
+
} catch (err) {
|
|
201
|
+
s.stop();
|
|
202
|
+
throw new Error(
|
|
203
|
+
`Failed to install dependencies: ${err instanceof Error ? err.message : String(err)}`
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
async cleanViteDefaults() {
|
|
208
|
+
const s = spinner("Removing default Vite template files...");
|
|
209
|
+
s.start();
|
|
210
|
+
for (const file of FILES_TO_REMOVE) {
|
|
211
|
+
const fullPath = path2.join(this.config.targetDir, file);
|
|
212
|
+
if (await fs2.pathExists(fullPath)) {
|
|
213
|
+
await fs2.remove(fullPath);
|
|
214
|
+
debug(`Removed: ${file}`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
const publicPath = path2.join(this.config.targetDir, "public");
|
|
218
|
+
if (await fs2.pathExists(publicPath)) {
|
|
219
|
+
await fs2.emptyDir(publicPath);
|
|
220
|
+
}
|
|
221
|
+
s.stop();
|
|
222
|
+
success("Cleaned default Vite template files");
|
|
223
|
+
}
|
|
224
|
+
async copySharedFiles() {
|
|
225
|
+
const s = spinner("Copying shared extension files...");
|
|
226
|
+
s.start();
|
|
227
|
+
const sharedDir = path2.join(this.templatesDir, "shared");
|
|
228
|
+
const sharedSrcDir = path2.join(sharedDir, "src");
|
|
229
|
+
if (await fs2.pathExists(sharedSrcDir)) {
|
|
230
|
+
await fs2.copy(sharedSrcDir, path2.join(this.config.targetDir, "src"));
|
|
231
|
+
}
|
|
232
|
+
const sharedPublicDir = path2.join(sharedDir, "public");
|
|
233
|
+
if (await fs2.pathExists(sharedPublicDir)) {
|
|
234
|
+
await fs2.copy(sharedPublicDir, path2.join(this.config.targetDir, "public"));
|
|
235
|
+
}
|
|
236
|
+
s.stop();
|
|
237
|
+
success("Copied shared extension files");
|
|
238
|
+
}
|
|
239
|
+
async copyBrowserFiles() {
|
|
240
|
+
const s = spinner(`Copying ${this.config.browser} browser files...`);
|
|
241
|
+
s.start();
|
|
242
|
+
const browserDir = path2.join(this.templatesDir, this.config.browser);
|
|
243
|
+
const browserSrcDir = path2.join(browserDir, "src");
|
|
244
|
+
if (await fs2.pathExists(browserSrcDir)) {
|
|
245
|
+
await fs2.copy(browserSrcDir, path2.join(this.config.targetDir, "src"), {
|
|
246
|
+
overwrite: true
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
s.stop();
|
|
250
|
+
success(`Copied ${this.config.browser} browser files`);
|
|
251
|
+
}
|
|
252
|
+
async overrideConfigs() {
|
|
253
|
+
const s = spinner("Overriding config files...");
|
|
254
|
+
s.start();
|
|
255
|
+
const browserDir = path2.join(this.templatesDir, this.config.browser);
|
|
256
|
+
const viteConfigFrom = path2.join(browserDir, "vite.config.ts");
|
|
257
|
+
const viteConfigTo = path2.join(this.config.targetDir, "vite.config.ts");
|
|
258
|
+
if (await fs2.pathExists(viteConfigFrom)) {
|
|
259
|
+
await fs2.copy(viteConfigFrom, viteConfigTo, { overwrite: true });
|
|
260
|
+
debug("Overrode: vite.config.ts");
|
|
261
|
+
}
|
|
262
|
+
s.stop();
|
|
263
|
+
success("Overrode config files");
|
|
264
|
+
}
|
|
265
|
+
async patchTsConfig() {
|
|
266
|
+
const s = spinner("Patching tsconfig for extension types...");
|
|
267
|
+
s.start();
|
|
268
|
+
const tsconfigAppPath = path2.join(this.config.targetDir, "tsconfig.app.json");
|
|
269
|
+
if (await fs2.pathExists(tsconfigAppPath)) {
|
|
270
|
+
let content = await fs2.readFile(tsconfigAppPath, "utf-8");
|
|
271
|
+
const typeToAdd = "chrome";
|
|
272
|
+
content = content.replace(
|
|
273
|
+
/("types"\s*:\s*\[)([\s\S]*?)(\])/,
|
|
274
|
+
(match, prefix, existing, suffix) => {
|
|
275
|
+
const trimmed = existing.trim();
|
|
276
|
+
if (trimmed.includes(`"${typeToAdd}"`)) return match;
|
|
277
|
+
if (trimmed.length === 0) {
|
|
278
|
+
return `${prefix}"${typeToAdd}"${suffix}`;
|
|
279
|
+
}
|
|
280
|
+
return `${prefix}${existing.trimEnd()}, "${typeToAdd}"${suffix}`;
|
|
281
|
+
}
|
|
282
|
+
);
|
|
283
|
+
await fs2.writeFile(tsconfigAppPath, content, "utf-8");
|
|
284
|
+
debug(`Patched tsconfig.app.json with type: ${typeToAdd}`);
|
|
285
|
+
}
|
|
286
|
+
s.stop();
|
|
287
|
+
success("Patched tsconfig for extension types");
|
|
288
|
+
}
|
|
289
|
+
async injectProjectName() {
|
|
290
|
+
const s = spinner("Injecting project name...");
|
|
291
|
+
s.start();
|
|
292
|
+
if (this.config.browser === "chrome") {
|
|
293
|
+
const manifestPath = path2.join(this.config.targetDir, "src", "manifest.ts");
|
|
294
|
+
if (await fs2.pathExists(manifestPath)) {
|
|
295
|
+
let manifest = await fs2.readFile(manifestPath, "utf-8");
|
|
296
|
+
manifest = manifest.replace(/__EXT_NAME__/g, this.config.name);
|
|
297
|
+
await fs2.writeFile(manifestPath, manifest, "utf-8");
|
|
298
|
+
}
|
|
299
|
+
} else if (this.config.browser === "firefox") {
|
|
300
|
+
const manifestPath = path2.join(this.config.targetDir, "src", "manifest.json");
|
|
301
|
+
if (await fs2.pathExists(manifestPath)) {
|
|
302
|
+
let manifest = await fs2.readFile(manifestPath, "utf-8");
|
|
303
|
+
manifest = manifest.replace(/__EXT_NAME__/g, this.config.name);
|
|
304
|
+
await fs2.writeFile(manifestPath, manifest, "utf-8");
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
const pkgPath = path2.join(this.config.targetDir, "package.json");
|
|
308
|
+
if (await fs2.pathExists(pkgPath)) {
|
|
309
|
+
let pkg = await fs2.readFile(pkgPath, "utf-8");
|
|
310
|
+
pkg = pkg.replace(/"__EXT_NAME__"/g, `"${this.config.name}"`);
|
|
311
|
+
await fs2.writeFile(pkgPath, pkg, "utf-8");
|
|
312
|
+
}
|
|
313
|
+
s.stop();
|
|
314
|
+
success("Injected project name");
|
|
315
|
+
}
|
|
316
|
+
async installExtensionDeps() {
|
|
317
|
+
const deps = this.config.browser === "firefox" ? FIREFOX_DEV_DEPS : CHROME_DEV_DEPS;
|
|
318
|
+
const s = spinner(`Installing ${this.config.browser} extension dev dependencies...`);
|
|
319
|
+
s.start();
|
|
320
|
+
try {
|
|
321
|
+
execSync(`npm install --save-dev ${deps.join(" ")}`, {
|
|
322
|
+
cwd: this.config.targetDir,
|
|
323
|
+
stdio: "pipe",
|
|
324
|
+
maxBuffer: 10 * 1024 * 1024
|
|
325
|
+
});
|
|
326
|
+
s.stop();
|
|
327
|
+
success(`Installed ${this.config.browser} extension dev dependencies`);
|
|
328
|
+
} catch (err) {
|
|
329
|
+
s.stop();
|
|
330
|
+
throw new Error(
|
|
331
|
+
`Failed to install extension dependencies: ${err instanceof Error ? err.message : String(err)}`
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
// src/commands/create.ts
|
|
338
|
+
function createCommand() {
|
|
339
|
+
const cmd = new Command("create").description("Create a new browser extension project").argument("[project-name]", "Name of the extension project").option("--chrome", "Target Chrome / Edge (default)").option("--firefox", "Target Firefox").option("--force", "Overwrite existing directory").action(async (projectName, options) => {
|
|
340
|
+
try {
|
|
341
|
+
await handleCreate(projectName, options);
|
|
342
|
+
} catch (err) {
|
|
343
|
+
error(err instanceof Error ? err.message : String(err));
|
|
344
|
+
process.exit(1);
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
return cmd;
|
|
348
|
+
}
|
|
349
|
+
function resolveBrowserFromFlags(options) {
|
|
350
|
+
if (options.chrome) return "chrome";
|
|
351
|
+
if (options.firefox) return "firefox";
|
|
352
|
+
return null;
|
|
353
|
+
}
|
|
354
|
+
async function handleCreate(projectName, options) {
|
|
355
|
+
if (!projectName) {
|
|
356
|
+
projectName = await promptProjectName();
|
|
357
|
+
}
|
|
358
|
+
const nameValidation = validateProjectName(projectName);
|
|
359
|
+
if (nameValidation !== true) {
|
|
360
|
+
throw new Error(nameValidation);
|
|
361
|
+
}
|
|
362
|
+
let browser = resolveBrowserFromFlags(options);
|
|
363
|
+
if (!browser) {
|
|
364
|
+
browser = await promptBrowser();
|
|
365
|
+
}
|
|
366
|
+
const targetDir = path3.resolve(process.cwd(), projectName);
|
|
367
|
+
if (!options.force) {
|
|
368
|
+
const dirValidation = await validateDirectory(targetDir);
|
|
369
|
+
if (dirValidation !== true) {
|
|
370
|
+
throw new Error(dirValidation);
|
|
371
|
+
}
|
|
372
|
+
} else if (await fs3.pathExists(targetDir)) {
|
|
373
|
+
await fs3.remove(targetDir);
|
|
374
|
+
}
|
|
375
|
+
const config = {
|
|
376
|
+
name: projectName,
|
|
377
|
+
targetDir,
|
|
378
|
+
browser
|
|
379
|
+
};
|
|
380
|
+
const browserMeta = BROWSERS[browser];
|
|
381
|
+
newLine();
|
|
382
|
+
banner(`\u{1F680} Creating Vextro Extension for ${browserMeta.displayName} \u2014 "${config.name}"...`);
|
|
383
|
+
newLine();
|
|
384
|
+
const cleanup = async () => {
|
|
385
|
+
newLine();
|
|
386
|
+
error("Interrupted! Cleaning up...");
|
|
387
|
+
try {
|
|
388
|
+
if (await fs3.pathExists(targetDir)) {
|
|
389
|
+
await fs3.remove(targetDir);
|
|
390
|
+
}
|
|
391
|
+
} catch {
|
|
392
|
+
}
|
|
393
|
+
process.exit(1);
|
|
394
|
+
};
|
|
395
|
+
process.on("SIGINT", cleanup);
|
|
396
|
+
const generator = new ExtensionGenerator(config);
|
|
397
|
+
await generator.generate();
|
|
398
|
+
process.removeListener("SIGINT", cleanup);
|
|
399
|
+
newLine();
|
|
400
|
+
success(
|
|
401
|
+
`Vextro project "${config.name}" created successfully for ${browserMeta.displayName}! \u{1F389}`
|
|
402
|
+
);
|
|
403
|
+
newLine();
|
|
404
|
+
console.log("Next steps:");
|
|
405
|
+
console.log(` cd ${config.name}`);
|
|
406
|
+
console.log(" npm run dev");
|
|
407
|
+
newLine();
|
|
408
|
+
if (browser === "chrome") {
|
|
409
|
+
console.log("Load extension:");
|
|
410
|
+
console.log(" 1. Open chrome://extensions (or edge://extensions)");
|
|
411
|
+
console.log(' 2. Enable "Developer mode"');
|
|
412
|
+
console.log(' 3. Click "Load unpacked" \u2192 select the dist/ folder');
|
|
413
|
+
} else if (browser === "firefox") {
|
|
414
|
+
console.log("Load extension:");
|
|
415
|
+
console.log(" 1. Open about:debugging#/runtime/this-firefox");
|
|
416
|
+
console.log(' 2. Click "Load Temporary Add-on"');
|
|
417
|
+
console.log(" 3. Select any file in the dist/ folder");
|
|
418
|
+
}
|
|
419
|
+
newLine();
|
|
420
|
+
console.log("Happy coding! \u{1F680}");
|
|
421
|
+
newLine();
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// package.json
|
|
425
|
+
var package_default = {
|
|
426
|
+
name: "create-vextro",
|
|
427
|
+
version: "0.1.0",
|
|
428
|
+
description: "Scaffold modern browser extensions (Chrome, Edge, Firefox) with Vite + React + Tailwind",
|
|
429
|
+
type: "module",
|
|
430
|
+
bin: {
|
|
431
|
+
"create-vextro": "./dist/index.js"
|
|
432
|
+
},
|
|
433
|
+
engines: {
|
|
434
|
+
node: ">=20.0.0"
|
|
435
|
+
},
|
|
436
|
+
scripts: {
|
|
437
|
+
build: "tsup",
|
|
438
|
+
dev: "tsup --watch",
|
|
439
|
+
test: "vitest run",
|
|
440
|
+
"test:watch": "vitest",
|
|
441
|
+
lint: "eslint src/",
|
|
442
|
+
format: "prettier --write .",
|
|
443
|
+
"format:check": "prettier --check .",
|
|
444
|
+
prepublishOnly: "npm run build && npm test",
|
|
445
|
+
typecheck: "tsc --noEmit"
|
|
446
|
+
},
|
|
447
|
+
dependencies: {
|
|
448
|
+
chalk: "^5.3.0",
|
|
449
|
+
commander: "^12.1.0",
|
|
450
|
+
"fs-extra": "^11.2.0",
|
|
451
|
+
inquirer: "^9.2.8",
|
|
452
|
+
ora: "^8.0.0"
|
|
453
|
+
},
|
|
454
|
+
devDependencies: {
|
|
455
|
+
"@changesets/cli": "^2.27.0",
|
|
456
|
+
"@types/fs-extra": "^11.0.4",
|
|
457
|
+
"@types/inquirer": "^9.0.7",
|
|
458
|
+
"@types/node": "^20.0.0",
|
|
459
|
+
eslint: "^9.0.0",
|
|
460
|
+
prettier: "^3.3.0",
|
|
461
|
+
tsup: "^8.0.0",
|
|
462
|
+
typescript: "^5.5.0",
|
|
463
|
+
"typescript-eslint": "^8.0.0",
|
|
464
|
+
vitest: "^2.0.0"
|
|
465
|
+
},
|
|
466
|
+
files: [
|
|
467
|
+
"dist",
|
|
468
|
+
"src/templates"
|
|
469
|
+
],
|
|
470
|
+
keywords: [
|
|
471
|
+
"vite",
|
|
472
|
+
"chrome-extension",
|
|
473
|
+
"firefox-extension",
|
|
474
|
+
"browser-extension",
|
|
475
|
+
"vite-plugin",
|
|
476
|
+
"react",
|
|
477
|
+
"tailwind",
|
|
478
|
+
"crxjs",
|
|
479
|
+
"manifest-v3",
|
|
480
|
+
"scaffold",
|
|
481
|
+
"cli",
|
|
482
|
+
"generator",
|
|
483
|
+
"starter-kit"
|
|
484
|
+
],
|
|
485
|
+
repository: {
|
|
486
|
+
type: "git",
|
|
487
|
+
url: "https://github.com/CodeCanvasCollective/vextro.git"
|
|
488
|
+
},
|
|
489
|
+
author: "Lasantha <lasanthaslakmal@gmail.com>",
|
|
490
|
+
license: "MIT",
|
|
491
|
+
bugs: {
|
|
492
|
+
url: "https://github.com/CodeCanvasCollective/vextro/issues"
|
|
493
|
+
},
|
|
494
|
+
homepage: "https://github.com/CodeCanvasCollective/vextro#readme"
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
// src/cli.ts
|
|
498
|
+
var CLI_VERSION = package_default.version || "0.1.0";
|
|
499
|
+
function createCli() {
|
|
500
|
+
const program2 = new Command2();
|
|
501
|
+
program2.name("create-vextro").description("Scaffold modern browser extensions with Vite + React + Tailwind").version(CLI_VERSION).option("--verbose", "Enable verbose output").hook("preAction", (thisCommand) => {
|
|
502
|
+
const opts = thisCommand.opts();
|
|
503
|
+
if (opts.verbose) {
|
|
504
|
+
setVerbose(true);
|
|
505
|
+
}
|
|
506
|
+
});
|
|
507
|
+
program2.addCommand(createCommand(), { isDefault: true });
|
|
508
|
+
return program2;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// src/index.ts
|
|
512
|
+
var program = createCli();
|
|
513
|
+
program.parse();
|
|
514
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/commands/create.ts","../src/prompts/index.ts","../src/constants.ts","../src/utils/logger.ts","../src/utils/file.ts","../src/utils/validator.ts","../src/generators/extension.ts","../package.json","../src/index.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { createCommand } from './commands/create.js';\nimport { setVerbose } from './utils/logger.js';\nimport pkg from '../package.json' with { type: 'json' };\n\nconst CLI_VERSION = pkg.version || '0.1.0';\n\nexport function createCli(): Command {\n const program = new Command();\n\n program\n .name('create-vextro')\n .description('Scaffold modern browser extensions with Vite + React + Tailwind')\n .version(CLI_VERSION)\n .option('--verbose', 'Enable verbose output')\n .hook('preAction', (thisCommand) => {\n const opts = thisCommand.opts();\n if (opts.verbose) {\n setVerbose(true);\n }\n });\n\n program.addCommand(createCommand(), { isDefault: true });\n\n return program;\n}\n","import { Command } from 'commander';\nimport path from 'node:path';\nimport fs from 'fs-extra';\nimport { promptProjectName, promptBrowser } from '../prompts/index.js';\nimport { ExtensionGenerator } from '../generators/extension.js';\nimport {\n validateProjectName,\n validateDirectory,\n error,\n success,\n newLine,\n banner,\n} from '../utils/index.js';\nimport { BROWSERS } from '../constants.js';\nimport type { CreateOptions, ProjectConfig, BrowserTarget } from '../types/index.js';\n\nexport function createCommand(): Command {\n const cmd = new Command('create')\n .description('Create a new browser extension project')\n .argument('[project-name]', 'Name of the extension project')\n .option('--chrome', 'Target Chrome / Edge (default)')\n .option('--firefox', 'Target Firefox')\n .option('--force', 'Overwrite existing directory')\n .action(async (projectName: string | undefined, options: CreateOptions) => {\n try {\n await handleCreate(projectName, options);\n } catch (err) {\n error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n });\n\n return cmd;\n}\n\nfunction resolveBrowserFromFlags(options: CreateOptions): BrowserTarget | null {\n if (options.chrome) return 'chrome';\n if (options.firefox) return 'firefox';\n return null;\n}\n\nasync function handleCreate(\n projectName: string | undefined,\n options: CreateOptions,\n): Promise<void> {\n // If no project name given as arg, prompt for it\n if (!projectName) {\n projectName = await promptProjectName();\n }\n\n // Validate the project name\n const nameValidation = validateProjectName(projectName);\n if (nameValidation !== true) {\n throw new Error(nameValidation);\n }\n\n // Resolve browser target\n let browser = resolveBrowserFromFlags(options);\n if (!browser) {\n browser = await promptBrowser();\n }\n\n const targetDir = path.resolve(process.cwd(), projectName);\n\n // Check if directory already exists\n if (!options.force) {\n const dirValidation = await validateDirectory(targetDir);\n if (dirValidation !== true) {\n throw new Error(dirValidation);\n }\n } else if (await fs.pathExists(targetDir)) {\n await fs.remove(targetDir);\n }\n\n const config: ProjectConfig = {\n name: projectName,\n targetDir,\n browser,\n };\n\n const browserMeta = BROWSERS[browser];\n\n newLine();\n banner(`🚀 Creating Vextro Extension for ${browserMeta.displayName} — \"${config.name}\"...`);\n newLine();\n\n // Handle SIGINT — clean up partial directory\n const cleanup = async () => {\n newLine();\n error('Interrupted! Cleaning up...');\n try {\n if (await fs.pathExists(targetDir)) {\n await fs.remove(targetDir);\n }\n } catch {\n // best-effort cleanup\n }\n process.exit(1);\n };\n process.on('SIGINT', cleanup);\n\n const generator = new ExtensionGenerator(config);\n await generator.generate();\n\n // Remove SIGINT handler after success\n process.removeListener('SIGINT', cleanup);\n\n newLine();\n success(\n `Vextro project \"${config.name}\" created successfully for ${browserMeta.displayName}! 🎉`,\n );\n newLine();\n console.log('Next steps:');\n console.log(` cd ${config.name}`);\n console.log(' npm run dev');\n newLine();\n\n if (browser === 'chrome') {\n console.log('Load extension:');\n console.log(' 1. Open chrome://extensions (or edge://extensions)');\n console.log(' 2. Enable \"Developer mode\"');\n console.log(' 3. Click \"Load unpacked\" → select the dist/ folder');\n } else if (browser === 'firefox') {\n console.log('Load extension:');\n console.log(' 1. Open about:debugging#/runtime/this-firefox');\n console.log(' 2. Click \"Load Temporary Add-on\"');\n console.log(' 3. Select any file in the dist/ folder');\n }\n\n newLine();\n console.log('Happy coding! 🚀');\n newLine();\n}\n","import inquirer from 'inquirer';\nimport { DEFAULT_PROJECT_NAME, BROWSERS } from '../constants.js';\nimport { validateProjectName } from '../utils/index.js';\nimport type { BrowserTarget } from '../types/index.js';\n\nexport async function promptProjectName(): Promise<string> {\n const { projectName } = await inquirer.prompt([\n {\n name: 'projectName',\n message: 'Enter your extension project name:',\n default: DEFAULT_PROJECT_NAME,\n validate: (input: string) => {\n const result = validateProjectName(input);\n return result === true ? true : result;\n },\n },\n ]);\n return projectName;\n}\n\nexport async function promptBrowser(): Promise<BrowserTarget> {\n const { browser } = await inquirer.prompt([\n {\n type: 'list',\n name: 'browser',\n message: 'Select target browser:',\n choices: Object.entries(BROWSERS).map(([value, meta]) => ({\n name: `${meta.displayName} — ${meta.description}`,\n value,\n })),\n default: 'chrome',\n },\n ]);\n return browser;\n}\n","import type { BrowserTarget } from './types/index.js';\n\nexport const PACKAGE_NAME = 'create-vextro';\nexport const BRAND_NAME = 'Vextro';\n\nexport const VALID_PROJECT_NAME = /^(?:@[a-z0-9-~][a-z0-9-._~]*\\/)?[a-z0-9-~][a-z0-9-._~]*$/;\n\nexport const DEFAULT_PROJECT_NAME = 'my-extension';\nexport const DEFAULT_BROWSER: BrowserTarget = 'chrome';\n\nexport const FILES_TO_REMOVE = [\n 'src/App.tsx',\n 'src/main.tsx',\n 'src/index.css',\n 'src/App.css',\n 'src/assets',\n 'index.html',\n];\n\nexport const BROWSERS: Record<BrowserTarget, { displayName: string; description: string }> = {\n chrome: {\n displayName: 'Chrome / Edge',\n description: 'Google Chrome & Microsoft Edge (Chromium-based, uses CRXJS)',\n },\n firefox: {\n displayName: 'Firefox',\n description: 'Mozilla Firefox (uses vite-plugin-web-extension)',\n },\n};\n\nexport const CHROME_DEV_DEPS = [\n '@types/chrome',\n '@types/node',\n '@crxjs/vite-plugin',\n 'tailwindcss',\n '@tailwindcss/vite',\n];\n\nexport const FIREFOX_DEV_DEPS = [\n '@types/chrome',\n '@types/node',\n 'vite-plugin-web-extension',\n 'tailwindcss',\n '@tailwindcss/vite',\n];\n","import chalk from 'chalk';\nimport ora, { type Ora } from 'ora';\n\nlet verbose = false;\n\nexport function setVerbose(value: boolean): void {\n verbose = value;\n}\n\nexport function info(message: string): void {\n console.log(chalk.blue('ℹ'), message);\n}\n\nexport function success(message: string): void {\n console.log(chalk.green('✔'), message);\n}\n\nexport function warn(message: string): void {\n console.log(chalk.yellow('⚠'), message);\n}\n\nexport function error(message: string): void {\n console.log(chalk.red('✖'), message);\n}\n\nexport function debug(message: string): void {\n if (verbose) {\n console.log(chalk.gray('⬥'), chalk.gray(message));\n }\n}\n\nexport function spinner(text: string): Ora {\n return ora({ text, color: 'cyan' });\n}\n\nexport function newLine(): void {\n console.log();\n}\n\nexport function banner(text: string): void {\n console.log(chalk.bold.cyan(text));\n}\n","import path from 'node:path';\nimport fs from 'fs-extra';\nimport { fileURLToPath } from 'node:url';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\nexport function getTemplatesDir(): string {\n // When bundled with tsup, __dirname is dist/ → go up 1 level to project root\n // When running unbundled (vitest), __dirname is src/utils/ → go up 2 levels to project root\n const isBundled =\n __dirname.replace(/\\\\/g, '/').endsWith('/dist') ||\n __dirname.replace(/\\\\/g, '/').includes('/dist/');\n const projectRoot = isBundled\n ? path.resolve(__dirname, '..')\n : path.resolve(__dirname, '..', '..');\n return path.resolve(projectRoot, 'src', 'templates');\n}\n\nexport async function createDir(dirPath: string): Promise<void> {\n await fs.ensureDir(dirPath);\n}\n\nexport async function directoryExists(dirPath: string): Promise<boolean> {\n try {\n const stat = await fs.stat(dirPath);\n return stat.isDirectory();\n } catch {\n return false;\n }\n}\n\nexport async function fileExists(filePath: string): Promise<boolean> {\n try {\n const stat = await fs.stat(filePath);\n return stat.isFile();\n } catch {\n return false;\n }\n}\n","import { VALID_PROJECT_NAME } from '../constants.js';\nimport { directoryExists } from './file.js';\n\nexport function validateProjectName(name: string): string | true {\n if (!name || name.trim().length === 0) {\n return 'Project name is required.';\n }\n\n if (!VALID_PROJECT_NAME.test(name)) {\n return 'Project name must be a valid npm package name (lowercase, no spaces, can use hyphens and dots).';\n }\n\n if (name.length > 214) {\n return 'Project name must be less than 214 characters.';\n }\n\n return true;\n}\n\nexport async function validateDirectory(dirPath: string): Promise<string | true> {\n if (await directoryExists(dirPath)) {\n return `Directory \"${dirPath}\" already exists. Use --force to overwrite.`;\n }\n return true;\n}\n\nexport function checkNodeVersion(minMajor: number = 20): string | true {\n const version = process.version;\n const major = parseInt(version.slice(1).split('.')[0], 10);\n\n if (major < minMajor) {\n return `Node.js ${minMajor}+ is required. You are using ${version}.`;\n }\n\n return true;\n}\n","import path from 'node:path';\nimport { execSync } from 'node:child_process';\nimport fs from 'fs-extra';\nimport { getTemplatesDir, spinner, success, debug } from '../utils/index.js';\nimport { FILES_TO_REMOVE, CHROME_DEV_DEPS, FIREFOX_DEV_DEPS } from '../constants.js';\nimport type { ProjectConfig } from '../types/index.js';\n\nexport class ExtensionGenerator {\n private config: ProjectConfig;\n private templatesDir: string;\n\n constructor(config: ProjectConfig) {\n this.config = config;\n this.templatesDir = getTemplatesDir();\n }\n\n async generate(): Promise<void> {\n await this.scaffoldVite();\n await this.installBaseDeps();\n await this.cleanViteDefaults();\n await this.copySharedFiles();\n await this.copyBrowserFiles();\n await this.overrideConfigs();\n await this.patchTsConfig();\n await this.injectProjectName();\n await this.installExtensionDeps();\n }\n\n private async scaffoldVite(): Promise<void> {\n const s = spinner('Creating base Vite + React + TS project...');\n s.start();\n try {\n execSync(`npm create vite@latest ${this.config.name} -- --template react-ts`, {\n cwd: path.dirname(this.config.targetDir),\n stdio: 'pipe',\n maxBuffer: 10 * 1024 * 1024,\n });\n s.stop();\n success('Created base Vite + React + TS project');\n } catch (err) {\n s.stop();\n throw new Error(\n `Failed to scaffold Vite project: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n private async installBaseDeps(): Promise<void> {\n const s = spinner('Installing base dependencies...');\n s.start();\n try {\n execSync('npm install', {\n cwd: this.config.targetDir,\n stdio: 'pipe',\n maxBuffer: 10 * 1024 * 1024,\n });\n s.stop();\n success('Installed base dependencies');\n } catch (err) {\n s.stop();\n throw new Error(\n `Failed to install dependencies: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n private async cleanViteDefaults(): Promise<void> {\n const s = spinner('Removing default Vite template files...');\n s.start();\n\n for (const file of FILES_TO_REMOVE) {\n const fullPath = path.join(this.config.targetDir, file);\n if (await fs.pathExists(fullPath)) {\n await fs.remove(fullPath);\n debug(`Removed: ${file}`);\n }\n }\n\n const publicPath = path.join(this.config.targetDir, 'public');\n if (await fs.pathExists(publicPath)) {\n await fs.emptyDir(publicPath);\n }\n\n s.stop();\n success('Cleaned default Vite template files');\n }\n\n private async copySharedFiles(): Promise<void> {\n const s = spinner('Copying shared extension files...');\n s.start();\n\n const sharedDir = path.join(this.templatesDir, 'shared');\n\n // Copy shared src files (popup, options, utils, styles)\n const sharedSrcDir = path.join(sharedDir, 'src');\n if (await fs.pathExists(sharedSrcDir)) {\n await fs.copy(sharedSrcDir, path.join(this.config.targetDir, 'src'));\n }\n\n // Copy shared public files (icons)\n const sharedPublicDir = path.join(sharedDir, 'public');\n if (await fs.pathExists(sharedPublicDir)) {\n await fs.copy(sharedPublicDir, path.join(this.config.targetDir, 'public'));\n }\n\n s.stop();\n success('Copied shared extension files');\n }\n\n private async copyBrowserFiles(): Promise<void> {\n const s = spinner(`Copying ${this.config.browser} browser files...`);\n s.start();\n\n const browserDir = path.join(this.templatesDir, this.config.browser);\n\n // Copy browser-specific src files (manifest, background, content)\n const browserSrcDir = path.join(browserDir, 'src');\n if (await fs.pathExists(browserSrcDir)) {\n await fs.copy(browserSrcDir, path.join(this.config.targetDir, 'src'), {\n overwrite: true,\n });\n }\n\n s.stop();\n success(`Copied ${this.config.browser} browser files`);\n }\n\n private async overrideConfigs(): Promise<void> {\n const s = spinner('Overriding config files...');\n s.start();\n\n const browserDir = path.join(this.templatesDir, this.config.browser);\n const viteConfigFrom = path.join(browserDir, 'vite.config.ts');\n const viteConfigTo = path.join(this.config.targetDir, 'vite.config.ts');\n\n if (await fs.pathExists(viteConfigFrom)) {\n await fs.copy(viteConfigFrom, viteConfigTo, { overwrite: true });\n debug('Overrode: vite.config.ts');\n }\n\n s.stop();\n success('Overrode config files');\n }\n\n private async patchTsConfig(): Promise<void> {\n const s = spinner('Patching tsconfig for extension types...');\n s.start();\n\n const tsconfigAppPath = path.join(this.config.targetDir, 'tsconfig.app.json');\n\n if (await fs.pathExists(tsconfigAppPath)) {\n let content = await fs.readFile(tsconfigAppPath, 'utf-8');\n\n // Both Chrome and Firefox use @types/chrome (Firefox MV3 supports chrome.* namespace)\n const typeToAdd = 'chrome';\n\n // Inject the type into the \"types\" array using string replacement\n // This preserves comments and formatting in the JSONC file\n content = content.replace(\n /(\"types\"\\s*:\\s*\\[)([\\s\\S]*?)(\\])/,\n (match, prefix, existing, suffix) => {\n const trimmed = existing.trim();\n if (trimmed.includes(`\"${typeToAdd}\"`)) return match; // already present\n if (trimmed.length === 0) {\n return `${prefix}\"${typeToAdd}\"${suffix}`;\n }\n return `${prefix}${existing.trimEnd()}, \"${typeToAdd}\"${suffix}`;\n },\n );\n\n await fs.writeFile(tsconfigAppPath, content, 'utf-8');\n debug(`Patched tsconfig.app.json with type: ${typeToAdd}`);\n }\n\n s.stop();\n success('Patched tsconfig for extension types');\n }\n\n private async injectProjectName(): Promise<void> {\n const s = spinner('Injecting project name...');\n s.start();\n\n if (this.config.browser === 'chrome') {\n // Chrome uses manifest.ts\n const manifestPath = path.join(this.config.targetDir, 'src', 'manifest.ts');\n if (await fs.pathExists(manifestPath)) {\n let manifest = await fs.readFile(manifestPath, 'utf-8');\n manifest = manifest.replace(/__EXT_NAME__/g, this.config.name);\n await fs.writeFile(manifestPath, manifest, 'utf-8');\n }\n } else if (this.config.browser === 'firefox') {\n // Firefox uses manifest.json\n const manifestPath = path.join(this.config.targetDir, 'src', 'manifest.json');\n if (await fs.pathExists(manifestPath)) {\n let manifest = await fs.readFile(manifestPath, 'utf-8');\n manifest = manifest.replace(/__EXT_NAME__/g, this.config.name);\n await fs.writeFile(manifestPath, manifest, 'utf-8');\n }\n }\n\n // Update package.json name\n const pkgPath = path.join(this.config.targetDir, 'package.json');\n if (await fs.pathExists(pkgPath)) {\n let pkg = await fs.readFile(pkgPath, 'utf-8');\n pkg = pkg.replace(/\"__EXT_NAME__\"/g, `\"${this.config.name}\"`);\n await fs.writeFile(pkgPath, pkg, 'utf-8');\n }\n\n s.stop();\n success('Injected project name');\n }\n\n private async installExtensionDeps(): Promise<void> {\n const deps = this.config.browser === 'firefox' ? FIREFOX_DEV_DEPS : CHROME_DEV_DEPS;\n\n const s = spinner(`Installing ${this.config.browser} extension dev dependencies...`);\n s.start();\n try {\n execSync(`npm install --save-dev ${deps.join(' ')}`, {\n cwd: this.config.targetDir,\n stdio: 'pipe',\n maxBuffer: 10 * 1024 * 1024,\n });\n s.stop();\n success(`Installed ${this.config.browser} extension dev dependencies`);\n } catch (err) {\n s.stop();\n throw new Error(\n `Failed to install extension dependencies: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n}\n","{\n \"name\": \"create-vextro\",\n \"version\": \"0.1.0\",\n \"description\": \"Scaffold modern browser extensions (Chrome, Edge, Firefox) with Vite + React + Tailwind\",\n \"type\": \"module\",\n \"bin\": {\n \"create-vextro\": \"./dist/index.js\"\n },\n \"engines\": {\n \"node\": \">=20.0.0\"\n },\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"lint\": \"eslint src/\",\n \"format\": \"prettier --write .\",\n \"format:check\": \"prettier --check .\",\n \"prepublishOnly\": \"npm run build && npm test\",\n \"typecheck\": \"tsc --noEmit\"\n },\n \"dependencies\": {\n \"chalk\": \"^5.3.0\",\n \"commander\": \"^12.1.0\",\n \"fs-extra\": \"^11.2.0\",\n \"inquirer\": \"^9.2.8\",\n \"ora\": \"^8.0.0\"\n },\n \"devDependencies\": {\n \"@changesets/cli\": \"^2.27.0\",\n \"@types/fs-extra\": \"^11.0.4\",\n \"@types/inquirer\": \"^9.0.7\",\n \"@types/node\": \"^20.0.0\",\n \"eslint\": \"^9.0.0\",\n \"prettier\": \"^3.3.0\",\n \"tsup\": \"^8.0.0\",\n \"typescript\": \"^5.5.0\",\n \"typescript-eslint\": \"^8.0.0\",\n \"vitest\": \"^2.0.0\"\n },\n \"files\": [\n \"dist\",\n \"src/templates\"\n ],\n \"keywords\": [\n \"vite\",\n \"chrome-extension\",\n \"firefox-extension\",\n \"browser-extension\",\n \"vite-plugin\",\n \"react\",\n \"tailwind\",\n \"crxjs\",\n \"manifest-v3\",\n \"scaffold\",\n \"cli\",\n \"generator\",\n \"starter-kit\"\n ],\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/CodeCanvasCollective/vextro.git\"\n },\n \"author\": \"Lasantha <lasanthaslakmal@gmail.com>\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/CodeCanvasCollective/vextro/issues\"\n },\n \"homepage\": \"https://github.com/CodeCanvasCollective/vextro#readme\"\n}\n","import { createCli } from './cli.js';\n\nconst program = createCli();\nprogram.parse();\n"],"mappings":"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","names":["Command","path","fs","__filename","__dirname","path","fs","path","fs","path","fs","program","Command"]}
|