Skip to content

Commit 84fc92d

Browse files
Merge pull request #17 from BootNodeDev/refactor/remove-canton
feat!: remove the canton stack
2 parents e8c4662 + a0a3ad4 commit 84fc92d

42 files changed

Lines changed: 815 additions & 2538 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎AGENTS.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
# Agent Configuration
1+
# Agent configuration
22

33
This project's agent configuration lives in [`CLAUDE.md`](./CLAUDE.md).
44

5-
Claude Code reads `CLAUDE.md` natively. If your agent reads `AGENTS.md` (Cursor, Windsurf, etc.), load rules from `CLAUDE.md` -- it is the single source of truth for this project's conventions, stack, and working rules.
5+
Claude Code reads `CLAUDE.md` natively. If your agent reads `AGENTS.md` (Cursor, Windsurf, etc.), load rules from `CLAUDE.md`. It is the single source of truth for this project's conventions, stack, and working rules.

‎CLAUDE.md‎

Lines changed: 75 additions & 69 deletions
Large diffs are not rendered by default.

‎architecture.md‎

Lines changed: 106 additions & 159 deletions
Large diffs are not rendered by default.

‎demo.svg‎

Lines changed: 1 addition & 1 deletion
Loading

‎readme.md‎

Lines changed: 14 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
# dAppBooster installer
22

3+
Scaffolds a new project from [dAppBooster](https://dappbooster.dev/), our blockchain boilerplate for EVM projects.
4+
5+
Looking for Canton? It has its own installer: [`create-canton-dappbooster`](https://github.com/BootNodeDev/canton-dappbooster/tree/main/canton-create).
6+
37
## Requirements
48

59
- Node >= 24.15.0
610
- pnpm
711

8-
Requirements for each dAppBooster stack might differ, check the [EVM](https://github.com/bootnodedev/dappbooster/) and [Canton](https://github.com/BootNodeDev/canton-dappbooster) repos for more info.
9-
1012
## Interactive mode
1113

1214
<img src="./demo.svg" width="600" height="355" alt="Terminal recording of the dAppBooster wizard.">
@@ -15,44 +17,26 @@ Requirements for each dAppBooster stack might differ, check the [EVM](https://gi
1517
pnpm dlx dappbooster
1618
```
1719

18-
The wizard will guide you through the different choices available: project name, stack, features, etc.
20+
The wizard guides you through the choices: project name, installation mode and features.
1921

20-
Alternatively you can start with a pre-selected stack:
21-
22-
- **EVM:** our [blockchain boilerplate](https://dappbooster.dev/) built to quickly get you started with your next EVM project.
23-
- **Canton:** our [local development stack](https://www.dappbooster.cc/) for the Canton network.
24-
25-
```shell
26-
pnpm dlx dappbooster --evm # EVM stack
27-
pnpm dlx dappbooster --canton # Canton stack
28-
```
29-
30-
## Non-interactive mode (Agents & CI)
22+
## Non-interactive mode (agents & CI)
3123

3224
| Flag | Purpose |
3325
|---|---|
34-
| `--canton` / `--evm` | Pick the stack (mutually exclusive shortcuts) |
35-
| `--stack <evm\|canton>` | Pick the stack by name (useful when scripting) |
3626
| `--name <name>` | Project directory name (`/^[a-zA-Z0-9_][a-zA-Z0-9_-]*$/`, so no leading dash) |
37-
| `--mode <full\|custom>` | `full` installs every feature; `custom` needs `--features`. Only for a stack whose `modes` list is not empty |
27+
| `--mode <full\|custom>` | `full` installs every feature; `custom` needs `--features` |
3828
| `--features <a,b,c>` | Comma-separated feature keys (custom mode only) |
39-
| `--info` | Print stacks and features as JSON and exit. Filter with `--stack`, `--canton` or `--evm` |
40-
| `--ni`, `--non-interactive` | Force non-interactive mode. Auto-enabled when stdout is not a TTY |
29+
| `--info` | Print the features as JSON and exit |
30+
| `--ni`, `--non-interactive` | Force non-interactive mode. It turns on by itself when stdout is not a TTY |
4131
| `--help` | Show the usage text |
4232
| `--version` | Show the installer version |
4333

44-
Example: discover stacks and features first, then install.
34+
Example: discover the features first, then install.
4535

4636
```shell
47-
pnpm dlx dappbooster --info # all stacks + features as JSON
48-
pnpm dlx dappbooster --info --stack canton # filter to one stack (or --info --canton)
49-
```
50-
51-
### EVM stack
52-
53-
```shell
54-
pnpm dlx dappbooster --evm --ni --name my_dapp --mode full
55-
pnpm dlx dappbooster --evm --ni --name my_dapp --mode custom --features demo,subgraph
37+
pnpm dlx dappbooster --info
38+
pnpm dlx dappbooster --ni --name my_dapp --mode full
39+
pnpm dlx dappbooster --ni --name my_dapp --mode custom --features demo,subgraph
5640
```
5741

5842
**Feature options:**
@@ -65,12 +49,6 @@ pnpm dlx dappbooster --evm --ni --name my_dapp --mode custom --features demo,sub
6549
| Vocs | `vocs` | ✓ | Vocs documentation site |
6650
| Husky | `husky` | ✓ | Git hooks with Husky, lint-staged, and commitlint |
6751

68-
### Canton stack
69-
70-
```shell
71-
pnpm dlx dappbooster --canton --ni --name my_canton_dapp
72-
```
73-
7452
## Installer development
7553

7654
```shell
@@ -81,4 +59,4 @@ corepack enable
8159
pnpm i
8260
pnpm build
8361
node dist/cli.js
84-
```
62+
```

‎scripts/record-demo.py‎

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,10 +129,6 @@ def type_out(text: str, per_char: float = 0.085) -> None:
129129
pump(0.6)
130130
send("\r")
131131

132-
expect(r"Which stack", 15, "stack prompt")
133-
pump(1.3)
134-
send("\r") # EVM is first
135-
136132
expect(r"Choose installation type", 15, "mode prompt")
137133
pump(1.3)
138134
send("\r") # Full is first

‎source/__tests__/config.test.ts‎

Lines changed: 8 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,18 @@
11
import { describe, expect, it } from 'vitest'
2-
import {
3-
getDefaultFeatureNames,
4-
getFeatureNames,
5-
getInstallationModes,
6-
stackDefinitions,
7-
} from '../stacks/index.js'
2+
import { defaultFeatureNames, featureDefinitions, featureNames } from '../config.js'
83

9-
describe('getDefaultFeatureNames', () => {
4+
describe('defaultFeatureNames', () => {
105
it('returns only features whose default flag is true, in config order', () => {
11-
for (const stack of ['evm', 'canton'] as const) {
12-
const expected = Object.entries(stackDefinitions[stack].features)
13-
.filter(([, def]) => def.default)
14-
.map(([name]) => name)
15-
expect(getDefaultFeatureNames(stack)).toEqual(expected)
16-
}
6+
const expected = Object.entries(featureDefinitions)
7+
.filter(([, def]) => def.default)
8+
.map(([name]) => name)
9+
expect(defaultFeatureNames).toEqual(expected)
1710
})
1811

1912
it('is a subset of all feature names', () => {
20-
const all = new Set(getFeatureNames('evm'))
21-
for (const name of getDefaultFeatureNames('evm')) {
13+
const all = new Set(featureNames)
14+
for (const name of defaultFeatureNames) {
2215
expect(all.has(name)).toBe(true)
2316
}
2417
})
2518
})
26-
27-
describe('getInstallationModes', () => {
28-
it('offers full/custom for evm, where every feature is on by default', () => {
29-
expect(getInstallationModes('evm')).toEqual(['full', 'custom'])
30-
})
31-
32-
it('offers none for canton, which has no features to choose', () => {
33-
expect(getInstallationModes('canton')).toEqual([])
34-
})
35-
})
36-
37-
describe('canton stack config', () => {
38-
it('clones canton-dappbooster at its latest tag with pnpm', () => {
39-
expect(stackDefinitions.canton.repoUrl).toBe(
40-
'https://github.com/BootNodeDev/canton-dappbooster.git',
41-
)
42-
expect(stackDefinitions.canton).not.toHaveProperty('ref')
43-
expect(stackDefinitions.canton.packageManager).toBe('pnpm')
44-
})
45-
46-
it('copies a single root env file', () => {
47-
expect(stackDefinitions.canton.envFiles).toEqual([{ from: '.env.example', to: '.env' }])
48-
})
49-
50-
it('declares the Node floor the scaffolded project needs', () => {
51-
expect(stackDefinitions.canton.minNodeVersion).toBe('24.15.0')
52-
})
53-
54-
it('deletes the three library folders, so pnpm resolves them from npm', () => {
55-
for (const library of ['canton-connect', 'canton-dappbooster', 'canton-theme']) {
56-
expect(stackDefinitions.canton.prepare.paths).toContain(library)
57-
}
58-
})
59-
})

‎source/__tests__/info.test.ts‎

Lines changed: 23 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -1,144 +1,58 @@
11
import { describe, expect, it } from 'vitest'
2+
import { featureEntries, featureNames } from '../config.js'
23
import { getInfoOutput } from '../info.js'
3-
import {
4-
getFeatureEntries,
5-
getInstallationModes,
6-
stackDefinitions,
7-
stackNames,
8-
} from '../stacks/index.js'
94

10-
describe('getInfoOutput — no filter', () => {
5+
describe('getInfoOutput', () => {
116
it('returns valid JSON', () => {
127
expect(() => JSON.parse(getInfoOutput())).not.toThrow()
138
})
149

15-
it('has stacks and modes top-level keys', () => {
10+
it('has features and modes top-level keys', () => {
1611
const output = JSON.parse(getInfoOutput())
17-
expect(output).toHaveProperty('stacks')
18-
expect(output).toHaveProperty('modes')
12+
expect(Object.keys(output)).toEqual(['features', 'modes'])
1913
})
2014

21-
it('includes every defined stack', () => {
15+
it('lists every feature', () => {
2216
const output = JSON.parse(getInfoOutput())
23-
expect(Object.keys(output.stacks)).toEqual(stackNames)
24-
})
25-
26-
it('each stack lists its features', () => {
27-
const output = JSON.parse(getInfoOutput())
28-
29-
for (const stack of stackNames) {
30-
const expected = Object.keys(stackDefinitions[stack].features)
31-
expect(Object.keys(output.stacks[stack].features)).toEqual(expected)
32-
}
33-
})
34-
35-
it('each stack reports its package manager and label', () => {
36-
const output = JSON.parse(getInfoOutput())
37-
38-
for (const stack of stackNames) {
39-
expect(output.stacks[stack].label).toBe(stackDefinitions[stack].label)
40-
expect(output.stacks[stack].packageManager).toBe(stackDefinitions[stack].packageManager)
41-
}
17+
expect(Object.keys(output.features)).toEqual(featureNames)
4218
})
4319

4420
it('each feature has description and default', () => {
4521
const output = JSON.parse(getInfoOutput())
4622

47-
for (const stack of stackNames) {
48-
for (const name of Object.keys(stackDefinitions[stack].features)) {
49-
const feature = output.stacks[stack].features[name]
50-
expect(feature).toHaveProperty('description')
51-
expect(feature).toHaveProperty('default')
52-
expect(typeof feature.description).toBe('string')
53-
expect(typeof feature.default).toBe('boolean')
54-
}
23+
for (const name of featureNames) {
24+
const feature = output.features[name]
25+
expect(typeof feature.description).toBe('string')
26+
expect(typeof feature.default).toBe('boolean')
5527
}
5628
})
5729

5830
it('includes postInstall only for features that declare it', () => {
5931
const output = JSON.parse(getInfoOutput())
6032

61-
for (const stack of stackNames) {
62-
for (const [name, def] of getFeatureEntries(stack)) {
63-
const feature = output.stacks[stack].features[name]
64-
if (def.postInstall) {
65-
expect(feature.postInstall).toEqual(def.postInstall)
66-
} else {
67-
expect(feature).not.toHaveProperty('postInstall')
68-
}
69-
}
70-
}
71-
})
72-
73-
it('surfaces requires only for features that declare a dependency', () => {
74-
const output = JSON.parse(getInfoOutput())
75-
76-
for (const stack of stackNames) {
77-
for (const [name, def] of getFeatureEntries(stack)) {
78-
const feature = output.stacks[stack].features[name]
79-
if (def.requires) {
80-
expect(feature.requires).toEqual(def.requires)
81-
} else {
82-
expect(feature).not.toHaveProperty('requires')
83-
}
33+
for (const [name, def] of featureEntries) {
34+
const feature = output.features[name]
35+
if (def.postInstall) {
36+
expect(feature.postInstall).toEqual(def.postInstall)
37+
} else {
38+
expect(feature).not.toHaveProperty('postInstall')
8439
}
8540
}
8641
})
8742

8843
it('does not leak label, packages, or paths into feature output', () => {
8944
const output = JSON.parse(getInfoOutput())
9045

91-
for (const stack of stackNames) {
92-
for (const name of Object.keys(stackDefinitions[stack].features)) {
93-
const feature = output.stacks[stack].features[name]
94-
expect(feature).not.toHaveProperty('label')
95-
expect(feature).not.toHaveProperty('packages')
96-
expect(feature).not.toHaveProperty('paths')
97-
}
98-
}
99-
})
100-
101-
it('modes contains full and custom', () => {
102-
const output = JSON.parse(getInfoOutput())
103-
expect(output.modes).toHaveProperty('full')
104-
expect(output.modes).toHaveProperty('custom')
105-
})
106-
107-
it('modes documents the default mode', () => {
108-
const output = JSON.parse(getInfoOutput())
109-
expect(output.modes).toHaveProperty('default')
110-
})
111-
112-
it('lists the modes each stack accepts, so agents never send a rejected one', () => {
113-
const output = JSON.parse(getInfoOutput())
114-
115-
for (const stack of stackNames) {
116-
expect(output.stacks[stack].modes).toEqual(getInstallationModes(stack))
46+
for (const name of featureNames) {
47+
const feature = output.features[name]
48+
expect(feature).not.toHaveProperty('label')
49+
expect(feature).not.toHaveProperty('packages')
50+
expect(feature).not.toHaveProperty('paths')
11751
}
118-
119-
expect(output.stacks.evm.modes).not.toContain('default')
12052
})
12153

122-
it('reports canton as a stack with no features and no modes', () => {
54+
it('documents exactly the full and custom modes', () => {
12355
const output = JSON.parse(getInfoOutput())
124-
125-
expect(output.stacks.canton.features).toEqual({})
126-
expect(output.stacks.canton.modes).toEqual([])
127-
})
128-
})
129-
130-
describe('getInfoOutput — filter by stack', () => {
131-
it('returns only the requested stack', () => {
132-
const output = JSON.parse(getInfoOutput('canton'))
133-
expect(Object.keys(output.stacks)).toEqual(['canton'])
134-
})
135-
136-
it('throws on unknown stack filter', () => {
137-
expect(() => getInfoOutput('does-not-exist')).toThrow(/Unknown stack 'does-not-exist'/)
138-
})
139-
140-
it('filtering by evm hides canton', () => {
141-
const output = JSON.parse(getInfoOutput('evm'))
142-
expect(Object.keys(output.stacks)).toEqual(['evm'])
56+
expect(Object.keys(output.modes)).toEqual(['full', 'custom'])
14357
})
14458
})

0 commit comments

Comments
 (0)