@sha3/code 1.0.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/AGENTS.md +75 -0
- package/README.md +554 -0
- package/ai/adapters/codex.md +7 -0
- package/ai/adapters/copilot.md +7 -0
- package/ai/adapters/cursor.md +7 -0
- package/ai/adapters/windsurf.md +8 -0
- package/ai/constitution.md +12 -0
- package/bin/code-standards.mjs +47 -0
- package/biome.json +37 -0
- package/index.mjs +11 -0
- package/lib/cli/parse-args.mjs +416 -0
- package/lib/cli/post-run-guidance.mjs +43 -0
- package/lib/cli/run-init.mjs +123 -0
- package/lib/cli/run-profile.mjs +46 -0
- package/lib/cli/run-refactor.mjs +152 -0
- package/lib/cli/run-verify.mjs +67 -0
- package/lib/constants.mjs +167 -0
- package/lib/contract/load-rule-catalog.mjs +12 -0
- package/lib/contract/render-agents.mjs +79 -0
- package/lib/contract/render-contract-json.mjs +7 -0
- package/lib/contract/resolve-contract.mjs +52 -0
- package/lib/paths.mjs +50 -0
- package/lib/profile.mjs +108 -0
- package/lib/project/ai-instructions.mjs +28 -0
- package/lib/project/biome-ignore.mjs +14 -0
- package/lib/project/managed-files.mjs +105 -0
- package/lib/project/package-metadata.mjs +132 -0
- package/lib/project/prompt-files.mjs +111 -0
- package/lib/project/template-resolution.mjs +70 -0
- package/lib/refactor/materialize-refactor-context.mjs +106 -0
- package/lib/refactor/preservation-questions.mjs +33 -0
- package/lib/refactor/public-contract-extractor.mjs +22 -0
- package/lib/refactor/render-analysis-summary.mjs +50 -0
- package/lib/refactor/source-analysis.mjs +74 -0
- package/lib/utils/fs.mjs +220 -0
- package/lib/utils/prompts.mjs +63 -0
- package/lib/utils/text.mjs +43 -0
- package/lib/verify/change-audit-verifier.mjs +140 -0
- package/lib/verify/change-context.mjs +36 -0
- package/lib/verify/error-handling-verifier.mjs +164 -0
- package/lib/verify/explain-rule.mjs +54 -0
- package/lib/verify/issue-helpers.mjs +132 -0
- package/lib/verify/project-layout-verifier.mjs +259 -0
- package/lib/verify/project-verifier.mjs +267 -0
- package/lib/verify/readme-public-api.mjs +237 -0
- package/lib/verify/readme-verifier.mjs +216 -0
- package/lib/verify/render-json-report.mjs +3 -0
- package/lib/verify/render-text-report.mjs +34 -0
- package/lib/verify/source-analysis.mjs +126 -0
- package/lib/verify/source-rule-verifier.mjs +453 -0
- package/lib/verify/testing-verifier.mjs +113 -0
- package/lib/verify/tooling-verifier.mjs +82 -0
- package/lib/verify/typescript-style-verifier.mjs +407 -0
- package/package.json +55 -0
- package/profiles/default.profile.json +40 -0
- package/profiles/schema.json +96 -0
- package/prompts/init-contract.md +25 -0
- package/prompts/init-phase-2-implement.md +25 -0
- package/prompts/init-phase-3-verify.md +23 -0
- package/prompts/init.prompt.md +24 -0
- package/prompts/refactor-contract.md +26 -0
- package/prompts/refactor-phase-2-rebuild.md +25 -0
- package/prompts/refactor-phase-3-verify.md +24 -0
- package/prompts/refactor.prompt.md +26 -0
- package/resources/ai/AGENTS.md +18 -0
- package/resources/ai/adapters/codex.md +5 -0
- package/resources/ai/adapters/copilot.md +5 -0
- package/resources/ai/adapters/cursor.md +5 -0
- package/resources/ai/adapters/windsurf.md +5 -0
- package/resources/ai/contract.schema.json +68 -0
- package/resources/ai/rule-catalog.json +878 -0
- package/resources/ai/rule-catalog.schema.json +66 -0
- package/resources/ai/templates/adapters/codex.template.md +7 -0
- package/resources/ai/templates/adapters/copilot.template.md +7 -0
- package/resources/ai/templates/adapters/cursor.template.md +7 -0
- package/resources/ai/templates/adapters/windsurf.template.md +7 -0
- package/resources/ai/templates/agents.project.template.md +141 -0
- package/resources/ai/templates/examples/demo/src/billing/billing.service.ts +73 -0
- package/resources/ai/templates/examples/demo/src/config.ts +3 -0
- package/resources/ai/templates/examples/demo/src/invoice/invoice.errors.ts +51 -0
- package/resources/ai/templates/examples/demo/src/invoice/invoice.service.ts +96 -0
- package/resources/ai/templates/examples/demo/src/invoice/invoice.types.ts +9 -0
- package/resources/ai/templates/examples/rules/async-bad.ts +52 -0
- package/resources/ai/templates/examples/rules/async-good.ts +56 -0
- package/resources/ai/templates/examples/rules/class-first-bad.ts +36 -0
- package/resources/ai/templates/examples/rules/class-first-good.ts +74 -0
- package/resources/ai/templates/examples/rules/constructor-bad.ts +68 -0
- package/resources/ai/templates/examples/rules/constructor-good.ts +71 -0
- package/resources/ai/templates/examples/rules/control-flow-bad.ts +31 -0
- package/resources/ai/templates/examples/rules/control-flow-good.ts +54 -0
- package/resources/ai/templates/examples/rules/errors-bad.ts +42 -0
- package/resources/ai/templates/examples/rules/errors-good.ts +23 -0
- package/resources/ai/templates/examples/rules/functions-bad.ts +48 -0
- package/resources/ai/templates/examples/rules/functions-good.ts +58 -0
- package/resources/ai/templates/examples/rules/returns-bad.ts +38 -0
- package/resources/ai/templates/examples/rules/returns-good.ts +44 -0
- package/resources/ai/templates/examples/rules/testing-bad.ts +34 -0
- package/resources/ai/templates/examples/rules/testing-good.ts +54 -0
- package/resources/ai/templates/rules/architecture.md +41 -0
- package/resources/ai/templates/rules/async.md +13 -0
- package/resources/ai/templates/rules/class-first.md +45 -0
- package/resources/ai/templates/rules/control-flow.md +13 -0
- package/resources/ai/templates/rules/errors.md +18 -0
- package/resources/ai/templates/rules/functions.md +29 -0
- package/resources/ai/templates/rules/naming.md +13 -0
- package/resources/ai/templates/rules/readme.md +36 -0
- package/resources/ai/templates/rules/returns.md +13 -0
- package/resources/ai/templates/rules/testing.md +18 -0
- package/resources/ai/templates/rules.project.template.md +66 -0
- package/resources/ai/templates/skills/change-synchronization/SKILL.md +42 -0
- package/resources/ai/templates/skills/feature-shaping/SKILL.md +45 -0
- package/resources/ai/templates/skills/http-api-conventions/SKILL.md +171 -0
- package/resources/ai/templates/skills/init-workflow/SKILL.md +52 -0
- package/resources/ai/templates/skills/readme-authoring/SKILL.md +51 -0
- package/resources/ai/templates/skills/refactor-workflow/SKILL.md +50 -0
- package/resources/ai/templates/skills/simplicity-audit/SKILL.md +41 -0
- package/resources/ai/templates/skills/test-scope-selection/SKILL.md +50 -0
- package/resources/ai/templates/skills.index.template.md +25 -0
- package/standards/architecture.md +72 -0
- package/standards/changelog-policy.md +12 -0
- package/standards/manifest.json +36 -0
- package/standards/readme.md +56 -0
- package/standards/schema.json +124 -0
- package/standards/style.md +106 -0
- package/standards/testing.md +20 -0
- package/standards/tooling.md +38 -0
- package/templates/node-lib/.biomeignore +10 -0
- package/templates/node-lib/.vscode/extensions.json +1 -0
- package/templates/node-lib/.vscode/settings.json +9 -0
- package/templates/node-lib/README.md +172 -0
- package/templates/node-lib/biome.json +37 -0
- package/templates/node-lib/gitignore +6 -0
- package/templates/node-lib/package.json +32 -0
- package/templates/node-lib/scripts/release-publish.mjs +106 -0
- package/templates/node-lib/scripts/run-tests.mjs +65 -0
- package/templates/node-lib/src/config.ts +3 -0
- package/templates/node-lib/src/index.ts +2 -0
- package/templates/node-lib/src/logger.ts +7 -0
- package/templates/node-lib/src/package-info/package-info.service.ts +47 -0
- package/templates/node-lib/test/package-info.test.ts +10 -0
- package/templates/node-lib/tsconfig.build.json +1 -0
- package/templates/node-lib/tsconfig.json +5 -0
- package/templates/node-service/.biomeignore +10 -0
- package/templates/node-service/.vscode/extensions.json +1 -0
- package/templates/node-service/.vscode/settings.json +9 -0
- package/templates/node-service/README.md +244 -0
- package/templates/node-service/biome.json +37 -0
- package/templates/node-service/ecosystem.config.cjs +3 -0
- package/templates/node-service/gitignore +6 -0
- package/templates/node-service/package.json +42 -0
- package/templates/node-service/scripts/release-publish.mjs +106 -0
- package/templates/node-service/scripts/run-tests.mjs +65 -0
- package/templates/node-service/src/app/service-runtime.service.ts +57 -0
- package/templates/node-service/src/app-info/app-info.service.ts +47 -0
- package/templates/node-service/src/config.ts +11 -0
- package/templates/node-service/src/http/http-server.service.ts +66 -0
- package/templates/node-service/src/index.ts +2 -0
- package/templates/node-service/src/logger.ts +7 -0
- package/templates/node-service/src/main.ts +5 -0
- package/templates/node-service/test/service-runtime.test.ts +13 -0
- package/templates/node-service/tsconfig.build.json +1 -0
- package/templates/node-service/tsconfig.json +5 -0
- package/tsconfig/base.json +16 -0
- package/tsconfig/node-lib.json +5 -0
- package/tsconfig/node-service.json +1 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# 📚 {{packageName}}
|
|
2
|
+
|
|
3
|
+
Small TypeScript library that exposes package metadata through one stable, package-root API.
|
|
4
|
+
|
|
5
|
+
## TL;DR
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install
|
|
9
|
+
npm run check
|
|
10
|
+
npm run build
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { PackageInfoService } from "{{packageName}}";
|
|
15
|
+
|
|
16
|
+
const packageInfoService = PackageInfoService.createDefault();
|
|
17
|
+
console.log(packageInfoService.readPackageInfo());
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Why
|
|
21
|
+
|
|
22
|
+
Use this package when other modules, diagnostics code, or adapters need the package name without reaching into private files.
|
|
23
|
+
It gives consumers one stable import path and keeps the metadata mapping in a single service instead of scattering string literals through the codebase.
|
|
24
|
+
|
|
25
|
+
## Main Capabilities
|
|
26
|
+
|
|
27
|
+
- Exposes package metadata through a stable package-root import.
|
|
28
|
+
- Keeps the configured package name behind one service instead of repeating config access in callers.
|
|
29
|
+
- Returns plain serializable data that is easy to log, test, or hand to HTTP or CLI adapters.
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install {{packageName}}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import { PackageInfoService } from "{{packageName}}";
|
|
41
|
+
|
|
42
|
+
const packageInfoService = PackageInfoService.createDefault();
|
|
43
|
+
const packageInfo = packageInfoService.readPackageInfo();
|
|
44
|
+
|
|
45
|
+
console.log(packageInfo.packageName);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
This is the intended integration path: import from the package root, create the default service once, and reuse it anywhere you need package metadata.
|
|
49
|
+
|
|
50
|
+
## Examples
|
|
51
|
+
|
|
52
|
+
Read package metadata for logging or diagnostics:
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import { PackageInfoService, type PackageInfo } from "{{packageName}}";
|
|
56
|
+
|
|
57
|
+
const packageInfoService = PackageInfoService.createDefault();
|
|
58
|
+
const packageInfo: PackageInfo = packageInfoService.readPackageInfo();
|
|
59
|
+
|
|
60
|
+
console.log(`[package] ${packageInfo.packageName}`);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Create the default service once and reuse it across boundaries:
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import { PackageInfoService } from "{{packageName}}";
|
|
67
|
+
|
|
68
|
+
const packageInfoService = PackageInfoService.createDefault();
|
|
69
|
+
const firstRead = packageInfoService.readPackageInfo();
|
|
70
|
+
const secondRead = packageInfoService.readPackageInfo();
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Public API
|
|
74
|
+
|
|
75
|
+
### `PackageInfoService`
|
|
76
|
+
|
|
77
|
+
Primary entrypoint for reading the package metadata exposed by the library.
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
import { PackageInfoService } from "{{packageName}}";
|
|
81
|
+
|
|
82
|
+
const packageInfoService = PackageInfoService.createDefault();
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
#### `createDefault()`
|
|
86
|
+
|
|
87
|
+
Creates a `PackageInfoService` using the package configuration.
|
|
88
|
+
|
|
89
|
+
Parameters:
|
|
90
|
+
|
|
91
|
+
- none
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
|
|
95
|
+
- a ready-to-use `PackageInfoService`
|
|
96
|
+
|
|
97
|
+
Behavior notes:
|
|
98
|
+
|
|
99
|
+
- reads the default package name from `config.PACKAGE_NAME`
|
|
100
|
+
- keeps callers out of `src/config.ts`
|
|
101
|
+
- gives consumers a stable construction path that stays valid if the internals change
|
|
102
|
+
|
|
103
|
+
#### `readPackageInfo()`
|
|
104
|
+
|
|
105
|
+
Reads the package metadata exposed by the public API.
|
|
106
|
+
|
|
107
|
+
Parameters:
|
|
108
|
+
|
|
109
|
+
- none
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
|
|
113
|
+
- a `PackageInfo` object containing the configured package name
|
|
114
|
+
|
|
115
|
+
Behavior notes:
|
|
116
|
+
|
|
117
|
+
- returns a plain serializable object
|
|
118
|
+
- performs no filesystem or network I/O
|
|
119
|
+
- is safe to call from application code, tests, and adapters
|
|
120
|
+
|
|
121
|
+
### `PackageInfo`
|
|
122
|
+
|
|
123
|
+
Public type that describes the shape returned by `readPackageInfo()`.
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
type PackageInfo = { packageName: string };
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Compatibility
|
|
130
|
+
|
|
131
|
+
- Node.js 20+
|
|
132
|
+
- ESM (`"type": "module"`)
|
|
133
|
+
- Strict TypeScript
|
|
134
|
+
|
|
135
|
+
## Configuration
|
|
136
|
+
|
|
137
|
+
Configuration is centralized in `src/config.ts`.
|
|
138
|
+
|
|
139
|
+
- `config.PACKAGE_NAME`: package name returned by `readPackageInfo()` and exposed to consumers of this package.
|
|
140
|
+
|
|
141
|
+
## Scripts
|
|
142
|
+
|
|
143
|
+
- `npm run standards:check`: verify deterministic project contract rules
|
|
144
|
+
- `npm run check`: standards + lint + format + typecheck + tests
|
|
145
|
+
- `npm run fix`: Biome autofix + format write
|
|
146
|
+
- `npm run build`: compile to `dist/`
|
|
147
|
+
- `npm run test`: run Node test runner with `tsx`
|
|
148
|
+
|
|
149
|
+
## Structure
|
|
150
|
+
|
|
151
|
+
- `src/config.ts`: canonical hardcoded configuration
|
|
152
|
+
- `src/package-info/package-info.service.ts`: implementation of the public metadata service
|
|
153
|
+
- `src/index.ts`: public exports
|
|
154
|
+
- `test/package-info.test.ts`: behavior test
|
|
155
|
+
|
|
156
|
+
## Troubleshooting
|
|
157
|
+
|
|
158
|
+
### Import errors
|
|
159
|
+
|
|
160
|
+
Ensure the consumer project supports Node.js ESM and TypeScript extension rewriting.
|
|
161
|
+
|
|
162
|
+
### Standards warnings
|
|
163
|
+
|
|
164
|
+
Run `npm run standards:check` for contract errors, README coverage problems, and advisory warnings from the project verifier.
|
|
165
|
+
|
|
166
|
+
## AI Workflow
|
|
167
|
+
|
|
168
|
+
- Read `AGENTS.md`, `ai/contract.json`, and the relevant `ai/<assistant>.md` file before coding.
|
|
169
|
+
- Do not edit managed contract/tooling files during normal implementation.
|
|
170
|
+
- Keep this README focused on consumer-facing behavior, configuration, and public API.
|
|
171
|
+
- Rewrite examples and API notes whenever the exported behavior changes.
|
|
172
|
+
- Run `npm run check` before finalizing changes.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"files": {
|
|
3
|
+
"ignoreUnknown": true,
|
|
4
|
+
"ignore": [".code-standards", "dist"]
|
|
5
|
+
},
|
|
6
|
+
"formatter": {
|
|
7
|
+
"enabled": true,
|
|
8
|
+
"indentStyle": "space",
|
|
9
|
+
"indentWidth": 2,
|
|
10
|
+
"lineWidth": 160
|
|
11
|
+
},
|
|
12
|
+
"linter": {
|
|
13
|
+
"enabled": true,
|
|
14
|
+
"rules": {
|
|
15
|
+
"recommended": true,
|
|
16
|
+
"correctness": {
|
|
17
|
+
"noUnusedFunctionParameters": "error",
|
|
18
|
+
"noUnusedImports": "error",
|
|
19
|
+
"noUnusedPrivateClassMembers": "error",
|
|
20
|
+
"noUnusedVariables": "error"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"javascript": {
|
|
25
|
+
"formatter": {
|
|
26
|
+
"quoteStyle": "double",
|
|
27
|
+
"semicolons": "always",
|
|
28
|
+
"arrowParentheses": "always",
|
|
29
|
+
"bracketSpacing": true
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"json": {
|
|
33
|
+
"formatter": {
|
|
34
|
+
"enabled": true
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{packageName}}",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": ["dist"],
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc -p tsconfig.build.json",
|
|
11
|
+
"check": "npm run standards:check && npm run lint && npm run format:check && npm run typecheck && npm run test",
|
|
12
|
+
"fix": "npm run lint:fix && npm run format:write",
|
|
13
|
+
"lint": "biome check .",
|
|
14
|
+
"lint:fix": "biome check . --write",
|
|
15
|
+
"format:check": "biome format .",
|
|
16
|
+
"format:write": "biome format . --write",
|
|
17
|
+
"standards:check": "code-standards verify",
|
|
18
|
+
"typecheck": "tsc --noEmit",
|
|
19
|
+
"test": "node scripts/run-tests.mjs",
|
|
20
|
+
"publish": "node scripts/release-publish.mjs"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@sha3/logger": "^2.0.0"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@sha3/code": "^{{packageVersion}}",
|
|
27
|
+
"@biomejs/biome": "^1.9.4",
|
|
28
|
+
"@types/node": "^22.13.10",
|
|
29
|
+
"tsx": "^4.19.3",
|
|
30
|
+
"typescript": "^5.8.2"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
function run(command, args, cwd, stdio = "inherit") {
|
|
7
|
+
return new Promise((resolve, reject) => {
|
|
8
|
+
const child = spawn(command, args, { cwd, stdio, shell: process.platform === "win32" });
|
|
9
|
+
let stdout = "";
|
|
10
|
+
let stderr = "";
|
|
11
|
+
|
|
12
|
+
if (stdio === "pipe") {
|
|
13
|
+
child.stdout.on("data", (chunk) => {
|
|
14
|
+
stdout += String(chunk);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
child.stderr.on("data", (chunk) => {
|
|
18
|
+
stderr += String(chunk);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
child.on("error", reject);
|
|
23
|
+
child.on("exit", (code) => {
|
|
24
|
+
resolve({ code: code ?? 1, stdout, stderr });
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function isNotFoundError(output) {
|
|
30
|
+
const lower = output.toLowerCase();
|
|
31
|
+
return lower.includes("e404") || lower.includes("404 not found") || lower.includes("no match found");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function parseSemver(version) {
|
|
35
|
+
const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(version);
|
|
36
|
+
if (!match) {
|
|
37
|
+
throw new Error(`Unsupported version format: ${version}. Expected x.y.z`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return { major: Number.parseInt(match[1], 10), minor: Number.parseInt(match[2], 10), patch: Number.parseInt(match[3], 10) };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function bumpMinor(version) {
|
|
44
|
+
const parsed = parseSemver(version);
|
|
45
|
+
return `${parsed.major}.${parsed.minor + 1}.0`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function readPackageJson(packageJsonPath) {
|
|
49
|
+
const raw = await readFile(packageJsonPath, "utf8");
|
|
50
|
+
return JSON.parse(raw);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function writePackageJson(packageJsonPath, pkg) {
|
|
54
|
+
await writeFile(packageJsonPath, `${JSON.stringify(pkg, null, 2)}\n`, "utf8");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function versionExistsOnNpm(packageName, version, cwd) {
|
|
58
|
+
const result = await run("npm", ["view", `${packageName}@${version}`, "version", "--json"], cwd, "pipe");
|
|
59
|
+
const combined = `${result.stdout}\n${result.stderr}`;
|
|
60
|
+
if (result.code === 0) {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
if (isNotFoundError(combined)) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
throw new Error(`Unable to verify npm version ${packageName}@${version}: ${combined.trim()}`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function main() {
|
|
71
|
+
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
|
72
|
+
const projectRoot = path.resolve(scriptDir, "..");
|
|
73
|
+
const packageJsonPath = path.join(projectRoot, "package.json");
|
|
74
|
+
const packageJson = await readPackageJson(packageJsonPath);
|
|
75
|
+
if (typeof packageJson.name !== "string" || packageJson.name.length === 0) {
|
|
76
|
+
throw new Error("package.json name is required.");
|
|
77
|
+
}
|
|
78
|
+
if (typeof packageJson.version !== "string" || packageJson.version.length === 0) {
|
|
79
|
+
throw new Error("package.json version is required.");
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const exists = await versionExistsOnNpm(packageJson.name, packageJson.version, projectRoot);
|
|
83
|
+
if (exists) {
|
|
84
|
+
const nextVersion = bumpMinor(packageJson.version);
|
|
85
|
+
packageJson.version = nextVersion;
|
|
86
|
+
await writePackageJson(packageJsonPath, packageJson);
|
|
87
|
+
console.log(`Version ${packageJson.name}@${packageJson.version} already existed. Bumped to ${nextVersion}.`);
|
|
88
|
+
} else {
|
|
89
|
+
console.log(`Publishing new version ${packageJson.name}@${packageJson.version}.`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const build = await run("npm", ["run", "build"], projectRoot);
|
|
93
|
+
if (build.code !== 0) {
|
|
94
|
+
throw new Error("npm run build failed.");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const publish = await run("npm", ["publish", "--access", "public", "--ignore-scripts"], projectRoot);
|
|
98
|
+
if (publish.code !== 0) {
|
|
99
|
+
throw new Error("npm publish failed.");
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
main().catch((error) => {
|
|
104
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
105
|
+
process.exit(1);
|
|
106
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { readdir } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
const rootDir = process.cwd();
|
|
6
|
+
const testRoots = ["test", "src"];
|
|
7
|
+
|
|
8
|
+
async function collectTestsFrom(directoryPath, collector) {
|
|
9
|
+
let entries;
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
entries = await readdir(directoryPath, { withFileTypes: true });
|
|
13
|
+
} catch (error) {
|
|
14
|
+
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
throw error;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
for (const entry of entries) {
|
|
22
|
+
const absolutePath = path.join(directoryPath, entry.name);
|
|
23
|
+
if (entry.isDirectory()) {
|
|
24
|
+
await collectTestsFrom(absolutePath, collector);
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
if (!entry.isFile()) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (entry.name.endsWith(".test.ts")) {
|
|
31
|
+
collector.push(path.relative(rootDir, absolutePath));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function runNodeTests(files) {
|
|
37
|
+
return new Promise((resolve, reject) => {
|
|
38
|
+
const args = ["--import", "tsx", "--test", ...files];
|
|
39
|
+
const child = spawn(process.execPath, args, { cwd: rootDir, stdio: "inherit", shell: process.platform === "win32" });
|
|
40
|
+
|
|
41
|
+
child.on("error", reject);
|
|
42
|
+
child.on("exit", (code) => {
|
|
43
|
+
resolve(code ?? 1);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function main() {
|
|
49
|
+
const testFiles = [];
|
|
50
|
+
|
|
51
|
+
for (const root of testRoots) {
|
|
52
|
+
await collectTestsFrom(path.join(rootDir, root), testFiles);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const uniqueFiles = [...new Set(testFiles)].sort((left, right) => left.localeCompare(right));
|
|
56
|
+
const exitCode = await runNodeTests(uniqueFiles);
|
|
57
|
+
if (exitCode !== 0) {
|
|
58
|
+
process.exit(exitCode);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
main().catch((error) => {
|
|
63
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
64
|
+
process.exit(1);
|
|
65
|
+
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import Logger from "@sha3/logger";
|
|
2
|
+
|
|
3
|
+
const PACKAGE_NAME = "{{packageName}}";
|
|
4
|
+
const LOGGER_NAME = PACKAGE_NAME.startsWith("@") ? PACKAGE_NAME.split("/")[1] || PACKAGE_NAME : PACKAGE_NAME;
|
|
5
|
+
const logger = new Logger({ loggerName: LOGGER_NAME });
|
|
6
|
+
|
|
7
|
+
export default logger;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @section imports:internals
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import config from "../config.ts";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @section types
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export type PackageInfo = { packageName: string };
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @section class
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
export class PackageInfoService {
|
|
18
|
+
/**
|
|
19
|
+
* @section private:attributes
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
private readonly packageName: string;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @section constructor
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
public constructor(packageName: string) {
|
|
29
|
+
this.packageName = packageName;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @section factory
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
public static createDefault(): PackageInfoService {
|
|
37
|
+
return new PackageInfoService(config.PACKAGE_NAME);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @section public:methods
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
public readPackageInfo(): PackageInfo {
|
|
45
|
+
return { packageName: this.packageName };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as assert from "node:assert/strict";
|
|
2
|
+
import { test } from "node:test";
|
|
3
|
+
|
|
4
|
+
import { PackageInfoService } from "../src/index.ts";
|
|
5
|
+
|
|
6
|
+
test("PackageInfoService exposes the configured package name", () => {
|
|
7
|
+
const packageInfoService = PackageInfoService.createDefault();
|
|
8
|
+
|
|
9
|
+
assert.deepEqual(packageInfoService.readPackageInfo(), { packageName: "{{packageName}}" });
|
|
10
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "extends": "./tsconfig.json", "compilerOptions": { "noEmit": false, "rootDir": "./src", "outDir": "./dist" }, "exclude": ["test/**/*.ts"] }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "recommendations": ["biomejs.biome"], "unwantedRecommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode", "rvest.vs-code-prettier-eslint"] }
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"editor.formatOnSave": true,
|
|
3
|
+
"editor.formatOnSaveMode": "file",
|
|
4
|
+
"editor.defaultFormatter": "biomejs.biome",
|
|
5
|
+
"[typescript]": { "editor.defaultFormatter": "biomejs.biome", "editor.formatOnSave": true },
|
|
6
|
+
"[javascript]": { "editor.defaultFormatter": "biomejs.biome", "editor.formatOnSave": true },
|
|
7
|
+
"[json]": { "editor.defaultFormatter": "biomejs.biome", "editor.formatOnSave": true },
|
|
8
|
+
"[jsonc]": { "editor.defaultFormatter": "biomejs.biome", "editor.formatOnSave": true }
|
|
9
|
+
}
|