dappbooster 3.2.0 → 3.3.0
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/dist/app.js +2 -2
- package/dist/cli.js +8 -7
- package/dist/components/steps/FileCleanup.js +5 -3
- package/dist/components/steps/Install/Install.js +5 -3
- package/dist/components/steps/InstallationMode.d.ts +2 -0
- package/dist/components/steps/InstallationMode.js +8 -11
- package/dist/components/steps/OptionalPackages.js +7 -2
- package/dist/components/steps/PostInstall.js +7 -8
- package/dist/constants/config.d.ts +4 -0
- package/dist/constants/config.js +28 -21
- package/dist/info.js +1 -0
- package/dist/nonInteractive.js +14 -6
- package/dist/operations/cleanupFiles.js +54 -44
- package/dist/types/types.d.ts +1 -1
- package/dist/utils/utils.d.ts +4 -2
- package/dist/utils/utils.js +20 -4
- package/package.json +2 -4
- package/readme.md +29 -25
package/dist/app.js
CHANGED
|
@@ -35,7 +35,7 @@ const App = ({ preselectedStack }) => {
|
|
|
35
35
|
setCurrentStep(1);
|
|
36
36
|
setAttempt((prev) => prev + 1);
|
|
37
37
|
}, [preselectedStack]);
|
|
38
|
-
const skipFeatures = setupType?.value === 'full';
|
|
38
|
+
const skipFeatures = setupType?.value === 'full' || setupType?.value === 'default';
|
|
39
39
|
const mode = setupType?.value ?? 'full';
|
|
40
40
|
const planFeatures = selectedFeatures?.map((item) => item.value) ?? [];
|
|
41
41
|
const planSummary = stack === undefined ? '' : describeInstallPlan(stack, projectName, mode, planFeatures);
|
|
@@ -52,7 +52,7 @@ const App = ({ preselectedStack }) => {
|
|
|
52
52
|
return orderedSteps;
|
|
53
53
|
}
|
|
54
54
|
// --- remaining questions (need the stack) ---
|
|
55
|
-
orderedSteps.push(_jsx(InstallationMode, { onCompletion: finishStep, onSelect: onSelectSetupType }, `installation-mode-${attempt}`));
|
|
55
|
+
orderedSteps.push(_jsx(InstallationMode, { stack: stack, onCompletion: finishStep, onSelect: onSelectSetupType }, `installation-mode-${attempt}`));
|
|
56
56
|
orderedSteps.push(_jsx(OptionalPackages, { stack: stack, onCompletion: finishStep, onSubmit: onSelectSelectedFeatures, skip: skipFeatures }, `optional-packages-${attempt}`));
|
|
57
57
|
orderedSteps.push(_jsx(Confirmation, { summary: planSummary, onConfirm: finishStep, onCancel: restart }, `confirmation-${attempt}`));
|
|
58
58
|
// --- operations (disk writes) ---
|
package/dist/cli.js
CHANGED
|
@@ -16,7 +16,7 @@ const cli = meow(`
|
|
|
16
16
|
|
|
17
17
|
Common options
|
|
18
18
|
--name <string> Project name (alphanumeric, underscores)
|
|
19
|
-
--mode <full|custom>
|
|
19
|
+
--mode <full|default|custom> Installation mode (default mode is Canton-only)
|
|
20
20
|
--features <list> Comma-separated features (with --mode=custom)
|
|
21
21
|
EVM:
|
|
22
22
|
demo Component demos and example pages
|
|
@@ -25,12 +25,13 @@ const cli = meow(`
|
|
|
25
25
|
vocs Vocs documentation site
|
|
26
26
|
husky Git hooks (Husky, lint-staged, commitlint)
|
|
27
27
|
Canton:
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
github GitHub issue/PR templates and workflows
|
|
29
|
+
precommit Pre-commit hooks (Husky, lint-staged, commitlint)
|
|
30
30
|
carpincho Carpincho browser-extension wallet
|
|
31
31
|
llm LLM and agent artifacts (.claude, AGENTS.md, …)
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
Canton's default mode keeps carpincho + llm and removes
|
|
33
|
+
github + precommit; full keeps everything; custom lets you
|
|
34
|
+
pick (github + precommit start unchecked).
|
|
34
35
|
--non-interactive, --ni Run without prompts (auto-enabled when not a TTY)
|
|
35
36
|
--info Output feature metadata as JSON (filter with --stack)
|
|
36
37
|
--help Show this help
|
|
@@ -52,8 +53,8 @@ const cli = meow(`
|
|
|
52
53
|
Interactive (prompts for stack and options):
|
|
53
54
|
$ dappbooster
|
|
54
55
|
|
|
55
|
-
Canton stack,
|
|
56
|
-
$ dappbooster --canton --ni --name my_dapp --mode
|
|
56
|
+
Canton stack, recommended install (non-interactive):
|
|
57
|
+
$ dappbooster --canton --ni --name my_dapp --mode default
|
|
57
58
|
|
|
58
59
|
EVM stack, custom install:
|
|
59
60
|
$ dappbooster --evm --ni --name my_dapp --mode custom --features demo,subgraph
|
|
@@ -3,7 +3,7 @@ import { Text } from 'ink';
|
|
|
3
3
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
4
4
|
import { cleanupFiles } from '../../operations/index.js';
|
|
5
5
|
import { completeInstall } from '../../operations/installGuard.js';
|
|
6
|
-
import { deriveStepDisplay, getProjectFolder } from '../../utils/utils.js';
|
|
6
|
+
import { deriveStepDisplay, getProjectFolder, resolveModeFeatures } from '../../utils/utils.js';
|
|
7
7
|
import Divider from '../Divider.js';
|
|
8
8
|
const FileCleanup = ({ stack, onCompletion, installationConfig, projectName }) => {
|
|
9
9
|
const { installationType, selectedFeatures } = installationConfig;
|
|
@@ -15,8 +15,10 @@ const FileCleanup = ({ stack, onCompletion, installationConfig, projectName }) =
|
|
|
15
15
|
setSteps((prev) => [...prev, step]);
|
|
16
16
|
}, []);
|
|
17
17
|
useEffect(() => {
|
|
18
|
-
const
|
|
19
|
-
|
|
18
|
+
const mode = installationType ?? 'full';
|
|
19
|
+
const selectedNames = selectedFeatures?.map((f) => f.value) ?? [];
|
|
20
|
+
const features = resolveModeFeatures(stack, mode, selectedNames);
|
|
21
|
+
cleanupFiles(stack, projectFolder, mode, features, handleProgress)
|
|
20
22
|
.then(() => {
|
|
21
23
|
// Scaffold is complete — an interrupt from here on must not delete the finished project.
|
|
22
24
|
completeInstall();
|
|
@@ -3,7 +3,7 @@ import { Text } from 'ink';
|
|
|
3
3
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
4
4
|
import { createEnvFile } from '../../../operations/createEnvFile.js';
|
|
5
5
|
import { installPackages } from '../../../operations/installPackages.js';
|
|
6
|
-
import { deriveStepDisplay, getProjectFolder } from '../../../utils/utils.js';
|
|
6
|
+
import { deriveStepDisplay, getProjectFolder, resolveModeFeatures } from '../../../utils/utils.js';
|
|
7
7
|
import Divider from '../../Divider.js';
|
|
8
8
|
const Install = ({ stack, projectName, onCompletion, installationConfig }) => {
|
|
9
9
|
const { installationType, selectedFeatures } = installationConfig;
|
|
@@ -18,11 +18,13 @@ const Install = ({ stack, projectName, onCompletion, installationConfig }) => {
|
|
|
18
18
|
setSteps((prev) => [...prev, step]);
|
|
19
19
|
}, []);
|
|
20
20
|
useEffect(() => {
|
|
21
|
-
const
|
|
21
|
+
const mode = installationType ?? 'full';
|
|
22
|
+
const selectedNames = selectedFeatures?.map((f) => f.value) ?? [];
|
|
23
|
+
const features = resolveModeFeatures(stack, mode, selectedNames);
|
|
22
24
|
const run = async () => {
|
|
23
25
|
handleProgress('Creating env files');
|
|
24
26
|
await createEnvFile(stack, projectFolder, features);
|
|
25
|
-
await installPackages(stack, projectFolder,
|
|
27
|
+
await installPackages(stack, projectFolder, mode, features, handleProgress);
|
|
26
28
|
};
|
|
27
29
|
run()
|
|
28
30
|
.then(() => {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { type FC } from 'react';
|
|
2
|
+
import { type Stack } from '../../constants/config.js';
|
|
2
3
|
import type { InstallationSelectItem } from '../../types/types.js';
|
|
3
4
|
interface Props {
|
|
5
|
+
stack: Stack;
|
|
4
6
|
onCompletion: () => void;
|
|
5
7
|
onSelect: (item: InstallationSelectItem) => void;
|
|
6
8
|
}
|
|
@@ -3,19 +3,16 @@ import figures from 'figures';
|
|
|
3
3
|
import { Text } from 'ink';
|
|
4
4
|
import SelectInput from 'ink-select-input';
|
|
5
5
|
import { useState } from 'react';
|
|
6
|
+
import { getInstallationModes } from '../../constants/config.js';
|
|
6
7
|
import Divider from '../Divider.js';
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
label: 'Custom',
|
|
14
|
-
value: 'custom',
|
|
15
|
-
},
|
|
16
|
-
];
|
|
17
|
-
const InstallationMode = ({ onCompletion, onSelect }) => {
|
|
8
|
+
const MODE_LABELS = {
|
|
9
|
+
default: 'Default (recommended)',
|
|
10
|
+
full: 'Full',
|
|
11
|
+
custom: 'Custom',
|
|
12
|
+
};
|
|
13
|
+
const InstallationMode = ({ stack, onCompletion, onSelect }) => {
|
|
18
14
|
const [selected, setSelected] = useState();
|
|
15
|
+
const installationTypeItems = getInstallationModes(stack).map((value) => ({ label: MODE_LABELS[value], value }));
|
|
19
16
|
const handleSelect = (item) => {
|
|
20
17
|
onSelect(item);
|
|
21
18
|
setSelected(item);
|
|
@@ -13,7 +13,12 @@ const OptionalPackages = ({ stack, onCompletion, onSubmit, skip = false }) => {
|
|
|
13
13
|
value: name,
|
|
14
14
|
}));
|
|
15
15
|
}, [stack]);
|
|
16
|
-
//
|
|
16
|
+
// Pre-check only default:true features (e.g. Canton's github/precommit start unchecked).
|
|
17
|
+
const defaultSelected = useMemo(() => {
|
|
18
|
+
const features = getStackConfig(stack).features;
|
|
19
|
+
return customPackages.filter((pkg) => features[pkg.value]?.default);
|
|
20
|
+
}, [stack, customPackages]);
|
|
21
|
+
// Keep the selection dependency-consistent as the user toggles (resolves any feature `requires`).
|
|
17
22
|
const transformSelection = useCallback((nextSelected, toggledItem, action) => {
|
|
18
23
|
const selectedValues = nextSelected.map((item) => item.value);
|
|
19
24
|
const resolved = applyFeatureToggle(stack, selectedValues, toggledItem.value, action);
|
|
@@ -38,6 +43,6 @@ const OptionalPackages = ({ stack, onCompletion, onSubmit, skip = false }) => {
|
|
|
38
43
|
if (submitted) {
|
|
39
44
|
return (_jsxs(Text, { children: ["Optional packages:", ' ', _jsx(Text, { bold: true, color: 'green', children: submitted.length > 0 ? submitted.map((item) => item.label).join(', ') : 'none' })] }));
|
|
40
45
|
}
|
|
41
|
-
return (_jsxs(_Fragment, { children: [_jsx(Text, { color: 'whiteBright', children: "Choose optional packages" }), _jsx(MultiSelect, { defaultSelected:
|
|
46
|
+
return (_jsxs(_Fragment, { children: [_jsx(Text, { color: 'whiteBright', children: "Choose optional packages" }), _jsx(MultiSelect, { defaultSelected: defaultSelected, focus: true, items: customPackages, onSubmit: onHandleSubmit, transformSelection: transformSelection })] }));
|
|
42
47
|
};
|
|
43
48
|
export default OptionalPackages;
|
|
@@ -3,21 +3,20 @@ import figures from 'figures';
|
|
|
3
3
|
import { Box, Text } from 'ink';
|
|
4
4
|
import Link from 'ink-link';
|
|
5
5
|
import { getStackConfig } from '../../constants/config.js';
|
|
6
|
-
import { isFeatureSelected } from '../../utils/utils.js';
|
|
6
|
+
import { isFeatureSelected, resolveModeFeatures } from '../../utils/utils.js';
|
|
7
7
|
import Divider from '../Divider.js';
|
|
8
8
|
const SubgraphWarningMessage = () => (_jsxs(Box, { flexDirection: 'column', rowGap: 1, children: [_jsx(Box, { alignItems: 'center', borderColor: 'yellow', borderStyle: 'bold', flexDirection: 'column', justifyContent: 'center', padding: 1, children: _jsxs(Text, { color: 'yellow', children: [figures.warning, figures.warning, " ", _jsx(Text, { bold: true, children: "WARNING:" }), " You ", _jsx(Text, { bold: true, children: "MUST" }), " finish the subgraph's configuration manually ", figures.warning, figures.warning] }) }), _jsx(Text, { color: 'whiteBright', children: "Follow these steps:" }), _jsxs(Box, { flexDirection: 'column', children: [_jsxs(Text, { children: ["1- Provide your own API key for ", _jsx(Text, { color: 'gray', children: "PUBLIC_SUBGRAPHS_API_KEY" }), " in", ' ', _jsx(Text, { color: 'gray', children: ".env.local" }), " You can get one", ' ', _jsx(Link, { url: "https://thegraph.com/studio/apikeys", children: "here" })] }), _jsxs(Text, { children: ["2- After the API key is correctly configured, run", ' ', _jsx(Text, { color: 'gray', children: "pnpm subgraph-codegen" }), " in your console from the project's folder"] })] }), _jsxs(Text, { children: ["More configuration info in", ' ', _jsx(Link, { url: 'https://docs.dappbooster.dev/introduction/getting-started', children: "the docs" }), "."] }), _jsxs(Text, { color: 'yellow', bold: true, children: [figures.info, " Only after you have followed the previous steps you may proceed."] })] }));
|
|
9
9
|
const EvmPostInstallMessage = ({ projectName }) => (_jsxs(Box, { flexDirection: 'column', rowGap: 1, paddingBottom: 2, children: [_jsx(Text, { color: 'whiteBright', children: "To start development on your project:" }), _jsxs(Box, { flexDirection: 'column', children: [_jsxs(Text, { children: ["1- Move into the project's folder with ", _jsxs(Text, { color: 'gray', children: ["cd ", projectName] })] }), _jsxs(Text, { children: ["2- Start the development server with ", _jsx(Text, { color: 'gray', children: "pnpm dev" })] })] }), _jsx(Text, { color: 'whiteBright', children: "More info:" }), _jsxs(Box, { flexDirection: 'column', children: [_jsxs(Text, { children: ["- Check out ", _jsx(Text, { color: 'gray', children: ".env.local" }), " for more configurations."] }), _jsxs(Text, { children: ["- Read ", _jsx(Link, { url: "https://docs.dappbooster.dev", children: "the docs" }), " to know more about", ' ', _jsx(Text, { color: 'gray', children: "dAppBooster" }), "!"] }), _jsxs(Text, { children: ["- Report issues with this installer", ' ', _jsx(Link, { url: "https://github.com/BootNodeDev/dAppBoosterInstallScript/issues", children: "here" })] })] })] }));
|
|
10
|
-
const CantonPostInstallMessage = ({ projectName, features
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
const carpinchoEnabled = isFull || isFeatureSelected('carpincho', features);
|
|
14
|
-
return (_jsxs(Box, { flexDirection: 'column', rowGap: 1, paddingBottom: 2, children: [_jsx(Text, { color: 'whiteBright', children: "To start the Canton stack:" }), _jsxs(Box, { flexDirection: 'column', children: [_jsxs(Text, { children: ["1- Move into the project's folder with ", _jsxs(Text, { color: 'gray', children: ["cd ", projectName] })] }), _jsxs(Text, { children: ["2- Configure canton-barebones: ", _jsx(Text, { color: 'gray', children: "canton-barebones/.env" }), " was created from the example \u2014 review it."] }), _jsxs(Text, { children: ["3- Start the local Canton stack with ", _jsx(Text, { color: 'gray', children: "npm run canton:up" })] }), counterEnabled && (_jsxs(Text, { children: ["4- In a separate terminal, run the counter dapp:", ' ', _jsx(Text, { color: 'gray', children: "npm run app:dev" })] }))] }), carpinchoEnabled && (_jsx(Box, { alignItems: 'center', borderColor: 'yellow', borderStyle: 'bold', flexDirection: 'column', justifyContent: 'center', padding: 1, children: _jsxs(Text, { color: 'yellow', children: [figures.info, " ", _jsx(Text, { bold: true, children: "Carpincho Wallet" }), ": build it with", ' ', _jsx(Text, { color: 'gray', children: "npm run carpincho:build:extension" }), " and load", ' ', _jsx(Text, { color: 'gray', children: "carpincho-wallet/dist-extension" }), " as an unpacked browser extension ", figures.info] }) })), _jsx(Text, { children: "See the Canton stack README inside the project for full instructions." })] }));
|
|
10
|
+
const CantonPostInstallMessage = ({ projectName, features }) => {
|
|
11
|
+
const carpinchoEnabled = isFeatureSelected('carpincho', features);
|
|
12
|
+
return (_jsxs(Box, { flexDirection: 'column', rowGap: 1, paddingBottom: 2, children: [_jsx(Text, { color: 'whiteBright', children: "To start the Canton stack:" }), _jsxs(Box, { flexDirection: 'column', children: [_jsxs(Text, { children: ["1- Move into the project's folder with ", _jsxs(Text, { color: 'gray', children: ["cd ", projectName] })] }), _jsxs(Text, { children: ["2- Configure canton-barebones: ", _jsx(Text, { color: 'gray', children: "canton-barebones/.env" }), " was created from the example \u2014 review it."] }), _jsxs(Text, { children: ["3- Start the local Canton stack with ", _jsx(Text, { color: 'gray', children: "npm run canton:up" })] }), _jsxs(Text, { children: ["4- In a separate terminal, run the dapp frontend:", ' ', _jsx(Text, { color: 'gray', children: "npm run app:dev" })] })] }), carpinchoEnabled && (_jsx(Box, { alignItems: 'center', borderColor: 'yellow', borderStyle: 'bold', flexDirection: 'column', justifyContent: 'center', padding: 1, children: _jsxs(Text, { color: 'yellow', children: [figures.info, " ", _jsx(Text, { bold: true, children: "Carpincho Wallet" }), ": build it with", ' ', _jsx(Text, { color: 'gray', children: "npm run carpincho:build:extension" }), " and load", ' ', _jsx(Text, { color: 'gray', children: "carpincho-wallet/dist-extension" }), " as an unpacked browser extension ", figures.info] }) })), _jsx(Text, { children: "See the Canton stack README inside the project for full instructions." })] }));
|
|
15
13
|
};
|
|
16
14
|
const PostInstall = ({ stack, installationConfig, projectName }) => {
|
|
17
15
|
const { selectedFeatures, installationType } = installationConfig;
|
|
18
|
-
const
|
|
16
|
+
const selectedNames = selectedFeatures?.map((f) => f.value) ?? [];
|
|
17
|
+
const features = resolveModeFeatures(stack, installationType ?? 'full', selectedNames);
|
|
19
18
|
const stackLabel = getStackConfig(stack).label;
|
|
20
19
|
return (_jsxs(_Fragment, { children: [_jsx(Divider, { title: `Post-install instructions — ${stackLabel}` }), _jsxs(Box, { flexDirection: 'column', rowGap: 2, children: [stack === 'evm' &&
|
|
21
|
-
(isFeatureSelected('subgraph', features) || installationType === 'full') && (_jsx(SubgraphWarningMessage, {})), stack === 'evm' && _jsx(EvmPostInstallMessage, { projectName: projectName }), stack === 'canton' && (_jsx(CantonPostInstallMessage, { projectName: projectName, features: features
|
|
20
|
+
(isFeatureSelected('subgraph', features) || installationType === 'full') && (_jsx(SubgraphWarningMessage, {})), stack === 'evm' && _jsx(EvmPostInstallMessage, { projectName: projectName }), stack === 'canton' && (_jsx(CantonPostInstallMessage, { projectName: projectName, features: features }))] })] }));
|
|
22
21
|
};
|
|
23
22
|
export default PostInstall;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { InstallationType } from '../types/types.js';
|
|
1
2
|
export type Stack = 'evm' | 'canton';
|
|
2
3
|
export type RefType = 'tag-latest' | 'branch';
|
|
3
4
|
export type PackageManager = 'pnpm' | 'npm';
|
|
@@ -24,6 +25,7 @@ export type StackConfig = {
|
|
|
24
25
|
ref?: string;
|
|
25
26
|
packageManager: PackageManager;
|
|
26
27
|
removeAfterClone: string[];
|
|
28
|
+
postInstall?: string[];
|
|
27
29
|
envFiles: EnvFile[];
|
|
28
30
|
features: Record<FeatureName, FeatureDefinition>;
|
|
29
31
|
};
|
|
@@ -33,3 +35,5 @@ export declare function getStackConfig(stack: Stack): StackConfig;
|
|
|
33
35
|
export declare function getFeatureNames(stack: Stack): FeatureName[];
|
|
34
36
|
export declare function isFeatureNameValid(stack: Stack, name: string): boolean;
|
|
35
37
|
export declare function isStackName(name: string): name is Stack;
|
|
38
|
+
export declare function getDefaultFeatureNames(stack: Stack): FeatureName[];
|
|
39
|
+
export declare function getInstallationModes(stack: Stack): InstallationType[];
|
package/dist/constants/config.js
CHANGED
|
@@ -65,13 +65,14 @@ export const stackDefinitions = {
|
|
|
65
65
|
ref: 'main',
|
|
66
66
|
packageManager: 'npm',
|
|
67
67
|
removeAfterClone: [],
|
|
68
|
+
postInstall: [
|
|
69
|
+
'Review canton-barebones/.env (created from the example)',
|
|
70
|
+
'Run npm run canton:up to start the local Canton stack',
|
|
71
|
+
'Run npm run app:dev to start the dapp frontend',
|
|
72
|
+
],
|
|
68
73
|
envFiles: [
|
|
69
74
|
{ from: 'canton-barebones/.env.example', to: 'canton-barebones/.env' },
|
|
70
|
-
{
|
|
71
|
-
from: 'counter/frontend/.env.local.example',
|
|
72
|
-
to: 'counter/frontend/.env.local',
|
|
73
|
-
ifFeature: 'counter',
|
|
74
|
-
},
|
|
75
|
+
{ from: 'dapp/frontend/.env.local.example', to: 'dapp/frontend/.env.local' },
|
|
75
76
|
{
|
|
76
77
|
from: 'carpincho-wallet/.env.local.example',
|
|
77
78
|
to: 'carpincho-wallet/.env.local',
|
|
@@ -79,25 +80,19 @@ export const stackDefinitions = {
|
|
|
79
80
|
},
|
|
80
81
|
],
|
|
81
82
|
features: {
|
|
82
|
-
|
|
83
|
-
description: '
|
|
84
|
-
label: '
|
|
83
|
+
github: {
|
|
84
|
+
description: 'GitHub issue/PR templates and workflows (.github)',
|
|
85
|
+
label: 'GitHub templates & workflows',
|
|
85
86
|
packages: [],
|
|
86
|
-
default:
|
|
87
|
-
paths: ['
|
|
88
|
-
postInstall: [
|
|
89
|
-
'Review canton-barebones/.env (created from the example)',
|
|
90
|
-
'Run npm run canton:up to start the local Canton stack',
|
|
91
|
-
'Run npm run app:dev to start the counter dapp frontend',
|
|
92
|
-
],
|
|
87
|
+
default: false,
|
|
88
|
+
paths: ['.github'],
|
|
93
89
|
},
|
|
94
|
-
|
|
95
|
-
description: '
|
|
96
|
-
label: '
|
|
90
|
+
precommit: {
|
|
91
|
+
description: 'Pre-commit hooks (Husky, lint-staged, commitlint)',
|
|
92
|
+
label: 'Pre-commit hooks',
|
|
97
93
|
packages: [],
|
|
98
|
-
default:
|
|
99
|
-
paths: ['
|
|
100
|
-
requires: ['counter'],
|
|
94
|
+
default: false,
|
|
95
|
+
paths: ['.husky', '.lintstagedrc.mjs', 'commitlint.config.js'],
|
|
101
96
|
},
|
|
102
97
|
carpincho: {
|
|
103
98
|
description: 'Carpincho browser-extension wallet (frontend + build tooling)',
|
|
@@ -153,3 +148,15 @@ export function isFeatureNameValid(stack, name) {
|
|
|
153
148
|
export function isStackName(name) {
|
|
154
149
|
return stackNames.includes(name);
|
|
155
150
|
}
|
|
151
|
+
export function getDefaultFeatureNames(stack) {
|
|
152
|
+
return Object.entries(stackDefinitions[stack].features)
|
|
153
|
+
.filter(([, definition]) => definition.default)
|
|
154
|
+
.map(([name]) => name);
|
|
155
|
+
}
|
|
156
|
+
// Available installation modes per stack. `default` (keep only default:true features) is offered
|
|
157
|
+
// only when the stack has at least one opt-out (default:false) feature; otherwise it would be
|
|
158
|
+
// identical to `full`. Today that means Canton only (EVM's features are all default:true).
|
|
159
|
+
export function getInstallationModes(stack) {
|
|
160
|
+
const hasOptOutFeature = getDefaultFeatureNames(stack).length < getFeatureNames(stack).length;
|
|
161
|
+
return hasOptOutFeature ? ['default', 'full', 'custom'] : ['full', 'custom'];
|
|
162
|
+
}
|
package/dist/info.js
CHANGED
package/dist/nonInteractive.js
CHANGED
|
@@ -2,7 +2,7 @@ import process from 'node:process';
|
|
|
2
2
|
import { getFeatureNames, isFeatureNameValid, isStackName, stackNames, } from './constants/config.js';
|
|
3
3
|
import { cleanupFiles, cloneRepo, createEnvFile, installPackages } from './operations/index.js';
|
|
4
4
|
import { beginInstall, completeInstall } from './operations/installGuard.js';
|
|
5
|
-
import { getPostInstallMessages, getProjectFolder, isValidName, projectDirectoryExists,
|
|
5
|
+
import { getPostInstallMessages, getProjectFolder, isValidName, projectDirectoryExists, resolveModeFeatures, } from './utils/utils.js';
|
|
6
6
|
function fail(error) {
|
|
7
7
|
const result = { success: false, error };
|
|
8
8
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -40,14 +40,22 @@ function validate(flags) {
|
|
|
40
40
|
if (!isValidName(flags.name)) {
|
|
41
41
|
fail('Invalid project name: only letters, numbers, and underscores are allowed');
|
|
42
42
|
}
|
|
43
|
-
if (flags.mode !== 'full' && flags.mode !== 'custom') {
|
|
44
|
-
fail("Invalid mode: must be 'full' or 'custom'");
|
|
43
|
+
if (flags.mode !== 'full' && flags.mode !== 'default' && flags.mode !== 'custom') {
|
|
44
|
+
fail("Invalid mode: must be 'full', 'default', or 'custom'");
|
|
45
45
|
}
|
|
46
|
-
if (flags.mode === '
|
|
46
|
+
if (flags.mode === 'default' && stack === 'evm') {
|
|
47
|
+
fail("Invalid mode: 'default' is only available for the canton stack");
|
|
48
|
+
}
|
|
49
|
+
if (flags.mode === 'full' || flags.mode === 'default') {
|
|
47
50
|
if (projectDirectoryExists(flags.name)) {
|
|
48
51
|
fail(`Project directory '${flags.name}' already exists`);
|
|
49
52
|
}
|
|
50
|
-
return {
|
|
53
|
+
return {
|
|
54
|
+
stack,
|
|
55
|
+
name: flags.name,
|
|
56
|
+
mode: flags.mode,
|
|
57
|
+
features: resolveModeFeatures(stack, flags.mode),
|
|
58
|
+
};
|
|
51
59
|
}
|
|
52
60
|
if (!flags.features) {
|
|
53
61
|
fail('--mode custom requires --features. Use --info to see available features.');
|
|
@@ -68,7 +76,7 @@ function validate(flags) {
|
|
|
68
76
|
stack,
|
|
69
77
|
name: flags.name,
|
|
70
78
|
mode: flags.mode,
|
|
71
|
-
features:
|
|
79
|
+
features: resolveModeFeatures(stack, 'custom', features),
|
|
72
80
|
};
|
|
73
81
|
}
|
|
74
82
|
export async function runNonInteractive(flags) {
|
|
@@ -29,32 +29,37 @@ function removePackageKeys(packageBlock, keys) {
|
|
|
29
29
|
}
|
|
30
30
|
return changed;
|
|
31
31
|
}
|
|
32
|
+
// Strip the husky/lint-staged/commitlint tooling scripts and dependencies from a parsed
|
|
33
|
+
// package.json (mutates in place). Returns whether anything was removed.
|
|
34
|
+
function stripToolingEntries(packageJson, scripts) {
|
|
35
|
+
let changed = false;
|
|
36
|
+
if (scripts) {
|
|
37
|
+
for (const scriptName of TOOLING_SCRIPTS_TO_REMOVE) {
|
|
38
|
+
if (scripts[scriptName] !== undefined) {
|
|
39
|
+
scripts[scriptName] = undefined;
|
|
40
|
+
changed = true;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const dependencyGroups = [
|
|
45
|
+
packageJson.dependencies,
|
|
46
|
+
packageJson.devDependencies,
|
|
47
|
+
packageJson.optionalDependencies,
|
|
48
|
+
packageJson.peerDependencies,
|
|
49
|
+
];
|
|
50
|
+
for (const group of dependencyGroups) {
|
|
51
|
+
if (removePackageKeys(group, TOOLING_PACKAGES_TO_REMOVE)) {
|
|
52
|
+
changed = true;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return changed;
|
|
56
|
+
}
|
|
32
57
|
function sanitizeRepositoryPackageJson(projectFolder) {
|
|
33
58
|
const packageJsonPath = resolve(projectFolder, 'package.json');
|
|
34
59
|
try {
|
|
35
60
|
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
|
36
61
|
const scripts = packageJson.scripts;
|
|
37
|
-
|
|
38
|
-
if (scripts) {
|
|
39
|
-
for (const scriptName of TOOLING_SCRIPTS_TO_REMOVE) {
|
|
40
|
-
if (scripts[scriptName] !== undefined) {
|
|
41
|
-
scripts[scriptName] = undefined;
|
|
42
|
-
changed = true;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
const dependencyGroups = [
|
|
47
|
-
packageJson.dependencies,
|
|
48
|
-
packageJson.devDependencies,
|
|
49
|
-
packageJson.optionalDependencies,
|
|
50
|
-
packageJson.peerDependencies,
|
|
51
|
-
];
|
|
52
|
-
for (const group of dependencyGroups) {
|
|
53
|
-
if (removePackageKeys(group, TOOLING_PACKAGES_TO_REMOVE)) {
|
|
54
|
-
changed = true;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
if (changed) {
|
|
62
|
+
if (stripToolingEntries(packageJson, scripts)) {
|
|
58
63
|
writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`);
|
|
59
64
|
}
|
|
60
65
|
}
|
|
@@ -67,10 +72,11 @@ async function removePaths(projectFolder, relativePaths) {
|
|
|
67
72
|
await rm(resolve(projectFolder, relativePath), { recursive: true, force: true });
|
|
68
73
|
}
|
|
69
74
|
}
|
|
70
|
-
|
|
75
|
+
// EVM-only hygiene: always strip CI metadata, agent metadata, and git automation. Canton models
|
|
76
|
+
// .github and pre-commit hooks as optional features instead (see cleanupCantonFiles).
|
|
77
|
+
async function cleanupRepositoryHygiene(projectFolder, onProgress) {
|
|
71
78
|
onProgress?.('Repository metadata');
|
|
72
|
-
|
|
73
|
-
await removePaths(projectFolder, metadataPaths);
|
|
79
|
+
await removePaths(projectFolder, [...EVM_METADATA_PATHS, ...CI_PATHS]);
|
|
74
80
|
onProgress?.('Git hooks and commit linting');
|
|
75
81
|
await removePaths(projectFolder, AUTOMATION_PATHS);
|
|
76
82
|
sanitizeRepositoryPackageJson(projectFolder);
|
|
@@ -102,35 +108,36 @@ function patchPackageJsonEvm(projectFolder, features) {
|
|
|
102
108
|
scripts['commitlint:ci'] = undefined;
|
|
103
109
|
writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`);
|
|
104
110
|
}
|
|
105
|
-
// Strip scripts by what they run (e.g. `npm --prefix
|
|
111
|
+
// Strip scripts by what they run (e.g. `npm --prefix carpincho-wallet ...`)
|
|
106
112
|
// rather than by name, so cleanup tracks directory removal even as scripts change.
|
|
107
113
|
function scriptTargetsRemovedDir(command, removedDirs) {
|
|
108
114
|
const tokens = command.split(/\s+/);
|
|
109
115
|
return removedDirs.some((dir) => tokens.some((token) => token === dir || token.startsWith(`${dir}/`)));
|
|
110
116
|
}
|
|
111
|
-
function patchPackageJsonCanton(projectFolder, removedDirs) {
|
|
117
|
+
function patchPackageJsonCanton(projectFolder, removedDirs, precommitRemoved) {
|
|
112
118
|
const packageJsonPath = resolve(projectFolder, 'package.json');
|
|
113
119
|
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
|
114
120
|
const scripts = packageJson.scripts;
|
|
115
|
-
if (
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if (command !== undefined && scriptTargetsRemovedDir(command, removedDirs)) {
|
|
121
|
-
scripts[name] = undefined;
|
|
121
|
+
if (scripts) {
|
|
122
|
+
for (const [name, command] of Object.entries(scripts)) {
|
|
123
|
+
if (command !== undefined && scriptTargetsRemovedDir(command, removedDirs)) {
|
|
124
|
+
scripts[name] = undefined;
|
|
125
|
+
}
|
|
122
126
|
}
|
|
123
127
|
}
|
|
124
|
-
//
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
scripts['commitlint:ci'] = undefined;
|
|
128
|
+
// The husky tooling (prepare/commitlint scripts + husky/lint-staged/commitlint deps) only leaves
|
|
129
|
+
// with the pre-commit feature.
|
|
130
|
+
if (precommitRemoved) {
|
|
131
|
+
stripToolingEntries(packageJson, scripts);
|
|
132
|
+
}
|
|
130
133
|
writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`);
|
|
131
134
|
}
|
|
132
135
|
async function createInitialCommit(projectFolder) {
|
|
133
136
|
await execFile('git', ['add', '.'], { cwd: projectFolder });
|
|
137
|
+
// --no-verify: the scaffold's baseline commit must not run the project's own git hooks. When the
|
|
138
|
+
// `precommit` feature is kept, husky's pre-commit/commit-msg hooks are present and would lint and
|
|
139
|
+
// test the freshly-cloned tree (and fail), blocking the install. The user's later commits still
|
|
140
|
+
// run hooks normally.
|
|
134
141
|
await execFile('git', [
|
|
135
142
|
'-c',
|
|
136
143
|
'user.name=dAppBooster',
|
|
@@ -139,6 +146,7 @@ async function createInitialCommit(projectFolder) {
|
|
|
139
146
|
'-c',
|
|
140
147
|
'commit.gpgsign=false',
|
|
141
148
|
'commit',
|
|
149
|
+
'--no-verify',
|
|
142
150
|
'-m',
|
|
143
151
|
'chore: initial commit',
|
|
144
152
|
], { cwd: projectFolder });
|
|
@@ -190,10 +198,11 @@ async function cleanupEvmFiles(projectFolder, mode, features, onProgress) {
|
|
|
190
198
|
}
|
|
191
199
|
async function cleanupCantonFiles(projectFolder, mode, features, onProgress) {
|
|
192
200
|
const cantonFeatures = getStackConfig('canton').features;
|
|
193
|
-
// Each deselected feature contributes its paths to removal
|
|
194
|
-
// also feed script stripping, so a removed feature's
|
|
201
|
+
// Each deselected feature contributes its paths to removal. `default` and `custom` remove;
|
|
202
|
+
// `full` keeps everything. Directory paths also feed script stripping, so a removed feature's
|
|
203
|
+
// package.json scripts disappear with it.
|
|
195
204
|
const removedDirs = [];
|
|
196
|
-
if (mode
|
|
205
|
+
if (mode !== 'full') {
|
|
197
206
|
for (const [name, definition] of Object.entries(cantonFeatures)) {
|
|
198
207
|
if (isFeatureSelected(name, features) || !definition.paths || definition.paths.length === 0) {
|
|
199
208
|
continue;
|
|
@@ -203,15 +212,16 @@ async function cleanupCantonFiles(projectFolder, mode, features, onProgress) {
|
|
|
203
212
|
removedDirs.push(...definition.paths);
|
|
204
213
|
}
|
|
205
214
|
}
|
|
206
|
-
|
|
215
|
+
const precommitRemoved = mode !== 'full' && !isFeatureSelected('precommit', features);
|
|
216
|
+
patchPackageJsonCanton(projectFolder, removedDirs, precommitRemoved);
|
|
207
217
|
}
|
|
208
218
|
export async function cleanupFiles(stack, projectFolder, mode, features = [], onProgress) {
|
|
209
|
-
await cleanupRepositoryHygiene(stack, projectFolder, onProgress);
|
|
210
219
|
if (stack === 'canton') {
|
|
211
220
|
await cleanupCantonFiles(projectFolder, mode, features, onProgress);
|
|
212
221
|
onProgress?.('Initial commit');
|
|
213
222
|
await createInitialCommit(projectFolder);
|
|
214
223
|
return;
|
|
215
224
|
}
|
|
225
|
+
await cleanupRepositoryHygiene(projectFolder, onProgress);
|
|
216
226
|
await cleanupEvmFiles(projectFolder, mode, features, onProgress);
|
|
217
227
|
}
|
package/dist/types/types.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export type SelectItem = {
|
|
|
3
3
|
value: string;
|
|
4
4
|
};
|
|
5
5
|
export type MultiSelectItem = SelectItem;
|
|
6
|
-
export type InstallationType = 'full' | 'custom';
|
|
6
|
+
export type InstallationType = 'full' | 'default' | 'custom';
|
|
7
7
|
export type InstallationSelectItem = {
|
|
8
8
|
label: string;
|
|
9
9
|
value: InstallationType;
|
package/dist/utils/utils.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type FeatureName, type Stack } from '../constants/config.js';
|
|
2
|
+
import type { InstallationType } from '../types/types.js';
|
|
2
3
|
export declare function getProjectFolder(projectName: string): string;
|
|
3
4
|
export declare function isValidName(name: string): boolean;
|
|
4
5
|
export declare function isAnswerConfirmed(answer?: string, errorMessage?: string): boolean;
|
|
@@ -7,9 +8,10 @@ export declare function isFeatureSelected(feature: FeatureName, selectedFeatures
|
|
|
7
8
|
type FeatureToggleAction = 'select' | 'unselect';
|
|
8
9
|
export declare function resolveSelectedFeatures(stack: Stack, selectedFeatures: FeatureName[]): FeatureName[];
|
|
9
10
|
export declare function applyFeatureToggle(stack: Stack, selectedFeatures: FeatureName[], toggledFeature: FeatureName, action: FeatureToggleAction): FeatureName[];
|
|
10
|
-
export declare function describeInstallPlan(stack: Stack, projectName: string, mode:
|
|
11
|
+
export declare function describeInstallPlan(stack: Stack, projectName: string, mode: InstallationType, selectedFeatures: FeatureName[]): string;
|
|
11
12
|
export declare function getPackagesToRemove(stack: Stack, selectedFeatures: FeatureName[]): string[];
|
|
12
|
-
export declare function getPostInstallMessages(stack: Stack, mode:
|
|
13
|
+
export declare function getPostInstallMessages(stack: Stack, mode: InstallationType, selectedFeatures: FeatureName[]): string[];
|
|
14
|
+
export declare function resolveModeFeatures(stack: Stack, mode: InstallationType, customSelection?: FeatureName[]): FeatureName[];
|
|
13
15
|
export declare function projectDirectoryExists(projectName: string): boolean;
|
|
14
16
|
type StepStatus = 'running' | 'done' | 'error';
|
|
15
17
|
type StepDisplay = {
|
package/dist/utils/utils.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import process from 'node:process';
|
|
4
|
-
import { getFeatureNames, getStackConfig, } from '../constants/config.js';
|
|
4
|
+
import { getDefaultFeatureNames, getFeatureNames, getStackConfig, } from '../constants/config.js';
|
|
5
5
|
export function getProjectFolder(projectName) {
|
|
6
6
|
return join(process.cwd(), projectName);
|
|
7
7
|
}
|
|
@@ -68,6 +68,9 @@ export function describeInstallPlan(stack, projectName, mode, selectedFeatures)
|
|
|
68
68
|
if (mode === 'full') {
|
|
69
69
|
return `${head} · Mode: full (all features)`;
|
|
70
70
|
}
|
|
71
|
+
if (mode === 'default') {
|
|
72
|
+
return `${head} · Mode: default (recommended)`;
|
|
73
|
+
}
|
|
71
74
|
const features = selectedFeatures.length > 0 ? selectedFeatures.join(', ') : 'none';
|
|
72
75
|
return `${head} · Mode: custom · Features: ${features}`;
|
|
73
76
|
}
|
|
@@ -78,11 +81,24 @@ export function getPackagesToRemove(stack, selectedFeatures) {
|
|
|
78
81
|
.flatMap(([, def]) => def.packages);
|
|
79
82
|
}
|
|
80
83
|
export function getPostInstallMessages(stack, mode, selectedFeatures) {
|
|
81
|
-
const
|
|
84
|
+
const config = getStackConfig(stack);
|
|
85
|
+
const features = config.features;
|
|
86
|
+
const stackLevel = config.postInstall ?? [];
|
|
87
|
+
const kept = resolveModeFeatures(stack, mode, selectedFeatures);
|
|
88
|
+
const featureMessages = kept.flatMap((name) => features[name]?.postInstall ?? []);
|
|
89
|
+
return [...stackLevel, ...featureMessages];
|
|
90
|
+
}
|
|
91
|
+
// Resolves the kept-feature list for a mode: full → all, default → default:true set,
|
|
92
|
+
// custom → the user's selection (transitive requires resolved). Shared by the non-interactive
|
|
93
|
+
// path and the interactive Install/FileCleanup/PostInstall steps.
|
|
94
|
+
export function resolveModeFeatures(stack, mode, customSelection = []) {
|
|
82
95
|
if (mode === 'full') {
|
|
83
|
-
return
|
|
96
|
+
return getFeatureNames(stack);
|
|
97
|
+
}
|
|
98
|
+
if (mode === 'default') {
|
|
99
|
+
return getDefaultFeatureNames(stack);
|
|
84
100
|
}
|
|
85
|
-
return
|
|
101
|
+
return resolveSelectedFeatures(stack, customSelection);
|
|
86
102
|
}
|
|
87
103
|
export function projectDirectoryExists(projectName) {
|
|
88
104
|
return existsSync(getProjectFolder(projectName));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dappbooster",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"description": "Agent-friendly dAppBooster installer that scaffolds Web3 dApps via TUI or non-interactive CLI/CI.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dappbooster",
|
|
@@ -39,9 +39,7 @@
|
|
|
39
39
|
"lint": "pnpm biome check",
|
|
40
40
|
"lint:fix": "pnpm biome check --write"
|
|
41
41
|
},
|
|
42
|
-
"files": [
|
|
43
|
-
"dist"
|
|
44
|
-
],
|
|
42
|
+
"files": ["dist"],
|
|
45
43
|
"dependencies": {
|
|
46
44
|
"figures": "^6.1.0",
|
|
47
45
|
"ink": "^5.2.1",
|
package/readme.md
CHANGED
|
@@ -33,8 +33,9 @@ Omit the flag to be prompted for the stack in the wizard. Jump to the [EVM stack
|
|
|
33
33
|
pnpm dlx dappbooster
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
The wizard prompts for stack → project name → mode (full / custom
|
|
37
|
-
installs, cleans up, and prints next steps. Pass
|
|
36
|
+
The wizard prompts for stack → project name → mode (Canton offers default / full / custom; EVM
|
|
37
|
+
offers full / custom) → features, then clones, installs, cleans up, and prints next steps. Pass
|
|
38
|
+
`--evm` or `--canton` to skip the stack prompt.
|
|
38
39
|
|
|
39
40
|
dAppBooster documentation: https://docs.dappbooster.dev/
|
|
40
41
|
|
|
@@ -55,7 +56,7 @@ pnpm dlx dappbooster --info --stack canton # filter to one stack (or --info --
|
|
|
55
56
|
| `--canton` / `--evm` | Pick the stack (mutually exclusive shortcuts) |
|
|
56
57
|
| `--stack <evm\|canton>` | Pick the stack by name (useful when scripting) |
|
|
57
58
|
| `--name <name>` | Project directory name (`/^[a-zA-Z0-9_]+$/`) |
|
|
58
|
-
| `--mode <full\|custom>` | `full` installs every feature; `custom` needs `--features` |
|
|
59
|
+
| `--mode <full\|default\|custom>` | `default` (Canton only) keeps the recommended set; `full` installs every feature; `custom` needs `--features` |
|
|
59
60
|
| `--features <a,b,c>` | Comma-separated feature keys (custom mode only) |
|
|
60
61
|
| `--ni` | Force non-interactive mode |
|
|
61
62
|
|
|
@@ -65,7 +66,7 @@ accepts only its own feature keys, and validation errors name the stack:
|
|
|
65
66
|
```json
|
|
66
67
|
{
|
|
67
68
|
"success": false,
|
|
68
|
-
"error": "Unknown features for stack 'canton': subgraph. Valid features:
|
|
69
|
+
"error": "Unknown features for stack 'canton': subgraph. Valid features: github, precommit, carpincho, llm"
|
|
69
70
|
}
|
|
70
71
|
```
|
|
71
72
|
|
|
@@ -79,7 +80,7 @@ A successful install prints:
|
|
|
79
80
|
"success": true,
|
|
80
81
|
"stack": "evm|canton",
|
|
81
82
|
"projectName": "...",
|
|
82
|
-
"mode": "full|custom",
|
|
83
|
+
"mode": "full|default|custom",
|
|
83
84
|
"features": ["..."],
|
|
84
85
|
"path": "/absolute/path",
|
|
85
86
|
"postInstall": ["..."]
|
|
@@ -131,49 +132,52 @@ pnpm dlx dappbooster --canton
|
|
|
131
132
|
Interactive (skips the stack prompt) or non-interactive:
|
|
132
133
|
|
|
133
134
|
```shell
|
|
134
|
-
pnpm dlx dappbooster --canton --ni --name my_canton_dapp --mode
|
|
135
|
-
pnpm dlx dappbooster --canton --ni --name my_canton --mode custom --features
|
|
135
|
+
pnpm dlx dappbooster --canton --ni --name my_canton_dapp --mode default
|
|
136
|
+
pnpm dlx dappbooster --canton --ni --name my_canton --mode custom --features carpincho,github
|
|
136
137
|
```
|
|
137
138
|
|
|
138
139
|
| Feature | Key | Default | Description |
|
|
139
140
|
|---|---|---|---|
|
|
140
|
-
|
|
|
141
|
-
|
|
|
141
|
+
| GitHub templates & workflows | `github` | | GitHub issue/PR templates and workflows (`.github`) |
|
|
142
|
+
| Pre-commit hooks | `precommit` | | Husky, lint-staged, and commitlint |
|
|
142
143
|
| Carpincho wallet | `carpincho` | ✓ | Carpincho browser-extension wallet (frontend + build tooling) |
|
|
143
144
|
| LLM & agent artifacts | `llm` | ✓ | `.claude`, `AGENTS.md`, `CLAUDE.md`, `architecture.md`, `llms.txt`, … |
|
|
144
145
|
|
|
145
|
-
`
|
|
146
|
-
`
|
|
147
|
-
`counter`
|
|
146
|
+
`default` mode (the recommended Canton install) keeps `carpincho` + `llm` and removes `github` +
|
|
147
|
+
`precommit`; `full` keeps all four; `custom` lets you pick (in the wizard `github` and `precommit`
|
|
148
|
+
start unchecked). To remove the demo features (`counter`, `sign-message`) after scaffolding, follow
|
|
149
|
+
the "Removing a feature" guide in the generated `dapp/frontend/README.md` — the installer never
|
|
150
|
+
deletes demo source itself.
|
|
148
151
|
|
|
149
152
|
The Canton scaffold uses **npm** (a property of the generated project, not this installer). After
|
|
150
153
|
install: review `canton-barebones/.env`, run `npm run canton:up` to start the local Canton stack,
|
|
151
|
-
and `npm run app:dev` to run the
|
|
152
|
-
|
|
153
|
-
|
|
154
|
+
and `npm run app:dev` to run the dapp frontend. When `carpincho` is included, build the extension
|
|
155
|
+
with `npm run carpincho:build:extension` and load `carpincho-wallet/dist-extension` as an unpacked
|
|
156
|
+
browser extension.
|
|
154
157
|
|
|
155
158
|
**What gets stripped:**
|
|
156
159
|
|
|
157
|
-
- **
|
|
158
|
-
|
|
159
|
-
`
|
|
160
|
-
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
-
|
|
160
|
+
- **EVM** always removes CI config (`.github`) and the husky/commitlint automation as hygiene.
|
|
161
|
+
- **Canton** treats `.github` and pre-commit hooks as optional features: `default` mode removes
|
|
162
|
+
both; `full` keeps both; `custom` removes whichever you uncheck. Deselecting `carpincho` removes
|
|
163
|
+
`carpincho-wallet/` and its scripts (`wallet:dev`, `carpincho:build:extension`); deselecting `llm`
|
|
164
|
+
removes the agent docs. Removing `precommit` also strips the `prepare` script and the
|
|
165
|
+
husky/lint-staged/commitlint dev-dependencies from the root `package.json`.
|
|
166
|
+
- The Canton installer never deletes demo source (the `counter`/`sign-message` features) — that is
|
|
167
|
+
user-controlled via the template's `dapp/frontend/README.md`.
|
|
164
168
|
|
|
165
169
|
```json
|
|
166
170
|
{
|
|
167
171
|
"success": true,
|
|
168
172
|
"stack": "canton",
|
|
169
173
|
"projectName": "my_canton_dapp",
|
|
170
|
-
"mode": "
|
|
171
|
-
"features": ["
|
|
174
|
+
"mode": "default",
|
|
175
|
+
"features": ["carpincho", "llm"],
|
|
172
176
|
"path": "/absolute/path/to/my_canton_dapp",
|
|
173
177
|
"postInstall": [
|
|
174
178
|
"Review canton-barebones/.env (created from the example)",
|
|
175
179
|
"Run npm run canton:up to start the local Canton stack",
|
|
176
|
-
"Run npm run app:dev to start the
|
|
180
|
+
"Run npm run app:dev to start the dapp frontend",
|
|
177
181
|
"Build the Carpincho extension with npm run carpincho:build:extension",
|
|
178
182
|
"Load carpincho-wallet/dist-extension as an unpacked browser extension"
|
|
179
183
|
]
|