eslint-plugin-traceability 1.19.2 → 1.19.4
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/CHANGELOG.md +2 -2
- package/README.md +41 -0
- package/lib/src/utils/branch-annotation-helpers.d.ts +2 -0
- package/lib/src/utils/branch-annotation-helpers.js +68 -30
- package/lib/src/utils/branch-annotation-indent-helpers.d.ts +52 -0
- package/lib/src/utils/branch-annotation-indent-helpers.js +137 -0
- package/lib/src/utils/branch-annotation-report-helpers.d.ts +1 -0
- package/lib/src/utils/branch-annotation-report-helpers.js +95 -13
- package/lib/src/utils/branch-annotation-story-fix-helpers.d.ts +27 -0
- package/lib/src/utils/branch-annotation-story-fix-helpers.js +48 -0
- package/lib/src/utils/branch-annotation-switch-helpers.d.ts +11 -0
- package/lib/src/utils/branch-annotation-switch-helpers.js +68 -0
- package/lib/tests/integration/annotation-placement-inside-prettier.integration.test.d.ts +1 -0
- package/lib/tests/integration/annotation-placement-inside-prettier.integration.test.js +132 -0
- package/lib/tests/integration/catch-annotation-prettier.integration.test.js +4 -15
- package/lib/tests/integration/else-if-annotation-prettier.integration.test.js +3 -14
- package/lib/tests/integration/prettier-test-helpers.d.ts +9 -0
- package/lib/tests/integration/prettier-test-helpers.js +34 -0
- package/lib/tests/rules/require-branch-annotation.test.js +70 -55
- package/lib/tests/utils/branch-annotation-helpers.test.js +156 -9
- package/package.json +1 -1
- package/user-docs/api-reference.md +7 -1
- package/user-docs/migration-guide.md +40 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.gatherSwitchCaseCommentText = gatherSwitchCaseCommentText;
|
|
4
|
+
const branch_annotation_helpers_1 = require("./branch-annotation-helpers");
|
|
5
|
+
/**
|
|
6
|
+
* Gather comment text from the first contiguous comment lines "inside" a SwitchCase body.
|
|
7
|
+
* Prefers a BlockStatement consequent when present, with a fallback to the entire case range.
|
|
8
|
+
* @story docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md
|
|
9
|
+
* @supports REQ-INSIDE-BRACE-PLACEMENT
|
|
10
|
+
* @supports REQ-PLACEMENT-CONFIG
|
|
11
|
+
*/
|
|
12
|
+
function getInsideSwitchCaseCommentText(sourceCode, node) {
|
|
13
|
+
const lines = sourceCode.lines;
|
|
14
|
+
const firstConsequent = node.consequent && node.consequent[0];
|
|
15
|
+
if (firstConsequent &&
|
|
16
|
+
firstConsequent.type === "BlockStatement" &&
|
|
17
|
+
firstConsequent.loc &&
|
|
18
|
+
firstConsequent.loc.start &&
|
|
19
|
+
firstConsequent.loc.end &&
|
|
20
|
+
typeof firstConsequent.loc.start.line === "number" &&
|
|
21
|
+
typeof firstConsequent.loc.end.line === "number") {
|
|
22
|
+
const startIndex = firstConsequent.loc.start.line - 1;
|
|
23
|
+
const endIndex = firstConsequent.loc.end.line - 1;
|
|
24
|
+
const insideText = (0, branch_annotation_helpers_1.scanCommentLinesInRange)(lines, startIndex + 1, endIndex);
|
|
25
|
+
if (insideText) {
|
|
26
|
+
return insideText;
|
|
27
|
+
}
|
|
28
|
+
return "";
|
|
29
|
+
}
|
|
30
|
+
if (node.loc &&
|
|
31
|
+
node.loc.start &&
|
|
32
|
+
node.loc.end &&
|
|
33
|
+
typeof node.loc.start.line === "number" &&
|
|
34
|
+
typeof node.loc.end.line === "number") {
|
|
35
|
+
const startIndex = node.loc.start.line - 1;
|
|
36
|
+
const endIndex = node.loc.end.line - 1;
|
|
37
|
+
const insideText = (0, branch_annotation_helpers_1.scanCommentLinesInRange)(lines, startIndex + 1, endIndex);
|
|
38
|
+
if (insideText) {
|
|
39
|
+
return insideText;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return "";
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Gather annotation text for SwitchCase branches, honoring the configured placement
|
|
46
|
+
* while preserving legacy before-branch behavior in the default mode.
|
|
47
|
+
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
48
|
+
* @story docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md
|
|
49
|
+
* @supports REQ-PLACEMENT-CONFIG
|
|
50
|
+
* @supports REQ-INSIDE-BRACE-PLACEMENT
|
|
51
|
+
*/
|
|
52
|
+
function gatherSwitchCaseCommentText(sourceCode, node, annotationPlacement, beforeText) {
|
|
53
|
+
if (annotationPlacement === "inside") {
|
|
54
|
+
const insideText = getInsideSwitchCaseCommentText(sourceCode, node);
|
|
55
|
+
if (insideText) {
|
|
56
|
+
return insideText;
|
|
57
|
+
}
|
|
58
|
+
return "";
|
|
59
|
+
}
|
|
60
|
+
if (/@story\b/.test(beforeText) ||
|
|
61
|
+
/@req\b/.test(beforeText) ||
|
|
62
|
+
/@supports\b/.test(beforeText)) {
|
|
63
|
+
return beforeText;
|
|
64
|
+
}
|
|
65
|
+
// In before-placement mode, rely on the caller's beforeText and any
|
|
66
|
+
// configured PRE_COMMENT_OFFSET logic in the main helpers module.
|
|
67
|
+
return beforeText;
|
|
68
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
/**
|
|
7
|
+
* Prettier integration tests for annotationPlacement: "inside" across multiple branch types.
|
|
8
|
+
* @story docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md
|
|
9
|
+
* @supports docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md REQ-PRETTIER-STABLE REQ-INSIDE-BRACE-PLACEMENT REQ-PLACEMENT-CONFIG
|
|
10
|
+
*/
|
|
11
|
+
const path_1 = __importDefault(require("path"));
|
|
12
|
+
const child_process_1 = require("child_process");
|
|
13
|
+
const prettier_test_helpers_1 = require("./prettier-test-helpers");
|
|
14
|
+
describe("annotationPlacement: 'inside' with Prettier (Story 028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION)", () => {
|
|
15
|
+
const eslintPkgDir = path_1.default.dirname(require.resolve("eslint/package.json"));
|
|
16
|
+
const eslintCliPath = path_1.default.join(eslintPkgDir, "bin", "eslint.js");
|
|
17
|
+
const configPath = path_1.default.resolve(__dirname, "../../eslint.config.js");
|
|
18
|
+
function buildInsidePlacementArgs(stdinFilename) {
|
|
19
|
+
return [
|
|
20
|
+
"--no-config-lookup",
|
|
21
|
+
"--config",
|
|
22
|
+
configPath,
|
|
23
|
+
"--stdin",
|
|
24
|
+
"--stdin-filename",
|
|
25
|
+
stdinFilename,
|
|
26
|
+
"--rule",
|
|
27
|
+
"no-unused-vars:off",
|
|
28
|
+
"--rule",
|
|
29
|
+
"no-magic-numbers:off",
|
|
30
|
+
"--rule",
|
|
31
|
+
"no-undef:off",
|
|
32
|
+
"--rule",
|
|
33
|
+
"no-console:off",
|
|
34
|
+
"--rule",
|
|
35
|
+
'traceability/require-branch-annotation:["error",{"annotationPlacement":"inside"}]',
|
|
36
|
+
];
|
|
37
|
+
}
|
|
38
|
+
function runEslintWithInsidePlacement(code, _filename) {
|
|
39
|
+
// Pin stdin filename to a tsconfig-included path to satisfy @typescript-eslint/parser's project lookup in these integration tests.
|
|
40
|
+
const args = buildInsidePlacementArgs("src/annotation-placement-inside.ts");
|
|
41
|
+
return (0, child_process_1.spawnSync)(process.execPath, [eslintCliPath, ...args], {
|
|
42
|
+
encoding: "utf-8",
|
|
43
|
+
input: code,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
function formatWithPrettier(source) {
|
|
47
|
+
return (0, prettier_test_helpers_1.formatWithPrettier)(source, { parser: "typescript" });
|
|
48
|
+
}
|
|
49
|
+
it("[REQ-PRETTIER-STABLE][REQ-INSIDE-BRACE-PLACEMENT] accepts formatted code with inside-brace annotations for if/else and loops", () => {
|
|
50
|
+
const original = `
|
|
51
|
+
function demo(value: number) {
|
|
52
|
+
if (value > 0) {
|
|
53
|
+
// @story docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md
|
|
54
|
+
// @req REQ-IF-INSIDE
|
|
55
|
+
console.log('positive');
|
|
56
|
+
} else if (value < 0) {
|
|
57
|
+
// @story docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md
|
|
58
|
+
// @req REQ-ELSE-IF-INSIDE
|
|
59
|
+
console.log('negative');
|
|
60
|
+
} else {
|
|
61
|
+
// @story docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md
|
|
62
|
+
// @req REQ-ELSE-INSIDE
|
|
63
|
+
console.log('zero');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
for (const item of [1, 2, 3]) {
|
|
67
|
+
// @story docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md
|
|
68
|
+
// @req REQ-LOOP-INSIDE
|
|
69
|
+
console.log(item);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
`;
|
|
73
|
+
const formatted = formatWithPrettier(original);
|
|
74
|
+
const result = runEslintWithInsidePlacement(formatted, "annotation-placement-inside-if-loop.ts");
|
|
75
|
+
expect(result.stdout).not.toContain("traceability/require-branch-annotation");
|
|
76
|
+
expect([0, 1]).toContain(result.status);
|
|
77
|
+
});
|
|
78
|
+
it("[REQ-PRETTIER-STABLE][REQ-INSIDE-BRACE-PLACEMENT] accepts formatted code with inside-brace annotations for try/finally and catch", () => {
|
|
79
|
+
const original = `
|
|
80
|
+
function demoTry(flag: boolean) {
|
|
81
|
+
try {
|
|
82
|
+
// @story docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md
|
|
83
|
+
// @req REQ-TRY-INSIDE
|
|
84
|
+
if (flag) {
|
|
85
|
+
throw new Error('boom');
|
|
86
|
+
}
|
|
87
|
+
} catch (error) {
|
|
88
|
+
// @story docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md
|
|
89
|
+
// @req REQ-CATCH-INSIDE
|
|
90
|
+
console.error(error);
|
|
91
|
+
} finally {
|
|
92
|
+
// @story docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md
|
|
93
|
+
// @req REQ-FINALLY-INSIDE
|
|
94
|
+
console.log('cleanup');
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
`;
|
|
98
|
+
const formatted = formatWithPrettier(original);
|
|
99
|
+
const result = runEslintWithInsidePlacement(formatted, "annotation-placement-inside-try.ts");
|
|
100
|
+
expect(result.stdout).not.toContain("traceability/require-branch-annotation");
|
|
101
|
+
expect([0, 1]).toContain(result.status);
|
|
102
|
+
});
|
|
103
|
+
it("[REQ-PRETTIER-STABLE][REQ-INSIDE-BRACE-PLACEMENT] accepts formatted code with inside-brace annotations for switch cases", () => {
|
|
104
|
+
const original = `
|
|
105
|
+
function demoSwitch(status: 'pending' | 'done' | 'other') {
|
|
106
|
+
switch (status) {
|
|
107
|
+
case 'pending': {
|
|
108
|
+
// @story docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md
|
|
109
|
+
// @req REQ-SWITCH-PENDING-INSIDE
|
|
110
|
+
console.log('pending');
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
case 'done': {
|
|
114
|
+
// @story docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md
|
|
115
|
+
// @req REQ-SWITCH-DONE-INSIDE
|
|
116
|
+
console.log('done');
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
default: {
|
|
120
|
+
// @story docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md
|
|
121
|
+
// @req REQ-SWITCH-DEFAULT-INSIDE
|
|
122
|
+
console.log('other');
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
`;
|
|
127
|
+
const formatted = formatWithPrettier(original);
|
|
128
|
+
const result = runEslintWithInsidePlacement(formatted, "annotation-placement-inside-switch.ts");
|
|
129
|
+
expect(result.stdout).not.toContain("traceability/require-branch-annotation");
|
|
130
|
+
expect([0, 1]).toContain(result.status);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
@@ -10,12 +10,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
10
10
|
*/
|
|
11
11
|
const path_1 = __importDefault(require("path"));
|
|
12
12
|
const child_process_1 = require("child_process");
|
|
13
|
+
const prettier_test_helpers_1 = require("./prettier-test-helpers");
|
|
13
14
|
describe("CatchClause annotations with Prettier (Story 025.0-DEV-CATCH-ANNOTATION-POSITION)", () => {
|
|
14
15
|
const eslintPkgDir = path_1.default.dirname(require.resolve("eslint/package.json"));
|
|
15
16
|
const eslintCliPath = path_1.default.join(eslintPkgDir, "bin", "eslint.js");
|
|
16
17
|
const configPath = path_1.default.resolve(__dirname, "../../eslint.config.js");
|
|
17
|
-
const prettierPackageJson = require.resolve("prettier/package.json");
|
|
18
|
-
const prettierCliPath = path_1.default.join(path_1.default.dirname(prettierPackageJson), "bin", "prettier.cjs");
|
|
19
18
|
function runEslintWithRequireBranchAnnotation(code) {
|
|
20
19
|
const args = [
|
|
21
20
|
"--no-config-lookup",
|
|
@@ -40,16 +39,6 @@ describe("CatchClause annotations with Prettier (Story 025.0-DEV-CATCH-ANNOTATIO
|
|
|
40
39
|
input: code,
|
|
41
40
|
});
|
|
42
41
|
}
|
|
43
|
-
function formatWithPrettier(source) {
|
|
44
|
-
const result = (0, child_process_1.spawnSync)(process.execPath, [prettierCliPath, "--parser", "typescript"], {
|
|
45
|
-
encoding: "utf-8",
|
|
46
|
-
input: source,
|
|
47
|
-
});
|
|
48
|
-
if (result.status !== 0) {
|
|
49
|
-
throw new Error(`Prettier formatting failed: ${result.stderr || result.stdout}`);
|
|
50
|
-
}
|
|
51
|
-
return result.stdout;
|
|
52
|
-
}
|
|
53
42
|
it("[REQ-PRETTIER-COMPATIBILITY-BEFORE] accepts code where annotations start before catch but are moved inside by Prettier", () => {
|
|
54
43
|
const original = `
|
|
55
44
|
function doSomething() {
|
|
@@ -71,7 +60,7 @@ catch (error) {
|
|
|
71
60
|
handleError(error);
|
|
72
61
|
}
|
|
73
62
|
`;
|
|
74
|
-
const formatted = formatWithPrettier(original);
|
|
63
|
+
const formatted = (0, prettier_test_helpers_1.formatWithPrettier)(original);
|
|
75
64
|
// Sanity check: Prettier should move the branch annotations inside the catch body.
|
|
76
65
|
expect(formatted).toContain("catch (error) {");
|
|
77
66
|
const catchIndex = formatted.indexOf("catch (error) {");
|
|
@@ -100,7 +89,7 @@ try {
|
|
|
100
89
|
handleError(error);
|
|
101
90
|
}
|
|
102
91
|
`;
|
|
103
|
-
const formatted = formatWithPrettier(original);
|
|
92
|
+
const formatted = (0, prettier_test_helpers_1.formatWithPrettier)(original);
|
|
104
93
|
// Sanity: annotations should still be associated with the catch body after formatting.
|
|
105
94
|
expect(formatted).toContain("catch (error) {");
|
|
106
95
|
const catchIndex = formatted.indexOf("catch (error) {");
|
|
@@ -124,7 +113,7 @@ try {
|
|
|
124
113
|
// @req REQ-CATCH-EMPTY
|
|
125
114
|
}
|
|
126
115
|
`;
|
|
127
|
-
const formatted = formatWithPrettier(original);
|
|
116
|
+
const formatted = (0, prettier_test_helpers_1.formatWithPrettier)(original);
|
|
128
117
|
const result = runEslintWithRequireBranchAnnotation(formatted);
|
|
129
118
|
expect(result.status).toBe(0);
|
|
130
119
|
});
|
|
@@ -10,12 +10,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
10
10
|
*/
|
|
11
11
|
const path_1 = __importDefault(require("path"));
|
|
12
12
|
const child_process_1 = require("child_process");
|
|
13
|
+
const prettier_test_helpers_1 = require("./prettier-test-helpers");
|
|
13
14
|
describe("Else-if annotations with Prettier (Story 026.0-DEV-ELSE-IF-ANNOTATION-POSITION)", () => {
|
|
14
15
|
const eslintPkgDir = path_1.default.dirname(require.resolve("eslint/package.json"));
|
|
15
16
|
const eslintCliPath = path_1.default.join(eslintPkgDir, "bin", "eslint.js");
|
|
16
17
|
const configPath = path_1.default.resolve(__dirname, "../../eslint.config.js");
|
|
17
|
-
const prettierPackageJson = require.resolve("prettier/package.json");
|
|
18
|
-
const prettierCliPath = path_1.default.join(path_1.default.dirname(prettierPackageJson), "bin", "prettier.cjs");
|
|
19
18
|
function runEslintWithRequireBranchAnnotation(code) {
|
|
20
19
|
const args = [
|
|
21
20
|
"--no-config-lookup",
|
|
@@ -40,16 +39,6 @@ describe("Else-if annotations with Prettier (Story 026.0-DEV-ELSE-IF-ANNOTATION-
|
|
|
40
39
|
input: code,
|
|
41
40
|
});
|
|
42
41
|
}
|
|
43
|
-
function formatWithPrettier(source) {
|
|
44
|
-
const result = (0, child_process_1.spawnSync)(process.execPath, [prettierCliPath, "--parser", "typescript"], {
|
|
45
|
-
encoding: "utf-8",
|
|
46
|
-
input: source,
|
|
47
|
-
});
|
|
48
|
-
if (result.status !== 0) {
|
|
49
|
-
throw new Error(`Prettier formatting failed: ${result.stderr || result.stdout}`);
|
|
50
|
-
}
|
|
51
|
-
return result.stdout;
|
|
52
|
-
}
|
|
53
42
|
it("[REQ-PRETTIER-COMPATIBILITY-ELSE-IF-BEFORE] accepts code where annotations start before else-if but are moved between condition and body by Prettier", () => {
|
|
54
43
|
const original = `
|
|
55
44
|
function doA() {
|
|
@@ -71,7 +60,7 @@ else if (anotherVeryLongConditionThatForcesWrapping && someOtherCondition) {
|
|
|
71
60
|
doB();
|
|
72
61
|
}
|
|
73
62
|
`;
|
|
74
|
-
const formatted = formatWithPrettier(original);
|
|
63
|
+
const formatted = (0, prettier_test_helpers_1.formatWithPrettier)(original);
|
|
75
64
|
// Sanity checks: Prettier should keep both the else-if branch and the associated story annotation,
|
|
76
65
|
// but the exact layout and comment movement may vary between versions.
|
|
77
66
|
expect(formatted).toContain("else if");
|
|
@@ -101,7 +90,7 @@ if (aVeryLongConditionThatForcesPrettierToWrapTheElseIfBranch && anotherConditio
|
|
|
101
90
|
doB();
|
|
102
91
|
}
|
|
103
92
|
`;
|
|
104
|
-
const formatted = formatWithPrettier(original);
|
|
93
|
+
const formatted = (0, prettier_test_helpers_1.formatWithPrettier)(original);
|
|
105
94
|
// Note: Prettier's exact layout of the else-if and its comments may differ between versions;
|
|
106
95
|
// the rule should accept any of the supported annotation positions regardless of formatting.
|
|
107
96
|
const result = runEslintWithRequireBranchAnnotation(formatted);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
interface FormatOptions {
|
|
2
|
+
parser?: "babel" | "typescript" | "babel-ts" | "espree" | string;
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* Format arbitrary source with Prettier using the installed CLI binary.
|
|
6
|
+
* Defaults to the TypeScript parser when none is provided.
|
|
7
|
+
*/
|
|
8
|
+
export declare function formatWithPrettier(source: string, options?: FormatOptions): string;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.formatWithPrettier = formatWithPrettier;
|
|
7
|
+
/**
|
|
8
|
+
* Shared helpers for Prettier-based integration tests.
|
|
9
|
+
* @story docs/stories/025.0-DEV-CATCH-ANNOTATION-POSITION.story.md
|
|
10
|
+
* @story docs/stories/026.0-DEV-ELSE-IF-ANNOTATION-POSITION.story.md
|
|
11
|
+
* @story docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md
|
|
12
|
+
* @supports docs/stories/025.0-DEV-CATCH-ANNOTATION-POSITION.story.md REQ-PRETTIER-COMPATIBILITY
|
|
13
|
+
* @supports docs/stories/026.0-DEV-ELSE-IF-ANNOTATION-POSITION.story.md REQ-PRETTIER-AUTOFIX-ELSE-IF
|
|
14
|
+
* @supports docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md REQ-PRETTIER-STABLE
|
|
15
|
+
*/
|
|
16
|
+
const path_1 = __importDefault(require("path"));
|
|
17
|
+
const child_process_1 = require("child_process");
|
|
18
|
+
/**
|
|
19
|
+
* Format arbitrary source with Prettier using the installed CLI binary.
|
|
20
|
+
* Defaults to the TypeScript parser when none is provided.
|
|
21
|
+
*/
|
|
22
|
+
function formatWithPrettier(source, options = {}) {
|
|
23
|
+
const prettierPackageJson = require.resolve("prettier/package.json");
|
|
24
|
+
const prettierCliPath = path_1.default.join(path_1.default.dirname(prettierPackageJson), "bin", "prettier.cjs");
|
|
25
|
+
const parser = options.parser || "typescript";
|
|
26
|
+
const result = (0, child_process_1.spawnSync)(process.execPath, [prettierCliPath, "--parser", parser], {
|
|
27
|
+
encoding: "utf-8",
|
|
28
|
+
input: source,
|
|
29
|
+
});
|
|
30
|
+
if (result.status !== 0) {
|
|
31
|
+
throw new Error(`Prettier formatting failed: ${result.stderr || result.stdout}`);
|
|
32
|
+
}
|
|
33
|
+
return result.stdout;
|
|
34
|
+
}
|
|
@@ -107,6 +107,17 @@ catch (error) {
|
|
|
107
107
|
handleError(error);
|
|
108
108
|
}`,
|
|
109
109
|
},
|
|
110
|
+
{
|
|
111
|
+
name: "[REQ-INSIDE-BRACE-PLACEMENT][REQ-PLACEMENT-CONFIG] try block annotated inside body under annotationPlacement: 'inside' (Story 028.0)",
|
|
112
|
+
code: `try {
|
|
113
|
+
// @story docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md
|
|
114
|
+
// @req REQ-TRY-INSIDE-BRANCH
|
|
115
|
+
doWork();
|
|
116
|
+
} finally {
|
|
117
|
+
cleanup();
|
|
118
|
+
}`,
|
|
119
|
+
options: [{ annotationPlacement: "inside" }],
|
|
120
|
+
},
|
|
110
121
|
{
|
|
111
122
|
name: "[REQ-BRANCH-DETECTION] valid do-while loop with annotations",
|
|
112
123
|
code: `/* @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md */
|
|
@@ -201,19 +212,6 @@ if (condition) {}`,
|
|
|
201
212
|
// @story docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md
|
|
202
213
|
// @req REQ-INSIDE-BRACE-PLACEMENT
|
|
203
214
|
doSomething();
|
|
204
|
-
}`,
|
|
205
|
-
options: [{ annotationPlacement: "inside" }],
|
|
206
|
-
},
|
|
207
|
-
{
|
|
208
|
-
name: "[REQ-INSIDE-BRACE-PLACEMENT][REQ-PLACEMENT-CONFIG] catch clause annotated inside block under annotationPlacement: 'inside' (Story 028.0)",
|
|
209
|
-
code: `// @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
|
|
210
|
-
// @req REQ-BRANCH-TRY
|
|
211
|
-
try {
|
|
212
|
-
doSomething();
|
|
213
|
-
} catch (error) {
|
|
214
|
-
// @story docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md
|
|
215
|
-
// @req REQ-INSIDE-CATCH
|
|
216
|
-
handleError(error);
|
|
217
215
|
}`,
|
|
218
216
|
options: [{ annotationPlacement: "inside" }],
|
|
219
217
|
},
|
|
@@ -223,6 +221,22 @@ try {
|
|
|
223
221
|
// @story docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md
|
|
224
222
|
// @req REQ-LOOP-INSIDE
|
|
225
223
|
process(item);
|
|
224
|
+
}`,
|
|
225
|
+
options: [{ annotationPlacement: "inside" }],
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
name: "[REQ-INSIDE-BRACE-PLACEMENT][REQ-PLACEMENT-CONFIG] switch cases annotated inside block under annotationPlacement: 'inside' (Story 028.0)",
|
|
229
|
+
code: `switch (value) {
|
|
230
|
+
case 'a': {
|
|
231
|
+
// @story docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md
|
|
232
|
+
// @req REQ-SWITCH-CASE-INSIDE
|
|
233
|
+
doSomething();
|
|
234
|
+
}
|
|
235
|
+
default: {
|
|
236
|
+
// @story docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md
|
|
237
|
+
// @req REQ-SWITCH-DEFAULT-INSIDE
|
|
238
|
+
doDefault();
|
|
239
|
+
}
|
|
226
240
|
}`,
|
|
227
241
|
options: [{ annotationPlacement: "inside" }],
|
|
228
242
|
},
|
|
@@ -447,19 +461,30 @@ if (a) {
|
|
|
447
461
|
errors: makeMissingAnnotationErrors("@story", "@req", "@story", "@req"),
|
|
448
462
|
},
|
|
449
463
|
{
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
464
|
+
// Current behavior: inside-only catch annotations do NOT satisfy try branch in inside-placement mode.
|
|
465
|
+
name: "TODO-FUTURE-BEHAVIOR: [REQ-INSIDE-BRACE-PLACEMENT][REQ-PLACEMENT-CONFIG] catch clause annotated inside block under annotationPlacement: 'inside' (Story 028.0)",
|
|
466
|
+
code: `// @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
|
|
467
|
+
// @req REQ-BRANCH-TRY
|
|
468
|
+
try {
|
|
454
469
|
doSomething();
|
|
470
|
+
} catch (error) {
|
|
471
|
+
// @story docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md
|
|
472
|
+
// @req REQ-INSIDE-CATCH
|
|
473
|
+
handleError(error);
|
|
455
474
|
}`,
|
|
456
475
|
options: [{ annotationPlacement: "inside" }],
|
|
457
|
-
output:
|
|
476
|
+
output: "\n\ntry {\n // @story <story-file>.story.md\n doSomething();\n} catch (error) {\n // @story docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md\n // @req REQ-INSIDE-CATCH\n handleError(error);\n}",
|
|
477
|
+
errors: makeMissingAnnotationErrors("@story", "@req"),
|
|
478
|
+
},
|
|
479
|
+
{
|
|
480
|
+
name: "[REQ-INSIDE-BRACE-PLACEMENT][REQ-BEFORE-BRACE-ERROR][REQ-PLACEMENT-CONFIG] before-brace annotations ignored when annotationPlacement: 'inside'",
|
|
481
|
+
code: `// @story docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md
|
|
458
482
|
// @req REQ-BEFORE-BRACE-ERROR
|
|
459
483
|
if (condition) {
|
|
460
|
-
// @story <story-file>.story.md
|
|
461
484
|
doSomething();
|
|
462
485
|
}`,
|
|
486
|
+
options: [{ annotationPlacement: "inside" }],
|
|
487
|
+
output: "\n\nif (condition) {\n // @story <story-file>.story.md\n doSomething();\n}",
|
|
463
488
|
errors: makeMissingAnnotationErrors("@story", "@req"),
|
|
464
489
|
},
|
|
465
490
|
{
|
|
@@ -470,12 +495,7 @@ for (const item of items) {
|
|
|
470
495
|
process(item);
|
|
471
496
|
}`,
|
|
472
497
|
options: [{ annotationPlacement: "inside" }],
|
|
473
|
-
output:
|
|
474
|
-
// @req REQ-LOOP-BEFORE
|
|
475
|
-
// @story <story-file>.story.md
|
|
476
|
-
for (const item of items) {
|
|
477
|
-
process(item);
|
|
478
|
-
}`,
|
|
498
|
+
output: "\n\nfor (const item of items) {\n // @story <story-file>.story.md\n process(item);\n}",
|
|
479
499
|
errors: makeMissingAnnotationErrors("@story", "@req"),
|
|
480
500
|
},
|
|
481
501
|
{
|
|
@@ -491,17 +511,20 @@ catch (error) {
|
|
|
491
511
|
handleError(error);
|
|
492
512
|
}`,
|
|
493
513
|
options: [{ annotationPlacement: "inside" }],
|
|
494
|
-
output:
|
|
495
|
-
|
|
514
|
+
output: "\n\ntry {\n // @story <story-file>.story.md\n doSomething();\n}\n// @story docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md\n// @req REQ-CATCH-BEFORE\ncatch (error) {\n handleError(error);\n}",
|
|
515
|
+
errors: makeMissingAnnotationErrors("@story", "@req", "@story", "@req"),
|
|
516
|
+
},
|
|
517
|
+
{
|
|
518
|
+
name: "[REQ-INSIDE-BRACE-PLACEMENT][REQ-BEFORE-BRACE-ERROR][REQ-PLACEMENT-CONFIG] before-try annotations ignored when annotationPlacement: 'inside' for TryStatement (Story 028.0)",
|
|
519
|
+
code: `// @story docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md
|
|
520
|
+
// @req REQ-TRY-BEFORE
|
|
496
521
|
try {
|
|
497
|
-
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
// @req REQ-CATCH-BEFORE
|
|
501
|
-
catch (error) {
|
|
502
|
-
// @story <story-file>.story.md
|
|
503
|
-
handleError(error);
|
|
522
|
+
doWork();
|
|
523
|
+
} finally {
|
|
524
|
+
cleanup();
|
|
504
525
|
}`,
|
|
526
|
+
options: [{ annotationPlacement: "inside" }],
|
|
527
|
+
output: "\n\ntry {\n // @story <story-file>.story.md\n doWork();\n} finally {\n cleanup();\n}",
|
|
505
528
|
errors: makeMissingAnnotationErrors("@story", "@req"),
|
|
506
529
|
},
|
|
507
530
|
{
|
|
@@ -517,17 +540,7 @@ else if (b) {
|
|
|
517
540
|
doB();
|
|
518
541
|
}`,
|
|
519
542
|
options: [{ annotationPlacement: "inside" }],
|
|
520
|
-
output:
|
|
521
|
-
// @story docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md
|
|
522
|
-
// @req REQ-OUTER-IF-INSIDE
|
|
523
|
-
doA();
|
|
524
|
-
}
|
|
525
|
-
// @story docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md
|
|
526
|
-
// @req REQ-ELSE-IF-BEFORE
|
|
527
|
-
else if (b) {
|
|
528
|
-
// @story <story-file>.story.md
|
|
529
|
-
doB();
|
|
530
|
-
}`,
|
|
543
|
+
output: "if (a) {\n // @story docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md\n // @req REQ-OUTER-IF-INSIDE\n doA();\n}\n// @story docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md\n// @req REQ-ELSE-IF-BEFORE\nelse if (b) {\n // @story <story-file>.story.md\n doB();\n}",
|
|
531
544
|
errors: makeMissingAnnotationErrors("@story", "@req"),
|
|
532
545
|
},
|
|
533
546
|
{
|
|
@@ -544,18 +557,20 @@ if (a) {
|
|
|
544
557
|
doC();
|
|
545
558
|
}`,
|
|
546
559
|
options: [{ annotationPlacement: "inside" }],
|
|
547
|
-
output:
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
560
|
+
output: "\n\nif (a) {\n // @story <story-file>.story.md\n doA();\n} else if (b) {\n // @story docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md\n // @req REQ-INSIDE-ELSE-IF\n doB();\n} else {\n doC();\n}",
|
|
561
|
+
errors: makeMissingAnnotationErrors("@story", "@req"),
|
|
562
|
+
},
|
|
563
|
+
{
|
|
564
|
+
name: "[REQ-INSIDE-BRACE-PLACEMENT][REQ-BEFORE-BRACE-ERROR][REQ-PLACEMENT-CONFIG] before-case annotations ignored when annotationPlacement: 'inside' for SwitchCase",
|
|
565
|
+
code: `switch (value) {
|
|
553
566
|
// @story docs/stories/028.0-DEV-ANNOTATION-PLACEMENT-STANDARDIZATION.story.md
|
|
554
|
-
// @req REQ-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
567
|
+
// @req REQ-SWITCH-BEFORE
|
|
568
|
+
case 'a': {
|
|
569
|
+
doSomething();
|
|
570
|
+
}
|
|
558
571
|
}`,
|
|
572
|
+
options: [{ annotationPlacement: "inside" }],
|
|
573
|
+
output: "switch (value) {\n \n \n // @story <story-file>.story.md\n case 'a': {\n doSomething();\n }\n}",
|
|
559
574
|
errors: makeMissingAnnotationErrors("@story", "@req"),
|
|
560
575
|
},
|
|
561
576
|
],
|