create-claude-webapp 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/.husky/pre-commit +1 -0
- package/.husky/pre-push +3 -0
- package/.madgerc +14 -0
- package/.tsprunerc +11 -0
- package/CLAUDE.md +102 -0
- package/LICENSE +21 -0
- package/README.md +322 -0
- package/bin/create-project.js +87 -0
- package/biome.json +51 -0
- package/docs/guides/en/quickstart.md +109 -0
- package/docs/guides/en/rule-editing-guide.md +264 -0
- package/docs/guides/en/use-cases.md +308 -0
- package/package.json +99 -0
- package/scripts/check-unused-exports.js +69 -0
- package/scripts/cleanup-test-processes.sh +32 -0
- package/scripts/post-setup.js +110 -0
- package/scripts/setup-project.js +150 -0
- package/scripts/show-coverage.js +74 -0
- package/src/index.ts +11 -0
- package/templates/.gitignore.template +52 -0
- package/tsconfig.json +50 -0
- package/vitest.config.mjs +47 -0
package/package.json
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-claude-webapp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"packageManager": "npm@10.8.2",
|
|
5
|
+
"description": "TypeScript boilerplate with skills and sub-agents for Claude Code. Prevents context exhaustion through role-based task splitting.",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"typescript",
|
|
8
|
+
"boilerplate",
|
|
9
|
+
"template",
|
|
10
|
+
"starter",
|
|
11
|
+
"scaffolding",
|
|
12
|
+
"cli",
|
|
13
|
+
"claude-code",
|
|
14
|
+
"anthropic",
|
|
15
|
+
"ai-development",
|
|
16
|
+
"llm-development",
|
|
17
|
+
"skills",
|
|
18
|
+
"sub-agents",
|
|
19
|
+
"agentic",
|
|
20
|
+
"workflow",
|
|
21
|
+
"multi-agent"
|
|
22
|
+
],
|
|
23
|
+
"author": "Sven Malvik",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "git+https://github.com/svenmalvik/claude-boilerplate-4-web.git"
|
|
28
|
+
},
|
|
29
|
+
"bin": {
|
|
30
|
+
"create-claude-webapp": "bin/create-project.js"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "tsc && tsc-alias",
|
|
34
|
+
"build:backend": "tsc && tsc-alias",
|
|
35
|
+
"build:frontend": "vite build",
|
|
36
|
+
"dev": "tsx src/index.ts",
|
|
37
|
+
"dev:frontend": "vite",
|
|
38
|
+
"watch": "tsx watch src/index.ts",
|
|
39
|
+
"preview": "vite preview",
|
|
40
|
+
"type-check": "tsc --noEmit",
|
|
41
|
+
"test": "vitest run",
|
|
42
|
+
"test:ui": "vitest --ui",
|
|
43
|
+
"test:coverage": "vitest run --coverage",
|
|
44
|
+
"test:coverage:summary": "node scripts/show-coverage.js",
|
|
45
|
+
"test:coverage:clean": "rm -rf coverage .vitest-cache",
|
|
46
|
+
"test:coverage:fresh": "npm run test:coverage:clean && npm run test:coverage",
|
|
47
|
+
"test:watch": "vitest",
|
|
48
|
+
"format": "biome format --write src",
|
|
49
|
+
"format:check": "biome format src",
|
|
50
|
+
"lint": "biome lint src",
|
|
51
|
+
"lint:fix": "biome lint --write src",
|
|
52
|
+
"check": "biome check src",
|
|
53
|
+
"check:fix": "biome check --write src",
|
|
54
|
+
"check:unused": "node scripts/check-unused-exports.js",
|
|
55
|
+
"check:unused:all": "ts-prune --project tsconfig.json --ignore 'src/index.ts|__tests__|test|vitest'",
|
|
56
|
+
"check:deps": "madge --circular --extensions ts src",
|
|
57
|
+
"check:deps:graph": "madge --extensions ts --image graph.svg src",
|
|
58
|
+
"check:code": "npm run check && npm run check:unused && npm run check:deps && npm run build",
|
|
59
|
+
"check:all": "npm run check:code && npm run test",
|
|
60
|
+
"cleanup:processes": "bash ./scripts/cleanup-test-processes.sh",
|
|
61
|
+
"test:safe": "npm test && npm run cleanup:processes",
|
|
62
|
+
"prepare": "husky"
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"@biomejs/biome": "^1.9.4",
|
|
66
|
+
"@testing-library/jest-dom": "^6.6.3",
|
|
67
|
+
"@testing-library/react": "^16.0.0",
|
|
68
|
+
"@types/node": "^20.0.0",
|
|
69
|
+
"@types/react": "^18.0.0",
|
|
70
|
+
"@types/react-dom": "^18.0.0",
|
|
71
|
+
"@vitejs/plugin-react": "^4.0.0",
|
|
72
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
73
|
+
"@vitest/ui": "^3.2.4",
|
|
74
|
+
"c8": "^10.1.3",
|
|
75
|
+
"fast-check": "^4.3.0",
|
|
76
|
+
"husky": "^9.1.7",
|
|
77
|
+
"jsdom": "^25.0.0",
|
|
78
|
+
"lint-staged": "^16.1.0",
|
|
79
|
+
"madge": "^8.0.0",
|
|
80
|
+
"react": "^18.0.0",
|
|
81
|
+
"react-dom": "^18.0.0",
|
|
82
|
+
"ts-node": "^10.9.1",
|
|
83
|
+
"ts-prune": "^0.10.3",
|
|
84
|
+
"tsc-alias": "^1.8.7",
|
|
85
|
+
"tsx": "^4.19.4",
|
|
86
|
+
"typescript": "^5.0.0",
|
|
87
|
+
"vite": "^5.0.0",
|
|
88
|
+
"vitest": "^3.2.4"
|
|
89
|
+
},
|
|
90
|
+
"engines": {
|
|
91
|
+
"node": ">=20"
|
|
92
|
+
},
|
|
93
|
+
"lint-staged": {
|
|
94
|
+
"src/**/*.{ts,tsx}": [
|
|
95
|
+
"biome check --write --no-errors-on-unmatched",
|
|
96
|
+
"biome format --write --no-errors-on-unmatched"
|
|
97
|
+
]
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Unused exports checker script
|
|
5
|
+
* Filters out "used in module" from ts-prune output to show only truly unused exports
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { execSync } = require('child_process')
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
// Run ts-prune
|
|
12
|
+
const output = execSync(
|
|
13
|
+
'npx ts-prune --project tsconfig.json --ignore "src/index.ts|__tests__|test|vitest"',
|
|
14
|
+
{ encoding: 'utf8' }
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
// Process each line
|
|
18
|
+
const lines = output.split('\n').filter(line => line.trim())
|
|
19
|
+
const results = {
|
|
20
|
+
usedInModule: [],
|
|
21
|
+
trulyUnused: [],
|
|
22
|
+
total: 0
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
for (const line of lines) {
|
|
26
|
+
if (line.includes(' - ')) {
|
|
27
|
+
results.total++
|
|
28
|
+
if (line.includes('(used in module)')) {
|
|
29
|
+
results.usedInModule.push(line)
|
|
30
|
+
} else {
|
|
31
|
+
results.trulyUnused.push(line)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Display results
|
|
37
|
+
console.log('=== Unused Exports Analysis ===\n')
|
|
38
|
+
|
|
39
|
+
if (results.trulyUnused.length > 0) {
|
|
40
|
+
console.log(`š“ Truly unused exports: ${results.trulyUnused.length}`)
|
|
41
|
+
console.log('ā'.repeat(50))
|
|
42
|
+
results.trulyUnused.forEach(line => console.log(line))
|
|
43
|
+
console.log('')
|
|
44
|
+
} else {
|
|
45
|
+
console.log('ā
No truly unused exports found\n')
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (results.usedInModule.length > 0) {
|
|
49
|
+
console.log(`ā ļø Used only in module (unnecessary exports): ${results.usedInModule.length}`)
|
|
50
|
+
console.log('ā'.repeat(50))
|
|
51
|
+
results.usedInModule.forEach(line => console.log(line))
|
|
52
|
+
console.log('')
|
|
53
|
+
} else {
|
|
54
|
+
console.log('ā
No unnecessary internal exports found\n')
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Summary
|
|
58
|
+
console.log('=== Summary ===')
|
|
59
|
+
console.log(`Total unnecessary exports: ${results.total}`)
|
|
60
|
+
console.log(`āāā Truly unused: ${results.trulyUnused.length} (delete immediately)`)
|
|
61
|
+
console.log(`āāā Used in module only: ${results.usedInModule.length} (remove export keyword)`)
|
|
62
|
+
|
|
63
|
+
// Exit code
|
|
64
|
+
process.exit(results.trulyUnused.length > 0 ? 1 : 0)
|
|
65
|
+
|
|
66
|
+
} catch (error) {
|
|
67
|
+
console.error('Error occurred:', error.message)
|
|
68
|
+
process.exit(1)
|
|
69
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Vitest process cleanup script
|
|
4
|
+
# Check and remove zombie processes after test execution
|
|
5
|
+
|
|
6
|
+
echo "š Checking for remaining test processes..."
|
|
7
|
+
|
|
8
|
+
# Vitest process check
|
|
9
|
+
VITEST_PROCESSES=$(ps aux | grep vitest | grep -v grep || true)
|
|
10
|
+
if [ -n "$VITEST_PROCESSES" ]; then
|
|
11
|
+
echo "ā ļø Found vitest processes:"
|
|
12
|
+
echo "$VITEST_PROCESSES"
|
|
13
|
+
echo "š„ Killing vitest processes..."
|
|
14
|
+
pkill -f vitest
|
|
15
|
+
echo "ā
Vitest processes cleaned up"
|
|
16
|
+
else
|
|
17
|
+
echo "ā
No vitest processes found"
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
# Node test process check
|
|
21
|
+
NODE_TEST_PROCESSES=$(ps aux | grep "node.*test" | grep -v grep || true)
|
|
22
|
+
if [ -n "$NODE_TEST_PROCESSES" ]; then
|
|
23
|
+
echo "ā ļø Found node test processes:"
|
|
24
|
+
echo "$NODE_TEST_PROCESSES"
|
|
25
|
+
echo "š„ Killing node test processes..."
|
|
26
|
+
pkill -f "node.*test"
|
|
27
|
+
echo "ā
Node test processes cleaned up"
|
|
28
|
+
else
|
|
29
|
+
echo "ā
No node test processes found"
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
echo "š§¹ Process cleanup completed!"
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { execSync } = require('child_process');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Initialize git repository
|
|
9
|
+
*/
|
|
10
|
+
function initGit() {
|
|
11
|
+
try {
|
|
12
|
+
console.log('š§ Initializing git repository...');
|
|
13
|
+
execSync('git init', { stdio: 'inherit' });
|
|
14
|
+
|
|
15
|
+
console.log('š Creating initial commit...');
|
|
16
|
+
execSync('git add -A', { stdio: 'inherit' });
|
|
17
|
+
execSync('git commit -m "Initial commit from AI Coding Project Boilerplate"', { stdio: 'inherit' });
|
|
18
|
+
|
|
19
|
+
return true;
|
|
20
|
+
} catch (error) {
|
|
21
|
+
console.warn('ā ļø Git initialization skipped (git might not be installed)');
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Clean up files that shouldn't be in the user's project
|
|
28
|
+
*/
|
|
29
|
+
function cleanupFiles() {
|
|
30
|
+
console.log('š§¹ Cleaning up setup files...');
|
|
31
|
+
|
|
32
|
+
const filesToRemove = [
|
|
33
|
+
'bin/create-project.js',
|
|
34
|
+
'templates/.gitignore.template',
|
|
35
|
+
'scripts/setup-project.js',
|
|
36
|
+
'scripts/post-setup.js'
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
const dirsToRemove = [
|
|
40
|
+
'bin',
|
|
41
|
+
'templates'
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
// Remove files
|
|
45
|
+
for (const file of filesToRemove) {
|
|
46
|
+
const filePath = path.join(process.cwd(), file);
|
|
47
|
+
if (fs.existsSync(filePath)) {
|
|
48
|
+
fs.unlinkSync(filePath);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Remove empty directories
|
|
53
|
+
for (const dir of dirsToRemove) {
|
|
54
|
+
const dirPath = path.join(process.cwd(), dir);
|
|
55
|
+
if (fs.existsSync(dirPath)) {
|
|
56
|
+
try {
|
|
57
|
+
fs.rmdirSync(dirPath);
|
|
58
|
+
} catch (e) {
|
|
59
|
+
// Directory might not be empty, ignore
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Show next steps to the user
|
|
67
|
+
*/
|
|
68
|
+
function showNextSteps() {
|
|
69
|
+
const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8'));
|
|
70
|
+
const projectName = packageJson.name;
|
|
71
|
+
|
|
72
|
+
console.log('\nš Project setup completed successfully!\n');
|
|
73
|
+
console.log('š Next steps:\n');
|
|
74
|
+
console.log(` cd ${projectName}`);
|
|
75
|
+
console.log(' npm install');
|
|
76
|
+
console.log(' npm run dev\n');
|
|
77
|
+
console.log('š Language switching:\n');
|
|
78
|
+
console.log(' npm run lang:ja # Switch to Japanese');
|
|
79
|
+
console.log(' npm run lang:en # Switch to English\n');
|
|
80
|
+
console.log('š Available commands:\n');
|
|
81
|
+
console.log(' npm run dev # Start development');
|
|
82
|
+
console.log(' npm run build # Build for production');
|
|
83
|
+
console.log(' npm run test # Run tests');
|
|
84
|
+
console.log(' npm run check:all # Run all quality checks\n');
|
|
85
|
+
console.log('Happy coding! š\n');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Main post-setup process
|
|
90
|
+
*/
|
|
91
|
+
function main() {
|
|
92
|
+
try {
|
|
93
|
+
// Initialize git repository
|
|
94
|
+
initGit();
|
|
95
|
+
|
|
96
|
+
// Clean up setup-specific files
|
|
97
|
+
cleanupFiles();
|
|
98
|
+
|
|
99
|
+
// Show next steps
|
|
100
|
+
showNextSteps();
|
|
101
|
+
} catch (error) {
|
|
102
|
+
console.error(`ā Post-setup failed: ${error.message}`);
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Run post-setup
|
|
108
|
+
if (require.main === module) {
|
|
109
|
+
main();
|
|
110
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { execSync } = require('child_process');
|
|
6
|
+
|
|
7
|
+
// Get command line arguments
|
|
8
|
+
const projectName = process.argv[2];
|
|
9
|
+
|
|
10
|
+
if (!projectName) {
|
|
11
|
+
console.error('ā Project name is required');
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const sourceRoot = path.join(__dirname, '..');
|
|
16
|
+
const targetRoot = path.resolve(process.cwd(), projectName);
|
|
17
|
+
|
|
18
|
+
// Files and directories to exclude from copying
|
|
19
|
+
const excludeList = [
|
|
20
|
+
'node_modules',
|
|
21
|
+
'.git',
|
|
22
|
+
'dist',
|
|
23
|
+
'coverage',
|
|
24
|
+
'.vitest-cache',
|
|
25
|
+
'tmp',
|
|
26
|
+
'.claudelang',
|
|
27
|
+
'docs/rules',
|
|
28
|
+
'docs/guides/sub-agents.md',
|
|
29
|
+
'bin', // Exclude bin directory for production use
|
|
30
|
+
'templates' // Exclude templates directory for production use
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
// Files to process with template replacements
|
|
34
|
+
const templateFiles = [
|
|
35
|
+
'package.json',
|
|
36
|
+
'README.md'
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Recursively copy directory with exclusions
|
|
41
|
+
*/
|
|
42
|
+
function copyDirectory(source, target, projectName, rootSource = source) {
|
|
43
|
+
if (!fs.existsSync(source)) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Create target directory
|
|
48
|
+
if (!fs.existsSync(target)) {
|
|
49
|
+
fs.mkdirSync(target, { recursive: true });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const entries = fs.readdirSync(source, { withFileTypes: true });
|
|
53
|
+
|
|
54
|
+
for (const entry of entries) {
|
|
55
|
+
const sourcePath = path.join(source, entry.name);
|
|
56
|
+
const targetPath = path.join(target, entry.name);
|
|
57
|
+
const relativePath = path.relative(rootSource, sourcePath);
|
|
58
|
+
|
|
59
|
+
// Check if should exclude
|
|
60
|
+
const shouldExclude = excludeList.some(exclude => {
|
|
61
|
+
const excludePath = path.normalize(exclude);
|
|
62
|
+
const relativeNormalized = path.normalize(relativePath);
|
|
63
|
+
return relativeNormalized === excludePath || relativeNormalized.startsWith(excludePath + path.sep);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
if (shouldExclude) {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (entry.isDirectory()) {
|
|
71
|
+
copyDirectory(sourcePath, targetPath, projectName, rootSource);
|
|
72
|
+
} else {
|
|
73
|
+
// Check if it's a template file that needs processing
|
|
74
|
+
if (templateFiles.includes(entry.name)) {
|
|
75
|
+
processTemplateFile(sourcePath, targetPath, projectName);
|
|
76
|
+
} else {
|
|
77
|
+
fs.copyFileSync(sourcePath, targetPath);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Process template files with replacements
|
|
85
|
+
*/
|
|
86
|
+
function processTemplateFile(source, target, projectName) {
|
|
87
|
+
let content = fs.readFileSync(source, 'utf8');
|
|
88
|
+
|
|
89
|
+
// Replace placeholders based on file
|
|
90
|
+
const fileName = path.basename(source);
|
|
91
|
+
|
|
92
|
+
if (fileName === 'package.json') {
|
|
93
|
+
const packageJson = JSON.parse(content);
|
|
94
|
+
packageJson.name = projectName;
|
|
95
|
+
packageJson.version = '0.1.0';
|
|
96
|
+
packageJson.description = `${projectName} - AI-powered TypeScript project`;
|
|
97
|
+
|
|
98
|
+
// Remove bin field for user projects
|
|
99
|
+
delete packageJson.bin;
|
|
100
|
+
|
|
101
|
+
// Remove scripts related to package maintenance
|
|
102
|
+
delete packageJson.scripts['lang:status'];
|
|
103
|
+
delete packageJson.scripts.postinstall;
|
|
104
|
+
|
|
105
|
+
content = JSON.stringify(packageJson, null, 2);
|
|
106
|
+
} else if (fileName === 'README.md') {
|
|
107
|
+
// Replace project name in README
|
|
108
|
+
content = content.replace(/claude-boilerplate-4-web/g, projectName);
|
|
109
|
+
content = content.replace(/AI Coding Project Boilerplate/g, projectName);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
fs.writeFileSync(target, content);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Main setup process
|
|
117
|
+
*/
|
|
118
|
+
async function setupProject() {
|
|
119
|
+
try {
|
|
120
|
+
console.log('š Creating project directory...');
|
|
121
|
+
fs.mkdirSync(targetRoot, { recursive: true });
|
|
122
|
+
|
|
123
|
+
console.log('š Copying project files...');
|
|
124
|
+
copyDirectory(sourceRoot, targetRoot, projectName);
|
|
125
|
+
|
|
126
|
+
// Change to project directory
|
|
127
|
+
process.chdir(targetRoot);
|
|
128
|
+
|
|
129
|
+
console.log('š§ Running post-setup tasks...');
|
|
130
|
+
const postSetupScript = path.join(sourceRoot, 'scripts', 'post-setup.js');
|
|
131
|
+
if (fs.existsSync(postSetupScript)) {
|
|
132
|
+
execSync(`node ${postSetupScript}`, { stdio: 'inherit', cwd: targetRoot });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
console.log('ā
Project setup completed!');
|
|
136
|
+
} catch (error) {
|
|
137
|
+
console.error(`ā Setup failed: ${error.message}`);
|
|
138
|
+
|
|
139
|
+
// Cleanup on failure
|
|
140
|
+
if (fs.existsSync(targetRoot)) {
|
|
141
|
+
console.log('š§¹ Cleaning up...');
|
|
142
|
+
fs.rmSync(targetRoot, { recursive: true, force: true });
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Run setup
|
|
150
|
+
setupProject();
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs')
|
|
4
|
+
const path = require('path')
|
|
5
|
+
|
|
6
|
+
const coverageFile = path.join(__dirname, '..', 'coverage', 'coverage-final.json')
|
|
7
|
+
|
|
8
|
+
if (!fs.existsSync(coverageFile)) {
|
|
9
|
+
console.error('ā Coverage report not found.')
|
|
10
|
+
console.error(' Please run npm run test:coverage first.')
|
|
11
|
+
process.exit(1)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
const coverage = JSON.parse(fs.readFileSync(coverageFile, 'utf8'))
|
|
16
|
+
|
|
17
|
+
let totalStatements = 0
|
|
18
|
+
let coveredStatements = 0
|
|
19
|
+
let totalBranches = 0
|
|
20
|
+
let coveredBranches = 0
|
|
21
|
+
let totalFunctions = 0
|
|
22
|
+
let coveredFunctions = 0
|
|
23
|
+
let totalLines = 0
|
|
24
|
+
let coveredLines = 0
|
|
25
|
+
|
|
26
|
+
Object.values(coverage).forEach(file => {
|
|
27
|
+
totalStatements += file.s ? Object.keys(file.s).length : 0
|
|
28
|
+
coveredStatements += file.s ? Object.values(file.s).filter(count => count > 0).length : 0
|
|
29
|
+
|
|
30
|
+
totalBranches += file.b ? Object.values(file.b).flat().length : 0
|
|
31
|
+
coveredBranches += file.b ? Object.values(file.b).flat().filter(count => count > 0).length : 0
|
|
32
|
+
|
|
33
|
+
totalFunctions += file.f ? Object.keys(file.f).length : 0
|
|
34
|
+
coveredFunctions += file.f ? Object.values(file.f).filter(count => count > 0).length : 0
|
|
35
|
+
|
|
36
|
+
totalLines += file.l ? Object.keys(file.l).length : 0
|
|
37
|
+
coveredLines += file.l ? Object.values(file.l).filter(count => count > 0).length : 0
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
const statementsCoverage = totalStatements > 0 ? ((coveredStatements / totalStatements) * 100).toFixed(2) : '0.00'
|
|
41
|
+
const branchesCoverage = totalBranches > 0 ? ((coveredBranches / totalBranches) * 100).toFixed(2) : '0.00'
|
|
42
|
+
const functionsCoverage = totalFunctions > 0 ? ((coveredFunctions / totalFunctions) * 100).toFixed(2) : '0.00'
|
|
43
|
+
const linesCoverage = totalLines > 0 ? ((coveredLines / totalLines) * 100).toFixed(2) : '0.00'
|
|
44
|
+
|
|
45
|
+
console.log('\nš Test Coverage Summary')
|
|
46
|
+
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā')
|
|
47
|
+
console.log(` Statements : ${statementsCoverage.padStart(6)}% (${coveredStatements}/${totalStatements})`)
|
|
48
|
+
console.log(` Branches : ${branchesCoverage.padStart(6)}% (${coveredBranches}/${totalBranches})`)
|
|
49
|
+
console.log(` Functions : ${functionsCoverage.padStart(6)}% (${coveredFunctions}/${totalFunctions})`)
|
|
50
|
+
console.log(` Lines : ${linesCoverage.padStart(6)}% (${coveredLines}/${totalLines})`)
|
|
51
|
+
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā')
|
|
52
|
+
|
|
53
|
+
// Assessment against 80% target
|
|
54
|
+
const allMetrics = [
|
|
55
|
+
parseFloat(statementsCoverage),
|
|
56
|
+
parseFloat(branchesCoverage),
|
|
57
|
+
parseFloat(functionsCoverage),
|
|
58
|
+
parseFloat(linesCoverage)
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
const failedMetrics = allMetrics.filter(metric => metric < 80).length
|
|
62
|
+
|
|
63
|
+
if (failedMetrics === 0) {
|
|
64
|
+
console.log('\nā
All metrics achieved the 80% target!')
|
|
65
|
+
} else {
|
|
66
|
+
console.log('\nā ļø Some metrics have not reached the 80% target.')
|
|
67
|
+
console.log(' Please check coverage/index.html for details.')
|
|
68
|
+
}
|
|
69
|
+
console.log()
|
|
70
|
+
|
|
71
|
+
} catch (error) {
|
|
72
|
+
console.error('ā Failed to read coverage report:', error.message)
|
|
73
|
+
process.exit(1)
|
|
74
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Application entry point
|
|
3
|
+
*
|
|
4
|
+
* This file serves as a usage example for the boilerplate.
|
|
5
|
+
* In actual projects, implement your application-specific logic here.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
console.log('Hello from TypeScript boilerplate!')
|
|
9
|
+
|
|
10
|
+
// Add your project-specific implementation here
|
|
11
|
+
export {}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Dependencies
|
|
2
|
+
node_modules/
|
|
3
|
+
npm-debug.log*
|
|
4
|
+
yarn-debug.log*
|
|
5
|
+
yarn-error.log*
|
|
6
|
+
|
|
7
|
+
# Build artifacts
|
|
8
|
+
dist/
|
|
9
|
+
build/
|
|
10
|
+
*.tsbuildinfo
|
|
11
|
+
|
|
12
|
+
# Environment variables
|
|
13
|
+
.env
|
|
14
|
+
.env.local
|
|
15
|
+
.env.development.local
|
|
16
|
+
.env.test.local
|
|
17
|
+
.env.production.local
|
|
18
|
+
|
|
19
|
+
# Log files
|
|
20
|
+
logs
|
|
21
|
+
*.log
|
|
22
|
+
|
|
23
|
+
# Runtime data
|
|
24
|
+
.nyc_output
|
|
25
|
+
coverage
|
|
26
|
+
test-output.json
|
|
27
|
+
|
|
28
|
+
# IDE and editor specific files
|
|
29
|
+
.vscode/
|
|
30
|
+
.idea/
|
|
31
|
+
*.swp
|
|
32
|
+
*.swo
|
|
33
|
+
*~
|
|
34
|
+
|
|
35
|
+
# OS specific files
|
|
36
|
+
.DS_Store
|
|
37
|
+
Thumbs.db
|
|
38
|
+
|
|
39
|
+
# Temporary files
|
|
40
|
+
*.tmp
|
|
41
|
+
*.temp
|
|
42
|
+
tmp/
|
|
43
|
+
|
|
44
|
+
# Work plans (excluded from commits)
|
|
45
|
+
docs/plans/*
|
|
46
|
+
!docs/plans/template-*.md
|
|
47
|
+
|
|
48
|
+
# Work plan tasks (auto-generated)
|
|
49
|
+
docs/plans/tasks/
|
|
50
|
+
|
|
51
|
+
# Multi-language related (dynamically generated by npm scripts)
|
|
52
|
+
.claudelang
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
// Basic configuration
|
|
4
|
+
"target": "ES2020",
|
|
5
|
+
"module": "commonjs",
|
|
6
|
+
"lib": ["ES2020"],
|
|
7
|
+
"types": ["node"],
|
|
8
|
+
"baseUrl": ".", // Use project root as reference
|
|
9
|
+
"paths": {
|
|
10
|
+
"src/*": ["src/*"]
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
// Output configuration
|
|
14
|
+
"outDir": "./dist",
|
|
15
|
+
"rootDir": "./src",
|
|
16
|
+
"sourceMap": true,
|
|
17
|
+
"declaration": true,
|
|
18
|
+
"declarationMap": true,
|
|
19
|
+
|
|
20
|
+
// Strict type checking (this is important!)
|
|
21
|
+
"strict": true, // Enable all strict checks
|
|
22
|
+
"noImplicitAny": true, // Prohibit implicit any
|
|
23
|
+
"strictNullChecks": true, // Strict null/undefined checks
|
|
24
|
+
"strictFunctionTypes": true, // Strict function type checks
|
|
25
|
+
"strictBindCallApply": true, // Strict bind/call/apply checks
|
|
26
|
+
"strictPropertyInitialization": true, // Strict property initialization checks
|
|
27
|
+
"noImplicitThis": true, // Prohibit implicit this
|
|
28
|
+
"alwaysStrict": true, // Always enable strict mode
|
|
29
|
+
|
|
30
|
+
// Additional safety checks (currently disabled in this PR)
|
|
31
|
+
"noUncheckedIndexedAccess": true, // Safety checks for index access
|
|
32
|
+
"exactOptionalPropertyTypes": true, // Strict type checking for optional properties
|
|
33
|
+
"noPropertyAccessFromIndexSignature": true, // Prohibit property access from index signature
|
|
34
|
+
|
|
35
|
+
// Error detection
|
|
36
|
+
"noFallthroughCasesInSwitch": true, // Prohibit fallthrough in switch statements
|
|
37
|
+
"noImplicitReturns": true, // Prohibit implicit returns
|
|
38
|
+
"noImplicitOverride": true, // Require override keyword
|
|
39
|
+
"noUnusedLocals": true, // Detect unused local variables
|
|
40
|
+
"noUnusedParameters": true, // Detect unused parameters
|
|
41
|
+
|
|
42
|
+
// Module resolution
|
|
43
|
+
"esModuleInterop": true,
|
|
44
|
+
"skipLibCheck": true, // Skip type checking of declaration files (build optimization)
|
|
45
|
+
"forceConsistentCasingInFileNames": true,
|
|
46
|
+
"resolveJsonModule": true
|
|
47
|
+
},
|
|
48
|
+
"include": ["src/**/*"],
|
|
49
|
+
"exclude": ["node_modules", "dist", "**/*.spec.ts", "**/*.test.ts"]
|
|
50
|
+
}
|