@deltabox/toolchain 1.10.4 → 1.11.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/CHANGELOG.md CHANGED
@@ -1,15 +1,130 @@
1
1
  # Changelog
2
2
 
3
- ## 1.10.4 (October 2026)
3
+ ## 1.11.3 (October 2026)
4
4
 
5
5
  <!-- release:start -->
6
6
 
7
- ### Documentation
8
- - Removed video preview dimensions ([#322b4b1](https://github.com/deltabox-studio/toolchain/commit/322b4b1) by [T0chi](https://github.com/T0chi))
7
+ ### Bug fixes
8
+ #### Ci
9
+ - Emit the dbx.config.ts fence title in the generated reference ([#d154039](https://github.com/deltabox-studio/toolchain/commit/d154039) by [T0chi](https://github.com/T0chi))
10
+ - `apps/docs/config/reference.md` is generated by `scripts/generate-config.ts`
11
+ (and the CI `Verify generated config artifacts are up to date` step), so its
12
+ "Full example" fence must be produced by the generator. The docs rebrand added
13
+ a `[dbx.config.ts]` title to that fence by hand, which `yarn config:generate`
14
+ then stripped, failing the drift check. Emit the title from the generator so
15
+ the regenerated (and formatted) file matches the committed one.
16
+ ### Styles
17
+ - Format the docs brand stylesheet ([#fb3a711](https://github.com/deltabox-studio/toolchain/commit/fb3a711) by [T0chi](https://github.com/T0chi))
18
+ - Run the formatter over `apps/docs/.vitepress/theme/style.css` (trailing
19
+ whitespace, indentation, numeric normalization) so `yarn format` no longer
20
+ dirties the tree.
9
21
 
10
22
  <!-- release:end -->
11
23
 
12
24
 
25
+ ## 1.11.2 (October 2026)
26
+
27
+ ### Chores
28
+ - Update `README.md` ([#3e7f4e1](https://github.com/deltabox-studio/toolchain/commit/3e7f4e1) by [T0chi](https://github.com/T0chi))
29
+
30
+ ## 1.11.1 (October 2026)
31
+
32
+ ### Bug fixes
33
+ - Write scaffold files only at the end and resolve "." names ([#c01fcf3](https://github.com/deltabox-studio/toolchain/commit/c01fcf3) by [T0chi](https://github.com/T0chi))
34
+ - Two scaffolding robustness fixes:
35
+ - Cancelling any prompt (scope, package manager, include-config, the
36
+ folder picker) aborts the whole command instead of falling back to a
37
+ default and creating orphan files/dirs. The local-template copier also
38
+ reads and renders the entire tree into memory before writing, so a
39
+ failed or interrupted run can never leave a half-copied template.
40
+ - directory it is created in (`--out`/`--root`), so
41
+ `dbx create --name . --out ./my-app` names the package `my-app`.
42
+ - Tests: emulate the `.` name through both `create` and `project:init`.
43
+ #### Shared
44
+ - Quote a Windows command path that contains spaces ([#2e1f2ab](https://github.com/deltabox-studio/toolchain/commit/2e1f2ab) by [T0chi](https://github.com/T0chi))
45
+ - `spawnCross` routes every Windows command through `cmd.exe`, which splits an
46
+ unquoted command at whitespace, so an absolute path like
47
+ `C:\Program Files\nodejs\node.exe` was resolved as `C:\Program` and never ran.
48
+ Quote the command when it contains whitespace instead of caret-escaping the
49
+ space (which `cmd.exe` still splits on). The same fix is mirrored in the
50
+ extension's `win-spawn` port.
51
+ - Tests: a functional case that hardlinks `node.exe` into a space-containing
52
+ temp dir and runs it, plus unit assertions for `escapeCmdCommand`.
53
+ #### Vscode
54
+ - Run template create without a shell so npm scopes survive ([#0863cf2](https://github.com/deltabox-studio/toolchain/commit/0863cf2) by [T0chi](https://github.com/T0chi))
55
+ - `createFromTemplate` sent online/Vite+ creates to an integrated terminal as a
56
+ shell string. On PowerShell a bare `@scope` is read as splatting (`@deltabox` ->
57
+ splat of an undefined variable), so the value was dropped and commander failed
58
+ with `option '-s, --scope <scope>' argument missing`.
59
+ - Run every template through the same argv pipeline (`runCli`, no shell) so values
60
+ like an npm scope are never mangled. Vite+ projects skip the post-create sync
61
+ since they are not dbx-managed.
62
+ ### Documentation
63
+ - Updated `vscode-example.mp4` ([#58c0061](https://github.com/deltabox-studio/toolchain/commit/58c0061) by [T0chi](https://github.com/T0chi))
64
+ - Rebrand to the new logo and blue theme ([#e28c1f9](https://github.com/deltabox-studio/toolchain/commit/e28c1f9) by [T0chi](https://github.com/T0chi))
65
+ - Swap the toolchain/box2d logos for the new `logo/*` assets, re-theme the
66
+ site with the blue brand tokens and hero/logo animations, update the home
67
+ page (GitHub action, feature icons), and refresh the guide/reference copy
68
+ and code fences. Regenerated `llms.txt` / `llms-full.txt`.
69
+ - Adopt the Carbon theme with code-group icons ([#09b2608](https://github.com/deltabox-studio/toolchain/commit/09b2608) by [T0chi](https://github.com/T0chi))
70
+ - Build the docs on vitepress-carbon: spread its base config and override
71
+ `vite.plugins` so the LLM plugin runs once (with our copy instead of
72
+ Carbon's default), add `vitepress-plugin-group-icons` for language icons on
73
+ `::: code-group` / `[title]` blocks, and register the theme. Also refresh
74
+ the year/footer config. Adds the two dev dependencies.
75
+ - Rename the "LLM context files" page to "LLMs" ([#9a9e8c7](https://github.com/deltabox-studio/toolchain/commit/9a9e8c7) by [T0chi](https://github.com/T0chi))
76
+ ### Chores
77
+ #### Vscode
78
+ - Rebrand the extension icon and README ([#bbde675](https://github.com/deltabox-studio/toolchain/commit/bbde675) by [T0chi](https://github.com/T0chi))
79
+ - Point the extension icon at the new `resources/logo.png` / `icon.svg`,
80
+ drop the old `icon.png` / `logo.svg`, and update the README with the new
81
+ banner.
82
+
83
+ ### Package changes
84
+ #### Added
85
+ - `vitepress-carbon` `^1.7.0`
86
+ - `vitepress-plugin-group-icons` `^1.8.1`
87
+
88
+ ## 1.11.0 (October 2026)
89
+
90
+ ### New features
91
+ #### Vscode
92
+ - Scaffold any template and drive Vite+ create from the extension ([#fb4a55c](https://github.com/deltabox-studio/toolchain/commit/fb4a55c) by [T0chi](https://github.com/T0chi))
93
+ - project" picker (`template:list --online`) instead of only the dbx builtins;
94
+ each entry runs the same create pipeline (name/scope/variables/actions).
95
+ bare `vite-plus` template — through the extension's prompt pipeline: pick a
96
+ concrete Vite+ template, then create like any other template. Consumers no
97
+ longer land in the interactive `vp create` CLI menu.
98
+ ### Bug fixes
99
+ #### Project
100
+ - Tag a scaffolded workspace with its own project type ([#9d05637](https://github.com/deltabox-studio/toolchain/commit/9d05637) by [T0chi](https://github.com/T0chi))
101
+ - `resolveType` fell back to the loaded config's `project.type`, but a scaffold
102
+ runs with the config of the directory it is invoked from — the monorepo root
103
+ when adding a workspace. A workspace was therefore tagged `monorepo-root`, and
104
+ the post-scaffold sync then generated root-only CI, husky and VS Code files
105
+ inside the workspace.
106
+ - Only honour an explicit `--type` (or the command's implied default); ignore the
107
+ ambient config type.
108
+ ### Documentation
109
+ - Update descriptions and vscode screenshot ([#215bc4f](https://github.com/deltabox-studio/toolchain/commit/215bc4f) by [T0chi](https://github.com/T0chi))
110
+ ### Styles
111
+ - Wrap long description lines for the formatter ([#213ea73](https://github.com/deltabox-studio/toolchain/commit/213ea73) by [T0chi](https://github.com/T0chi))
112
+ - Long single-line descriptions from earlier commits; `vp fmt` wraps them back.
113
+ ### Tests
114
+ - Emulate custom templates/providers and workspace scaffolding ([#37d370e](https://github.com/deltabox-studio/toolchain/commit/37d370e) by [T0chi](https://github.com/T0chi))
115
+ - Code extension writes) and assert the CLI lists, scaffolds and runs them —
116
+ i.e. the payloads the extension's trees consume.
117
+ assert the workspace does NOT inherit their outputs
118
+ (.github/.husky/.vscode/commitlint.config.mjs).
119
+ ### Chores
120
+ #### Vscode
121
+ - Rename the Home "All actions" section to "More actions" ([#b8a2137](https://github.com/deltabox-studio/toolchain/commit/b8a2137) by [T0chi](https://github.com/T0chi))
122
+
123
+ ## 1.10.4 (October 2026)
124
+
125
+ ### Documentation
126
+ - Removed video preview dimensions ([#322b4b1](https://github.com/deltabox-studio/toolchain/commit/322b4b1) by [T0chi](https://github.com/T0chi))
127
+
13
128
  ## 1.10.3 (October 2026)
14
129
 
15
130
  ### Bug fixes
package/README.md CHANGED
@@ -1,10 +1,14 @@
1
+ <div align="center">
2
+ <img src="./apps/docs/public/logo/logo.svg" width="150" />
3
+ </div>
4
+
1
5
  # deltabox toolchain
2
6
 
3
7
  [![license: MPL-2.0](https://img.shields.io/badge/license-MPL--2.0-blue.svg)](./package.json)
4
8
  [![tests](https://img.shields.io/github/actions/workflow/status/deltabox-studio/toolchain/ci.yml?label=tests&logo=github)](https://github.com/deltabox-studio/toolchain/actions/workflows/ci.yml)
5
9
  [![npm version](https://img.shields.io/npm/v/@deltabox/toolchain.svg?color=cb3837&label=npm)](https://www.npmjs.com/package/@deltabox/toolchain)
6
10
 
7
- All-in-one, config-driven dev toolchain: scaffold projects, generate and manage configs, tsconfigs, syncing, changelogs and CI, run Vite+, and migrations.
11
+ All-in-one development toolchain that let's you scaffold projects, generate and manage configs, tsconfigs, custom templates, syncing, changelogs, CI, Vite+, migrations, and more.
8
12
 
9
13
  - Save and use templates (local/remote) so you never have to scaffold the same projects over and over again.
10
14
  - Generate config types, env variables, cli flags, markdown and JSON schemas, so your codebase stays consistent, validated and well-documented.
@@ -14,21 +18,17 @@ All-in-one, config-driven dev toolchain: scaffold projects, generate and manage
14
18
 
15
19
  See the [list of features](#features).
16
20
 
17
- <div align="left">
18
- <video controls>
19
- <source src="./apps/docs/public/vscode-example.mp4" type="video/mp4">
20
- </video>
21
- </div>
21
+ See [CHANGELOG.md](/CHANGELOG.md) for latest updates.
22
22
 
23
- <div align="left">
24
- <img src="./apps/docs/public/vscode-screenshot.png" width="300" />
25
- </div>
23
+ Documentation, guides and examples at [toolchain.deltabox.studio](https://toolchain.deltabox.studio/guide).
26
24
 
27
- What you see above is the VS Code extension. Read more about it at [toolchain.deltabox.studio/guide/vscode](https://toolchain.deltabox.studio/guide/vscode).
25
+ The video and screenshot below is the VS Code extension. Read more about it at [toolchain.deltabox.studio/guide/vscode](https://toolchain.deltabox.studio/guide/vscode).
28
26
 
29
- See [CHANGELOG.md](/CHANGELOG.md) for latest updates.
27
+ https://github.com/user-attachments/assets/a4f1ad75-fedd-40ee-b84d-3365f7025a3e
30
28
 
31
- Documentation, guides and examples at [toolchain.deltabox.studio](https://toolchain.deltabox.studio/guide).
29
+ <div align="left">
30
+ <img src="./apps/docs/public/vscode-screenshot.png" width="300" />
31
+ </div>
32
32
 
33
33
  ## Table of contents
34
34
 
@@ -64,7 +64,7 @@ See more at [toolchain.deltabox.studio](https://toolchain.deltabox.studio).
64
64
 
65
65
  ## Install
66
66
 
67
- ```bash
67
+ ```sh
68
68
  # as a dev dependency in the project you want to manage
69
69
  yarn add -D @deltabox/toolchain
70
70
 
@@ -74,13 +74,13 @@ npm install -g @deltabox/toolchain
74
74
 
75
75
  The package exposes one binary, `dbx`:
76
76
 
77
- ```bash
77
+ ```sh
78
78
  yarn dbx --help
79
79
  ```
80
80
 
81
81
  ## Quickstart
82
82
 
83
- ```bash
83
+ ```sh
84
84
  # 1. Write a dbx.config.ts for the current project
85
85
  dbx project:config
86
86
 
@@ -230,7 +230,7 @@ Download from the [Visual Studio Code Marketplace](https://marketplace.visualstu
230
230
 
231
231
  or through the CLI:
232
232
 
233
- ```bash
233
+ ```sh
234
234
  code --install-extension deltabox.toolchain-vscode
235
235
  ```
236
236
 
@@ -241,7 +241,7 @@ See [packages/vscode/README.md](./packages/vscode/README.md) to learn more about
241
241
  This repo is a Yarn 4 workspace; Vite+ (`vp`) drives format, lint, test and the
242
242
  task runner from the root `vite.config.ts`.
243
243
 
244
- ```bash
244
+ ```sh
245
245
  yarn install # fresh checkout
246
246
  yarn build # packages via `vp run` + root tsdown
247
247
  yarn test # package + root tests
@@ -266,3 +266,7 @@ The source pages live in `apps/docs`.
266
266
  ## License
267
267
 
268
268
  [MPL-2.0](./LICENSE) © deltabox
269
+
270
+ <div align="center">
271
+ <img src="./apps/docs/public/logo/outline_colored.svg" width="150" />
272
+ </div>
package/dist/cli/index.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import"node:path";import{fileURLToPath as e}from"node:url";import.meta.url;import{LOG_LEVELS as t,logger as n,normalizeLogLevel as r,setLogOverrides as i,stripAnsi as a}from"../packages/shared/src/logger.js";import"../packages/shared/src/index.js";import{version as o}from"../package.js";import{projectCommands as s}from"../packages/project/src/cli/index.js";import{resolveVpCommand as c}from"../packages/vite/src/commands.js";import{viteCommands as l}from"../packages/vite/src/cli/index.js";import{changelogCommands as u}from"../packages/changelog/src/cli/index.js";import{migrateCommands as d}from"../packages/migrate/src/cli/index.js";import{templateCommands as f}from"../packages/template/src/cli/index.js";import{tsconfigCommands as p}from"../packages/tsconfig/src/cli/index.js";import{cleanupCommands as m}from"../packages/tsdown/src/cli/index.js";import{syncCommands as h}from"./commands/index.js";import{isPassthroughCommand as g,registerToolchainCommands as _}from"./register.js";import{runUpdateCheck as v}from"./update-check.js";import{realpathSync as y}from"node:fs";import{Command as b}from"commander";function x(){return[...h(),...p(),...m(),...u(),...d(),...s(),...f(),...l()]}function S(){return new Set(x().map(e=>e.name))}function C(e,t=S()){let n=e[2];return!n||n.startsWith(`-`)||t.has(n)||!c(n)?e:[...e.slice(0,2),`vite`,...e.slice(2)]}function w(e){let t={},n=[];for(let i=0;i<e.length;i++){let a=e[i];if(a===`--`){n.push(...e.slice(i));break}if(a===`--no-color`)t.color=!1;else if(a===`--color`)t.color=!0;else if(a===`--log-level`){let n=e[++i];n!==void 0&&(t.level=r(n))}else a.startsWith(`--log-level=`)?t.level=r(a.slice(12)):n.push(a)}return{argv:n,options:t}}function T(e){let t=!0,n=[];for(let r=0;r<e.length;r++){let i=e[r];if(i===`--`){n.push(...e.slice(r));break}i===`--no-update-check`?t=!1:i===`--update-check`?t=!0:n.push(i)}return{argv:n,updateCheck:t}}function E(){let e=new b,r=e.configureOutput();return e.configureOutput({writeOut:e=>g()?r.writeOut?.(e):n.write(`out`,e),writeErr:e=>g()?r.writeErr?.(e):n.write(`err`,e),getOutHasColors:()=>g()?r.getOutHasColors?.()??!1:n.colors.isColorSupported,getErrHasColors:()=>g()?r.getErrHasColors?.()??!1:n.colors.isColorSupported,stripColor:e=>g()?r.stripColor?.(e)??e:a(e)}),e.name(`dbx`).description(`All-in-one project toolchain: configs, tsconfigs, changelogs, scaffolding.`).version(o,`-V, --version`,`Output the version number`).option(`--log-level <level>`,`Minimum log level (${t.join(`|`)}).`).option(`--no-color`,`Disable coloured output.`).option(`--no-update-check`,`Do not check for a newer dbx version.`).showHelpAfterError(),e.configureHelp({styleTitle:e=>n.colors.bold(e),styleCommandText:e=>n.colors.cyan(e),styleOptionText:e=>n.colors.yellow(e),styleOptionTerm:e=>n.colors.yellow(e),styleSubcommandText:e=>n.colors.cyan(e),styleSubcommandTerm:e=>n.colors.cyan(e),styleArgumentText:e=>n.colors.yellow(e),styleArgumentTerm:e=>n.colors.yellow(e)}),_(e,x()),e}async function D(e=process.argv){let{argv:t,options:n}=w(e),{argv:r,updateCheck:a}=T(t);i(n);let s=e.some(e=>e===`--help`||e===`-h`||e===`--version`),c=r.some(e=>e===`--json`);!s&&!c&&v(o,a),await E().parseAsync(C(r))}function O(){let t=process.argv[1];if(!t)return!1;try{return y(t)===e(import.meta.url)}catch{return!1}}O()&&D();export{x as builtinCommands,E as createCli,w as extractLogOptions,T as extractUpdateCheck,_ as registerToolchainCommands,D as runCli,C as withDefaultVite};
2
+ import"node:path";import{fileURLToPath as e}from"node:url";import.meta.url;import{LOG_LEVELS as t,logger as n,normalizeLogLevel as r,setLogOverrides as i,stripAnsi as a}from"../packages/shared/src/logger.js";import"../packages/shared/src/index.js";import{version as o}from"../package.js";import{projectCommands as s}from"../packages/project/src/cli/index.js";import{resolveVpCommand as c}from"../packages/vite/src/commands.js";import{viteCommands as l}from"../packages/vite/src/cli/index.js";import{changelogCommands as u}from"../packages/changelog/src/cli/index.js";import{migrateCommands as d}from"../packages/migrate/src/cli/index.js";import{templateCommands as f}from"../packages/template/src/cli/index.js";import{tsconfigCommands as p}from"../packages/tsconfig/src/cli/index.js";import{cleanupCommands as m}from"../packages/tsdown/src/cli/index.js";import{syncCommands as h}from"./commands/index.js";import{isPassthroughCommand as g,registerToolchainCommands as _}from"./register.js";import{runUpdateCheck as v}from"./update-check.js";import{realpathSync as y}from"node:fs";import{Command as b}from"commander";function x(){return[...h(),...p(),...m(),...u(),...d(),...s(),...f(),...l()]}function S(){return new Set(x().map(e=>e.name))}function C(e,t=S()){let n=e[2];return!n||n.startsWith(`-`)||t.has(n)||!c(n)?e:[...e.slice(0,2),`vite`,...e.slice(2)]}function w(e){let t={},n=[];for(let i=0;i<e.length;i++){let a=e[i];if(a===`--`){n.push(...e.slice(i));break}if(a===`--no-color`)t.color=!1;else if(a===`--color`)t.color=!0;else if(a===`--log-level`){let n=e[++i];n!==void 0&&(t.level=r(n))}else a.startsWith(`--log-level=`)?t.level=r(a.slice(12)):n.push(a)}return{argv:n,options:t}}function T(e){let t=!0,n=[];for(let r=0;r<e.length;r++){let i=e[r];if(i===`--`){n.push(...e.slice(r));break}i===`--no-update-check`?t=!1:i===`--update-check`?t=!0:n.push(i)}return{argv:n,updateCheck:t}}function E(){let e=new b,r=e.configureOutput();return e.configureOutput({writeOut:e=>g()?r.writeOut?.(e):n.write(`out`,e),writeErr:e=>g()?r.writeErr?.(e):n.write(`err`,e),getOutHasColors:()=>g()?r.getOutHasColors?.()??!1:n.colors.isColorSupported,getErrHasColors:()=>g()?r.getErrHasColors?.()??!1:n.colors.isColorSupported,stripColor:e=>g()?r.stripColor?.(e)??e:a(e)}),e.name(`dbx`).description(`All-in-one project toolchain: configs, tsconfigs, changelogs, CI, templates, scaffolding, migrations.`).version(o,`-V, --version`,`Output the version number`).option(`--log-level <level>`,`Minimum log level (${t.join(`|`)}).`).option(`--no-color`,`Disable coloured output.`).option(`--no-update-check`,`Do not check for a newer dbx version.`).showHelpAfterError(),e.configureHelp({styleTitle:e=>n.colors.bold(e),styleCommandText:e=>n.colors.cyan(e),styleOptionText:e=>n.colors.yellow(e),styleOptionTerm:e=>n.colors.yellow(e),styleSubcommandText:e=>n.colors.cyan(e),styleSubcommandTerm:e=>n.colors.cyan(e),styleArgumentText:e=>n.colors.yellow(e),styleArgumentTerm:e=>n.colors.yellow(e)}),_(e,x()),e}async function D(e=process.argv){let{argv:t,options:n}=w(e),{argv:r,updateCheck:a}=T(t);i(n);let s=e.some(e=>e===`--help`||e===`-h`||e===`--version`),c=r.some(e=>e===`--json`);!s&&!c&&v(o,a),await E().parseAsync(C(r))}function O(){let t=process.argv[1];if(!t)return!1;try{return y(t)===e(import.meta.url)}catch{return!1}}O()&&D();export{x as builtinCommands,E as createCli,w as extractLogOptions,T as extractUpdateCheck,_ as registerToolchainCommands,D as runCli,C as withDefaultVite};
package/dist/package.js CHANGED
@@ -1 +1 @@
1
- import"node:path";import"node:url";import.meta.url;var e=`1.10.4`;export{e as version};
1
+ import"node:path";import"node:url";import.meta.url;var e=`1.11.3`;export{e as version};
@@ -1 +1 @@
1
- import"node:path";import"node:url";import.meta.url;import{logger as e}from"../../../shared/src/logger.js";import"../../../shared/src/index.js";import{createMonorepoRootFiles as t,createRootProjectFiles as n,createWorkspaceFiles as r,writeGeneratedFiles as i}from"../typescript.js";import{generateProjectConfigs as a}from"../config.js";const o=[{flags:`--no-config`,description:`Skip the dbx.config.ts file.`},{flags:`--type <type>`,description:`Project type: project | monorepo-root | monorepo-workspace.`}],s={flags:`--package-manager <pm>`,description:`Package manager: yarn (default) | pnpm | npm | bun.`},c=[`project`,`monorepo-root`,`monorepo-workspace`];function l(e,t){let n=String(e.name??t.project?.name??``).trim();if(!n)throw Error("a project name is required: pass `--name <name>` or set `project.name` in dbx.config.*.");return n}function u(e,t,n){let r=e.type??t.project?.type??n;if(!c.includes(r))throw Error(`invalid project type "${r}" (expected one of: ${c.join(`, `)}).`);return r}function d(){return[{name:`project:init`,description:`Scaffold a single-package TypeScript project.`,options:[{flags:`-n, --name <name>`,description:`Project name.`},{flags:`-s, --scope <scope>`,description:`npm scope, eg. @acme.`},{flags:`-o, --out <dir>`,description:`Output directory (default: cwd).`},s,...o],run:(t,r)=>{let a=l(t,r.config),o=n({name:a,...t.scope?{scope:t.scope}:{},...t.packageManager?{packageManager:t.packageManager}:{},type:u(t,r.config,`project`),includeConfig:t.config??!0}),s=i(o,t.out??r.cwd);for(let t of s)e.info(`Created ${t}`)}},{name:`project:init:monorepo`,description:`Scaffold a monorepo root (add --example for one example workspace).`,options:[{flags:`-n, --name <name>`,description:`Project name.`},{flags:`-s, --scope <scope>`,description:`npm scope, eg. @acme.`},{flags:`-o, --out <dir>`,description:`Output directory (default: cwd).`},{flags:`--example`,description:`Also scaffold one example workspace package.`},s,...o],run:(n,a)=>{let o=l(n,a.config),s=n.scope??a.config.project?.scope??`@${o}`,c=n.out??a.cwd,d=n.config??!0,f=u(n,a.config,`monorepo-root`),p=!!n.example,m=n.packageManager,h=t({name:o,scope:s,type:f,includeConfig:d,...m?{packageManager:m}:{},workspaces:p?[{alias:`core`,dir:`packages/core`}]:[]});p&&h.push(...r({name:o,scope:s,wsName:`core`,wsDir:`packages/core`,type:`monorepo-workspace`,includeConfig:d,useCatalog:!0,...m?{packageManager:m}:{}}).map(e=>({...e,path:`packages/core/${e.path}`})));let g=i(h,c);for(let t of g)e.info(`Created ${t}`)}},{name:`project:init:workspace`,description:`Scaffold a monorepo workspace package into an existing monorepo root.`,options:[{flags:`-n, --name <name>`,description:`Workspace package name (unscoped).`},{flags:`-s, --scope <scope>`,description:`npm scope, eg. @acme.`},{flags:`-r, --root <dir>`,description:`Monorepo root (default: cwd).`},{flags:`-d, --dir <dir>`,description:`Workspace dir relative to the root (default: packages/<name>).`},s,...o],run:(t,n)=>{let a=t.root??n.cwd,o=l(t,n.config),s=t.scope??n.config.project?.scope??`@${o}`,c=(t.dir??`packages/${o}`).replace(/\\/g,`/`).replace(/\/+$/,``),d=t.config??!0,f=u(t,n.config,`monorepo-workspace`),p=t.packageManager,m=r({name:o,scope:s,wsName:o,wsDir:c,type:f,includeConfig:d,...p?{packageManager:p}:{}}).map(e=>({...e,path:`${c}/${e.path}`})),h=i(m,a);for(let t of h)e.info(`Created ${t}`)}},{name:`project:config`,description:`Generate dbx.config.ts for existing projects that are missing one.`,options:[{flags:`--cwd <dir>`,description:`Root to scan (default: cwd).`},{flags:`--include <pattern...>`,description:`Only projects matching these patterns (glob or substring).`},{flags:`--exclude <pattern...>`,description:`Skip projects matching these patterns (glob or substring).`},{flags:`--type <type>`,description:`Force a project type for every config.`},{flags:`--force`,description:`Overwrite configs that already exist.`},{flags:`--dry-run`,description:`Report what would be written without writing.`},{flags:`--json`,description:`Print the result as JSON.`}],run:(t,n)=>{let r=t.cwd??n.cwd,i=a({root:r,...t.include?{include:t.include}:{},...t.exclude?{exclude:t.exclude}:{},...t.type?{type:t.type}:{},force:!!t.force,dryRun:!!t.dryRun});if(t.json){e.raw(JSON.stringify(i,null,4));return}let o=i.dryRun?`would create`:`created`;e.info(`${o} ${i.created.length} config(s), skipped ${i.skipped.length}.`);for(let t of i.configs)t.created&&e.info(` + ${t.rel===`.`?`dbx.config.ts`:`${t.rel}/dbx.config.ts`} [${t.type}]`)}}]}export{d as projectCommands};
1
+ import{default as e}from"node:path";import"node:url";import.meta.url;import{logger as t}from"../../../shared/src/logger.js";import"../../../shared/src/index.js";import{createMonorepoRootFiles as n,createRootProjectFiles as r,createWorkspaceFiles as i,writeGeneratedFiles as a}from"../typescript.js";import{generateProjectConfigs as o}from"../config.js";const s=[{flags:`--no-config`,description:`Skip the dbx.config.ts file.`},{flags:`--type <type>`,description:`Project type: project | monorepo-root | monorepo-workspace.`}],c={flags:`--package-manager <pm>`,description:`Package manager: yarn (default) | pnpm | npm | bun.`},l=[`project`,`monorepo-root`,`monorepo-workspace`];function u(e,t){let n=String(e.name??t.project?.name??``).trim();if(!n)throw Error("a project name is required: pass `--name <name>` or set `project.name` in dbx.config.*.");return n}function d(t,n){return t===`.`?e.basename(e.resolve(n)):t}function f(e,t){let n=e.type??t;if(!l.includes(n))throw Error(`invalid project type "${n}" (expected one of: ${l.join(`, `)}).`);return n}function p(){return[{name:`project:init`,description:`Scaffold a single-package TypeScript project.`,options:[{flags:`-n, --name <name>`,description:`Project name.`},{flags:`-s, --scope <scope>`,description:`npm scope, eg. @acme.`},{flags:`-o, --out <dir>`,description:`Output directory (default: cwd).`},c,...s],run:(e,n)=>{let i=e.out??n.cwd,o=d(u(e,n.config),i),s=r({name:o,...e.scope?{scope:e.scope}:{},...e.packageManager?{packageManager:e.packageManager}:{},type:f(e,`project`),includeConfig:e.config??!0}),c=a(s,i);for(let e of c)t.info(`Created ${e}`)}},{name:`project:init:monorepo`,description:`Scaffold a monorepo root (add --example for one example workspace).`,options:[{flags:`-n, --name <name>`,description:`Project name.`},{flags:`-s, --scope <scope>`,description:`npm scope, eg. @acme.`},{flags:`-o, --out <dir>`,description:`Output directory (default: cwd).`},{flags:`--example`,description:`Also scaffold one example workspace package.`},c,...s],run:(e,r)=>{let o=e.out??r.cwd,s=d(u(e,r.config),o),c=e.scope??r.config.project?.scope??`@${s}`,l=e.config??!0,p=f(e,`monorepo-root`),m=!!e.example,h=e.packageManager,g=n({name:s,scope:c,type:p,includeConfig:l,...h?{packageManager:h}:{},workspaces:m?[{alias:`core`,dir:`packages/core`}]:[]});m&&g.push(...i({name:s,scope:c,wsName:`core`,wsDir:`packages/core`,type:`monorepo-workspace`,includeConfig:l,useCatalog:!0,...h?{packageManager:h}:{}}).map(e=>({...e,path:`packages/core/${e.path}`})));let _=a(g,o);for(let e of _)t.info(`Created ${e}`)}},{name:`project:init:workspace`,description:`Scaffold a monorepo workspace package into an existing monorepo root.`,options:[{flags:`-n, --name <name>`,description:`Workspace package name (unscoped).`},{flags:`-s, --scope <scope>`,description:`npm scope, eg. @acme.`},{flags:`-r, --root <dir>`,description:`Monorepo root (default: cwd).`},{flags:`-d, --dir <dir>`,description:`Workspace dir relative to the root (default: packages/<name>).`},c,...s],run:(e,n)=>{let r=e.root??n.cwd,o=d(u(e,n.config),r),s=e.scope??n.config.project?.scope??`@${o}`,c=(e.dir??`packages/${o}`).replace(/\\/g,`/`).replace(/\/+$/,``),l=e.config??!0,p=f(e,`monorepo-workspace`),m=e.packageManager,h=i({name:o,scope:s,wsName:o,wsDir:c,type:p,includeConfig:l,...m?{packageManager:m}:{}}).map(e=>({...e,path:`${c}/${e.path}`})),g=a(h,r);for(let e of g)t.info(`Created ${e}`)}},{name:`project:config`,description:`Generate dbx.config.ts for existing projects that are missing one.`,options:[{flags:`--cwd <dir>`,description:`Root to scan (default: cwd).`},{flags:`--include <pattern...>`,description:`Only projects matching these patterns (glob or substring).`},{flags:`--exclude <pattern...>`,description:`Skip projects matching these patterns (glob or substring).`},{flags:`--type <type>`,description:`Force a project type for every config.`},{flags:`--force`,description:`Overwrite configs that already exist.`},{flags:`--dry-run`,description:`Report what would be written without writing.`},{flags:`--json`,description:`Print the result as JSON.`}],run:(e,n)=>{let r=e.cwd??n.cwd,i=o({root:r,...e.include?{include:e.include}:{},...e.exclude?{exclude:e.exclude}:{},...e.type?{type:e.type}:{},force:!!e.force,dryRun:!!e.dryRun});if(e.json){t.raw(JSON.stringify(i,null,4));return}let a=i.dryRun?`would create`:`created`;t.info(`${a} ${i.created.length} config(s), skipped ${i.skipped.length}.`);for(let e of i.configs)e.created&&t.info(` + ${e.rel===`.`?`dbx.config.ts`:`${e.rel}/dbx.config.ts`} [${e.type}]`)}}]}export{p as projectCommands};
@@ -1 +1 @@
1
- import{default as e}from"node:path";import"node:url";import.meta.url;import{logger as t}from"./logger.js";import n from"node:fs";import{spawn as r,spawnSync as i}from"node:child_process";const a=process.platform===`win32`;let o=!1;const s=/([()\][%!^"`<>&|;, *?])/g;function c(e){return e.replace(s,`^$1`)}function l(e,t){let n=String(e);return n=n.replace(/(?=(\\+?)?)\1"/g,`$1$1\\"`),n=n.replace(/(?=(\\+?)?)\1$/,`$1$1`),n=`"${n}"`,n=n.replace(s,`^$1`),t&&(n=n.replace(s,`^$1`)),n}function u(t){if(e.extname(t))return n.existsSync(t)?t:void 0;let r=(process.env.PATHEXT??`.COM;.EXE;.BAT;.CMD`).split(`;`),i=(process.env.PATH??``).split(e.delimiter);for(let a of r)for(let r of i){let i=e.join(r,`${t}${a.toLowerCase()}`);if(n.existsSync(i))return i}}function d(e){let t=u(e)??e;return/\.(cmd|bat)$/i.test(t)}function f(e,t=`SIGKILL`){let n=e.pid;if(n!==void 0){if(a)try{i(`taskkill`,[`/pid`,String(n),`/t`,`/f`],{stdio:`ignore`});return}catch{}try{e.kill(t)}catch{}}}function p(e,n,i={}){let{timeoutMs:s,onTimeout:u,...p}=i,m;if(a){let i=d(e),a=[c(e),...n.map(e=>l(e,i))].join(` `),s=process.env.ComSpec;!s&&!o&&(o=!0,t.warn(`ComSpec is not set — falling back to cmd.exe.`)),m=r(s??`cmd.exe`,[`/d`,`/s`,`/c`,`"${a}"`],{stdio:`inherit`,...p,shell:!1,windowsVerbatimArguments:!0})}else m=r(e,n,{stdio:`inherit`,...p,shell:!1});if(s!==void 0&&s>0){let n=setTimeout(()=>{u?u(e,s):t.warn(`\`${e}\` exceeded ${s}ms — terminating it.`),f(m)},s);n.unref?.(),m.once(`exit`,()=>clearTimeout(n))}return m}function m(e,n,r){let{exitWithMainProc:i=!0,argsEndingDashes:a=!0,verbose:o=!0,timeoutMs:s,...c}=r??{},l=[...n,...a?[`--`]:[]];o&&t.info(`$ ${e} ${l.join(` `)}`);let u=p(e,l,{...c,...s===void 0?{}:{timeoutMs:s}});return i&&u.on(`exit`,process.exit),u}export{f as killProcessTree,m as run,p as spawnCross};
1
+ import{default as e}from"node:path";import"node:url";import.meta.url;import{logger as t}from"./logger.js";import n from"node:fs";import{spawn as r,spawnSync as i}from"node:child_process";const a=process.platform===`win32`;let o=!1;const s=/([()\][%!^"`<>&|;, *?])/g;function c(e){return/\s/.test(e)?`"${e.replace(/(?=(\\+?)?)\1"/g,`$1$1\\"`).replace(/(?=(\\+?)?)\1$/,`$1$1`)}"`:e.replace(s,`^$1`)}function l(e,t){let n=String(e);return n=n.replace(/(?=(\\+?)?)\1"/g,`$1$1\\"`),n=n.replace(/(?=(\\+?)?)\1$/,`$1$1`),n=`"${n}"`,n=n.replace(s,`^$1`),t&&(n=n.replace(s,`^$1`)),n}function u(t){if(e.extname(t))return n.existsSync(t)?t:void 0;let r=(process.env.PATHEXT??`.COM;.EXE;.BAT;.CMD`).split(`;`),i=(process.env.PATH??``).split(e.delimiter);for(let a of r)for(let r of i){let i=e.join(r,`${t}${a.toLowerCase()}`);if(n.existsSync(i))return i}}function d(e){let t=u(e)??e;return/\.(cmd|bat)$/i.test(t)}function f(e,t=`SIGKILL`){let n=e.pid;if(n!==void 0){if(a)try{i(`taskkill`,[`/pid`,String(n),`/t`,`/f`],{stdio:`ignore`});return}catch{}try{e.kill(t)}catch{}}}function p(e,n,i={}){let{timeoutMs:s,onTimeout:u,...p}=i,m;if(a){let i=d(e),a=[c(e),...n.map(e=>l(e,i))].join(` `),s=process.env.ComSpec;!s&&!o&&(o=!0,t.warn(`ComSpec is not set — falling back to cmd.exe.`)),m=r(s??`cmd.exe`,[`/d`,`/s`,`/c`,`"${a}"`],{stdio:`inherit`,...p,shell:!1,windowsVerbatimArguments:!0})}else m=r(e,n,{stdio:`inherit`,...p,shell:!1});if(s!==void 0&&s>0){let n=setTimeout(()=>{u?u(e,s):t.warn(`\`${e}\` exceeded ${s}ms — terminating it.`),f(m)},s);n.unref?.(),m.once(`exit`,()=>clearTimeout(n))}return m}function m(e,n,r){let{exitWithMainProc:i=!0,argsEndingDashes:a=!0,verbose:o=!0,timeoutMs:s,...c}=r??{},l=[...n,...a?[`--`]:[]];o&&t.info(`$ ${e} ${l.join(` `)}`);let u=p(e,l,{...c,...s===void 0?{}:{timeoutMs:s}});return i&&u.on(`exit`,process.exit),u}export{f as killProcessTree,m as run,p as spawnCross};
@@ -1 +1 @@
1
- import{default as e}from"node:path";import"node:url";import.meta.url;import{logger as t}from"../../../shared/src/logger.js";import"../../../shared/src/index.js";import{TEMPLATE_MANIFEST as n,writeTemplateManifest as r}from"../local.js";import{parseSourceSpec as i}from"../online.js";import{addTemplateSource as a,loadTemplates as o,readTemplateRegistry as s,removeTemplateSource as c}from"../registry.js";import{scaffoldTemplate as l}from"../scaffold.js";import u from"node:fs";import{cancel as d,confirm as f,isCancel as p,select as m,text as h}from"@clack/prompts";const g={flags:`--cwd <dir>`,description:`Project root (default: cwd).`},_={flags:`--kind <kind>`,description:`project | workspace | application | folder | tooling | pipeline.`};function v(e){return{id:e.id,name:e.name,...e.description?{description:e.description}:{},kind:e.kind,type:e.source.type,online:!!e.online,...e.tags?{tags:e.tags}:{},...e.variables?{variables:e.variables}:{},...e.actions?{actions:e.actions}:{}}}function y(e){let t={};for(let n of e??[]){let e=n.indexOf(`=`);e!==-1&&(t[n.slice(0,e)]=n.slice(e+1))}return t}function b(e){return(e??[]).map(e=>{let t=e.indexOf(`:`);return t===-1?{name:e}:{name:e.slice(0,t),default:e.slice(t+1)}})}function x(e){return!!(process.stdin.isTTY&&process.stdout.isTTY)&&e.json!==!0}function S(){d(`Cancelled.`),process.exitCode=1}function C(){return[{name:`template:list`,description:`List templates (builtin + local + opted-in online sources).`,options:[g,{flags:`--online`,description:`Resolve online sources (npm/git).`},{flags:`--offline`,description:`Force offline (skip npm/git).`},_,{flags:`--registry`,description:`List only the .dbx/toolchain/templates.json sources.`},{flags:`--json`,description:`Print the templates as JSON.`}],run:async(e,n)=>{let r=e.cwd??n.cwd;if(e.registry){let n=s(r).sources;if(e.json){t.raw(JSON.stringify(n.map(e=>({key:`${e.type}:${e.locate}`,...e})),null,4));return}if(n.length===0){t.info(`the template registry is empty.`);return}for(let e of n)t.info(` ${`${e.type}:${e.locate}`.padEnd(40)} ${e.name??e.id??``}`);return}let i=!!e.online&&!e.offline,a=await o({cwd:r,config:n.config,online:i}),c=e.kind,l=c?a.filter(e=>e.kind===c):a;if(e.json)t.raw(JSON.stringify(l.map(v),null,4));else if(l.length===0)t.info(`no templates found.`);else for(let e of l){let n=[e.kind,e.source.type,e.online?`online`:`offline`].join(`, `);t.info(` ${e.id.padEnd(24)} ${e.name} [${n}]${e.description?`\n ${e.description}`:``}`)}}},{name:`template:init`,description:`Write a template.json to a directory, making it a local template.`,args:[{name:`dir`,description:`Directory (default: cwd).`,required:!1}],options:[g,_,{flags:`--name <name>`,description:`Display name (default: folder name).`},{flags:`--description <text>`,description:`Description.`},{flags:`--var <pair...>`,description:`Variable name[:default] (repeatable).`},{flags:`--tag <tag...>`,description:`Tag (repeatable).`},{flags:`--force`,description:`Overwrite an existing template.json.`},{flags:`--add`,description:`Also record the folder in .dbx/toolchain/templates.json.`},{flags:`--json`,description:`Print the manifest as JSON.`}],run:(i,o)=>{let s=e.resolve(o.cwd,String(i.dir??`.`)),c=e.join(s,n);if(u.existsSync(c)&&!i.force)throw Error(`template.json already exists at ${c} (use --force).`);let l=b(i.var),d={name:String(i.name??e.basename(s)),kind:i.kind??`folder`,...i.description?{description:String(i.description)}:{},...l.length?{variables:l}:{},...i.tag?{tags:i.tag}:{}};r(s,d),i.add&&a(o.cwd,{type:`local`,locate:s,kind:d.kind}),i.json?t.raw(JSON.stringify(d,null,4)):(t.info(`wrote ${c}`),i.add&&t.info(`registered the folder as a template.`))}},{name:`template:add`,description:`Record a template source in .dbx/toolchain/templates.json.`,args:[{name:`source`,description:`local path | git URL | @scope/create | type:locate`}],options:[g,{flags:`--type <type>`,description:`Force: builtin | local | npm | git | vite-plus.`},{flags:`--name <name>`,description:`Display name.`},_,{flags:`--ref <ref>`,description:`Git branch/tag/commit.`},{flags:`--subdir <dir>`,description:`Subdirectory within the source.`},{flags:`--json`,description:`Print the updated registry as JSON.`}],run:(n,o)=>{let s=i(String(n.source),o.cwd);n.type&&(s.type=n.type),n.name&&(s.name=String(n.name)),n.kind&&(s.kind=n.kind),n.ref&&(s.ref=String(n.ref)),n.subdir&&(s.subdir=String(n.subdir)),s.type===`local`&&!u.existsSync(e.join(s.locate,`template.json`))&&r(s.locate,{name:s.name??e.basename(s.locate),kind:n.kind??`folder`});let c=a(o.cwd,s);n.json?t.raw(JSON.stringify(c,null,4)):t.info(`added ${s.type}:${s.locate} (${c.sources.length} source(s)).`)}},{name:`template:remove`,description:`Remove a source from .dbx/toolchain/templates.json.`,args:[{name:`id`,description:`Source id or type:locate.`}],options:[g,{flags:`--json`,description:`Print the registry as JSON.`}],run:(e,n)=>{let{registry:r,removed:i}=c(n.cwd,String(e.id));e.json?t.raw(JSON.stringify(r,null,4)):i===0?(t.error(`no source matching "${e.id}".`),process.exitCode=1):t.info(`removed ${i} source(s) (${r.sources.length} left).`)}},{name:`template:info`,description:`Show one template by id.`,args:[{name:`id`,description:`The template id.`}],options:[g,{flags:`--online`,description:`Resolve online sources.`},{flags:`--json`,description:`Print the template as JSON (the default).`}],run:async(e,n)=>{let r=e.cwd??n.cwd,i=(await o({cwd:r,config:n.config,online:!!e.online})).find(t=>t.id===e.id);i?t.raw(JSON.stringify(v(i),null,4)):(t.error(`unknown template "${e.id}".`),process.exitCode=1)}},{name:`create`,description:`Scaffold a project from a template (builtin, local or Vite+).`,options:[{flags:`-t, --template <id>`,description:`Template id (see template:list).`},_,{flags:`-n, --name <name>`,description:`Project / package name.`},{flags:`-s, --scope <scope>`,description:`npm scope, eg. @acme.`},{flags:`-o, --out <dir>`,description:`Output directory (default: cwd).`},g,{flags:`--var <pair...>`,description:`Template variables as key=value (repeatable).`},{flags:`--action <id...>`,description:`Action id to run after scaffolding (repeatable).`},{flags:`--online`,description:`Resolve online templates too.`},{flags:`--json`,description:`Print the result as JSON.`}],run:async(e,n)=>{let r=e.out??n.cwd,i=e.kind,a=await o({cwd:n.cwd,config:n.config,online:!0}),s=x(e),c=String(e.template??``),u=e.name,d=e.scope;if(s&&!c&&!i){if(a.length===0)throw Error("No templates available. Pass --template <id> (see `dbx template:list`).");let e=await m({message:`Pick a template`,options:a.map(e=>({value:e.id,label:e.name,...e.description?{hint:e.description}:{}}))});if(p(e))return S();c=String(e)}if(s&&!u){let e=await h({message:`Project name`,placeholder:n.config.project?.name??`app`,defaultValue:n.config.project?.name??`app`});if(p(e))return S();u=String(e)}if(s&&!d){let e=await h({message:`npm scope (optional)`,placeholder:n.config.project?.scope??`@${u??`app`}`});if(p(e))return S();d=String(e)||void 0}let g=a.find(e=>e.id===c);if(!g&&!c&&i&&(g=a.find(e=>e.kind===i)),!g&&c&&(g={id:c,name:c,kind:i??`project`,source:{type:`vite-plus`,locate:c},online:!0}),!g)throw Error("No template selected. Pass --template <id> or --kind <kind> (see `dbx template:list`).");let _={...n.config.project,name:u??n.config.project?.name??`app`,...d?{scope:d}:{}},v=y(e.var),b=new Set(e.action??[]);if(s){for(let e of g.variables??[]){if(e.name===`name`||e.name===`scope`||v[e.name]!==void 0)continue;let t=e.prompt??e.name;if(e.options&&e.options.length>0){let n=await m({message:t,options:e.options.map(e=>({value:e,label:e})),...e.default?{initialValue:e.default}:{}});if(p(n))return S();v[e.name]=String(n)}else{let n=await h({message:t,...e.default?{placeholder:e.default,defaultValue:e.default}:{}});if(p(n))return S();let r=String(n??``);v[e.name]=r||(e.default??``)}}for(let e of g.actions??[]){if(b.has(e.id))continue;let t=await f({message:e.prompt??`Run ${e.id}?`,initialValue:e.default??!1});if(p(t))return S();t&&b.add(e.id)}}let C=await l(g,{cwd:r,tokens:_,variables:v,actions:[...b]});if(e.json)t.raw(JSON.stringify({template:g.id,out:r,...C},null,4));else if(g.source.type===`vite-plus`||g.source.type===`npm`||g.source.type===`git`)C.code&&(process.exitCode=C.code);else for(let e of C.written)t.info(`Created ${e}`)}}]}export{C as templateCommands};
1
+ import{default as e}from"node:path";import"node:url";import.meta.url;import{logger as t}from"../../../shared/src/logger.js";import"../../../shared/src/index.js";import{TEMPLATE_MANIFEST as n,writeTemplateManifest as r}from"../local.js";import{parseSourceSpec as i}from"../online.js";import{addTemplateSource as a,loadTemplates as o,readTemplateRegistry as s,removeTemplateSource as c}from"../registry.js";import{scaffoldTemplate as l}from"../scaffold.js";import u from"node:fs";import{cancel as d,confirm as f,isCancel as p,select as m,text as h}from"@clack/prompts";const g={flags:`--cwd <dir>`,description:`Project root (default: cwd).`},_={flags:`--kind <kind>`,description:`project | workspace | application | folder | tooling | pipeline.`};function v(e){return{id:e.id,name:e.name,...e.description?{description:e.description}:{},kind:e.kind,type:e.source.type,online:!!e.online,...e.tags?{tags:e.tags}:{},...e.variables?{variables:e.variables}:{},...e.actions?{actions:e.actions}:{}}}function y(e){let t={};for(let n of e??[]){let e=n.indexOf(`=`);e!==-1&&(t[n.slice(0,e)]=n.slice(e+1))}return t}function b(e){return(e??[]).map(e=>{let t=e.indexOf(`:`);return t===-1?{name:e}:{name:e.slice(0,t),default:e.slice(t+1)}})}function x(e){return!!(process.stdin.isTTY&&process.stdout.isTTY)&&e.json!==!0}function S(){d(`Cancelled.`),process.exitCode=1}function C(){return[{name:`template:list`,description:`List templates (builtin + local + opted-in online sources).`,options:[g,{flags:`--online`,description:`Resolve online sources (npm/git).`},{flags:`--offline`,description:`Force offline (skip npm/git).`},_,{flags:`--registry`,description:`List only the .dbx/toolchain/templates.json sources.`},{flags:`--json`,description:`Print the templates as JSON.`}],run:async(e,n)=>{let r=e.cwd??n.cwd;if(e.registry){let n=s(r).sources;if(e.json){t.raw(JSON.stringify(n.map(e=>({key:`${e.type}:${e.locate}`,...e})),null,4));return}if(n.length===0){t.info(`the template registry is empty.`);return}for(let e of n)t.info(` ${`${e.type}:${e.locate}`.padEnd(40)} ${e.name??e.id??``}`);return}let i=!!e.online&&!e.offline,a=await o({cwd:r,config:n.config,online:i}),c=e.kind,l=c?a.filter(e=>e.kind===c):a;if(e.json)t.raw(JSON.stringify(l.map(v),null,4));else if(l.length===0)t.info(`no templates found.`);else for(let e of l){let n=[e.kind,e.source.type,e.online?`online`:`offline`].join(`, `);t.info(` ${e.id.padEnd(24)} ${e.name} [${n}]${e.description?`\n ${e.description}`:``}`)}}},{name:`template:init`,description:`Write a template.json to a directory, making it a local template.`,args:[{name:`dir`,description:`Directory (default: cwd).`,required:!1}],options:[g,_,{flags:`--name <name>`,description:`Display name (default: folder name).`},{flags:`--description <text>`,description:`Description.`},{flags:`--var <pair...>`,description:`Variable name[:default] (repeatable).`},{flags:`--tag <tag...>`,description:`Tag (repeatable).`},{flags:`--force`,description:`Overwrite an existing template.json.`},{flags:`--add`,description:`Also record the folder in .dbx/toolchain/templates.json.`},{flags:`--json`,description:`Print the manifest as JSON.`}],run:(i,o)=>{let s=e.resolve(o.cwd,String(i.dir??`.`)),c=e.join(s,n);if(u.existsSync(c)&&!i.force)throw Error(`template.json already exists at ${c} (use --force).`);let l=b(i.var),d={name:String(i.name??e.basename(s)),kind:i.kind??`folder`,...i.description?{description:String(i.description)}:{},...l.length?{variables:l}:{},...i.tag?{tags:i.tag}:{}};r(s,d),i.add&&a(o.cwd,{type:`local`,locate:s,kind:d.kind}),i.json?t.raw(JSON.stringify(d,null,4)):(t.info(`wrote ${c}`),i.add&&t.info(`registered the folder as a template.`))}},{name:`template:add`,description:`Record a template source in .dbx/toolchain/templates.json.`,args:[{name:`source`,description:`local path | git URL | @scope/create | type:locate`}],options:[g,{flags:`--type <type>`,description:`Force: builtin | local | npm | git | vite-plus.`},{flags:`--name <name>`,description:`Display name.`},_,{flags:`--ref <ref>`,description:`Git branch/tag/commit.`},{flags:`--subdir <dir>`,description:`Subdirectory within the source.`},{flags:`--json`,description:`Print the updated registry as JSON.`}],run:(n,o)=>{let s=i(String(n.source),o.cwd);n.type&&(s.type=n.type),n.name&&(s.name=String(n.name)),n.kind&&(s.kind=n.kind),n.ref&&(s.ref=String(n.ref)),n.subdir&&(s.subdir=String(n.subdir)),s.type===`local`&&!u.existsSync(e.join(s.locate,`template.json`))&&r(s.locate,{name:s.name??e.basename(s.locate),kind:n.kind??`folder`});let c=a(o.cwd,s);n.json?t.raw(JSON.stringify(c,null,4)):t.info(`added ${s.type}:${s.locate} (${c.sources.length} source(s)).`)}},{name:`template:remove`,description:`Remove a source from .dbx/toolchain/templates.json.`,args:[{name:`id`,description:`Source id or type:locate.`}],options:[g,{flags:`--json`,description:`Print the registry as JSON.`}],run:(e,n)=>{let{registry:r,removed:i}=c(n.cwd,String(e.id));e.json?t.raw(JSON.stringify(r,null,4)):i===0?(t.error(`no source matching "${e.id}".`),process.exitCode=1):t.info(`removed ${i} source(s) (${r.sources.length} left).`)}},{name:`template:info`,description:`Show one template by id.`,args:[{name:`id`,description:`The template id.`}],options:[g,{flags:`--online`,description:`Resolve online sources.`},{flags:`--json`,description:`Print the template as JSON (the default).`}],run:async(e,n)=>{let r=e.cwd??n.cwd,i=(await o({cwd:r,config:n.config,online:!!e.online})).find(t=>t.id===e.id);i?t.raw(JSON.stringify(v(i),null,4)):(t.error(`unknown template "${e.id}".`),process.exitCode=1)}},{name:`create`,description:`Scaffold a project from a template (builtin, local or Vite+).`,options:[{flags:`-t, --template <id>`,description:`Template id (see template:list).`},_,{flags:`-n, --name <name>`,description:`Project / package name.`},{flags:`-s, --scope <scope>`,description:`npm scope, eg. @acme.`},{flags:`-o, --out <dir>`,description:`Output directory (default: cwd).`},g,{flags:`--var <pair...>`,description:`Template variables as key=value (repeatable).`},{flags:`--action <id...>`,description:`Action id to run after scaffolding (repeatable).`},{flags:`--online`,description:`Resolve online templates too.`},{flags:`--json`,description:`Print the result as JSON.`}],run:async(n,r)=>{let i=n.out??r.cwd,a=n.kind,s=await o({cwd:r.cwd,config:r.config,online:!0}),c=x(n),u=String(n.template??``),d=n.name,g=n.scope;if(c&&!u&&!a){if(s.length===0)throw Error("No templates available. Pass --template <id> (see `dbx template:list`).");let e=await m({message:`Pick a template`,options:s.map(e=>({value:e.id,label:e.name,...e.description?{hint:e.description}:{}}))});if(p(e))return S();u=String(e)}if(c&&!d){let e=await h({message:`Project name`,placeholder:r.config.project?.name??`app`,defaultValue:r.config.project?.name??`app`});if(p(e))return S();d=String(e)}if(c&&!g){let e=await h({message:`npm scope (optional)`,placeholder:r.config.project?.scope??`@${d??`app`}`});if(p(e))return S();g=String(e)||void 0}let _=s.find(e=>e.id===u);if(!_&&!u&&a&&(_=s.find(e=>e.kind===a)),!_&&u&&(_={id:u,name:u,kind:a??`project`,source:{type:`vite-plus`,locate:u},online:!0}),!_)throw Error("No template selected. Pass --template <id> or --kind <kind> (see `dbx template:list`).");d===`.`&&(d=e.basename(e.resolve(i)));let v={...r.config.project,name:d??r.config.project?.name??`app`,...g?{scope:g}:{}},b=y(n.var),C=new Set(n.action??[]);if(c){for(let e of _.variables??[]){if(e.name===`name`||e.name===`scope`||b[e.name]!==void 0)continue;let t=e.prompt??e.name;if(e.options&&e.options.length>0){let n=await m({message:t,options:e.options.map(e=>({value:e,label:e})),...e.default?{initialValue:e.default}:{}});if(p(n))return S();b[e.name]=String(n)}else{let n=await h({message:t,...e.default?{placeholder:e.default,defaultValue:e.default}:{}});if(p(n))return S();let r=String(n??``);b[e.name]=r||(e.default??``)}}for(let e of _.actions??[]){if(C.has(e.id))continue;let t=await f({message:e.prompt??`Run ${e.id}?`,initialValue:e.default??!1});if(p(t))return S();t&&C.add(e.id)}}let w=await l(_,{cwd:i,tokens:v,variables:b,actions:[...C]});if(n.json)t.raw(JSON.stringify({template:_.id,out:i,...w},null,4));else if(_.source.type===`vite-plus`||_.source.type===`npm`||_.source.type===`git`)w.code&&(process.exitCode=w.code);else for(let e of w.written)t.info(`Created ${e}`)}}]}export{C as templateCommands};
@@ -1 +1 @@
1
- import{default as e}from"node:path";import{pathToFileURL as t}from"node:url";import.meta.url;import{spawnCross as n}from"../../shared/src/run.js";import{applyReplacements as r}from"../../shared/src/template.js";import"../../shared/src/index.js";import{createMonorepoRootFiles as i,createRootProjectFiles as a,createWorkspaceFiles as o,writeGeneratedFiles as s}from"../../project/src/typescript.js";import"../../project/src/index.js";import{runVp as c}from"../../vite/src/vp.js";import"../../vite/src/index.js";import{readTemplateManifest as l}from"./local.js";import u from"node:fs";import{spawn as d}from"node:child_process";const f=/* @__PURE__ */ new Set([`node_modules`,`.git`,`dist`,`.turbo`,`.dbx`,`.DS_Store`,`template.json`]);function p(e){return{...e.tokens,...e.variables}}function m(e,t=!0){return e==null||e===``?t:typeof e==`boolean`?e:![`no`,`false`,`0`,`off`].includes(String(e).toLowerCase())}function h(e,t){let n=p(t),r=String(n.name??`app`),c=n.scope?String(n.scope):void 0,l=m(n.includeConfig),u=n.packageManager?String(n.packageManager):void 0,d=[];switch(e.builtin){case`monorepo-root`:{let e=c??`@${r}`;d.push(...i({name:r,scope:e,type:`monorepo-root`,includeConfig:l,...u?{packageManager:u}:{},workspaces:[]}));break}case`monorepo-example`:{let e=c??`@${r}`;d.push(...i({name:r,scope:e,type:`monorepo-root`,includeConfig:l,...u?{packageManager:u}:{},workspaces:[{alias:`core`,dir:`packages/core`}]})),d.push(...o({name:r,scope:e,wsName:`core`,wsDir:`packages/core`,type:`monorepo-workspace`,includeConfig:l,useCatalog:!0,...u?{packageManager:u}:{}}).map(e=>({...e,path:`packages/core/${e.path}`})));break}case`monorepo-workspace`:{let e=`packages/${r}`;d.push(...o({name:r,...c?{scope:c}:{},wsName:r,wsDir:e,type:`monorepo-workspace`,includeConfig:l,...u?{packageManager:u}:{}}).map(t=>({...t,path:`${e}/${t.path}`})));break}case`folder`:d.push({path:`README.md`,content:`# ${r}\n`});break;default:d.push(...a({name:r,...c?{scope:c}:{},type:`project`,includeConfig:l,...u?{packageManager:u}:{}}))}return{written:s(d,t.cwd)}}function g(t,n){let i=t.dir;if(!i)throw Error(`Local template "${t.id}" has no directory.`);let a=l(i),o=/* @__PURE__ */ new Set([...f,...a?.ignore??[]]);for(let e of t.actions??[])e.module&&o.add(e.module.replace(/\\/g,`/`));let s=t.tokens?.open??`{{`,c=t.tokens?.close??`}}`,d=p(n),m=n.overwrite!==!1,h=[],g=t=>{let a=e.join(i,t);for(let l of u.readdirSync(a,{withFileTypes:!0})){if(o.has(l.name))continue;let a=t?`${t}/${l.name}`:l.name;if(l.isDirectory()){g(a);continue}let f=e.join(n.cwd,r(a,d,{open:s,close:c}));if(!m&&u.existsSync(f))continue;u.mkdirSync(e.dirname(f),{recursive:!0});let p=u.readFileSync(e.join(i,a),`utf-8`);u.writeFileSync(f,r(p,d,{open:s,close:c}),`utf-8`),h.push(f)}};return g(``),{written:h}}async function _(e,t){let n=e.source.subdir?`${e.source.locate}/${e.source.subdir}`:e.source.locate,r=[`create`];n&&r.push(n);let i=t.tokens?.name;return i&&r.push(String(i)),{written:[],code:(await c(r,{cwd:t.cwd})).code}}function v(e,t,r){return new Promise(i=>{let a;try{a=n(e,t,{cwd:r,stdio:`inherit`})}catch{i(1);return}a.on(`error`,()=>i(1)),a.on(`exit`,e=>i(e??0))})}function y(e,t){return new Promise(n=>{let r=d(e,{cwd:t,stdio:`inherit`,shell:!0});r.on(`error`,()=>n(1)),r.on(`exit`,e=>n(e??0))})}function b(e,t){let n=p(t),r={};for(let[e,n]of Object.entries(t.variables??{}))r[e]=n==null?``:String(n);return{cwd:t.cwd,...e.dir?{templateDir:e.dir}:{},tokens:n,variables:r,packageManager:String(n.packageManager??`yarn`),log:e=>console.log(e)}}async function x(n,r,i,a){if(r.builtin===`install`){a.log(`Installing dependencies with ${a.packageManager}…`);let e=await v(a.packageManager,[`install`],i.cwd);if(e!==0)throw Error(`\`${a.packageManager} install\` failed with exit code ${e}.`)}else if(r.module){let o=n.dir??i.cwd,s=e.resolve(o,r.module);if(!u.existsSync(s))throw Error(`template action module not found: ${r.module}`);let c=await import(t(s).href),l=c.default??c.run;if(typeof l!=`function`)throw Error(`template action module "${r.module}" does not export a function.`);await l(a)}else if(r.command){let e=await y(r.command,i.cwd);if(e!==0)throw Error(`template command failed with exit code ${e}: ${r.command}`)}else throw Error(`template action "${r.id}" declares no builtin, module or command.`)}async function S(e,t){let n=new Set(t.actions??[]),r=[];for(let i of e.actions??[])n.has(i.id)&&(await x(e,i,t,b(e,t)),r.push(i.id));return r}async function C(e,t){if(e.builtin){let n=h(e,t),r=await S(e,t);return r.length>0?{...n,ranActions:r}:n}switch(e.source.type){case`local`:{let n=g(e,t),r=await S(e,t);return r.length>0?{...n,ranActions:r}:n}case`vite-plus`:case`npm`:case`git`:return _(e,t);default:throw Error(`Template "${e.id}" has no scaffold handler.`)}}export{C as scaffoldTemplate};
1
+ import{default as e}from"node:path";import{pathToFileURL as t}from"node:url";import.meta.url;import{spawnCross as n}from"../../shared/src/run.js";import{applyReplacements as r}from"../../shared/src/template.js";import"../../shared/src/index.js";import{createMonorepoRootFiles as i,createRootProjectFiles as a,createWorkspaceFiles as o,writeGeneratedFiles as s}from"../../project/src/typescript.js";import"../../project/src/index.js";import{runVp as c}from"../../vite/src/vp.js";import"../../vite/src/index.js";import{readTemplateManifest as l}from"./local.js";import u from"node:fs";import{spawn as d}from"node:child_process";const f=/* @__PURE__ */ new Set([`node_modules`,`.git`,`dist`,`.turbo`,`.dbx`,`.DS_Store`,`template.json`]);function p(e){return{...e.tokens,...e.variables}}function m(e,t=!0){return e==null||e===``?t:typeof e==`boolean`?e:![`no`,`false`,`0`,`off`].includes(String(e).toLowerCase())}function h(e,t){let n=p(t),r=String(n.name??`app`),c=n.scope?String(n.scope):void 0,l=m(n.includeConfig),u=n.packageManager?String(n.packageManager):void 0,d=[];switch(e.builtin){case`monorepo-root`:{let e=c??`@${r}`;d.push(...i({name:r,scope:e,type:`monorepo-root`,includeConfig:l,...u?{packageManager:u}:{},workspaces:[]}));break}case`monorepo-example`:{let e=c??`@${r}`;d.push(...i({name:r,scope:e,type:`monorepo-root`,includeConfig:l,...u?{packageManager:u}:{},workspaces:[{alias:`core`,dir:`packages/core`}]})),d.push(...o({name:r,scope:e,wsName:`core`,wsDir:`packages/core`,type:`monorepo-workspace`,includeConfig:l,useCatalog:!0,...u?{packageManager:u}:{}}).map(e=>({...e,path:`packages/core/${e.path}`})));break}case`monorepo-workspace`:{let e=`packages/${r}`;d.push(...o({name:r,...c?{scope:c}:{},wsName:r,wsDir:e,type:`monorepo-workspace`,includeConfig:l,...u?{packageManager:u}:{}}).map(t=>({...t,path:`${e}/${t.path}`})));break}case`folder`:d.push({path:`README.md`,content:`# ${r}\n`});break;default:d.push(...a({name:r,...c?{scope:c}:{},type:`project`,includeConfig:l,...u?{packageManager:u}:{}}))}return{written:s(d,t.cwd)}}function g(t,n){let i=t.dir;if(!i)throw Error(`Local template "${t.id}" has no directory.`);let a=l(i),o=/* @__PURE__ */ new Set([...f,...a?.ignore??[]]);for(let e of t.actions??[])e.module&&o.add(e.module.replace(/\\/g,`/`));let s=t.tokens?.open??`{{`,c=t.tokens?.close??`}}`,d=p(n),m=n.overwrite!==!1,h=[],g=t=>{let a=e.join(i,t);for(let l of u.readdirSync(a,{withFileTypes:!0})){if(o.has(l.name))continue;let a=t?`${t}/${l.name}`:l.name;if(l.isDirectory()){g(a);continue}let f=e.join(n.cwd,r(a,d,{open:s,close:c}));if(!m&&u.existsSync(f))continue;let p=u.readFileSync(e.join(i,a),`utf-8`);h.push({target:f,content:r(p,d,{open:s,close:c})})}};g(``);let _=[];for(let t of h)u.mkdirSync(e.dirname(t.target),{recursive:!0}),u.writeFileSync(t.target,t.content,`utf-8`),_.push(t.target);return{written:_}}async function _(e,t){let n=e.source.subdir?`${e.source.locate}/${e.source.subdir}`:e.source.locate,r=[`create`];n&&r.push(n);let i=t.tokens?.name;return i&&r.push(String(i)),{written:[],code:(await c(r,{cwd:t.cwd})).code}}function v(e,t,r){return new Promise(i=>{let a;try{a=n(e,t,{cwd:r,stdio:`inherit`})}catch{i(1);return}a.on(`error`,()=>i(1)),a.on(`exit`,e=>i(e??0))})}function y(e,t){return new Promise(n=>{let r=d(e,{cwd:t,stdio:`inherit`,shell:!0});r.on(`error`,()=>n(1)),r.on(`exit`,e=>n(e??0))})}function b(e,t){let n=p(t),r={};for(let[e,n]of Object.entries(t.variables??{}))r[e]=n==null?``:String(n);return{cwd:t.cwd,...e.dir?{templateDir:e.dir}:{},tokens:n,variables:r,packageManager:String(n.packageManager??`yarn`),log:e=>console.log(e)}}async function x(n,r,i,a){if(r.builtin===`install`){a.log(`Installing dependencies with ${a.packageManager}…`);let e=await v(a.packageManager,[`install`],i.cwd);if(e!==0)throw Error(`\`${a.packageManager} install\` failed with exit code ${e}.`)}else if(r.module){let o=n.dir??i.cwd,s=e.resolve(o,r.module);if(!u.existsSync(s))throw Error(`template action module not found: ${r.module}`);let c=await import(t(s).href),l=c.default??c.run;if(typeof l!=`function`)throw Error(`template action module "${r.module}" does not export a function.`);await l(a)}else if(r.command){let e=await y(r.command,i.cwd);if(e!==0)throw Error(`template command failed with exit code ${e}: ${r.command}`)}else throw Error(`template action "${r.id}" declares no builtin, module or command.`)}async function S(e,t){let n=new Set(t.actions??[]),r=[];for(let i of e.actions??[])n.has(i.id)&&(await x(e,i,t,b(e,t)),r.push(i.id));return r}async function C(e,t){if(e.builtin){let n=h(e,t),r=await S(e,t);return r.length>0?{...n,ranActions:r}:n}switch(e.source.type){case`local`:{let n=g(e,t),r=await S(e,t);return r.length>0?{...n,ranActions:r}:n}case`vite-plus`:case`npm`:case`git`:return _(e,t);default:throw Error(`Template "${e.id}" has no scaffold handler.`)}}export{C as scaffoldTemplate};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deltabox/toolchain",
3
- "version": "1.10.4",
4
- "description": "All-in-one, config-driven dev toolchain: scaffold projects, generate and manage configs, tsconfigs, syncing, changelogs and CI, run Vite+, and migrations.",
3
+ "version": "1.11.3",
4
+ "description": "All-in-one development toolchain that let's you scaffold projects, generate and manage configs, tsconfigs, custom templates, syncing, changelogs, CI, Vite+, migrations, and more.",
5
5
  "homepage": "https://toolchain.deltabox.studio",
6
6
  "author": "Victor Sango <victor@deltabox.studio>",
7
7
  "repository": {
@@ -79,17 +79,17 @@
79
79
  "@commitlint/cli": "^21.2.2",
80
80
  "@commitlint/config-conventional": "^21.2.2",
81
81
  "@deltabox/toolchain": "portal:.",
82
- "@deltabox/toolchain-biome": "1.10.4",
83
- "@deltabox/toolchain-changelog": "1.10.4",
84
- "@deltabox/toolchain-config": "1.10.4",
85
- "@deltabox/toolchain-core": "1.10.4",
86
- "@deltabox/toolchain-migrate": "1.10.4",
87
- "@deltabox/toolchain-project": "1.10.4",
88
- "@deltabox/toolchain-shared": "1.10.4",
89
- "@deltabox/toolchain-template": "1.10.4",
90
- "@deltabox/toolchain-tsconfig": "1.10.4",
91
- "@deltabox/toolchain-tsdown": "1.10.4",
92
- "@deltabox/toolchain-vite": "1.10.4",
82
+ "@deltabox/toolchain-biome": "1.11.3",
83
+ "@deltabox/toolchain-changelog": "1.11.3",
84
+ "@deltabox/toolchain-config": "1.11.3",
85
+ "@deltabox/toolchain-core": "1.11.3",
86
+ "@deltabox/toolchain-migrate": "1.11.3",
87
+ "@deltabox/toolchain-project": "1.11.3",
88
+ "@deltabox/toolchain-shared": "1.11.3",
89
+ "@deltabox/toolchain-template": "1.11.3",
90
+ "@deltabox/toolchain-tsconfig": "1.11.3",
91
+ "@deltabox/toolchain-tsdown": "1.11.3",
92
+ "@deltabox/toolchain-vite": "1.11.3",
93
93
  "@types/node": "^22.10.2",
94
94
  "@typescript/native-preview": "7.0.0-dev.20260707.2",
95
95
  "cross-env": "^10.1.0",