vyriy 0.3.4 → 0.3.8
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/README.md +46 -21
- package/cli/args/args.js +4 -0
- package/cli/args/types.d.ts +4 -0
- package/cli/cli.js +13 -2
- package/commands/new/new.js +77 -6
- package/commands/new/types.d.ts +2 -0
- package/package.json +21 -1
- package/presets/agentsTemplate.d.ts +1 -0
- package/presets/agentsTemplate.js +105 -0
- package/presets/createProjectFiles.js +374 -18
- package/project-plan/api/api.js +5 -17
- package/project-plan/api/types.d.ts +1 -0
- package/project-plan/ci/ci.d.ts +1 -1
- package/project-plan/ci/ci.js +3 -2
- package/project-plan/create/create.js +70 -74
- package/project-plan/kind/kind.js +1 -16
- package/project-plan/types.d.ts +8 -8
- package/prompts/project-plan/project-plan.js +87 -25
- package/shared/index.d.ts +1 -0
- package/shared/index.js +1 -0
- package/shared/runCommand.d.ts +9 -0
- package/shared/runCommand.js +34 -0
- package/shared/types.d.ts +7 -0
package/README.md
CHANGED
|
@@ -8,6 +8,9 @@ Interactive project master for calm cloud-ready applications.
|
|
|
8
8
|
|
|
9
9
|
The CLI checks the local environment, runs a small project wizard, creates a normalized `VyriyProjectPlan`, builds generated project files in memory, prints a file plan, and writes only files that are safe to create or explicitly allowed to overwrite.
|
|
10
10
|
|
|
11
|
+
For a detailed map of CLI choices, diagrams, current generated files, and next
|
|
12
|
+
generator targets, see [PROJECT-MASTER.md](./PROJECT-MASTER.md).
|
|
13
|
+
|
|
11
14
|
## Install
|
|
12
15
|
|
|
13
16
|
Install globally:
|
|
@@ -33,6 +36,10 @@ vyriy init
|
|
|
33
36
|
vyriy doctor
|
|
34
37
|
vyriy --dry-run
|
|
35
38
|
vyriy --yes
|
|
39
|
+
vyriy --no-install
|
|
40
|
+
vyriy --no-verify
|
|
41
|
+
vyriy --install-only
|
|
42
|
+
vyriy --verify
|
|
36
43
|
vyriy --overwrite
|
|
37
44
|
vyriy --skip-existing
|
|
38
45
|
vyriy --help
|
|
@@ -45,7 +52,7 @@ Runs the same flow as `vyriy new`.
|
|
|
45
52
|
|
|
46
53
|
### `vyriy new [name]`
|
|
47
54
|
|
|
48
|
-
Starts the project planning wizard, prints the project summary and file plan,
|
|
55
|
+
Starts the project planning wizard, prints the project summary and file plan, writes generated files when no unresolved conflicts exist, installs dependencies, and runs generated project checks.
|
|
49
56
|
|
|
50
57
|
If `name` is provided, it is used as the default project name and target directory.
|
|
51
58
|
|
|
@@ -76,11 +83,27 @@ Node.js is fatal when unsupported. Yarn and Git are warnings so generation can c
|
|
|
76
83
|
|
|
77
84
|
### `--dry-run`
|
|
78
85
|
|
|
79
|
-
Prints the doctor report, project summary, and file plan without writing files or running
|
|
86
|
+
Prints the doctor report, project summary, and file plan without writing files, installing dependencies, or running checks.
|
|
87
|
+
|
|
88
|
+
### `--no-install`
|
|
89
|
+
|
|
90
|
+
Writes generated files but skips `yarn install` and `yarn check`.
|
|
91
|
+
|
|
92
|
+
### `--no-verify`
|
|
93
|
+
|
|
94
|
+
Writes generated files and runs `yarn install`, but skips `yarn check`.
|
|
95
|
+
|
|
96
|
+
### `--install-only`
|
|
97
|
+
|
|
98
|
+
Alias for `--no-verify`.
|
|
99
|
+
|
|
100
|
+
### `--verify`
|
|
101
|
+
|
|
102
|
+
Explicitly enables `yarn check`. This is already the default unless `--no-install`, `--no-verify`, or `--install-only` is passed.
|
|
80
103
|
|
|
81
104
|
### `--yes`
|
|
82
105
|
|
|
83
|
-
Uses default wizard answers and avoids prompts where possible. It does not overwrite existing files unless `--overwrite` is also passed.
|
|
106
|
+
Uses default wizard answers and avoids prompts where possible. In non-interactive mode, the default preset is `empty` with no CI/CD provider. It does not overwrite existing files unless `--overwrite` is also passed.
|
|
84
107
|
|
|
85
108
|
### `--overwrite`
|
|
86
109
|
|
|
@@ -103,39 +126,41 @@ The wizard collects:
|
|
|
103
126
|
- project preset
|
|
104
127
|
- API style for API-capable presets
|
|
105
128
|
- CI/CD provider
|
|
106
|
-
-
|
|
129
|
+
- infrastructure provider
|
|
107
130
|
- confirmation
|
|
108
131
|
|
|
109
|
-
After confirmation, the CLI prints the project plan, creates generated files in memory, builds a conflict-aware file plan,
|
|
132
|
+
After confirmation, the CLI prints the project plan, creates generated files in memory, builds a conflict-aware file plan, writes the accepted file plan, runs `yarn install`, and runs `yarn check`.
|
|
110
133
|
|
|
111
134
|
Presets do not write to disk directly.
|
|
112
135
|
|
|
136
|
+
Generated projects always include `AGENTS.md` based on the shared Vyriy package agent guide.
|
|
137
|
+
|
|
113
138
|
## Project Presets
|
|
114
139
|
|
|
115
140
|
Supported presets:
|
|
116
141
|
|
|
142
|
+
- `empty`
|
|
117
143
|
- `library`
|
|
118
144
|
- `api`
|
|
119
|
-
- `
|
|
120
|
-
- `
|
|
121
|
-
- `
|
|
122
|
-
- `mfe`
|
|
123
|
-
- `openmfe`
|
|
124
|
-
- `mfe-bff`
|
|
125
|
-
- `openmfe-bff`
|
|
145
|
+
- `ssr`
|
|
146
|
+
- `ssg`
|
|
147
|
+
- `csr`
|
|
126
148
|
- `fullstack`
|
|
127
|
-
- `
|
|
128
|
-
|
|
149
|
+
- `mfe`
|
|
150
|
+
|
|
151
|
+
The preset is the concrete future generated setup. The project kind is the broader architecture category. The infrastructure choice is selected separately: Docker is the default local/container shape, while AWS selects CDK plus Lambda/API Gateway for API-capable presets.
|
|
152
|
+
|
|
153
|
+
The `mfe` preset uses OpenMFE as the default MFE contract shape. There is no separate `openmfe` preset unless a future use case proves that split is useful.
|
|
129
154
|
|
|
130
|
-
|
|
155
|
+
Workspace kinds describe deployment intent: `ui` is universal UI output, `api` is Docker-oriented, `lambda` is the AWS API runtime, `fargate` is an AWS container runtime, and `stack` contains AWS infrastructure.
|
|
131
156
|
|
|
132
157
|
Examples:
|
|
133
158
|
|
|
134
|
-
- `
|
|
135
|
-
- `
|
|
136
|
-
- `
|
|
137
|
-
- `
|
|
138
|
-
- `
|
|
159
|
+
- `csr` -> `csr`
|
|
160
|
+
- `ssr` -> `ssr`
|
|
161
|
+
- `ssg` -> `ssg`
|
|
162
|
+
- `mfe` -> `mfe`
|
|
163
|
+
- `fullstack` -> `fullstack`
|
|
139
164
|
|
|
140
165
|
## Public API
|
|
141
166
|
|
|
@@ -173,7 +198,7 @@ It includes:
|
|
|
173
198
|
- architecture: `preset`, `projectKind`
|
|
174
199
|
- selected features
|
|
175
200
|
- CI/CD planning: enabled state, providers, and validation pipelines
|
|
176
|
-
- API planning for API-capable presets: REST
|
|
201
|
+
- API planning for API-capable presets: REST or GraphQL API style
|
|
177
202
|
- future package plans
|
|
178
203
|
- future workspace plans
|
|
179
204
|
|
package/cli/args/args.js
CHANGED
|
@@ -6,6 +6,8 @@ export const parseArgs = (args) => {
|
|
|
6
6
|
return { type: 'version' };
|
|
7
7
|
}
|
|
8
8
|
const dryRun = args.includes('--dry-run');
|
|
9
|
+
const install = !args.includes('--no-install');
|
|
10
|
+
const verify = install && (args.includes('--verify') || (!args.includes('--no-verify') && !args.includes('--install-only')));
|
|
9
11
|
const yes = args.includes('--yes') || args.includes('-y');
|
|
10
12
|
const overwrite = args.includes('--overwrite');
|
|
11
13
|
const skipExisting = args.includes('--skip-existing');
|
|
@@ -13,8 +15,10 @@ export const parseArgs = (args) => {
|
|
|
13
15
|
const [command, projectName] = positionalArgs;
|
|
14
16
|
const options = {
|
|
15
17
|
dryRun,
|
|
18
|
+
install,
|
|
16
19
|
yes,
|
|
17
20
|
overwrite,
|
|
21
|
+
verify,
|
|
18
22
|
skipExisting,
|
|
19
23
|
};
|
|
20
24
|
if (!command) {
|
package/cli/args/types.d.ts
CHANGED
|
@@ -2,14 +2,18 @@ export type VyriyCliCommand = {
|
|
|
2
2
|
readonly type: 'new';
|
|
3
3
|
readonly projectName?: string;
|
|
4
4
|
readonly dryRun: boolean;
|
|
5
|
+
readonly install: boolean;
|
|
5
6
|
readonly yes: boolean;
|
|
6
7
|
readonly overwrite: boolean;
|
|
8
|
+
readonly verify: boolean;
|
|
7
9
|
readonly skipExisting: boolean;
|
|
8
10
|
} | {
|
|
9
11
|
readonly type: 'init';
|
|
10
12
|
readonly dryRun: boolean;
|
|
13
|
+
readonly install: boolean;
|
|
11
14
|
readonly yes: boolean;
|
|
12
15
|
readonly overwrite: boolean;
|
|
16
|
+
readonly verify: boolean;
|
|
13
17
|
readonly skipExisting: boolean;
|
|
14
18
|
} | {
|
|
15
19
|
readonly type: 'doctor' | 'help' | 'version';
|
package/cli/cli.js
CHANGED
|
@@ -10,9 +10,16 @@ Usage:
|
|
|
10
10
|
vyriy init Initialize the current directory
|
|
11
11
|
vyriy . Initialize the current directory
|
|
12
12
|
vyriy doctor Check local environment
|
|
13
|
+
vyriy --yes, -y Use defaults where possible (empty preset)
|
|
13
14
|
vyriy --dry-run Print checks and file plan without writing
|
|
14
|
-
vyriy --
|
|
15
|
-
vyriy --
|
|
15
|
+
vyriy --overwrite Overwrite existing generated paths
|
|
16
|
+
vyriy --skip-existing Leave existing generated paths untouched
|
|
17
|
+
vyriy --no-install Create files without installing dependencies
|
|
18
|
+
vyriy --no-verify Install dependencies without running checks
|
|
19
|
+
vyriy --install-only Alias for --no-verify
|
|
20
|
+
vyriy --verify Explicitly enable generated project checks
|
|
21
|
+
vyriy --help, -h Show help
|
|
22
|
+
vyriy --version, -v Show version
|
|
16
23
|
|
|
17
24
|
Examples:
|
|
18
25
|
vyriy new my-app
|
|
@@ -25,19 +32,23 @@ export const runVyriyCli = async (args = [], { output = console } = {}) => {
|
|
|
25
32
|
case 'new':
|
|
26
33
|
code = await runNewCommand({
|
|
27
34
|
dryRun: command.dryRun,
|
|
35
|
+
install: command.install,
|
|
28
36
|
output,
|
|
29
37
|
overwrite: command.overwrite,
|
|
30
38
|
projectName: command.projectName,
|
|
31
39
|
skipExisting: command.skipExisting,
|
|
40
|
+
verify: command.verify,
|
|
32
41
|
yes: command.yes,
|
|
33
42
|
});
|
|
34
43
|
break;
|
|
35
44
|
case 'init':
|
|
36
45
|
code = await runInitCommand({
|
|
37
46
|
dryRun: command.dryRun,
|
|
47
|
+
install: command.install,
|
|
38
48
|
output,
|
|
39
49
|
overwrite: command.overwrite,
|
|
40
50
|
skipExisting: command.skipExisting,
|
|
51
|
+
verify: command.verify,
|
|
41
52
|
yes: command.yes,
|
|
42
53
|
});
|
|
43
54
|
break;
|
package/commands/new/new.js
CHANGED
|
@@ -5,6 +5,8 @@ import { createFilePlan, printFilePlan, writeFilePlan } from '../../file-plan/in
|
|
|
5
5
|
import { createProjectFiles } from '../../presets/index.js';
|
|
6
6
|
import { askProjectPlan as askProjectPlanDefault } from '../../prompts/project-plan/index.js';
|
|
7
7
|
import { createProjectPlanFromPreset, printProjectPlan } from '../../project-plan/index.js';
|
|
8
|
+
import { runCommand } from '../../shared/index.js';
|
|
9
|
+
const defaultYesPreset = 'empty';
|
|
8
10
|
const getConflicts = (filePlan) => filePlan.filter((item) => item.status === 'conflict');
|
|
9
11
|
const logConflicts = (output, conflicts, method) => {
|
|
10
12
|
output[method]('\nExisting files found:\n');
|
|
@@ -28,6 +30,70 @@ const createResolvedFilePlan = async (plan, projectFiles, resolution) => createF
|
|
|
28
30
|
overwrite: resolution === 'overwrite',
|
|
29
31
|
skipExisting: resolution === 'skip',
|
|
30
32
|
});
|
|
33
|
+
const formatCommand = (command, args) => [command, ...args].join(' ');
|
|
34
|
+
const printFailedPostGenerationCommand = ({ args, command, intro, output, projectDirectory, }) => {
|
|
35
|
+
const commandText = formatCommand(command, args);
|
|
36
|
+
output.error(`\n${intro}\n`);
|
|
37
|
+
output.error(`Failed command:\n ${commandText}\n`);
|
|
38
|
+
output.error(`Project directory:\n ${projectDirectory}\n`);
|
|
39
|
+
output.error(`You can inspect it and run manually:\n cd ${projectDirectory}\n ${commandText}`);
|
|
40
|
+
};
|
|
41
|
+
const runPostGenerationCommands = async ({ install, output, projectDirectory, verify, }) => {
|
|
42
|
+
if (!install) {
|
|
43
|
+
output.log('Installing dependencies... SKIPPED');
|
|
44
|
+
output.log('Running checks... SKIPPED');
|
|
45
|
+
output.log('\nProject files were created.');
|
|
46
|
+
return 0;
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
await runCommand({
|
|
50
|
+
args: ['install'],
|
|
51
|
+
command: 'yarn',
|
|
52
|
+
cwd: projectDirectory,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
printFailedPostGenerationCommand({
|
|
57
|
+
args: ['install'],
|
|
58
|
+
command: 'yarn',
|
|
59
|
+
intro: 'Project files were created, but dependency installation failed.',
|
|
60
|
+
output,
|
|
61
|
+
projectDirectory,
|
|
62
|
+
});
|
|
63
|
+
return 1;
|
|
64
|
+
}
|
|
65
|
+
output.log('Installing dependencies... OK');
|
|
66
|
+
if (!verify) {
|
|
67
|
+
output.log('Running checks... SKIPPED');
|
|
68
|
+
output.log('\nProject files were created and dependencies were installed.');
|
|
69
|
+
return 0;
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
await runCommand({
|
|
73
|
+
args: ['fix'],
|
|
74
|
+
command: 'yarn',
|
|
75
|
+
cwd: projectDirectory,
|
|
76
|
+
});
|
|
77
|
+
await runCommand({
|
|
78
|
+
args: ['check'],
|
|
79
|
+
command: 'yarn',
|
|
80
|
+
cwd: projectDirectory,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
printFailedPostGenerationCommand({
|
|
85
|
+
args: ['check'],
|
|
86
|
+
command: 'yarn',
|
|
87
|
+
intro: 'Project files were created and dependencies were installed, but verification failed.',
|
|
88
|
+
output,
|
|
89
|
+
projectDirectory,
|
|
90
|
+
});
|
|
91
|
+
return 1;
|
|
92
|
+
}
|
|
93
|
+
output.log('Running checks... OK');
|
|
94
|
+
output.log('\nProject is ready.');
|
|
95
|
+
return 0;
|
|
96
|
+
};
|
|
31
97
|
const resolveInteractiveConflicts = async (plan, projectFiles, output, conflicts, askConflictResolution) => {
|
|
32
98
|
logConflicts(output, conflicts, 'log');
|
|
33
99
|
printConflictPrompt(output);
|
|
@@ -46,7 +112,7 @@ const resolveInteractiveConflicts = async (plan, projectFiles, output, conflicts
|
|
|
46
112
|
export const askConflictResolutionDefault = async () => {
|
|
47
113
|
const readline = createInterface({ input: stdin, output: stdout });
|
|
48
114
|
try {
|
|
49
|
-
const answer = (await readline.question('What should Vyriy do
|
|
115
|
+
const answer = (await readline.question('What should Vyriy do?\n\n 1. overwrite existing files,\n\n 2. skip existing files,\n\n 3. abort (abort): '))
|
|
50
116
|
.trim()
|
|
51
117
|
.toLowerCase();
|
|
52
118
|
if (answer === '1' || answer === 'overwrite') {
|
|
@@ -61,7 +127,7 @@ export const askConflictResolutionDefault = async () => {
|
|
|
61
127
|
readline.close();
|
|
62
128
|
}
|
|
63
129
|
};
|
|
64
|
-
export const runNewCommand = async ({ askConflictResolution = askConflictResolutionDefault, askProjectPlan = askProjectPlanDefault, dryRun = false, output = console, overwrite = false, projectName = 'my-app', skipExisting = false, yes = false, } = {}) => {
|
|
130
|
+
export const runNewCommand = async ({ askConflictResolution = askConflictResolutionDefault, askProjectPlan = askProjectPlanDefault, dryRun = false, install = true, output = console, overwrite = false, projectName = 'my-app', skipExisting = false, verify = true, yes = false, } = {}) => {
|
|
65
131
|
if (overwrite && skipExisting) {
|
|
66
132
|
output.error('Cannot use --overwrite and --skip-existing together.');
|
|
67
133
|
return 1;
|
|
@@ -72,13 +138,13 @@ export const runNewCommand = async ({ askConflictResolution = askConflictResolut
|
|
|
72
138
|
output.error('\nPlease install Node.js 24+ and run the command again.');
|
|
73
139
|
return 1;
|
|
74
140
|
}
|
|
75
|
-
const plan =
|
|
141
|
+
const plan = yes
|
|
76
142
|
? createProjectPlanFromPreset({
|
|
77
143
|
apiStyle: 'rest',
|
|
78
144
|
ciProvider: 'none',
|
|
79
145
|
description: 'Calm cloud-ready application.',
|
|
80
146
|
packageScope: `@${projectName}`,
|
|
81
|
-
preset:
|
|
147
|
+
preset: defaultYesPreset,
|
|
82
148
|
projectName,
|
|
83
149
|
targetDirectory: projectName,
|
|
84
150
|
})
|
|
@@ -113,6 +179,11 @@ export const runNewCommand = async ({ askConflictResolution = askConflictResolut
|
|
|
113
179
|
output.log(`\n${printFilePlan(resolved.filePlan)}`);
|
|
114
180
|
}
|
|
115
181
|
await writeFilePlan(plan.targetDirectory, filePlan);
|
|
116
|
-
output.log('\
|
|
117
|
-
return
|
|
182
|
+
output.log('\nCreating project files... OK');
|
|
183
|
+
return runPostGenerationCommands({
|
|
184
|
+
install,
|
|
185
|
+
output,
|
|
186
|
+
projectDirectory: plan.targetDirectory,
|
|
187
|
+
verify: install && verify,
|
|
188
|
+
});
|
|
118
189
|
};
|
package/commands/new/types.d.ts
CHANGED
|
@@ -6,8 +6,10 @@ export type RunNewCommandOptions = {
|
|
|
6
6
|
readonly askConflictResolution?: () => Promise<ConflictResolution>;
|
|
7
7
|
readonly output?: Pick<typeof console, 'log' | 'error'>;
|
|
8
8
|
readonly dryRun?: boolean;
|
|
9
|
+
readonly install?: boolean;
|
|
9
10
|
readonly yes?: boolean;
|
|
10
11
|
readonly overwrite?: boolean;
|
|
12
|
+
readonly verify?: boolean;
|
|
11
13
|
readonly skipExisting?: boolean;
|
|
12
14
|
};
|
|
13
15
|
export type RunNewCommand = (options?: RunNewCommandOptions) => Promise<number>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vyriy",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.8",
|
|
4
4
|
"description": "Interactive project master for calm cloud-ready applications.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.js",
|
|
@@ -289,6 +289,16 @@
|
|
|
289
289
|
"import": "./index.js",
|
|
290
290
|
"default": "./index.js"
|
|
291
291
|
},
|
|
292
|
+
"./presets/agentsTemplate": {
|
|
293
|
+
"types": "./presets/agentsTemplate.d.ts",
|
|
294
|
+
"import": "./presets/agentsTemplate.js",
|
|
295
|
+
"default": "./presets/agentsTemplate.js"
|
|
296
|
+
},
|
|
297
|
+
"./presets/agentsTemplate.js": {
|
|
298
|
+
"types": "./presets/agentsTemplate.d.ts",
|
|
299
|
+
"import": "./presets/agentsTemplate.js",
|
|
300
|
+
"default": "./presets/agentsTemplate.js"
|
|
301
|
+
},
|
|
292
302
|
"./presets/createProjectFiles": {
|
|
293
303
|
"types": "./presets/createProjectFiles.d.ts",
|
|
294
304
|
"import": "./presets/createProjectFiles.js",
|
|
@@ -479,6 +489,16 @@
|
|
|
479
489
|
"import": "./shared/index.js",
|
|
480
490
|
"default": "./shared/index.js"
|
|
481
491
|
},
|
|
492
|
+
"./shared/runCommand": {
|
|
493
|
+
"types": "./shared/runCommand.d.ts",
|
|
494
|
+
"import": "./shared/runCommand.js",
|
|
495
|
+
"default": "./shared/runCommand.js"
|
|
496
|
+
},
|
|
497
|
+
"./shared/runCommand.js": {
|
|
498
|
+
"types": "./shared/runCommand.d.ts",
|
|
499
|
+
"import": "./shared/runCommand.js",
|
|
500
|
+
"default": "./shared/runCommand.js"
|
|
501
|
+
},
|
|
482
502
|
"./shared/semver": {
|
|
483
503
|
"types": "./shared/semver.d.ts",
|
|
484
504
|
"import": "./shared/semver.js",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const agentsTemplate: string;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
export const agentsTemplate = [
|
|
2
|
+
'# Project Agent Guide',
|
|
3
|
+
'',
|
|
4
|
+
'This repository follows a calm engineering style: changes should be explicit, reusable, typed, documented, tested, and easy to reason about.',
|
|
5
|
+
'',
|
|
6
|
+
'Use this guide as the default behavior for AI agents and contributors working in this repository. Prefer local package conventions when they are more specific than this document.',
|
|
7
|
+
'',
|
|
8
|
+
'## Core Principles',
|
|
9
|
+
'',
|
|
10
|
+
'- Prefer simple modules over clever frameworks or hidden conventions.',
|
|
11
|
+
'- Keep package and project boundaries explicit.',
|
|
12
|
+
'- Avoid project-specific coupling in reusable code.',
|
|
13
|
+
'- Extract only proven reusable behavior.',
|
|
14
|
+
'- Keep public APIs small, typed, documented, and stable.',
|
|
15
|
+
'- Prefer SSR-friendly and SSG-friendly code paths when working with frontend or shared code.',
|
|
16
|
+
'- Keep integrations replaceable and avoid hard coupling to a CMS, framework, vendor, or runtime host.',
|
|
17
|
+
'- Prefer infrastructure assumptions that are easy to deploy, observe, and replace.',
|
|
18
|
+
'- Prefer the option that is simpler to explain, easier to evolve, and calmer to maintain.',
|
|
19
|
+
'',
|
|
20
|
+
'## File Shape',
|
|
21
|
+
'',
|
|
22
|
+
'- Prefer one exported runtime method, component, helper, or class per production file when it stays readable.',
|
|
23
|
+
'- Prefer one matching test file per production file, for example `feature.ts` and `feature.test.ts`.',
|
|
24
|
+
'- Use focused folders when behavior naturally splits into several related files.',
|
|
25
|
+
'- Keep `index.ts` as a public re-export surface only. Do not place implementation logic in it.',
|
|
26
|
+
'- Use relative import and export specifiers that match the package module style.',
|
|
27
|
+
'- Use `.js` relative specifiers in TypeScript source for ESM/NodeNext packages.',
|
|
28
|
+
'- Add `types.ts` when public shared types are part of the package contract.',
|
|
29
|
+
'- Keep constants near the code that owns them unless they are shared or clarify repeated behavior.',
|
|
30
|
+
'',
|
|
31
|
+
'## Public Surface',
|
|
32
|
+
'',
|
|
33
|
+
'- Every new public export must be re-exported from the package or module public entry point.',
|
|
34
|
+
'- Add or update public-surface tests when exports change.',
|
|
35
|
+
'- Add JSDoc for public exports when behavior, parameters, return values, or usage expectations need explanation.',
|
|
36
|
+
'- Avoid exporting internal helpers only to make tests easier.',
|
|
37
|
+
'- Do not hand-maintain package `exports` maps unless the project has a real custom publishing need.',
|
|
38
|
+
'',
|
|
39
|
+
'## Tests',
|
|
40
|
+
'',
|
|
41
|
+
'- Cover public behavior and meaningful edge cases.',
|
|
42
|
+
'- Prefer behavior-focused tests over private implementation lock-in.',
|
|
43
|
+
'- Keep tests deterministic.',
|
|
44
|
+
'- Avoid real network, filesystem, timers, browser, or cloud dependencies unless the behavior specifically requires them.',
|
|
45
|
+
'- When mocking modules, install mocks before loading the module under test.',
|
|
46
|
+
'- Use focused validation when changing behavior.',
|
|
47
|
+
'',
|
|
48
|
+
'Example validation commands:',
|
|
49
|
+
'',
|
|
50
|
+
'```bash',
|
|
51
|
+
'yarn test',
|
|
52
|
+
'```',
|
|
53
|
+
'',
|
|
54
|
+
'For workspaces, prefer the project convention, for example:',
|
|
55
|
+
'',
|
|
56
|
+
'```bash',
|
|
57
|
+
'yarn workspace <package-name> test',
|
|
58
|
+
'```',
|
|
59
|
+
'',
|
|
60
|
+
'For Jest-based packages, focused validation may look like:',
|
|
61
|
+
'',
|
|
62
|
+
'```bash',
|
|
63
|
+
'yarn jest packages/<package> --runInBand --coverage=false',
|
|
64
|
+
'```',
|
|
65
|
+
'',
|
|
66
|
+
'## Documentation',
|
|
67
|
+
'',
|
|
68
|
+
'- Keep `README.md` concise and usage-oriented.',
|
|
69
|
+
'- Start package READMEs with `# <package>`.',
|
|
70
|
+
'- Document real public exports, supported options, and examples that actually work.',
|
|
71
|
+
'- Update docs when public behavior changes.',
|
|
72
|
+
'- Keep generated docs wrappers, such as `doc.mdx`, aligned with the README when the project uses them.',
|
|
73
|
+
'- For component packages, include visual documentation or stories for supported states and common usage.',
|
|
74
|
+
'',
|
|
75
|
+
'## Components',
|
|
76
|
+
'',
|
|
77
|
+
'- Prefer lightweight React components with TypeScript when working in React packages.',
|
|
78
|
+
'- Keep components SSR-friendly and avoid browser globals during render.',
|
|
79
|
+
'- Prefer composable props and predictable ergonomics.',
|
|
80
|
+
'- Put each public component in its own file with a matching test.',
|
|
81
|
+
'- Add stories or examples when a component has visual states, variants, or interaction states.',
|
|
82
|
+
'- Keep styling explicit and reusable. Avoid hidden theme assumptions unless they are part of the package contract.',
|
|
83
|
+
'',
|
|
84
|
+
'## Change Discipline',
|
|
85
|
+
'',
|
|
86
|
+
'- Keep changes scoped to the requested behavior.',
|
|
87
|
+
'- Avoid unrelated refactors and metadata churn.',
|
|
88
|
+
'- Sync implementation, tests, docs, examples, and public re-exports together.',
|
|
89
|
+
'- Do not introduce new dependencies unless they clearly reduce complexity or are already part of the project direction.',
|
|
90
|
+
'- Prefer small, reviewable changes over broad rewrites.',
|
|
91
|
+
'- Preserve existing conventions unless there is a clear reason to change them.',
|
|
92
|
+
'',
|
|
93
|
+
'## Before Finishing',
|
|
94
|
+
'',
|
|
95
|
+
'Check that the change is complete:',
|
|
96
|
+
'',
|
|
97
|
+
'- Public exports are updated.',
|
|
98
|
+
'- Public-surface tests are updated when exports change.',
|
|
99
|
+
'- Matching unit tests exist for new behavior.',
|
|
100
|
+
'- README examples still match the real API.',
|
|
101
|
+
'- Visual docs, stories, or examples are updated for visible component behavior.',
|
|
102
|
+
'- TypeScript imports follow the package module style.',
|
|
103
|
+
'- No unrelated files, formatting churn, or generated artifacts were changed.',
|
|
104
|
+
'',
|
|
105
|
+
].join('\n');
|