@ontrails/warden 1.0.0-beta.0 → 1.0.0-beta.10
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/.turbo/turbo-lint.log +1 -1
- package/CHANGELOG.md +159 -0
- package/README.md +57 -77
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +1 -4
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/rules/ast.d.ts +15 -8
- package/dist/rules/ast.d.ts.map +1 -1
- package/dist/rules/ast.js +99 -44
- package/dist/rules/ast.js.map +1 -1
- package/dist/rules/context-no-surface-types.js +1 -1
- package/dist/rules/context-no-surface-types.js.map +1 -1
- package/dist/rules/follow-declarations.d.ts +13 -0
- package/dist/rules/follow-declarations.d.ts.map +1 -0
- package/dist/rules/follow-declarations.js +264 -0
- package/dist/rules/follow-declarations.js.map +1 -0
- package/dist/rules/implementation-returns-result.d.ts +1 -1
- package/dist/rules/implementation-returns-result.d.ts.map +1 -1
- package/dist/rules/implementation-returns-result.js +52 -6
- package/dist/rules/implementation-returns-result.js.map +1 -1
- package/dist/rules/index.d.ts +2 -8
- package/dist/rules/index.d.ts.map +1 -1
- package/dist/rules/index.js +4 -8
- package/dist/rules/index.js.map +1 -1
- package/dist/rules/no-direct-impl-in-route.d.ts +4 -4
- package/dist/rules/no-direct-impl-in-route.d.ts.map +1 -1
- package/dist/rules/no-direct-impl-in-route.js +15 -14
- package/dist/rules/no-direct-impl-in-route.js.map +1 -1
- package/dist/rules/no-direct-implementation-call.d.ts +3 -3
- package/dist/rules/no-direct-implementation-call.js +7 -7
- package/dist/rules/no-direct-implementation-call.js.map +1 -1
- package/dist/rules/no-sync-result-assumption.d.ts +1 -1
- package/dist/rules/no-sync-result-assumption.js +5 -5
- package/dist/rules/no-sync-result-assumption.js.map +1 -1
- package/dist/rules/no-throw-in-detour-target.js +2 -2
- package/dist/rules/no-throw-in-detour-target.js.map +1 -1
- package/dist/rules/no-throw-in-implementation.d.ts +1 -1
- package/dist/rules/no-throw-in-implementation.js +3 -3
- package/dist/rules/no-throw-in-implementation.js.map +1 -1
- package/dist/rules/specs.d.ts +1 -1
- package/dist/rules/specs.d.ts.map +1 -1
- package/dist/rules/specs.js +2 -2
- package/dist/rules/specs.js.map +1 -1
- package/dist/trails/context-no-surface-types.trail.d.ts +13 -0
- package/dist/trails/context-no-surface-types.trail.d.ts.map +1 -0
- package/dist/trails/context-no-surface-types.trail.js +21 -0
- package/dist/trails/context-no-surface-types.trail.js.map +1 -0
- package/dist/trails/follow-declarations.trail.d.ts +13 -0
- package/dist/trails/follow-declarations.trail.d.ts.map +1 -0
- package/dist/trails/follow-declarations.trail.js +22 -0
- package/dist/trails/follow-declarations.trail.js.map +1 -0
- package/dist/trails/implementation-returns-result.trail.d.ts +13 -0
- package/dist/trails/implementation-returns-result.trail.d.ts.map +1 -0
- package/dist/trails/implementation-returns-result.trail.js +20 -0
- package/dist/trails/implementation-returns-result.trail.js.map +1 -0
- package/dist/trails/index.d.ts +14 -0
- package/dist/trails/index.d.ts.map +1 -0
- package/dist/trails/index.js +13 -0
- package/dist/trails/index.js.map +1 -0
- package/dist/trails/no-direct-impl-in-route.trail.d.ts +13 -0
- package/dist/trails/no-direct-impl-in-route.trail.d.ts.map +1 -0
- package/dist/trails/no-direct-impl-in-route.trail.js +22 -0
- package/dist/trails/no-direct-impl-in-route.trail.js.map +1 -0
- package/dist/trails/no-direct-implementation-call.trail.d.ts +13 -0
- package/dist/trails/no-direct-implementation-call.trail.d.ts.map +1 -0
- package/dist/trails/no-direct-implementation-call.trail.js +16 -0
- package/dist/trails/no-direct-implementation-call.trail.js.map +1 -0
- package/dist/trails/no-sync-result-assumption.trail.d.ts +13 -0
- package/dist/trails/no-sync-result-assumption.trail.d.ts.map +1 -0
- package/dist/trails/no-sync-result-assumption.trail.js +19 -0
- package/dist/trails/no-sync-result-assumption.trail.js.map +1 -0
- package/dist/trails/no-throw-in-detour-target.trail.d.ts +14 -0
- package/dist/trails/no-throw-in-detour-target.trail.d.ts.map +1 -0
- package/dist/trails/no-throw-in-detour-target.trail.js +20 -0
- package/dist/trails/no-throw-in-detour-target.trail.js.map +1 -0
- package/dist/trails/no-throw-in-implementation.trail.d.ts +13 -0
- package/dist/trails/no-throw-in-implementation.trail.d.ts.map +1 -0
- package/dist/trails/no-throw-in-implementation.trail.js +20 -0
- package/dist/trails/no-throw-in-implementation.trail.js.map +1 -0
- package/dist/trails/prefer-schema-inference.trail.d.ts +13 -0
- package/dist/trails/prefer-schema-inference.trail.d.ts.map +1 -0
- package/dist/trails/prefer-schema-inference.trail.js +21 -0
- package/dist/trails/prefer-schema-inference.trail.js.map +1 -0
- package/dist/trails/run.d.ts +16 -0
- package/dist/trails/run.d.ts.map +1 -0
- package/dist/trails/run.js +30 -0
- package/dist/trails/run.js.map +1 -0
- package/dist/trails/schema.d.ts +52 -0
- package/dist/trails/schema.d.ts.map +1 -0
- package/dist/trails/schema.js +38 -0
- package/dist/trails/schema.js.map +1 -0
- package/dist/trails/topo.d.ts +3 -0
- package/dist/trails/topo.d.ts.map +1 -0
- package/dist/trails/topo.js +5 -0
- package/dist/trails/topo.js.map +1 -0
- package/dist/trails/valid-describe-refs.trail.d.ts +14 -0
- package/dist/trails/valid-describe-refs.trail.d.ts.map +1 -0
- package/dist/trails/valid-describe-refs.trail.js +18 -0
- package/dist/trails/valid-describe-refs.trail.js.map +1 -0
- package/dist/trails/valid-detour-refs.trail.d.ts +14 -0
- package/dist/trails/valid-detour-refs.trail.d.ts.map +1 -0
- package/dist/trails/valid-detour-refs.trail.js +24 -0
- package/dist/trails/valid-detour-refs.trail.js.map +1 -0
- package/dist/trails/wrap-rule.d.ts +29 -0
- package/dist/trails/wrap-rule.d.ts.map +1 -0
- package/dist/trails/wrap-rule.js +43 -0
- package/dist/trails/wrap-rule.js.map +1 -0
- package/package.json +5 -4
- package/src/__tests__/cli.test.ts +7 -7
- package/src/__tests__/drift.test.ts +1 -1
- package/src/__tests__/follow-declarations.test.ts +303 -0
- package/src/__tests__/implementation-returns-result.test.ts +60 -6
- package/src/__tests__/no-direct-implementation-call.test.ts +8 -8
- package/src/__tests__/no-sync-result-assumption.test.ts +6 -6
- package/src/__tests__/no-throw-in-detour-target.test.ts +6 -6
- package/src/__tests__/prefer-schema-inference.test.ts +4 -4
- package/src/__tests__/rules.test.ts +59 -20
- package/src/__tests__/trails.test.ts +19 -0
- package/src/__tests__/valid-describe-refs.test.ts +4 -4
- package/src/cli.ts +1 -4
- package/src/index.ts +21 -0
- package/src/rules/ast.ts +126 -57
- package/src/rules/context-no-surface-types.ts +1 -1
- package/src/rules/follow-declarations.ts +380 -0
- package/src/rules/implementation-returns-result.ts +63 -6
- package/src/rules/index.ts +4 -8
- package/src/rules/no-direct-impl-in-route.ts +20 -16
- package/src/rules/no-direct-implementation-call.ts +7 -7
- package/src/rules/no-sync-result-assumption.ts +5 -5
- package/src/rules/no-throw-in-detour-target.ts +2 -2
- package/src/rules/no-throw-in-implementation.ts +3 -3
- package/src/rules/specs.ts +5 -5
- package/src/trails/context-no-surface-types.trail.ts +21 -0
- package/src/trails/follow-declarations.trail.ts +22 -0
- package/src/trails/implementation-returns-result.trail.ts +20 -0
- package/src/trails/index.ts +14 -0
- package/src/trails/no-direct-impl-in-route.trail.ts +22 -0
- package/src/trails/no-direct-implementation-call.trail.ts +16 -0
- package/src/trails/no-sync-result-assumption.trail.ts +19 -0
- package/src/trails/no-throw-in-detour-target.trail.ts +20 -0
- package/src/trails/no-throw-in-implementation.trail.ts +20 -0
- package/src/trails/prefer-schema-inference.trail.ts +21 -0
- package/src/trails/run.ts +40 -0
- package/src/trails/schema.ts +46 -0
- package/src/trails/topo.ts +6 -0
- package/src/trails/valid-describe-refs.trail.ts +18 -0
- package/src/trails/valid-detour-refs.trail.ts +24 -0
- package/src/trails/wrap-rule.ts +84 -0
- package/tsconfig.tsbuildinfo +1 -1
package/dist/rules/ast.js
CHANGED
|
@@ -54,24 +54,32 @@ export const offsetToLine = (sourceCode, offset) => {
|
|
|
54
54
|
}
|
|
55
55
|
return line;
|
|
56
56
|
};
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
// Config property extraction helpers
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
/** Find a Property node by key name inside an ObjectExpression config. */
|
|
61
|
+
export const findConfigProperty = (config, propertyName) => {
|
|
62
|
+
if (config.type !== 'ObjectExpression') {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
const properties = config['properties'];
|
|
66
|
+
if (!properties) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
for (const prop of properties) {
|
|
70
|
+
if (prop.type === 'Property' && prop.key?.name === propertyName) {
|
|
71
|
+
return prop;
|
|
65
72
|
}
|
|
66
|
-
}
|
|
67
|
-
return
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
68
75
|
};
|
|
69
76
|
/**
|
|
70
|
-
* Find all `trail("id", { ... })
|
|
77
|
+
* Find all `trail("id", { ... })`, `trail({ id: "x", ... })`, and
|
|
78
|
+
* `event("id", { ... })` call sites.
|
|
71
79
|
*
|
|
72
80
|
* Returns the trail ID, kind, and config object node for each definition.
|
|
73
81
|
*/
|
|
74
|
-
const TRAIL_CALLEE_NAMES = new Set(['trail', '
|
|
82
|
+
const TRAIL_CALLEE_NAMES = new Set(['trail', 'event']);
|
|
75
83
|
const getTrailCalleeName = (node) => {
|
|
76
84
|
if (node.type !== 'CallExpression') {
|
|
77
85
|
return null;
|
|
@@ -83,16 +91,39 @@ const getTrailCalleeName = (node) => {
|
|
|
83
91
|
const { name } = callee;
|
|
84
92
|
return name && TRAIL_CALLEE_NAMES.has(name) ? name : null;
|
|
85
93
|
};
|
|
94
|
+
/** Extract args from a trail() call, handling both two-arg and single-object forms. */
|
|
86
95
|
const extractTrailArgs = (node) => {
|
|
87
96
|
const args = node['arguments'];
|
|
88
|
-
if (!args || args.length
|
|
97
|
+
if (!args || args.length === 0) {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
const [firstArg, secondArg] = args;
|
|
101
|
+
if (!firstArg) {
|
|
89
102
|
return null;
|
|
90
103
|
}
|
|
91
|
-
|
|
92
|
-
if (
|
|
104
|
+
// Two-arg form: trail('id', { ... })
|
|
105
|
+
if (secondArg && firstArg.type !== 'ObjectExpression') {
|
|
106
|
+
return { configArg: secondArg, idArg: firstArg };
|
|
107
|
+
}
|
|
108
|
+
// Single-object form: trail({ id: 'x', ... })
|
|
109
|
+
return firstArg.type === 'ObjectExpression'
|
|
110
|
+
? { configArg: firstArg, idArg: null }
|
|
111
|
+
: null;
|
|
112
|
+
};
|
|
113
|
+
/** Extract the string value from an `id` property inside a config ObjectExpression. */
|
|
114
|
+
const extractIdFromConfig = (config) => {
|
|
115
|
+
const idProp = findConfigProperty(config, 'id');
|
|
116
|
+
if (!idProp || !idProp.value) {
|
|
93
117
|
return null;
|
|
94
118
|
}
|
|
95
|
-
|
|
119
|
+
const val = idProp.value.value;
|
|
120
|
+
return typeof val === 'string' ? val : null;
|
|
121
|
+
};
|
|
122
|
+
const extractTrailId = (trailArgs) => {
|
|
123
|
+
if (trailArgs.idArg) {
|
|
124
|
+
return trailArgs.idArg.value ?? null;
|
|
125
|
+
}
|
|
126
|
+
return extractIdFromConfig(trailArgs.configArg);
|
|
96
127
|
};
|
|
97
128
|
const extractTrailDefinition = (node) => {
|
|
98
129
|
const calleeName = getTrailCalleeName(node);
|
|
@@ -103,7 +134,7 @@ const extractTrailDefinition = (node) => {
|
|
|
103
134
|
if (!trailArgs) {
|
|
104
135
|
return null;
|
|
105
136
|
}
|
|
106
|
-
const trailId = trailArgs
|
|
137
|
+
const trailId = extractTrailId(trailArgs);
|
|
107
138
|
if (!trailId) {
|
|
108
139
|
return null;
|
|
109
140
|
}
|
|
@@ -114,23 +145,6 @@ const extractTrailDefinition = (node) => {
|
|
|
114
145
|
start: node.start,
|
|
115
146
|
};
|
|
116
147
|
};
|
|
117
|
-
/** Check if a node is a call to `.implementation()` on some object. */
|
|
118
|
-
export const isImplementationCall = (node) => {
|
|
119
|
-
if (node.type !== 'CallExpression') {
|
|
120
|
-
return false;
|
|
121
|
-
}
|
|
122
|
-
const callee = node['callee'];
|
|
123
|
-
if (!callee) {
|
|
124
|
-
return false;
|
|
125
|
-
}
|
|
126
|
-
if (callee.type !== 'StaticMemberExpression' &&
|
|
127
|
-
callee.type !== 'MemberExpression') {
|
|
128
|
-
return false;
|
|
129
|
-
}
|
|
130
|
-
const prop = callee.property;
|
|
131
|
-
return (prop?.type === 'Identifier' &&
|
|
132
|
-
prop.name === 'implementation');
|
|
133
|
-
};
|
|
134
148
|
export const findTrailDefinitions = (ast) => {
|
|
135
149
|
const definitions = [];
|
|
136
150
|
walk(ast, (node) => {
|
|
@@ -142,22 +156,63 @@ export const findTrailDefinitions = (ast) => {
|
|
|
142
156
|
return definitions;
|
|
143
157
|
};
|
|
144
158
|
// ---------------------------------------------------------------------------
|
|
145
|
-
//
|
|
159
|
+
// Run body extraction
|
|
146
160
|
// ---------------------------------------------------------------------------
|
|
147
|
-
/**
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
161
|
+
/**
|
|
162
|
+
* Extract top-level `run:` property values from an ObjectExpression's direct properties.
|
|
163
|
+
*
|
|
164
|
+
* Does not recurse into nested objects, so `metadata: { run: ... }` is ignored.
|
|
165
|
+
*/
|
|
166
|
+
const extractRunFromConfig = (config) => {
|
|
167
|
+
const bodies = [];
|
|
152
168
|
const properties = config['properties'];
|
|
153
169
|
if (!properties) {
|
|
154
|
-
return
|
|
170
|
+
return bodies;
|
|
155
171
|
}
|
|
156
172
|
for (const prop of properties) {
|
|
157
|
-
if (prop.type === 'Property' && prop.key?.name ===
|
|
158
|
-
|
|
173
|
+
if (prop.type === 'Property' && prop.key?.name === 'run' && prop.value) {
|
|
174
|
+
bodies.push(prop.value);
|
|
159
175
|
}
|
|
160
176
|
}
|
|
161
|
-
return
|
|
177
|
+
return bodies;
|
|
178
|
+
};
|
|
179
|
+
/**
|
|
180
|
+
* Find `run:` property values.
|
|
181
|
+
*
|
|
182
|
+
* When given an ObjectExpression (trail config), returns only its direct `run:`
|
|
183
|
+
* properties. When given a full AST, finds trail definitions first and extracts
|
|
184
|
+
* `run:` from each config — in both cases ignoring nested `run:` properties
|
|
185
|
+
* (e.g. `metadata: { run: ... }`).
|
|
186
|
+
*/
|
|
187
|
+
export const findRunBodies = (node) => {
|
|
188
|
+
if (node.type === 'ObjectExpression') {
|
|
189
|
+
return extractRunFromConfig(node);
|
|
190
|
+
}
|
|
191
|
+
// Full AST — find trail definitions and extract run from their configs
|
|
192
|
+
const bodies = [];
|
|
193
|
+
for (const def of findTrailDefinitions(node)) {
|
|
194
|
+
bodies.push(...extractRunFromConfig(def.config));
|
|
195
|
+
}
|
|
196
|
+
return bodies;
|
|
197
|
+
};
|
|
198
|
+
// ---------------------------------------------------------------------------
|
|
199
|
+
// Misc helpers
|
|
200
|
+
// ---------------------------------------------------------------------------
|
|
201
|
+
/** Check if a node is a call to `.run()` on some object. */
|
|
202
|
+
export const isRunCall = (node) => {
|
|
203
|
+
if (node.type !== 'CallExpression') {
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
const callee = node['callee'];
|
|
207
|
+
if (!callee) {
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
if (callee.type !== 'StaticMemberExpression' &&
|
|
211
|
+
callee.type !== 'MemberExpression') {
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
const prop = callee.property;
|
|
215
|
+
return (prop?.type === 'Identifier' &&
|
|
216
|
+
prop.name === 'run');
|
|
162
217
|
};
|
|
163
218
|
//# sourceMappingURL=ast.js.map
|
package/dist/rules/ast.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ast.js","sourceRoot":"","sources":["../../src/rules/ast.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAgBvC,8EAA8E;AAC9E,SAAS;AACT,8EAA8E;AAE9E,0EAA0E;AAC1E,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,QAAgB,EAAE,UAAkB,EAAkB,EAAE;IAC5E,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,SAAS,CAAC,QAAQ,EAAE,UAAU,EAAE,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC,CAAC;QACzE,OAAO,MAAM,CAAC,OAA6B,CAAC;IAC9C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC,CAAC;AAEF,8EAA8E;AAC9E,SAAS;AACT,8EAA8E;AAE9E,4DAA4D;AAC5D,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,IAAa,EAAE,KAA8B,EAAQ,EAAE;IAC1E,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtC,OAAO;IACT,CAAC;IACD,MAAM,CAAC,GAAG,IAAe,CAAC;IAC1B,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;QACX,KAAK,CAAC,CAAC,CAAC,CAAC;IACX,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;QACnC,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YACvB,KAAK,MAAM,IAAI,IAAI,GAAG,EAAE,CAAC;gBACvB,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YACpB,CAAC;QACH,CAAC;aAAM,IAAI,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAK,GAAe,CAAC,IAAI,EAAE,CAAC;YACnE,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACnB,CAAC;IACH,CAAC;AACH,CAAC,CAAC;AAEF,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E,mEAAmE;AACnE,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,UAAkB,EAAE,MAAc,EAAU,EAAE;IACzE,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,IAAI,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAC5D,IAAI,UAAU,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;YAC3B,IAAI,IAAI,CAAC,CAAC;QACZ,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AAEF,
|
|
1
|
+
{"version":3,"file":"ast.js","sourceRoot":"","sources":["../../src/rules/ast.ts"],"names":[],"mappings":"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"}
|
|
@@ -73,7 +73,7 @@ const classifyImport = (node, filePath, sourceCode) => {
|
|
|
73
73
|
*/
|
|
74
74
|
export const contextNoSurfaceTypes = {
|
|
75
75
|
check(sourceCode, filePath) {
|
|
76
|
-
if (!/\
|
|
76
|
+
if (!/\btrail\s*\(/.test(sourceCode)) {
|
|
77
77
|
return [];
|
|
78
78
|
}
|
|
79
79
|
const ast = parse(filePath, sourceCode);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-no-surface-types.js","sourceRoot":"","sources":["../../src/rules/context-no-surface-types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAGrD,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC;IAC9B,SAAS;IACT,MAAM;IACN,SAAS;IACT,2BAA2B;IAC3B,WAAW;IACX,YAAY;IACZ,mBAAmB;IACnB,KAAK;CACN,CAAC,CAAC;AAEH,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC;IACjC,SAAS;IACT,UAAU;IACV,cAAc;IACd,YAAY;IACZ,oBAAoB;IACpB,iBAAiB;IACjB,gBAAgB;CACjB,CAAC,CAAC;AAaH,MAAM,QAAQ,GAAG,CACf,QAAgB,EAChB,UAAkB,EAClB,IAAa,EACb,OAAe,EACG,EAAE,CAAC,CAAC;IACtB,QAAQ;IACR,IAAI,EAAE,YAAY,CAAC,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC;IAC1C,OAAO;IACP,IAAI,EAAE,0BAA0B;IAChC,QAAQ,EAAE,OAAO;CAClB,CAAC,CAAC;AAEH,MAAM,mBAAmB,GAAG,CAC1B,UAAsC,EAClB,EAAE;IACtB,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,EAAE,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC;QACrD,IAAI,IAAI,IAAI,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YACzC,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC,CAAC;AAEF,MAAM,mBAAmB,GAAG,CAAC,IAAa,EAAiB,EAAE;IAC3D,IAAI,IAAI,CAAC,IAAI,KAAK,mBAAmB,EAAE,CAAC;QACtC,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAA4C,CAAC;IACzE,OAAO,MAAM,EAAE,KAAK,IAAI,IAAI,CAAC;AAC/B,CAAC,CAAC;AAEF,MAAM,8BAA8B,GAAG,CACrC,IAAa,EACb,QAAgB,EAChB,UAAkB,EACY,EAAE;IAChC,MAAM,UAAU,GAAG,IAAI,CAAC,YAAY,CAEvB,CAAC;IACd,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,QAAQ,GAAG,mBAAmB,CAAC,UAAU,CAAC,CAAC;IACjD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,QAAQ,CACb,QAAQ,EACR,UAAU,EACV,IAAI,EACJ,+BAA+B,QAAQ,kCAAkC,CAC1E,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,cAAc,GAAG,CACrB,IAAa,EACb,QAAgB,EAChB,UAAkB,EACY,EAAE;IAChC,MAAM,UAAU,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAC7C,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,eAAe,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;QACpC,OAAO,QAAQ,CACb,QAAQ,EACR,UAAU,EACV,IAAI,EACJ,sCAAsC,UAAU,kCAAkC,CACnF,CAAC;IACJ,CAAC;IAED,OAAO,8BAA8B,CAAC,IAAI,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC;AACpE,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAe;IAC/C,KAAK,CAAC,UAAkB,EAAE,QAAgB;QACxC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"context-no-surface-types.js","sourceRoot":"","sources":["../../src/rules/context-no-surface-types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validates that `ctx.follow()` calls match the declared `follow` array.
|
|
3
|
+
*
|
|
4
|
+
* Statically analyzes trail run functions to find `ctx.follow('trailId', ...)`
|
|
5
|
+
* calls and compares them against the `follow: [...]` declaration in the trail
|
|
6
|
+
* config. Reports errors for undeclared follows and warnings for unused ones.
|
|
7
|
+
*/
|
|
8
|
+
import type { WardenRule } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Validates that `ctx.follow()` calls align with declared `follow` arrays.
|
|
11
|
+
*/
|
|
12
|
+
export declare const followDeclarations: WardenRule;
|
|
13
|
+
//# sourceMappingURL=follow-declarations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"follow-declarations.d.ts","sourceRoot":"","sources":["../../src/rules/follow-declarations.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAYH,OAAO,KAAK,EAAoB,UAAU,EAAE,MAAM,YAAY,CAAC;AA+U/D;;GAEG;AACH,eAAO,MAAM,kBAAkB,EAAE,UAuBhC,CAAC"}
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validates that `ctx.follow()` calls match the declared `follow` array.
|
|
3
|
+
*
|
|
4
|
+
* Statically analyzes trail run functions to find `ctx.follow('trailId', ...)`
|
|
5
|
+
* calls and compares them against the `follow: [...]` declaration in the trail
|
|
6
|
+
* config. Reports errors for undeclared follows and warnings for unused ones.
|
|
7
|
+
*/
|
|
8
|
+
import { findConfigProperty, findRunBodies, findTrailDefinitions, offsetToLine, parse, walk, } from './ast.js';
|
|
9
|
+
import { isTestFile } from './scan.js';
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Shared identifier helpers
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
/** Get the name of an Identifier node, or null. */
|
|
14
|
+
const identifierName = (node) => {
|
|
15
|
+
if (node?.type !== 'Identifier') {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
return node.name ?? null;
|
|
19
|
+
};
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// String literal helpers
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
/** Check if a node is a string literal (covers `StringLiteral` and `Literal` with string value). */
|
|
24
|
+
const isStringLiteral = (node) => {
|
|
25
|
+
if (node.type === 'StringLiteral') {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
if (node.type === 'Literal') {
|
|
29
|
+
return typeof node.value === 'string';
|
|
30
|
+
}
|
|
31
|
+
return false;
|
|
32
|
+
};
|
|
33
|
+
/** Extract the string value from a string literal node. */
|
|
34
|
+
const getStringValue = (node) => {
|
|
35
|
+
const val = node.value;
|
|
36
|
+
return typeof val === 'string' ? val : null;
|
|
37
|
+
};
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
// Const identifier resolution
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
/**
|
|
42
|
+
* Best-effort resolution of `const NAME = 'value'` declarations via regex.
|
|
43
|
+
*
|
|
44
|
+
* Returns the string value if a simple `const <name> = '...'` or `"..."` is
|
|
45
|
+
* found in the source. Returns null for anything more complex.
|
|
46
|
+
*/
|
|
47
|
+
const resolveConstString = (name, sourceCode) => {
|
|
48
|
+
const pattern = new RegExp(`const\\s+${name}\\s*=\\s*(?:'([^']*)'|"([^"]*)")`);
|
|
49
|
+
const match = pattern.exec(sourceCode);
|
|
50
|
+
if (!match) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
return match[1] ?? match[2] ?? null;
|
|
54
|
+
};
|
|
55
|
+
/** Try to resolve an Identifier element to a string via const declaration. */
|
|
56
|
+
const resolveIdentifierElement = (el, sourceCode) => {
|
|
57
|
+
const name = identifierName(el);
|
|
58
|
+
if (!name) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
return resolveConstString(name, sourceCode);
|
|
62
|
+
};
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
// Declared follow extraction
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
/** Extract the ArrayExpression elements from a config's `follow` property. */
|
|
67
|
+
const getFollowElements = (config) => {
|
|
68
|
+
const followProp = findConfigProperty(config, 'follow');
|
|
69
|
+
if (!followProp) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
const arrayNode = followProp.value;
|
|
73
|
+
if (!arrayNode || arrayNode.type !== 'ArrayExpression') {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
const elements = arrayNode['elements'];
|
|
77
|
+
return elements ?? null;
|
|
78
|
+
};
|
|
79
|
+
/** Collect string IDs from array elements, resolving identifiers when possible. */
|
|
80
|
+
const collectStringIds = (elements, sourceCode) => {
|
|
81
|
+
const ids = new Set();
|
|
82
|
+
for (const el of elements) {
|
|
83
|
+
if (isStringLiteral(el)) {
|
|
84
|
+
const val = getStringValue(el);
|
|
85
|
+
if (val) {
|
|
86
|
+
ids.add(val);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
else if (el.type === 'Identifier') {
|
|
90
|
+
const resolved = resolveIdentifierElement(el, sourceCode);
|
|
91
|
+
if (resolved) {
|
|
92
|
+
ids.add(resolved);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return ids;
|
|
97
|
+
};
|
|
98
|
+
/** Extract string literal elements from a `follow: [...]` array property. */
|
|
99
|
+
const extractDeclaredFollows = (config, sourceCode) => {
|
|
100
|
+
const elements = getFollowElements(config);
|
|
101
|
+
return elements ? collectStringIds(elements, sourceCode) : new Set();
|
|
102
|
+
};
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
// Called follow extraction — member expression helpers
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
const MEMBER_TYPES = new Set(['StaticMemberExpression', 'MemberExpression']);
|
|
107
|
+
/** Extract object and property Identifier names from a MemberExpression. */
|
|
108
|
+
const extractMemberPair = (callee) => {
|
|
109
|
+
if (!MEMBER_TYPES.has(callee.type)) {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
const objName = identifierName(callee.object);
|
|
113
|
+
const propName = identifierName(callee.property);
|
|
114
|
+
return objName && propName ? { objName, propName } : null;
|
|
115
|
+
};
|
|
116
|
+
/** Extract the first argument string from a CallExpression's arguments list. */
|
|
117
|
+
const extractFirstStringArg = (node) => {
|
|
118
|
+
const args = node['arguments'];
|
|
119
|
+
if (!args || args.length === 0) {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
const [firstArg] = args;
|
|
123
|
+
if (!firstArg || !isStringLiteral(firstArg)) {
|
|
124
|
+
// Dynamic ID — cannot resolve statically
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
return getStringValue(firstArg);
|
|
128
|
+
};
|
|
129
|
+
/**
|
|
130
|
+
* Extract the second parameter name from a run function node.
|
|
131
|
+
*
|
|
132
|
+
* Handles `(input, ctx) => ...` and `async (input, context) => ...` and
|
|
133
|
+
* `function(input, ctx) { ... }` forms.
|
|
134
|
+
*/
|
|
135
|
+
const extractContextParamName = (runBody) => {
|
|
136
|
+
const params = runBody['params'];
|
|
137
|
+
if (!params || params.length < 2) {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
return identifierName(params[1]);
|
|
141
|
+
};
|
|
142
|
+
/** Check if a callee is a member-style follow call: <ctxName>.follow(...). */
|
|
143
|
+
const isMemberFollowCall = (callee, ctxNames) => {
|
|
144
|
+
const pair = extractMemberPair(callee);
|
|
145
|
+
return !!pair && ctxNames.has(pair.objName) && pair.propName === 'follow';
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* Check if a node is a `<ctxName>.follow(...)` call and return the string trail ID.
|
|
149
|
+
*
|
|
150
|
+
* Also matches bare `follow(...)` calls (destructured pattern).
|
|
151
|
+
*/
|
|
152
|
+
const extractFollowCallId = (node, ctxNames) => {
|
|
153
|
+
if (node.type !== 'CallExpression') {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
const callee = node['callee'];
|
|
157
|
+
if (!callee) {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
if (isMemberFollowCall(callee, ctxNames)) {
|
|
161
|
+
return extractFirstStringArg(node);
|
|
162
|
+
}
|
|
163
|
+
// Match bare follow(...) — destructured pattern
|
|
164
|
+
if (identifierName(callee) === 'follow') {
|
|
165
|
+
return extractFirstStringArg(node);
|
|
166
|
+
}
|
|
167
|
+
return null;
|
|
168
|
+
};
|
|
169
|
+
/** Build the set of context parameter names to match against. */
|
|
170
|
+
const buildCtxNames = (body) => {
|
|
171
|
+
const ctxNames = new Set(['ctx', 'context']);
|
|
172
|
+
const paramName = extractContextParamName(body);
|
|
173
|
+
if (paramName) {
|
|
174
|
+
ctxNames.add(paramName);
|
|
175
|
+
}
|
|
176
|
+
return ctxNames;
|
|
177
|
+
};
|
|
178
|
+
/** Walk run bodies and collect all statically resolvable ctx.follow() trail IDs. */
|
|
179
|
+
const extractCalledFollows = (config) => {
|
|
180
|
+
const ids = new Set();
|
|
181
|
+
for (const body of findRunBodies(config)) {
|
|
182
|
+
const ctxNames = buildCtxNames(body);
|
|
183
|
+
walk(body, (node) => {
|
|
184
|
+
const id = extractFollowCallId(node, ctxNames);
|
|
185
|
+
if (id) {
|
|
186
|
+
ids.add(id);
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
return ids;
|
|
191
|
+
};
|
|
192
|
+
// ---------------------------------------------------------------------------
|
|
193
|
+
// Diagnostic builders
|
|
194
|
+
// ---------------------------------------------------------------------------
|
|
195
|
+
const buildUndeclaredDiagnostic = (trailId, followId, filePath, line) => ({
|
|
196
|
+
filePath,
|
|
197
|
+
line,
|
|
198
|
+
message: `Trail "${trailId}": ctx.follow('${followId}') called but '${followId}' is not declared in follow`,
|
|
199
|
+
rule: 'follow-declarations',
|
|
200
|
+
severity: 'error',
|
|
201
|
+
});
|
|
202
|
+
const buildUnusedDiagnostic = (trailId, followId, filePath, line) => ({
|
|
203
|
+
filePath,
|
|
204
|
+
line,
|
|
205
|
+
message: `Trail "${trailId}": '${followId}' declared in follow but ctx.follow('${followId}') never called`,
|
|
206
|
+
rule: 'follow-declarations',
|
|
207
|
+
severity: 'warn',
|
|
208
|
+
});
|
|
209
|
+
// ---------------------------------------------------------------------------
|
|
210
|
+
// Comparison
|
|
211
|
+
// ---------------------------------------------------------------------------
|
|
212
|
+
/** Emit error for each called ID not present in declared set. */
|
|
213
|
+
const reportUndeclared = (called, declared, ctx, diagnostics) => {
|
|
214
|
+
for (const id of called) {
|
|
215
|
+
if (!declared.has(id)) {
|
|
216
|
+
diagnostics.push(buildUndeclaredDiagnostic(ctx.trailId, id, ctx.filePath, ctx.line));
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
/** Emit warning for each declared ID not present in called set. */
|
|
221
|
+
const reportUnused = (declared, called, ctx, diagnostics) => {
|
|
222
|
+
for (const id of declared) {
|
|
223
|
+
if (!called.has(id)) {
|
|
224
|
+
diagnostics.push(buildUnusedDiagnostic(ctx.trailId, id, ctx.filePath, ctx.line));
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
const checkTrailDefinition = (def, filePath, sourceCode, diagnostics) => {
|
|
229
|
+
const declared = extractDeclaredFollows(def.config, sourceCode);
|
|
230
|
+
const called = extractCalledFollows(def.config);
|
|
231
|
+
if (declared.size === 0 && called.size === 0) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
const line = offsetToLine(sourceCode, def.start);
|
|
235
|
+
const ctx = { filePath, line, trailId: def.id };
|
|
236
|
+
reportUndeclared(called, declared, ctx, diagnostics);
|
|
237
|
+
reportUnused(declared, called, ctx, diagnostics);
|
|
238
|
+
};
|
|
239
|
+
// ---------------------------------------------------------------------------
|
|
240
|
+
// Rule
|
|
241
|
+
// ---------------------------------------------------------------------------
|
|
242
|
+
/**
|
|
243
|
+
* Validates that `ctx.follow()` calls align with declared `follow` arrays.
|
|
244
|
+
*/
|
|
245
|
+
export const followDeclarations = {
|
|
246
|
+
check(sourceCode, filePath) {
|
|
247
|
+
if (isTestFile(filePath)) {
|
|
248
|
+
return [];
|
|
249
|
+
}
|
|
250
|
+
const ast = parse(filePath, sourceCode);
|
|
251
|
+
if (!ast) {
|
|
252
|
+
return [];
|
|
253
|
+
}
|
|
254
|
+
const diagnostics = [];
|
|
255
|
+
for (const def of findTrailDefinitions(ast)) {
|
|
256
|
+
checkTrailDefinition(def, filePath, sourceCode, diagnostics);
|
|
257
|
+
}
|
|
258
|
+
return diagnostics;
|
|
259
|
+
},
|
|
260
|
+
description: 'Ensure ctx.follow() calls match the declared follow array in trail definitions.',
|
|
261
|
+
name: 'follow-declarations',
|
|
262
|
+
severity: 'error',
|
|
263
|
+
};
|
|
264
|
+
//# sourceMappingURL=follow-declarations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"follow-declarations.js","sourceRoot":"","sources":["../../src/rules/follow-declarations.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Finds implementations that return raw values instead of `Result`.
|
|
3
3
|
*
|
|
4
|
-
* Uses AST parsing to find `
|
|
4
|
+
* Uses AST parsing to find `run:` bodies and check that
|
|
5
5
|
* every return statement returns Result.ok(), Result.err(), ctx.follow(),
|
|
6
6
|
* or a tracked Result-typed variable.
|
|
7
7
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"implementation-returns-result.d.ts","sourceRoot":"","sources":["../../src/rules/implementation-returns-result.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAUH,OAAO,KAAK,EAAoB,UAAU,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"implementation-returns-result.d.ts","sourceRoot":"","sources":["../../src/rules/implementation-returns-result.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAUH,OAAO,KAAK,EAAoB,UAAU,EAAE,MAAM,YAAY,CAAC;AA2W/D;;GAEG;AACH,eAAO,MAAM,2BAA2B,EAAE,UAiBzC,CAAC"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Finds implementations that return raw values instead of `Result`.
|
|
3
3
|
*
|
|
4
|
-
* Uses AST parsing to find `
|
|
4
|
+
* Uses AST parsing to find `run:` bodies and check that
|
|
5
5
|
* every return statement returns Result.ok(), Result.err(), ctx.follow(),
|
|
6
6
|
* or a tracked Result-typed variable.
|
|
7
7
|
*/
|
|
8
|
-
import {
|
|
8
|
+
import { findRunBodies, findTrailDefinitions, offsetToLine, parse, walk, } from './ast.js';
|
|
9
9
|
import { isTestFile } from './scan.js';
|
|
10
10
|
// ---------------------------------------------------------------------------
|
|
11
11
|
// Member expression helpers
|
|
@@ -35,7 +35,7 @@ const isResultMemberCall = (callee) => {
|
|
|
35
35
|
if (objName === 'ctx' && propName === 'follow') {
|
|
36
36
|
return true;
|
|
37
37
|
}
|
|
38
|
-
return propName === '
|
|
38
|
+
return propName === 'run';
|
|
39
39
|
};
|
|
40
40
|
// ---------------------------------------------------------------------------
|
|
41
41
|
// Expression classification
|
|
@@ -109,12 +109,58 @@ const trackResultVariable = (node, resultVars) => {
|
|
|
109
109
|
}
|
|
110
110
|
};
|
|
111
111
|
// ---------------------------------------------------------------------------
|
|
112
|
+
// Shallow walk (stops at nested function boundaries)
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
const FUNCTION_BOUNDARY_TYPES = new Set([
|
|
115
|
+
'ArrowFunctionExpression',
|
|
116
|
+
'FunctionExpression',
|
|
117
|
+
'FunctionDeclaration',
|
|
118
|
+
]);
|
|
119
|
+
/** Check if a value is a function-boundary AST node that should not be recursed into. */
|
|
120
|
+
const isFunctionBoundary = (val) => !!val &&
|
|
121
|
+
typeof val === 'object' &&
|
|
122
|
+
FUNCTION_BOUNDARY_TYPES.has(val.type);
|
|
123
|
+
/** Recurse into a single AST property value, skipping function boundaries. */
|
|
124
|
+
const visitValue = (val, visit, recurse) => {
|
|
125
|
+
if (Array.isArray(val)) {
|
|
126
|
+
for (const item of val) {
|
|
127
|
+
if (!isFunctionBoundary(item)) {
|
|
128
|
+
recurse(item, visit);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else if (val &&
|
|
133
|
+
typeof val === 'object' &&
|
|
134
|
+
val.type &&
|
|
135
|
+
!isFunctionBoundary(val)) {
|
|
136
|
+
recurse(val, visit);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
/**
|
|
140
|
+
* Walk an AST node tree without recursing into nested function bodies.
|
|
141
|
+
*
|
|
142
|
+
* This ensures that return statements inside `.map()`, `.filter()`, `.then()`
|
|
143
|
+
* callbacks etc. are not mistakenly checked as implementation-level returns.
|
|
144
|
+
*/
|
|
145
|
+
const walkShallow = (node, visit) => {
|
|
146
|
+
if (!node || typeof node !== 'object') {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const n = node;
|
|
150
|
+
if (n.type) {
|
|
151
|
+
visit(n);
|
|
152
|
+
}
|
|
153
|
+
for (const val of Object.values(n)) {
|
|
154
|
+
visitValue(val, visit, walkShallow);
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
// ---------------------------------------------------------------------------
|
|
112
158
|
// Return statement checking
|
|
113
159
|
// ---------------------------------------------------------------------------
|
|
114
160
|
/** Check return statements in a block body for non-Result returns. */
|
|
115
161
|
const checkReturnStatements = (blockBody, trailInfo, filePath, sourceCode, helperNames, diagnostics) => {
|
|
116
162
|
const resultVars = new Set();
|
|
117
|
-
|
|
163
|
+
walkShallow(blockBody, (node) => {
|
|
118
164
|
if (node.type === 'VariableDeclarator') {
|
|
119
165
|
trackResultVariable(node, resultVars);
|
|
120
166
|
}
|
|
@@ -203,8 +249,8 @@ const checkAllDefinitions = (ast, filePath, sourceCode) => {
|
|
|
203
249
|
const diagnostics = [];
|
|
204
250
|
const helperNames = collectResultHelperNames(ast, sourceCode);
|
|
205
251
|
for (const def of findTrailDefinitions(ast)) {
|
|
206
|
-
const info = { id: def.id, label:
|
|
207
|
-
for (const implValue of
|
|
252
|
+
const info = { id: def.id, label: 'Trail' };
|
|
253
|
+
for (const implValue of findRunBodies(def.config)) {
|
|
208
254
|
checkImplementation(implValue, info, filePath, sourceCode, helperNames, diagnostics);
|
|
209
255
|
}
|
|
210
256
|
}
|