create-addfox-app 0.1.1-beta.10 → 0.1.1-beta.12

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.
Files changed (148) hide show
  1. package/README.md +1 -1
  2. package/dist/cli.js +94 -81
  3. package/package.json +2 -1
  4. package/templates/README.md +3 -0
  5. package/templates/template-preact-js/addfox.config.js +27 -0
  6. package/templates/template-preact-js/app/background/index.js +8 -0
  7. package/templates/template-preact-js/app/content/index.js +1 -0
  8. package/templates/template-preact-js/app/options/App.jsx +5 -0
  9. package/templates/template-preact-js/app/options/index.jsx +5 -0
  10. package/templates/template-preact-js/app/popup/App.jsx +23 -0
  11. package/templates/template-preact-js/app/popup/index.jsx +5 -0
  12. package/templates/template-preact-js/app/sidepanel/App.jsx +5 -0
  13. package/templates/template-preact-js/app/sidepanel/index.jsx +5 -0
  14. package/templates/template-preact-js/package.json +19 -0
  15. package/templates/template-preact-js/public/icons/icon_128.png +0 -0
  16. package/templates/template-preact-js/public/welcome.html +19 -0
  17. package/templates/template-preact-ts/addfox.config.ts +27 -0
  18. package/templates/template-preact-ts/app/background/index.ts +8 -0
  19. package/templates/template-preact-ts/app/content/index.ts +1 -0
  20. package/templates/template-preact-ts/app/options/App.tsx +5 -0
  21. package/templates/template-preact-ts/app/options/index.tsx +5 -0
  22. package/templates/template-preact-ts/app/popup/App.tsx +23 -0
  23. package/templates/template-preact-ts/app/popup/index.tsx +5 -0
  24. package/templates/template-preact-ts/app/sidepanel/App.tsx +5 -0
  25. package/templates/template-preact-ts/app/sidepanel/index.tsx +5 -0
  26. package/templates/template-preact-ts/package.json +21 -0
  27. package/templates/template-preact-ts/public/icons/icon_128.png +0 -0
  28. package/templates/template-preact-ts/public/welcome.html +19 -0
  29. package/templates/template-preact-ts/tsconfig.json +19 -0
  30. package/templates/template-react-js/addfox.config.js +27 -0
  31. package/templates/template-react-js/app/background/index.js +8 -0
  32. package/templates/template-react-js/app/content/index.js +1 -0
  33. package/templates/template-react-js/app/options/App.jsx +3 -0
  34. package/templates/template-react-js/app/options/index.jsx +5 -0
  35. package/templates/template-react-js/app/popup/App.jsx +21 -0
  36. package/templates/template-react-js/app/popup/index.jsx +5 -0
  37. package/templates/template-react-js/app/sidepanel/App.jsx +3 -0
  38. package/templates/template-react-js/app/sidepanel/index.jsx +5 -0
  39. package/templates/template-react-js/package.json +20 -0
  40. package/templates/template-react-js/public/icons/icon_128.png +0 -0
  41. package/templates/template-react-js/public/welcome.html +19 -0
  42. package/templates/template-react-ts/addfox.config.ts +27 -0
  43. package/templates/template-react-ts/app/background/index.ts +8 -0
  44. package/templates/template-react-ts/app/content/index.ts +1 -0
  45. package/templates/template-react-ts/app/options/App.tsx +3 -0
  46. package/templates/template-react-ts/app/options/index.tsx +5 -0
  47. package/templates/template-react-ts/app/popup/App.tsx +21 -0
  48. package/templates/template-react-ts/app/popup/index.tsx +5 -0
  49. package/templates/template-react-ts/app/sidepanel/App.tsx +3 -0
  50. package/templates/template-react-ts/app/sidepanel/index.tsx +5 -0
  51. package/templates/template-react-ts/package.json +24 -0
  52. package/templates/template-react-ts/public/icons/icon_128.png +0 -0
  53. package/templates/template-react-ts/public/welcome.html +19 -0
  54. package/templates/template-react-ts/tsconfig.json +19 -0
  55. package/templates/template-solid-js/addfox.config.js +28 -0
  56. package/templates/template-solid-js/app/background/index.js +8 -0
  57. package/templates/template-solid-js/app/content/index.js +1 -0
  58. package/templates/template-solid-js/app/options/App.jsx +3 -0
  59. package/templates/template-solid-js/app/options/index.jsx +5 -0
  60. package/templates/template-solid-js/app/popup/App.jsx +26 -0
  61. package/templates/template-solid-js/app/popup/index.jsx +5 -0
  62. package/templates/template-solid-js/app/sidepanel/App.jsx +3 -0
  63. package/templates/template-solid-js/app/sidepanel/index.jsx +5 -0
  64. package/templates/template-solid-js/package.json +20 -0
  65. package/templates/template-solid-js/public/icons/icon_128.png +0 -0
  66. package/templates/template-solid-js/public/welcome.html +19 -0
  67. package/templates/template-solid-ts/addfox.config.ts +28 -0
  68. package/templates/template-solid-ts/app/background/index.ts +8 -0
  69. package/templates/template-solid-ts/app/content/index.ts +1 -0
  70. package/templates/template-solid-ts/app/options/App.tsx +3 -0
  71. package/templates/template-solid-ts/app/options/index.tsx +5 -0
  72. package/templates/template-solid-ts/app/popup/App.tsx +26 -0
  73. package/templates/template-solid-ts/app/popup/index.tsx +5 -0
  74. package/templates/template-solid-ts/app/sidepanel/App.tsx +3 -0
  75. package/templates/template-solid-ts/app/sidepanel/index.tsx +5 -0
  76. package/templates/template-solid-ts/package.json +22 -0
  77. package/templates/template-solid-ts/public/icons/icon_128.png +0 -0
  78. package/templates/template-solid-ts/public/welcome.html +19 -0
  79. package/templates/template-solid-ts/tsconfig.json +19 -0
  80. package/templates/template-svelte-js/addfox.config.js +27 -0
  81. package/templates/template-svelte-js/app/background/index.js +8 -0
  82. package/templates/template-svelte-js/app/content/index.js +1 -0
  83. package/templates/template-svelte-js/app/options/App.svelte +4 -0
  84. package/templates/template-svelte-js/app/options/index.js +4 -0
  85. package/templates/template-svelte-js/app/popup/App.svelte +24 -0
  86. package/templates/template-svelte-js/app/popup/index.js +4 -0
  87. package/templates/template-svelte-js/app/sidepanel/App.svelte +4 -0
  88. package/templates/template-svelte-js/app/sidepanel/index.js +4 -0
  89. package/templates/template-svelte-js/package.json +19 -0
  90. package/templates/template-svelte-js/public/icons/icon_128.png +0 -0
  91. package/templates/template-svelte-js/public/welcome.html +19 -0
  92. package/templates/template-svelte-ts/addfox.config.ts +27 -0
  93. package/templates/template-svelte-ts/app/background/index.ts +8 -0
  94. package/templates/template-svelte-ts/app/content/index.ts +1 -0
  95. package/templates/template-svelte-ts/app/options/App.svelte +4 -0
  96. package/templates/template-svelte-ts/app/options/index.ts +4 -0
  97. package/templates/template-svelte-ts/app/popup/App.svelte +24 -0
  98. package/templates/template-svelte-ts/app/popup/index.ts +4 -0
  99. package/templates/template-svelte-ts/app/sidepanel/App.svelte +4 -0
  100. package/templates/template-svelte-ts/app/sidepanel/index.ts +4 -0
  101. package/templates/template-svelte-ts/package.json +21 -0
  102. package/templates/template-svelte-ts/public/icons/icon_128.png +0 -0
  103. package/templates/template-svelte-ts/public/welcome.html +19 -0
  104. package/templates/template-svelte-ts/tsconfig.json +19 -0
  105. package/templates/template-vanilla-js/addfox.config.js +25 -0
  106. package/templates/template-vanilla-js/app/background/index.js +8 -0
  107. package/templates/template-vanilla-js/app/content/index.js +1 -0
  108. package/templates/template-vanilla-js/app/options/index.js +2 -0
  109. package/templates/template-vanilla-js/app/popup/index.js +37 -0
  110. package/templates/template-vanilla-js/app/sidepanel/index.js +2 -0
  111. package/templates/template-vanilla-js/package.json +15 -0
  112. package/templates/template-vanilla-js/public/icons/icon_128.png +0 -0
  113. package/templates/template-vanilla-js/public/welcome.html +19 -0
  114. package/templates/template-vanilla-ts/addfox.config.ts +25 -0
  115. package/templates/template-vanilla-ts/app/background/index.ts +8 -0
  116. package/templates/template-vanilla-ts/app/content/index.ts +1 -0
  117. package/templates/template-vanilla-ts/app/options/index.ts +2 -0
  118. package/templates/template-vanilla-ts/app/popup/index.ts +37 -0
  119. package/templates/template-vanilla-ts/app/sidepanel/index.ts +2 -0
  120. package/templates/template-vanilla-ts/package.json +17 -0
  121. package/templates/template-vanilla-ts/public/icons/icon_128.png +0 -0
  122. package/templates/template-vanilla-ts/public/welcome.html +19 -0
  123. package/templates/template-vanilla-ts/tsconfig.json +17 -0
  124. package/templates/template-vue-js/addfox.config.js +27 -0
  125. package/templates/template-vue-js/app/background/index.js +8 -0
  126. package/templates/template-vue-js/app/content/index.js +1 -0
  127. package/templates/template-vue-js/app/options/App.vue +6 -0
  128. package/templates/template-vue-js/app/options/index.js +5 -0
  129. package/templates/template-vue-js/app/popup/App.vue +28 -0
  130. package/templates/template-vue-js/app/popup/index.js +5 -0
  131. package/templates/template-vue-js/app/sidepanel/App.vue +6 -0
  132. package/templates/template-vue-js/app/sidepanel/index.js +5 -0
  133. package/templates/template-vue-js/package.json +20 -0
  134. package/templates/template-vue-js/public/icons/icon_128.png +0 -0
  135. package/templates/template-vue-js/public/welcome.html +19 -0
  136. package/templates/template-vue-ts/addfox.config.ts +27 -0
  137. package/templates/template-vue-ts/app/background/index.ts +8 -0
  138. package/templates/template-vue-ts/app/content/index.ts +1 -0
  139. package/templates/template-vue-ts/app/options/App.vue +6 -0
  140. package/templates/template-vue-ts/app/options/index.ts +5 -0
  141. package/templates/template-vue-ts/app/popup/App.vue +26 -0
  142. package/templates/template-vue-ts/app/popup/index.ts +5 -0
  143. package/templates/template-vue-ts/app/sidepanel/App.vue +6 -0
  144. package/templates/template-vue-ts/app/sidepanel/index.ts +5 -0
  145. package/templates/template-vue-ts/package.json +23 -0
  146. package/templates/template-vue-ts/public/icons/icon_128.png +0 -0
  147. package/templates/template-vue-ts/public/welcome.html +19 -0
  148. package/templates/template-vue-ts/tsconfig.json +19 -0
package/README.md CHANGED
@@ -15,4 +15,4 @@ Interactive scaffolder: generates a addfox-based extension project from options
15
15
 
16
16
  ## Templates
17
17
 
18
- Scaffold templates live at **repo root** in **`templates/`** (e.g. `template-vanilla-ts`, `template-react-ts`). The CLI downloads the chosen template from the GitHub repo tarball, so the repo must have `templates/` at root and committed. The npm package does not bundle templates; it fetches from GitHub at scaffold time.
18
+ Scaffold templates ship inside this package under **`templates/`** (e.g. `template-vanilla-ts`, `template-react-ts`). They are included in the published npm tarball; the CLI copies the chosen folder into your new project (no remote download). The template’s **`addfox.config`** (including `manifest`) is **preserved**; the CLI only **merges** Rsbuild Less/Sass plugins when those style engines are selected. If no config file exists, a full fallback config is generated.
package/dist/cli.js CHANGED
@@ -8,91 +8,40 @@ import prompts from "prompts";
8
8
  import { blue, cyan, dim, gray, green, lightBlue, magenta, red, trueColor, yellow } from "kolorist";
9
9
  import minimist from "minimist";
10
10
  import { cp, mkdir } from "node:fs/promises";
11
- import { gunzipSync } from "node:zlib";
12
11
  import { detectPackageManager, getExecCommand, getInstallCommand, getRunCommand } from "@addfox/pkg-manager";
13
- const GITHUB_REPO = "addfox/addfox";
14
12
  const TEMPLATE_BASE = "templates";
15
- function getRepoRoot() {
16
- const fromDist = external_node_path_resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "..");
17
- return fromDist;
13
+ function getPackageRoot() {
14
+ return external_node_path_resolve(dirname(fileURLToPath(import.meta.url)), "..");
15
+ }
16
+ function getBundledTemplatesDir() {
17
+ return join(getPackageRoot(), TEMPLATE_BASE);
18
18
  }
19
19
  function hasLocalTemplate(templateName) {
20
- const repoRoot = getRepoRoot();
21
- return existsSync(join(repoRoot, TEMPLATE_BASE, templateName));
20
+ return existsSync(join(getBundledTemplatesDir(), templateName));
22
21
  }
23
- const SKIP_TEMPLATE_PATH_PARTS = new Set([
22
+ const SKIP_RELATIVE_SEGMENTS = new Set([
24
23
  "node_modules",
25
24
  ".git",
26
25
  ".pnpm"
27
26
  ]);
28
- function shouldCopyLocalTemplatePath(src) {
29
- const normalized = src.split(/[/\\]/);
30
- for (const part of normalized)if (SKIP_TEMPLATE_PATH_PARTS.has(part)) return false;
27
+ function shouldCopyLocalTemplatePath(src, templateRoot) {
28
+ const root = external_node_path_resolve(templateRoot);
29
+ const rel = relative(root, external_node_path_resolve(src));
30
+ if (rel.startsWith("..")) return true;
31
+ for (const part of rel.split(/[/\\]/))if (SKIP_RELATIVE_SEGMENTS.has(part)) return false;
31
32
  return true;
32
33
  }
33
- async function tryLocalTemplates(templateName, destDir) {
34
- const repoRoot = getRepoRoot();
35
- const templatePath = join(repoRoot, TEMPLATE_BASE, templateName);
36
- if (!existsSync(templatePath)) return false;
34
+ async function copyBundledTemplate(templateName, destDir) {
35
+ const templatePath = join(getBundledTemplatesDir(), templateName);
36
+ if (!existsSync(templatePath)) throw new Error(`Bundled template "${templateName}" not found under create-addfox-app. Reinstall the package.`);
37
37
  await mkdir(destDir, {
38
38
  recursive: true
39
39
  });
40
- await cp(templatePath, destDir, {
40
+ const resolvedTemplate = external_node_path_resolve(templatePath);
41
+ await cp(resolvedTemplate, destDir, {
41
42
  recursive: true,
42
- filter: (src)=>shouldCopyLocalTemplatePath(src)
43
+ filter: (src)=>shouldCopyLocalTemplatePath(src, resolvedTemplate)
43
44
  });
44
- return true;
45
- }
46
- function parseTarHeader(buf) {
47
- if (buf.every((b)=>0 === b)) return null;
48
- const rawName = buf.subarray(0, 100).toString("utf8").replace(/\0+$/, "");
49
- const sizeStr = buf.subarray(124, 136).toString("utf8").replace(/\0+$/, "").trim();
50
- const type = String.fromCharCode(buf[156]);
51
- const prefix = buf.subarray(345, 500).toString("utf8").replace(/\0+$/, "");
52
- return {
53
- name: prefix ? `${prefix}/${rawName}` : rawName,
54
- size: parseInt(sizeStr, 8) || 0,
55
- type
56
- };
57
- }
58
- function extractMatchingFiles(tarBuffer, templatePrefix, destDir) {
59
- let offset = 0;
60
- while(offset + 512 <= tarBuffer.length){
61
- const header = parseTarHeader(tarBuffer.subarray(offset, offset + 512));
62
- offset += 512;
63
- if (!header) break;
64
- const dataBlocks = 512 * Math.ceil(header.size / 512);
65
- const idx = header.name.indexOf(templatePrefix);
66
- if (-1 !== idx) {
67
- const relativePath = header.name.substring(idx + templatePrefix.length).replace(/^\//, "");
68
- if (relativePath) {
69
- const destPath = join(destDir, relativePath);
70
- const isDir = "5" === header.type || relativePath.endsWith("/");
71
- if (isDir) mkdirSync(destPath, {
72
- recursive: true
73
- });
74
- else if ("0" === header.type || "\0" === header.type) {
75
- mkdirSync(dirname(destPath), {
76
- recursive: true
77
- });
78
- writeFileSync(destPath, tarBuffer.subarray(offset, offset + header.size));
79
- }
80
- }
81
- }
82
- offset += dataBlocks;
83
- }
84
- }
85
- async function downloadTemplate(templateName, destDir, branch = "main") {
86
- const url = `https://codeload.github.com/${GITHUB_REPO}/tar.gz/${branch}`;
87
- const templatePrefix = `${TEMPLATE_BASE}/${templateName}/`;
88
- const response = await fetch(url);
89
- if (!response.ok) throw new Error(`HTTP ${response.status}: ${response.statusText}`);
90
- const compressed = Buffer.from(await response.arrayBuffer());
91
- const tarBuffer = gunzipSync(compressed);
92
- mkdirSync(destDir, {
93
- recursive: true
94
- });
95
- extractMatchingFiles(tarBuffer, templatePrefix, destDir);
96
45
  }
97
46
  const FRAMES = [
98
47
  "⠋",
@@ -295,7 +244,7 @@ function getStylePlugin(engine) {
295
244
  };
296
245
  return null;
297
246
  }
298
- const MINIMAL_MANIFEST = ' name: "My Extension",\n version: "1.0.0",\n manifest_version: 3,\n description: "Browser extension built with addfox",\n permissions: ["storage", "activeTab"],';
247
+ const MINIMAL_MANIFEST = ' name: "My Extension",\n version: "1.0.0",\n manifest_version: 3,\n description: "Browser extension built with addfox",\n permissions: ["activeTab"],\n icons: {\n "16": "icons/icon_128.png",\n "48": "icons/icon_128.png",\n "128": "icons/icon_128.png",\n },\n action: {\n default_icon: {\n "16": "icons/icon_128.png",\n "48": "icons/icon_128.png",\n "128": "icons/icon_128.png",\n },\n },';
299
248
  function generateAddfoxConfig(framework, _language, styleEngine) {
300
249
  const importLines = [];
301
250
  const fwImport = getFrameworkPluginImport(framework);
@@ -323,6 +272,67 @@ function generateAddfoxConfig(framework, _language, styleEngine) {
323
272
  ];
324
273
  return parts.filter(Boolean).join("\n");
325
274
  }
275
+ const MANIFEST_LINE = " manifest: { chromium: manifest, firefox: { ...manifest } },";
276
+ function findMatchingArrayClose(source, openBracketIdx) {
277
+ if ("[" !== source[openBracketIdx]) return -1;
278
+ let depth = 0;
279
+ for(let i = openBracketIdx; i < source.length; i++){
280
+ const c = source[i];
281
+ if ("[" === c) depth++;
282
+ else if ("]" === c) {
283
+ depth--;
284
+ if (0 === depth) return i;
285
+ }
286
+ }
287
+ return -1;
288
+ }
289
+ function hasImportFromModule(source, modulePath) {
290
+ return source.includes(`from "${modulePath}"`) || source.includes(`from '${modulePath}'`) || source.includes(`from \`${modulePath}\``);
291
+ }
292
+ function insertImportAfterLastImport(source, importLine) {
293
+ const trimmed = importLine.trim();
294
+ const moduleMatch = /from\s+["']([^"']+)["']/.exec(trimmed);
295
+ if (moduleMatch && hasImportFromModule(source, moduleMatch[1])) return source;
296
+ const lines = source.split("\n");
297
+ let lastImportIdx = -1;
298
+ for(let i = 0; i < lines.length; i++)if (lines[i].trim().startsWith("import ")) lastImportIdx = i;
299
+ if (-1 === lastImportIdx) return `${importLine}\n${source}`;
300
+ const next = [
301
+ ...lines
302
+ ];
303
+ next.splice(lastImportIdx + 1, 0, importLine);
304
+ return next.join("\n");
305
+ }
306
+ function pluginCallPresent(inner, call) {
307
+ const fn = call.replace(/\(\)$/, "");
308
+ return inner.includes(`${fn}(`);
309
+ }
310
+ function mergeCallsIntoPluginsArray(source, calls) {
311
+ const pluginsIdx = source.indexOf("plugins:");
312
+ if (-1 === pluginsIdx) return addPluginsBlockAfterManifest(source, calls);
313
+ const bracketStart = source.indexOf("[", pluginsIdx);
314
+ if (-1 === bracketStart) return source;
315
+ const bracketEnd = findMatchingArrayClose(source, bracketStart);
316
+ if (-1 === bracketEnd) return source;
317
+ let inner = source.slice(bracketStart + 1, bracketEnd).trim();
318
+ for (const c of calls)if (!pluginCallPresent(inner, c)) inner = inner ? `${inner}, ${c}` : c;
319
+ return source.slice(0, bracketStart + 1) + inner + source.slice(bracketEnd);
320
+ }
321
+ function addPluginsBlockAfterManifest(source, calls) {
322
+ const idx = source.indexOf(MANIFEST_LINE);
323
+ if (-1 === idx) return source;
324
+ const insertPos = idx + MANIFEST_LINE.length;
325
+ return `${source.slice(0, insertPos)}\n plugins: [${calls.join(", ")}],${source.slice(insertPos)}`;
326
+ }
327
+ function mergeScaffoldIntoAddfoxConfig(source, styleEngine) {
328
+ const st = getStylePlugin(styleEngine);
329
+ if (!st) return source;
330
+ let out = insertImportAfterLastImport(source, st.importLine);
331
+ out = mergeCallsIntoPluginsArray(out, [
332
+ st.call
333
+ ]);
334
+ return out;
335
+ }
326
336
  const UTF8_BOM = "\uFEFF";
327
337
  function stripUtf8Bom(text) {
328
338
  return text.startsWith(UTF8_BOM) ? text.slice(UTF8_BOM.length) : text;
@@ -941,21 +951,20 @@ async function main() {
941
951
  if (!testSelection) process.exit(0);
942
952
  const pm = options.packageManager;
943
953
  const templateName = getTemplateName(options.framework, options.language);
944
- const useLocalTemplate = hasLocalTemplate(templateName);
945
- const templateLabel = useLocalTemplate ? "Copying local template..." : "Downloading template...";
946
- const LOCAL_TEMPLATE_SPINNER_MIN_MS = 800;
954
+ const templateReady = hasLocalTemplate(templateName);
955
+ const templateLabel = "Copying template...";
956
+ const TEMPLATE_SPINNER_MIN_MS = 800;
947
957
  try {
948
958
  await runWithTemplateSpinner(templateLabel, async ()=>{
949
- await new Promise((resolve)=>{
950
- setImmediate(resolve);
959
+ await new Promise((resolveSpinner)=>{
960
+ setImmediate(resolveSpinner);
951
961
  });
952
- if (await tryLocalTemplates(templateName, root)) return;
953
- await downloadTemplate(templateName, root);
954
- }, useLocalTemplate ? {
955
- minVisibleMs: LOCAL_TEMPLATE_SPINNER_MIN_MS
962
+ await copyBundledTemplate(templateName, root);
963
+ }, templateReady ? {
964
+ minVisibleMs: TEMPLATE_SPINNER_MIN_MS
956
965
  } : void 0);
957
966
  } catch (err) {
958
- console.error(red(`\n Failed to download template: ${err.message}\n`));
967
+ console.error(red(`\n Failed to copy template: ${err.message}\n`));
959
968
  process.exit(1);
960
969
  }
961
970
  const useAllEntries = options.entries.includes("__all__");
@@ -966,7 +975,11 @@ async function main() {
966
975
  if (!useAllEntries && effectiveEntries.length > 0) filterAppEntries(root, effectiveEntries);
967
976
  const configExt = "ts" === options.language ? "ts" : "js";
968
977
  const configPath = external_node_path_resolve(root, `addfox.config.${configExt}`);
969
- const configContent = generateAddfoxConfig(options.framework, options.language, options.styleEngine);
978
+ let configContent;
979
+ if (existsSync(configPath)) {
980
+ const existing = readFileSync(configPath, "utf-8");
981
+ configContent = existing.trim().length > 0 && existing.includes("defineConfig") ? mergeScaffoldIntoAddfoxConfig(existing, options.styleEngine) : generateAddfoxConfig(options.framework, options.language, options.styleEngine);
982
+ } else configContent = generateAddfoxConfig(options.framework, options.language, options.styleEngine);
970
983
  writeFileSync(configPath, configContent, "utf-8");
971
984
  updatePackageName(root, targetDir);
972
985
  applyStyleEngine(root, options.framework, options.language, options.styleEngine);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-addfox-app",
3
- "version": "0.1.1-beta.10",
3
+ "version": "0.1.1-beta.12",
4
4
  "description": "Interactive scaffolder for addfox extension projects",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -9,6 +9,7 @@
9
9
  },
10
10
  "files": [
11
11
  "dist",
12
+ "templates",
12
13
  "LICENSE"
13
14
  ],
14
15
  "dependencies": {
@@ -0,0 +1,3 @@
1
+ # Scaffold templates
2
+
3
+ Templates used by **create-addfox-app**. Each subdirectory (e.g. `template-vanilla-ts`, `template-react-ts`) is a full project skeleton. They live under this package and ship with the published npm tarball; the CLI copies the chosen folder into the new project directory.
@@ -0,0 +1,27 @@
1
+ import { defineConfig } from "addfox";
2
+ import { pluginPreact } from "@rsbuild/plugin-preact";
3
+
4
+ const manifest = {
5
+ name: "My Extension",
6
+ version: "1.0.0",
7
+ manifest_version: 3,
8
+ description: "Browser extension built with addfox",
9
+ permissions: ["activeTab"],
10
+ icons: {
11
+ "16": "icons/icon_128.png",
12
+ "48": "icons/icon_128.png",
13
+ "128": "icons/icon_128.png",
14
+ },
15
+ action: {
16
+ default_icon: {
17
+ "16": "icons/icon_128.png",
18
+ "48": "icons/icon_128.png",
19
+ "128": "icons/icon_128.png",
20
+ },
21
+ },
22
+ };
23
+
24
+ export default defineConfig({
25
+ manifest: { chromium: manifest, firefox: { ...manifest } },
26
+ plugins: [pluginPreact()],
27
+ });
@@ -0,0 +1,8 @@
1
+ function openWelcomePage() {
2
+ chrome.tabs.create({ url: chrome.runtime.getURL("welcome.html") });
3
+ }
4
+
5
+ chrome.runtime.onInstalled.addListener((details) => {
6
+ if (details.reason === "install" || details.reason === "update") openWelcomePage();
7
+ console.log("Extension installed");
8
+ });
@@ -0,0 +1 @@
1
+ console.log("Content script loaded");
@@ -0,0 +1,5 @@
1
+ import { h } from "preact";
2
+
3
+ export default function App() {
4
+ return <div>Hello from Preact</div>;
5
+ }
@@ -0,0 +1,5 @@
1
+ import { render, h } from "preact";
2
+ import App from "./App";
3
+
4
+ const root = document.getElementById("root");
5
+ if (root) render(h(App, null), root);
@@ -0,0 +1,23 @@
1
+ import { h } from "preact";
2
+
3
+ export default function App() {
4
+ const popupStyle = {
5
+ width: 320,
6
+ minHeight: 200,
7
+ padding: 16,
8
+ boxSizing: "border-box",
9
+ display: "flex",
10
+ flexDirection: "column",
11
+ alignItems: "center",
12
+ justifyContent: "center",
13
+ gap: 12,
14
+ fontFamily: 'system-ui, -apple-system, "Segoe UI", sans-serif',
15
+ };
16
+
17
+ return (
18
+ <div style={popupStyle}>
19
+ <img src="/icons/icon_128.png" alt="" width={64} height={64} />
20
+ <div style={{ fontSize: 18, fontWeight: 600 }}>AddFox Preact</div>
21
+ </div>
22
+ );
23
+ }
@@ -0,0 +1,5 @@
1
+ import { render, h } from "preact";
2
+ import App from "./App";
3
+
4
+ const root = document.getElementById("root");
5
+ if (root) render(h(App, null), root);
@@ -0,0 +1,5 @@
1
+ import { h } from "preact";
2
+
3
+ export default function App() {
4
+ return <div>Hello from Preact</div>;
5
+ }
@@ -0,0 +1,5 @@
1
+ import { render, h } from "preact";
2
+ import App from "./App";
3
+
4
+ const root = document.getElementById("root");
5
+ if (root) render(h(App, null), root);
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "my-extension",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "addfox dev",
8
+ "build": "addfox build"
9
+ },
10
+ "dependencies": {
11
+ "addfox": "^0.1.1-beta.7",
12
+ "@rsbuild/core": "^1.7.3",
13
+ "@addfox/utils": "^0.1.1-beta.5",
14
+ "preact": "^10.29.0"
15
+ },
16
+ "devDependencies": {
17
+ "@rsbuild/plugin-preact": "^1.7.2"
18
+ }
19
+ }
@@ -0,0 +1,19 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Welcome - Addfox + Preact</title>
7
+ <style>
8
+ body { font-family: system-ui, sans-serif; display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 100vh; margin: 0; background: #fafafa; color: #1a1a1a; }
9
+ h1 { font-size: 1.75rem; font-weight: 700; margin: 0 0 0.5rem; }
10
+ p { font-size: 1rem; color: #666; margin: 0; }
11
+ .welcome-icon { width: 96px; height: 96px; margin-bottom: 1rem; object-fit: contain; border-radius: 12px; }
12
+ </style>
13
+ </head>
14
+ <body>
15
+ <img class="welcome-icon" src="/icons/icon_128.png" alt="" width="96" height="96" />
16
+ <h1>Addfox + Preact</h1>
17
+ <p>Your extension is ready. Happy building!</p>
18
+ </body>
19
+ </html>
@@ -0,0 +1,27 @@
1
+ import { defineConfig } from "addfox";
2
+ import { pluginPreact } from "@rsbuild/plugin-preact";
3
+
4
+ const manifest = {
5
+ name: "My Extension",
6
+ version: "1.0.0",
7
+ manifest_version: 3,
8
+ description: "Browser extension built with addfox",
9
+ permissions: ["activeTab"],
10
+ icons: {
11
+ "16": "icons/icon_128.png",
12
+ "48": "icons/icon_128.png",
13
+ "128": "icons/icon_128.png",
14
+ },
15
+ action: {
16
+ default_icon: {
17
+ "16": "icons/icon_128.png",
18
+ "48": "icons/icon_128.png",
19
+ "128": "icons/icon_128.png",
20
+ },
21
+ },
22
+ };
23
+
24
+ export default defineConfig({
25
+ manifest: { chromium: manifest, firefox: { ...manifest } },
26
+ plugins: [pluginPreact()],
27
+ });
@@ -0,0 +1,8 @@
1
+ function openWelcomePage() {
2
+ chrome.tabs.create({ url: chrome.runtime.getURL("welcome.html") });
3
+ }
4
+
5
+ chrome.runtime.onInstalled.addListener((details) => {
6
+ if (details.reason === "install" || details.reason === "update") openWelcomePage();
7
+ console.log("Extension installed");
8
+ });
@@ -0,0 +1 @@
1
+ console.log("Content script loaded");
@@ -0,0 +1,5 @@
1
+ import { h } from "preact";
2
+
3
+ export default function App() {
4
+ return <div>Hello from Preact</div>;
5
+ }
@@ -0,0 +1,5 @@
1
+ import { createRoot } from "react-dom/client";
2
+ import App from "./App";
3
+
4
+ const root = document.getElementById("root");
5
+ if (root) createRoot(root).render(<App />);
@@ -0,0 +1,23 @@
1
+ import { h } from "preact";
2
+
3
+ export default function App() {
4
+ const popupStyle = {
5
+ width: 320,
6
+ minHeight: 200,
7
+ padding: 16,
8
+ boxSizing: "border-box" as const,
9
+ display: "flex",
10
+ flexDirection: "column" as const,
11
+ alignItems: "center",
12
+ justifyContent: "center",
13
+ gap: 12,
14
+ fontFamily: 'system-ui, -apple-system, "Segoe UI", sans-serif',
15
+ };
16
+
17
+ return (
18
+ <div style={popupStyle}>
19
+ <img src="/icons/icon_128.png" alt="" width={64} height={64} />
20
+ <div style={{ fontSize: 18, fontWeight: 600 }}>AddFox Preact</div>
21
+ </div>
22
+ );
23
+ }
@@ -0,0 +1,5 @@
1
+ import { createRoot } from "react-dom/client";
2
+ import App from "./App";
3
+
4
+ const root = document.getElementById("root");
5
+ if (root) createRoot(root).render(<App />);
@@ -0,0 +1,5 @@
1
+ import { h } from "preact";
2
+
3
+ export default function App() {
4
+ return <div>Hello from Preact</div>;
5
+ }
@@ -0,0 +1,5 @@
1
+ import { createRoot } from "react-dom/client";
2
+ import App from "./App";
3
+
4
+ const root = document.getElementById("root");
5
+ if (root) createRoot(root).render(<App />);
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "my-extension",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "addfox dev",
8
+ "build": "addfox build"
9
+ },
10
+ "dependencies": {
11
+ "addfox": "^0.1.1-beta.7",
12
+ "@rsbuild/core": "^1.7.3",
13
+ "@addfox/utils": "^0.1.1-beta.5",
14
+ "preact": "^10.29.0"
15
+ },
16
+ "devDependencies": {
17
+ "@rsbuild/plugin-preact": "^1.7.2",
18
+ "typescript": "^5.9.3",
19
+ "@types/chrome": "^0.1.38"
20
+ }
21
+ }
@@ -0,0 +1,19 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Welcome - Addfox + Preact</title>
7
+ <style>
8
+ body { font-family: system-ui, sans-serif; display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 100vh; margin: 0; background: #fafafa; color: #1a1a1a; }
9
+ h1 { font-size: 1.75rem; font-weight: 700; margin: 0 0 0.5rem; }
10
+ p { font-size: 1rem; color: #666; margin: 0; }
11
+ .welcome-icon { width: 96px; height: 96px; margin-bottom: 1rem; object-fit: contain; border-radius: 12px; }
12
+ </style>
13
+ </head>
14
+ <body>
15
+ <img class="welcome-icon" src="/icons/icon_128.png" alt="" width="96" height="96" />
16
+ <h1>Addfox + Preact</h1>
17
+ <p>Your extension is ready. Happy building!</p>
18
+ </body>
19
+ </html>
@@ -0,0 +1,19 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "jsx": "react-jsx",
8
+ "resolveJsonModule": true,
9
+ "isolatedModules": true,
10
+ "noEmit": true,
11
+ "skipLibCheck": true,
12
+ "types": [
13
+ "chrome"
14
+ ]
15
+ },
16
+ "include": [
17
+ "app"
18
+ ]
19
+ }
@@ -0,0 +1,27 @@
1
+ import { defineConfig } from "addfox";
2
+ import { pluginReact } from "@rsbuild/plugin-react";
3
+
4
+ const manifest = {
5
+ name: "My Extension",
6
+ version: "1.0.0",
7
+ manifest_version: 3,
8
+ description: "Browser extension built with addfox",
9
+ permissions: ["activeTab"],
10
+ icons: {
11
+ "16": "icons/icon_128.png",
12
+ "48": "icons/icon_128.png",
13
+ "128": "icons/icon_128.png",
14
+ },
15
+ action: {
16
+ default_icon: {
17
+ "16": "icons/icon_128.png",
18
+ "48": "icons/icon_128.png",
19
+ "128": "icons/icon_128.png",
20
+ },
21
+ },
22
+ };
23
+
24
+ export default defineConfig({
25
+ manifest: { chromium: manifest, firefox: { ...manifest } },
26
+ plugins: [pluginReact()],
27
+ });
@@ -0,0 +1,8 @@
1
+ function openWelcomePage() {
2
+ chrome.tabs.create({ url: chrome.runtime.getURL("welcome.html") });
3
+ }
4
+
5
+ chrome.runtime.onInstalled.addListener((details) => {
6
+ if (details.reason === "install" || details.reason === "update") openWelcomePage();
7
+ console.log("Extension installed");
8
+ });
@@ -0,0 +1 @@
1
+ console.log("Content script loaded");
@@ -0,0 +1,3 @@
1
+ export default function App() {
2
+ return <div>Hello from React</div>;
3
+ }
@@ -0,0 +1,5 @@
1
+ import { createRoot } from "react-dom/client";
2
+ import App from "./App";
3
+
4
+ const root = document.getElementById("root");
5
+ if (root) createRoot(root).render(<App />);
@@ -0,0 +1,21 @@
1
+ export default function App() {
2
+ const popupStyle = {
3
+ width: 320,
4
+ minHeight: 200,
5
+ padding: 16,
6
+ boxSizing: "border-box",
7
+ display: "flex",
8
+ flexDirection: "column",
9
+ alignItems: "center",
10
+ justifyContent: "center",
11
+ gap: 12,
12
+ fontFamily: 'system-ui, -apple-system, "Segoe UI", sans-serif',
13
+ };
14
+
15
+ return (
16
+ <div style={popupStyle}>
17
+ <img src="/icons/icon_128.png" alt="" width={64} height={64} />
18
+ <div style={{ fontSize: 18, fontWeight: 600 }}>AddFox React</div>
19
+ </div>
20
+ );
21
+ }