create-addfox-app 0.2.1 → 0.2.3

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
@@ -10,7 +10,8 @@
10
10
 
11
11
  Interactive scaffolder: generates a addfox-based extension project from options (template, package manager, entries, skills).
12
12
 
13
- - Commands: `create-addfox-app` or `pnpm create addfox-app` (and npm/yarn/bun equivalents)
13
+ - Commands: `addfox create` or `npx addfox@latest create` (subcommand mode)
14
+ - Legacy: `create-addfox-app` or `pnpm create addfox-app` (and npm/yarn/bun equivalents)
14
15
  - Flow: (1) select framework (vanilla / vue / react / preact / svelte / solid), (2) language (TypeScript / JavaScript), (3) package manager (pnpm / npm / yarn / bun), (4) entries to include (multi-select), (5) whether to install addfox skills (yes/no). Output to cwd or a given directory. Generated project uses **addfox.config.ts** or **addfox.config.js** with minimal manifest (entry discovery; no built-in entry paths in manifest).
15
16
 
16
17
  ## Templates
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ export declare function resolveLatestVersion(pkgName: string): string | null;
3
+ export declare function updatePackageVersions(destDir: string, versions?: {
4
+ addfox?: string | null;
5
+ utils?: string | null;
6
+ }): void;
7
+ export declare function runCreateApp(rawArgv?: string[]): Promise<void>;
package/dist/cli.js CHANGED
@@ -212,6 +212,12 @@ const FRAMEWORKS = [
212
212
  function getTemplateName(framework, language) {
213
213
  return `template-${framework}-${language}`;
214
214
  }
215
+ const PACKAGE_MANAGER_ORDER = [
216
+ "pnpm",
217
+ "npm",
218
+ "yarn",
219
+ "bun"
220
+ ];
215
221
  const PACKAGE_MANAGER_CHOICES = [
216
222
  {
217
223
  title: "pnpm",
@@ -826,6 +832,7 @@ function printHelp() {
826
832
  Create a new addfox extension project
827
833
 
828
834
  Usage:
835
+ addfox create [project-name] [options]
829
836
  create-addfox-app [project-name] [options]
830
837
 
831
838
  Options:
@@ -906,6 +913,7 @@ async function promptOptions() {
906
913
  name: "packageManager",
907
914
  message: "Select package manager",
908
915
  choices: getPackageManagerChoicesColored(),
916
+ initial: Math.max(0, PACKAGE_MANAGER_ORDER.indexOf(detectPackageManager())),
909
917
  hint: PROMPT_SELECT_HINT
910
918
  },
911
919
  {
@@ -987,19 +995,60 @@ function updatePackageName(destDir, projectName) {
987
995
  pkg.name = projectName.replace(/\s+/g, "-").toLowerCase();
988
996
  writeJsonFile(pkgPath, pkg);
989
997
  }
990
- async function main() {
991
- const argv = parseArgv(process.argv.slice(2));
998
+ function resolveLatestVersion(pkgName) {
999
+ try {
1000
+ const output = execSync(`npm view ${pkgName} version --registry https://registry.npmjs.org/`, {
1001
+ encoding: "utf-8",
1002
+ stdio: [
1003
+ "pipe",
1004
+ "pipe",
1005
+ "ignore"
1006
+ ],
1007
+ timeout: 3000
1008
+ });
1009
+ const version = output.trim();
1010
+ return version || null;
1011
+ } catch {
1012
+ return null;
1013
+ }
1014
+ }
1015
+ function updatePackageVersions(destDir, versions = {}) {
1016
+ const pkgPath = external_node_path_resolve(destDir, "package.json");
1017
+ if (!existsSync(pkgPath)) return;
1018
+ const addfoxVersion = versions.addfox ?? resolveLatestVersion("addfox");
1019
+ const utilsVersion = versions.utils ?? resolveLatestVersion("@addfox/utils");
1020
+ if (!addfoxVersion && !utilsVersion) return;
1021
+ const pkg = readJsonFile(pkgPath);
1022
+ if (addfoxVersion && pkg.devDependencies && "object" == typeof pkg.devDependencies) {
1023
+ const devDeps = pkg.devDependencies;
1024
+ if (devDeps["addfox"]) devDeps["addfox"] = `^${addfoxVersion}`;
1025
+ }
1026
+ if (utilsVersion) {
1027
+ if (pkg.dependencies && "object" == typeof pkg.dependencies) {
1028
+ const deps = pkg.dependencies;
1029
+ if (deps["@addfox/utils"]) deps["@addfox/utils"] = `^${utilsVersion}`;
1030
+ }
1031
+ if (pkg.devDependencies && "object" == typeof pkg.devDependencies) {
1032
+ const devDeps = pkg.devDependencies;
1033
+ if (devDeps["@addfox/utils"]) devDeps["@addfox/utils"] = `^${utilsVersion}`;
1034
+ }
1035
+ }
1036
+ writeJsonFile(pkgPath, pkg);
1037
+ }
1038
+ async function runCreateApp(rawArgv = process.argv.slice(2)) {
1039
+ const argv = parseArgv(rawArgv);
992
1040
  if (argv.help || argv.h) return void printHelp();
993
1041
  if (argv.version || argv.v) return void console.log(getVersion());
994
- const targetDir = argv._[0] ?? "my-extension";
1042
+ const targetDir = String(argv._[0] ?? "my-extension");
995
1043
  const cliFramework = argv.framework;
996
1044
  const cliLanguage = argv.language;
1045
+ console.log(dim(` create-addfox-app v${getVersion()}`));
997
1046
  printAddfoxLogo();
998
1047
  console.log(blue("\n Create Addfox App\n"));
999
1048
  const root = external_node_path_resolve(process.cwd(), targetDir);
1000
1049
  if (existsSync(root)) {
1001
1050
  const confirmed = await confirmOverwrite(targetDir);
1002
- if (!confirmed) process.exit(0);
1051
+ if (!confirmed) return;
1003
1052
  rmSync(root, {
1004
1053
  recursive: true,
1005
1054
  force: true
@@ -1014,9 +1063,9 @@ async function main() {
1014
1063
  "__all__"
1015
1064
  ]
1016
1065
  } : await promptOptions();
1017
- if (!options) process.exit(0);
1066
+ if (!options) return;
1018
1067
  const testSelection = cliFramework && cliLanguage ? resolveTestSelectionFromArgv(argv) : await promptTestAndReport();
1019
- if (!testSelection) process.exit(0);
1068
+ if (!testSelection) return;
1020
1069
  const pm = options.packageManager;
1021
1070
  const templateName = getTemplateName(options.framework, options.language);
1022
1071
  const templateReady = hasLocalTemplate(templateName);
@@ -1032,8 +1081,7 @@ async function main() {
1032
1081
  minVisibleMs: TEMPLATE_SPINNER_MIN_MS
1033
1082
  } : void 0);
1034
1083
  } catch (err) {
1035
- console.error(red(`\n Failed to copy template: ${err.message}\n`));
1036
- process.exit(1);
1084
+ throw new Error(`Failed to copy template: ${err.message}`);
1037
1085
  }
1038
1086
  const useAllEntries = options.entries.includes("__all__");
1039
1087
  const existingDirs = getExistingAppEntryDirs(root);
@@ -1050,6 +1098,7 @@ async function main() {
1050
1098
  } else configContent = generateAddfoxConfig(options.framework, options.language, options.styleEngine);
1051
1099
  writeFileSync(configPath, configContent, "utf-8");
1052
1100
  updatePackageName(root, targetDir);
1101
+ updatePackageVersions(root);
1053
1102
  applyStyleEngine(root, options.framework, options.language, options.styleEngine);
1054
1103
  applyTestAndReportSetup(root, options.language, testSelection);
1055
1104
  const installCmd = getInstallCommand(pm);
@@ -1090,7 +1139,12 @@ async function main() {
1090
1139
  console.log(` ${installCmd}`);
1091
1140
  console.log(` ${devCmd}\n`);
1092
1141
  }
1093
- main().catch((e)=>{
1142
+ async function main() {
1143
+ await runCreateApp();
1144
+ }
1145
+ const isCli = import.meta.url.startsWith("file://") && process.argv[1] === fileURLToPath(import.meta.url);
1146
+ if (isCli) main().catch((e)=>{
1094
1147
  console.error(e);
1095
1148
  process.exit(1);
1096
1149
  });
1150
+ export { resolveLatestVersion, runCreateApp, updatePackageVersions };
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Published dependency ranges (caret baseline) for scaffold `package.json` under `create-addfox-app/templates/`.
3
+ * Bump when releasing new versions of `addfox`, `@addfox/utils`, or `@rsbuild/plugin-vue` in templates.
4
+ *
5
+ * Templates put `addfox` and `@rsbuild/plugin-*` in **devDependencies** (build-time only).
6
+ * `@addfox/utils` is in **dependencies** so extension runtime code can import it.
7
+ * Do **not** list `@rsbuild/core` in templates: it is pulled in by `addfox` → `@addfox/cli` / `@addfox/core`
8
+ * and satisfies peers for Rsbuild plugins.
9
+ *
10
+ * `ADDFOX_0_1_SCAFFOLD_RANGE` is a semver range for tooling/docs that must accept 0.1.x prereleases.
11
+ */
12
+ export declare const ADDFOX_CLI_PACKAGE_VERSION: "^0.2.0";
13
+ export declare const ADDFOX_UTILS_PACKAGE_VERSION: "^0.2.0";
14
+ /** `@rsbuild/plugin-vue` range in `template-vue-ts` / `template-vue-js` (aligned across JS/TS). */
15
+ export declare const RSBUILD_PLUGIN_VUE_PACKAGE_VERSION: "^1.2.8";
16
+ /**
17
+ * `^0.2.0` matches stable releases in the 0.2.x line.
18
+ */
19
+ export declare const ADDFOX_0_1_SCAFFOLD_RANGE: ">=0.2.0-0 <0.3.0";
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Full `addfox.config` codegen when the template has no usable file (fallback only).
3
+ * Normal scaffold: the template’s addfox.config is kept and merged (see `./merge.ts`).
4
+ */
5
+ import type { Framework, Language, StyleEngine } from "../template/catalog.ts";
6
+ export declare function getStylePlugin(engine: StyleEngine | undefined): {
7
+ importLine: string;
8
+ call: string;
9
+ } | null;
10
+ export declare function generateAddfoxConfig(framework: Framework, _language: Language, styleEngine?: StyleEngine): string;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Merges scaffold-time changes into an existing addfox.config from the template
3
+ * (preserves manifest and framework plugins; only injects Less/Sass Rsbuild plugins when selected).
4
+ */
5
+ import type { StyleEngine } from "../template/catalog.ts";
6
+ /**
7
+ * After copying a template, merge only Less/Sass Rsbuild plugins into the existing config.
8
+ * Tailwind/UnoCSS need no changes here (postcss + applyStyleEngine). Manifest stays as in template.
9
+ */
10
+ export declare function mergeScaffoldIntoAddfoxConfig(source: string, styleEngine: StyleEngine): string;
@@ -0,0 +1 @@
1
+ export { runCreateApp } from "./cli/index.ts";
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export { runCreateApp } from "./cli.js";
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Lightweight ANSI color helpers.
3
+ * Replaces the `kolorist` npm package to reduce supply-chain risk.
4
+ */
5
+ export declare const black: (s: string) => string;
6
+ export declare const red: (s: string) => string;
7
+ export declare const green: (s: string) => string;
8
+ export declare const yellow: (s: string) => string;
9
+ export declare const blue: (s: string) => string;
10
+ export declare const magenta: (s: string) => string;
11
+ export declare const cyan: (s: string) => string;
12
+ export declare const white: (s: string) => string;
13
+ export declare const gray: (s: string) => string;
14
+ export declare const lightRed: (s: string) => string;
15
+ export declare const lightGreen: (s: string) => string;
16
+ export declare const lightYellow: (s: string) => string;
17
+ export declare const lightBlue: (s: string) => string;
18
+ export declare const lightMagenta: (s: string) => string;
19
+ export declare const lightCyan: (s: string) => string;
20
+ export declare const lightGray: (s: string) => string;
21
+ export declare const dim: (s: string) => string;
22
+ /** True-color (24-bit) helper. */
23
+ export declare function trueColor(r: number, g: number, b: number): (s: string) => string;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Read/write JSON files; strips UTF-8 BOM on read so JSON.parse never fails.
3
+ */
4
+ export declare function stripUtf8Bom(text: string): string;
5
+ export declare function readJsonFile<T = unknown>(path: string): T;
6
+ export declare function writeJsonFile(path: string, data: unknown): void;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Minimal argv parser.
3
+ * Replaces the `minimist` npm package to reduce supply-chain risk.
4
+ */
5
+ export interface ParsedArgv {
6
+ _: (string | number)[];
7
+ [key: string]: string | number | boolean | (string | number)[] | undefined;
8
+ }
9
+ export declare function parseArgv(argv: string[]): ParsedArgv;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * CLI banner: FIGlet-style block letters (same family as Vercel skills CLI).
3
+ *
4
+ * Uses **256-color** ANSI (`\x1b[38;5;Nm`) for the orange→pink gradient, not 24-bit
5
+ * truecolor (`38;2;r;g;b`). Apple Terminal + zsh often mishandle or strip truecolor;
6
+ * Vercel `skills` uses per-line 256-color grays for the same reason.
7
+ *
8
+ * @see https://github.com/vercel-labs/skills/blob/main/src/cli.ts (showLogo / GRAYS)
9
+ */
10
+ export declare function printAddfoxLogo(): void;
@@ -0,0 +1,6 @@
1
+ import type { PackageManager } from "@addfox/pkg-manager";
2
+ export declare const PACKAGE_MANAGER_ORDER: PackageManager[];
3
+ export declare const PACKAGE_MANAGER_CHOICES: {
4
+ title: string;
5
+ value: PackageManager;
6
+ }[];
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Skills step: fetch addfox/skills list and run skills add via package manager.
3
+ */
4
+ export interface SkillItem {
5
+ name: string;
6
+ value: string;
7
+ }
8
+ /** Fetch list of skill names (directories with SKILL.md) from addfox/skills. */
9
+ export declare function fetchSkillsList(): Promise<SkillItem[]>;
10
+ export declare function getSkillsChoices(skills: SkillItem[]): {
11
+ title: string;
12
+ value: string;
13
+ }[];
14
+ /** Build the skills add command args: repo or repo/skill1 repo/skill2 ... */
15
+ export declare function getSkillsAddArgs(selected: string[]): string[];
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Extension entry types supported by create-addfox-app.
3
+ * Matches addfox MANIFEST_ENTRY_KEYS; app/ subdir names align with these.
4
+ */
5
+ export declare const ENTRY_NAMES: readonly ["popup", "options", "background", "content", "devtools", "sidepanel", "sandbox", "newtab", "bookmarks", "history", "offscreen"];
6
+ export type EntryName = (typeof ENTRY_NAMES)[number];
7
+ /** app/ directory name for each entry (same as entry name) */
8
+ export declare const ENTRY_APP_DIRS: readonly EntryName[];
9
+ export declare const ENTRY_CHOICES: {
10
+ title: string;
11
+ value: string;
12
+ }[];
13
+ /** Entries that require extra permissions when added to manifest */
14
+ export declare const ENTRY_EXTRA_PERMISSIONS: Record<string, string[]>;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Apply selected style engine after template copy: deps, config files, and entry imports.
3
+ */
4
+ import type { Framework, Language, StyleEngine } from "../template/catalog.ts";
5
+ export declare function applyStyleEngine(root: string, _framework: Framework, language: Language, engine: StyleEngine): void;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Optional rstest + Rsdoctor setup for scaffolded projects.
3
+ * Versions align with @rstest/core usage in this monorepo.
4
+ */
5
+ import type { Language } from "../template/catalog.ts";
6
+ export type TestKind = "unit" | "e2e";
7
+ export interface TestSetupSelection {
8
+ testKinds: TestKind[];
9
+ installRsdoctor: boolean;
10
+ }
11
+ export declare function generateRstestConfig(language: Language, testKinds: TestKind[]): string;
12
+ export declare function applyTestAndReportSetup(root: string, language: Language, selection: TestSetupSelection): void;
@@ -0,0 +1,13 @@
1
+ /** True if the bundled `templates/<name>` directory exists in this package. */
2
+ export declare function hasLocalTemplate(templateName: string): boolean;
3
+ /**
4
+ * Skip dependency trees and VCS **inside the template folder** when copying.
5
+ * Must use paths relative to `templateRoot`: the install path often contains
6
+ * `.pnpm` / `node_modules` (e.g. pnpm store), which must not cause every file to be skipped.
7
+ */
8
+ export declare function shouldCopyLocalTemplatePath(src: string, templateRoot: string): boolean;
9
+ /**
10
+ * Copy bundled template from node_modules/create-addfox-app/templates into destDir.
11
+ * @throws If the template folder is missing (broken install or forgot to run build).
12
+ */
13
+ export declare function copyBundledTemplate(templateName: string, destDir: string): Promise<void>;
@@ -0,0 +1,17 @@
1
+ export type Framework = "vanilla" | "vue" | "react" | "preact" | "svelte" | "solid";
2
+ export type Language = "js" | "ts";
3
+ /** Style / CSS tooling (after framework). `none` = no extra style stack. */
4
+ export type StyleEngine = "none" | "tailwindcss" | "unocss" | "less" | "sass";
5
+ export declare const STYLE_ENGINES: {
6
+ title: string;
7
+ value: StyleEngine;
8
+ }[];
9
+ export declare const FRAMEWORKS: {
10
+ title: string;
11
+ value: Framework;
12
+ }[];
13
+ export declare const LANGUAGES: {
14
+ title: string;
15
+ value: Language;
16
+ }[];
17
+ export declare function getTemplateName(framework: Framework, language: Language): string;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Removes app/ entry dirs that are not in the selected entries list.
3
+ * Call after copying template when user did not select "all" entries.
4
+ */
5
+ export declare function filterAppEntries(destDir: string, selectedEntries: string[]): void;
6
+ /** Returns entry dir names that exist under app/ in the template. */
7
+ export declare function getExistingAppEntryDirs(destDir: string): string[];
@@ -0,0 +1,12 @@
1
+ export type TemplateSpinnerOptions = {
2
+ /**
3
+ * Keep the spinner visible at least this long after `fn` starts.
4
+ * Use for fast local copies so the animation is noticeable (sync work used to block the spinner).
5
+ */
6
+ minVisibleMs?: number;
7
+ };
8
+ /**
9
+ * Runs `fn` while showing a Braille-dot spinner + label on one terminal line.
10
+ * No-op animation when stdout is not a TTY (e.g. CI).
11
+ */
12
+ export declare function runWithTemplateSpinner<T>(label: string, fn: () => Promise<T>, options?: TemplateSpinnerOptions): Promise<T>;
package/package.json CHANGED
@@ -1,9 +1,14 @@
1
1
  {
2
2
  "name": "create-addfox-app",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Interactive scaffolder for addfox extension projects",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
+ "main": "./dist/index.js",
8
+ "exports": {
9
+ ".": "./dist/index.js",
10
+ "./cli": "./dist/cli.js"
11
+ },
7
12
  "bin": {
8
13
  "create-addfox-app": "./dist/cli.js"
9
14
  },
@@ -14,7 +19,7 @@
14
19
  ],
15
20
  "dependencies": {
16
21
  "prompts": "^2.4.2",
17
- "@addfox/pkg-manager": "0.2.1"
22
+ "@addfox/pkg-manager": "0.2.3"
18
23
  },
19
24
  "engines": {
20
25
  "node": ">=18.0.0"
@@ -24,6 +29,7 @@
24
29
  "@rstest/core": "^0.9.9",
25
30
  "@rstest/coverage-istanbul": "^0.3.2",
26
31
  "@types/node": "^20.0.0",
32
+ "@types/prompts": "^2.4.9",
27
33
  "typescript": "^5.0.0"
28
34
  },
29
35
  "scripts": {