eslint-plugin-traceability 1.1.1 → 1.1.3
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/package.json +6 -1
- package/.env.example +0 -6
- package/.github/workflows/ci-cd.yml +0 -110
- package/.husky/pre-commit +0 -1
- package/.husky/pre-push +0 -1
- package/.prettierignore +0 -27
- package/.prettierrc +0 -4
- package/.releaserc.json +0 -20
- package/.voder/history.md +0 -162
- package/.voder/implementation-progress.md +0 -144
- package/.voder/last-action.md +0 -83
- package/.voder/plan.md +0 -12
- package/.voder/progress-chart.png +0 -0
- package/.voder/progress-log-areas.csv +0 -39
- package/.voder/progress-log.csv +0 -38
- package/.voder/traceability/docs-stories-001.0-DEV-PLUGIN-SETUP.story.xml +0 -17
- package/.voder/traceability/docs-stories-002.0-DEV-ESLINT-CONFIG.story.xml +0 -13
- package/.voder/traceability/docs-stories-003.0-DEV-FUNCTION-ANNOTATIONS.story.xml +0 -9
- package/.voder/traceability/docs-stories-004.0-DEV-BRANCH-ANNOTATIONS.story.xml +0 -9
- package/.voder/traceability/docs-stories-005.0-DEV-ANNOTATION-VALIDATION.story.xml +0 -9
- package/.voder/traceability/docs-stories-006.0-DEV-FILE-VALIDATION.story.xml +0 -9
- package/.voder/traceability/docs-stories-007.0-DEV-ERROR-REPORTING.story.xml +0 -9
- package/.voder/traceability/docs-stories-008.0-DEV-AUTO-FIX.story.xml +0 -9
- package/.voder/traceability/docs-stories-009.0-DEV-MAINTENANCE-TOOLS.story.xml +0 -16
- package/.voder/traceability/docs-stories-010.0-DEV-DEEP-VALIDATION.story.xml +0 -11
- package/CHANGELOG.md +0 -58
- package/CONTRIBUTING.md +0 -99
- package/cli-integration.js +0 -103
- package/docs/cli-integration.md +0 -105
- package/docs/config-presets.md +0 -38
- package/docs/conventional-commits-guide.md +0 -185
- package/docs/decisions/001-typescript-for-eslint-plugin.accepted.md +0 -111
- package/docs/decisions/002-jest-for-eslint-testing.accepted.md +0 -137
- package/docs/decisions/003-code-quality-ratcheting-plan.md +0 -48
- package/docs/decisions/004-automated-version-bumping-for-ci-cd.md +0 -196
- package/docs/decisions/005-github-actions-validation-tooling.accepted.md +0 -144
- package/docs/decisions/006-semantic-release-for-automated-publishing.accepted.md +0 -227
- package/docs/eslint-9-setup-guide.md +0 -517
- package/docs/eslint-plugin-development-guide.md +0 -483
- package/docs/jest-testing-guide.md +0 -100
- package/docs/rules/require-branch-annotation.md +0 -34
- package/docs/rules/require-req-annotation.md +0 -39
- package/docs/rules/require-story-annotation.md +0 -36
- package/docs/rules/valid-annotation-format.md +0 -52
- package/docs/rules/valid-req-reference.md +0 -58
- package/docs/rules/valid-story-reference.md +0 -47
- package/docs/security-incidents/unresolved-vulnerabilities.md +0 -11
- package/docs/stories/001.0-DEV-PLUGIN-SETUP.story.md +0 -82
- package/docs/stories/002.0-DEV-ESLINT-CONFIG.story.md +0 -82
- package/docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md +0 -85
- package/docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md +0 -107
- package/docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md +0 -119
- package/docs/stories/006.0-DEV-FILE-VALIDATION.story.md +0 -127
- package/docs/stories/007.0-DEV-ERROR-REPORTING.story.md +0 -89
- package/docs/stories/008.0-DEV-AUTO-FIX.story.md +0 -104
- package/docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md +0 -104
- package/docs/stories/010.0-DEV-DEEP-VALIDATION.story.md +0 -110
- package/docs/stories/developer-story.map.md +0 -118
- package/eslint.config.js +0 -146
- package/jest.config.js +0 -21
- package/scripts/smoke-test.sh +0 -51
- package/src/index.ts +0 -56
- package/src/maintenance/batch.ts +0 -29
- package/src/maintenance/detect.ts +0 -42
- package/src/maintenance/index.ts +0 -14
- package/src/maintenance/report.ts +0 -15
- package/src/maintenance/update.ts +0 -40
- package/src/maintenance/utils.ts +0 -28
- package/src/rules/require-branch-annotation.ts +0 -114
- package/src/rules/require-req-annotation.ts +0 -36
- package/src/rules/require-story-annotation.ts +0 -52
- package/src/rules/valid-annotation-format.ts +0 -62
- package/src/rules/valid-req-reference.ts +0 -114
- package/src/rules/valid-story-reference.ts +0 -213
- package/tests/fixtures/stale/example.ts +0 -2
- package/tests/fixtures/story_bullet.md +0 -6
- package/tests/fixtures/update/example.ts +0 -2
- package/tests/fixtures/valid-annotations/example.ts +0 -2
- package/tests/maintenance/batch.test.ts +0 -55
- package/tests/maintenance/detect-isolated.test.ts +0 -65
- package/tests/maintenance/detect.test.ts +0 -19
- package/tests/maintenance/report.test.ts +0 -37
- package/tests/maintenance/update-isolated.test.ts +0 -39
- package/tests/maintenance/update.test.ts +0 -21
- package/tests/plugin-default-export-and-configs.test.ts +0 -50
- package/tests/plugin-setup.test.ts +0 -17
- package/tests/rules/require-branch-annotation.test.ts +0 -250
- package/tests/rules/require-req-annotation.test.ts +0 -38
- package/tests/rules/require-story-annotation.test.ts +0 -33
- package/tests/rules/valid-annotation-format.test.ts +0 -55
- package/tests/rules/valid-req-reference.test.ts +0 -85
- package/tests/rules/valid-story-reference.test.ts +0 -66
- package/tsconfig.json +0 -15
- package/user-docs/api-reference.md +0 -135
- package/user-docs/examples.md +0 -73
- package/user-docs/migration-guide.md +0 -71
package/eslint.config.js
DELETED
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ESLint flat config for Traceability Plugin
|
|
3
|
-
* @story docs/stories/002.0-DEV-ESLINT-CONFIG.story.md
|
|
4
|
-
* @req REQ-FLAT-CONFIG - Setup ESLint v9 flat config for plugin usage
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const typescriptParser = require("@typescript-eslint/parser");
|
|
8
|
-
const js = require("@eslint/js");
|
|
9
|
-
|
|
10
|
-
// Try to load the plugin, but handle case where it doesn't exist yet
|
|
11
|
-
let plugin;
|
|
12
|
-
try {
|
|
13
|
-
plugin = require("./lib/index.js");
|
|
14
|
-
} catch {
|
|
15
|
-
console.warn("Plugin not built yet, skipping traceability rules");
|
|
16
|
-
plugin = {};
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
module.exports = [
|
|
20
|
-
js.configs.recommended,
|
|
21
|
-
{
|
|
22
|
-
// Node.js config files
|
|
23
|
-
files: ["*.config.js", "*.config.mjs", "jest.config.js"],
|
|
24
|
-
languageOptions: {
|
|
25
|
-
ecmaVersion: 2022,
|
|
26
|
-
sourceType: "commonjs",
|
|
27
|
-
globals: {
|
|
28
|
-
require: "readonly",
|
|
29
|
-
module: "readonly",
|
|
30
|
-
exports: "readonly",
|
|
31
|
-
__dirname: "readonly",
|
|
32
|
-
__filename: "readonly",
|
|
33
|
-
console: "readonly",
|
|
34
|
-
process: "readonly",
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
files: ["cli-integration.js"],
|
|
40
|
-
languageOptions: {
|
|
41
|
-
ecmaVersion: 2022,
|
|
42
|
-
sourceType: "script",
|
|
43
|
-
globals: {
|
|
44
|
-
require: "readonly",
|
|
45
|
-
module: "readonly",
|
|
46
|
-
exports: "readonly",
|
|
47
|
-
__dirname: "readonly",
|
|
48
|
-
__filename: "readonly",
|
|
49
|
-
console: "readonly",
|
|
50
|
-
process: "readonly",
|
|
51
|
-
},
|
|
52
|
-
},
|
|
53
|
-
plugins: { ...(plugin.rules ? { traceability: plugin } : {}) },
|
|
54
|
-
rules: { complexity: "error" },
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
files: ["**/*.ts", "**/*.tsx"],
|
|
58
|
-
languageOptions: {
|
|
59
|
-
parser: typescriptParser,
|
|
60
|
-
parserOptions: {
|
|
61
|
-
project: "./tsconfig.json",
|
|
62
|
-
tsconfigRootDir: __dirname,
|
|
63
|
-
ecmaVersion: 2022,
|
|
64
|
-
sourceType: "module",
|
|
65
|
-
},
|
|
66
|
-
globals: {
|
|
67
|
-
process: "readonly",
|
|
68
|
-
},
|
|
69
|
-
},
|
|
70
|
-
plugins: {
|
|
71
|
-
...(plugin.rules ? { traceability: plugin } : {}),
|
|
72
|
-
},
|
|
73
|
-
rules: {
|
|
74
|
-
complexity: "error",
|
|
75
|
-
// Enforce maximum lines per function for maintainability
|
|
76
|
-
"max-lines-per-function": ["error", { max: 80, skipBlankLines: true, skipComments: true }],
|
|
77
|
-
// Enforce maximum lines per file for maintainability
|
|
78
|
-
"max-lines": ["error", { max: 350, skipBlankLines: true, skipComments: true }],
|
|
79
|
-
},
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
files: ["**/*.js", "**/*.jsx"],
|
|
83
|
-
languageOptions: {
|
|
84
|
-
ecmaVersion: 2022,
|
|
85
|
-
sourceType: "module",
|
|
86
|
-
},
|
|
87
|
-
plugins: {
|
|
88
|
-
...(plugin.rules ? { traceability: plugin } : {}),
|
|
89
|
-
},
|
|
90
|
-
rules: {
|
|
91
|
-
complexity: "error",
|
|
92
|
-
// Enforce maximum lines per function for maintainability
|
|
93
|
-
"max-lines-per-function": ["error", { max: 80, skipBlankLines: true, skipComments: true }],
|
|
94
|
-
// Enforce maximum lines per file for maintainability
|
|
95
|
-
"max-lines": ["error", { max: 350, skipBlankLines: true, skipComments: true }],
|
|
96
|
-
},
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
files: ["src/rules/**/*.ts"],
|
|
100
|
-
languageOptions: {
|
|
101
|
-
globals: {
|
|
102
|
-
process: "readonly",
|
|
103
|
-
},
|
|
104
|
-
},
|
|
105
|
-
},
|
|
106
|
-
{
|
|
107
|
-
// Test files
|
|
108
|
-
files: [
|
|
109
|
-
"**/*.test.{js,ts,tsx}",
|
|
110
|
-
"**/__tests__/**/*.{js,ts,tsx}",
|
|
111
|
-
"**/tests/**/*.{js,ts,tsx}",
|
|
112
|
-
],
|
|
113
|
-
languageOptions: {
|
|
114
|
-
globals: {
|
|
115
|
-
describe: "readonly",
|
|
116
|
-
it: "readonly",
|
|
117
|
-
test: "readonly",
|
|
118
|
-
expect: "readonly",
|
|
119
|
-
beforeEach: "readonly",
|
|
120
|
-
afterEach: "readonly",
|
|
121
|
-
beforeAll: "readonly",
|
|
122
|
-
afterAll: "readonly",
|
|
123
|
-
jest: "readonly",
|
|
124
|
-
require: "readonly",
|
|
125
|
-
__dirname: "readonly",
|
|
126
|
-
},
|
|
127
|
-
},
|
|
128
|
-
rules: {
|
|
129
|
-
"max-lines-per-function": "off",
|
|
130
|
-
"max-lines": "off",
|
|
131
|
-
},
|
|
132
|
-
},
|
|
133
|
-
{
|
|
134
|
-
// Ignore build output and node_modules
|
|
135
|
-
ignores: [
|
|
136
|
-
"lib/**",
|
|
137
|
-
"node_modules/**",
|
|
138
|
-
"coverage/**",
|
|
139
|
-
".cursor/**",
|
|
140
|
-
"**/.cursor/**",
|
|
141
|
-
".voder/**",
|
|
142
|
-
"docs/**",
|
|
143
|
-
"*.md",
|
|
144
|
-
],
|
|
145
|
-
},
|
|
146
|
-
];
|
package/jest.config.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/****
|
|
2
|
-
* Jest configuration for ESLint Traceability Plugin tests
|
|
3
|
-
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
4
|
-
* @req REQ-TEST-SETUP - Provide testing infrastructure for plugin
|
|
5
|
-
*/
|
|
6
|
-
module.exports = {
|
|
7
|
-
preset: "ts-jest",
|
|
8
|
-
testEnvironment: "node",
|
|
9
|
-
testPathIgnorePatterns: ["<rootDir>/lib/"],
|
|
10
|
-
testMatch: ["<rootDir>/tests/**/*.test.ts"],
|
|
11
|
-
coveragePathIgnorePatterns: ["<rootDir>/lib/"],
|
|
12
|
-
coverageThreshold: {
|
|
13
|
-
global: {
|
|
14
|
-
branches: 47,
|
|
15
|
-
functions: 42,
|
|
16
|
-
lines: 59,
|
|
17
|
-
statements: 57,
|
|
18
|
-
},
|
|
19
|
-
},
|
|
20
|
-
moduleFileExtensions: ["ts", "js"],
|
|
21
|
-
};
|
package/scripts/smoke-test.sh
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
set -e
|
|
3
|
-
|
|
4
|
-
echo "🧪 Running smoke test for eslint-plugin-traceability"
|
|
5
|
-
echo ""
|
|
6
|
-
|
|
7
|
-
# Pack the package first
|
|
8
|
-
echo "📦 Packing the package..."
|
|
9
|
-
tarball=$(npm pack 2>&1 | tail -1)
|
|
10
|
-
echo " Created: $tarball"
|
|
11
|
-
|
|
12
|
-
# Create temporary directory
|
|
13
|
-
workdir=$(mktemp -d)
|
|
14
|
-
echo "📁 Created test directory: $workdir"
|
|
15
|
-
|
|
16
|
-
# Cleanup on exit
|
|
17
|
-
cleanup() {
|
|
18
|
-
echo "🧹 Cleaning up test directory and tarball"
|
|
19
|
-
rm -rf "$workdir"
|
|
20
|
-
rm -f "$tarball"
|
|
21
|
-
}
|
|
22
|
-
trap cleanup EXIT
|
|
23
|
-
|
|
24
|
-
cd "$workdir"
|
|
25
|
-
|
|
26
|
-
# Initialize npm project
|
|
27
|
-
echo "📦 Initializing npm project..."
|
|
28
|
-
npm init -y > /dev/null
|
|
29
|
-
|
|
30
|
-
# Install the packed tarball
|
|
31
|
-
echo "📥 Installing eslint-plugin-traceability from packed tarball..."
|
|
32
|
-
npm install "$OLDPWD/$tarball" > /dev/null
|
|
33
|
-
|
|
34
|
-
# Create ESLint config (CommonJS format)
|
|
35
|
-
echo "⚙️ Creating ESLint config..."
|
|
36
|
-
cat > eslint.config.js << 'EOF'
|
|
37
|
-
const traceability = require('eslint-plugin-traceability');
|
|
38
|
-
module.exports = [
|
|
39
|
-
{
|
|
40
|
-
plugins: { traceability },
|
|
41
|
-
rules: {}
|
|
42
|
-
}
|
|
43
|
-
];
|
|
44
|
-
EOF
|
|
45
|
-
|
|
46
|
-
# Test the plugin loads
|
|
47
|
-
echo "🔍 Testing plugin configuration..."
|
|
48
|
-
npx eslint --print-config eslint.config.js > /dev/null
|
|
49
|
-
|
|
50
|
-
echo ""
|
|
51
|
-
echo "✅ Smoke test passed! Plugin loads successfully."
|
package/src/index.ts
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ESLint Traceability Plugin
|
|
3
|
-
* @story docs/stories/001.0-DEV-PLUGIN-SETUP.story.md
|
|
4
|
-
* @req REQ-PLUGIN-STRUCTURE - Provide foundational plugin export and registration
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import requireStoryAnnotation from "./rules/require-story-annotation";
|
|
8
|
-
import requireReqAnnotation from "./rules/require-req-annotation";
|
|
9
|
-
import requireBranchAnnotation from "./rules/require-branch-annotation";
|
|
10
|
-
import validAnnotationFormat from "./rules/valid-annotation-format";
|
|
11
|
-
import validStoryReference from "./rules/valid-story-reference";
|
|
12
|
-
import validReqReference from "./rules/valid-req-reference";
|
|
13
|
-
|
|
14
|
-
export const rules = {
|
|
15
|
-
"require-story-annotation": requireStoryAnnotation,
|
|
16
|
-
"require-req-annotation": requireReqAnnotation,
|
|
17
|
-
"require-branch-annotation": requireBranchAnnotation,
|
|
18
|
-
"valid-annotation-format": validAnnotationFormat,
|
|
19
|
-
"valid-story-reference": validStoryReference,
|
|
20
|
-
"valid-req-reference": validReqReference,
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
export const configs = {
|
|
24
|
-
recommended: [
|
|
25
|
-
{
|
|
26
|
-
plugins: {
|
|
27
|
-
traceability: {},
|
|
28
|
-
},
|
|
29
|
-
rules: {
|
|
30
|
-
"traceability/require-story-annotation": "error",
|
|
31
|
-
"traceability/require-req-annotation": "error",
|
|
32
|
-
"traceability/require-branch-annotation": "error",
|
|
33
|
-
"traceability/valid-annotation-format": "error",
|
|
34
|
-
"traceability/valid-story-reference": "error",
|
|
35
|
-
"traceability/valid-req-reference": "error",
|
|
36
|
-
},
|
|
37
|
-
},
|
|
38
|
-
],
|
|
39
|
-
strict: [
|
|
40
|
-
{
|
|
41
|
-
plugins: {
|
|
42
|
-
traceability: {},
|
|
43
|
-
},
|
|
44
|
-
rules: {
|
|
45
|
-
"traceability/require-story-annotation": "error",
|
|
46
|
-
"traceability/require-req-annotation": "error",
|
|
47
|
-
"traceability/require-branch-annotation": "error",
|
|
48
|
-
"traceability/valid-annotation-format": "error",
|
|
49
|
-
"traceability/valid-story-reference": "error",
|
|
50
|
-
"traceability/valid-req-reference": "error",
|
|
51
|
-
},
|
|
52
|
-
},
|
|
53
|
-
],
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
export default { rules, configs };
|
package/src/maintenance/batch.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { updateAnnotationReferences } from "./update";
|
|
2
|
-
import { detectStaleAnnotations } from "./detect";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Batch update annotations and verify references
|
|
6
|
-
* @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
|
|
7
|
-
* @req REQ-MAINT-BATCH - Perform batch updates
|
|
8
|
-
* @req REQ-MAINT-VERIFY - Verify annotation references
|
|
9
|
-
*/
|
|
10
|
-
export function batchUpdateAnnotations(
|
|
11
|
-
codebasePath: string,
|
|
12
|
-
mappings: { oldPath: string; newPath: string }[],
|
|
13
|
-
): number {
|
|
14
|
-
let totalUpdated = 0;
|
|
15
|
-
for (const { oldPath, newPath } of mappings) {
|
|
16
|
-
totalUpdated += updateAnnotationReferences(codebasePath, oldPath, newPath);
|
|
17
|
-
}
|
|
18
|
-
return totalUpdated;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Verify annotation references in codebase after maintenance operations
|
|
23
|
-
* @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
|
|
24
|
-
* @req REQ-MAINT-VERIFY - Verify annotation references
|
|
25
|
-
*/
|
|
26
|
-
export function verifyAnnotations(codebasePath: string): boolean {
|
|
27
|
-
const staleAnnotations = detectStaleAnnotations(codebasePath);
|
|
28
|
-
return staleAnnotations.length === 0;
|
|
29
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import * as fs from "fs";
|
|
2
|
-
import * as path from "path";
|
|
3
|
-
import { getAllFiles } from "./utils";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Detect stale annotation references that point to moved or deleted story files
|
|
7
|
-
* @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
|
|
8
|
-
* @req REQ-MAINT-DETECT - Detect stale annotation references
|
|
9
|
-
*/
|
|
10
|
-
export function detectStaleAnnotations(codebasePath: string): string[] {
|
|
11
|
-
if (
|
|
12
|
-
!fs.existsSync(codebasePath) ||
|
|
13
|
-
!fs.statSync(codebasePath).isDirectory()
|
|
14
|
-
) {
|
|
15
|
-
return [];
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const cwd = process.cwd();
|
|
19
|
-
const baseDir = path.resolve(cwd, codebasePath);
|
|
20
|
-
|
|
21
|
-
const stale = new Set<string>();
|
|
22
|
-
|
|
23
|
-
const files = getAllFiles(codebasePath);
|
|
24
|
-
for (const file of files) {
|
|
25
|
-
const content = fs.readFileSync(file, "utf8");
|
|
26
|
-
const regex = /@story\s+([^\s]+)/g;
|
|
27
|
-
let match: RegExpExecArray | null;
|
|
28
|
-
while ((match = regex.exec(content)) !== null) {
|
|
29
|
-
const storyPath = match[1];
|
|
30
|
-
const storyProjectPath = path.resolve(cwd, storyPath);
|
|
31
|
-
const storyCodebasePath = path.resolve(baseDir, storyPath);
|
|
32
|
-
if (
|
|
33
|
-
!fs.existsSync(storyProjectPath) &&
|
|
34
|
-
!fs.existsSync(storyCodebasePath)
|
|
35
|
-
) {
|
|
36
|
-
stale.add(storyPath);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return Array.from(stale);
|
|
42
|
-
}
|
package/src/maintenance/index.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Maintenance Tools Module
|
|
3
|
-
* @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
|
|
4
|
-
* @req REQ-MAINT-DETECT
|
|
5
|
-
* @req REQ-MAINT-UPDATE
|
|
6
|
-
* @req REQ-MAINT-BATCH
|
|
7
|
-
* @req REQ-MAINT-VERIFY
|
|
8
|
-
* @req REQ-MAINT-REPORT
|
|
9
|
-
* @req REQ-MAINT-SAFE
|
|
10
|
-
*/
|
|
11
|
-
export { detectStaleAnnotations } from "./detect";
|
|
12
|
-
export { updateAnnotationReferences } from "./update";
|
|
13
|
-
export { batchUpdateAnnotations, verifyAnnotations } from "./batch";
|
|
14
|
-
export { generateMaintenanceReport } from "./report";
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { detectStaleAnnotations } from "./detect";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Generate a report of maintenance operations performed
|
|
5
|
-
* @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
|
|
6
|
-
* @req REQ-MAINT-REPORT - Generate maintenance report
|
|
7
|
-
* @req REQ-MAINT-SAFE - Ensure operations are safe and reversible
|
|
8
|
-
*/
|
|
9
|
-
export function generateMaintenanceReport(codebasePath: string): string {
|
|
10
|
-
const staleAnnotations = detectStaleAnnotations(codebasePath);
|
|
11
|
-
if (staleAnnotations.length === 0) {
|
|
12
|
-
return "";
|
|
13
|
-
}
|
|
14
|
-
return staleAnnotations.join("\n");
|
|
15
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import * as fs from "fs";
|
|
2
|
-
import { getAllFiles } from "./utils";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Update annotation references when story files are moved or renamed
|
|
6
|
-
* @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
|
|
7
|
-
* @req REQ-MAINT-UPDATE - Update annotation references
|
|
8
|
-
*/
|
|
9
|
-
export function updateAnnotationReferences(
|
|
10
|
-
codebasePath: string,
|
|
11
|
-
oldPath: string,
|
|
12
|
-
newPath: string,
|
|
13
|
-
): number {
|
|
14
|
-
if (
|
|
15
|
-
!fs.existsSync(codebasePath) ||
|
|
16
|
-
!fs.statSync(codebasePath).isDirectory()
|
|
17
|
-
) {
|
|
18
|
-
return 0;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
let replacementCount = 0;
|
|
22
|
-
const escapedOldPath = oldPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
23
|
-
const regex = new RegExp(`(@story\\s*)${escapedOldPath}`, "g");
|
|
24
|
-
|
|
25
|
-
const files = getAllFiles(codebasePath);
|
|
26
|
-
for (const fullPath of files) {
|
|
27
|
-
const stat = fs.statSync(fullPath);
|
|
28
|
-
if (!stat.isFile()) continue;
|
|
29
|
-
const content = fs.readFileSync(fullPath, "utf8");
|
|
30
|
-
const newContent = content.replace(regex, (match, p1) => {
|
|
31
|
-
replacementCount++;
|
|
32
|
-
return `${p1}${newPath}`;
|
|
33
|
-
});
|
|
34
|
-
if (newContent !== content) {
|
|
35
|
-
fs.writeFileSync(fullPath, newContent, "utf8");
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return replacementCount;
|
|
40
|
-
}
|
package/src/maintenance/utils.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import * as fs from "fs";
|
|
2
|
-
import * as path from "path";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Recursively retrieve all files in a directory.
|
|
6
|
-
* @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
|
|
7
|
-
* @req REQ-MAINT-UTILS - Extract common file traversal logic for maintenance tools
|
|
8
|
-
*/
|
|
9
|
-
export function getAllFiles(dir: string): string[] {
|
|
10
|
-
const fileList: string[] = [];
|
|
11
|
-
if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) {
|
|
12
|
-
return fileList;
|
|
13
|
-
}
|
|
14
|
-
function traverse(currentDir: string) {
|
|
15
|
-
const entries = fs.readdirSync(currentDir);
|
|
16
|
-
for (const entry of entries) {
|
|
17
|
-
const fullPath = path.join(currentDir, entry);
|
|
18
|
-
const stat = fs.statSync(fullPath);
|
|
19
|
-
if (stat.isDirectory()) {
|
|
20
|
-
traverse(fullPath);
|
|
21
|
-
} else if (stat.isFile()) {
|
|
22
|
-
fileList.push(fullPath);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
traverse(dir);
|
|
27
|
-
return fileList;
|
|
28
|
-
}
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Rule to enforce @story and @req annotations on significant code branches
|
|
3
|
-
* @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
|
|
4
|
-
* @req REQ-BRANCH-DETECTION - Detect significant code branches for traceability annotations
|
|
5
|
-
*/
|
|
6
|
-
export default {
|
|
7
|
-
meta: {
|
|
8
|
-
type: "problem",
|
|
9
|
-
docs: {
|
|
10
|
-
description: "Require @story and @req annotations on code branches",
|
|
11
|
-
recommended: "error",
|
|
12
|
-
},
|
|
13
|
-
fixable: "code",
|
|
14
|
-
messages: {
|
|
15
|
-
missingAnnotation: "Missing {{missing}} annotation on code branch",
|
|
16
|
-
},
|
|
17
|
-
schema: [],
|
|
18
|
-
},
|
|
19
|
-
create(context: any) {
|
|
20
|
-
const sourceCode = context.getSourceCode();
|
|
21
|
-
/**
|
|
22
|
-
* Helper to check a branch AST node for traceability annotations.
|
|
23
|
-
* @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
|
|
24
|
-
* @req REQ-BRANCH-DETECTION - Detect significant code branches for traceability annotations
|
|
25
|
-
*/
|
|
26
|
-
function checkBranch(node: any) {
|
|
27
|
-
// @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
|
|
28
|
-
// @req REQ-BRANCH-DETECTION - Skip default switch cases during annotation checks
|
|
29
|
-
// skip default cases in switch
|
|
30
|
-
if (node.type === "SwitchCase" && node.test == null) {
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
// collect comments before node
|
|
34
|
-
let comments = sourceCode.getCommentsBefore(node) || [];
|
|
35
|
-
// @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
|
|
36
|
-
// @req REQ-BRANCH-DETECTION - Fallback scanning for SwitchCase when leading comments are absent
|
|
37
|
-
// fallback scanning for SwitchCase if no leading comment nodes
|
|
38
|
-
/* istanbul ignore if */
|
|
39
|
-
if (node.type === "SwitchCase" && comments.length === 0) {
|
|
40
|
-
const lines = sourceCode.lines;
|
|
41
|
-
const startLine = node.loc.start.line;
|
|
42
|
-
let i = startLine - 1;
|
|
43
|
-
const fallbackComments: string[] = [];
|
|
44
|
-
while (i > 0) {
|
|
45
|
-
const lineText = lines[i - 1];
|
|
46
|
-
if (/^\s*(\/\/|\/\*)/.test(lineText)) {
|
|
47
|
-
fallbackComments.unshift(lineText.trim());
|
|
48
|
-
i--;
|
|
49
|
-
} else if (/^\s*$/.test(lineText)) {
|
|
50
|
-
break;
|
|
51
|
-
} else {
|
|
52
|
-
break;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
comments = fallbackComments.map((text) => ({ value: text }));
|
|
56
|
-
}
|
|
57
|
-
const text = comments.map((c: any) => c.value).join(" ");
|
|
58
|
-
|
|
59
|
-
const missingStory = !/@story\b/.test(text);
|
|
60
|
-
const missingReq = !/@req\b/.test(text);
|
|
61
|
-
|
|
62
|
-
if (missingStory) {
|
|
63
|
-
const reportObj: any = {
|
|
64
|
-
node,
|
|
65
|
-
messageId: "missingAnnotation",
|
|
66
|
-
data: { missing: "@story" },
|
|
67
|
-
};
|
|
68
|
-
if (node.type !== "CatchClause") {
|
|
69
|
-
if (node.type === "SwitchCase") {
|
|
70
|
-
const indent = " ".repeat(node.loc.start.column);
|
|
71
|
-
reportObj.fix = (fixer: any) =>
|
|
72
|
-
fixer.insertTextBefore(
|
|
73
|
-
node,
|
|
74
|
-
`// @story <story-file>.story.md\n${indent}`,
|
|
75
|
-
);
|
|
76
|
-
} else {
|
|
77
|
-
reportObj.fix = (fixer: any) =>
|
|
78
|
-
fixer.insertTextBefore(node, `// @story <story-file>.story.md\n`);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
context.report(reportObj);
|
|
82
|
-
}
|
|
83
|
-
if (missingReq) {
|
|
84
|
-
const reportObj: any = {
|
|
85
|
-
node,
|
|
86
|
-
messageId: "missingAnnotation",
|
|
87
|
-
data: { missing: "@req" },
|
|
88
|
-
};
|
|
89
|
-
if (!missingStory && node.type !== "CatchClause") {
|
|
90
|
-
if (node.type === "SwitchCase") {
|
|
91
|
-
const indent = " ".repeat(node.loc.start.column);
|
|
92
|
-
reportObj.fix = (fixer: any) =>
|
|
93
|
-
fixer.insertTextBefore(node, `// @req <REQ-ID>\n${indent}`);
|
|
94
|
-
} else {
|
|
95
|
-
reportObj.fix = (fixer: any) =>
|
|
96
|
-
fixer.insertTextBefore(node, `// @req <REQ-ID>\n`);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
context.report(reportObj);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
return {
|
|
103
|
-
IfStatement: checkBranch,
|
|
104
|
-
SwitchCase: checkBranch,
|
|
105
|
-
TryStatement: checkBranch,
|
|
106
|
-
CatchClause: checkBranch,
|
|
107
|
-
ForStatement: checkBranch,
|
|
108
|
-
ForOfStatement: checkBranch,
|
|
109
|
-
ForInStatement: checkBranch,
|
|
110
|
-
WhileStatement: checkBranch,
|
|
111
|
-
DoWhileStatement: checkBranch,
|
|
112
|
-
};
|
|
113
|
-
},
|
|
114
|
-
} as any;
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Rule to enforce @req annotation on functions
|
|
3
|
-
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
4
|
-
* @req REQ-ANNOTATION-REQUIRED - Require @req annotation on functions
|
|
5
|
-
*/
|
|
6
|
-
export default {
|
|
7
|
-
meta: {
|
|
8
|
-
type: "problem",
|
|
9
|
-
fixable: "code",
|
|
10
|
-
docs: {
|
|
11
|
-
description: "Require @req annotations on functions",
|
|
12
|
-
recommended: "error",
|
|
13
|
-
},
|
|
14
|
-
messages: {
|
|
15
|
-
missingReq: "Missing @req annotation",
|
|
16
|
-
},
|
|
17
|
-
schema: [],
|
|
18
|
-
},
|
|
19
|
-
create(context: any) {
|
|
20
|
-
const sourceCode = context.getSourceCode();
|
|
21
|
-
return {
|
|
22
|
-
FunctionDeclaration(node: any) {
|
|
23
|
-
const jsdoc = sourceCode.getJSDocComment(node);
|
|
24
|
-
if (!jsdoc || !jsdoc.value.includes("@req")) {
|
|
25
|
-
context.report({
|
|
26
|
-
node,
|
|
27
|
-
messageId: "missingReq",
|
|
28
|
-
fix(fixer: any) {
|
|
29
|
-
return fixer.insertTextBefore(node, "/** @req <REQ-ID> */\n");
|
|
30
|
-
},
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
},
|
|
34
|
-
};
|
|
35
|
-
},
|
|
36
|
-
} as any;
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Rule to enforce @story annotation on functions
|
|
3
|
-
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
4
|
-
* @req REQ-ANNOTATION-REQUIRED - Require @story annotation on functions
|
|
5
|
-
*/
|
|
6
|
-
export default {
|
|
7
|
-
meta: {
|
|
8
|
-
type: "problem",
|
|
9
|
-
docs: {
|
|
10
|
-
description: "Require @story annotations on functions",
|
|
11
|
-
recommended: "error",
|
|
12
|
-
},
|
|
13
|
-
fixable: "code",
|
|
14
|
-
messages: {
|
|
15
|
-
missingStory: "Missing @story annotation",
|
|
16
|
-
},
|
|
17
|
-
schema: [],
|
|
18
|
-
},
|
|
19
|
-
create(context: any) {
|
|
20
|
-
const sourceCode = context.getSourceCode();
|
|
21
|
-
return {
|
|
22
|
-
FunctionDeclaration(node: any) {
|
|
23
|
-
const jsdoc = sourceCode.getJSDocComment(node);
|
|
24
|
-
let hasStory = false;
|
|
25
|
-
// @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
26
|
-
// @req REQ-JSDOC-PARSING - Detect JSDoc @story annotation presence
|
|
27
|
-
if (jsdoc && jsdoc.value.includes("@story")) {
|
|
28
|
-
hasStory = true;
|
|
29
|
-
// @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
30
|
-
// @req REQ-JSDOC-PARSING - Fallback to loading comments before node for @story annotation detection
|
|
31
|
-
} else {
|
|
32
|
-
const commentsBefore = sourceCode.getCommentsBefore(node) || [];
|
|
33
|
-
hasStory = commentsBefore.some((comment: any) =>
|
|
34
|
-
comment.value.includes("@story"),
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
if (!hasStory) {
|
|
38
|
-
context.report({
|
|
39
|
-
node,
|
|
40
|
-
messageId: "missingStory",
|
|
41
|
-
fix(fixer: any) {
|
|
42
|
-
return fixer.insertTextBefore(
|
|
43
|
-
node,
|
|
44
|
-
"/** @story <story-file>.story.md */\n",
|
|
45
|
-
);
|
|
46
|
-
},
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
},
|
|
50
|
-
};
|
|
51
|
-
},
|
|
52
|
-
} as any;
|