@harness-engineering/eslint-plugin 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +16 -0
- package/dist/index.js +354 -234
- package/package.json +14 -12
package/dist/index.d.ts
CHANGED
|
@@ -22,6 +22,9 @@ declare const rules: {
|
|
|
22
22
|
'no-nested-loops-in-critical': _typescript_eslint_utils_ts_eslint.RuleModule<"nestedLoopInCritical", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
23
23
|
name: string;
|
|
24
24
|
};
|
|
25
|
+
'no-process-env-in-spawn': _typescript_eslint_utils_ts_eslint.RuleModule<"processEnvInSpawn", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
26
|
+
name: string;
|
|
27
|
+
};
|
|
25
28
|
'no-sync-io-in-async': _typescript_eslint_utils_ts_eslint.RuleModule<"syncIoInAsync", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
26
29
|
name: string;
|
|
27
30
|
};
|
|
@@ -66,6 +69,9 @@ declare const plugin: {
|
|
|
66
69
|
'no-nested-loops-in-critical': _typescript_eslint_utils_ts_eslint.RuleModule<"nestedLoopInCritical", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
67
70
|
name: string;
|
|
68
71
|
};
|
|
72
|
+
'no-process-env-in-spawn': _typescript_eslint_utils_ts_eslint.RuleModule<"processEnvInSpawn", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
73
|
+
name: string;
|
|
74
|
+
};
|
|
69
75
|
'no-sync-io-in-async': _typescript_eslint_utils_ts_eslint.RuleModule<"syncIoInAsync", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
70
76
|
name: string;
|
|
71
77
|
};
|
|
@@ -95,6 +101,7 @@ declare const plugin: {
|
|
|
95
101
|
'@harness-engineering/enforce-doc-exports': string;
|
|
96
102
|
'@harness-engineering/no-unix-shell-command': string;
|
|
97
103
|
'@harness-engineering/no-hardcoded-path-separator': string;
|
|
104
|
+
'@harness-engineering/no-process-env-in-spawn': string;
|
|
98
105
|
'@harness-engineering/require-path-normalization': string;
|
|
99
106
|
};
|
|
100
107
|
};
|
|
@@ -110,6 +117,7 @@ declare const plugin: {
|
|
|
110
117
|
'@harness-engineering/enforce-doc-exports': string;
|
|
111
118
|
'@harness-engineering/no-unix-shell-command': string;
|
|
112
119
|
'@harness-engineering/no-hardcoded-path-separator': string;
|
|
120
|
+
'@harness-engineering/no-process-env-in-spawn': string;
|
|
113
121
|
'@harness-engineering/require-path-normalization': string;
|
|
114
122
|
};
|
|
115
123
|
};
|
|
@@ -146,6 +154,9 @@ declare const configs: {
|
|
|
146
154
|
'no-nested-loops-in-critical': _typescript_eslint_utils_ts_eslint.RuleModule<"nestedLoopInCritical", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
147
155
|
name: string;
|
|
148
156
|
};
|
|
157
|
+
'no-process-env-in-spawn': _typescript_eslint_utils_ts_eslint.RuleModule<"processEnvInSpawn", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
158
|
+
name: string;
|
|
159
|
+
};
|
|
149
160
|
'no-sync-io-in-async': _typescript_eslint_utils_ts_eslint.RuleModule<"syncIoInAsync", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
150
161
|
name: string;
|
|
151
162
|
};
|
|
@@ -173,6 +184,7 @@ declare const configs: {
|
|
|
173
184
|
'@harness-engineering/enforce-doc-exports': string;
|
|
174
185
|
'@harness-engineering/no-unix-shell-command': string;
|
|
175
186
|
'@harness-engineering/no-hardcoded-path-separator': string;
|
|
187
|
+
'@harness-engineering/no-process-env-in-spawn': string;
|
|
176
188
|
'@harness-engineering/require-path-normalization': string;
|
|
177
189
|
};
|
|
178
190
|
};
|
|
@@ -205,6 +217,9 @@ declare const configs: {
|
|
|
205
217
|
'no-nested-loops-in-critical': _typescript_eslint_utils_ts_eslint.RuleModule<"nestedLoopInCritical", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
206
218
|
name: string;
|
|
207
219
|
};
|
|
220
|
+
'no-process-env-in-spawn': _typescript_eslint_utils_ts_eslint.RuleModule<"processEnvInSpawn", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
221
|
+
name: string;
|
|
222
|
+
};
|
|
208
223
|
'no-sync-io-in-async': _typescript_eslint_utils_ts_eslint.RuleModule<"syncIoInAsync", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
209
224
|
name: string;
|
|
210
225
|
};
|
|
@@ -232,6 +247,7 @@ declare const configs: {
|
|
|
232
247
|
'@harness-engineering/enforce-doc-exports': string;
|
|
233
248
|
'@harness-engineering/no-unix-shell-command': string;
|
|
234
249
|
'@harness-engineering/no-hardcoded-path-separator': string;
|
|
250
|
+
'@harness-engineering/no-process-env-in-spawn': string;
|
|
235
251
|
'@harness-engineering/require-path-normalization': string;
|
|
236
252
|
};
|
|
237
253
|
};
|
package/dist/index.js
CHANGED
|
@@ -3,25 +3,25 @@ import { ESLintUtils, AST_NODE_TYPES as AST_NODE_TYPES2 } from "@typescript-esli
|
|
|
3
3
|
|
|
4
4
|
// src/utils/ast-helpers.ts
|
|
5
5
|
import { AST_NODE_TYPES } from "@typescript-eslint/utils";
|
|
6
|
-
function
|
|
7
|
-
if (!node.range) return false;
|
|
8
|
-
const textBefore = sourceCode.slice(0, node.range[0]);
|
|
9
|
-
const lines = textBefore.split("\n");
|
|
10
|
-
let foundJSDoc = false;
|
|
6
|
+
function lastNonEmptyLineEndsBlockComment(lines) {
|
|
11
7
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
12
8
|
const line = lines[i]?.trim() ?? "";
|
|
13
9
|
if (line === "") continue;
|
|
14
|
-
|
|
15
|
-
const startIdx = textBefore.lastIndexOf("/**");
|
|
16
|
-
const endIdx = textBefore.lastIndexOf("*/");
|
|
17
|
-
if (startIdx !== -1 && endIdx > startIdx) {
|
|
18
|
-
foundJSDoc = true;
|
|
19
|
-
}
|
|
20
|
-
break;
|
|
21
|
-
}
|
|
22
|
-
break;
|
|
10
|
+
return line.endsWith("*/");
|
|
23
11
|
}
|
|
24
|
-
return
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
function textContainsJSDoc(text) {
|
|
15
|
+
const startIdx = text.lastIndexOf("/**");
|
|
16
|
+
const endIdx = text.lastIndexOf("*/");
|
|
17
|
+
return startIdx !== -1 && endIdx > startIdx;
|
|
18
|
+
}
|
|
19
|
+
function hasJSDocComment(node, sourceCode) {
|
|
20
|
+
if (!node.range) return false;
|
|
21
|
+
const textBefore = sourceCode.slice(0, node.range[0]);
|
|
22
|
+
const lines = textBefore.split("\n");
|
|
23
|
+
if (!lastNonEmptyLineEndsBlockComment(lines)) return false;
|
|
24
|
+
return textContainsJSDoc(textBefore);
|
|
25
25
|
}
|
|
26
26
|
var SKIP_KEYS = /* @__PURE__ */ new Set(["parent", "loc", "range", "tokens", "comments"]);
|
|
27
27
|
var ZOD_PARSE_METHODS = /* @__PURE__ */ new Set(["parse", "safeParse"]);
|
|
@@ -33,17 +33,20 @@ function isZodParseCall(node) {
|
|
|
33
33
|
if (prop.type !== AST_NODE_TYPES.Identifier) return false;
|
|
34
34
|
return ZOD_PARSE_METHODS.has(prop.name);
|
|
35
35
|
}
|
|
36
|
+
function visitArrayItems(items, visitor) {
|
|
37
|
+
for (const item of items) {
|
|
38
|
+
if (item && typeof item === "object" && "type" in item) {
|
|
39
|
+
visitor(item);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
36
43
|
function visitChildren(node, visitor) {
|
|
37
44
|
for (const key of Object.keys(node)) {
|
|
38
45
|
if (SKIP_KEYS.has(key)) continue;
|
|
39
46
|
const value = node[key];
|
|
40
47
|
if (!value || typeof value !== "object") continue;
|
|
41
48
|
if (Array.isArray(value)) {
|
|
42
|
-
|
|
43
|
-
if (item && typeof item === "object" && "type" in item) {
|
|
44
|
-
visitor(item);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
49
|
+
visitArrayItems(value, visitor);
|
|
47
50
|
} else if ("type" in value) {
|
|
48
51
|
visitor(value);
|
|
49
52
|
}
|
|
@@ -77,6 +80,43 @@ function isMarkedInternal(node, sourceCode) {
|
|
|
77
80
|
var createRule = ESLintUtils.RuleCreator(
|
|
78
81
|
(name) => `https://github.com/harness-engineering/eslint-plugin/blob/main/docs/rules/${name}.md`
|
|
79
82
|
);
|
|
83
|
+
function getVariableExports(decl) {
|
|
84
|
+
return decl.declarations.filter((d) => d.id.type === AST_NODE_TYPES2.Identifier).map((d) => ({ kind: "variable", name: d.id.name }));
|
|
85
|
+
}
|
|
86
|
+
function getExportInfo(decl, ignoreTypes) {
|
|
87
|
+
const declType = decl.type;
|
|
88
|
+
if (declType === AST_NODE_TYPES2.FunctionDeclaration) {
|
|
89
|
+
const fn = decl;
|
|
90
|
+
return fn.id ? [{ kind: "function", name: fn.id.name }] : [];
|
|
91
|
+
}
|
|
92
|
+
if (declType === AST_NODE_TYPES2.ClassDeclaration) {
|
|
93
|
+
const cls = decl;
|
|
94
|
+
return cls.id ? [{ kind: "class", name: cls.id.name }] : [];
|
|
95
|
+
}
|
|
96
|
+
if (declType === AST_NODE_TYPES2.VariableDeclaration) {
|
|
97
|
+
return getVariableExports(decl);
|
|
98
|
+
}
|
|
99
|
+
if (declType === AST_NODE_TYPES2.TSTypeAliasDeclaration && !ignoreTypes) {
|
|
100
|
+
return [{ kind: "type", name: decl.id.name }];
|
|
101
|
+
}
|
|
102
|
+
if (declType === AST_NODE_TYPES2.TSInterfaceDeclaration && !ignoreTypes) {
|
|
103
|
+
return [{ kind: "interface", name: decl.id.name }];
|
|
104
|
+
}
|
|
105
|
+
return [];
|
|
106
|
+
}
|
|
107
|
+
function checkExportNode(node, kind, name, sourceCode, ignoreInternal, report) {
|
|
108
|
+
if (ignoreInternal && isMarkedInternal(node, sourceCode)) return;
|
|
109
|
+
if (!hasJSDocComment(node, sourceCode)) {
|
|
110
|
+
report({ node, messageId: "missingJSDoc", data: { kind, name } });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function handleExportNamedDeclaration(node, ignoreTypes, sourceCode, ignoreInternal, report) {
|
|
114
|
+
const decl = node.declaration;
|
|
115
|
+
if (!decl) return;
|
|
116
|
+
for (const info of getExportInfo(decl, ignoreTypes)) {
|
|
117
|
+
checkExportNode(node, info.kind, info.name, sourceCode, ignoreInternal, report);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
80
120
|
var enforce_doc_exports_default = createRule({
|
|
81
121
|
name: "enforce-doc-exports",
|
|
82
122
|
meta: {
|
|
@@ -101,47 +141,12 @@ var enforce_doc_exports_default = createRule({
|
|
|
101
141
|
defaultOptions: [{ ignoreTypes: false, ignoreInternal: true }],
|
|
102
142
|
create(context, [options]) {
|
|
103
143
|
const sourceCode = context.sourceCode.getText();
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
if (!hasJSDocComment(node, sourceCode)) {
|
|
109
|
-
context.report({
|
|
110
|
-
node,
|
|
111
|
-
messageId: "missingJSDoc",
|
|
112
|
-
data: { kind, name }
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
function getExportInfo(decl) {
|
|
117
|
-
const declType = decl.type;
|
|
118
|
-
if (declType === AST_NODE_TYPES2.FunctionDeclaration) {
|
|
119
|
-
const fn = decl;
|
|
120
|
-
return fn.id ? [{ kind: "function", name: fn.id.name }] : [];
|
|
121
|
-
}
|
|
122
|
-
if (declType === AST_NODE_TYPES2.ClassDeclaration) {
|
|
123
|
-
const cls = decl;
|
|
124
|
-
return cls.id ? [{ kind: "class", name: cls.id.name }] : [];
|
|
125
|
-
}
|
|
126
|
-
if (declType === AST_NODE_TYPES2.VariableDeclaration) {
|
|
127
|
-
const varDecl = decl;
|
|
128
|
-
return varDecl.declarations.filter((d) => d.id.type === AST_NODE_TYPES2.Identifier).map((d) => ({ kind: "variable", name: d.id.name }));
|
|
129
|
-
}
|
|
130
|
-
if (declType === AST_NODE_TYPES2.TSTypeAliasDeclaration && !options.ignoreTypes) {
|
|
131
|
-
return [{ kind: "type", name: decl.id.name }];
|
|
132
|
-
}
|
|
133
|
-
if (declType === AST_NODE_TYPES2.TSInterfaceDeclaration && !options.ignoreTypes) {
|
|
134
|
-
return [{ kind: "interface", name: decl.id.name }];
|
|
135
|
-
}
|
|
136
|
-
return [];
|
|
137
|
-
}
|
|
144
|
+
const ignoreTypes = options.ignoreTypes ?? false;
|
|
145
|
+
const ignoreInternal = options.ignoreInternal ?? true;
|
|
146
|
+
const report = context.report.bind(context);
|
|
138
147
|
return {
|
|
139
148
|
ExportNamedDeclaration(node) {
|
|
140
|
-
|
|
141
|
-
if (!decl) return;
|
|
142
|
-
for (const info of getExportInfo(decl)) {
|
|
143
|
-
checkExport(node, info.kind, info.name);
|
|
144
|
-
}
|
|
149
|
+
handleExportNamedDeclaration(node, ignoreTypes, sourceCode, ignoreInternal, report);
|
|
145
150
|
}
|
|
146
151
|
};
|
|
147
152
|
}
|
|
@@ -196,6 +201,24 @@ function normalizePath(filePath) {
|
|
|
196
201
|
}
|
|
197
202
|
return path.basename(filePath);
|
|
198
203
|
}
|
|
204
|
+
function checkImportDeclaration(node, context, currentFile) {
|
|
205
|
+
const importPath = node.source.value;
|
|
206
|
+
if (!importPath.startsWith(".")) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
const importingDir = path.dirname(context.filename);
|
|
210
|
+
const resolvedPath = path.resolve(importingDir, importPath);
|
|
211
|
+
const normalizedImport = normalizePath(resolvedPath);
|
|
212
|
+
const cycle = detectCycle(currentFile, normalizedImport);
|
|
213
|
+
if (cycle) {
|
|
214
|
+
context.report({
|
|
215
|
+
node,
|
|
216
|
+
messageId: "circularDep",
|
|
217
|
+
data: { cycle: cycle.map((f) => path.basename(f)).join(" \u2192 ") }
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
addEdge(currentFile, normalizedImport);
|
|
221
|
+
}
|
|
199
222
|
var no_circular_deps_default = createRule2({
|
|
200
223
|
name: "no-circular-deps",
|
|
201
224
|
meta: {
|
|
@@ -213,24 +236,7 @@ var no_circular_deps_default = createRule2({
|
|
|
213
236
|
const currentFile = normalizePath(context.filename);
|
|
214
237
|
return {
|
|
215
238
|
ImportDeclaration(node) {
|
|
216
|
-
|
|
217
|
-
if (!importPath.startsWith(".")) {
|
|
218
|
-
return;
|
|
219
|
-
}
|
|
220
|
-
const importingDir = path.dirname(context.filename);
|
|
221
|
-
const resolvedPath = path.resolve(importingDir, importPath);
|
|
222
|
-
const normalizedImport = normalizePath(resolvedPath);
|
|
223
|
-
const cycle = detectCycle(currentFile, normalizedImport);
|
|
224
|
-
if (cycle) {
|
|
225
|
-
context.report({
|
|
226
|
-
node,
|
|
227
|
-
messageId: "circularDep",
|
|
228
|
-
data: {
|
|
229
|
-
cycle: cycle.map((f) => path.basename(f)).join(" \u2192 ")
|
|
230
|
-
}
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
addEdge(currentFile, normalizedImport);
|
|
239
|
+
checkImportDeclaration(node, context, currentFile);
|
|
234
240
|
}
|
|
235
241
|
};
|
|
236
242
|
}
|
|
@@ -348,6 +354,34 @@ function normalizePath2(filePath) {
|
|
|
348
354
|
var createRule3 = ESLintUtils3.RuleCreator(
|
|
349
355
|
(name) => `https://github.com/harness-engineering/eslint-plugin/blob/main/docs/rules/${name}.md`
|
|
350
356
|
);
|
|
357
|
+
function importMatchesDisallowed(importPath, resolvedImport, disallowed) {
|
|
358
|
+
return importPath === disallowed || matchesPattern(resolvedImport, disallowed) || matchesPattern(importPath, disallowed);
|
|
359
|
+
}
|
|
360
|
+
function findViolatedRule(applicableRules, importPath, resolvedImport) {
|
|
361
|
+
for (const rule of applicableRules) {
|
|
362
|
+
for (const disallowed of rule.disallow) {
|
|
363
|
+
if (importMatchesDisallowed(importPath, resolvedImport, disallowed)) {
|
|
364
|
+
return rule;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return void 0;
|
|
369
|
+
}
|
|
370
|
+
function buildForbiddenMessage(rule, importPath) {
|
|
371
|
+
return rule.message ?? `Import "${importPath}" is forbidden in files matching "${rule.from}"`;
|
|
372
|
+
}
|
|
373
|
+
function checkImportDeclaration2(node, applicableRules, filename, report) {
|
|
374
|
+
const importPath = node.source.value;
|
|
375
|
+
const resolvedImport = resolveImportPath(importPath, filename);
|
|
376
|
+
const violatedRule = findViolatedRule(applicableRules, importPath, resolvedImport);
|
|
377
|
+
if (violatedRule) {
|
|
378
|
+
report({
|
|
379
|
+
node,
|
|
380
|
+
messageId: "forbiddenImport",
|
|
381
|
+
data: { message: buildForbiddenMessage(violatedRule, importPath) }
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
}
|
|
351
385
|
var no_forbidden_imports_default = createRule3({
|
|
352
386
|
name: "no-forbidden-imports",
|
|
353
387
|
meta: {
|
|
@@ -373,25 +407,10 @@ var no_forbidden_imports_default = createRule3({
|
|
|
373
407
|
if (applicableRules.length === 0) {
|
|
374
408
|
return {};
|
|
375
409
|
}
|
|
410
|
+
const report = context.report.bind(context);
|
|
376
411
|
return {
|
|
377
412
|
ImportDeclaration(node) {
|
|
378
|
-
|
|
379
|
-
const resolvedImport = resolveImportPath(importPath, context.filename);
|
|
380
|
-
for (const rule of applicableRules) {
|
|
381
|
-
for (const disallowed of rule.disallow) {
|
|
382
|
-
const isMatch = importPath === disallowed || matchesPattern(resolvedImport, disallowed) || matchesPattern(importPath, disallowed);
|
|
383
|
-
if (isMatch) {
|
|
384
|
-
context.report({
|
|
385
|
-
node,
|
|
386
|
-
messageId: "forbiddenImport",
|
|
387
|
-
data: {
|
|
388
|
-
message: rule.message || `Import "${importPath}" is forbidden in files matching "${rule.from}"`
|
|
389
|
-
}
|
|
390
|
-
});
|
|
391
|
-
return;
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
}
|
|
413
|
+
checkImportDeclaration2(node, applicableRules, context.filename, report);
|
|
395
414
|
}
|
|
396
415
|
};
|
|
397
416
|
}
|
|
@@ -402,6 +421,25 @@ import { ESLintUtils as ESLintUtils4 } from "@typescript-eslint/utils";
|
|
|
402
421
|
var createRule4 = ESLintUtils4.RuleCreator(
|
|
403
422
|
(name) => `https://github.com/harness-engineering/eslint-plugin/blob/main/docs/rules/${name}.md`
|
|
404
423
|
);
|
|
424
|
+
function isLayerViolation(importLayer, currentLayer, currentLayerDef) {
|
|
425
|
+
return importLayer !== currentLayer && !currentLayerDef.allowedDependencies.includes(importLayer);
|
|
426
|
+
}
|
|
427
|
+
function resolveImportLayer(importPath, filename, layers) {
|
|
428
|
+
if (!importPath.startsWith(".")) return null;
|
|
429
|
+
const resolvedImport = resolveImportPath(importPath, filename);
|
|
430
|
+
return getLayerForFile(resolvedImport, layers) ?? null;
|
|
431
|
+
}
|
|
432
|
+
function checkLayerImport(node, context, currentLayer, currentLayerDef, layers) {
|
|
433
|
+
const importLayer = resolveImportLayer(node.source.value, context.filename, layers);
|
|
434
|
+
if (!importLayer) return;
|
|
435
|
+
if (isLayerViolation(importLayer, currentLayer, currentLayerDef)) {
|
|
436
|
+
context.report({
|
|
437
|
+
node,
|
|
438
|
+
messageId: "layerViolation",
|
|
439
|
+
data: { fromLayer: currentLayer, toLayer: importLayer }
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
}
|
|
405
443
|
var no_layer_violation_default = createRule4({
|
|
406
444
|
name: "no-layer-violation",
|
|
407
445
|
meta: {
|
|
@@ -429,27 +467,10 @@ var no_layer_violation_default = createRule4({
|
|
|
429
467
|
if (!currentLayerDef) {
|
|
430
468
|
return {};
|
|
431
469
|
}
|
|
470
|
+
const layers = config.layers;
|
|
432
471
|
return {
|
|
433
472
|
ImportDeclaration(node) {
|
|
434
|
-
|
|
435
|
-
if (!importPath.startsWith(".")) {
|
|
436
|
-
return;
|
|
437
|
-
}
|
|
438
|
-
const resolvedImport = resolveImportPath(importPath, context.filename);
|
|
439
|
-
const importLayer = getLayerForFile(resolvedImport, config.layers);
|
|
440
|
-
if (!importLayer) {
|
|
441
|
-
return;
|
|
442
|
-
}
|
|
443
|
-
if (importLayer !== currentLayer && !currentLayerDef.allowedDependencies.includes(importLayer)) {
|
|
444
|
-
context.report({
|
|
445
|
-
node,
|
|
446
|
-
messageId: "layerViolation",
|
|
447
|
-
data: {
|
|
448
|
-
fromLayer: currentLayer,
|
|
449
|
-
toLayer: importLayer
|
|
450
|
-
}
|
|
451
|
-
});
|
|
452
|
-
}
|
|
473
|
+
checkLayerImport(node, context, currentLayer, currentLayerDef, layers);
|
|
453
474
|
}
|
|
454
475
|
};
|
|
455
476
|
}
|
|
@@ -460,6 +481,49 @@ import { ESLintUtils as ESLintUtils5 } from "@typescript-eslint/utils";
|
|
|
460
481
|
var createRule5 = ESLintUtils5.RuleCreator(
|
|
461
482
|
(name) => `https://github.com/harness-engineering/eslint-plugin/blob/main/docs/rules/${name}.md`
|
|
462
483
|
);
|
|
484
|
+
function getAnnotationTarget(node) {
|
|
485
|
+
const parentType = node.parent?.type;
|
|
486
|
+
if (parentType === "ExportNamedDeclaration" || parentType === "VariableDeclaration") {
|
|
487
|
+
return node.parent;
|
|
488
|
+
}
|
|
489
|
+
return node;
|
|
490
|
+
}
|
|
491
|
+
function hasCriticalAnnotation(node, sourceText) {
|
|
492
|
+
const target = getAnnotationTarget(node);
|
|
493
|
+
const startLine = target.loc.start.line;
|
|
494
|
+
const lines = sourceText.split("\n");
|
|
495
|
+
for (let i = Math.max(0, startLine - 2); i < startLine; i++) {
|
|
496
|
+
if (lines[i]?.includes("@perf-critical")) return true;
|
|
497
|
+
}
|
|
498
|
+
return false;
|
|
499
|
+
}
|
|
500
|
+
function makeEnterFunction(criticalStack, loopDepthRef, sourceText) {
|
|
501
|
+
return function enterFunction(node) {
|
|
502
|
+
criticalStack.push(hasCriticalAnnotation(node, sourceText));
|
|
503
|
+
loopDepthRef.value = 0;
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
function makeExitFunction(criticalStack, loopDepthRef) {
|
|
507
|
+
return function exitFunction() {
|
|
508
|
+
criticalStack.pop();
|
|
509
|
+
loopDepthRef.value = 0;
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
function makeEnterLoop(criticalStack, loopDepthRef, reportFn) {
|
|
513
|
+
return function enterLoop(node) {
|
|
514
|
+
if (criticalStack.length === 0 || criticalStack[criticalStack.length - 1] !== true) return;
|
|
515
|
+
loopDepthRef.value++;
|
|
516
|
+
if (loopDepthRef.value > 1) {
|
|
517
|
+
reportFn(node);
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
function makeExitLoop(criticalStack, loopDepthRef) {
|
|
522
|
+
return function exitLoop() {
|
|
523
|
+
if (criticalStack.length === 0 || criticalStack[criticalStack.length - 1] !== true) return;
|
|
524
|
+
loopDepthRef.value--;
|
|
525
|
+
};
|
|
526
|
+
}
|
|
463
527
|
var no_nested_loops_in_critical_default = createRule5({
|
|
464
528
|
name: "no-nested-loops-in-critical",
|
|
465
529
|
meta: {
|
|
@@ -479,38 +543,14 @@ var no_nested_loops_in_critical_default = createRule5({
|
|
|
479
543
|
return {};
|
|
480
544
|
}
|
|
481
545
|
const criticalStack = [];
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
for (let i = Math.max(0, startLine - 2); i < startLine; i++) {
|
|
491
|
-
if (lines[i]?.includes("@perf-critical")) return true;
|
|
492
|
-
}
|
|
493
|
-
return false;
|
|
494
|
-
}
|
|
495
|
-
function enterFunction(node) {
|
|
496
|
-
criticalStack.push(hasCriticalAnnotation(node));
|
|
497
|
-
loopDepth = 0;
|
|
498
|
-
}
|
|
499
|
-
function exitFunction() {
|
|
500
|
-
criticalStack.pop();
|
|
501
|
-
loopDepth = 0;
|
|
502
|
-
}
|
|
503
|
-
function enterLoop(node) {
|
|
504
|
-
if (!isCritical()) return;
|
|
505
|
-
loopDepth++;
|
|
506
|
-
if (loopDepth > 1) {
|
|
507
|
-
context.report({ node, messageId: "nestedLoopInCritical" });
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
function exitLoop() {
|
|
511
|
-
if (!isCritical()) return;
|
|
512
|
-
loopDepth--;
|
|
513
|
-
}
|
|
546
|
+
const loopDepthRef = { value: 0 };
|
|
547
|
+
const reportNestedLoop = (node) => {
|
|
548
|
+
context.report({ node, messageId: "nestedLoopInCritical" });
|
|
549
|
+
};
|
|
550
|
+
const enterFunction = makeEnterFunction(criticalStack, loopDepthRef, sourceText);
|
|
551
|
+
const exitFunction = makeExitFunction(criticalStack, loopDepthRef);
|
|
552
|
+
const enterLoop = makeEnterLoop(criticalStack, loopDepthRef, reportNestedLoop);
|
|
553
|
+
const exitLoop = makeExitLoop(criticalStack, loopDepthRef);
|
|
514
554
|
return {
|
|
515
555
|
FunctionDeclaration: enterFunction,
|
|
516
556
|
"FunctionDeclaration:exit": exitFunction,
|
|
@@ -549,6 +589,32 @@ var SYNC_FS_METHODS = /* @__PURE__ */ new Set([
|
|
|
549
589
|
"renameSync",
|
|
550
590
|
"accessSync"
|
|
551
591
|
]);
|
|
592
|
+
function getSyncMethodName(node) {
|
|
593
|
+
if (node.callee.type === "Identifier" && SYNC_FS_METHODS.has(node.callee.name)) {
|
|
594
|
+
return node.callee.name;
|
|
595
|
+
}
|
|
596
|
+
if (node.callee.type === "MemberExpression" && node.callee.property.type === "Identifier" && SYNC_FS_METHODS.has(node.callee.property.name)) {
|
|
597
|
+
return node.callee.property.name;
|
|
598
|
+
}
|
|
599
|
+
return void 0;
|
|
600
|
+
}
|
|
601
|
+
function adjustAsyncDepth(node, depthRef, delta) {
|
|
602
|
+
if (node.async) {
|
|
603
|
+
depthRef.value += delta;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
function checkSyncCallInAsync(node, depthRef, reportFn) {
|
|
607
|
+
if (depthRef.value === 0) return;
|
|
608
|
+
const name = getSyncMethodName(node);
|
|
609
|
+
if (name) {
|
|
610
|
+
reportFn(name);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
function reportSyncCallExpression(node, context, asyncDepthRef) {
|
|
614
|
+
checkSyncCallInAsync(node, asyncDepthRef, (name) => {
|
|
615
|
+
context.report({ node, messageId: "syncIoInAsync", data: { name } });
|
|
616
|
+
});
|
|
617
|
+
}
|
|
552
618
|
var no_sync_io_in_async_default = createRule6({
|
|
553
619
|
name: "no-sync-io-in-async",
|
|
554
620
|
meta: {
|
|
@@ -563,17 +629,9 @@ var no_sync_io_in_async_default = createRule6({
|
|
|
563
629
|
},
|
|
564
630
|
defaultOptions: [],
|
|
565
631
|
create(context) {
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
asyncDepth++;
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
function exitFunction(node) {
|
|
573
|
-
if (node.async) {
|
|
574
|
-
asyncDepth--;
|
|
575
|
-
}
|
|
576
|
-
}
|
|
632
|
+
const asyncDepthRef = { value: 0 };
|
|
633
|
+
const enterFunction = (node) => adjustAsyncDepth(node, asyncDepthRef, 1);
|
|
634
|
+
const exitFunction = (node) => adjustAsyncDepth(node, asyncDepthRef, -1);
|
|
577
635
|
return {
|
|
578
636
|
FunctionDeclaration: enterFunction,
|
|
579
637
|
"FunctionDeclaration:exit": exitFunction,
|
|
@@ -582,21 +640,7 @@ var no_sync_io_in_async_default = createRule6({
|
|
|
582
640
|
ArrowFunctionExpression: enterFunction,
|
|
583
641
|
"ArrowFunctionExpression:exit": exitFunction,
|
|
584
642
|
CallExpression(node) {
|
|
585
|
-
|
|
586
|
-
let name;
|
|
587
|
-
if (node.callee.type === "Identifier" && SYNC_FS_METHODS.has(node.callee.name)) {
|
|
588
|
-
name = node.callee.name;
|
|
589
|
-
}
|
|
590
|
-
if (node.callee.type === "MemberExpression" && node.callee.property.type === "Identifier" && SYNC_FS_METHODS.has(node.callee.property.name)) {
|
|
591
|
-
name = node.callee.property.name;
|
|
592
|
-
}
|
|
593
|
-
if (name) {
|
|
594
|
-
context.report({
|
|
595
|
-
node,
|
|
596
|
-
messageId: "syncIoInAsync",
|
|
597
|
-
data: { name }
|
|
598
|
-
});
|
|
599
|
-
}
|
|
643
|
+
reportSyncCallExpression(node, context, asyncDepthRef);
|
|
600
644
|
}
|
|
601
645
|
};
|
|
602
646
|
}
|
|
@@ -618,6 +662,28 @@ var ARRAY_METHODS = /* @__PURE__ */ new Set([
|
|
|
618
662
|
"every",
|
|
619
663
|
"forEach"
|
|
620
664
|
]);
|
|
665
|
+
function isArrayMethodCall(node) {
|
|
666
|
+
return node.property.type === "Identifier" && ARRAY_METHODS.has(node.property.name);
|
|
667
|
+
}
|
|
668
|
+
function isInnerChainLink(node) {
|
|
669
|
+
const callExpr = node.parent;
|
|
670
|
+
if (!callExpr) return false;
|
|
671
|
+
const parentMember = callExpr.parent;
|
|
672
|
+
return parentMember !== null && parentMember !== void 0 && parentMember.type === "MemberExpression" && parentMember.property.type === "Identifier" && ARRAY_METHODS.has(parentMember.property.name);
|
|
673
|
+
}
|
|
674
|
+
function countChainLength(node) {
|
|
675
|
+
let length = 1;
|
|
676
|
+
let current = node;
|
|
677
|
+
while (true) {
|
|
678
|
+
const obj = current.object;
|
|
679
|
+
if (obj.type !== "CallExpression" || obj.callee.type !== "MemberExpression" || obj.callee.property.type !== "Identifier" || !ARRAY_METHODS.has(obj.callee.property.name)) {
|
|
680
|
+
break;
|
|
681
|
+
}
|
|
682
|
+
length++;
|
|
683
|
+
current = obj.callee;
|
|
684
|
+
}
|
|
685
|
+
return length;
|
|
686
|
+
}
|
|
621
687
|
var no_unbounded_array_chains_default = createRule7({
|
|
622
688
|
name: "no-unbounded-array-chains",
|
|
623
689
|
meta: {
|
|
@@ -634,30 +700,11 @@ var no_unbounded_array_chains_default = createRule7({
|
|
|
634
700
|
create(context) {
|
|
635
701
|
return {
|
|
636
702
|
"CallExpression > MemberExpression.callee"(node) {
|
|
637
|
-
if (
|
|
638
|
-
|
|
639
|
-
}
|
|
703
|
+
if (!isArrayMethodCall(node)) return;
|
|
704
|
+
if (isInnerChainLink(node)) return;
|
|
640
705
|
const callExpr = node.parent;
|
|
641
|
-
if (
|
|
642
|
-
|
|
643
|
-
}
|
|
644
|
-
let chainLength = 1;
|
|
645
|
-
let current = node;
|
|
646
|
-
while (true) {
|
|
647
|
-
const memberExpr = current;
|
|
648
|
-
const obj = memberExpr.object;
|
|
649
|
-
if (obj.type === "CallExpression" && obj.callee.type === "MemberExpression" && obj.callee.property.type === "Identifier" && ARRAY_METHODS.has(obj.callee.property.name)) {
|
|
650
|
-
chainLength++;
|
|
651
|
-
current = obj.callee;
|
|
652
|
-
} else {
|
|
653
|
-
break;
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
if (chainLength >= 3) {
|
|
657
|
-
context.report({
|
|
658
|
-
node: callExpr,
|
|
659
|
-
messageId: "unboundedArrayChain"
|
|
660
|
-
});
|
|
706
|
+
if (countChainLength(node) >= 3) {
|
|
707
|
+
context.report({ node: callExpr, messageId: "unboundedArrayChain" });
|
|
661
708
|
}
|
|
662
709
|
}
|
|
663
710
|
};
|
|
@@ -669,6 +716,25 @@ import { ESLintUtils as ESLintUtils8, AST_NODE_TYPES as AST_NODE_TYPES3 } from "
|
|
|
669
716
|
var createRule8 = ESLintUtils8.RuleCreator(
|
|
670
717
|
(name) => `https://github.com/harness-engineering/eslint-plugin/blob/main/docs/rules/${name}.md`
|
|
671
718
|
);
|
|
719
|
+
function extractFunctionDeclaration(node) {
|
|
720
|
+
const decl = node.declaration;
|
|
721
|
+
if (!decl || decl.type !== AST_NODE_TYPES3.FunctionDeclaration) {
|
|
722
|
+
return null;
|
|
723
|
+
}
|
|
724
|
+
const fn = decl;
|
|
725
|
+
if (!fn.id || !fn.body) return null;
|
|
726
|
+
return fn;
|
|
727
|
+
}
|
|
728
|
+
function isFileInBoundary(filePath, patterns) {
|
|
729
|
+
return patterns.some((pattern) => matchesPattern(filePath, pattern));
|
|
730
|
+
}
|
|
731
|
+
function checkBoundaryExport(node, report) {
|
|
732
|
+
const fn = extractFunctionDeclaration(node);
|
|
733
|
+
if (!fn) return;
|
|
734
|
+
if (!hasZodValidation(fn.body)) {
|
|
735
|
+
report({ node: fn, messageId: "missingSchema", data: { name: fn.id.name } });
|
|
736
|
+
}
|
|
737
|
+
}
|
|
672
738
|
var require_boundary_schema_default = createRule8({
|
|
673
739
|
name: "require-boundary-schema",
|
|
674
740
|
meta: {
|
|
@@ -688,27 +754,13 @@ var require_boundary_schema_default = createRule8({
|
|
|
688
754
|
return {};
|
|
689
755
|
}
|
|
690
756
|
const filePath = normalizePath2(context.filename);
|
|
691
|
-
|
|
692
|
-
(pattern) => matchesPattern(filePath, pattern)
|
|
693
|
-
);
|
|
694
|
-
if (!isBoundaryFile) {
|
|
757
|
+
if (!isFileInBoundary(filePath, config.boundaries.requireSchema)) {
|
|
695
758
|
return {};
|
|
696
759
|
}
|
|
760
|
+
const report = context.report.bind(context);
|
|
697
761
|
return {
|
|
698
762
|
ExportNamedDeclaration(node) {
|
|
699
|
-
|
|
700
|
-
if (!decl || decl.type !== AST_NODE_TYPES3.FunctionDeclaration) {
|
|
701
|
-
return;
|
|
702
|
-
}
|
|
703
|
-
const fn = decl;
|
|
704
|
-
if (!fn.id || !fn.body) return;
|
|
705
|
-
if (!hasZodValidation(fn.body)) {
|
|
706
|
-
context.report({
|
|
707
|
-
node: fn,
|
|
708
|
-
messageId: "missingSchema",
|
|
709
|
-
data: { name: fn.id.name }
|
|
710
|
-
});
|
|
711
|
-
}
|
|
763
|
+
checkBoundaryExport(node, report);
|
|
712
764
|
}
|
|
713
765
|
};
|
|
714
766
|
}
|
|
@@ -724,6 +776,28 @@ var UNIX_COMMAND_PATTERN = new RegExp(
|
|
|
724
776
|
`(?:^|[;&|]\\s*)(?:/(?:usr/)?(?:bin|sbin)/)?(?:${UNIX_COMMANDS.join("|")})(?:\\s|$)`
|
|
725
777
|
);
|
|
726
778
|
var FLAGGED_FUNCTIONS = /* @__PURE__ */ new Set(["exec", "execSync"]);
|
|
779
|
+
function getFlaggedFunctionName(node) {
|
|
780
|
+
if (node.callee.type === "Identifier" && FLAGGED_FUNCTIONS.has(node.callee.name)) {
|
|
781
|
+
return node.callee.name;
|
|
782
|
+
}
|
|
783
|
+
if (node.callee.type === "MemberExpression" && node.callee.property.type === "Identifier" && FLAGGED_FUNCTIONS.has(node.callee.property.name)) {
|
|
784
|
+
return node.callee.property.name;
|
|
785
|
+
}
|
|
786
|
+
return void 0;
|
|
787
|
+
}
|
|
788
|
+
function isFlaggedCall(node) {
|
|
789
|
+
return getFlaggedFunctionName(node) !== void 0;
|
|
790
|
+
}
|
|
791
|
+
function extractCommandString(arg) {
|
|
792
|
+
if (!arg) return void 0;
|
|
793
|
+
if (arg.type === "Literal" && typeof arg.value === "string") {
|
|
794
|
+
return arg.value;
|
|
795
|
+
}
|
|
796
|
+
if (arg.type === "TemplateLiteral" && arg.quasis.length > 0) {
|
|
797
|
+
return arg.quasis.map((q) => q.value.raw).join(" ");
|
|
798
|
+
}
|
|
799
|
+
return void 0;
|
|
800
|
+
}
|
|
727
801
|
var no_unix_shell_command_default = createRule9({
|
|
728
802
|
name: "no-unix-shell-command",
|
|
729
803
|
meta: {
|
|
@@ -740,27 +814,10 @@ var no_unix_shell_command_default = createRule9({
|
|
|
740
814
|
create(context) {
|
|
741
815
|
return {
|
|
742
816
|
CallExpression(node) {
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
functionName = node.callee.name;
|
|
746
|
-
}
|
|
747
|
-
if (node.callee.type === "MemberExpression" && node.callee.property.type === "Identifier" && FLAGGED_FUNCTIONS.has(node.callee.property.name)) {
|
|
748
|
-
functionName = node.callee.property.name;
|
|
749
|
-
}
|
|
750
|
-
if (!functionName) return;
|
|
751
|
-
const firstArg = node.arguments[0];
|
|
752
|
-
if (!firstArg) return;
|
|
753
|
-
let commandString;
|
|
754
|
-
if (firstArg.type === "Literal" && typeof firstArg.value === "string") {
|
|
755
|
-
commandString = firstArg.value;
|
|
756
|
-
} else if (firstArg.type === "TemplateLiteral" && firstArg.quasis.length > 0) {
|
|
757
|
-
commandString = firstArg.quasis.map((q) => q.value.raw).join(" ");
|
|
758
|
-
}
|
|
817
|
+
if (!isFlaggedCall(node)) return;
|
|
818
|
+
const commandString = extractCommandString(node.arguments[0]);
|
|
759
819
|
if (commandString && UNIX_COMMAND_PATTERN.test(commandString)) {
|
|
760
|
-
context.report({
|
|
761
|
-
node,
|
|
762
|
-
messageId: "unixShellCommand"
|
|
763
|
-
});
|
|
820
|
+
context.report({ node, messageId: "unixShellCommand" });
|
|
764
821
|
}
|
|
765
822
|
}
|
|
766
823
|
};
|
|
@@ -878,11 +935,71 @@ var no_hardcoded_path_separator_default = createRule10({
|
|
|
878
935
|
}
|
|
879
936
|
});
|
|
880
937
|
|
|
881
|
-
// src/rules/
|
|
938
|
+
// src/rules/no-process-env-in-spawn.ts
|
|
882
939
|
import { ESLintUtils as ESLintUtils11 } from "@typescript-eslint/utils";
|
|
883
940
|
var createRule11 = ESLintUtils11.RuleCreator(
|
|
884
941
|
(name) => `https://github.com/harness-engineering/eslint-plugin/blob/main/docs/rules/${name}.md`
|
|
885
942
|
);
|
|
943
|
+
var SPAWN_FUNCTIONS = /* @__PURE__ */ new Set(["spawn", "spawnSync", "execFile", "execFileSync", "fork"]);
|
|
944
|
+
function getSpawnFunctionName(node) {
|
|
945
|
+
if (node.callee.type === "Identifier" && SPAWN_FUNCTIONS.has(node.callee.name)) {
|
|
946
|
+
return node.callee.name;
|
|
947
|
+
}
|
|
948
|
+
if (node.callee.type === "MemberExpression" && node.callee.property.type === "Identifier" && SPAWN_FUNCTIONS.has(node.callee.property.name)) {
|
|
949
|
+
return node.callee.property.name;
|
|
950
|
+
}
|
|
951
|
+
return void 0;
|
|
952
|
+
}
|
|
953
|
+
function isProcessEnv(node) {
|
|
954
|
+
return node.type === "MemberExpression" && node.object.type === "Identifier" && node.object.name === "process" && node.property.type === "Identifier" && node.property.name === "env";
|
|
955
|
+
}
|
|
956
|
+
function hasProcessEnvProperty(node) {
|
|
957
|
+
for (const prop of node.properties) {
|
|
958
|
+
if (prop.type === "Property" && prop.key.type === "Identifier" && prop.key.name === "env" && isProcessEnv(prop.value)) {
|
|
959
|
+
return prop;
|
|
960
|
+
}
|
|
961
|
+
if (prop.type === "SpreadElement" && isProcessEnv(prop.argument)) {
|
|
962
|
+
return prop;
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
return void 0;
|
|
966
|
+
}
|
|
967
|
+
var no_process_env_in_spawn_default = createRule11({
|
|
968
|
+
name: "no-process-env-in-spawn",
|
|
969
|
+
meta: {
|
|
970
|
+
type: "problem",
|
|
971
|
+
docs: {
|
|
972
|
+
description: "Disallow passing process.env directly to spawn/execFile/fork, which leaks all server-side secrets to child processes"
|
|
973
|
+
},
|
|
974
|
+
messages: {
|
|
975
|
+
processEnvInSpawn: "Do not pass process.env directly to '{{name}}'. Build an explicit env object with only the variables the child process needs."
|
|
976
|
+
},
|
|
977
|
+
schema: []
|
|
978
|
+
},
|
|
979
|
+
defaultOptions: [],
|
|
980
|
+
create(context) {
|
|
981
|
+
return {
|
|
982
|
+
CallExpression(node) {
|
|
983
|
+
const name = getSpawnFunctionName(node);
|
|
984
|
+
if (!name) return;
|
|
985
|
+
for (const arg of node.arguments) {
|
|
986
|
+
if (arg.type === "ObjectExpression") {
|
|
987
|
+
const envProp = hasProcessEnvProperty(arg);
|
|
988
|
+
if (envProp) {
|
|
989
|
+
context.report({ node: envProp, messageId: "processEnvInSpawn", data: { name } });
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
};
|
|
995
|
+
}
|
|
996
|
+
});
|
|
997
|
+
|
|
998
|
+
// src/rules/require-path-normalization.ts
|
|
999
|
+
import { ESLintUtils as ESLintUtils12 } from "@typescript-eslint/utils";
|
|
1000
|
+
var createRule12 = ESLintUtils12.RuleCreator(
|
|
1001
|
+
(name) => `https://github.com/harness-engineering/eslint-plugin/blob/main/docs/rules/${name}.md`
|
|
1002
|
+
);
|
|
886
1003
|
function isRelativeCall(node) {
|
|
887
1004
|
const callee = node.callee;
|
|
888
1005
|
if (callee.type === "Identifier" && callee.name === "relative") {
|
|
@@ -908,7 +1025,7 @@ function hasNormalization(node) {
|
|
|
908
1025
|
if (grandparent?.type !== "CallExpression" || grandparent.arguments.length < 1) return false;
|
|
909
1026
|
return isBackslashNormalizationArg(grandparent.arguments[0]);
|
|
910
1027
|
}
|
|
911
|
-
var require_path_normalization_default =
|
|
1028
|
+
var require_path_normalization_default = createRule12({
|
|
912
1029
|
name: "require-path-normalization",
|
|
913
1030
|
meta: {
|
|
914
1031
|
type: "problem",
|
|
@@ -943,6 +1060,7 @@ var rules = {
|
|
|
943
1060
|
"no-hardcoded-path-separator": no_hardcoded_path_separator_default,
|
|
944
1061
|
"no-layer-violation": no_layer_violation_default,
|
|
945
1062
|
"no-nested-loops-in-critical": no_nested_loops_in_critical_default,
|
|
1063
|
+
"no-process-env-in-spawn": no_process_env_in_spawn_default,
|
|
946
1064
|
"no-sync-io-in-async": no_sync_io_in_async_default,
|
|
947
1065
|
"no-unbounded-array-chains": no_unbounded_array_chains_default,
|
|
948
1066
|
"no-unix-shell-command": no_unix_shell_command_default,
|
|
@@ -954,7 +1072,7 @@ var rules = {
|
|
|
954
1072
|
var plugin = {
|
|
955
1073
|
meta: {
|
|
956
1074
|
name: "@harness-engineering/eslint-plugin",
|
|
957
|
-
version: "0.
|
|
1075
|
+
version: "0.2.4"
|
|
958
1076
|
},
|
|
959
1077
|
rules,
|
|
960
1078
|
configs: {
|
|
@@ -972,6 +1090,7 @@ var plugin = {
|
|
|
972
1090
|
"@harness-engineering/enforce-doc-exports": "warn",
|
|
973
1091
|
"@harness-engineering/no-unix-shell-command": "warn",
|
|
974
1092
|
"@harness-engineering/no-hardcoded-path-separator": "warn",
|
|
1093
|
+
"@harness-engineering/no-process-env-in-spawn": "error",
|
|
975
1094
|
"@harness-engineering/require-path-normalization": "warn"
|
|
976
1095
|
}
|
|
977
1096
|
},
|
|
@@ -989,6 +1108,7 @@ var plugin = {
|
|
|
989
1108
|
"@harness-engineering/enforce-doc-exports": "error",
|
|
990
1109
|
"@harness-engineering/no-unix-shell-command": "error",
|
|
991
1110
|
"@harness-engineering/no-hardcoded-path-separator": "error",
|
|
1111
|
+
"@harness-engineering/no-process-env-in-spawn": "error",
|
|
992
1112
|
"@harness-engineering/require-path-normalization": "error"
|
|
993
1113
|
}
|
|
994
1114
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@harness-engineering/eslint-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "ESLint plugin for harness engineering architectural constraints",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -16,18 +16,19 @@
|
|
|
16
16
|
"README.md"
|
|
17
17
|
],
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@typescript-eslint/utils": "^8.
|
|
20
|
-
"minimatch": "^10.2.
|
|
21
|
-
"zod": "^3.
|
|
19
|
+
"@typescript-eslint/utils": "^8.59.0",
|
|
20
|
+
"minimatch": "^10.2.5",
|
|
21
|
+
"zod": "^3.25.76"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@types/node": "^22.
|
|
25
|
-
"@typescript-eslint/parser": "^8.
|
|
26
|
-
"@typescript-eslint/rule-tester": "^8.
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
24
|
+
"@types/node": "^22.19.15",
|
|
25
|
+
"@typescript-eslint/parser": "^8.59.0",
|
|
26
|
+
"@typescript-eslint/rule-tester": "^8.59.0",
|
|
27
|
+
"@vitest/coverage-v8": "^4.1.5",
|
|
28
|
+
"eslint": "^10.2.1",
|
|
29
|
+
"tsup": "^8.5.1",
|
|
30
|
+
"typescript": "^5.9.3",
|
|
31
|
+
"vitest": "^4.1.5"
|
|
31
32
|
},
|
|
32
33
|
"peerDependencies": {
|
|
33
34
|
"eslint": "^8.0.0 || ^9.0.0 || ^10.0.0",
|
|
@@ -59,6 +60,7 @@
|
|
|
59
60
|
"test:watch": "vitest",
|
|
60
61
|
"lint": "eslint src",
|
|
61
62
|
"typecheck": "tsc --noEmit",
|
|
62
|
-
"clean": "node ../../scripts/clean.mjs dist"
|
|
63
|
+
"clean": "node ../../scripts/clean.mjs dist",
|
|
64
|
+
"test:coverage": "vitest run --coverage"
|
|
63
65
|
}
|
|
64
66
|
}
|