@stacksjs/buddy 0.70.258 → 0.70.260

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 (112) hide show
  1. package/dist/cli.d.ts +11 -1
  2. package/dist/cli.js +5 -164
  3. package/dist/commands/about.js +1 -35
  4. package/dist/commands/add.js +1 -25
  5. package/dist/commands/ai-context.js +4 -27
  6. package/dist/commands/auth.js +1 -68
  7. package/dist/commands/build.js +1 -221
  8. package/dist/commands/cd.js +8 -39
  9. package/dist/commands/changelog.js +1 -32
  10. package/dist/commands/clean.js +1 -44
  11. package/dist/commands/cloud.js +2 -519
  12. package/dist/commands/commit.js +1 -16
  13. package/dist/commands/completion.js +6 -42
  14. package/dist/commands/config-migrate.js +6 -94
  15. package/dist/commands/configure.js +2 -45
  16. package/dist/commands/create.js +1 -162
  17. package/dist/commands/deploy.js +13 -1676
  18. package/dist/commands/desktop-apple.js +12 -399
  19. package/dist/commands/dev.js +6 -1083
  20. package/dist/commands/dns.js +3 -103
  21. package/dist/commands/docs/buddy-commands.js +10 -132
  22. package/dist/commands/docs/buddy-commands.test.js +1 -38
  23. package/dist/commands/docs/framework-repo.js +1 -14
  24. package/dist/commands/docs/framework-repo.test.js +1 -68
  25. package/dist/commands/docs/generated-artifacts.js +3 -70
  26. package/dist/commands/docs/generated-artifacts.test.js +1 -19
  27. package/dist/commands/docs/links.js +2 -85
  28. package/dist/commands/docs/links.test.js +4 -66
  29. package/dist/commands/docs.js +1 -24
  30. package/dist/commands/doctor.js +2 -478
  31. package/dist/commands/domains.js +2 -152
  32. package/dist/commands/email.js +31 -503
  33. package/dist/commands/env.js +1 -312
  34. package/dist/commands/extension.js +1 -239
  35. package/dist/commands/features.js +9 -345
  36. package/dist/commands/fresh.js +1 -46
  37. package/dist/commands/generate.js +2 -152
  38. package/dist/commands/http.js +1 -33
  39. package/dist/commands/index.js +1 -57
  40. package/dist/commands/install.js +1 -22
  41. package/dist/commands/key.js +1 -23
  42. package/dist/commands/link.js +2 -147
  43. package/dist/commands/lint.js +2 -84
  44. package/dist/commands/list.js +1 -140
  45. package/dist/commands/mail.js +15 -1477
  46. package/dist/commands/maintenance.js +1 -141
  47. package/dist/commands/make.js +1 -392
  48. package/dist/commands/migrate-project.js +1 -49
  49. package/dist/commands/migrate.js +27 -625
  50. package/dist/commands/outdated.js +1 -19
  51. package/dist/commands/package.js +1 -17
  52. package/dist/commands/phone.js +17 -181
  53. package/dist/commands/ports.js +2 -118
  54. package/dist/commands/prepublish.js +1 -17
  55. package/dist/commands/projects.js +1 -29
  56. package/dist/commands/protocol/craft-evidence.js +4 -98
  57. package/dist/commands/protocol/craft-evidence.test.js +1 -20
  58. package/dist/commands/protocol/desktop-lifecycle-report.js +3 -57
  59. package/dist/commands/protocol/desktop-lifecycle-report.test.js +1 -32
  60. package/dist/commands/protocol/desktop-support.js +2 -32
  61. package/dist/commands/protocol/desktop-workflow.test.js +3 -35
  62. package/dist/commands/protocol/driver-registry.js +2 -71
  63. package/dist/commands/protocol/pantry-evidence.js +4 -144
  64. package/dist/commands/protocol/pantry-evidence.test.js +1 -29
  65. package/dist/commands/protocol/release-manifest.js +3 -102
  66. package/dist/commands/protocol/release-manifest.test.js +2 -34
  67. package/dist/commands/protocol/run-conformance.js +4 -346
  68. package/dist/commands/protocol/run-conformance.test.js +1 -52
  69. package/dist/commands/protocol/run-driver-contracts.js +4 -83
  70. package/dist/commands/protocol/run-driver-contracts.test.js +1 -18
  71. package/dist/commands/protocol/source-manifest.js +3 -113
  72. package/dist/commands/protocol/sync-suite.js +3 -136
  73. package/dist/commands/protocol/sync-suite.test.js +1 -44
  74. package/dist/commands/protocol.js +1 -60
  75. package/dist/commands/publish.js +8 -500
  76. package/dist/commands/queue.js +1 -248
  77. package/dist/commands/release.js +1 -42
  78. package/dist/commands/route.js +1 -21
  79. package/dist/commands/run-tool.js +1 -9
  80. package/dist/commands/saas.js +1 -25
  81. package/dist/commands/schedule.js +1 -61
  82. package/dist/commands/search.js +1 -84
  83. package/dist/commands/seed.js +1 -47
  84. package/dist/commands/serve.js +1 -55
  85. package/dist/commands/setup-ai.js +2 -121
  86. package/dist/commands/setup.js +2 -305
  87. package/dist/commands/share.js +1 -214
  88. package/dist/commands/sms.js +32 -319
  89. package/dist/commands/stacks.js +1 -72
  90. package/dist/commands/telemetry.js +1 -74
  91. package/dist/commands/test.js +1 -130
  92. package/dist/commands/tinker.js +1 -37
  93. package/dist/commands/types.js +1 -18
  94. package/dist/commands/upgrade.js +1 -98
  95. package/dist/commands/user.js +3 -84
  96. package/dist/commands/version.js +1 -14
  97. package/dist/config.js +1 -231
  98. package/dist/custom-cli.js +1 -23
  99. package/dist/database-preflight.js +1 -88
  100. package/dist/global-options.js +1 -4
  101. package/dist/index.js +1 -1
  102. package/dist/lazy-commands.js +1 -241
  103. package/dist/migrators/index.js +2 -62
  104. package/dist/migrators/laravel/index.js +1 -148
  105. package/dist/migrators/laravel/migrations.js +4 -231
  106. package/dist/migrators/laravel/models.js +8 -111
  107. package/dist/migrators/rails/index.js +1 -11
  108. package/dist/production-server.js +1 -257
  109. package/dist/project-setup.js +1 -36
  110. package/dist/serve-entry.js +1 -5
  111. package/dist/version-info.js +1 -5
  112. package/package.json +53 -46
@@ -1,33 +1,4 @@
1
- import process from "node:process";
2
- import { log, onUnknownSubcommand } from "@stacksjs/cli";
3
- export function completion(buddy) {
4
- const descriptions = {
5
- completion: "Generate shell completion scripts",
6
- shell: "Shell type (bash, zsh, fish)"
7
- };
8
- buddy.command("completion [shell]", descriptions.completion).option("-s, --shell [shell]", descriptions.shell).example("buddy completion bash").example("buddy completion zsh").example("buddy completion fish").example("buddy completion bash > /usr/local/etc/bash_completion.d/buddy").action(async (shell, options) => {
9
- log.debug("Running `buddy completion` ...", options);
10
- const targetShell = shell || options.shell || "bash", commandNames = (buddy.commands || []).map((cmd) => cmd.name).filter((name) => Boolean(name));
11
- switch (targetShell) {
12
- case "bash":
13
- console.log(generateBashCompletion(commandNames));
14
- break;
15
- case "zsh":
16
- console.log(generateZshCompletion(commandNames));
17
- break;
18
- case "fish":
19
- console.log(generateFishCompletion(commandNames));
20
- break;
21
- default:
22
- console.error(`Unknown shell: ${targetShell}`);
23
- console.error("Supported shells: bash, zsh, fish");
24
- process.exit(1);
25
- }
26
- });
27
- onUnknownSubcommand(buddy, "completion");
28
- }
29
- function generateBashCompletion(commands) {
30
- return `# buddy completion for bash
1
+ import process from"node:process";import{log,onUnknownSubcommand}from"@stacksjs/cli";export function completion(buddy){const descriptions={completion:"Generate shell completion scripts",shell:"Shell type (bash, zsh, fish)"};buddy.command("completion [shell]",descriptions.completion).option("-s, --shell [shell]",descriptions.shell).example("buddy completion bash").example("buddy completion zsh").example("buddy completion fish").example("buddy completion bash > /usr/local/etc/bash_completion.d/buddy").action(async(shell,options)=>{log.debug("Running `buddy completion` ...",options);const targetShell=shell||options.shell||"bash",commandNames=(buddy.commands||[]).map((cmd)=>cmd.name).filter((name)=>Boolean(name));switch(targetShell){case"bash":console.log(generateBashCompletion(commandNames));break;case"zsh":console.log(generateZshCompletion(commandNames));break;case"fish":console.log(generateFishCompletion(commandNames));break;default:console.error(`Unknown shell: ${targetShell}`);console.error("Supported shells: bash, zsh, fish");process.exit(1)}});onUnknownSubcommand(buddy,"completion")}function generateBashCompletion(commands){return`# buddy completion for bash
31
2
 
32
3
  _buddy_completion() {
33
4
  local cur prev commands
@@ -60,17 +31,14 @@ complete -F _buddy_completion buddy
60
31
  # - macOS: /usr/local/etc/bash_completion.d/buddy
61
32
  #
62
33
  # Then reload your shell or run: source ~/.bashrc
63
- `;
64
- }
65
- function generateZshCompletion(commands) {
66
- return `#compdef buddy
34
+ `}function generateZshCompletion(commands){return`#compdef buddy
67
35
 
68
36
  # buddy completion for zsh
69
37
 
70
38
  _buddy() {
71
39
  local -a commands
72
40
  commands=(
73
- ${commands.map((cmd) => ` '${cmd}:${cmd} command'`).join(`
41
+ ${commands.map((cmd)=>` '${cmd}:${cmd} command'`).join(`
74
42
  `)}
75
43
  )
76
44
 
@@ -111,16 +79,13 @@ _buddy "$@"
111
79
  # Then add to your ~/.zshrc:
112
80
  # fpath=(~/.zsh/completions $fpath)
113
81
  # autoload -Uz compinit && compinit
114
- `;
115
- }
116
- function generateFishCompletion(commands) {
117
- return `# buddy completion for fish
82
+ `}function generateFishCompletion(commands){return`# buddy completion for fish
118
83
 
119
84
  # Remove old completions
120
85
  complete -c buddy -e
121
86
 
122
87
  # Command completions
123
- ${commands.map((cmd) => `complete -c buddy -n "__fish_use_subcommand" -a "${cmd}" -d "${cmd} command"`).join(`
88
+ ${commands.map((cmd)=>`complete -c buddy -n "__fish_use_subcommand" -a "${cmd}" -d "${cmd} command"`).join(`
124
89
  `)}
125
90
 
126
91
  # Global flag completions
@@ -139,5 +104,4 @@ complete -c buddy -s f -l force -d "Skip confirmation prompts"
139
104
  # ~/.config/fish/completions/buddy.fish
140
105
  #
141
106
  # Fish will automatically load it on startup
142
- `;
143
- }
107
+ `}
@@ -1,94 +1,6 @@
1
- import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
- import process from "node:process";
3
- import { italic, log } from "@stacksjs/cli";
4
- import { projectPath } from "@stacksjs/path";
5
- const CODEMODS = [
6
- {
7
- id: "0.65-cache-driver-rename",
8
- description: "Rename `cache.driver` to `cache.default` (introduced in 0.65).",
9
- files: ["cache.ts"],
10
- shouldApply: (c) => /\bdriver\s*:/.test(c) && !c.includes("default:"),
11
- apply: (c) => c.replace(/(\bcache\b[\s\S]*?)\bdriver(\s*:)/m, "$1default$2")
12
- },
13
- {
14
- id: "0.68-search-engine-name",
15
- description: "Rename `searchEngine.driver` to `searchEngine.default` (matches other configs).",
16
- files: ["search-engine.ts"],
17
- shouldApply: (c) => /\bdriver\s*:/.test(c),
18
- apply: (c) => c.replace(/\bdriver(\s*:)/g, "default$1")
19
- },
20
- {
21
- id: "0.70-ports-string-to-number",
22
- description: 'Convert string port values like `"3000"` to numbers (TypeScript types now require number).',
23
- files: ["ports.ts"],
24
- shouldApply: (c) => /:\s*['"]\d+['"]/.test(c),
25
- apply: (c) => c.replace(/:\s*['"](\d+)['"]/g, ": $1")
26
- }
27
- ];
28
- export async function runConfigMigrations(opts) {
29
- const changed = [], skipped = [], codemods = opts.only ? CODEMODS.filter((m) => m.id === opts.only) : CODEMODS;
30
- if (codemods.length === 0) {
31
- log.warn(`No matching codemods. Available: ${CODEMODS.map((m) => m.id).join(", ")}`);
32
- return { changed, skipped };
33
- }
34
- for (const cm of codemods) {
35
- log.info(`Codemod ${italic(cm.id)} \u2014 ${cm.description}`);
36
- for (const rel of cm.files) {
37
- const full = projectPath(`config/${rel}`);
38
- if (!existsSync(full)) {
39
- skipped.push(`${full} (missing)`);
40
- continue;
41
- }
42
- const before = readFileSync(full, "utf-8");
43
- if (!cm.shouldApply(before)) {
44
- skipped.push(`${full} (no changes)`);
45
- continue;
46
- }
47
- const after = cm.apply(before);
48
- if (after === before) {
49
- skipped.push(`${full} (no changes)`);
50
- continue;
51
- }
52
- if (opts.dryRun) {
53
- log.info(`[dry-run] would update ${full}`);
54
- printDiff(before, after);
55
- } else {
56
- writeFileSync(full, after);
57
- log.success(`updated ${full}`);
58
- }
59
- changed.push(full);
60
- }
61
- }
62
- return { changed, skipped };
63
- }
64
- function printDiff(before, after) {
65
- const beforeLines = before.split(`
66
- `), afterLines = after.split(`
67
- `), max = Math.max(beforeLines.length, afterLines.length);
68
- for (let i = 0;i < max; i++) {
69
- const b = beforeLines[i], a = afterLines[i];
70
- if (b === a)
71
- continue;
72
- if (b !== void 0)
73
- process.stdout.write(` - ${b}
74
- `);
75
- if (a !== void 0)
76
- process.stdout.write(` + ${a}
77
- `);
78
- }
79
- process.stdout.write(`
80
- `);
81
- }
82
- export function configMigrate(buddy) {
83
- buddy.command("config:migrate", "Apply config-shape codemods for the latest Stacks version").option("--dry-run", "Preview changes without writing", { default: !1 }).option("--only [id]", "Run a single codemod by id", { default: "" }).action(async (options) => {
84
- log.info("Running `buddy config:migrate` ...");
85
- const { changed, skipped } = await runConfigMigrations({
86
- dryRun: Boolean(options.dryRun),
87
- only: options.only?.length ? options.only : void 0
88
- });
89
- log.info(`Changed: ${changed.length}, Skipped: ${skipped.length}`);
90
- if (skipped.length > 0)
91
- for (const s of skipped)
92
- log.debug(` skipped: ${s}`);
93
- });
94
- }
1
+ import{existsSync,readFileSync,writeFileSync}from"node:fs";import process from"node:process";import{italic,log}from"@stacksjs/cli";import{projectPath}from"@stacksjs/path";const CODEMODS=[{id:"0.65-cache-driver-rename",description:"Rename `cache.driver` to `cache.default` (introduced in 0.65).",files:["cache.ts"],shouldApply:(c)=>/\bdriver\s*:/.test(c)&&!c.includes("default:"),apply:(c)=>c.replace(/(\bcache\b[\s\S]*?)\bdriver(\s*:)/m,"$1default$2")},{id:"0.68-search-engine-name",description:"Rename `searchEngine.driver` to `searchEngine.default` (matches other configs).",files:["search-engine.ts"],shouldApply:(c)=>/\bdriver\s*:/.test(c),apply:(c)=>c.replace(/\bdriver(\s*:)/g,"default$1")},{id:"0.70-ports-string-to-number",description:'Convert string port values like `"3000"` to numbers (TypeScript types now require number).',files:["ports.ts"],shouldApply:(c)=>/:\s*['"]\d+['"]/.test(c),apply:(c)=>c.replace(/:\s*['"](\d+)['"]/g,": $1")}];export async function runConfigMigrations(opts){const changed=[],skipped=[],codemods=opts.only?CODEMODS.filter((m)=>m.id===opts.only):CODEMODS;if(codemods.length===0){log.warn(`No matching codemods. Available: ${CODEMODS.map((m)=>m.id).join(", ")}`);return{changed,skipped}}for(const cm of codemods){log.info(`Codemod ${italic(cm.id)} \u2014 ${cm.description}`);for(const rel of cm.files){const full=projectPath(`config/${rel}`);if(!existsSync(full)){skipped.push(`${full} (missing)`);continue}const before=readFileSync(full,"utf-8");if(!cm.shouldApply(before)){skipped.push(`${full} (no changes)`);continue}const after=cm.apply(before);if(after===before){skipped.push(`${full} (no changes)`);continue}if(opts.dryRun){log.info(`[dry-run] would update ${full}`);printDiff(before,after)}else{writeFileSync(full,after);log.success(`updated ${full}`)}changed.push(full)}}return{changed,skipped}}function printDiff(before,after){const beforeLines=before.split(`
2
+ `),afterLines=after.split(`
3
+ `),max=Math.max(beforeLines.length,afterLines.length);for(let i=0;i<max;i++){const b=beforeLines[i],a=afterLines[i];if(b===a)continue;if(b!==void 0)process.stdout.write(` - ${b}
4
+ `);if(a!==void 0)process.stdout.write(` + ${a}
5
+ `)}process.stdout.write(`
6
+ `)}export function configMigrate(buddy){buddy.command("config:migrate","Apply config-shape codemods for the latest Stacks version").option("--dry-run","Preview changes without writing",{default:!1}).option("--only [id]","Run a single codemod by id",{default:""}).action(async(options)=>{log.info("Running `buddy config:migrate` ...");const{changed,skipped}=await runConfigMigrations({dryRun:Boolean(options.dryRun),only:options.only?.length?options.only:void 0});log.info(`Changed: ${changed.length}, Skipped: ${skipped.length}`);if(skipped.length>0)for(const s of skipped)log.debug(` skipped: ${s}`)})}
@@ -1,48 +1,5 @@
1
- import process from "node:process";
2
- import { log, onUnknownSubcommand, outro, runCommand } from "@stacksjs/cli";
3
- import { path as p } from "@stacksjs/path";
4
- import { ExitCode } from "@stacksjs/types";
5
- export function configure(buddy) {
6
- const descriptions = {
7
- configure: "Configure options",
8
- aws: "Configure the AWS connection",
9
- project: "Target a specific project",
10
- profile: "The AWS profile to use",
11
- verbose: "Enable verbose output"
12
- };
13
- buddy.command("configure", descriptions.configure).option("--aws", descriptions.aws, { default: !1 }).option("-p, --project [project]", descriptions.project, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
14
- log.debug("Running `buddy configure` ...", options);
15
- if (options?.aws) {
16
- await configureAws(options);
17
- process.exit(ExitCode.Success);
18
- }
19
- log.info("Not implemented yet. Please use the --aws flag to configure AWS.");
20
- process.exit(ExitCode.Success);
21
- });
22
- buddy.command("configure:aws", descriptions.aws).option("-p, --project [project]", descriptions.project, { default: !1 }).option("--profile", descriptions.profile, {
23
- default: process.env.AWS_PROFILE
24
- }).option("--verbose", descriptions.verbose, { default: !1 }).option("--access-key-id", "The AWS access key").option("--secret-access-key", "The AWS secret access key").option("--region", "The AWS region").option("--output", "The AWS output format").option("--quiet", "Suppress output").action(async (options) => {
25
- log.debug("Running `buddy configure:aws` ...", options);
26
- await configureAws(options);
27
- process.exit(ExitCode.Success);
28
- });
29
- onUnknownSubcommand(buddy, "configure");
30
- }
31
- async function configureAws(options) {
32
- const startTime = performance.now(), awsAccessKeyId = options?.accessKeyId ?? process.env.AWS_ACCESS_KEY_ID, awsSecretAccessKey = options?.secretAccessKey ?? process.env.AWS_SECRET_ACCESS_KEY, defaultRegion = "us-east-1", defaultOutputFormat = options?.output ?? "json", profile = process.env.AWS_PROFILE ?? options?.profile, command = profile ? `aws configure --profile ${profile}` : "aws configure", input = `${awsAccessKeyId}
1
+ import process from"node:process";import{log,onUnknownSubcommand,outro,runCommand}from"@stacksjs/cli";import{path as p}from"@stacksjs/path";import{ExitCode}from"@stacksjs/types";export function configure(buddy){const descriptions={configure:"Configure options",aws:"Configure the AWS connection",project:"Target a specific project",profile:"The AWS profile to use",verbose:"Enable verbose output"};buddy.command("configure",descriptions.configure).option("--aws",descriptions.aws,{default:!1}).option("-p, --project [project]",descriptions.project,{default:!1}).option("--verbose",descriptions.verbose,{default:!1}).action(async(options)=>{log.debug("Running `buddy configure` ...",options);if(options?.aws){await configureAws(options);process.exit(ExitCode.Success)}log.info("Not implemented yet. Please use the --aws flag to configure AWS.");process.exit(ExitCode.Success)});buddy.command("configure:aws",descriptions.aws).option("-p, --project [project]",descriptions.project,{default:!1}).option("--profile",descriptions.profile,{default:process.env.AWS_PROFILE}).option("--verbose",descriptions.verbose,{default:!1}).option("--access-key-id","The AWS access key").option("--secret-access-key","The AWS secret access key").option("--region","The AWS region").option("--output","The AWS output format").option("--quiet","Suppress output").action(async(options)=>{log.debug("Running `buddy configure:aws` ...",options);await configureAws(options);process.exit(ExitCode.Success)});onUnknownSubcommand(buddy,"configure")}async function configureAws(options){const startTime=performance.now(),awsAccessKeyId=options?.accessKeyId??process.env.AWS_ACCESS_KEY_ID,awsSecretAccessKey=options?.secretAccessKey??process.env.AWS_SECRET_ACCESS_KEY,defaultRegion="us-east-1",defaultOutputFormat=options?.output??"json",profile=process.env.AWS_PROFILE??options?.profile,command=profile?`aws configure --profile ${profile}`:"aws configure",input=`${awsAccessKeyId}
33
2
  ${awsSecretAccessKey}
34
3
  ${defaultRegion}
35
4
  ${defaultOutputFormat}
36
- `, result = await runCommand(command, {
37
- cwd: p.projectPath(),
38
- stdin: "pipe",
39
- input
40
- });
41
- if (result.isErr) {
42
- await outro("While running the cloud command, there was an issue", { startTime, useSeconds: !0 }, result.error);
43
- process.exit(ExitCode.FatalError);
44
- }
45
- if (options?.quiet)
46
- return;
47
- await outro("Exited", { startTime, useSeconds: !0 });
48
- }
5
+ `,result=await runCommand(command,{cwd:p.projectPath(),stdin:"pipe",input});if(result.isErr){await outro("While running the cloud command, there was an issue",{startTime,useSeconds:!0},result.error);process.exit(ExitCode.FatalError)}if(options?.quiet)return;await outro("Exited",{startTime,useSeconds:!0})}
@@ -1,162 +1 @@
1
- import { chmodSync, existsSync, readdirSync } from "node:fs";
2
- import process from "node:process";
3
- import { runAction } from "@stacksjs/actions";
4
- import { bold, cyan, dim, intro, log, onUnknownSubcommand, runCommand } from "@stacksjs/cli";
5
- import { Action } from "@stacksjs/enums";
6
- import { resolve } from "@stacksjs/path";
7
- import { isFolder } from "@stacksjs/storage";
8
- import { ExitCode } from "@stacksjs/types";
9
- import { uninstallAllFeatures } from "./features";
10
- import { ensurePantryDependencies, ensurePantryInstalled } from "./setup";
11
- export function create(buddy) {
12
- const descriptions = {
13
- name: "The name of the project",
14
- command: "Create a new Stacks project",
15
- ui: "Are you building a UI?",
16
- components: "Are you building UI components?",
17
- webComponents: "Automagically built optimized custom elements/web components?",
18
- views: "How about views?",
19
- functions: "Are you developing functions/composables?",
20
- api: "Are you building an API?",
21
- database: "Do you need a database?",
22
- notifications: "Do you need notifications? e.g. email, SMS, push or chat notifications",
23
- cache: "Do you need caching?",
24
- email: "Do you need email?",
25
- project: "Target a specific project",
26
- minimal: "Skip optional feature bundles (cms, commerce, dashboard, marketing, monitoring, realtime, queue) \u2014 bare-bones API/SPA starter that can re-add them later via `./buddy <feature>:install`.",
27
- withCore: "Keep the framework vendored in `storage/framework/core` as a Bun workspace, for working ON Stacks. Apps that only work WITH Stacks want the default, which resolves every @stacksjs/* package from npm.",
28
- verbose: "Enable verbose output"
29
- };
30
- buddy.command("new [name]", descriptions.command).alias("create [name]").option("-n, --name [name]", descriptions.name, { default: !1 }).option("-u, --ui", descriptions.ui, { default: !0 }).option("-c, --components", descriptions.components, { default: !0 }).option("-w, --web-components", descriptions.webComponents, { default: !0 }).option("-p, --views", descriptions.views, { default: !0 }).option("-f, --functions", descriptions.functions, { default: !0 }).option("-a, --api", descriptions.api, { default: !0 }).option("-d, --database", descriptions.database, { default: !0 }).option("-ca, --cache", descriptions.cache, { default: !1 }).option("-e, --email", descriptions.email, { default: !1 }).option("-P, --project [project]", descriptions.project, { default: !1 }).option("-m, --minimal", descriptions.minimal, { default: !1 }).option("--with-core", descriptions.withCore, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (name, options) => {
31
- log.debug("Running `buddy new <name>` ...", options);
32
- const startTime = await intro("buddy new");
33
- name = name ?? options.name;
34
- const path = resolve(process.cwd(), name);
35
- isFolderCheck(path);
36
- await onlineCheck();
37
- const result = await download(name, path, options);
38
- if (result.isErr) {
39
- log.error(result.error);
40
- process.exit(ExitCode.FatalError);
41
- }
42
- ensureExecutableScripts(path);
43
- await ensureEnv(path, options);
44
- await install(path, options);
45
- if (!options.withCore)
46
- await unvendorCore(path, options);
47
- if (options.minimal)
48
- await stripFeatures(path);
49
- if (startTime) {
50
- const time = performance.now() - startTime;
51
- log.success(dim(`[${time.toFixed(2)}ms] Completed`));
52
- }
53
- log.info(bold("Welcome to the Stacks Framework! \u269B\uFE0F"));
54
- log.info(`Get started: ${cyan(`cd ${name}`)} and then ${cyan("./buddy dev")}`);
55
- log.info(`Run ${cyan("./buddy doctor")} anytime to check your setup`);
56
- log.info("To learn more, visit https://stacksjs.com");
57
- process.exit(ExitCode.Success);
58
- });
59
- onUnknownSubcommand(buddy, "new");
60
- }
61
- function isFolderCheck(path) {
62
- if (!isFolder(path))
63
- return;
64
- if (readdirSync(path).filter((entry) => entry !== ".git").length === 0)
65
- return;
66
- log.error(`Path ${path} already exists`);
67
- process.exit(ExitCode.FatalError);
68
- }
69
- async function onlineCheck() {
70
- if (await isOnline())
71
- return;
72
- log.info("It appears you are disconnected from the internet.");
73
- log.info("Creating a new project requires a brief internet connection to download the template and install dependencies.");
74
- process.exit(ExitCode.FatalError);
75
- }
76
- async function isOnline() {
77
- try {
78
- return (await fetch("https://github.com", {
79
- method: "HEAD",
80
- signal: AbortSignal.timeout(3000)
81
- })).ok;
82
- } catch {
83
- return !1;
84
- }
85
- }
86
- async function download(name, path, _options) {
87
- log.info("Setting up your stack.");
88
- try {
89
- const { downloadTemplate } = await import("@stacksjs/gitit");
90
- await downloadTemplate("gh:stacksjs/stacks", { dir: name, force: !0 });
91
- log.success(`Successfully scaffolded your project at ${cyan(path)}`);
92
- return { isErr: !1 };
93
- } catch (error) {
94
- return { isErr: !0, error: error instanceof Error ? error.message : String(error) };
95
- }
96
- }
97
- function ensureExecutableScripts(path) {
98
- for (const script of ["buddy", "bootstrap"])
99
- try {
100
- chmodSync(resolve(path, script), 493);
101
- } catch {}
102
- }
103
- async function ensureEnv(path, _options) {
104
- log.info("Ensuring your environment is ready...");
105
- await ensurePantryInstalled();
106
- await ensurePantryDependencies(path);
107
- log.success("Environment is ready");
108
- }
109
- async function install(path, options) {
110
- log.info("Installing & setting up Stacks");
111
- log.info("Copying .env.example \u2192 .env");
112
- let result = await runCommand("cp .env.example .env", { ...options, cwd: path });
113
- if (result?.isErr) {
114
- log.error(result.error);
115
- process.exit(ExitCode.FatalError);
116
- }
117
- log.info("Removing template-encrypted env files...");
118
- const { rm } = await import("node:fs/promises");
119
- for (const stale of [".env.development", ".env.staging", ".env.production", ".env.keys"])
120
- await rm(`${path}/${stale}`, { force: !0 });
121
- log.info("Generating application key...");
122
- const keyResult = await runAction(Action.KeyGenerate, { ...options, cwd: path });
123
- if (keyResult.isErr) {
124
- log.error(keyResult.error);
125
- process.exit(ExitCode.FatalError);
126
- }
127
- if (existsSync(resolve(path, ".git")))
128
- log.info("Existing git repository detected, skipping git init");
129
- else {
130
- log.info("Initializing git repository...");
131
- result = await runCommand("git init", { ...options, cwd: path });
132
- if (result.isErr) {
133
- log.error(result.error);
134
- process.exit(ExitCode.FatalError);
135
- }
136
- }
137
- log.success("Installed & set-up \uD83D\uDE80");
138
- }
139
- async function unvendorCore(path, options) {
140
- log.info("Resolving the framework from npm (pass --with-core to keep it vendored)...");
141
- const result = await runCommand("./buddy unpublish:core --all --force", { ...options, cwd: path });
142
- if (result.isErr) {
143
- log.error(result.error);
144
- process.exit(ExitCode.FatalError);
145
- }
146
- log.success("Framework resolved from npm");
147
- }
148
- async function stripFeatures(path) {
149
- log.info("Stripping optional feature bundles (--minimal)...");
150
- const results = await uninstallAllFeatures({ root: path });
151
- let strippedAny = !1;
152
- for (const { feature, configOutcome, filesRemoved } of results)
153
- if (configOutcome === "flipped" || filesRemoved.length > 0) {
154
- strippedAny = !0;
155
- const fileSummary = filesRemoved.length > 0 ? ` (${filesRemoved.length} path${filesRemoved.length === 1 ? "" : "s"})` : "";
156
- log.info(` - ${feature}${fileSummary}`);
157
- }
158
- if (!strippedAny)
159
- log.info(" \u2192 no feature scaffolding present; nothing to strip.");
160
- else
161
- log.success("Minimal skeleton ready \u2014 run `./buddy <feature>:install` to add features back.");
162
- }
1
+ import{chmodSync,existsSync,readdirSync}from"node:fs";import process from"node:process";import{runAction}from"@stacksjs/actions";import{bold,cyan,dim,intro,log,onUnknownSubcommand,runCommand}from"@stacksjs/cli";import{Action}from"@stacksjs/enums";import{resolve}from"@stacksjs/path";import{isFolder}from"@stacksjs/storage";import{ExitCode}from"@stacksjs/types";import{uninstallAllFeatures}from"./features";import{ensurePantryDependencies,ensurePantryInstalled}from"./setup";export function create(buddy){const descriptions={name:"The name of the project",command:"Create a new Stacks project",ui:"Are you building a UI?",components:"Are you building UI components?",webComponents:"Automagically built optimized custom elements/web components?",views:"How about views?",functions:"Are you developing functions/composables?",api:"Are you building an API?",database:"Do you need a database?",notifications:"Do you need notifications? e.g. email, SMS, push or chat notifications",cache:"Do you need caching?",email:"Do you need email?",project:"Target a specific project",minimal:"Skip optional feature bundles (cms, commerce, dashboard, marketing, monitoring, realtime, queue) \u2014 bare-bones API/SPA starter that can re-add them later via `./buddy <feature>:install`.",withCore:"Keep the framework vendored in `storage/framework/core` as a Bun workspace, for working ON Stacks. Apps that only work WITH Stacks want the default, which resolves every @stacksjs/* package from npm.",verbose:"Enable verbose output"};buddy.command("new [name]",descriptions.command).alias("create [name]").option("-n, --name [name]",descriptions.name,{default:!1}).option("-u, --ui",descriptions.ui,{default:!0}).option("-c, --components",descriptions.components,{default:!0}).option("-w, --web-components",descriptions.webComponents,{default:!0}).option("-p, --views",descriptions.views,{default:!0}).option("-f, --functions",descriptions.functions,{default:!0}).option("-a, --api",descriptions.api,{default:!0}).option("-d, --database",descriptions.database,{default:!0}).option("-ca, --cache",descriptions.cache,{default:!1}).option("-e, --email",descriptions.email,{default:!1}).option("-P, --project [project]",descriptions.project,{default:!1}).option("-m, --minimal",descriptions.minimal,{default:!1}).option("--with-core",descriptions.withCore,{default:!1}).option("--verbose",descriptions.verbose,{default:!1}).action(async(name,options)=>{log.debug("Running `buddy new <name>` ...",options);const startTime=await intro("buddy new");name=name??options.name;const path=resolve(process.cwd(),name);isFolderCheck(path);await onlineCheck();const result=await download(name,path,options);if(result.isErr){log.error(result.error);process.exit(ExitCode.FatalError)}ensureExecutableScripts(path);await ensureEnv(path,options);await install(path,options);if(!options.withCore)await unvendorCore(path,options);if(options.minimal)await stripFeatures(path);if(startTime){const time=performance.now()-startTime;log.success(dim(`[${time.toFixed(2)}ms] Completed`))}log.info(bold("Welcome to the Stacks Framework! \u269B\uFE0F"));log.info(`Get started: ${cyan(`cd ${name}`)} and then ${cyan("./buddy dev")}`);log.info(`Run ${cyan("./buddy doctor")} anytime to check your setup`);log.info("To learn more, visit https://stacksjs.com");process.exit(ExitCode.Success)});onUnknownSubcommand(buddy,"new")}function isFolderCheck(path){if(!isFolder(path))return;if(readdirSync(path).filter((entry)=>entry!==".git").length===0)return;log.error(`Path ${path} already exists`);process.exit(ExitCode.FatalError)}async function onlineCheck(){if(await isOnline())return;log.info("It appears you are disconnected from the internet.");log.info("Creating a new project requires a brief internet connection to download the template and install dependencies.");process.exit(ExitCode.FatalError)}async function isOnline(){try{return(await fetch("https://github.com",{method:"HEAD",signal:AbortSignal.timeout(3000)})).ok}catch{return!1}}async function download(name,path,_options){log.info("Setting up your stack.");try{const{downloadTemplate}=await import("@stacksjs/gitit");await downloadTemplate("gh:stacksjs/stacks",{dir:name,force:!0});log.success(`Successfully scaffolded your project at ${cyan(path)}`);return{isErr:!1}}catch(error){return{isErr:!0,error:error instanceof Error?error.message:String(error)}}}function ensureExecutableScripts(path){for(const script of["buddy","bootstrap"])try{chmodSync(resolve(path,script),493)}catch{}}async function ensureEnv(path,_options){log.info("Ensuring your environment is ready...");await ensurePantryInstalled();await ensurePantryDependencies(path);log.success("Environment is ready")}async function install(path,options){log.info("Installing & setting up Stacks");log.info("Copying .env.example \u2192 .env");let result=await runCommand("cp .env.example .env",{...options,cwd:path});if(result?.isErr){log.error(result.error);process.exit(ExitCode.FatalError)}log.info("Removing template-encrypted env files...");const{rm}=await import("node:fs/promises");for(const stale of[".env.development",".env.staging",".env.production",".env.keys"])await rm(`${path}/${stale}`,{force:!0});log.info("Generating application key...");const keyResult=await runAction(Action.KeyGenerate,{...options,cwd:path});if(keyResult.isErr){log.error(keyResult.error);process.exit(ExitCode.FatalError)}if(existsSync(resolve(path,".git")))log.info("Existing git repository detected, skipping git init");else{log.info("Initializing git repository...");result=await runCommand("git init",{...options,cwd:path});if(result.isErr){log.error(result.error);process.exit(ExitCode.FatalError)}}log.success("Installed & set-up \uD83D\uDE80")}async function unvendorCore(path,options){log.info("Resolving the framework from npm (pass --with-core to keep it vendored)...");const result=await runCommand("./buddy unpublish:core --all --force",{...options,cwd:path});if(result.isErr){log.error(result.error);process.exit(ExitCode.FatalError)}log.success("Framework resolved from npm")}async function stripFeatures(path){log.info("Stripping optional feature bundles (--minimal)...");const results=await uninstallAllFeatures({root:path});let strippedAny=!1;for(const{feature,configOutcome,filesRemoved}of results)if(configOutcome==="flipped"||filesRemoved.length>0){strippedAny=!0;const fileSummary=filesRemoved.length>0?` (${filesRemoved.length} path${filesRemoved.length===1?"":"s"})`:"";log.info(` - ${feature}${fileSummary}`)}if(!strippedAny)log.info(" \u2192 no feature scaffolding present; nothing to strip.");else log.success("Minimal skeleton ready \u2014 run `./buddy <feature>:install` to add features back.")}