@brnshkr/config 0.0.1-beta.3 → 0.0.1-beta.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/README.md +174 -226
- package/conf/.gitignore.dist +15 -0
- package/conf/Makefile +3778 -0
- package/conf/Makefile.dist +13 -0
- package/conf/bunfig.dist.toml +5 -0
- package/conf/commitlint.dist.mjs +1 -0
- package/conf/editorconfig.dist +19 -0
- package/conf/launch.dist.json +31 -0
- package/conf/markdownlint.dist.mjs +1 -0
- package/conf/spelling/defaults.json +185 -0
- package/conf/tsconfig.dist.json +3 -0
- package/conf/tsconfig.json +31 -27
- package/conf/vitest.dist.mjs +1 -0
- package/conf/vscode-css-custom-data.dist.json +95 -0
- package/conf/vscode-extensions.dist.json +21 -0
- package/conf/vscode-settings.dist.jsonc +338 -0
- package/dist/commitlint/index.d.mts +72 -0
- package/dist/commitlint/index.mjs +239 -0
- package/dist/eslint/index.d.mts +3890 -1387
- package/dist/eslint/index.mjs +2662 -471
- package/dist/markdownlint/index.d.mts +2304 -0
- package/dist/markdownlint/index.mjs +440 -0
- package/dist/shared.mjs +406 -64
- package/dist/spelling/index.d.mts +30 -0
- package/dist/spelling/index.mjs +2 -0
- package/dist/spelling/spelling.test.d.mts +1 -0
- package/dist/spelling/spelling.test.mjs +12 -0
- package/dist/stylelint/index.d.mts +47 -9
- package/dist/stylelint/index.mjs +145 -47
- package/dist/vitest/index.d.mts +73 -0
- package/dist/vitest/index.mjs +181 -0
- package/package.json +192 -105
- package/conf/tsconfig.json.example +0 -3
- package/dist/scripts/eslint.mjs +0 -16
- package/dist/scripts/stylelint.mjs +0 -31
- /package/conf/{eslint.config.mjs.example → eslint.dist.mjs} +0 -0
- /package/conf/{stylelint.config.mjs.example → stylelint.dist.mjs} +0 -0
package/dist/eslint/index.mjs
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { C as objectAssign, D as objectValues, E as objectKeys, O as packageOrganization, S as isPlainObject, T as objectFromEntries, a as getMtime, c as toPosix$1, d as GLOB_TEST_FILES$1, f as createModuleState, h as resolvePackagesSharedAsynchronously, i as findNearestPackageJson, j as version, k as packageOrganizationUpper, l as GLOB_BENCHMARK_FILES, m as isModuleEnabledByDefault, n as QUOTES, o as readJsonObjectFile, p as doAllPackagesExist, r as doesFileExist, s as readTextFile, u as GLOB_IGNORES, v as ESLINT_PACKAGES, w as objectEntries } from "../shared.mjs";
|
|
2
2
|
import { FlatConfigComposer } from "eslint-flat-config-utils";
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
|
+
import { includeIgnoreFile } from "eslint/config";
|
|
5
6
|
import jsEslint from "@eslint/js";
|
|
6
7
|
import confusingBrowserGlobals from "confusing-browser-globals";
|
|
7
8
|
import globals from "globals";
|
|
8
9
|
//#region ../src/js/eslint/types/scopes.ts
|
|
10
|
+
/**
|
|
11
|
+
* @internal @brnshkr/config/eslint
|
|
12
|
+
*/
|
|
9
13
|
const MAIN_SCOPES = {
|
|
10
14
|
[packageOrganizationUpper]: "builtin",
|
|
11
15
|
COMMENTS: "comments",
|
|
@@ -33,7 +37,7 @@ const SUB_SCOPES = {
|
|
|
33
37
|
BASE: "base",
|
|
34
38
|
DEVELOPMENT: "development",
|
|
35
39
|
EXAMPLES: "examples",
|
|
36
|
-
|
|
40
|
+
FILES: "files",
|
|
37
41
|
GLOBAL: "global",
|
|
38
42
|
PARSER: "parser",
|
|
39
43
|
PROCESSOR: "processor",
|
|
@@ -43,13 +47,16 @@ const SUB_SCOPES = {
|
|
|
43
47
|
};
|
|
44
48
|
//#endregion
|
|
45
49
|
//#region ../src/js/eslint/utils/config.ts
|
|
50
|
+
/**
|
|
51
|
+
* @internal @brnshkr/config/eslint
|
|
52
|
+
*/
|
|
46
53
|
const buildConfigName = (mainScope, subScope) => [
|
|
47
54
|
packageOrganization,
|
|
48
55
|
mainScope,
|
|
49
56
|
subScope
|
|
50
57
|
].filter(Boolean).join("/");
|
|
51
|
-
const renameRules = (rules, map) =>
|
|
52
|
-
for (const [from, to] of
|
|
58
|
+
const renameRules = (rules, map) => objectFromEntries(objectEntries(rules ?? {}).map(([key, value]) => {
|
|
59
|
+
for (const [from, to] of objectEntries(map)) if (key.startsWith(`${from}/`)) return [to + key.slice(from.length), value];
|
|
53
60
|
return [key, value];
|
|
54
61
|
}));
|
|
55
62
|
const isValidGlobalAdditionalConfigKey = (key) => [
|
|
@@ -64,7 +71,7 @@ const isValidGlobalAdditionalConfigKey = (key) => [
|
|
|
64
71
|
const getGlobalAdditionalConfig = (options) => {
|
|
65
72
|
const config = {};
|
|
66
73
|
for (const [key, value] of objectEntries(options)) if (isValidGlobalAdditionalConfigKey(key)) config[key] = value;
|
|
67
|
-
if (
|
|
74
|
+
if (objectKeys(config).length === 0) return;
|
|
68
75
|
config.name ??= buildConfigName(MAIN_SCOPES.USERLAND, SUB_SCOPES.GLOBAL);
|
|
69
76
|
return config;
|
|
70
77
|
};
|
|
@@ -76,7 +83,7 @@ const ensureNamesForSyncAdditionalConfigs = (additionalConfigs) => {
|
|
|
76
83
|
validConfigs.push(config);
|
|
77
84
|
continue;
|
|
78
85
|
}
|
|
79
|
-
validConfigs.push(
|
|
86
|
+
validConfigs.push(objectKeys(config).length > 0 ? config : void 0);
|
|
80
87
|
config.name = buildConfigName(MAIN_SCOPES.USERLAND, `${SUB_SCOPES.UNNAMED}-${String(currentIndex)}`);
|
|
81
88
|
currentIndex += 1;
|
|
82
89
|
}
|
|
@@ -85,6 +92,9 @@ const ensureNamesForSyncAdditionalConfigs = (additionalConfigs) => {
|
|
|
85
92
|
const getUserConfigs = (resolvedOptions, additionalConfigs) => [getGlobalAdditionalConfig(resolvedOptions), ...ensureNamesForSyncAdditionalConfigs(additionalConfigs)];
|
|
86
93
|
//#endregion
|
|
87
94
|
//#region ../src/js/eslint/utils/globs.ts
|
|
95
|
+
/**
|
|
96
|
+
* @internal @brnshkr/config/eslint
|
|
97
|
+
*/
|
|
88
98
|
const GLOB_CJS = "**/*.cjs";
|
|
89
99
|
const GLOB_TS = "**/*.?(c|m)ts?(x)";
|
|
90
100
|
const GLOB_DTS = "**/*.d.?(c|m)ts";
|
|
@@ -107,18 +117,24 @@ const GLOB_SCRIPT_FILES = [
|
|
|
107
117
|
const GLOB_SCRIPT_FILES_WITHOUT_TS = ["**/*.?(c|m)js?(x)", GLOB_DTS];
|
|
108
118
|
const GLOB_DEVELOPMENT_FILES = [
|
|
109
119
|
"**/*.config.?(c|m)[jt]s",
|
|
120
|
+
"**/__mocks__/**",
|
|
110
121
|
"**/{conf,tests}/**",
|
|
111
122
|
"**/types/declarations/reset.d.ts"
|
|
112
123
|
];
|
|
113
|
-
const
|
|
114
|
-
"
|
|
115
|
-
"
|
|
116
|
-
"
|
|
117
|
-
"
|
|
118
|
-
"
|
|
124
|
+
const GLOB_YAML_FIXED_EXTENSION_FILES = [
|
|
125
|
+
"**/.circleci/**/*.yml",
|
|
126
|
+
"**/.github/**/*.yml",
|
|
127
|
+
"**/.gitlab-ci.yml",
|
|
128
|
+
"**/.gitlab/**/*.yml",
|
|
129
|
+
"**/.travis.yml",
|
|
130
|
+
"**/appveyor.yml"
|
|
119
131
|
];
|
|
132
|
+
const GLOB_TEST_FILES = [...GLOB_TEST_FILES$1, ...GLOB_BENCHMARK_FILES];
|
|
120
133
|
//#endregion
|
|
121
134
|
//#region ../src/js/eslint/utils/tsconfig.ts
|
|
135
|
+
/**
|
|
136
|
+
* @internal @brnshkr/config/eslint
|
|
137
|
+
*/
|
|
122
138
|
const DEFAULT_TSCONFIG_FILENAME = "tsconfig.json";
|
|
123
139
|
const JSONC_TOKEN_PATTERN = /"(?:[^"\\]|\\.)*"|\/\/[^\n]*|\/\*[\s\S]*?\*\//gv;
|
|
124
140
|
const TRAILING_COMMA_PATTERN = /,(?=\s*[\]\}])/gv;
|
|
@@ -131,12 +147,12 @@ const parseTsConfigFile = (filePath) => {
|
|
|
131
147
|
return;
|
|
132
148
|
}
|
|
133
149
|
};
|
|
134
|
-
const resolveExtends = (
|
|
135
|
-
if (
|
|
136
|
-
const resolved = path.resolve(fromDirectory,
|
|
150
|
+
const resolveExtends = (parentConfig, fromDirectory) => {
|
|
151
|
+
if (parentConfig.startsWith(".") || path.isAbsolute(parentConfig)) {
|
|
152
|
+
const resolved = path.resolve(fromDirectory, parentConfig);
|
|
137
153
|
return path.extname(resolved) === "" ? `${resolved}.json` : resolved;
|
|
138
154
|
}
|
|
139
|
-
return path.resolve(fromDirectory, "node_modules",
|
|
155
|
+
return path.resolve(fromDirectory, "node_modules", parentConfig);
|
|
140
156
|
};
|
|
141
157
|
const normalizeExtends = (raw) => {
|
|
142
158
|
if (raw.extends === void 0) return [];
|
|
@@ -159,7 +175,7 @@ const loadInternal = (filePath, visitedPaths) => {
|
|
|
159
175
|
} };
|
|
160
176
|
};
|
|
161
177
|
const cache = /* @__PURE__ */ new Map();
|
|
162
|
-
const toPosix
|
|
178
|
+
const toPosix = (filePath) => filePath.replaceAll("\\", "/");
|
|
163
179
|
const loadTsConfigPaths = (tsConfigPath) => {
|
|
164
180
|
const absolutePath = path.resolve(tsConfigPath);
|
|
165
181
|
if (cache.has(absolutePath)) return cache.get(absolutePath);
|
|
@@ -170,7 +186,7 @@ const loadTsConfigPaths = (tsConfigPath) => {
|
|
|
170
186
|
return;
|
|
171
187
|
}
|
|
172
188
|
const baseUrl = path.resolve(path.dirname(absolutePath), mergedConfig.compilerOptions?.baseUrl ?? ".");
|
|
173
|
-
const result = objectFromEntries(objectEntries(paths).map(([pattern, targets]) => [pattern, targets.map((target) => toPosix
|
|
189
|
+
const result = objectFromEntries(objectEntries(paths).map(([pattern, targets]) => [pattern, targets.map((target) => toPosix(path.resolve(baseUrl, target)))]));
|
|
174
190
|
cache.set(absolutePath, result);
|
|
175
191
|
return result;
|
|
176
192
|
};
|
|
@@ -191,36 +207,1546 @@ const resolveTsConfigPath = (typescriptOptions) => {
|
|
|
191
207
|
return path.resolve(process.cwd(), tsconfig);
|
|
192
208
|
};
|
|
193
209
|
//#endregion
|
|
210
|
+
//#region ../src/js/eslint/utils/ast.ts
|
|
211
|
+
const isFluentReturn = (returnType) => returnType?.typeAnnotation.type === "TSThisType";
|
|
212
|
+
const isVoidLikeReturn = (returnType) => returnType === void 0 || returnType.typeAnnotation.type === "TSVoidKeyword" || returnType.typeAnnotation.type === "TSNeverKeyword";
|
|
213
|
+
const resolveParameterIdentifier = (parameter) => {
|
|
214
|
+
if (parameter.type === "Identifier") return parameter;
|
|
215
|
+
if (parameter.type === "AssignmentPattern" && parameter.left.type === "Identifier") return parameter.left;
|
|
216
|
+
if (parameter.type === "RestElement" && parameter.argument.type === "Identifier") return parameter.argument;
|
|
217
|
+
if (parameter.type === "TSParameterProperty") return resolveParameterIdentifier(parameter.parameter);
|
|
218
|
+
};
|
|
219
|
+
const getParameterName = (parameter) => resolveParameterIdentifier(parameter)?.name;
|
|
220
|
+
const isAccessibleMethod = (method) => method.accessibility !== "private" && method.accessibility !== "protected" && method.key.type !== "PrivateIdentifier";
|
|
221
|
+
const isAbstractMethod = (method) => method.type === "TSAbstractMethodDefinition";
|
|
222
|
+
const isClassMethodLike = (node) => node.type === "MethodDefinition" || node.type === "TSAbstractMethodDefinition";
|
|
223
|
+
const getNamedKeyText = (key) => {
|
|
224
|
+
if (key.type === "Identifier" || key.type === "PrivateIdentifier") return key.name;
|
|
225
|
+
if (key.type === "Literal") return String(key.value);
|
|
226
|
+
return "<computed>";
|
|
227
|
+
};
|
|
228
|
+
const isFunctionInitializer = (candidate) => candidate?.type === "ArrowFunctionExpression" || candidate?.type === "FunctionExpression";
|
|
229
|
+
const resolveFunctionShape = (declaration) => {
|
|
230
|
+
if (declaration.type === "VariableDeclarator") {
|
|
231
|
+
if (!isFunctionInitializer(declaration.init)) return;
|
|
232
|
+
return {
|
|
233
|
+
parameters: declaration.init.params,
|
|
234
|
+
returnType: declaration.init.returnType
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
return {
|
|
238
|
+
parameters: declaration.params,
|
|
239
|
+
returnType: declaration.returnType
|
|
240
|
+
};
|
|
241
|
+
};
|
|
242
|
+
const TAG_INTERNAL = "internal";
|
|
243
|
+
const escapeRegExp = (value) => value.replaceAll(/[$\(\)*+.?\[\\\]^\{\|\}]/gv, String.raw`\$&`);
|
|
244
|
+
const hasProseAfter = (pattern, comment) => {
|
|
245
|
+
const prose = pattern.exec(comment)?.groups?.["prose"];
|
|
246
|
+
return typeof prose === "string" && /[A-Za-z]/v.test(prose);
|
|
247
|
+
};
|
|
248
|
+
const isBlockComment = (comment) => comment?.type === "Block" && comment.value.startsWith("*");
|
|
249
|
+
const extractBlockComment = (comments, node) => {
|
|
250
|
+
let expectedEndLine = node.loc.start.line - 1;
|
|
251
|
+
for (const comment of comments.toReversed()) {
|
|
252
|
+
if (comment.loc.end.line !== expectedEndLine) return;
|
|
253
|
+
expectedEndLine = comment.loc.start.line - 1;
|
|
254
|
+
if (isBlockComment(comment)) return `/*${comment.value}*/`;
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
const hasTag = (comment, tag) => comment !== void 0 && new RegExp(String.raw`@${tag}\b`, "v").test(comment);
|
|
258
|
+
const hasDescription = (comment) => {
|
|
259
|
+
if (comment === void 0) return false;
|
|
260
|
+
const beforeTags = /\/\*\*(?<body>.*?)(?:\n[\t ]*\*[\t ]+@|\*\/)/sv.exec(comment)?.groups?.["body"] ?? "";
|
|
261
|
+
return /^[\t ]*\*[\t ]+[[^\s*\/]--@][^\n]*/mv.test(beforeTags);
|
|
262
|
+
};
|
|
263
|
+
const hasParameterProse = (comment, parameterName) => hasProseAfter(new RegExp(String.raw`@param\b[^\n]*?\b${escapeRegExp(parameterName)}\b(?<prose>[^\n]*)`, "v"), comment);
|
|
264
|
+
const hasReturnsWithProse = (comment) => hasProseAfter(/@returns?\s+\S+\s+(?<prose>\S[^\n]*)/v, comment);
|
|
265
|
+
const getVisibilityTag = (comment) => {
|
|
266
|
+
if (hasTag(comment, "internal")) return TAG_INTERNAL;
|
|
267
|
+
if (hasTag(comment, "api")) return "api";
|
|
268
|
+
};
|
|
269
|
+
const hasConflictingVisibilityTags = (comment) => hasTag(comment, "api") && hasTag(comment, "internal");
|
|
270
|
+
const hasModuleSource = (node) => "source" in node && (node.source ?? void 0) !== void 0;
|
|
271
|
+
const findFileLevelComment$1 = (sourceCode) => {
|
|
272
|
+
const [firstStatement] = sourceCode.ast.body;
|
|
273
|
+
if (firstStatement === void 0) return;
|
|
274
|
+
const leadingComments = sourceCode.getCommentsBefore(firstStatement).filter(isBlockComment);
|
|
275
|
+
const [fileComment] = leadingComments;
|
|
276
|
+
if (fileComment === void 0) return;
|
|
277
|
+
if (leadingComments.length > 1 || hasModuleSource(firstStatement)) return fileComment;
|
|
278
|
+
return firstStatement.loc.start.line - fileComment.loc.end.line > 1 ? fileComment : void 0;
|
|
279
|
+
};
|
|
280
|
+
const getFileLevelBlockComment = (sourceCode) => {
|
|
281
|
+
const comment = findFileLevelComment$1(sourceCode);
|
|
282
|
+
return comment === void 0 ? void 0 : `/*${comment.value}*/`;
|
|
283
|
+
};
|
|
284
|
+
const getEffectiveVisibilityTag = (symbolComment, fileComment) => getVisibilityTag(symbolComment) ?? getVisibilityTag(fileComment);
|
|
285
|
+
//#endregion
|
|
286
|
+
//#region ../src/js/eslint/utils/exports.ts
|
|
287
|
+
/**
|
|
288
|
+
* @internal @brnshkr/config/eslint
|
|
289
|
+
*/
|
|
290
|
+
const DEFAULT_NAME = "default";
|
|
291
|
+
const ANONYMOUS_NAME = "<anonymous>";
|
|
292
|
+
const ATOMIC_NAMED_KIND_MAP = {
|
|
293
|
+
ClassDeclaration: "Class",
|
|
294
|
+
FunctionDeclaration: "Function",
|
|
295
|
+
TSDeclareFunction: "Function",
|
|
296
|
+
TSEnumDeclaration: "Enum",
|
|
297
|
+
TSInterfaceDeclaration: "Interface",
|
|
298
|
+
TSTypeAliasDeclaration: "Type"
|
|
299
|
+
};
|
|
300
|
+
const DEFAULT_KIND_MAP = {
|
|
301
|
+
ArrowFunctionExpression: "Function",
|
|
302
|
+
CallExpression: "Constant",
|
|
303
|
+
FunctionExpression: "Function",
|
|
304
|
+
Identifier: "Constant",
|
|
305
|
+
NewExpression: "Constant",
|
|
306
|
+
ObjectExpression: "Constant"
|
|
307
|
+
};
|
|
308
|
+
const collectAtomicNamedDeclaration = (declaration, anchor, comment) => {
|
|
309
|
+
const kind = ATOMIC_NAMED_KIND_MAP[declaration.type];
|
|
310
|
+
if (kind === void 0) return;
|
|
311
|
+
return {
|
|
312
|
+
anchor,
|
|
313
|
+
declaration,
|
|
314
|
+
comment,
|
|
315
|
+
kind,
|
|
316
|
+
name: declaration.id?.name ?? ANONYMOUS_NAME
|
|
317
|
+
};
|
|
318
|
+
};
|
|
319
|
+
const collectVariableDeclaration = (declaration, anchor, comment) => declaration.declarations.filter((declarator) => declarator.id.type === "Identifier").map((declarator) => ({
|
|
320
|
+
anchor,
|
|
321
|
+
declaration: declarator,
|
|
322
|
+
comment,
|
|
323
|
+
kind: isFunctionInitializer(declarator.init) ? "Function" : "Constant",
|
|
324
|
+
name: declarator.id.name
|
|
325
|
+
}));
|
|
326
|
+
const collectNamedDeclaration = (declaration, anchor, comment) => {
|
|
327
|
+
const atomic = collectAtomicNamedDeclaration(declaration, anchor, comment);
|
|
328
|
+
if (atomic !== void 0) return [atomic];
|
|
329
|
+
if (declaration.type === "VariableDeclaration") return collectVariableDeclaration(declaration, anchor, comment);
|
|
330
|
+
return [];
|
|
331
|
+
};
|
|
332
|
+
const collectDefaultDeclaration = (declaration, anchor, comment) => {
|
|
333
|
+
if (declaration.type === "ClassDeclaration" || declaration.type === "FunctionDeclaration") return collectNamedDeclaration(declaration, anchor, comment);
|
|
334
|
+
const kind = DEFAULT_KIND_MAP[declaration.type];
|
|
335
|
+
if (kind === void 0) return [];
|
|
336
|
+
return [{
|
|
337
|
+
anchor,
|
|
338
|
+
declaration,
|
|
339
|
+
comment,
|
|
340
|
+
kind,
|
|
341
|
+
name: declaration.type === "Identifier" ? declaration.name : DEFAULT_NAME
|
|
342
|
+
}];
|
|
343
|
+
};
|
|
344
|
+
const buildExportVisitors = (sourceCode, onSymbol) => {
|
|
345
|
+
const emit = (node, collectDeclaration) => {
|
|
346
|
+
if (!node.declaration) return;
|
|
347
|
+
const comment = extractBlockComment(sourceCode.getCommentsBefore(node), node);
|
|
348
|
+
for (const symbol of collectDeclaration(node.declaration, node, comment)) onSymbol(symbol);
|
|
349
|
+
};
|
|
350
|
+
return {
|
|
351
|
+
ExportDefaultDeclaration: (node) => {
|
|
352
|
+
emit(node, collectDefaultDeclaration);
|
|
353
|
+
},
|
|
354
|
+
ExportNamedDeclaration: (node) => {
|
|
355
|
+
emit(node, collectNamedDeclaration);
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
};
|
|
359
|
+
//#endregion
|
|
360
|
+
//#region ../src/js/eslint/utils/package-exports.ts
|
|
361
|
+
/**
|
|
362
|
+
* @internal @brnshkr/config/eslint
|
|
363
|
+
*/
|
|
364
|
+
const DEFAULT_DIST_ROOT = "./dist";
|
|
365
|
+
const DEFAULT_SRC_EXTENSIONS = [
|
|
366
|
+
".ts",
|
|
367
|
+
".tsx",
|
|
368
|
+
".mts",
|
|
369
|
+
".cts",
|
|
370
|
+
".d.ts",
|
|
371
|
+
".d.mts",
|
|
372
|
+
".d.cts",
|
|
373
|
+
".js",
|
|
374
|
+
".jsx",
|
|
375
|
+
".mjs",
|
|
376
|
+
".cjs",
|
|
377
|
+
".svelte",
|
|
378
|
+
".svelte.ts",
|
|
379
|
+
".svelte.js"
|
|
380
|
+
];
|
|
381
|
+
const SRC_ROOT_CANDIDATES = [
|
|
382
|
+
"./src",
|
|
383
|
+
"./src/js",
|
|
384
|
+
"./src/main",
|
|
385
|
+
"./source",
|
|
386
|
+
"./sources",
|
|
387
|
+
"./lib",
|
|
388
|
+
"."
|
|
389
|
+
];
|
|
390
|
+
const REEXPORT_NAME_PART = String.raw`(?:\*(?:\s+as\s+[\p{ID_Start}$_][\p{ID_Continue}$]*)?|\{[^\}]*\})`;
|
|
391
|
+
const normalizeRoot = (value) => toPosix$1(value).replace(/^\.\//v, "").replace(/\/$/v, "");
|
|
392
|
+
const collectStringEntries = (node, accumulator) => {
|
|
393
|
+
if (typeof node === "string") {
|
|
394
|
+
accumulator.push(node);
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
if (Array.isArray(node)) {
|
|
398
|
+
for (const value of node) collectStringEntries(value, accumulator);
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
if (!isPlainObject(node)) return;
|
|
402
|
+
for (const value of objectValues(node)) collectStringEntries(value, accumulator);
|
|
403
|
+
};
|
|
404
|
+
const findFirstExistingFile = (candidates) => {
|
|
405
|
+
for (const candidate of candidates) if (doesFileExist(candidate)) return toPosix$1(candidate);
|
|
406
|
+
};
|
|
407
|
+
const buildSourceCandidates = (baseAbsolute, sourceExtensions) => [...sourceExtensions.map((extension) => `${baseAbsolute}${extension}`), ...sourceExtensions.map((extension) => path.resolve(baseAbsolute, `index${extension}`))];
|
|
408
|
+
const resolveSourceForDistributionFile = (distributionRelativePath, packageRoot, sourceRoot, sourceExtensions) => {
|
|
409
|
+
const baseAbsolute = path.resolve(packageRoot, sourceRoot, distributionRelativePath.replace(/(?:\.d\.[cm]?ts|\.[cm]?jsx?)$/v, ""));
|
|
410
|
+
return findFirstExistingFile(buildSourceCandidates(baseAbsolute, sourceExtensions));
|
|
411
|
+
};
|
|
412
|
+
const resolveImportSpecifier = (fromFilePath, specifier, sourceExtensions) => {
|
|
413
|
+
if (!specifier.startsWith(".")) return;
|
|
414
|
+
const absoluteBase = path.resolve(path.dirname(fromFilePath), specifier);
|
|
415
|
+
const candidates = buildSourceCandidates(absoluteBase, sourceExtensions);
|
|
416
|
+
return findFirstExistingFile(path.extname(absoluteBase) === "" ? candidates : [absoluteBase, ...candidates]);
|
|
417
|
+
};
|
|
418
|
+
const collectReexports = (entryPath, sourceExtensions, visitedPaths) => {
|
|
419
|
+
if (visitedPaths.has(entryPath)) return;
|
|
420
|
+
visitedPaths.add(entryPath);
|
|
421
|
+
const content = readTextFile(entryPath);
|
|
422
|
+
if (content === void 0) return;
|
|
423
|
+
const reexportPattern = new RegExp(String.raw`\bexport\s+(?:type\s+)?${REEXPORT_NAME_PART}\s+from\s+["'](?<specifier>[^"']+)["']`, "gv");
|
|
424
|
+
for (const match of content.matchAll(reexportPattern)) {
|
|
425
|
+
const specifier = match.groups?.["specifier"];
|
|
426
|
+
if (specifier === void 0) continue;
|
|
427
|
+
const resolvedPath = resolveImportSpecifier(entryPath, specifier, sourceExtensions);
|
|
428
|
+
if (resolvedPath !== void 0) collectReexports(resolvedPath, sourceExtensions, visitedPaths);
|
|
429
|
+
}
|
|
430
|
+
};
|
|
431
|
+
const stripPrefix = (filePath, normalizedPrefix) => {
|
|
432
|
+
const normalizedFile = toPosix$1(filePath).replace(/^\.\//v, "");
|
|
433
|
+
if (!normalizedFile.startsWith(normalizedPrefix)) return;
|
|
434
|
+
return normalizedFile.slice(normalizedPrefix.length);
|
|
435
|
+
};
|
|
436
|
+
const collectApiSourceFiles = (distributionRelativePaths, packageRoot, sourceRoots, sourceExtensions) => {
|
|
437
|
+
const apiSourceFiles = /* @__PURE__ */ new Set();
|
|
438
|
+
for (const distributionRelativePath of distributionRelativePaths) {
|
|
439
|
+
const resolvedPath = sourceRoots.values().map((sourceRoot) => resolveSourceForDistributionFile(distributionRelativePath, packageRoot, sourceRoot, sourceExtensions)).find((candidatePath) => candidatePath !== void 0);
|
|
440
|
+
if (resolvedPath !== void 0) collectReexports(resolvedPath, sourceExtensions, apiSourceFiles);
|
|
441
|
+
}
|
|
442
|
+
return apiSourceFiles;
|
|
443
|
+
};
|
|
444
|
+
const resolvePackageApiSources = (options) => {
|
|
445
|
+
const packageJsonPath = path.resolve(options.packageJsonPath);
|
|
446
|
+
const manifest = readJsonObjectFile(packageJsonPath);
|
|
447
|
+
if (manifest?.["exports"] === void 0) return;
|
|
448
|
+
const packageRoot = path.dirname(packageJsonPath);
|
|
449
|
+
const distributionRoot = options.distRoot ?? DEFAULT_DIST_ROOT;
|
|
450
|
+
const sourceExtensions = options.srcExtensions ?? DEFAULT_SRC_EXTENSIONS;
|
|
451
|
+
const sourceRoots = options.srcRoot === void 0 ? SRC_ROOT_CANDIDATES : [options.srcRoot];
|
|
452
|
+
const normalizedDistribution = `${normalizeRoot(distributionRoot)}/`;
|
|
453
|
+
const exportEntries = [];
|
|
454
|
+
collectStringEntries(manifest["exports"], exportEntries);
|
|
455
|
+
const distributionRelativePaths = exportEntries.filter((value) => value.startsWith("./") && !value.includes("*")).map((value) => stripPrefix(value, normalizedDistribution)).filter((value) => value !== void 0);
|
|
456
|
+
if (distributionRelativePaths.length === 0) return;
|
|
457
|
+
const apiSourceFiles = collectApiSourceFiles(distributionRelativePaths, packageRoot, sourceRoots, sourceExtensions);
|
|
458
|
+
if (apiSourceFiles.size === 0) return;
|
|
459
|
+
return {
|
|
460
|
+
packageJsonPath: toPosix$1(packageJsonPath),
|
|
461
|
+
packageRoot: toPosix$1(packageRoot),
|
|
462
|
+
distRoot: distributionRoot,
|
|
463
|
+
srcRoots: sourceRoots,
|
|
464
|
+
apiSourceFiles
|
|
465
|
+
};
|
|
466
|
+
};
|
|
467
|
+
//#endregion
|
|
468
|
+
//#region ../src/js/eslint/utils/public-api.ts
|
|
469
|
+
/**
|
|
470
|
+
* @internal @brnshkr/config/eslint
|
|
471
|
+
*/
|
|
472
|
+
const resolutionCache = /* @__PURE__ */ new Map();
|
|
473
|
+
const getCacheKey = (options, cwd) => JSON.stringify({
|
|
474
|
+
cwd,
|
|
475
|
+
packageJsonPath: options.packageJsonPath,
|
|
476
|
+
distRoot: options.distRoot,
|
|
477
|
+
srcRoot: options.srcRoot,
|
|
478
|
+
srcExtensions: options.srcExtensions
|
|
479
|
+
});
|
|
480
|
+
const loadPublicApiResolution = (options, cwd) => {
|
|
481
|
+
const cacheKey = getCacheKey(options, cwd);
|
|
482
|
+
const packageJsonPath = options.packageJsonPath ?? findNearestPackageJson(cwd);
|
|
483
|
+
const mtime = packageJsonPath === void 0 ? void 0 : getMtime(packageJsonPath);
|
|
484
|
+
const cacheEntry = resolutionCache.get(cacheKey);
|
|
485
|
+
if (cacheEntry !== void 0 && cacheEntry.mtime === mtime) return cacheEntry.resolution;
|
|
486
|
+
const resolution = packageJsonPath === void 0 ? void 0 : resolvePackageApiSources({
|
|
487
|
+
packageJsonPath,
|
|
488
|
+
distRoot: options.distRoot,
|
|
489
|
+
srcRoot: options.srcRoot,
|
|
490
|
+
srcExtensions: options.srcExtensions
|
|
491
|
+
});
|
|
492
|
+
resolutionCache.set(cacheKey, {
|
|
493
|
+
mtime,
|
|
494
|
+
resolution
|
|
495
|
+
});
|
|
496
|
+
return resolution;
|
|
497
|
+
};
|
|
498
|
+
const isPublicApiFile = (options, cwd, filename) => {
|
|
499
|
+
const resolution = loadPublicApiResolution(options, cwd);
|
|
500
|
+
return resolution === void 0 ? false : resolution.apiSourceFiles.has(toPosix$1(path.resolve(filename)));
|
|
501
|
+
};
|
|
502
|
+
//#endregion
|
|
503
|
+
//#region ../src/js/eslint/configs/builtin/api-or-internal-tag.ts
|
|
504
|
+
/**
|
|
505
|
+
* @internal @brnshkr/config/eslint
|
|
506
|
+
*/
|
|
507
|
+
const MESSAGE_ID_MISSING_TAG = "missingTag";
|
|
508
|
+
const MESSAGE_ID_UNEXPECTED_TAG_CONFLICT = "unexpectedTagConflict";
|
|
509
|
+
const MESSAGE_ID_UNEXPECTED_FILE_TAG_CONFLICT = "unexpectedFileTagConflict";
|
|
510
|
+
/**
|
|
511
|
+
* @see https://github.com/brnshkr/config/blob/master/docs/js/eslint/rules/api-or-internal-tag.md
|
|
512
|
+
*/
|
|
513
|
+
const apiOrInternalTagRule = {
|
|
514
|
+
meta: {
|
|
515
|
+
type: "suggestion",
|
|
516
|
+
docs: {
|
|
517
|
+
description: "Require every exported declaration in a public-API source file to carry either an `@api` or an `@internal` tag, and never both; a file-level docblock carrying either tag covers all symbols below it.",
|
|
518
|
+
url: "https://github.com/brnshkr/config/blob/master/docs/js/eslint/rules/api-or-internal-tag.md"
|
|
519
|
+
},
|
|
520
|
+
schema: [{
|
|
521
|
+
type: "object",
|
|
522
|
+
additionalProperties: false,
|
|
523
|
+
properties: {
|
|
524
|
+
packageJsonPath: { type: "string" },
|
|
525
|
+
distRoot: { type: "string" },
|
|
526
|
+
srcRoot: { type: "string" },
|
|
527
|
+
srcExtensions: {
|
|
528
|
+
type: "array",
|
|
529
|
+
items: { type: "string" }
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}],
|
|
533
|
+
messages: {
|
|
534
|
+
[MESSAGE_ID_MISSING_TAG]: "{{ kind }} `{{ name }}` is exported from a public-API source file and must carry an `@api` or `@internal` JSDoc tag.",
|
|
535
|
+
[MESSAGE_ID_UNEXPECTED_TAG_CONFLICT]: "{{ kind }} `{{ name }}` must declare exactly one visibility, but carries both `@api` and `@internal`.",
|
|
536
|
+
[MESSAGE_ID_UNEXPECTED_FILE_TAG_CONFLICT]: "The file-level docblock must declare exactly one visibility, but carries both `@api` and `@internal`."
|
|
537
|
+
}
|
|
538
|
+
},
|
|
539
|
+
create: (context) => {
|
|
540
|
+
const options = context.options[0] ?? {};
|
|
541
|
+
const sourceCode = context.sourceCode;
|
|
542
|
+
const fileCommentNode = findFileLevelComment$1(sourceCode);
|
|
543
|
+
const fileComment = fileCommentNode === void 0 ? void 0 : `/*${fileCommentNode.value}*/`;
|
|
544
|
+
const isInPublicApiFile = isPublicApiFile(options, context.cwd, context.filename);
|
|
545
|
+
return {
|
|
546
|
+
...buildExportVisitors(sourceCode, (symbol) => {
|
|
547
|
+
if (hasConflictingVisibilityTags(symbol.comment)) {
|
|
548
|
+
context.report({
|
|
549
|
+
node: symbol.anchor,
|
|
550
|
+
messageId: MESSAGE_ID_UNEXPECTED_TAG_CONFLICT,
|
|
551
|
+
data: {
|
|
552
|
+
kind: symbol.kind,
|
|
553
|
+
name: symbol.name
|
|
554
|
+
}
|
|
555
|
+
});
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
if (!isInPublicApiFile || getEffectiveVisibilityTag(symbol.comment, fileComment) !== void 0) return;
|
|
559
|
+
context.report({
|
|
560
|
+
node: symbol.anchor,
|
|
561
|
+
messageId: MESSAGE_ID_MISSING_TAG,
|
|
562
|
+
data: {
|
|
563
|
+
kind: symbol.kind,
|
|
564
|
+
name: symbol.name
|
|
565
|
+
}
|
|
566
|
+
});
|
|
567
|
+
}),
|
|
568
|
+
"Program:exit": () => {
|
|
569
|
+
if (fileCommentNode !== void 0 && hasConflictingVisibilityTags(fileComment)) context.report({
|
|
570
|
+
loc: fileCommentNode.loc,
|
|
571
|
+
messageId: MESSAGE_ID_UNEXPECTED_FILE_TAG_CONFLICT
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
};
|
|
577
|
+
//#endregion
|
|
578
|
+
//#region ../src/js/eslint/utils/boolish-classification.ts
|
|
579
|
+
/**
|
|
580
|
+
* @internal @brnshkr/config/eslint
|
|
581
|
+
*/
|
|
582
|
+
const TYPE_CLASSIFICATIONS = {
|
|
583
|
+
BOOL: "bool",
|
|
584
|
+
NON_BOOL: "non-bool",
|
|
585
|
+
UNKNOWN: "unknown"
|
|
586
|
+
};
|
|
587
|
+
const BOOLEAN_TYPE_NAMES = /* @__PURE__ */ new Set([
|
|
588
|
+
"boolean",
|
|
589
|
+
"false",
|
|
590
|
+
"true"
|
|
591
|
+
]);
|
|
592
|
+
const NULLISH_TYPE_NAMES = /* @__PURE__ */ new Set(["null", "undefined"]);
|
|
593
|
+
const UNRESOLVED_TYPE_NAMES = /* @__PURE__ */ new Set([
|
|
594
|
+
"any",
|
|
595
|
+
"error",
|
|
596
|
+
"never",
|
|
597
|
+
"unknown"
|
|
598
|
+
]);
|
|
599
|
+
const BOOLEAN_TYPE_NODES = /* @__PURE__ */ new Set(["TSBooleanKeyword", "TSTypePredicate"]);
|
|
600
|
+
const NULLISH_TYPE_NODES = /* @__PURE__ */ new Set(["TSNullKeyword", "TSUndefinedKeyword"]);
|
|
601
|
+
const UNRESOLVED_TYPE_NODES = /* @__PURE__ */ new Set([
|
|
602
|
+
"TSAnyKeyword",
|
|
603
|
+
"TSConditionalType",
|
|
604
|
+
"TSImportType",
|
|
605
|
+
"TSIndexedAccessType",
|
|
606
|
+
"TSInferType",
|
|
607
|
+
"TSIntersectionType",
|
|
608
|
+
"TSMappedType",
|
|
609
|
+
"TSNeverKeyword",
|
|
610
|
+
"TSThisType",
|
|
611
|
+
"TSTypeOperator",
|
|
612
|
+
"TSTypeQuery",
|
|
613
|
+
"TSTypeReference",
|
|
614
|
+
"TSUnknownKeyword"
|
|
615
|
+
]);
|
|
616
|
+
const NON_BOOLEAN_EXPRESSIONS = /* @__PURE__ */ new Set([
|
|
617
|
+
"ArrayExpression",
|
|
618
|
+
"NewExpression",
|
|
619
|
+
"ObjectExpression",
|
|
620
|
+
"TemplateLiteral"
|
|
621
|
+
]);
|
|
622
|
+
const UNWRAPPED_EXPRESSIONS = /* @__PURE__ */ new Set([
|
|
623
|
+
"TSAsExpression",
|
|
624
|
+
"TSNonNullExpression",
|
|
625
|
+
"TSSatisfiesExpression",
|
|
626
|
+
"TSTypeAssertion"
|
|
627
|
+
]);
|
|
628
|
+
const BOOLEAN_BINARY_OPERATORS = /* @__PURE__ */ new Set([
|
|
629
|
+
"!=",
|
|
630
|
+
"!==",
|
|
631
|
+
"<",
|
|
632
|
+
"<=",
|
|
633
|
+
"==",
|
|
634
|
+
"===",
|
|
635
|
+
">",
|
|
636
|
+
">=",
|
|
637
|
+
"in",
|
|
638
|
+
"instanceof"
|
|
639
|
+
]);
|
|
640
|
+
const BOOLEAN_CONSTRUCTOR_NAME = "Boolean";
|
|
641
|
+
const PROMISE_TYPE_NAME = "Promise";
|
|
642
|
+
const EXTERNAL_PATH_SEGMENT = "/node_modules/";
|
|
643
|
+
const combineClassifications = (classifications) => {
|
|
644
|
+
if (classifications.length === 0) return TYPE_CLASSIFICATIONS.UNKNOWN;
|
|
645
|
+
if (classifications.every((classification) => classification === TYPE_CLASSIFICATIONS.BOOL)) return TYPE_CLASSIFICATIONS.BOOL;
|
|
646
|
+
return classifications.every((classification) => classification === TYPE_CLASSIFICATIONS.NON_BOOL) ? TYPE_CLASSIFICATIONS.NON_BOOL : TYPE_CLASSIFICATIONS.UNKNOWN;
|
|
647
|
+
};
|
|
648
|
+
const getTypeName = (checker, type) => checker.typeToString(checker.getBaseTypeOfLiteralType(type));
|
|
649
|
+
const classifyPlainType = (checker, type) => {
|
|
650
|
+
const symbol = type.getSymbol();
|
|
651
|
+
if (type.isTypeParameter()) return TYPE_CLASSIFICATIONS.UNKNOWN;
|
|
652
|
+
if (symbol !== void 0) return TYPE_CLASSIFICATIONS.NON_BOOL;
|
|
653
|
+
const typeName = getTypeName(checker, type);
|
|
654
|
+
if (BOOLEAN_TYPE_NAMES.has(typeName)) return TYPE_CLASSIFICATIONS.BOOL;
|
|
655
|
+
return UNRESOLVED_TYPE_NAMES.has(typeName) ? TYPE_CLASSIFICATIONS.UNKNOWN : TYPE_CLASSIFICATIONS.NON_BOOL;
|
|
656
|
+
};
|
|
657
|
+
const classifyType = (checker, type) => {
|
|
658
|
+
if (!type.isUnion()) return classifyPlainType(checker, type);
|
|
659
|
+
return combineClassifications(type.types.filter((member) => !NULLISH_TYPE_NAMES.has(getTypeName(checker, member))).map((member) => classifyPlainType(checker, member)));
|
|
660
|
+
};
|
|
661
|
+
const unwrapPromiseType = (checker, type) => type.getSymbol()?.getName() === PROMISE_TYPE_NAME ? checker.getTypeArguments(type)[0] ?? type : type;
|
|
662
|
+
const resolveReturnType = (checker, type) => {
|
|
663
|
+
const [signature] = type.getCallSignatures();
|
|
664
|
+
return signature === void 0 ? void 0 : unwrapPromiseType(checker, signature.getReturnType());
|
|
665
|
+
};
|
|
666
|
+
const isExternalSymbol = (symbol) => symbol.declarations?.some((declaration) => toPosix$1(declaration.getSourceFile().fileName).includes(EXTERNAL_PATH_SEGMENT)) ?? true;
|
|
667
|
+
const resolveInstanceType = (type) => type.getConstructSignatures()[0]?.getReturnType() ?? type;
|
|
668
|
+
const hasExternalUpstreamMember = (services, enclosingClass, name) => [...enclosingClass.superClass ? [enclosingClass.superClass] : [], ...enclosingClass.implements].some((heritageNode) => {
|
|
669
|
+
const member = resolveInstanceType(services.getTypeAtLocation(heritageNode)).getProperty(name);
|
|
670
|
+
return member !== void 0 && isExternalSymbol(member);
|
|
671
|
+
});
|
|
672
|
+
const classifyTypeAnnotation = (node) => {
|
|
673
|
+
if (node.type === "TSUnionType") return combineClassifications(node.types.filter((member) => !NULLISH_TYPE_NODES.has(member.type)).map((member) => classifyTypeAnnotation(member)));
|
|
674
|
+
if (node.type === "TSLiteralType") return node.literal.type === "Literal" && typeof node.literal.value === "boolean" ? TYPE_CLASSIFICATIONS.BOOL : TYPE_CLASSIFICATIONS.NON_BOOL;
|
|
675
|
+
if (BOOLEAN_TYPE_NODES.has(node.type)) return TYPE_CLASSIFICATIONS.BOOL;
|
|
676
|
+
return UNRESOLVED_TYPE_NODES.has(node.type) ? TYPE_CLASSIFICATIONS.UNKNOWN : TYPE_CLASSIFICATIONS.NON_BOOL;
|
|
677
|
+
};
|
|
678
|
+
const classifyBooleanOperator = (node) => {
|
|
679
|
+
if (node.type === "UnaryExpression") return node.operator === "!" ? TYPE_CLASSIFICATIONS.BOOL : TYPE_CLASSIFICATIONS.NON_BOOL;
|
|
680
|
+
if (node.type === "BinaryExpression") return BOOLEAN_BINARY_OPERATORS.has(node.operator) ? TYPE_CLASSIFICATIONS.BOOL : TYPE_CLASSIFICATIONS.NON_BOOL;
|
|
681
|
+
if (node.type === "CallExpression" && node.callee.type === "Identifier") return node.callee.name === BOOLEAN_CONSTRUCTOR_NAME ? TYPE_CLASSIFICATIONS.BOOL : void 0;
|
|
682
|
+
};
|
|
683
|
+
const classifyExpression = (node) => {
|
|
684
|
+
if (node.type === "Literal") return typeof node.value === "boolean" ? TYPE_CLASSIFICATIONS.BOOL : TYPE_CLASSIFICATIONS.NON_BOOL;
|
|
685
|
+
if (UNWRAPPED_EXPRESSIONS.has(node.type)) return classifyExpression(node.expression);
|
|
686
|
+
return NON_BOOLEAN_EXPRESSIONS.has(node.type) ? TYPE_CLASSIFICATIONS.NON_BOOL : classifyBooleanOperator(node) ?? TYPE_CLASSIFICATIONS.UNKNOWN;
|
|
687
|
+
};
|
|
688
|
+
//#endregion
|
|
689
|
+
//#region ../src/js/eslint/utils/boolish-prefixes.ts
|
|
690
|
+
/**
|
|
691
|
+
* Auxiliary, modal, and copula verbs.
|
|
692
|
+
* Boolish in either direction, on any kind.
|
|
693
|
+
*/
|
|
694
|
+
const AUXILIARY_PREFIXES = [
|
|
695
|
+
"are",
|
|
696
|
+
"can",
|
|
697
|
+
"did",
|
|
698
|
+
"does",
|
|
699
|
+
"has",
|
|
700
|
+
"is",
|
|
701
|
+
"may",
|
|
702
|
+
"should",
|
|
703
|
+
"was",
|
|
704
|
+
"will"
|
|
705
|
+
];
|
|
706
|
+
/**
|
|
707
|
+
* Capability and need verbs.
|
|
708
|
+
* Read as boolean value flags too, so boolish in either direction, on any kind.
|
|
709
|
+
*/
|
|
710
|
+
const CAPABILITY_PREFIXES = [
|
|
711
|
+
"expects",
|
|
712
|
+
"needs",
|
|
713
|
+
"prefers",
|
|
714
|
+
"requires",
|
|
715
|
+
"supports",
|
|
716
|
+
"wants"
|
|
717
|
+
];
|
|
718
|
+
/**
|
|
719
|
+
* Object-relation predicate verbs.
|
|
720
|
+
* Boolish in either direction, on any kind — `allowsNull` reads as a flag, `equals()` as a predicate.
|
|
721
|
+
*/
|
|
722
|
+
const RELATIONAL_PREFIXES = [
|
|
723
|
+
"accepts",
|
|
724
|
+
"allows",
|
|
725
|
+
"belongs",
|
|
726
|
+
"contains",
|
|
727
|
+
"covers",
|
|
728
|
+
"denies",
|
|
729
|
+
"depends",
|
|
730
|
+
"disallows",
|
|
731
|
+
"equals",
|
|
732
|
+
"excludes",
|
|
733
|
+
"exists",
|
|
734
|
+
"extends",
|
|
735
|
+
"handles",
|
|
736
|
+
"ignores",
|
|
737
|
+
"implements",
|
|
738
|
+
"includes",
|
|
739
|
+
"intersects",
|
|
740
|
+
"owns",
|
|
741
|
+
"provides",
|
|
742
|
+
"rejects",
|
|
743
|
+
"satisfies",
|
|
744
|
+
"uses"
|
|
745
|
+
];
|
|
746
|
+
/**
|
|
747
|
+
* Verbs whose third-person form is a canonical non-boolean value (`matches`, `startsAt`, `endsAt`)
|
|
748
|
+
* — reserved on methods and functions only, never on value-holders.
|
|
749
|
+
*/
|
|
750
|
+
const COLLIDER_PREFIXES = [
|
|
751
|
+
"ends",
|
|
752
|
+
"matches",
|
|
753
|
+
"starts"
|
|
754
|
+
];
|
|
755
|
+
/**
|
|
756
|
+
* Command verbs — allowed on a boolean return, never reserved, since `doReset(): void` and the like
|
|
757
|
+
* legitimately return a non-boolean.
|
|
758
|
+
*/
|
|
759
|
+
const DIRECTIVE_PREFIXES = ["do"];
|
|
760
|
+
/**
|
|
761
|
+
* Prefixes a non-boolean value-holder must not start with.
|
|
762
|
+
* The auxiliary, capability, and object-relation verbs — each reads as a boolean flag on a value.
|
|
763
|
+
*/
|
|
764
|
+
const RESERVED_VALUE_PREFIXES = [
|
|
765
|
+
...AUXILIARY_PREFIXES,
|
|
766
|
+
...CAPABILITY_PREFIXES,
|
|
767
|
+
...RELATIONAL_PREFIXES
|
|
768
|
+
];
|
|
769
|
+
/**
|
|
770
|
+
* Prefixes a non-boolean method or function must not start with.
|
|
771
|
+
* The value-reserved set plus the colliders — they read as a predicate on a method, data on a value.
|
|
772
|
+
*/
|
|
773
|
+
const RESERVED_METHOD_PREFIXES = [...RESERVED_VALUE_PREFIXES, ...COLLIDER_PREFIXES];
|
|
774
|
+
/**
|
|
775
|
+
* Prefixes a boolean-returning method or function may start with.
|
|
776
|
+
* The method-reserved set plus the `do` directive, which commands may also use on non-boolean returns.
|
|
777
|
+
*/
|
|
778
|
+
const PREDICATE_PREFIXES = [...RESERVED_METHOD_PREFIXES, ...DIRECTIVE_PREFIXES];
|
|
779
|
+
/**
|
|
780
|
+
* Prefixes a boolean value-holder may start with.
|
|
781
|
+
* The value-reserved set plus the `do` directive and the representation flag `as`.
|
|
782
|
+
*/
|
|
783
|
+
const FLAG_PREFIXES = [
|
|
784
|
+
...RESERVED_VALUE_PREFIXES,
|
|
785
|
+
...DIRECTIVE_PREFIXES,
|
|
786
|
+
"as"
|
|
787
|
+
];
|
|
788
|
+
const KIND_CONSTANT = "Constant";
|
|
789
|
+
const KIND_FUNCTION = "Function";
|
|
790
|
+
const KIND_METHOD$1 = "Method";
|
|
791
|
+
const KIND_PARAMETER = "Parameter";
|
|
792
|
+
const KIND_PROPERTY = "Property";
|
|
793
|
+
const KIND_VARIABLE = "Variable";
|
|
794
|
+
const CALLABLE_KINDS = /* @__PURE__ */ new Set([KIND_FUNCTION, KIND_METHOD$1]);
|
|
795
|
+
const isCallableKind = (kind) => CALLABLE_KINDS.has(kind);
|
|
796
|
+
const getFirstWord = (name) => (/^(?:[0-9a-z]+|[0-9A-Z]+)/v.exec(name)?.[0] ?? "").toLowerCase();
|
|
797
|
+
const getBooleanConverterToken = (name, kind) => isCallableKind(kind) ? /^(?:as|to)bool(?:ean)?/iv.exec(name)?.[0] : void 0;
|
|
798
|
+
const getPrefixesForKind = (kind) => isCallableKind(kind) ? PREDICATE_PREFIXES : FLAG_PREFIXES;
|
|
799
|
+
const isBoolishName = (name, kind) => getPrefixesForKind(kind).includes(getFirstWord(name)) || getBooleanConverterToken(name, kind) !== void 0;
|
|
800
|
+
const getReservedToken = (name, kind) => {
|
|
801
|
+
const converterToken = getBooleanConverterToken(name, kind);
|
|
802
|
+
if (converterToken !== void 0) return converterToken;
|
|
803
|
+
const firstWord = getFirstWord(name);
|
|
804
|
+
return (isCallableKind(kind) ? RESERVED_METHOD_PREFIXES : RESERVED_VALUE_PREFIXES).includes(firstWord) ? firstWord : void 0;
|
|
805
|
+
};
|
|
806
|
+
//#endregion
|
|
807
|
+
//#region ../src/js/eslint/configs/builtin/boolish-prefix.ts
|
|
808
|
+
/**
|
|
809
|
+
* @internal @brnshkr/config/eslint
|
|
810
|
+
*/
|
|
811
|
+
const MESSAGE_ID_MISSING_PREFIX = "missingPrefix";
|
|
812
|
+
const MESSAGE_ID_UNEXPECTED_PREFIX = "unexpectedPrefix";
|
|
813
|
+
const CLASS_MEMBER_NODES = /* @__PURE__ */ new Set([
|
|
814
|
+
"MethodDefinition",
|
|
815
|
+
"PropertyDefinition",
|
|
816
|
+
"TSAbstractMethodDefinition",
|
|
817
|
+
"TSAbstractPropertyDefinition"
|
|
818
|
+
]);
|
|
819
|
+
const CALLABLE_INITIALIZERS = /* @__PURE__ */ new Set(["ArrowFunctionExpression", "FunctionExpression"]);
|
|
820
|
+
const RETURN_OWNER_NODES = /* @__PURE__ */ new Set([
|
|
821
|
+
"ArrowFunctionExpression",
|
|
822
|
+
"FunctionDeclaration",
|
|
823
|
+
"FunctionExpression"
|
|
824
|
+
]);
|
|
825
|
+
const PARAMETER_OWNER_SELECTOR = `${[
|
|
826
|
+
"ArrowFunctionExpression",
|
|
827
|
+
"FunctionDeclaration",
|
|
828
|
+
"FunctionExpression",
|
|
829
|
+
"TSDeclareFunction",
|
|
830
|
+
"TSEmptyBodyFunctionExpression",
|
|
831
|
+
"TSFunctionType",
|
|
832
|
+
"TSMethodSignature"
|
|
833
|
+
].join(", ")}:exit`;
|
|
834
|
+
const resolveNameNode = (node) => node.type === "Identifier" || node.type === "PrivateIdentifier" ? node : void 0;
|
|
835
|
+
const isSetterMember = (node) => {
|
|
836
|
+
if (node.type !== "MethodDefinition" && node.type !== "TSAbstractMethodDefinition") return false;
|
|
837
|
+
return node.kind === "set";
|
|
838
|
+
};
|
|
839
|
+
const isSkippedMember = (ruleContext, node) => {
|
|
840
|
+
if (!CLASS_MEMBER_NODES.has(node.type)) return false;
|
|
841
|
+
const member = node;
|
|
842
|
+
const nameNode = resolveNameNode(member.key);
|
|
843
|
+
if (nameNode === void 0 || isSetterMember(member)) return true;
|
|
844
|
+
return ruleContext.services !== void 0 && hasExternalUpstreamMember(ruleContext.services, member.parent.parent, nameNode.name);
|
|
845
|
+
};
|
|
846
|
+
const findEnclosingCallable = (node) => {
|
|
847
|
+
let current = node.parent;
|
|
848
|
+
while (current !== void 0 && !RETURN_OWNER_NODES.has(current.type)) current = current.parent;
|
|
849
|
+
return current;
|
|
850
|
+
};
|
|
851
|
+
const resolveCallableResult = (ruleContext, callable) => {
|
|
852
|
+
if (callable.returnType) return classifyTypeAnnotation(callable.returnType.typeAnnotation);
|
|
853
|
+
if (callable.type === "ArrowFunctionExpression" && callable.body.type !== "BlockStatement") return classifyExpression(callable.body);
|
|
854
|
+
return combineClassifications(ruleContext.returnClassifications.get(callable) ?? []);
|
|
855
|
+
};
|
|
856
|
+
const resolveSyntacticCallable = (target, annotation) => {
|
|
857
|
+
if (target.callable !== void 0) return target.callable;
|
|
858
|
+
if (annotation?.type === "TSFunctionType") return annotation;
|
|
859
|
+
return CALLABLE_INITIALIZERS.has(target.initializer?.type ?? "") ? target.initializer : void 0;
|
|
860
|
+
};
|
|
861
|
+
const resolveHeldClassification = (target, annotation) => {
|
|
862
|
+
if (annotation !== void 0) return classifyTypeAnnotation(annotation);
|
|
863
|
+
return target.initializer ? classifyExpression(target.initializer) : TYPE_CLASSIFICATIONS.UNKNOWN;
|
|
864
|
+
};
|
|
865
|
+
const resolveFromSyntax = (ruleContext, target) => {
|
|
866
|
+
const annotation = target.typeAnnotation?.typeAnnotation;
|
|
867
|
+
const callable = resolveSyntacticCallable(target, annotation);
|
|
868
|
+
if (callable === void 0) return {
|
|
869
|
+
classification: resolveHeldClassification(target, annotation),
|
|
870
|
+
isCallable: false
|
|
871
|
+
};
|
|
872
|
+
return {
|
|
873
|
+
classification: resolveCallableResult(ruleContext, callable),
|
|
874
|
+
isCallable: true
|
|
875
|
+
};
|
|
876
|
+
};
|
|
877
|
+
const resolveFromTypes = (services, checker, nameNode) => {
|
|
878
|
+
const type = services.getTypeAtLocation(nameNode);
|
|
879
|
+
const returnType = resolveReturnType(checker, type);
|
|
880
|
+
return {
|
|
881
|
+
classification: classifyType(checker, returnType ?? type),
|
|
882
|
+
isCallable: returnType !== void 0
|
|
883
|
+
};
|
|
884
|
+
};
|
|
885
|
+
const checkTarget = (ruleContext, target) => {
|
|
886
|
+
const { checker, services } = ruleContext;
|
|
887
|
+
const { classification, isCallable } = services === void 0 || checker === void 0 ? resolveFromSyntax(ruleContext, target) : resolveFromTypes(services, checker, target.nameNode);
|
|
888
|
+
const kind = isCallable ? target.callableKind : target.valueKind;
|
|
889
|
+
const { name } = target.nameNode;
|
|
890
|
+
if (classification === TYPE_CLASSIFICATIONS.BOOL) {
|
|
891
|
+
if (!isBoolishName(name, kind)) ruleContext.report(target.nameNode, MESSAGE_ID_MISSING_PREFIX, {
|
|
892
|
+
kind,
|
|
893
|
+
name,
|
|
894
|
+
prefixes: getPrefixesForKind(kind).join(", ")
|
|
895
|
+
});
|
|
896
|
+
return;
|
|
897
|
+
}
|
|
898
|
+
const reservedToken = classification === TYPE_CLASSIFICATIONS.NON_BOOL ? getReservedToken(name, kind) : void 0;
|
|
899
|
+
if (reservedToken !== void 0) ruleContext.report(target.nameNode, MESSAGE_ID_UNEXPECTED_PREFIX, {
|
|
900
|
+
kind,
|
|
901
|
+
name,
|
|
902
|
+
prefix: reservedToken
|
|
903
|
+
});
|
|
904
|
+
};
|
|
905
|
+
const checkParameters = (ruleContext, node) => {
|
|
906
|
+
if (isSkippedMember(ruleContext, node.parent)) return;
|
|
907
|
+
for (const parameter of node.params) {
|
|
908
|
+
const nameNode = resolveParameterIdentifier(parameter);
|
|
909
|
+
if (nameNode !== void 0) checkTarget(ruleContext, {
|
|
910
|
+
nameNode,
|
|
911
|
+
valueKind: parameter.type === "TSParameterProperty" ? KIND_PROPERTY : KIND_PARAMETER,
|
|
912
|
+
callableKind: KIND_FUNCTION,
|
|
913
|
+
typeAnnotation: nameNode.typeAnnotation,
|
|
914
|
+
initializer: parameter.type === "AssignmentPattern" ? parameter.right : void 0
|
|
915
|
+
});
|
|
916
|
+
}
|
|
917
|
+
};
|
|
918
|
+
const checkClassMember = (ruleContext, node) => {
|
|
919
|
+
const nameNode = resolveNameNode(node.key);
|
|
920
|
+
if (nameNode === void 0 || isSkippedMember(ruleContext, node)) return;
|
|
921
|
+
if (node.type === "PropertyDefinition" || node.type === "TSAbstractPropertyDefinition") {
|
|
922
|
+
checkTarget(ruleContext, {
|
|
923
|
+
nameNode,
|
|
924
|
+
valueKind: KIND_PROPERTY,
|
|
925
|
+
callableKind: KIND_METHOD$1,
|
|
926
|
+
typeAnnotation: node.typeAnnotation,
|
|
927
|
+
initializer: node.value
|
|
928
|
+
});
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
931
|
+
if (node.kind !== "constructor") checkTarget(ruleContext, {
|
|
932
|
+
nameNode,
|
|
933
|
+
valueKind: KIND_PROPERTY,
|
|
934
|
+
callableKind: node.kind === "get" ? KIND_PROPERTY : KIND_METHOD$1,
|
|
935
|
+
callable: node.value
|
|
936
|
+
});
|
|
937
|
+
};
|
|
938
|
+
const collectReturnClassification = (ruleContext, node) => {
|
|
939
|
+
const callable = ruleContext.services === void 0 ? findEnclosingCallable(node) : void 0;
|
|
940
|
+
if (callable === void 0) return;
|
|
941
|
+
ruleContext.returnClassifications.set(callable, [...ruleContext.returnClassifications.get(callable) ?? [], node.argument ? classifyExpression(node.argument) : TYPE_CLASSIFICATIONS.NON_BOOL]);
|
|
942
|
+
};
|
|
943
|
+
const buildVisitors = (ruleContext) => ({
|
|
944
|
+
[PARAMETER_OWNER_SELECTOR]: (node) => {
|
|
945
|
+
checkParameters(ruleContext, node);
|
|
946
|
+
},
|
|
947
|
+
"FunctionDeclaration, TSDeclareFunction:exit": (node) => {
|
|
948
|
+
if (node.id) checkTarget(ruleContext, {
|
|
949
|
+
nameNode: node.id,
|
|
950
|
+
valueKind: KIND_FUNCTION,
|
|
951
|
+
callableKind: KIND_FUNCTION,
|
|
952
|
+
callable: node
|
|
953
|
+
});
|
|
954
|
+
},
|
|
955
|
+
"MethodDefinition, PropertyDefinition, TSAbstractMethodDefinition, TSAbstractPropertyDefinition:exit": (node) => {
|
|
956
|
+
checkClassMember(ruleContext, node);
|
|
957
|
+
},
|
|
958
|
+
"ReturnStatement:exit": (node) => {
|
|
959
|
+
collectReturnClassification(ruleContext, node);
|
|
960
|
+
},
|
|
961
|
+
"TSEnumMember:exit": (node) => {
|
|
962
|
+
const nameNode = resolveNameNode(node.id);
|
|
963
|
+
if (nameNode !== void 0) checkTarget(ruleContext, {
|
|
964
|
+
nameNode,
|
|
965
|
+
valueKind: KIND_CONSTANT,
|
|
966
|
+
callableKind: KIND_CONSTANT,
|
|
967
|
+
initializer: node.initializer
|
|
968
|
+
});
|
|
969
|
+
},
|
|
970
|
+
"TSMethodSignature:exit": (node) => {
|
|
971
|
+
const nameNode = resolveNameNode(node.key);
|
|
972
|
+
if (nameNode !== void 0) checkTarget(ruleContext, {
|
|
973
|
+
nameNode,
|
|
974
|
+
valueKind: KIND_METHOD$1,
|
|
975
|
+
callableKind: KIND_METHOD$1,
|
|
976
|
+
callable: node
|
|
977
|
+
});
|
|
978
|
+
},
|
|
979
|
+
"TSPropertySignature:exit": (node) => {
|
|
980
|
+
const nameNode = resolveNameNode(node.key);
|
|
981
|
+
if (nameNode !== void 0) checkTarget(ruleContext, {
|
|
982
|
+
nameNode,
|
|
983
|
+
valueKind: KIND_PROPERTY,
|
|
984
|
+
callableKind: KIND_METHOD$1,
|
|
985
|
+
typeAnnotation: node.typeAnnotation
|
|
986
|
+
});
|
|
987
|
+
},
|
|
988
|
+
"VariableDeclarator:exit": (node) => {
|
|
989
|
+
if (node.id.type === "Identifier") checkTarget(ruleContext, {
|
|
990
|
+
nameNode: node.id,
|
|
991
|
+
valueKind: KIND_VARIABLE,
|
|
992
|
+
callableKind: KIND_FUNCTION,
|
|
993
|
+
typeAnnotation: node.id.typeAnnotation,
|
|
994
|
+
initializer: node.init
|
|
995
|
+
});
|
|
996
|
+
}
|
|
997
|
+
});
|
|
998
|
+
/**
|
|
999
|
+
* @see https://github.com/brnshkr/config/blob/master/docs/js/eslint/rules/boolish-prefix.md
|
|
1000
|
+
*/
|
|
1001
|
+
const boolishPrefixRule = {
|
|
1002
|
+
meta: {
|
|
1003
|
+
type: "suggestion",
|
|
1004
|
+
docs: {
|
|
1005
|
+
description: "Keep boolean-ness and names aligned in both directions.",
|
|
1006
|
+
url: "https://github.com/brnshkr/config/blob/master/docs/js/eslint/rules/boolish-prefix.md"
|
|
1007
|
+
},
|
|
1008
|
+
messages: {
|
|
1009
|
+
[MESSAGE_ID_MISSING_PREFIX]: "{{ kind }} name `{{ name }}` must have one of the following prefixes: {{ prefixes }}.",
|
|
1010
|
+
[MESSAGE_ID_UNEXPECTED_PREFIX]: "{{ kind }} name `{{ name }}` must not start with the boolish prefix `{{ prefix }}` because it is not boolean."
|
|
1011
|
+
}
|
|
1012
|
+
},
|
|
1013
|
+
create: (context) => {
|
|
1014
|
+
const { parserServices } = context.sourceCode;
|
|
1015
|
+
const services = parserServices;
|
|
1016
|
+
const program = services?.program;
|
|
1017
|
+
return buildVisitors({
|
|
1018
|
+
report: (nameNode, messageId, data) => {
|
|
1019
|
+
context.report({
|
|
1020
|
+
node: nameNode,
|
|
1021
|
+
messageId,
|
|
1022
|
+
data
|
|
1023
|
+
});
|
|
1024
|
+
},
|
|
1025
|
+
services: program === void 0 ? void 0 : services,
|
|
1026
|
+
checker: program?.getTypeChecker(),
|
|
1027
|
+
returnClassifications: /* @__PURE__ */ new Map()
|
|
1028
|
+
});
|
|
1029
|
+
}
|
|
1030
|
+
};
|
|
1031
|
+
//#endregion
|
|
1032
|
+
//#region ../src/js/eslint/configs/builtin/interface-suffix.ts
|
|
1033
|
+
const MESSAGE_ID_MISSING_SUFFIX = "missingSuffix";
|
|
1034
|
+
const getImplementedName = (expression) => {
|
|
1035
|
+
if (expression.type === "Identifier") return expression.name;
|
|
1036
|
+
if (expression.type === "MemberExpression" && expression.property.type === "Identifier") return expression.property.name;
|
|
1037
|
+
return "";
|
|
1038
|
+
};
|
|
1039
|
+
/**
|
|
1040
|
+
* @see https://github.com/brnshkr/config/blob/master/docs/js/eslint/rules/interface-suffix.md
|
|
1041
|
+
*/
|
|
1042
|
+
const interfaceSuffixRule = {
|
|
1043
|
+
meta: {
|
|
1044
|
+
type: "suggestion",
|
|
1045
|
+
docs: {
|
|
1046
|
+
description: "Require classes that implement a single `*Interface` to end with the matching suffix.",
|
|
1047
|
+
url: "https://github.com/brnshkr/config/blob/master/docs/js/eslint/rules/interface-suffix.md"
|
|
1048
|
+
},
|
|
1049
|
+
messages: { [MESSAGE_ID_MISSING_SUFFIX]: "Class `{{ name }}` implements `{{ interfaceName }}` and must end with suffix `{{ suffix }}`." }
|
|
1050
|
+
},
|
|
1051
|
+
create: (context) => {
|
|
1052
|
+
const checkHeritage = (node) => {
|
|
1053
|
+
if (!node.id) return;
|
|
1054
|
+
const implementedNames = node.implements.map((implemented) => getImplementedName(implemented.expression)).filter((name) => name.length > 9 && name.endsWith("Interface"));
|
|
1055
|
+
if (implementedNames.length !== 1) return;
|
|
1056
|
+
const [interfaceName = ""] = implementedNames;
|
|
1057
|
+
const suffix = interfaceName.slice(0, -9);
|
|
1058
|
+
if (node.id.name.endsWith(suffix)) return;
|
|
1059
|
+
context.report({
|
|
1060
|
+
node: node.id,
|
|
1061
|
+
messageId: MESSAGE_ID_MISSING_SUFFIX,
|
|
1062
|
+
data: {
|
|
1063
|
+
name: node.id.name,
|
|
1064
|
+
interfaceName,
|
|
1065
|
+
suffix
|
|
1066
|
+
}
|
|
1067
|
+
});
|
|
1068
|
+
};
|
|
1069
|
+
return {
|
|
1070
|
+
ClassDeclaration: checkHeritage,
|
|
1071
|
+
ClassExpression: checkHeritage
|
|
1072
|
+
};
|
|
1073
|
+
}
|
|
1074
|
+
};
|
|
1075
|
+
//#endregion
|
|
1076
|
+
//#region ../src/js/eslint/configs/builtin/internal-usage.ts
|
|
1077
|
+
/**
|
|
1078
|
+
* @internal @brnshkr/config/eslint
|
|
1079
|
+
*/
|
|
1080
|
+
const MESSAGE_ID_UNEXPECTED_INTERNAL_USAGE = "unexpectedInternalUsage";
|
|
1081
|
+
const MESSAGE_ID_UNEXPECTED_TARGETED_INTERNAL_USAGE = "unexpectedTargetedInternalUsage";
|
|
1082
|
+
const PLAIN_INTERNAL = "@internal";
|
|
1083
|
+
const PUBLIC_API = "@api";
|
|
1084
|
+
const NAMESPACE_SEPARATORS = [
|
|
1085
|
+
"/",
|
|
1086
|
+
"#",
|
|
1087
|
+
"."
|
|
1088
|
+
];
|
|
1089
|
+
const LAYOUT_SEGMENTS = /* @__PURE__ */ new Set([
|
|
1090
|
+
"dist",
|
|
1091
|
+
"js",
|
|
1092
|
+
"lib",
|
|
1093
|
+
"src"
|
|
1094
|
+
]);
|
|
1095
|
+
const WILDCARD_SUFFIX$1 = "/*";
|
|
1096
|
+
const MODULE_SPECIFIER_SEARCH_DEPTH = 4;
|
|
1097
|
+
const DELIMITED_PATTERN = /^(?<delimiter>[^\w\\])(?<source>.*)\k<delimiter>(?<flags>[A-Za-z]*)$/sv;
|
|
1098
|
+
const OPTION_NAMES = ["allowedInternals", "allowedCallers"];
|
|
1099
|
+
const MODULE_EXTENSIONS = [
|
|
1100
|
+
".ts",
|
|
1101
|
+
".tsx",
|
|
1102
|
+
".mts",
|
|
1103
|
+
".cts",
|
|
1104
|
+
".d.ts",
|
|
1105
|
+
".js",
|
|
1106
|
+
".jsx",
|
|
1107
|
+
".mjs",
|
|
1108
|
+
".cjs"
|
|
1109
|
+
];
|
|
1110
|
+
const IMPORT_SPECIFIER_TYPES = /* @__PURE__ */ new Set([
|
|
1111
|
+
"ImportDefaultSpecifier",
|
|
1112
|
+
"ImportNamespaceSpecifier",
|
|
1113
|
+
"ImportSpecifier"
|
|
1114
|
+
]);
|
|
1115
|
+
const packageCache = /* @__PURE__ */ new Map();
|
|
1116
|
+
const packageJsonPathCache = /* @__PURE__ */ new Map();
|
|
1117
|
+
const moduleVisibilityCache = /* @__PURE__ */ new Map();
|
|
1118
|
+
const internalMarkerCache = /* @__PURE__ */ new WeakMap();
|
|
1119
|
+
const fileVisibilityCache = /* @__PURE__ */ new WeakMap();
|
|
1120
|
+
const trimSeparators = (value) => {
|
|
1121
|
+
let start = 0;
|
|
1122
|
+
let end = value.length;
|
|
1123
|
+
while (start < end && value.startsWith("/", start)) start += 1;
|
|
1124
|
+
while (end > start && value.endsWith("/", end)) end -= 1;
|
|
1125
|
+
return value.slice(start, end);
|
|
1126
|
+
};
|
|
1127
|
+
const isInSubtree = (value, prefix) => value === prefix || NAMESPACE_SEPARATORS.some((separator) => value.startsWith(`${prefix}${separator}`));
|
|
1128
|
+
const resolveInternalTarget = (comment) => {
|
|
1129
|
+
const target = /\*\s+@internal(?=\s|$)(?<target>[^\n]*)/v.exec(comment)?.groups?.["target"];
|
|
1130
|
+
if (target === void 0) return;
|
|
1131
|
+
const trimmedTarget = target.replace(/\*\/\s*$/v, "").trim();
|
|
1132
|
+
return /^[\w\-.\/@]+$/v.test(trimmedTarget) ? trimSeparators(trimmedTarget) : PLAIN_INTERNAL;
|
|
1133
|
+
};
|
|
1134
|
+
const findPackageJsonPath = (directory) => {
|
|
1135
|
+
if (packageJsonPathCache.has(directory)) return packageJsonPathCache.get(directory);
|
|
1136
|
+
const packageJsonPath = findNearestPackageJson(directory);
|
|
1137
|
+
packageJsonPathCache.set(directory, packageJsonPath);
|
|
1138
|
+
return packageJsonPath;
|
|
1139
|
+
};
|
|
1140
|
+
const loadPackageIdentity = (directory) => {
|
|
1141
|
+
const packageJsonPath = findPackageJsonPath(directory);
|
|
1142
|
+
if (packageJsonPath === void 0) return;
|
|
1143
|
+
const mtime = getMtime(packageJsonPath);
|
|
1144
|
+
const cacheEntry = packageCache.get(packageJsonPath);
|
|
1145
|
+
if (cacheEntry !== void 0 && cacheEntry.mtime === mtime) return cacheEntry.identity;
|
|
1146
|
+
const declaredName = readJsonObjectFile(packageJsonPath)?.["name"];
|
|
1147
|
+
const packageName = typeof declaredName === "string" ? declaredName : "";
|
|
1148
|
+
const identity = packageName.length === 0 ? void 0 : {
|
|
1149
|
+
packageName,
|
|
1150
|
+
packageRoot: toPosix$1(path.dirname(packageJsonPath))
|
|
1151
|
+
};
|
|
1152
|
+
packageCache.set(packageJsonPath, {
|
|
1153
|
+
mtime,
|
|
1154
|
+
identity
|
|
1155
|
+
});
|
|
1156
|
+
return identity;
|
|
1157
|
+
};
|
|
1158
|
+
const dropModuleExtension = (modulePath) => modulePath.replace(/(?:\.d)?\.[cm]?[jt]sx?$/v, "");
|
|
1159
|
+
const stripLayoutSegments = (modulePath) => {
|
|
1160
|
+
const segments = modulePath.split("/");
|
|
1161
|
+
let start = 0;
|
|
1162
|
+
while (start < segments.length - 1 && LAYOUT_SEGMENTS.has(segments[start] ?? "")) start += 1;
|
|
1163
|
+
return segments.slice(start).join("/");
|
|
1164
|
+
};
|
|
1165
|
+
const toNamespace = (moduleId) => {
|
|
1166
|
+
const lastSeparator = moduleId.lastIndexOf("/");
|
|
1167
|
+
return lastSeparator === -1 ? moduleId : moduleId.slice(0, lastSeparator);
|
|
1168
|
+
};
|
|
1169
|
+
const toAliasModuleId = (pattern, target, absolutePath) => {
|
|
1170
|
+
if (!pattern.endsWith(WILDCARD_SUFFIX$1) || !target.endsWith(WILDCARD_SUFFIX$1)) return dropModuleExtension(target) === dropModuleExtension(absolutePath) ? pattern : void 0;
|
|
1171
|
+
const targetRoot = target.slice(0, -2);
|
|
1172
|
+
if (!absolutePath.startsWith(`${targetRoot}/`)) return;
|
|
1173
|
+
const relativePath = stripLayoutSegments(dropModuleExtension(absolutePath.slice(targetRoot.length + 1)));
|
|
1174
|
+
return `${pattern.slice(0, -2)}/${relativePath}`;
|
|
1175
|
+
};
|
|
1176
|
+
const buildAliasNamespaces = (absolutePath, aliasPaths) => {
|
|
1177
|
+
const namespaces = [];
|
|
1178
|
+
const aliasEntries = objectEntries(aliasPaths ?? {});
|
|
1179
|
+
for (const [pattern, targets] of aliasEntries) for (const target of targets) {
|
|
1180
|
+
const aliasModuleId = toAliasModuleId(pattern, target, absolutePath);
|
|
1181
|
+
if (aliasModuleId !== void 0) namespaces.push(toNamespace(aliasModuleId));
|
|
1182
|
+
}
|
|
1183
|
+
return namespaces;
|
|
1184
|
+
};
|
|
1185
|
+
const buildModuleIdentity = (filePath, aliasPaths) => {
|
|
1186
|
+
const absolutePath = toPosix$1(path.resolve(filePath));
|
|
1187
|
+
const identity = loadPackageIdentity(path.dirname(absolutePath));
|
|
1188
|
+
if (identity === void 0 || !absolutePath.startsWith(`${identity.packageRoot}/`)) return;
|
|
1189
|
+
const relativePath = stripLayoutSegments(dropModuleExtension(absolutePath.slice(identity.packageRoot.length + 1)));
|
|
1190
|
+
const moduleId = `${identity.packageName}/${relativePath}`;
|
|
1191
|
+
const namespace = toNamespace(moduleId);
|
|
1192
|
+
return {
|
|
1193
|
+
moduleId,
|
|
1194
|
+
namespace,
|
|
1195
|
+
namespaces: [namespace, ...buildAliasNamespaces(absolutePath, aliasPaths)]
|
|
1196
|
+
};
|
|
1197
|
+
};
|
|
1198
|
+
const createExportPattern = () => new RegExp(String.raw`^\s*export\s+(?<defaultKeyword>default\s+)?` + String.raw`(?:(?:abstract|async|declare)\s+)*` + String.raw`(?<keyword>class|const|enum|function|interface|let|type|var)\s+` + String.raw`(?<name>[\p{ID_Start}$_][\p{ID_Continue}$]*)`, "v");
|
|
1199
|
+
const readExportName = (following) => {
|
|
1200
|
+
const match = createExportPattern().exec(following);
|
|
1201
|
+
if (!match) return;
|
|
1202
|
+
return match.groups?.["defaultKeyword"] === void 0 ? match.groups?.["name"] : "default";
|
|
1203
|
+
};
|
|
1204
|
+
const hasInternalMarker = (sourceFile) => {
|
|
1205
|
+
if (internalMarkerCache.has(sourceFile)) return internalMarkerCache.get(sourceFile) === true;
|
|
1206
|
+
const hasMarker = sourceFile.text.includes(`@${TAG_INTERNAL}`);
|
|
1207
|
+
internalMarkerCache.set(sourceFile, hasMarker);
|
|
1208
|
+
return hasMarker;
|
|
1209
|
+
};
|
|
1210
|
+
const resolveVisibility = (comment) => resolveInternalTarget(comment) ?? (hasTag(comment, "api") ? PUBLIC_API : void 0);
|
|
1211
|
+
const isCallableExport = (following) => createExportPattern().exec(following)?.groups?.["keyword"] === "function";
|
|
1212
|
+
const startsWithBlankLine = (following) => /^[^\n]*\n[^\S\n]*\n/v.test(following);
|
|
1213
|
+
const isFileLevelComment = (following) => {
|
|
1214
|
+
const trimmed = following.trimStart();
|
|
1215
|
+
const [firstLine = ""] = trimmed.split("\n", 1);
|
|
1216
|
+
return startsWithBlankLine(following) || trimmed.startsWith("/**") || firstLine.startsWith("import ") || firstLine.startsWith("export ") && firstLine.includes(" from ");
|
|
1217
|
+
};
|
|
1218
|
+
const resolveLexicalFileVisibility = (content, docblockMatches) => {
|
|
1219
|
+
const [fileComment] = docblockMatches;
|
|
1220
|
+
if (fileComment === void 0 || !isFileLevelComment(content.slice(fileComment.index + fileComment[0].length))) return;
|
|
1221
|
+
return resolveVisibility(fileComment[0]);
|
|
1222
|
+
};
|
|
1223
|
+
const buildModuleVisibility = (filePath) => {
|
|
1224
|
+
const content = readTextFile(filePath) ?? "";
|
|
1225
|
+
const docblockMatches = content.matchAll(/\/\*\*(?:[^*]|\*(?!\/))*\*\//gv).toArray();
|
|
1226
|
+
const visibilityByExportName = /* @__PURE__ */ new Map();
|
|
1227
|
+
const callableExportNames = /* @__PURE__ */ new Set();
|
|
1228
|
+
for (const match of docblockMatches) {
|
|
1229
|
+
const visibility = resolveVisibility(match[0]);
|
|
1230
|
+
const following = content.slice(match.index + match[0].length);
|
|
1231
|
+
const exportName = readExportName(following);
|
|
1232
|
+
if (exportName === void 0) continue;
|
|
1233
|
+
if (visibility !== void 0) visibilityByExportName.set(exportName, visibility);
|
|
1234
|
+
if (isCallableExport(following)) callableExportNames.add(exportName);
|
|
1235
|
+
}
|
|
1236
|
+
return {
|
|
1237
|
+
fileVisibility: resolveLexicalFileVisibility(content, docblockMatches),
|
|
1238
|
+
visibilityByExportName,
|
|
1239
|
+
callableExportNames
|
|
1240
|
+
};
|
|
1241
|
+
};
|
|
1242
|
+
const loadModuleVisibility = (filePath) => {
|
|
1243
|
+
const mtime = getMtime(filePath);
|
|
1244
|
+
const cacheEntry = moduleVisibilityCache.get(filePath);
|
|
1245
|
+
if (cacheEntry !== void 0 && cacheEntry.mtime === mtime) return cacheEntry.visibility;
|
|
1246
|
+
const visibility = buildModuleVisibility(filePath);
|
|
1247
|
+
moduleVisibilityCache.set(filePath, {
|
|
1248
|
+
mtime,
|
|
1249
|
+
visibility
|
|
1250
|
+
});
|
|
1251
|
+
return visibility;
|
|
1252
|
+
};
|
|
1253
|
+
const resolveRelativeModulePath = (fromFilePath, specifier) => {
|
|
1254
|
+
if (!specifier.startsWith(".")) return;
|
|
1255
|
+
const base = path.resolve(path.dirname(fromFilePath), specifier);
|
|
1256
|
+
return [...MODULE_EXTENSIONS.map((extension) => `${base}${extension}`), ...MODULE_EXTENSIONS.map((extension) => path.join(base, `index${extension}`))].find((candidate) => doesFileExist(candidate));
|
|
1257
|
+
};
|
|
1258
|
+
const toPattern = (entry) => {
|
|
1259
|
+
const groups = DELIMITED_PATTERN.exec(entry)?.groups;
|
|
1260
|
+
if (groups === void 0) return;
|
|
1261
|
+
const source = groups["source"] ?? "";
|
|
1262
|
+
const flags = (groups["flags"] ?? "").replaceAll(/[gy]/gv, "");
|
|
1263
|
+
try {
|
|
1264
|
+
return new RegExp(source, flags);
|
|
1265
|
+
} catch {
|
|
1266
|
+
return;
|
|
1267
|
+
}
|
|
1268
|
+
};
|
|
1269
|
+
const buildMatcher = (optionName, entries) => {
|
|
1270
|
+
const prefixes = [];
|
|
1271
|
+
const patterns = [];
|
|
1272
|
+
for (const entry of entries) {
|
|
1273
|
+
if (entry instanceof RegExp) {
|
|
1274
|
+
patterns.push(new RegExp(entry.source, entry.flags.replaceAll(/[gy]/gv, "")));
|
|
1275
|
+
continue;
|
|
1276
|
+
}
|
|
1277
|
+
const text = typeof entry === "string" ? entry : "";
|
|
1278
|
+
const pattern = toPattern(text);
|
|
1279
|
+
if (pattern !== void 0) {
|
|
1280
|
+
patterns.push(pattern);
|
|
1281
|
+
continue;
|
|
1282
|
+
}
|
|
1283
|
+
const prefix = trimSeparators(text);
|
|
1284
|
+
if (prefix.length === 0 || !/^[\w@]/v.test(prefix)) throw new Error(`Entry "${entry}" for option "${optionName}" is neither a namespace prefix nor a regular expression.`);
|
|
1285
|
+
prefixes.push(prefix);
|
|
1286
|
+
}
|
|
1287
|
+
return {
|
|
1288
|
+
prefixes,
|
|
1289
|
+
patterns
|
|
1290
|
+
};
|
|
1291
|
+
};
|
|
1292
|
+
const isMappedEntry = (entry) => typeof entry === "object" && (entry ?? void 0) !== void 0 && !Array.isArray(entry) && !(entry instanceof RegExp);
|
|
1293
|
+
const buildAllowList = (optionName, entries) => {
|
|
1294
|
+
const bare = [];
|
|
1295
|
+
const bounded = [];
|
|
1296
|
+
const allowEntries = entries ?? [];
|
|
1297
|
+
for (const entry of allowEntries) {
|
|
1298
|
+
if (!isMappedEntry(entry)) {
|
|
1299
|
+
bare.push(entry);
|
|
1300
|
+
continue;
|
|
1301
|
+
}
|
|
1302
|
+
for (const [subject, counterparts] of objectEntries(entry)) {
|
|
1303
|
+
if (!Array.isArray(counterparts)) throw new TypeError(`Entry "${subject}" for option "${optionName}" must map to a list of namespace prefixes or regular expressions.`);
|
|
1304
|
+
bounded.push({
|
|
1305
|
+
subject: buildMatcher(optionName, [subject]),
|
|
1306
|
+
counterparts: buildMatcher(optionName, counterparts)
|
|
1307
|
+
});
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
return {
|
|
1311
|
+
bare: buildMatcher(optionName, bare),
|
|
1312
|
+
bounded
|
|
1313
|
+
};
|
|
1314
|
+
};
|
|
1315
|
+
const matches = (values, matcher) => values.some((value) => matcher.prefixes.some((prefix) => isInSubtree(value, prefix)) || matcher.patterns.some((pattern) => pattern.test(value)));
|
|
1316
|
+
const isAllowedBy = (values, counterparts, allowList) => matches(values, allowList.bare) || allowList.bounded.some((entry) => matches(values, entry.subject) && matches(counterparts, entry.counterparts));
|
|
1317
|
+
const hasModuleSpecifierAbove = (node) => {
|
|
1318
|
+
let current = node;
|
|
1319
|
+
for (let depth = 0; current !== void 0 && depth < MODULE_SPECIFIER_SEARCH_DEPTH; depth += 1) {
|
|
1320
|
+
if (current.moduleSpecifier !== void 0) return true;
|
|
1321
|
+
current = current.parent;
|
|
1322
|
+
}
|
|
1323
|
+
return false;
|
|
1324
|
+
};
|
|
1325
|
+
const findDeclaredVisibility = (declaration) => {
|
|
1326
|
+
let current = declaration;
|
|
1327
|
+
while (current !== void 0) {
|
|
1328
|
+
const commentNodes = (current.jsDoc ?? []).toReversed();
|
|
1329
|
+
for (const commentNode of commentNodes) {
|
|
1330
|
+
const visibility = resolveVisibility(commentNode.getText());
|
|
1331
|
+
if (visibility !== void 0) return visibility;
|
|
1332
|
+
}
|
|
1333
|
+
current = current.parent;
|
|
1334
|
+
}
|
|
1335
|
+
};
|
|
1336
|
+
const isSeparatedByBlankLine = (sourceFile, commentNode, statement) => startsWithBlankLine(sourceFile.text.slice(commentNode.end, statement.getStart()));
|
|
1337
|
+
const findFileLevelComment = (sourceFile) => {
|
|
1338
|
+
const statement = sourceFile.statements?.[0];
|
|
1339
|
+
const commentNodes = statement?.jsDoc ?? [];
|
|
1340
|
+
const [fileCommentNode] = commentNodes;
|
|
1341
|
+
if (statement === void 0 || fileCommentNode === void 0) return;
|
|
1342
|
+
if (commentNodes.length > 1 || statement.moduleSpecifier !== void 0) return fileCommentNode.getText();
|
|
1343
|
+
return isSeparatedByBlankLine(sourceFile, fileCommentNode, statement) ? fileCommentNode.getText() : void 0;
|
|
1344
|
+
};
|
|
1345
|
+
const findFileVisibility = (sourceFile) => {
|
|
1346
|
+
if (fileVisibilityCache.has(sourceFile)) return fileVisibilityCache.get(sourceFile);
|
|
1347
|
+
const fileComment = findFileLevelComment(sourceFile);
|
|
1348
|
+
const visibility = fileComment === void 0 ? void 0 : resolveVisibility(fileComment);
|
|
1349
|
+
fileVisibilityCache.set(sourceFile, visibility);
|
|
1350
|
+
return visibility;
|
|
1351
|
+
};
|
|
1352
|
+
const toInternalTarget = (visibility) => visibility === PUBLIC_API ? void 0 : visibility;
|
|
1353
|
+
const buildSymbolId = (moduleId, symbolName, declaration) => {
|
|
1354
|
+
const names = [symbolName];
|
|
1355
|
+
let current = declaration.parent;
|
|
1356
|
+
while (current !== void 0) {
|
|
1357
|
+
const name = current.name?.getText?.();
|
|
1358
|
+
if (name !== void 0 && name.length > 0) names.push(name);
|
|
1359
|
+
current = current.parent;
|
|
1360
|
+
}
|
|
1361
|
+
return `${moduleId}#${names.toReversed().join(".")}${declaration.parameters === void 0 ? "" : "()"}`;
|
|
1362
|
+
};
|
|
1363
|
+
const isPublicExportName = (node) => "exported" in node.parent && node.parent.exported === node;
|
|
1364
|
+
const getImportedName = (specifier) => {
|
|
1365
|
+
if ("imported" in specifier) return "name" in specifier.imported ? specifier.imported.name : void 0;
|
|
1366
|
+
return specifier.type === "ImportDefaultSpecifier" ? "default" : void 0;
|
|
1367
|
+
};
|
|
1368
|
+
/**
|
|
1369
|
+
* @see https://github.com/brnshkr/config/blob/master/docs/js/eslint/rules/internal-usage.md
|
|
1370
|
+
*/
|
|
1371
|
+
const internalUsageRule = {
|
|
1372
|
+
meta: {
|
|
1373
|
+
type: "problem",
|
|
1374
|
+
docs: {
|
|
1375
|
+
description: "Forbid usage of an `@internal` symbol from outside the namespace it is internal to.",
|
|
1376
|
+
url: "https://github.com/brnshkr/config/blob/master/docs/js/eslint/rules/internal-usage.md"
|
|
1377
|
+
},
|
|
1378
|
+
schema: [{
|
|
1379
|
+
type: "object",
|
|
1380
|
+
additionalProperties: false,
|
|
1381
|
+
properties: {
|
|
1382
|
+
...objectFromEntries(OPTION_NAMES.map((optionName) => [optionName, {
|
|
1383
|
+
type: "array",
|
|
1384
|
+
tsType: "(string | RegExp | Record<string, (string | RegExp)[]>)[]"
|
|
1385
|
+
}])),
|
|
1386
|
+
tsConfigPath: { type: "string" }
|
|
1387
|
+
}
|
|
1388
|
+
}],
|
|
1389
|
+
messages: {
|
|
1390
|
+
[MESSAGE_ID_UNEXPECTED_INTERNAL_USAGE]: "`{{ symbol }}` is internal and must not be used from `{{ caller }}`.",
|
|
1391
|
+
[MESSAGE_ID_UNEXPECTED_TARGETED_INTERNAL_USAGE]: "`{{ symbol }}` is internal to `{{ target }}` and must not be used from `{{ caller }}`."
|
|
1392
|
+
}
|
|
1393
|
+
},
|
|
1394
|
+
create: (context) => {
|
|
1395
|
+
const options = context.options[0] ?? {};
|
|
1396
|
+
const allowLists = {
|
|
1397
|
+
allowedCallers: buildAllowList("allowedCallers", options.allowedCallers),
|
|
1398
|
+
allowedInternals: buildAllowList("allowedInternals", options.allowedInternals)
|
|
1399
|
+
};
|
|
1400
|
+
const sourceCode = context.sourceCode;
|
|
1401
|
+
const services = sourceCode.parserServices;
|
|
1402
|
+
const typeChecker = (services?.program)?.getTypeChecker();
|
|
1403
|
+
const aliasPaths = options.tsConfigPath === void 0 ? void 0 : loadTsConfigPaths(options.tsConfigPath);
|
|
1404
|
+
const resolveIdentity = (filePath) => buildModuleIdentity(filePath, aliasPaths);
|
|
1405
|
+
const callerIdentity = resolveIdentity(context.filename);
|
|
1406
|
+
if (callerIdentity === void 0) return {};
|
|
1407
|
+
const callerNamespace = callerIdentity.namespace;
|
|
1408
|
+
const callerNamespaces = callerIdentity.namespaces;
|
|
1409
|
+
const isReachable = (internal) => {
|
|
1410
|
+
const roots = internal.target === PLAIN_INTERNAL ? internal.namespaces : [internal.target];
|
|
1411
|
+
return callerNamespaces.some((namespace) => roots.some((root) => isInSubtree(namespace, root)));
|
|
1412
|
+
};
|
|
1413
|
+
const isUsageAllowed = (internal) => {
|
|
1414
|
+
const internalValues = [
|
|
1415
|
+
internal.target,
|
|
1416
|
+
...internal.namespaces,
|
|
1417
|
+
internal.symbolId
|
|
1418
|
+
];
|
|
1419
|
+
return isAllowedBy(internalValues, callerNamespaces, allowLists.allowedInternals) || isAllowedBy(callerNamespaces, internalValues, allowLists.allowedCallers) || isReachable(internal);
|
|
1420
|
+
};
|
|
1421
|
+
const reportUsage = (node, internal) => {
|
|
1422
|
+
if (isUsageAllowed(internal)) return;
|
|
1423
|
+
context.report({
|
|
1424
|
+
node,
|
|
1425
|
+
messageId: internal.target === PLAIN_INTERNAL ? MESSAGE_ID_UNEXPECTED_INTERNAL_USAGE : MESSAGE_ID_UNEXPECTED_TARGETED_INTERNAL_USAGE,
|
|
1426
|
+
data: {
|
|
1427
|
+
caller: callerNamespace,
|
|
1428
|
+
symbol: internal.symbolId,
|
|
1429
|
+
target: internal.target
|
|
1430
|
+
}
|
|
1431
|
+
});
|
|
1432
|
+
};
|
|
1433
|
+
const buildInternalSymbol = (identity, symbolId, visibility) => {
|
|
1434
|
+
const target = toInternalTarget(visibility);
|
|
1435
|
+
return target === void 0 ? void 0 : {
|
|
1436
|
+
namespace: identity.namespace,
|
|
1437
|
+
namespaces: identity.namespaces,
|
|
1438
|
+
symbolId,
|
|
1439
|
+
target
|
|
1440
|
+
};
|
|
1441
|
+
};
|
|
1442
|
+
const checkResolvedSymbol = (node, symbol, currentSourceFile) => {
|
|
1443
|
+
const declaration = symbol.declarations?.[0];
|
|
1444
|
+
if (declaration === void 0) return;
|
|
1445
|
+
const sourceFile = declaration.getSourceFile();
|
|
1446
|
+
if (sourceFile === currentSourceFile || !hasInternalMarker(sourceFile)) return;
|
|
1447
|
+
const target = toInternalTarget(findDeclaredVisibility(declaration) ?? findFileVisibility(sourceFile));
|
|
1448
|
+
const identity = target === void 0 ? void 0 : resolveIdentity(sourceFile.fileName);
|
|
1449
|
+
if (identity === void 0 || target === void 0) return;
|
|
1450
|
+
reportUsage(node, {
|
|
1451
|
+
namespace: identity.namespace,
|
|
1452
|
+
namespaces: identity.namespaces,
|
|
1453
|
+
symbolId: buildSymbolId(identity.moduleId, symbol.getName(), declaration),
|
|
1454
|
+
target
|
|
1455
|
+
});
|
|
1456
|
+
};
|
|
1457
|
+
const checkTypedIdentifier = (node, checker) => {
|
|
1458
|
+
const tsNode = services?.esTreeNodeToTSNodeMap.get(node);
|
|
1459
|
+
const symbol = tsNode === void 0 ? void 0 : checker.getSymbolAtLocation(tsNode);
|
|
1460
|
+
const declaration = symbol?.declarations?.[0];
|
|
1461
|
+
if (tsNode === void 0 || symbol === void 0 || declaration === void 0) return;
|
|
1462
|
+
checkResolvedSymbol(node, hasModuleSpecifierAbove(declaration) ? checker.getAliasedSymbol(symbol) : symbol, tsNode.getSourceFile());
|
|
1463
|
+
};
|
|
1464
|
+
const checkTypedModule = (node, checker) => {
|
|
1465
|
+
const tsNode = services?.esTreeNodeToTSNodeMap.get(node);
|
|
1466
|
+
const sourceFile = (tsNode === void 0 ? void 0 : checker.getSymbolAtLocation(tsNode))?.declarations?.[0];
|
|
1467
|
+
if (sourceFile === void 0 || !hasInternalMarker(sourceFile)) return;
|
|
1468
|
+
const identity = resolveIdentity(sourceFile.fileName);
|
|
1469
|
+
const internal = identity === void 0 ? void 0 : buildInternalSymbol(identity, identity.moduleId, findFileVisibility(sourceFile));
|
|
1470
|
+
if (internal !== void 0) reportUsage(node, internal);
|
|
1471
|
+
};
|
|
1472
|
+
const resolveScannedSymbol = (specifier, importedName) => {
|
|
1473
|
+
const modulePath = resolveRelativeModulePath(context.filename, specifier);
|
|
1474
|
+
const identity = modulePath === void 0 ? void 0 : resolveIdentity(modulePath);
|
|
1475
|
+
if (modulePath === void 0 || identity === void 0) return;
|
|
1476
|
+
const moduleVisibility = loadModuleVisibility(modulePath);
|
|
1477
|
+
return buildInternalSymbol(identity, importedName === void 0 ? identity.moduleId : `${identity.moduleId}#${importedName}${moduleVisibility.callableExportNames.has(importedName) ? "()" : ""}`, (importedName === void 0 ? void 0 : moduleVisibility.visibilityByExportName.get(importedName)) ?? moduleVisibility.fileVisibility);
|
|
1478
|
+
};
|
|
1479
|
+
const checkScannedImport = (node) => {
|
|
1480
|
+
for (const specifier of node.specifiers) {
|
|
1481
|
+
const internal = resolveScannedSymbol(node.source.value, getImportedName(specifier));
|
|
1482
|
+
if (internal === void 0) continue;
|
|
1483
|
+
const references = sourceCode.getDeclaredVariables(node).filter((variable) => variable.name === specifier.local.name).flatMap((variable) => variable.references);
|
|
1484
|
+
for (const reference of references) reportUsage(reference.identifier, internal);
|
|
1485
|
+
}
|
|
1486
|
+
};
|
|
1487
|
+
const checkScannedReexport = (node) => {
|
|
1488
|
+
for (const specifier of node.specifiers) {
|
|
1489
|
+
const localName = "name" in specifier.local ? specifier.local.name : void 0;
|
|
1490
|
+
const internal = node.source ? resolveScannedSymbol(node.source.value, localName) : void 0;
|
|
1491
|
+
if (internal !== void 0) reportUsage(specifier, internal);
|
|
1492
|
+
}
|
|
1493
|
+
};
|
|
1494
|
+
if (services === void 0 || typeChecker === void 0) return {
|
|
1495
|
+
ExportAllDeclaration: (node) => {
|
|
1496
|
+
const internal = resolveScannedSymbol(node.source.value);
|
|
1497
|
+
if (internal !== void 0) reportUsage(node, internal);
|
|
1498
|
+
},
|
|
1499
|
+
ExportNamedDeclaration: (node) => {
|
|
1500
|
+
checkScannedReexport(node);
|
|
1501
|
+
},
|
|
1502
|
+
ImportDeclaration: (node) => {
|
|
1503
|
+
checkScannedImport(node);
|
|
1504
|
+
},
|
|
1505
|
+
ImportExpression: (node) => {
|
|
1506
|
+
const specifier = "value" in node.source && typeof node.source.value === "string" ? node.source.value : void 0;
|
|
1507
|
+
const internal = specifier === void 0 ? void 0 : resolveScannedSymbol(specifier);
|
|
1508
|
+
if (internal !== void 0) reportUsage(node, internal);
|
|
1509
|
+
}
|
|
1510
|
+
};
|
|
1511
|
+
return {
|
|
1512
|
+
ExportAllDeclaration: (node) => {
|
|
1513
|
+
checkTypedModule(node.source, typeChecker);
|
|
1514
|
+
},
|
|
1515
|
+
Identifier: (node) => {
|
|
1516
|
+
if (!IMPORT_SPECIFIER_TYPES.has(node.parent.type) && !isPublicExportName(node)) checkTypedIdentifier(node, typeChecker);
|
|
1517
|
+
},
|
|
1518
|
+
ImportExpression: (node) => {
|
|
1519
|
+
checkTypedModule(node.source, typeChecker);
|
|
1520
|
+
}
|
|
1521
|
+
};
|
|
1522
|
+
}
|
|
1523
|
+
};
|
|
1524
|
+
//#endregion
|
|
1525
|
+
//#region ../src/js/eslint/configs/builtin/public-api-documentation.ts
|
|
1526
|
+
/**
|
|
1527
|
+
* @internal @brnshkr/config/eslint
|
|
1528
|
+
*/
|
|
1529
|
+
const MESSAGE_ID_MISSING_DESCRIPTION = "missingDescription";
|
|
1530
|
+
const MESSAGE_ID_MISSING_PARAM = "missingParam";
|
|
1531
|
+
const MESSAGE_ID_MISSING_RETURNS = "missingReturns";
|
|
1532
|
+
const MESSAGE_ID_MISSING_EXAMPLE = "missingExample";
|
|
1533
|
+
const KIND_METHOD = "Method";
|
|
1534
|
+
const KIND_CONSTRUCTOR = "Constructor";
|
|
1535
|
+
const readCommentText = (declaration) => {
|
|
1536
|
+
const [comment] = (declaration?.jsDoc ?? []).toReversed();
|
|
1537
|
+
return comment?.getText();
|
|
1538
|
+
};
|
|
1539
|
+
const hasInheritDocTag = (comment) => /\*\s+@inheritdoc\b/iv.test(comment ?? "");
|
|
1540
|
+
const isDocumentedByAncestor = (services, node, memberName) => {
|
|
1541
|
+
const program = services?.program;
|
|
1542
|
+
const declaration = services?.esTreeNodeToTSNodeMap.get(node);
|
|
1543
|
+
if (!program || declaration?.name === void 0) return false;
|
|
1544
|
+
const checker = program.getTypeChecker();
|
|
1545
|
+
return (declaration.heritageClauses ?? []).some((clause) => clause.types.some((typeNode) => hasDescription(readCommentText(checker.getTypeAtLocation(typeNode).getProperty(memberName)?.declarations?.[0]))));
|
|
1546
|
+
};
|
|
1547
|
+
const reportMissingDescription = (ruleContext, anchor, kind, name) => {
|
|
1548
|
+
ruleContext.context.report({
|
|
1549
|
+
node: anchor,
|
|
1550
|
+
messageId: MESSAGE_ID_MISSING_DESCRIPTION,
|
|
1551
|
+
data: {
|
|
1552
|
+
kind,
|
|
1553
|
+
name
|
|
1554
|
+
}
|
|
1555
|
+
});
|
|
1556
|
+
};
|
|
1557
|
+
const checkFunctionParameters = (ruleContext, functionLike, text) => {
|
|
1558
|
+
for (const parameter of functionLike.parameters) {
|
|
1559
|
+
const parameterName = getParameterName(parameter);
|
|
1560
|
+
if (parameterName !== void 0 && !hasParameterProse(text, parameterName)) ruleContext.context.report({
|
|
1561
|
+
node: functionLike.anchor,
|
|
1562
|
+
messageId: MESSAGE_ID_MISSING_PARAM,
|
|
1563
|
+
data: {
|
|
1564
|
+
kind: functionLike.kind,
|
|
1565
|
+
name: functionLike.name,
|
|
1566
|
+
parameterName
|
|
1567
|
+
}
|
|
1568
|
+
});
|
|
1569
|
+
}
|
|
1570
|
+
};
|
|
1571
|
+
const checkFunctionReturns = (ruleContext, functionLike, text, isFluent) => {
|
|
1572
|
+
if (isFluent || isVoidLikeReturn(functionLike.returnType) || hasReturnsWithProse(text)) return;
|
|
1573
|
+
ruleContext.context.report({
|
|
1574
|
+
node: functionLike.anchor,
|
|
1575
|
+
messageId: MESSAGE_ID_MISSING_RETURNS,
|
|
1576
|
+
data: {
|
|
1577
|
+
kind: functionLike.kind,
|
|
1578
|
+
name: functionLike.name
|
|
1579
|
+
}
|
|
1580
|
+
});
|
|
1581
|
+
};
|
|
1582
|
+
const checkFunctionExample = (ruleContext, functionLike, text, isFluent) => {
|
|
1583
|
+
if (isFluent || functionLike.parameters.length === 0 || functionLike.isAbstract === true || hasTag(text, "example")) return;
|
|
1584
|
+
ruleContext.context.report({
|
|
1585
|
+
node: functionLike.anchor,
|
|
1586
|
+
messageId: MESSAGE_ID_MISSING_EXAMPLE,
|
|
1587
|
+
data: {
|
|
1588
|
+
kind: functionLike.kind,
|
|
1589
|
+
name: functionLike.name
|
|
1590
|
+
}
|
|
1591
|
+
});
|
|
1592
|
+
};
|
|
1593
|
+
const checkFunctionLike = (ruleContext, functionLike) => {
|
|
1594
|
+
const text = functionLike.comment ?? "";
|
|
1595
|
+
if (functionLike.kind !== KIND_CONSTRUCTOR && !hasDescription(functionLike.comment)) reportMissingDescription(ruleContext, functionLike.anchor, functionLike.kind, functionLike.name);
|
|
1596
|
+
checkFunctionParameters(ruleContext, functionLike, text);
|
|
1597
|
+
const isFluent = isFluentReturn(functionLike.returnType);
|
|
1598
|
+
checkFunctionReturns(ruleContext, functionLike, text, isFluent);
|
|
1599
|
+
checkFunctionExample(ruleContext, functionLike, text, isFluent);
|
|
1600
|
+
};
|
|
1601
|
+
const checkClassMembers = (ruleContext, node) => {
|
|
1602
|
+
for (const member of node.body.body) {
|
|
1603
|
+
if (!isClassMethodLike(member) || !isAccessibleMethod(member)) continue;
|
|
1604
|
+
const methodComment = extractBlockComment(ruleContext.sourceCode.getCommentsBefore(member), member);
|
|
1605
|
+
if (getEffectiveVisibilityTag(methodComment, ruleContext.fileComment) === "internal" || hasInheritDocTag(methodComment) || isDocumentedByAncestor(ruleContext.services, node, getNamedKeyText(member.key))) continue;
|
|
1606
|
+
checkFunctionLike(ruleContext, {
|
|
1607
|
+
anchor: member,
|
|
1608
|
+
kind: member.kind === "constructor" ? KIND_CONSTRUCTOR : KIND_METHOD,
|
|
1609
|
+
name: getNamedKeyText(member.key),
|
|
1610
|
+
comment: methodComment,
|
|
1611
|
+
parameters: member.value.params,
|
|
1612
|
+
returnType: member.value.returnType,
|
|
1613
|
+
isAbstract: isAbstractMethod(member)
|
|
1614
|
+
});
|
|
1615
|
+
}
|
|
1616
|
+
};
|
|
1617
|
+
const checkInterfaceMembers = (ruleContext, node) => {
|
|
1618
|
+
for (const member of node.body.body) {
|
|
1619
|
+
if (member.type !== "TSMethodSignature") continue;
|
|
1620
|
+
const methodComment = extractBlockComment(ruleContext.sourceCode.getCommentsBefore(member), member);
|
|
1621
|
+
if (getEffectiveVisibilityTag(methodComment, ruleContext.fileComment) === "internal" || hasInheritDocTag(methodComment) || isDocumentedByAncestor(ruleContext.services, node, getNamedKeyText(member.key))) continue;
|
|
1622
|
+
checkFunctionLike(ruleContext, {
|
|
1623
|
+
anchor: member,
|
|
1624
|
+
kind: KIND_METHOD,
|
|
1625
|
+
name: getNamedKeyText(member.key),
|
|
1626
|
+
comment: methodComment,
|
|
1627
|
+
parameters: member.params,
|
|
1628
|
+
returnType: member.returnType,
|
|
1629
|
+
isAbstract: true
|
|
1630
|
+
});
|
|
1631
|
+
}
|
|
1632
|
+
};
|
|
1633
|
+
const checkSymbol = (ruleContext, symbol) => {
|
|
1634
|
+
if (symbol.kind === "Function") {
|
|
1635
|
+
const shape = resolveFunctionShape(symbol.declaration);
|
|
1636
|
+
if (shape === void 0) return;
|
|
1637
|
+
checkFunctionLike(ruleContext, {
|
|
1638
|
+
anchor: symbol.anchor,
|
|
1639
|
+
kind: symbol.kind,
|
|
1640
|
+
name: symbol.name,
|
|
1641
|
+
comment: symbol.comment,
|
|
1642
|
+
parameters: shape.parameters,
|
|
1643
|
+
returnType: shape.returnType
|
|
1644
|
+
});
|
|
1645
|
+
return;
|
|
1646
|
+
}
|
|
1647
|
+
if (!hasDescription(symbol.comment)) reportMissingDescription(ruleContext, symbol.anchor, symbol.kind, symbol.name);
|
|
1648
|
+
if (symbol.kind === "Class") checkClassMembers(ruleContext, symbol.declaration);
|
|
1649
|
+
else if (symbol.kind === "Interface") checkInterfaceMembers(ruleContext, symbol.declaration);
|
|
1650
|
+
};
|
|
1651
|
+
/**
|
|
1652
|
+
* @see https://github.com/brnshkr/config/blob/master/docs/js/eslint/rules/public-api-documentation.md
|
|
1653
|
+
*/
|
|
1654
|
+
const publicApiDocumentationRule = {
|
|
1655
|
+
meta: {
|
|
1656
|
+
type: "suggestion",
|
|
1657
|
+
docs: {
|
|
1658
|
+
description: "Hold every `@api` symbol in a public-API source file to a consistent docblock standard.",
|
|
1659
|
+
url: "https://github.com/brnshkr/config/blob/master/docs/js/eslint/rules/public-api-documentation.md"
|
|
1660
|
+
},
|
|
1661
|
+
schema: [{
|
|
1662
|
+
type: "object",
|
|
1663
|
+
additionalProperties: false,
|
|
1664
|
+
properties: {
|
|
1665
|
+
packageJsonPath: { type: "string" },
|
|
1666
|
+
distRoot: { type: "string" },
|
|
1667
|
+
srcRoot: { type: "string" },
|
|
1668
|
+
srcExtensions: {
|
|
1669
|
+
type: "array",
|
|
1670
|
+
items: { type: "string" }
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
}],
|
|
1674
|
+
messages: {
|
|
1675
|
+
[MESSAGE_ID_MISSING_DESCRIPTION]: "{{ kind }} `{{ name }}` is `@api` and must carry a description before the first JSDoc tag.",
|
|
1676
|
+
[MESSAGE_ID_MISSING_PARAM]: "{{ kind }} `{{ name }}` is `@api`; parameter `{{ parameterName }}` must have a `@param` tag with a description.",
|
|
1677
|
+
[MESSAGE_ID_MISSING_RETURNS]: "{{ kind }} `{{ name }}` is `@api` and returns a non-void type; a `@returns` tag with a description is required.",
|
|
1678
|
+
[MESSAGE_ID_MISSING_EXAMPLE]: "{{ kind }} `{{ name }}` is `@api` and accepts parameters; an `@example` tag is required."
|
|
1679
|
+
}
|
|
1680
|
+
},
|
|
1681
|
+
create: (context) => {
|
|
1682
|
+
const options = context.options[0] ?? {};
|
|
1683
|
+
if (!isPublicApiFile(options, context.cwd, context.filename)) return {};
|
|
1684
|
+
const sourceCode = context.sourceCode;
|
|
1685
|
+
const fileComment = getFileLevelBlockComment(sourceCode);
|
|
1686
|
+
const ruleContext = {
|
|
1687
|
+
context,
|
|
1688
|
+
sourceCode,
|
|
1689
|
+
fileComment,
|
|
1690
|
+
services: sourceCode.parserServices
|
|
1691
|
+
};
|
|
1692
|
+
return buildExportVisitors(sourceCode, (symbol) => {
|
|
1693
|
+
if (getEffectiveVisibilityTag(symbol.comment, fileComment) !== "api") return;
|
|
1694
|
+
checkSymbol(ruleContext, symbol);
|
|
1695
|
+
});
|
|
1696
|
+
}
|
|
1697
|
+
};
|
|
1698
|
+
//#endregion
|
|
194
1699
|
//#region ../src/js/eslint/configs/builtin/require-import-alias.ts
|
|
195
|
-
|
|
1700
|
+
/**
|
|
1701
|
+
* @internal @brnshkr/config/eslint
|
|
1702
|
+
*/
|
|
1703
|
+
const MESSAGE_ID_EXPECTED_ALIAS = "expectedAlias";
|
|
196
1704
|
const MESSAGE_ID_MISSING_ALIAS = "missingAlias";
|
|
197
1705
|
const WILDCARD_SUFFIX = "/*";
|
|
198
|
-
const WILDCARD_SUFFIX_LENGTH = 2;
|
|
199
1706
|
const RELATIVE_SPECIFIER_PREFIXES = ["./", "../"];
|
|
200
1707
|
const resolveAliases = (options) => options.aliases ?? loadTsConfigPaths(options.tsConfigPath ?? resolveTsConfigPath()) ?? {};
|
|
201
|
-
const toPosix = (value) => value.replaceAll("\\", "/");
|
|
202
1708
|
const buildAliasMappings = (aliases) => objectEntries(aliases).filter(([pattern]) => pattern.endsWith(WILDCARD_SUFFIX)).flatMap(([pattern, targets]) => {
|
|
203
|
-
const prefix = pattern.slice(0, -
|
|
1709
|
+
const prefix = pattern.slice(0, -2);
|
|
204
1710
|
return targets.filter((target) => target.endsWith(WILDCARD_SUFFIX)).map((target) => ({
|
|
205
1711
|
prefix,
|
|
206
|
-
baseDirectory: toPosix(target).slice(0, -
|
|
1712
|
+
baseDirectory: toPosix$1(target).slice(0, -2)
|
|
207
1713
|
}));
|
|
208
|
-
})
|
|
209
|
-
const
|
|
1714
|
+
});
|
|
1715
|
+
const buildAliasedSpecifier = ({ prefix, baseDirectory }, absolutePath) => {
|
|
1716
|
+
if (absolutePath === baseDirectory) return prefix;
|
|
1717
|
+
return absolutePath.startsWith(`${baseDirectory}/`) ? `${prefix}/${absolutePath.slice(baseDirectory.length + 1)}` : void 0;
|
|
1718
|
+
};
|
|
1719
|
+
const compareSpecifiers = (left, right) => {
|
|
1720
|
+
const segmentDifference = left.split("/").length - right.split("/").length;
|
|
1721
|
+
if (segmentDifference !== 0) return segmentDifference;
|
|
1722
|
+
const lengthDifference = left.length - right.length;
|
|
1723
|
+
return lengthDifference === 0 ? left.localeCompare(right) : lengthDifference;
|
|
1724
|
+
};
|
|
1725
|
+
const findAliasReplacement = (mappings, absolutePath) => mappings.map((mapping) => buildAliasedSpecifier(mapping, absolutePath)).filter((specifier) => specifier !== void 0).toSorted(compareSpecifiers)[0];
|
|
1726
|
+
const resolveAliasedPath = (mappings, source) => {
|
|
210
1727
|
for (const { prefix, baseDirectory } of mappings) {
|
|
211
|
-
if (
|
|
212
|
-
if (
|
|
1728
|
+
if (source === prefix) return baseDirectory;
|
|
1729
|
+
if (source.startsWith(`${prefix}/`)) return `${baseDirectory}/${source.slice(prefix.length + 1)}`;
|
|
213
1730
|
}
|
|
214
1731
|
};
|
|
215
1732
|
const isRelativeSpecifier = (source) => RELATIVE_SPECIFIER_PREFIXES.some((prefix) => source.startsWith(prefix));
|
|
216
|
-
const
|
|
217
|
-
const
|
|
1733
|
+
const isFileIgnored = (filename, patterns) => {
|
|
1734
|
+
const absoluteFilename = toPosix$1(filename);
|
|
1735
|
+
const relativeFilename = toPosix$1(path.relative(process.cwd(), filename));
|
|
1736
|
+
return patterns.some((pattern) => path.matchesGlob(absoluteFilename, pattern) || path.matchesGlob(relativeFilename, pattern));
|
|
1737
|
+
};
|
|
218
1738
|
const getQuote = (sourceNode) => "raw" in sourceNode && typeof sourceNode.raw === "string" && sourceNode.raw.startsWith("\"") ? "\"" : "'";
|
|
1739
|
+
/**
|
|
1740
|
+
* @see https://github.com/brnshkr/config/blob/master/docs/js/eslint/rules/require-import-alias.md
|
|
1741
|
+
*/
|
|
219
1742
|
const requireImportAliasRule = {
|
|
220
1743
|
meta: {
|
|
221
1744
|
type: "suggestion",
|
|
222
1745
|
fixable: "code",
|
|
223
|
-
docs: {
|
|
1746
|
+
docs: {
|
|
1747
|
+
description: "Require imports to use the TypeScript path alias with the fewest path segments when the target file is reachable through one.",
|
|
1748
|
+
url: "https://github.com/brnshkr/config/blob/master/docs/js/eslint/rules/require-import-alias.md"
|
|
1749
|
+
},
|
|
224
1750
|
schema: [{
|
|
225
1751
|
type: "object",
|
|
226
1752
|
additionalProperties: false,
|
|
@@ -240,7 +1766,7 @@ const requireImportAliasRule = {
|
|
|
240
1766
|
}
|
|
241
1767
|
}],
|
|
242
1768
|
messages: {
|
|
243
|
-
[
|
|
1769
|
+
[MESSAGE_ID_EXPECTED_ALIAS]: "Import path '{{ source }}' must use the configured alias '{{ alias }}'.",
|
|
244
1770
|
[MESSAGE_ID_MISSING_ALIAS]: "Import path '{{ source }}' resolves outside any configured TypeScript path alias. Add an alias for this location, remove all other aliases, or disable this rule."
|
|
245
1771
|
}
|
|
246
1772
|
},
|
|
@@ -249,12 +1775,14 @@ const requireImportAliasRule = {
|
|
|
249
1775
|
const ignoredPaths = options.ignoredPaths ?? [];
|
|
250
1776
|
const mappings = buildAliasMappings(resolveAliases(options));
|
|
251
1777
|
if (mappings.length === 0 || isFileIgnored(context.filename, ignoredPaths)) return {};
|
|
252
|
-
const fileDirectory = toPosix(path.dirname(context.filename));
|
|
1778
|
+
const fileDirectory = toPosix$1(path.dirname(context.filename));
|
|
253
1779
|
const checkSource = (sourceNode) => {
|
|
254
1780
|
if (sourceNode?.type !== "Literal" || typeof sourceNode.value !== "string") return;
|
|
255
1781
|
const source = sourceNode.value;
|
|
256
|
-
|
|
257
|
-
|
|
1782
|
+
const absolutePath = isRelativeSpecifier(source) ? path.posix.normalize(`${fileDirectory}/${source}`) : resolveAliasedPath(mappings, source);
|
|
1783
|
+
if (absolutePath === void 0) return;
|
|
1784
|
+
const replacement = findAliasReplacement(mappings, absolutePath);
|
|
1785
|
+
if (replacement === source) return;
|
|
258
1786
|
if (replacement === void 0) {
|
|
259
1787
|
context.report({
|
|
260
1788
|
node: sourceNode,
|
|
@@ -265,7 +1793,7 @@ const requireImportAliasRule = {
|
|
|
265
1793
|
}
|
|
266
1794
|
context.report({
|
|
267
1795
|
node: sourceNode,
|
|
268
|
-
messageId:
|
|
1796
|
+
messageId: MESSAGE_ID_EXPECTED_ALIAS,
|
|
269
1797
|
data: {
|
|
270
1798
|
source,
|
|
271
1799
|
alias: replacement
|
|
@@ -293,7 +1821,7 @@ const requireImportAliasRule = {
|
|
|
293
1821
|
//#region ../src/js/eslint/configs/builtin/require-import-attributes.ts
|
|
294
1822
|
const MESSAGE_ID_MISSING_WITH_KEYWORD = "missingWithKeyword";
|
|
295
1823
|
const MESSAGE_ID_MISSING_TYPE_PROPERTY = "missingTypeProperty";
|
|
296
|
-
const
|
|
1824
|
+
const MESSAGE_ID_UNEXPECTED_TYPE_VALUE = "unexpectedTypeValue";
|
|
297
1825
|
const FILE_TYPE_MAP = {
|
|
298
1826
|
".json": "json",
|
|
299
1827
|
".css": "css",
|
|
@@ -305,78 +1833,330 @@ const FILE_TYPE_MAP = {
|
|
|
305
1833
|
".oct": "bytes",
|
|
306
1834
|
".wasm": "webassembly"
|
|
307
1835
|
};
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
[MESSAGE_ID_MISSING_TYPE_PROPERTY]: "Import attributes for non-JavaScript imports must include the 'type' property.",
|
|
318
|
-
[MESSAGE_ID_WRONG_TYPE_VALUE]: "Import attribute 'type' for '{{ file }}' must be '{{ expectedValue }}'."
|
|
319
|
-
}
|
|
1836
|
+
/**
|
|
1837
|
+
* @see https://github.com/brnshkr/config/blob/master/docs/js/eslint/rules/require-import-attributes.md
|
|
1838
|
+
*/
|
|
1839
|
+
const requireImportAttributesRule = {
|
|
1840
|
+
meta: {
|
|
1841
|
+
type: "problem",
|
|
1842
|
+
docs: {
|
|
1843
|
+
description: "Require non-JavaScript imports (e.g. .json and .css) to include import attributes.",
|
|
1844
|
+
url: "https://github.com/brnshkr/config/blob/master/docs/js/eslint/rules/require-import-attributes.md"
|
|
320
1845
|
},
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
});
|
|
338
|
-
return;
|
|
339
|
-
}
|
|
340
|
-
const typeProperty = attributes.find((attribute) => attribute.key.type === "Identifier" && "name" in attribute.key && attribute.key.name === "type" || attribute.key.type === "Literal" && "value" in attribute.key && attribute.key.value === "type");
|
|
341
|
-
if (!typeProperty) {
|
|
342
|
-
context.report({
|
|
343
|
-
node,
|
|
344
|
-
messageId: MESSAGE_ID_MISSING_TYPE_PROPERTY
|
|
345
|
-
});
|
|
346
|
-
return;
|
|
347
|
-
}
|
|
348
|
-
if (typeProperty.value.value !== expectedValue) context.report({
|
|
1846
|
+
messages: {
|
|
1847
|
+
[MESSAGE_ID_MISSING_WITH_KEYWORD]: "Non-JavaScript import ('{{ extension }}') requires an import attributes object with the 'type' property set to '{{ expectedValue }}'.",
|
|
1848
|
+
[MESSAGE_ID_MISSING_TYPE_PROPERTY]: "Import attributes for non-JavaScript imports must include the 'type' property.",
|
|
1849
|
+
[MESSAGE_ID_UNEXPECTED_TYPE_VALUE]: "Import attribute 'type' for '{{ file }}' must be '{{ expectedValue }}'."
|
|
1850
|
+
}
|
|
1851
|
+
},
|
|
1852
|
+
create: (context) => ({ ImportDeclaration: (node) => {
|
|
1853
|
+
const sourceValue = node.source.value;
|
|
1854
|
+
const extensionIndex = sourceValue.lastIndexOf(".");
|
|
1855
|
+
if (extensionIndex === -1) return;
|
|
1856
|
+
const extension = sourceValue.slice(extensionIndex).toLowerCase();
|
|
1857
|
+
const expectedValue = FILE_TYPE_MAP[extension];
|
|
1858
|
+
if (expectedValue === void 0) return;
|
|
1859
|
+
const { attributes } = node;
|
|
1860
|
+
if (attributes.length === 0) {
|
|
1861
|
+
context.report({
|
|
349
1862
|
node,
|
|
350
|
-
messageId:
|
|
1863
|
+
messageId: MESSAGE_ID_MISSING_WITH_KEYWORD,
|
|
351
1864
|
data: {
|
|
352
|
-
|
|
353
|
-
|
|
1865
|
+
extension,
|
|
1866
|
+
expectedValue
|
|
354
1867
|
}
|
|
355
1868
|
});
|
|
356
|
-
|
|
1869
|
+
return;
|
|
1870
|
+
}
|
|
1871
|
+
const typeProperty = attributes.find((attribute) => attribute.key.type === "Identifier" && "name" in attribute.key && attribute.key.name === "type" || attribute.key.type === "Literal" && "value" in attribute.key && attribute.key.value === "type");
|
|
1872
|
+
if (!typeProperty) {
|
|
1873
|
+
context.report({
|
|
1874
|
+
node,
|
|
1875
|
+
messageId: MESSAGE_ID_MISSING_TYPE_PROPERTY
|
|
1876
|
+
});
|
|
1877
|
+
return;
|
|
1878
|
+
}
|
|
1879
|
+
if (typeProperty.value.value !== expectedValue) context.report({
|
|
1880
|
+
node,
|
|
1881
|
+
messageId: MESSAGE_ID_UNEXPECTED_TYPE_VALUE,
|
|
1882
|
+
data: {
|
|
1883
|
+
expectedValue,
|
|
1884
|
+
file: sourceValue
|
|
1885
|
+
}
|
|
1886
|
+
});
|
|
1887
|
+
} })
|
|
1888
|
+
};
|
|
1889
|
+
//#endregion
|
|
1890
|
+
//#region ../src/js/eslint/configs/builtin/resolvable-doc-reference.ts
|
|
1891
|
+
/**
|
|
1892
|
+
* @internal @brnshkr/config/eslint
|
|
1893
|
+
*/
|
|
1894
|
+
const MESSAGE_ID_MISSING_REFERENCE = "missingReference";
|
|
1895
|
+
const createReferencePattern = () => /(?:\{@|\*\s+@)(?:link(?:code|plain)?|see)\s+(?<target>[^\s\}]+)/gv;
|
|
1896
|
+
const startsUpperCase = (value) => /\p{Uppercase_Letter}/v.test(value.slice(0, 1));
|
|
1897
|
+
const isCheckableTarget = (target) => !target.includes("/") && !target.includes("~") && !target.includes(":");
|
|
1898
|
+
const startsContinuation = (trailingText) => trailingText.startsWith("(") || trailingText.startsWith(":");
|
|
1899
|
+
const getHeadIdentifier = (target) => target.split(/[#.]/v, 1)[0] ?? "";
|
|
1900
|
+
const getLeftmostNameNode = (nameNode) => {
|
|
1901
|
+
let leftmost = nameNode;
|
|
1902
|
+
while (leftmost.left !== void 0) leftmost = leftmost.left;
|
|
1903
|
+
return leftmost;
|
|
1904
|
+
};
|
|
1905
|
+
const collectInlineReferences = (docComment) => (Array.isArray(docComment.comment) ? docComment.comment : []).filter((part) => part.name !== void 0).map((part) => ({
|
|
1906
|
+
nameNode: part.name,
|
|
1907
|
+
isInlineTag: true,
|
|
1908
|
+
trailingText: part.text ?? ""
|
|
1909
|
+
}));
|
|
1910
|
+
const collectBlockReferences = (docComment) => (docComment.tags ?? []).filter((tag) => tag.name?.name !== void 0).map((tag) => ({
|
|
1911
|
+
nameNode: tag.name?.name,
|
|
1912
|
+
isInlineTag: false,
|
|
1913
|
+
trailingText: ""
|
|
1914
|
+
}));
|
|
1915
|
+
const collectNameReferences = (node) => (node.jsDoc ?? []).flatMap((docComment) => [...collectInlineReferences(docComment), ...collectBlockReferences(docComment)]);
|
|
1916
|
+
const resolveNameNodeType = (checker, nameNode) => {
|
|
1917
|
+
if (nameNode.left === void 0 || nameNode.right === void 0) {
|
|
1918
|
+
const symbol = checker.getSymbolAtLocation(nameNode);
|
|
1919
|
+
return symbol === void 0 ? void 0 : checker.getDeclaredTypeOfSymbol(symbol);
|
|
1920
|
+
}
|
|
1921
|
+
const property = resolveNameNodeType(checker, nameNode.left)?.getProperty(nameNode.right.getText());
|
|
1922
|
+
return property === void 0 ? void 0 : checker.getTypeOfSymbolAtLocation(property, nameNode);
|
|
1923
|
+
};
|
|
1924
|
+
const isMemberNameResolved = (checker, nameNode) => resolveNameNodeType(checker, nameNode) !== void 0;
|
|
1925
|
+
const collectDeclaredNames = (sourceCode) => {
|
|
1926
|
+
const declaredNames = /* @__PURE__ */ new Set();
|
|
1927
|
+
const scopes = sourceCode.scopeManager?.scopes ?? [];
|
|
1928
|
+
for (const scope of scopes) for (const variable of scope.variables) declaredNames.add(variable.name);
|
|
1929
|
+
return declaredNames;
|
|
1930
|
+
};
|
|
1931
|
+
const collectLexicalReferences = (comment) => {
|
|
1932
|
+
const references = [];
|
|
1933
|
+
const lines = `/*${comment.value}*/`.split("\n");
|
|
1934
|
+
for (const [offset, line] of lines.entries()) for (const match of line.matchAll(createReferencePattern())) {
|
|
1935
|
+
const rawTarget = match.groups?.["target"] ?? "";
|
|
1936
|
+
const target = rawTarget.split("|", 1)[0] ?? "";
|
|
1937
|
+
const targetColumn = match.index + (match[0].length - rawTarget.length);
|
|
1938
|
+
references.push({
|
|
1939
|
+
target,
|
|
1940
|
+
headIdentifier: getHeadIdentifier(target),
|
|
1941
|
+
loc: {
|
|
1942
|
+
line: comment.loc.start.line + offset,
|
|
1943
|
+
column: offset === 0 ? comment.loc.start.column + targetColumn : targetColumn
|
|
1944
|
+
}
|
|
1945
|
+
});
|
|
1946
|
+
}
|
|
1947
|
+
return references;
|
|
1948
|
+
};
|
|
1949
|
+
/**
|
|
1950
|
+
* @see https://github.com/brnshkr/config/blob/master/docs/js/eslint/rules/resolvable-doc-reference.md
|
|
1951
|
+
*/
|
|
1952
|
+
const resolvableDocReferenceRule = {
|
|
1953
|
+
meta: {
|
|
1954
|
+
type: "suggestion",
|
|
1955
|
+
docs: {
|
|
1956
|
+
description: "Require every `@see` and `@link` target in a JSDoc comment to name a symbol that exists.",
|
|
1957
|
+
url: "https://github.com/brnshkr/config/blob/master/docs/js/eslint/rules/resolvable-doc-reference.md"
|
|
1958
|
+
},
|
|
1959
|
+
messages: { [MESSAGE_ID_MISSING_REFERENCE]: "Reference `{{ name }}` does not exist." }
|
|
357
1960
|
},
|
|
358
|
-
|
|
1961
|
+
create: (context) => {
|
|
1962
|
+
const sourceCode = context.sourceCode;
|
|
1963
|
+
const services = sourceCode.parserServices;
|
|
1964
|
+
const typeChecker = (services?.program)?.getTypeChecker();
|
|
1965
|
+
const findings = /* @__PURE__ */ new Map();
|
|
1966
|
+
const addFinding = (target, loc) => {
|
|
1967
|
+
const key = `${String(loc.line)}:${String(loc.column)}:${target}`;
|
|
1968
|
+
findings.set(key, findings.get(key) ?? {
|
|
1969
|
+
target,
|
|
1970
|
+
loc
|
|
1971
|
+
});
|
|
1972
|
+
};
|
|
1973
|
+
const checkNameNode = (checker, reference) => {
|
|
1974
|
+
const { isInlineTag, nameNode, trailingText } = reference;
|
|
1975
|
+
const name = nameNode.getText();
|
|
1976
|
+
const target = startsContinuation(trailingText) ? `${name}${trailingText}` : name;
|
|
1977
|
+
if (!isCheckableTarget(target) || checker.getSymbolAtLocation(nameNode) !== void 0) return;
|
|
1978
|
+
if (nameNode.right !== void 0 && isMemberNameResolved(checker, nameNode)) return;
|
|
1979
|
+
const isHeadResolved = checker.getSymbolAtLocation(getLeftmostNameNode(nameNode)) !== void 0;
|
|
1980
|
+
if (!isInlineTag && !isHeadResolved && !startsUpperCase(target)) return;
|
|
1981
|
+
const position = nameNode.getSourceFile().getLineAndCharacterOfPosition(nameNode.getStart());
|
|
1982
|
+
addFinding(target, {
|
|
1983
|
+
line: position.line + 1,
|
|
1984
|
+
column: position.character
|
|
1985
|
+
});
|
|
1986
|
+
};
|
|
1987
|
+
return {
|
|
1988
|
+
"*": (node) => {
|
|
1989
|
+
if (services === void 0 || typeChecker === void 0) return;
|
|
1990
|
+
const tsNode = services.esTreeNodeToTSNodeMap.get(node);
|
|
1991
|
+
if (tsNode === void 0) return;
|
|
1992
|
+
for (const reference of collectNameReferences(tsNode)) checkNameNode(typeChecker, reference);
|
|
1993
|
+
},
|
|
1994
|
+
"Program:exit": () => {
|
|
1995
|
+
const declaredNames = collectDeclaredNames(sourceCode);
|
|
1996
|
+
for (const comment of sourceCode.getAllComments()) {
|
|
1997
|
+
if (!isBlockComment(comment)) continue;
|
|
1998
|
+
for (const reference of collectLexicalReferences(comment)) if (isCheckableTarget(reference.target) && startsUpperCase(reference.headIdentifier) && !declaredNames.has(reference.headIdentifier)) addFinding(reference.target, reference.loc);
|
|
1999
|
+
}
|
|
2000
|
+
for (const finding of findings.values()) context.report({
|
|
2001
|
+
loc: finding.loc,
|
|
2002
|
+
messageId: MESSAGE_ID_MISSING_REFERENCE,
|
|
2003
|
+
data: { name: finding.target }
|
|
2004
|
+
});
|
|
2005
|
+
}
|
|
2006
|
+
};
|
|
2007
|
+
}
|
|
2008
|
+
};
|
|
2009
|
+
//#endregion
|
|
2010
|
+
//#region ../src/js/eslint/configs/builtin/type-assertion-style.ts
|
|
2011
|
+
const MESSAGE_ID_EXPECTED_PARENTHESES = "expectedParentheses";
|
|
2012
|
+
const MESSAGE_ID_UNEXPECTED_PARENTHESES = "unexpectedParentheses";
|
|
2013
|
+
const MESSAGE_ID_EXPECTED_SPACE = "expectedSpace";
|
|
2014
|
+
const MESSAGE_ID_UNEXPECTED_SPACE = "unexpectedSpace";
|
|
2015
|
+
const ASSERTION_SELECTOR = "TSTypeAssertion, TSAsExpression";
|
|
2016
|
+
const OPERAND_KEYWORDS = /* @__PURE__ */ new Set([
|
|
2017
|
+
"async",
|
|
2018
|
+
"await",
|
|
2019
|
+
"class",
|
|
2020
|
+
"delete",
|
|
2021
|
+
"function",
|
|
2022
|
+
"new",
|
|
2023
|
+
"super",
|
|
2024
|
+
"this",
|
|
2025
|
+
"typeof",
|
|
2026
|
+
"void"
|
|
2027
|
+
]);
|
|
2028
|
+
const REQUIREMENT_SCHEMA = {
|
|
2029
|
+
type: "string",
|
|
2030
|
+
enum: ["always", "never"]
|
|
2031
|
+
};
|
|
2032
|
+
const isKeywordOperand = (sourceCode, operand) => {
|
|
2033
|
+
const [firstToken, secondToken] = sourceCode.getTokens(operand);
|
|
2034
|
+
if (firstToken === void 0 || !OPERAND_KEYWORDS.has(firstToken.value)) return false;
|
|
2035
|
+
return firstToken.value !== "new" || secondToken?.value !== ".";
|
|
2036
|
+
};
|
|
2037
|
+
const isParenthesizedOperand = (sourceCode, node) => sourceCode.getTokenBefore(node)?.value === "(" && sourceCode.getTokenAfter(node)?.value === ")";
|
|
2038
|
+
const resolveOperandRange = (sourceCode, assertion, isAngleBracket) => {
|
|
2039
|
+
const [start, end] = isAngleBracket ? [sourceCode.getTokenAfter(assertion.typeAnnotation)?.range[1], assertion.range[1]] : [assertion.range[0], sourceCode.getTokenBefore(assertion.typeAnnotation)?.range[0]];
|
|
2040
|
+
return start === void 0 || end === void 0 ? void 0 : [start, end];
|
|
2041
|
+
};
|
|
2042
|
+
const hasCommentInAnyRange = (sourceCode, ranges) => sourceCode.getAllComments().some(({ range }) => ranges.some(([start, end]) => range[0] < end && range[1] > start));
|
|
2043
|
+
const resolveGapRange = (sourceCode, assertion) => {
|
|
2044
|
+
const closingAngle = sourceCode.getTokenAfter(assertion.typeAnnotation) ?? void 0;
|
|
2045
|
+
const operandStart = closingAngle === void 0 ? void 0 : sourceCode.getTokenAfter(closingAngle) ?? void 0;
|
|
2046
|
+
const isUnparenthesizedKeyword = isKeywordOperand(sourceCode, assertion.expression) && !isParenthesizedOperand(sourceCode, assertion.expression);
|
|
2047
|
+
return closingAngle === void 0 || operandStart === void 0 || isUnparenthesizedKeyword ? void 0 : [closingAngle.range[1], operandStart.range[0]];
|
|
359
2048
|
};
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
2049
|
+
//#endregion
|
|
2050
|
+
//#region ../src/js/eslint/configs/builtin/index.ts
|
|
2051
|
+
/**
|
|
2052
|
+
* @internal @brnshkr/config/eslint
|
|
2053
|
+
*/
|
|
2054
|
+
const RULE_DEFINITIONS = {
|
|
2055
|
+
"api-or-internal-tag": apiOrInternalTagRule,
|
|
2056
|
+
"boolish-prefix": boolishPrefixRule,
|
|
2057
|
+
"interface-suffix": interfaceSuffixRule,
|
|
2058
|
+
"internal-usage": internalUsageRule,
|
|
2059
|
+
"public-api-documentation": publicApiDocumentationRule,
|
|
2060
|
+
"require-import-attributes": requireImportAttributesRule,
|
|
2061
|
+
"require-import-alias": requireImportAliasRule,
|
|
2062
|
+
"resolvable-doc-reference": resolvableDocReferenceRule,
|
|
2063
|
+
"type-assertion-style": {
|
|
363
2064
|
meta: {
|
|
364
|
-
|
|
365
|
-
|
|
2065
|
+
type: "layout",
|
|
2066
|
+
fixable: "code",
|
|
2067
|
+
docs: {
|
|
2068
|
+
description: "Require a type assertion to parenthesize an operand that reads wider than it casts, and to carry no trailing space.",
|
|
2069
|
+
url: "https://github.com/brnshkr/config/blob/master/docs/js/eslint/rules/type-assertion-style.md"
|
|
2070
|
+
},
|
|
2071
|
+
schema: [{ oneOf: [REQUIREMENT_SCHEMA, {
|
|
2072
|
+
type: "object",
|
|
2073
|
+
additionalProperties: false,
|
|
2074
|
+
properties: {
|
|
2075
|
+
parentheses: REQUIREMENT_SCHEMA,
|
|
2076
|
+
spacing: REQUIREMENT_SCHEMA
|
|
2077
|
+
}
|
|
2078
|
+
}] }],
|
|
2079
|
+
messages: {
|
|
2080
|
+
[MESSAGE_ID_EXPECTED_PARENTHESES]: "The operand of a type assertion must be parenthesized.",
|
|
2081
|
+
[MESSAGE_ID_UNEXPECTED_PARENTHESES]: "The operand of a type assertion must not be parenthesized.",
|
|
2082
|
+
[MESSAGE_ID_EXPECTED_SPACE]: "A type assertion must be followed by a space.",
|
|
2083
|
+
[MESSAGE_ID_UNEXPECTED_SPACE]: "A type assertion must not be followed by a space."
|
|
2084
|
+
}
|
|
366
2085
|
},
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
2086
|
+
create: (context) => {
|
|
2087
|
+
const options = context.options[0];
|
|
2088
|
+
const parentheses = (typeof options === "string" ? options : options?.parentheses) ?? "always";
|
|
2089
|
+
const spacing = (typeof options === "string" ? void 0 : options?.spacing) ?? "never";
|
|
2090
|
+
const sourceCode = context.sourceCode;
|
|
2091
|
+
const assertionsWithParenthesesFix = /* @__PURE__ */ new Set();
|
|
2092
|
+
const gapText = spacing === "always" ? " " : "";
|
|
2093
|
+
return {
|
|
2094
|
+
[ASSERTION_SELECTOR]: (assertion) => {
|
|
2095
|
+
const node = assertion.expression;
|
|
2096
|
+
if (!isKeywordOperand(sourceCode, node)) return;
|
|
2097
|
+
const isParenthesized = isParenthesizedOperand(sourceCode, node);
|
|
2098
|
+
if (isParenthesized === (parentheses === "always")) return;
|
|
2099
|
+
const isAngleBracket = assertion.typeAnnotation.range[0] < node.range[0];
|
|
2100
|
+
const operandRange = resolveOperandRange(sourceCode, assertion, isAngleBracket);
|
|
2101
|
+
if (operandRange === void 0) return;
|
|
2102
|
+
if (isAngleBracket) assertionsWithParenthesesFix.add(assertion);
|
|
2103
|
+
const operandText = sourceCode.getText(node);
|
|
2104
|
+
const replacementText = isParenthesized ? operandText : `(${operandText})`;
|
|
2105
|
+
const erasedRanges = [[operandRange[0], node.range[0]], [node.range[1], operandRange[1]]];
|
|
2106
|
+
context.report({
|
|
2107
|
+
node,
|
|
2108
|
+
messageId: isParenthesized ? MESSAGE_ID_UNEXPECTED_PARENTHESES : MESSAGE_ID_EXPECTED_PARENTHESES,
|
|
2109
|
+
fix: (fixer) => hasCommentInAnyRange(sourceCode, erasedRanges) ? [] : [fixer.replaceTextRange(operandRange, isAngleBracket ? `${gapText}${replacementText}` : `${replacementText} `)]
|
|
2110
|
+
});
|
|
2111
|
+
},
|
|
2112
|
+
"TSTypeAssertion:exit": (node) => {
|
|
2113
|
+
const gapRange = resolveGapRange(sourceCode, node);
|
|
2114
|
+
if (gapRange === void 0 || assertionsWithParenthesesFix.has(node)) return;
|
|
2115
|
+
const isSpaced = gapRange[0] !== gapRange[1];
|
|
2116
|
+
if (isSpaced === (spacing === "always")) return;
|
|
2117
|
+
context.report({
|
|
2118
|
+
node,
|
|
2119
|
+
messageId: isSpaced ? MESSAGE_ID_UNEXPECTED_SPACE : MESSAGE_ID_EXPECTED_SPACE,
|
|
2120
|
+
fix: (fixer) => hasCommentInAnyRange(sourceCode, [gapRange]) ? [] : [fixer.replaceTextRange(gapRange, gapText)]
|
|
2121
|
+
});
|
|
2122
|
+
}
|
|
2123
|
+
};
|
|
2124
|
+
}
|
|
375
2125
|
}
|
|
376
|
-
}
|
|
2126
|
+
};
|
|
2127
|
+
const builtin = (typescriptOptions) => {
|
|
2128
|
+
const tsConfigPath = resolveTsConfigPath(typeof typescriptOptions === "object" ? typescriptOptions : void 0);
|
|
2129
|
+
return [{
|
|
2130
|
+
name: buildConfigName(MAIN_SCOPES[packageOrganizationUpper], SUB_SCOPES.SETUP),
|
|
2131
|
+
plugins: { [packageOrganization]: {
|
|
2132
|
+
meta: {
|
|
2133
|
+
name: packageOrganization,
|
|
2134
|
+
version
|
|
2135
|
+
},
|
|
2136
|
+
rules: RULE_DEFINITIONS
|
|
2137
|
+
} }
|
|
2138
|
+
}, {
|
|
2139
|
+
name: buildConfigName(MAIN_SCOPES[packageOrganizationUpper], SUB_SCOPES.RULES),
|
|
2140
|
+
files: GLOB_SCRIPT_FILES,
|
|
2141
|
+
rules: {
|
|
2142
|
+
[`${packageOrganization}/api-or-internal-tag`]: "error",
|
|
2143
|
+
[`${packageOrganization}/boolish-prefix`]: "error",
|
|
2144
|
+
[`${packageOrganization}/interface-suffix`]: "error",
|
|
2145
|
+
[`${packageOrganization}/internal-usage`]: ["error", { tsConfigPath }],
|
|
2146
|
+
[`${packageOrganization}/public-api-documentation`]: "error",
|
|
2147
|
+
[`${packageOrganization}/require-import-alias`]: ["error", { tsConfigPath }],
|
|
2148
|
+
[`${packageOrganization}/require-import-attributes`]: "error",
|
|
2149
|
+
[`${packageOrganization}/resolvable-doc-reference`]: "error",
|
|
2150
|
+
[`${packageOrganization}/type-assertion-style`]: "error"
|
|
2151
|
+
}
|
|
2152
|
+
}];
|
|
2153
|
+
};
|
|
377
2154
|
const builtinConfig = { [packageOrganization]: builtin };
|
|
378
2155
|
//#endregion
|
|
379
2156
|
//#region ../src/js/eslint/utils/module.ts
|
|
2157
|
+
/**
|
|
2158
|
+
* @internal @brnshkr/config/eslint
|
|
2159
|
+
*/
|
|
380
2160
|
const MODULES = {
|
|
381
2161
|
[packageOrganization]: { name: packageOrganization },
|
|
382
2162
|
comments: {
|
|
@@ -387,15 +2167,11 @@ const MODULES = {
|
|
|
387
2167
|
name: "css",
|
|
388
2168
|
packages: { requiredAll: [ESLINT_PACKAGES.ESLINT_CSS] }
|
|
389
2169
|
},
|
|
390
|
-
gitignore: {
|
|
391
|
-
name: "gitignore",
|
|
392
|
-
packages: { requiredAll: [ESLINT_PACKAGES.ESLINT_FLAT_CONFIG_GITIGNORE] }
|
|
393
|
-
},
|
|
394
2170
|
import: {
|
|
395
2171
|
name: "import",
|
|
396
2172
|
packages: {
|
|
397
2173
|
requiredAny: [ESLINT_PACKAGES.ESLINT_PLUGIN_IMPORT_X, ESLINT_PACKAGES.ESLINT_PLUGIN_ANTFU],
|
|
398
|
-
optional: [ESLINT_PACKAGES.
|
|
2174
|
+
optional: [ESLINT_PACKAGES.ESLINT_IMPORT_RESOLVER_TYPESCRIPT]
|
|
399
2175
|
}
|
|
400
2176
|
},
|
|
401
2177
|
javascript: {
|
|
@@ -461,14 +2237,13 @@ const MODULES = {
|
|
|
461
2237
|
packages: { requiredAll: [ESLINT_PACKAGES.ESLINT_PLUGIN_YML] }
|
|
462
2238
|
}
|
|
463
2239
|
};
|
|
464
|
-
const resolvePackages =
|
|
465
|
-
const
|
|
466
|
-
const isModuleEnabled = (moduleInfo) => enabledStates[moduleInfo.name] ?? isModuleEnabledByDefault(moduleInfo);
|
|
467
|
-
const setModuleEnabled = (moduleInfo, state) => {
|
|
468
|
-
enabledStates[moduleInfo.name] = state;
|
|
469
|
-
};
|
|
2240
|
+
const resolvePackages = resolvePackagesSharedAsynchronously;
|
|
2241
|
+
const { isModuleEnabled, setModuleEnabled } = createModuleState();
|
|
470
2242
|
//#endregion
|
|
471
2243
|
//#region ../src/js/eslint/configs/comments.ts
|
|
2244
|
+
/**
|
|
2245
|
+
* @internal @brnshkr/config/eslint
|
|
2246
|
+
*/
|
|
472
2247
|
const comments = async () => {
|
|
473
2248
|
const { requiredAll: [pluginComments] } = await resolvePackages(MODULES.comments);
|
|
474
2249
|
if (!pluginComments) return [];
|
|
@@ -482,15 +2257,72 @@ const comments = async () => {
|
|
|
482
2257
|
files: GLOB_SCRIPT_FILES,
|
|
483
2258
|
rules: {
|
|
484
2259
|
...renameRules(recommendedRules, { "@eslint-community/eslint-comments": "comments" }),
|
|
485
|
-
"comments/require-description": "error"
|
|
2260
|
+
"comments/require-description": ["error", { additionalDirectives: [
|
|
2261
|
+
"@ts-expect-error",
|
|
2262
|
+
"c8 ignore",
|
|
2263
|
+
"istanbul ignore",
|
|
2264
|
+
"node:coverage ignore",
|
|
2265
|
+
"svelte-ignore",
|
|
2266
|
+
"v8 ignore"
|
|
2267
|
+
] }]
|
|
486
2268
|
}
|
|
487
2269
|
}];
|
|
488
2270
|
};
|
|
489
2271
|
//#endregion
|
|
490
2272
|
//#region ../src/js/eslint/configs/css.ts
|
|
491
|
-
|
|
2273
|
+
/**
|
|
2274
|
+
* @internal @brnshkr/config/eslint
|
|
2275
|
+
*/
|
|
2276
|
+
const buildTailwindSyntax = (defaultSyntax) => {
|
|
2277
|
+
const declarations = objectFromEntries(objectKeys(defaultSyntax.properties).map((property) => [property, "<declaration-value>"]));
|
|
2278
|
+
return { atrules: {
|
|
2279
|
+
apply: { prelude: "<any-value>" },
|
|
2280
|
+
config: { prelude: "<string>" },
|
|
2281
|
+
"custom-variant": { prelude: "<any-value>" },
|
|
2282
|
+
plugin: { prelude: "<string>" },
|
|
2283
|
+
reference: { prelude: "<string>" },
|
|
2284
|
+
slot: { prelude: null },
|
|
2285
|
+
source: { prelude: "<any-value>" },
|
|
2286
|
+
theme: { prelude: "<any-value>?" },
|
|
2287
|
+
utility: {
|
|
2288
|
+
prelude: "<any-value>",
|
|
2289
|
+
descriptors: declarations
|
|
2290
|
+
},
|
|
2291
|
+
variant: {
|
|
2292
|
+
prelude: "<any-value>",
|
|
2293
|
+
descriptors: declarations
|
|
2294
|
+
}
|
|
2295
|
+
} };
|
|
2296
|
+
};
|
|
2297
|
+
const mergeSyntaxDefinitions = (baseSyntax, overridingSyntax) => ({
|
|
2298
|
+
...baseSyntax,
|
|
2299
|
+
...overridingSyntax,
|
|
2300
|
+
atrules: {
|
|
2301
|
+
...baseSyntax.atrules,
|
|
2302
|
+
...overridingSyntax.atrules
|
|
2303
|
+
},
|
|
2304
|
+
properties: {
|
|
2305
|
+
...baseSyntax.properties,
|
|
2306
|
+
...overridingSyntax.properties
|
|
2307
|
+
},
|
|
2308
|
+
types: {
|
|
2309
|
+
...baseSyntax.types,
|
|
2310
|
+
...overridingSyntax.types
|
|
2311
|
+
}
|
|
2312
|
+
});
|
|
2313
|
+
const buildCustomSyntax = (customSyntax, isTailwindEnabled) => {
|
|
2314
|
+
if (!isTailwindEnabled) return customSyntax;
|
|
2315
|
+
return (defaultSyntax) => {
|
|
2316
|
+
const tailwindSyntax = buildTailwindSyntax(defaultSyntax);
|
|
2317
|
+
if (customSyntax === void 0) return tailwindSyntax;
|
|
2318
|
+
return mergeSyntaxDefinitions(tailwindSyntax, typeof customSyntax === "function" ? customSyntax(defaultSyntax) : customSyntax);
|
|
2319
|
+
};
|
|
2320
|
+
};
|
|
2321
|
+
const css = async (options) => {
|
|
492
2322
|
const { requiredAll: [pluginCss] } = await resolvePackages(MODULES.css);
|
|
493
2323
|
if (!pluginCss) return [];
|
|
2324
|
+
const isTailwindEnabled = options?.tailwind ?? doAllPackagesExist([ESLINT_PACKAGES.TAILWINDCSS]);
|
|
2325
|
+
const customSyntax = buildCustomSyntax(options?.customSyntax, isTailwindEnabled);
|
|
494
2326
|
return [{
|
|
495
2327
|
name: buildConfigName(MAIN_SCOPES.CSS, SUB_SCOPES.SETUP),
|
|
496
2328
|
plugins: { css: pluginCss }
|
|
@@ -498,41 +2330,68 @@ const css = async () => {
|
|
|
498
2330
|
name: buildConfigName(MAIN_SCOPES.CSS, SUB_SCOPES.RULES),
|
|
499
2331
|
files: [GLOB_CSS],
|
|
500
2332
|
language: "css/css",
|
|
2333
|
+
languageOptions: {
|
|
2334
|
+
...customSyntax === void 0 ? void 0 : { customSyntax },
|
|
2335
|
+
tolerant: options?.tolerant ?? isTailwindEnabled
|
|
2336
|
+
},
|
|
501
2337
|
rules: {
|
|
502
2338
|
...pluginCss.configs.recommended.rules,
|
|
2339
|
+
...isTailwindEnabled ? {
|
|
2340
|
+
"css/no-duplicate-imports": "off",
|
|
2341
|
+
"css/no-invalid-properties": "off"
|
|
2342
|
+
} : void 0,
|
|
503
2343
|
"css/prefer-logical-properties": "error",
|
|
504
2344
|
"css/relative-font-units": ["error", { allowUnits: ["em", "rem"] }],
|
|
505
|
-
|
|
2345
|
+
...isModuleEnabled(MODULES.unicorn) ? {
|
|
2346
|
+
"unicorn/no-missing-local-resource": "error",
|
|
2347
|
+
"unicorn/prefer-explicit-viewport-units": "error"
|
|
2348
|
+
} : void 0
|
|
506
2349
|
}
|
|
507
2350
|
}];
|
|
508
2351
|
};
|
|
509
2352
|
//#endregion
|
|
510
|
-
//#region ../src/js/eslint/configs/gitignore.ts
|
|
511
|
-
const gitignore = async (options) => {
|
|
512
|
-
const { requiredAll: [pluginGitignore] } = await resolvePackages(MODULES.gitignore);
|
|
513
|
-
if (!pluginGitignore) return [];
|
|
514
|
-
return [pluginGitignore({
|
|
515
|
-
name: buildConfigName(MAIN_SCOPES.IGNORES, SUB_SCOPES.GIT),
|
|
516
|
-
...options
|
|
517
|
-
})];
|
|
518
|
-
};
|
|
519
|
-
//#endregion
|
|
520
2353
|
//#region ../src/js/eslint/configs/ignores.ts
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
2354
|
+
/**
|
|
2355
|
+
* @internal @brnshkr/config/eslint
|
|
2356
|
+
*/
|
|
2357
|
+
const DEFAULT_IGNORE_FILE = ".gitignore";
|
|
2358
|
+
const isIgnoreFile = (customIgnore) => /^\.[\w\-]+ignore$/v.test(path.basename(customIgnore));
|
|
2359
|
+
const ignores = (customIgnores = []) => {
|
|
2360
|
+
const existingIgnoreFiles = [.../* @__PURE__ */ new Set([DEFAULT_IGNORE_FILE, ...customIgnores.filter((customIgnore) => isIgnoreFile(customIgnore))])].map((ignoreFile) => path.resolve(ignoreFile)).filter((ignoreFile) => doesFileExist(ignoreFile));
|
|
2361
|
+
return [{
|
|
2362
|
+
name: buildConfigName(MAIN_SCOPES.IGNORES, SUB_SCOPES.BASE),
|
|
2363
|
+
ignores: [...GLOB_IGNORES, ...customIgnores.filter((customIgnore) => !isIgnoreFile(customIgnore))]
|
|
2364
|
+
}, ...existingIgnoreFiles.map((ignoreFile) => ({
|
|
2365
|
+
...includeIgnoreFile(ignoreFile, { gitignoreResolution: true }),
|
|
2366
|
+
name: buildConfigName(MAIN_SCOPES.IGNORES, SUB_SCOPES.FILES)
|
|
2367
|
+
}))];
|
|
2368
|
+
};
|
|
525
2369
|
//#endregion
|
|
526
2370
|
//#region ../src/js/eslint/configs/import.ts
|
|
2371
|
+
/**
|
|
2372
|
+
* @internal @brnshkr/config/eslint
|
|
2373
|
+
*/
|
|
527
2374
|
const imports = async () => {
|
|
528
|
-
const { requiredAny: [pluginImport, pluginAntfu], optional: [
|
|
2375
|
+
const { requiredAny: [pluginImport, pluginAntfu], optional: [importResolverTypescript] } = await resolvePackages(MODULES.import);
|
|
529
2376
|
const plugins = {};
|
|
530
2377
|
const settings = {};
|
|
531
2378
|
let pluginImportRules = {};
|
|
2379
|
+
let pluginImportTsRules = {};
|
|
532
2380
|
let pluginAntfuRules = {};
|
|
533
2381
|
if (pluginImport) {
|
|
534
2382
|
plugins["import"] = pluginImport;
|
|
535
|
-
settings["import-x/resolver-next"] = [pluginImport.createNodeResolver(),
|
|
2383
|
+
settings["import-x/resolver-next"] = [pluginImport.createNodeResolver(), importResolverTypescript === void 0 ? void 0 : importResolverTypescript.createTypeScriptImportResolver({ bun: true })].filter(Boolean);
|
|
2384
|
+
if (isModuleEnabled(MODULES.typescript)) settings["import-x/extensions"] = [
|
|
2385
|
+
".cjs",
|
|
2386
|
+
".cts",
|
|
2387
|
+
".js",
|
|
2388
|
+
".jsx",
|
|
2389
|
+
".mjs",
|
|
2390
|
+
".mts",
|
|
2391
|
+
".ts",
|
|
2392
|
+
".tsx"
|
|
2393
|
+
];
|
|
2394
|
+
settings["import-x/ignore"] = [String.raw`[/\\]node_modules[/\\]`];
|
|
536
2395
|
settings["import-x/core-modules"] = [
|
|
537
2396
|
"bun",
|
|
538
2397
|
"bun:bundle",
|
|
@@ -541,33 +2400,46 @@ const imports = async () => {
|
|
|
541
2400
|
"bun:sqlite",
|
|
542
2401
|
"bun:test"
|
|
543
2402
|
];
|
|
544
|
-
|
|
2403
|
+
pluginImportTsRules = renameRules(pluginImport.flatConfigs.typescript.rules, { "import-x": "import" });
|
|
545
2404
|
pluginImportRules = {
|
|
546
2405
|
...renameRules(pluginImport.flatConfigs.recommended.rules, { "import-x": "import" }),
|
|
547
|
-
|
|
548
|
-
"import/consistent-type-specifier-style": ["error", "prefer-top-level"],
|
|
2406
|
+
"import/consistent-type-specifier-style": "error",
|
|
549
2407
|
"import/extensions": [
|
|
550
2408
|
"error",
|
|
551
2409
|
"ignorePackages",
|
|
552
2410
|
{
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
2411
|
+
checkTypeImports: true,
|
|
2412
|
+
pattern: {
|
|
2413
|
+
js: "never",
|
|
2414
|
+
ts: "never",
|
|
2415
|
+
cts: "never",
|
|
2416
|
+
mts: "never"
|
|
2417
|
+
},
|
|
2418
|
+
pathGroupOverrides: [{
|
|
2419
|
+
pattern: "{{.,..,../..,../../..,../../../..,../../../../..}/**/declarations,$types/declarations,$declarations}{,/**}",
|
|
2420
|
+
action: "ignore"
|
|
2421
|
+
}]
|
|
557
2422
|
}
|
|
558
2423
|
],
|
|
559
2424
|
"import/first": "error",
|
|
560
2425
|
"import/max-dependencies": ["error", { max: 15 }],
|
|
2426
|
+
"import/namespace": "off",
|
|
561
2427
|
"import/newline-after-import": "error",
|
|
562
2428
|
"import/no-absolute-path": "error",
|
|
563
2429
|
"import/no-amd": "error",
|
|
564
|
-
"import/no-cycle": ["error", {
|
|
2430
|
+
"import/no-cycle": ["error", {
|
|
2431
|
+
ignoreExternal: true,
|
|
2432
|
+
maxDepth: 3
|
|
2433
|
+
}],
|
|
565
2434
|
"import/no-default-export": "error",
|
|
566
2435
|
"import/no-deprecated": "error",
|
|
567
2436
|
"import/no-duplicates": "error",
|
|
568
2437
|
"import/no-dynamic-require": "error",
|
|
569
2438
|
"import/no-empty-named-blocks": "error",
|
|
570
|
-
"import/no-extraneous-dependencies": ["error", {
|
|
2439
|
+
"import/no-extraneous-dependencies": ["error", {
|
|
2440
|
+
devDependencies: GLOB_DEVELOPMENT_FILES,
|
|
2441
|
+
includeTypes: true
|
|
2442
|
+
}],
|
|
571
2443
|
"import/no-import-module-exports": "error",
|
|
572
2444
|
"import/no-mutable-exports": "error",
|
|
573
2445
|
"import/no-named-as-default-member": "error",
|
|
@@ -581,7 +2453,7 @@ const imports = async () => {
|
|
|
581
2453
|
"import/no-useless-path-segments": ["error", { noUselessIndex: true }],
|
|
582
2454
|
"import/no-unresolved": ["error", {
|
|
583
2455
|
commonjs: true,
|
|
584
|
-
|
|
2456
|
+
caseSensitiveStrict: true
|
|
585
2457
|
}],
|
|
586
2458
|
"import/no-webpack-loader-syntax": "error",
|
|
587
2459
|
"import/order": ["error", {
|
|
@@ -625,23 +2497,34 @@ const imports = async () => {
|
|
|
625
2497
|
"antfu/no-import-node-modules-by-path": "error"
|
|
626
2498
|
};
|
|
627
2499
|
}
|
|
628
|
-
if (
|
|
2500
|
+
if (objectKeys(plugins).length === 0) return [];
|
|
629
2501
|
const setupConfig = {
|
|
630
2502
|
name: buildConfigName(MAIN_SCOPES.IMPORT, SUB_SCOPES.SETUP),
|
|
631
2503
|
plugins
|
|
632
2504
|
};
|
|
633
|
-
if (
|
|
634
|
-
return [
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
2505
|
+
if (objectKeys(settings).length > 0) setupConfig.settings = settings;
|
|
2506
|
+
return [
|
|
2507
|
+
setupConfig,
|
|
2508
|
+
{
|
|
2509
|
+
name: buildConfigName(MAIN_SCOPES.IMPORT, SUB_SCOPES.RULES),
|
|
2510
|
+
files: GLOB_SCRIPT_FILES,
|
|
2511
|
+
rules: {
|
|
2512
|
+
...pluginImportRules,
|
|
2513
|
+
...pluginAntfuRules
|
|
2514
|
+
}
|
|
2515
|
+
},
|
|
2516
|
+
...isModuleEnabled(MODULES.typescript) ? [{
|
|
2517
|
+
name: buildConfigName(MAIN_SCOPES.IMPORT, `${SUB_SCOPES.RULES}-typescript`),
|
|
2518
|
+
files: [GLOB_TS],
|
|
2519
|
+
rules: pluginImportTsRules
|
|
2520
|
+
}] : []
|
|
2521
|
+
];
|
|
642
2522
|
};
|
|
643
2523
|
//#endregion
|
|
644
2524
|
//#region ../src/js/eslint/configs/javascript.ts
|
|
2525
|
+
/**
|
|
2526
|
+
* @internal @brnshkr/config/eslint
|
|
2527
|
+
*/
|
|
645
2528
|
const javascript = async () => {
|
|
646
2529
|
const { optional: [pluginAntfu, pluginUnusedImports] } = await resolvePackages(MODULES.javascript);
|
|
647
2530
|
const plugins = {};
|
|
@@ -673,16 +2556,19 @@ const javascript = async () => {
|
|
|
673
2556
|
ecmaFeatures: { jsx: true }
|
|
674
2557
|
}
|
|
675
2558
|
},
|
|
676
|
-
linterOptions: {
|
|
2559
|
+
linterOptions: {
|
|
2560
|
+
reportUnusedDisableDirectives: "error",
|
|
2561
|
+
reportUnusedInlineConfigs: "error"
|
|
2562
|
+
}
|
|
677
2563
|
}, {
|
|
678
2564
|
name: buildConfigName(MAIN_SCOPES.JAVASCRIPT, SUB_SCOPES.RULES),
|
|
679
2565
|
files: GLOB_SCRIPT_FILES,
|
|
680
2566
|
rules: {
|
|
681
2567
|
...jsEslint.configs.recommended.rules,
|
|
682
2568
|
...pluginUnusedImports ? { "no-unused-vars": "off" } : void 0,
|
|
683
|
-
"accessor-pairs": "error",
|
|
2569
|
+
"accessor-pairs": ["error", { enforceForTSTypes: true }],
|
|
684
2570
|
"array-callback-return": ["error", { checkForEach: true }],
|
|
685
|
-
"arrow-body-style":
|
|
2571
|
+
"arrow-body-style": "error",
|
|
686
2572
|
"block-scoped-var": "error",
|
|
687
2573
|
"capitalized-comments": [
|
|
688
2574
|
"error",
|
|
@@ -710,8 +2596,12 @@ const javascript = async () => {
|
|
|
710
2596
|
includeCommonJSModuleExports: true
|
|
711
2597
|
}],
|
|
712
2598
|
"func-names": "error",
|
|
713
|
-
"func-style":
|
|
714
|
-
"grouped-accessor-pairs":
|
|
2599
|
+
"func-style": "error",
|
|
2600
|
+
"grouped-accessor-pairs": [
|
|
2601
|
+
"error",
|
|
2602
|
+
"getBeforeSet",
|
|
2603
|
+
{ enforceForTSTypes: true }
|
|
2604
|
+
],
|
|
715
2605
|
"guard-for-in": "error",
|
|
716
2606
|
"init-declarations": "error",
|
|
717
2607
|
"logical-assignment-operators": "error",
|
|
@@ -728,7 +2618,10 @@ const javascript = async () => {
|
|
|
728
2618
|
skipBlankLines: true,
|
|
729
2619
|
skipComments: true
|
|
730
2620
|
}],
|
|
731
|
-
"max-nested-callbacks": ["error", {
|
|
2621
|
+
"max-nested-callbacks": ["error", {
|
|
2622
|
+
max: 3,
|
|
2623
|
+
checkConstructorCallCallbacks: true
|
|
2624
|
+
}],
|
|
732
2625
|
"max-params": ["error", { max: 4 }],
|
|
733
2626
|
"max-statements": ["error", { max: 30 }],
|
|
734
2627
|
"new-cap": "error",
|
|
@@ -739,6 +2632,8 @@ const javascript = async () => {
|
|
|
739
2632
|
"no-caller": "error",
|
|
740
2633
|
"no-cond-assign": ["error", "always"],
|
|
741
2634
|
"no-console": "error",
|
|
2635
|
+
"no-constant-binary-expression": ["error", { checkRelationalComparisons: true }],
|
|
2636
|
+
"no-constant-condition": ["error", { checkLoops: "all" }],
|
|
742
2637
|
"no-constructor-return": "error",
|
|
743
2638
|
"no-div-regex": "error",
|
|
744
2639
|
"no-duplicate-imports": isModuleEnabled(MODULES.import) ? "off" : ["error", { allowSeparateTypeImports: true }],
|
|
@@ -748,26 +2643,39 @@ const javascript = async () => {
|
|
|
748
2643
|
"no-eval": "error",
|
|
749
2644
|
"no-extend-native": "error",
|
|
750
2645
|
"no-extra-bind": "error",
|
|
2646
|
+
"no-extra-boolean-cast": ["error", { enforceForInnerExpressions: true }],
|
|
751
2647
|
"no-extra-label": "error",
|
|
2648
|
+
"no-fallthrough": ["error", {
|
|
2649
|
+
allowEmptyCase: true,
|
|
2650
|
+
reportUnusedFallthroughComment: true
|
|
2651
|
+
}],
|
|
752
2652
|
"no-implicit-coercion": ["error", { boolean: false }],
|
|
753
2653
|
"no-implicit-globals": "error",
|
|
754
2654
|
"no-implied-eval": "error",
|
|
755
2655
|
"no-inline-comments": "error",
|
|
756
|
-
"no-inner-declarations":
|
|
2656
|
+
"no-inner-declarations": [
|
|
2657
|
+
"error",
|
|
2658
|
+
"functions",
|
|
2659
|
+
{ blockScopedFunctions: "disallow" }
|
|
2660
|
+
],
|
|
757
2661
|
"no-invalid-this": "error",
|
|
2662
|
+
"no-irregular-whitespace": ["error", { skipStrings: false }],
|
|
758
2663
|
"no-iterator": "error",
|
|
759
2664
|
"no-label-var": "error",
|
|
760
2665
|
"no-labels": "error",
|
|
761
2666
|
"no-lone-blocks": "error",
|
|
762
2667
|
"no-lonely-if": "error",
|
|
763
2668
|
"no-loop-func": "error",
|
|
764
|
-
"no-magic-numbers": ["error", {
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
2669
|
+
"no-magic-numbers": ["error", {
|
|
2670
|
+
ignore: [
|
|
2671
|
+
-1,
|
|
2672
|
+
0,
|
|
2673
|
+
1,
|
|
2674
|
+
100,
|
|
2675
|
+
42069
|
|
2676
|
+
],
|
|
2677
|
+
enforceConst: true
|
|
2678
|
+
}],
|
|
771
2679
|
"no-multi-assign": "error",
|
|
772
2680
|
"no-multi-str": "error",
|
|
773
2681
|
"no-negated-condition": "error",
|
|
@@ -828,14 +2736,20 @@ const javascript = async () => {
|
|
|
828
2736
|
"no-return-assign": ["error", "always"],
|
|
829
2737
|
"no-script-url": "error",
|
|
830
2738
|
"no-self-compare": "error",
|
|
831
|
-
"no-sequences": "error",
|
|
832
|
-
"no-shadow": "error",
|
|
2739
|
+
"no-sequences": ["error", { allowInParentheses: false }],
|
|
2740
|
+
"no-shadow": ["error", { hoist: "all" }],
|
|
833
2741
|
"no-template-curly-in-string": "error",
|
|
834
2742
|
"no-throw-literal": "error",
|
|
835
|
-
"no-
|
|
836
|
-
"no-
|
|
2743
|
+
"no-undef": ["error", { typeof: true }],
|
|
2744
|
+
"no-underscore-dangle": ["error", {
|
|
2745
|
+
enforceInClassFields: true,
|
|
2746
|
+
enforceInMethodNames: true
|
|
2747
|
+
}],
|
|
2748
|
+
"no-unmodified-loop-condition": ["error", { checkConditionalExpressions: true }],
|
|
837
2749
|
"no-unneeded-ternary": ["error", { defaultAssignment: false }],
|
|
838
2750
|
"no-unreachable-loop": "error",
|
|
2751
|
+
"no-unsafe-negation": ["error", { enforceForOrderingRelations: true }],
|
|
2752
|
+
"no-unsafe-optional-chaining": ["error", { disallowArithmeticOperators: true }],
|
|
839
2753
|
"no-unused-expressions": "error",
|
|
840
2754
|
"no-use-before-define": "error",
|
|
841
2755
|
"no-useless-call": "error",
|
|
@@ -845,7 +2759,7 @@ const javascript = async () => {
|
|
|
845
2759
|
"no-useless-rename": "error",
|
|
846
2760
|
"no-useless-return": "error",
|
|
847
2761
|
"no-var": "error",
|
|
848
|
-
"no-void": "error",
|
|
2762
|
+
"no-void": ["error", { allowAsStatement: true }],
|
|
849
2763
|
"no-warning-comments": "error",
|
|
850
2764
|
"object-shorthand": [
|
|
851
2765
|
"error",
|
|
@@ -856,7 +2770,7 @@ const javascript = async () => {
|
|
|
856
2770
|
}
|
|
857
2771
|
],
|
|
858
2772
|
"operator-assignment": "error",
|
|
859
|
-
"prefer-arrow-callback": "error",
|
|
2773
|
+
"prefer-arrow-callback": ["error", { allowUnboundThis: false }],
|
|
860
2774
|
"prefer-const": ["error", { ignoreReadBeforeAssign: true }],
|
|
861
2775
|
"prefer-destructuring": "error",
|
|
862
2776
|
"prefer-exponentiation-operator": "error",
|
|
@@ -885,112 +2799,93 @@ const javascript = async () => {
|
|
|
885
2799
|
};
|
|
886
2800
|
//#endregion
|
|
887
2801
|
//#region ../src/js/eslint/configs/typescript.ts
|
|
2802
|
+
/**
|
|
2803
|
+
* @internal @brnshkr/config/eslint
|
|
2804
|
+
*/
|
|
888
2805
|
const DEFAULT_TYPE_AWARE_IGNORES = [`${GLOB_MD}/**`];
|
|
889
2806
|
const getTsEslintParserIfExists = async () => {
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
const { requiredAll: [, tsEslint] } = await resolvePackages(MODULES.typescript);
|
|
894
|
-
if (tsEslint) ({parser} = tsEslint);
|
|
895
|
-
}
|
|
896
|
-
return parser;
|
|
2807
|
+
if (!isModuleEnabled(MODULES.typescript)) return;
|
|
2808
|
+
const { requiredAll: [, tsEslint] } = await resolvePackages(MODULES.typescript);
|
|
2809
|
+
return tsEslint?.parser;
|
|
897
2810
|
};
|
|
898
|
-
const resolveTypeAwareOptions = (resolvedOptions, files,
|
|
2811
|
+
const resolveTypeAwareOptions = (resolvedOptions, files, ignoredGlobs) => {
|
|
899
2812
|
const typeAwareOptions = typeof resolvedOptions.typeAware === "object" ? resolvedOptions.typeAware : {
|
|
900
2813
|
ignores: DEFAULT_TYPE_AWARE_IGNORES,
|
|
901
2814
|
tsconfig: typeof resolvedOptions.typeAware === "string" ? resolvedOptions.typeAware : void 0
|
|
902
2815
|
};
|
|
903
|
-
typeAwareOptions.files = [
|
|
904
|
-
typeAwareOptions.ignores = [
|
|
2816
|
+
typeAwareOptions.files = [.../* @__PURE__ */ new Set([...typeAwareOptions.files ?? [], ...files])];
|
|
2817
|
+
typeAwareOptions.ignores = [.../* @__PURE__ */ new Set([...typeAwareOptions.ignores ?? [], ...ignoredGlobs])];
|
|
905
2818
|
return typeAwareOptions;
|
|
906
2819
|
};
|
|
907
2820
|
const extractRelevantRules = (configs, key) => {
|
|
908
2821
|
for (const config of configs) if (config.name === `typescript-eslint/${key}` && config.rules) return renameRules(config.rules, { "@typescript-eslint": "ts" });
|
|
909
2822
|
throw new Error(`Expected key "${key}" to be contained in given config.`);
|
|
910
2823
|
};
|
|
911
|
-
const getNamingConvention = (
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
{
|
|
929
|
-
selector: "variable",
|
|
930
|
-
format: null,
|
|
931
|
-
modifiers: ["destructured"]
|
|
932
|
-
},
|
|
933
|
-
{
|
|
934
|
-
selector: "typeLike",
|
|
935
|
-
format: ["StrictPascalCase"]
|
|
936
|
-
},
|
|
937
|
-
{
|
|
938
|
-
selector: "parameter",
|
|
939
|
-
format: null,
|
|
940
|
-
filter: {
|
|
941
|
-
regex: "^_+$",
|
|
942
|
-
match: false
|
|
943
|
-
}
|
|
944
|
-
},
|
|
945
|
-
{
|
|
946
|
-
selector: [
|
|
947
|
-
"classProperty",
|
|
948
|
-
"objectLiteralProperty",
|
|
949
|
-
"typeProperty",
|
|
950
|
-
"classMethod",
|
|
951
|
-
"objectLiteralMethod",
|
|
952
|
-
"typeMethod",
|
|
953
|
-
"accessor",
|
|
954
|
-
"enumMember"
|
|
955
|
-
],
|
|
956
|
-
format: null,
|
|
957
|
-
modifiers: ["requiresQuotes"]
|
|
958
|
-
},
|
|
959
|
-
{
|
|
960
|
-
selector: "typeParameter",
|
|
961
|
-
format: ["StrictPascalCase"],
|
|
962
|
-
prefix: ["T"],
|
|
963
|
-
custom: {
|
|
964
|
-
regex: "^[A-Z]",
|
|
965
|
-
match: true
|
|
966
|
-
}
|
|
967
|
-
},
|
|
968
|
-
{
|
|
969
|
-
selector: "interface",
|
|
970
|
-
format: ["StrictPascalCase"],
|
|
971
|
-
custom: {
|
|
972
|
-
regex: "^I[A-Z]",
|
|
973
|
-
match: false
|
|
974
|
-
}
|
|
975
|
-
}
|
|
976
|
-
];
|
|
977
|
-
if (isTypeAware) ruleOptions.push({
|
|
2824
|
+
const getNamingConvention = () => [
|
|
2825
|
+
"error",
|
|
2826
|
+
{
|
|
2827
|
+
selector: "default",
|
|
2828
|
+
format: [
|
|
2829
|
+
"strictCamelCase",
|
|
2830
|
+
"StrictPascalCase",
|
|
2831
|
+
"UPPER_CASE"
|
|
2832
|
+
],
|
|
2833
|
+
leadingUnderscore: "forbid",
|
|
2834
|
+
trailingUnderscore: "forbid"
|
|
2835
|
+
},
|
|
2836
|
+
{
|
|
2837
|
+
selector: ["objectLiteralProperty", "variable"],
|
|
2838
|
+
format: ["strictCamelCase", "UPPER_CASE"]
|
|
2839
|
+
},
|
|
2840
|
+
{
|
|
978
2841
|
selector: "variable",
|
|
979
|
-
|
|
2842
|
+
format: null,
|
|
2843
|
+
modifiers: ["destructured"]
|
|
2844
|
+
},
|
|
2845
|
+
{
|
|
2846
|
+
selector: "typeLike",
|
|
2847
|
+
format: ["StrictPascalCase"]
|
|
2848
|
+
},
|
|
2849
|
+
{
|
|
2850
|
+
selector: "parameter",
|
|
2851
|
+
format: null,
|
|
2852
|
+
filter: {
|
|
2853
|
+
regex: "^_+$",
|
|
2854
|
+
match: false
|
|
2855
|
+
}
|
|
2856
|
+
},
|
|
2857
|
+
{
|
|
2858
|
+
selector: [
|
|
2859
|
+
"classProperty",
|
|
2860
|
+
"objectLiteralProperty",
|
|
2861
|
+
"typeProperty",
|
|
2862
|
+
"classMethod",
|
|
2863
|
+
"objectLiteralMethod",
|
|
2864
|
+
"typeMethod",
|
|
2865
|
+
"accessor",
|
|
2866
|
+
"enumMember"
|
|
2867
|
+
],
|
|
2868
|
+
format: null,
|
|
2869
|
+
modifiers: ["requiresQuotes"]
|
|
2870
|
+
},
|
|
2871
|
+
{
|
|
2872
|
+
selector: "typeParameter",
|
|
980
2873
|
format: ["StrictPascalCase"],
|
|
981
|
-
prefix: [
|
|
982
|
-
|
|
983
|
-
"
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
}
|
|
2874
|
+
prefix: ["T"],
|
|
2875
|
+
custom: {
|
|
2876
|
+
regex: "^[A-Z]",
|
|
2877
|
+
match: true
|
|
2878
|
+
}
|
|
2879
|
+
},
|
|
2880
|
+
{
|
|
2881
|
+
selector: "interface",
|
|
2882
|
+
format: ["StrictPascalCase"],
|
|
2883
|
+
custom: {
|
|
2884
|
+
regex: "^I[A-Z]",
|
|
2885
|
+
match: false
|
|
2886
|
+
}
|
|
2887
|
+
}
|
|
2888
|
+
];
|
|
994
2889
|
const typescript = async (options) => {
|
|
995
2890
|
const { requiredAll: [isTypescriptInstalled, tsEslint] } = await resolvePackages(MODULES.typescript);
|
|
996
2891
|
if (!isTypescriptInstalled || !tsEslint) return [];
|
|
@@ -1002,14 +2897,14 @@ const typescript = async (options) => {
|
|
|
1002
2897
|
typeAware: doesTsConfigExist(resolveTsConfigPath(options)),
|
|
1003
2898
|
...options
|
|
1004
2899
|
};
|
|
1005
|
-
const
|
|
1006
|
-
const files = [
|
|
2900
|
+
const ignoredGlobs = resolvedOptions.ignores ?? [];
|
|
2901
|
+
const files = [.../* @__PURE__ */ new Set([...resolvedOptions.extraFileExtensions.map((extension) => `**/*.${extension}`), ...resolvedOptions.files ?? GLOB_SCRIPT_FILES])];
|
|
1007
2902
|
const hasEnabledTypeAwareness = resolvedOptions.typeAware !== false;
|
|
1008
|
-
const typeAwareOptions = hasEnabledTypeAwareness ? resolveTypeAwareOptions(resolvedOptions, files,
|
|
2903
|
+
const typeAwareOptions = hasEnabledTypeAwareness ? resolveTypeAwareOptions(resolvedOptions, files, ignoredGlobs) : {};
|
|
1009
2904
|
const createParserConfig = (isTypeAware) => ({
|
|
1010
2905
|
name: buildConfigName(MAIN_SCOPES.TYPESCRIPT, `${SUB_SCOPES.PARSER}${isTypeAware ? "-type-aware" : ""}`),
|
|
1011
2906
|
files: isTypeAware ? typeAwareOptions.files : files,
|
|
1012
|
-
ignores: isTypeAware ? typeAwareOptions.ignores :
|
|
2907
|
+
ignores: isTypeAware ? typeAwareOptions.ignores : ignoredGlobs,
|
|
1013
2908
|
languageOptions: {
|
|
1014
2909
|
parser: tsEslint.parser,
|
|
1015
2910
|
parserOptions: {
|
|
@@ -1025,9 +2920,9 @@ const typescript = async (options) => {
|
|
|
1025
2920
|
const createRulesConfig = (isTypeAware) => ({
|
|
1026
2921
|
name: buildConfigName(MAIN_SCOPES.TYPESCRIPT, `${SUB_SCOPES.RULES}${isTypeAware ? "-type-aware" : ""}`),
|
|
1027
2922
|
files: isTypeAware ? typeAwareOptions.files : files,
|
|
1028
|
-
ignores: isTypeAware ? typeAwareOptions.ignores :
|
|
2923
|
+
ignores: isTypeAware ? typeAwareOptions.ignores : ignoredGlobs,
|
|
1029
2924
|
rules: {
|
|
1030
|
-
"ts/naming-convention": getNamingConvention(
|
|
2925
|
+
"ts/naming-convention": getNamingConvention(),
|
|
1031
2926
|
...isTypeAware ? {
|
|
1032
2927
|
...extractRelevantRules(tsEslint.configs.recommendedTypeCheckedOnly, "recommended-type-checked-only"),
|
|
1033
2928
|
...extractRelevantRules(tsEslint.configs.strictTypeCheckedOnly, "strict-type-checked-only"),
|
|
@@ -1038,11 +2933,19 @@ const typescript = async (options) => {
|
|
|
1038
2933
|
"ts/no-unnecessary-qualifier": "error",
|
|
1039
2934
|
"prefer-destructuring": "off",
|
|
1040
2935
|
"ts/prefer-destructuring": "error",
|
|
1041
|
-
"ts/no-unnecessary-type-conversion": "error",
|
|
1042
2936
|
"ts/promise-function-async": "error",
|
|
1043
2937
|
"ts/prefer-readonly": "error",
|
|
1044
2938
|
"ts/require-array-sort-compare": "error",
|
|
1045
|
-
"ts/
|
|
2939
|
+
"ts/no-base-to-string": ["error", { checkUnknown: true }],
|
|
2940
|
+
"ts/no-floating-promises": ["error", { checkThenables: true }],
|
|
2941
|
+
"ts/no-unnecessary-condition": ["error", { checkTypePredicates: true }],
|
|
2942
|
+
"ts/only-throw-error": ["error", { allowThrowingUnknown: false }],
|
|
2943
|
+
"ts/prefer-nullish-coalescing": ["error", { ignoreConditionalTests: false }],
|
|
2944
|
+
"ts/return-await": ["error", "in-try-catch"],
|
|
2945
|
+
"ts/strict-boolean-expressions": ["error", {
|
|
2946
|
+
allowNumber: false,
|
|
2947
|
+
allowString: false
|
|
2948
|
+
}],
|
|
1046
2949
|
"ts/strict-void-return": "error",
|
|
1047
2950
|
"ts/switch-exhaustiveness-check": ["error", { requireDefaultForNonUnion: true }]
|
|
1048
2951
|
} : {
|
|
@@ -1052,7 +2955,36 @@ const typescript = async (options) => {
|
|
|
1052
2955
|
...pluginUnusedImports ? { "ts/no-unused-vars": "off" } : void 0,
|
|
1053
2956
|
"ts/consistent-type-assertions": ["error", { assertionStyle: "angle-bracket" }],
|
|
1054
2957
|
"ts/consistent-type-imports": "error",
|
|
1055
|
-
"ts/member-ordering": "error",
|
|
2958
|
+
"ts/member-ordering": ["error", { default: [
|
|
2959
|
+
"signature",
|
|
2960
|
+
"call-signature",
|
|
2961
|
+
["public-field", "public-accessor"],
|
|
2962
|
+
["protected-field", "protected-accessor"],
|
|
2963
|
+
["private-field", "private-accessor"],
|
|
2964
|
+
["#private-field", "#private-accessor"],
|
|
2965
|
+
"static-initialization",
|
|
2966
|
+
"constructor",
|
|
2967
|
+
[
|
|
2968
|
+
"public-get",
|
|
2969
|
+
"public-set",
|
|
2970
|
+
"public-method"
|
|
2971
|
+
],
|
|
2972
|
+
[
|
|
2973
|
+
"protected-get",
|
|
2974
|
+
"protected-set",
|
|
2975
|
+
"protected-method"
|
|
2976
|
+
],
|
|
2977
|
+
[
|
|
2978
|
+
"private-get",
|
|
2979
|
+
"private-set",
|
|
2980
|
+
"private-method"
|
|
2981
|
+
],
|
|
2982
|
+
[
|
|
2983
|
+
"#private-get",
|
|
2984
|
+
"#private-set",
|
|
2985
|
+
"#private-method"
|
|
2986
|
+
]
|
|
2987
|
+
] }],
|
|
1056
2988
|
"ts/method-signature-style": "error",
|
|
1057
2989
|
"ts/no-import-type-side-effects": "error",
|
|
1058
2990
|
"no-redeclare": "off",
|
|
@@ -1065,6 +2997,7 @@ const typescript = async (options) => {
|
|
|
1065
2997
|
}
|
|
1066
2998
|
}
|
|
1067
2999
|
});
|
|
3000
|
+
const coreRulesCoveredByTypescript = objectFromEntries(objectEntries(extractRelevantRules(tsEslint.configs.recommended, "eslint-recommended")).filter(([, severity]) => severity === "off"));
|
|
1068
3001
|
return [
|
|
1069
3002
|
{
|
|
1070
3003
|
name: buildConfigName(MAIN_SCOPES.TYPESCRIPT, SUB_SCOPES.SETUP),
|
|
@@ -1077,8 +3010,9 @@ const typescript = async (options) => {
|
|
|
1077
3010
|
{
|
|
1078
3011
|
name: buildConfigName(MAIN_SCOPES.TYPESCRIPT, `${SUB_SCOPES.RULES}-typescript`),
|
|
1079
3012
|
files: [GLOB_TS],
|
|
1080
|
-
ignores: typeAwareOptions.ignores ??
|
|
3013
|
+
ignores: typeAwareOptions.ignores ?? ignoredGlobs,
|
|
1081
3014
|
rules: {
|
|
3015
|
+
...coreRulesCoveredByTypescript,
|
|
1082
3016
|
"ts/explicit-function-return-type": "error",
|
|
1083
3017
|
"ts/explicit-member-accessibility": "error"
|
|
1084
3018
|
}
|
|
@@ -1087,9 +3021,129 @@ const typescript = async (options) => {
|
|
|
1087
3021
|
};
|
|
1088
3022
|
//#endregion
|
|
1089
3023
|
//#region ../src/js/eslint/configs/jsdoc.ts
|
|
3024
|
+
/**
|
|
3025
|
+
* @internal @brnshkr/config/eslint
|
|
3026
|
+
*/
|
|
3027
|
+
const TAGS_BY_MODULE = { test: ["vitest-environment", "vitest-environment-options"] };
|
|
3028
|
+
const EXAMPLE_CODE_REGEX = "/```(?:js|javascript|ts|typescript)\\s*\\n([\\s\\S]*?)\\n\\s*```/gv";
|
|
3029
|
+
const TAG_SEQUENCE = [
|
|
3030
|
+
{ tags: [
|
|
3031
|
+
"file",
|
|
3032
|
+
"fileoverview",
|
|
3033
|
+
"overview",
|
|
3034
|
+
"module"
|
|
3035
|
+
] },
|
|
3036
|
+
{ tags: ["api", "internal"] },
|
|
3037
|
+
{ tags: [
|
|
3038
|
+
"deprecated",
|
|
3039
|
+
"ignore",
|
|
3040
|
+
"since",
|
|
3041
|
+
"version",
|
|
3042
|
+
["to", "do"].join("")
|
|
3043
|
+
] },
|
|
3044
|
+
{ tags: [
|
|
3045
|
+
"author",
|
|
3046
|
+
"copyright",
|
|
3047
|
+
"license"
|
|
3048
|
+
] },
|
|
3049
|
+
{ tags: [
|
|
3050
|
+
"summary",
|
|
3051
|
+
"typeSummary",
|
|
3052
|
+
"desc",
|
|
3053
|
+
"description",
|
|
3054
|
+
"classdesc"
|
|
3055
|
+
] },
|
|
3056
|
+
{ tags: [
|
|
3057
|
+
"namespace",
|
|
3058
|
+
"category",
|
|
3059
|
+
"package"
|
|
3060
|
+
] },
|
|
3061
|
+
{ tags: ["import"] },
|
|
3062
|
+
{ tags: ["typedef"] },
|
|
3063
|
+
{ tags: ["template"] },
|
|
3064
|
+
{ tags: [
|
|
3065
|
+
"augments",
|
|
3066
|
+
"extends",
|
|
3067
|
+
"implements"
|
|
3068
|
+
] },
|
|
3069
|
+
{ tags: ["readonly"] },
|
|
3070
|
+
{ tags: [
|
|
3071
|
+
"override",
|
|
3072
|
+
"requires",
|
|
3073
|
+
"mixes",
|
|
3074
|
+
"mixin",
|
|
3075
|
+
"mixinClass",
|
|
3076
|
+
"mixinFunction",
|
|
3077
|
+
"borrows",
|
|
3078
|
+
"constructs",
|
|
3079
|
+
"lends",
|
|
3080
|
+
"final",
|
|
3081
|
+
"global",
|
|
3082
|
+
"abstract",
|
|
3083
|
+
"virtual",
|
|
3084
|
+
"static",
|
|
3085
|
+
"private",
|
|
3086
|
+
"protected",
|
|
3087
|
+
"public",
|
|
3088
|
+
"access",
|
|
3089
|
+
"const",
|
|
3090
|
+
"constant",
|
|
3091
|
+
"variation",
|
|
3092
|
+
"var",
|
|
3093
|
+
"member",
|
|
3094
|
+
"memberof",
|
|
3095
|
+
"inner",
|
|
3096
|
+
"instance",
|
|
3097
|
+
"inheritdoc",
|
|
3098
|
+
"inheritDoc",
|
|
3099
|
+
"hideconstructor"
|
|
3100
|
+
] },
|
|
3101
|
+
{ tags: ["name"] },
|
|
3102
|
+
{ tags: [
|
|
3103
|
+
"this",
|
|
3104
|
+
"interface",
|
|
3105
|
+
"enum",
|
|
3106
|
+
"event",
|
|
3107
|
+
"kind",
|
|
3108
|
+
"type",
|
|
3109
|
+
"alias",
|
|
3110
|
+
"external",
|
|
3111
|
+
"host",
|
|
3112
|
+
"async",
|
|
3113
|
+
"callback",
|
|
3114
|
+
"func",
|
|
3115
|
+
"function",
|
|
3116
|
+
"method",
|
|
3117
|
+
"class",
|
|
3118
|
+
"constructor",
|
|
3119
|
+
"generator",
|
|
3120
|
+
"fires",
|
|
3121
|
+
"emits",
|
|
3122
|
+
"listens"
|
|
3123
|
+
] },
|
|
3124
|
+
{ tags: ["prop", "property"] },
|
|
3125
|
+
{ tags: [
|
|
3126
|
+
"param",
|
|
3127
|
+
"arg",
|
|
3128
|
+
"argument"
|
|
3129
|
+
] },
|
|
3130
|
+
{ tags: ["return", "returns"] },
|
|
3131
|
+
{ tags: ["yield", "yields"] },
|
|
3132
|
+
{ tags: ["throws", "exception"] },
|
|
3133
|
+
{ tags: ["satisfies"] },
|
|
3134
|
+
{ tags: ["default", "defaultvalue"] },
|
|
3135
|
+
{ tags: ["exports"] },
|
|
3136
|
+
{ tags: [
|
|
3137
|
+
"link",
|
|
3138
|
+
"see",
|
|
3139
|
+
"tutorial"
|
|
3140
|
+
] },
|
|
3141
|
+
{ tags: ["example"] }
|
|
3142
|
+
];
|
|
1090
3143
|
const jsdoc = async () => {
|
|
1091
|
-
const { requiredAll: [pluginJsdoc], optional: [
|
|
3144
|
+
const { requiredAll: [pluginJsdoc], optional: [jsdocProcessorModule] } = await resolvePackages(MODULES.jsdoc);
|
|
1092
3145
|
if (!pluginJsdoc) return [];
|
|
3146
|
+
const definedTags = ["api", ...isModuleEnabled(MODULES.test) ? TAGS_BY_MODULE.test : []];
|
|
1093
3147
|
const createSetupConfig = (isForTypescript) => ({
|
|
1094
3148
|
name: buildConfigName(MAIN_SCOPES.JSDOC, `${SUB_SCOPES.SETUP}${isForTypescript ? "-typescript" : ""}`),
|
|
1095
3149
|
...isForTypescript ? void 0 : { plugins: { jsdoc: pluginJsdoc } },
|
|
@@ -1100,10 +3154,15 @@ const jsdoc = async () => {
|
|
|
1100
3154
|
files: isForTypescript ? [GLOB_TS] : [GLOB_TS, ...GLOB_SCRIPT_FILES_WITHOUT_TS],
|
|
1101
3155
|
rules: { ...isForTypescript ? {
|
|
1102
3156
|
...Object.fromEntries(objectEntries(pluginJsdoc.configs["flat/recommended-typescript-error"].rules ?? {}).map(([key, value]) => Object.is(pluginJsdoc.configs["flat/recommended-error"].rules?.[key], value) ? void 0 : [key, value]).filter(Boolean)),
|
|
3157
|
+
"jsdoc/check-tag-names": ["error", {
|
|
3158
|
+
definedTags,
|
|
3159
|
+
typed: true
|
|
3160
|
+
}],
|
|
1103
3161
|
"jsdoc/require-param": "off",
|
|
1104
3162
|
"jsdoc/require-returns": "off"
|
|
1105
3163
|
} : {
|
|
1106
3164
|
...pluginJsdoc.configs["flat/recommended-error"].rules,
|
|
3165
|
+
"jsdoc/check-tag-names": ["error", { definedTags }],
|
|
1107
3166
|
"jsdoc/check-indentation": ["error", { allowIndentedSections: true }],
|
|
1108
3167
|
"jsdoc/check-line-alignment": "error",
|
|
1109
3168
|
"jsdoc/check-syntax": "error",
|
|
@@ -1116,123 +3175,21 @@ const jsdoc = async () => {
|
|
|
1116
3175
|
noSingleLineBlocks: true,
|
|
1117
3176
|
singleLineTags: []
|
|
1118
3177
|
}],
|
|
1119
|
-
"jsdoc/no-bad-blocks": "error",
|
|
3178
|
+
"jsdoc/no-bad-blocks": ["error", { preventAllMultiAsteriskBlocks: true }],
|
|
1120
3179
|
"jsdoc/no-blank-block-descriptions": "error",
|
|
1121
3180
|
"jsdoc/no-blank-blocks": "error",
|
|
3181
|
+
"jsdoc/no-unnecessary-type-assertion": ["error", { preferConstToLiteralTuples: true }],
|
|
3182
|
+
"jsdoc/normalize-see-links": "error",
|
|
1122
3183
|
"jsdoc/prefer-import-tag": "error",
|
|
1123
|
-
"jsdoc/require-description-complete-sentence": "error",
|
|
1124
3184
|
"jsdoc/require-asterisk-prefix": "error",
|
|
1125
|
-
"jsdoc/require-hyphen-before-param-description": "error",
|
|
3185
|
+
"jsdoc/require-hyphen-before-param-description": ["error", "never"],
|
|
1126
3186
|
"jsdoc/require-param-description": "off",
|
|
1127
3187
|
"jsdoc/require-property-description": "off",
|
|
1128
3188
|
"jsdoc/require-returns-description": "off",
|
|
1129
3189
|
"jsdoc/require-jsdoc": "off",
|
|
1130
3190
|
"jsdoc/require-template": "error",
|
|
1131
3191
|
"jsdoc/require-throws": "error",
|
|
1132
|
-
"jsdoc/sort-tags": ["error", { tagSequence:
|
|
1133
|
-
{ tags: ["import"] },
|
|
1134
|
-
{ tags: [
|
|
1135
|
-
"deprecated",
|
|
1136
|
-
"ignore",
|
|
1137
|
-
"since",
|
|
1138
|
-
"version",
|
|
1139
|
-
["to", "do"].join("")
|
|
1140
|
-
] },
|
|
1141
|
-
{ tags: [
|
|
1142
|
-
"author",
|
|
1143
|
-
"copyright",
|
|
1144
|
-
"license"
|
|
1145
|
-
] },
|
|
1146
|
-
{ tags: ["summary", "typeSummary"] },
|
|
1147
|
-
{ tags: [
|
|
1148
|
-
"desc",
|
|
1149
|
-
"description",
|
|
1150
|
-
"classdesc"
|
|
1151
|
-
] },
|
|
1152
|
-
{ tags: [
|
|
1153
|
-
"internal",
|
|
1154
|
-
"namespace",
|
|
1155
|
-
"category",
|
|
1156
|
-
"package",
|
|
1157
|
-
"file",
|
|
1158
|
-
"fileoverview",
|
|
1159
|
-
"overview",
|
|
1160
|
-
"module",
|
|
1161
|
-
"override",
|
|
1162
|
-
"requires",
|
|
1163
|
-
"implements",
|
|
1164
|
-
"mixes",
|
|
1165
|
-
"mixin",
|
|
1166
|
-
"mixinClass",
|
|
1167
|
-
"mixinFunction",
|
|
1168
|
-
"borrows",
|
|
1169
|
-
"constructs",
|
|
1170
|
-
"lends",
|
|
1171
|
-
"final",
|
|
1172
|
-
"global",
|
|
1173
|
-
"readonly",
|
|
1174
|
-
"abstract",
|
|
1175
|
-
"virtual",
|
|
1176
|
-
"static",
|
|
1177
|
-
"private",
|
|
1178
|
-
"protected",
|
|
1179
|
-
"public",
|
|
1180
|
-
"access",
|
|
1181
|
-
"const",
|
|
1182
|
-
"constant",
|
|
1183
|
-
"variation",
|
|
1184
|
-
"var",
|
|
1185
|
-
"member",
|
|
1186
|
-
"memberof",
|
|
1187
|
-
"inner",
|
|
1188
|
-
"instance",
|
|
1189
|
-
"inheritdoc",
|
|
1190
|
-
"inheritDoc",
|
|
1191
|
-
"hideconstructor"
|
|
1192
|
-
] },
|
|
1193
|
-
{ tags: [
|
|
1194
|
-
"this",
|
|
1195
|
-
"interface",
|
|
1196
|
-
"enum",
|
|
1197
|
-
"event",
|
|
1198
|
-
"augments",
|
|
1199
|
-
"extends",
|
|
1200
|
-
"name",
|
|
1201
|
-
"kind",
|
|
1202
|
-
"type",
|
|
1203
|
-
"alias",
|
|
1204
|
-
"external",
|
|
1205
|
-
"host",
|
|
1206
|
-
"async",
|
|
1207
|
-
"callback",
|
|
1208
|
-
"func",
|
|
1209
|
-
"function",
|
|
1210
|
-
"method",
|
|
1211
|
-
"class",
|
|
1212
|
-
"constructor",
|
|
1213
|
-
"generator",
|
|
1214
|
-
"fires",
|
|
1215
|
-
"emits",
|
|
1216
|
-
"listens"
|
|
1217
|
-
] },
|
|
1218
|
-
{ tags: ["template"] },
|
|
1219
|
-
{ tags: ["typedef"] },
|
|
1220
|
-
{ tags: [
|
|
1221
|
-
"param",
|
|
1222
|
-
"arg",
|
|
1223
|
-
"argument",
|
|
1224
|
-
"prop",
|
|
1225
|
-
"property"
|
|
1226
|
-
] },
|
|
1227
|
-
{ tags: ["return", "returns"] },
|
|
1228
|
-
{ tags: ["yield", "yields"] },
|
|
1229
|
-
{ tags: ["throws", "exception"] },
|
|
1230
|
-
{ tags: ["satisfies"] },
|
|
1231
|
-
{ tags: ["default", "defaultvalue"] },
|
|
1232
|
-
{ tags: ["exports"] },
|
|
1233
|
-
{ tags: ["see", "tutorial"] },
|
|
1234
|
-
{ tags: ["example"] }
|
|
1235
|
-
] }],
|
|
3192
|
+
"jsdoc/sort-tags": ["error", { tagSequence: TAG_SEQUENCE }],
|
|
1236
3193
|
"jsdoc/tag-lines": [
|
|
1237
3194
|
"error",
|
|
1238
3195
|
"any",
|
|
@@ -1249,11 +3206,15 @@ const jsdoc = async () => {
|
|
|
1249
3206
|
} }
|
|
1250
3207
|
});
|
|
1251
3208
|
const parser = await getTsEslintParserIfExists();
|
|
1252
|
-
const examplePlugin =
|
|
3209
|
+
const examplePlugin = jsdocProcessorModule ? jsdocProcessorModule.getJsdocProcessorPlugin({
|
|
1253
3210
|
checkDefaults: true,
|
|
1254
3211
|
checkExamples: true,
|
|
1255
3212
|
checkParams: true,
|
|
1256
3213
|
checkProperties: true,
|
|
3214
|
+
exampleCodeRegex: EXAMPLE_CODE_REGEX,
|
|
3215
|
+
matchingFileNameDefaults: "dummy.jsdoc-defaults.md/*.js",
|
|
3216
|
+
matchingFileNameParams: "dummy.jsdoc-params.md/*.js",
|
|
3217
|
+
matchingFileNameProperties: "dummy.jsdoc-properties.md/*.js",
|
|
1257
3218
|
parser
|
|
1258
3219
|
}) : void 0;
|
|
1259
3220
|
return [
|
|
@@ -1273,6 +3234,9 @@ const jsdoc = async () => {
|
|
|
1273
3234
|
};
|
|
1274
3235
|
//#endregion
|
|
1275
3236
|
//#region ../src/js/eslint/configs/json.ts
|
|
3237
|
+
/**
|
|
3238
|
+
* @internal @brnshkr/config/eslint
|
|
3239
|
+
*/
|
|
1276
3240
|
const TSCONFIG_FILES = ["**/tsconfig.json", "**/tsconfig.*.json"];
|
|
1277
3241
|
const JSON_FILES_TO_TREAT_AS_JSONC = [...TSCONFIG_FILES, ".vscode/**/*.json"];
|
|
1278
3242
|
const LANGUAGE_TO_GLOB_MAP = {
|
|
@@ -1358,6 +3322,7 @@ const getJsoncSortConfigs = () => [
|
|
|
1358
3322
|
"lint-staged",
|
|
1359
3323
|
"eslintConfig",
|
|
1360
3324
|
"stylelint",
|
|
3325
|
+
"markdownlint-cli2",
|
|
1361
3326
|
"prettier",
|
|
1362
3327
|
"ava",
|
|
1363
3328
|
"stackblitz",
|
|
@@ -1733,7 +3698,7 @@ const json = async () => {
|
|
|
1733
3698
|
{ emptyObjects: "never" }
|
|
1734
3699
|
],
|
|
1735
3700
|
"jsonc/object-property-newline": "error",
|
|
1736
|
-
"jsonc/quotes":
|
|
3701
|
+
"jsonc/quotes": "error"
|
|
1737
3702
|
} : void 0
|
|
1738
3703
|
}
|
|
1739
3704
|
});
|
|
@@ -1756,8 +3721,14 @@ const json = async () => {
|
|
|
1756
3721
|
};
|
|
1757
3722
|
//#endregion
|
|
1758
3723
|
//#region ../src/js/eslint/configs/markdown.ts
|
|
3724
|
+
/**
|
|
3725
|
+
* @internal @brnshkr/config/eslint
|
|
3726
|
+
*/
|
|
1759
3727
|
const extractRelevantValues = (identifier, configs, key) => {
|
|
1760
|
-
for (const config of configs)
|
|
3728
|
+
for (const config of configs) {
|
|
3729
|
+
const value = config[identifier] ?? void 0;
|
|
3730
|
+
if (value !== void 0 && config.name === `markdown/${key}`) return value;
|
|
3731
|
+
}
|
|
1761
3732
|
throw new Error(`Expected key "${key}" to be contained in given config.`);
|
|
1762
3733
|
};
|
|
1763
3734
|
const markdown = async (options) => {
|
|
@@ -1808,7 +3779,10 @@ const markdown = async (options) => {
|
|
|
1808
3779
|
"summary",
|
|
1809
3780
|
"sup",
|
|
1810
3781
|
"var"
|
|
1811
|
-
] }]
|
|
3782
|
+
] }],
|
|
3783
|
+
"markdown/no-space-in-emphasis": ["error", { checkStrikethrough: true }],
|
|
3784
|
+
"markdown/table-column-count": ["error", { checkMissingCells: true }],
|
|
3785
|
+
...isModuleEnabled(MODULES.unicorn) ? { "unicorn/no-missing-local-resource": "error" } : void 0
|
|
1812
3786
|
}
|
|
1813
3787
|
},
|
|
1814
3788
|
{
|
|
@@ -1822,10 +3796,12 @@ const markdown = async (options) => {
|
|
|
1822
3796
|
"no-inline-comments": "off",
|
|
1823
3797
|
"no-magic-numbers": "off",
|
|
1824
3798
|
"import/no-default-export": "off",
|
|
3799
|
+
"import/no-extraneous-dependencies": "off",
|
|
1825
3800
|
"import/unambiguous": "off",
|
|
1826
3801
|
"node/no-missing-import": "off",
|
|
1827
3802
|
"ts/no-redeclare": "off",
|
|
1828
3803
|
"ts/no-unused-vars": "off",
|
|
3804
|
+
"unicorn/no-barrel-files": "off",
|
|
1829
3805
|
"unused/no-unused-imports": "off",
|
|
1830
3806
|
"unused/no-unused-vars": "off"
|
|
1831
3807
|
}
|
|
@@ -1834,6 +3810,9 @@ const markdown = async (options) => {
|
|
|
1834
3810
|
};
|
|
1835
3811
|
//#endregion
|
|
1836
3812
|
//#region ../src/js/eslint/configs/node.ts
|
|
3813
|
+
/**
|
|
3814
|
+
* @internal @brnshkr/config/eslint
|
|
3815
|
+
*/
|
|
1837
3816
|
const node = async (options) => {
|
|
1838
3817
|
const { requiredAll: [pluginNode] } = await resolvePackages(MODULES.node);
|
|
1839
3818
|
if (!pluginNode) return [];
|
|
@@ -1849,12 +3828,12 @@ const node = async (options) => {
|
|
|
1849
3828
|
"node/exports-style": "error",
|
|
1850
3829
|
"node/global-require": "error",
|
|
1851
3830
|
"node/handle-callback-err": "error",
|
|
1852
|
-
"node/no-missing-import": "error",
|
|
1853
3831
|
"node/no-mixed-requires": "error",
|
|
1854
3832
|
"node/no-new-require": "error",
|
|
1855
3833
|
"node/no-path-concat": "error",
|
|
1856
3834
|
"node/no-process-env": "error",
|
|
1857
3835
|
"node/no-sync": "error",
|
|
3836
|
+
"node/no-unpublished-bin": "error",
|
|
1858
3837
|
"node/prefer-global/buffer": "error",
|
|
1859
3838
|
"node/prefer-global/console": "error",
|
|
1860
3839
|
"node/prefer-global/crypto": "error",
|
|
@@ -1864,17 +3843,29 @@ const node = async (options) => {
|
|
|
1864
3843
|
"node/prefer-global/timers": "error",
|
|
1865
3844
|
"node/prefer-global/url-search-params": "error",
|
|
1866
3845
|
"node/prefer-global/url": "error",
|
|
3846
|
+
"node/prefer-import/assert-strict": "error",
|
|
1867
3847
|
"node/prefer-node-protocol": "error",
|
|
3848
|
+
"node/prefer-process-get-builtin-module": "error",
|
|
1868
3849
|
"node/prefer-promises/dns": "error",
|
|
1869
|
-
"node/prefer-promises/fs": "error"
|
|
3850
|
+
"node/prefer-promises/fs": "error",
|
|
3851
|
+
...isModuleEnabled(MODULES.import) && doAllPackagesExist([ESLINT_PACKAGES.ESLINT_PLUGIN_IMPORT_X]) ? {
|
|
3852
|
+
"node/no-extraneous-import": "off",
|
|
3853
|
+
"node/no-extraneous-require": "off"
|
|
3854
|
+
} : void 0
|
|
1870
3855
|
}
|
|
1871
3856
|
}];
|
|
1872
3857
|
};
|
|
1873
3858
|
//#endregion
|
|
1874
3859
|
//#region ../src/js/eslint/configs/unicorn.ts
|
|
3860
|
+
/**
|
|
3861
|
+
* @internal @brnshkr/config/eslint
|
|
3862
|
+
*/
|
|
1875
3863
|
const FILE_NAMES_TO_IGNORE = [
|
|
3864
|
+
"__mocks__",
|
|
3865
|
+
"__tests__",
|
|
1876
3866
|
"ACKNOWLEDGMENTS.md",
|
|
1877
3867
|
"ADOPTERS.md",
|
|
3868
|
+
"AGENT_INSTRUCTIONS.md",
|
|
1878
3869
|
"AGENTS.md",
|
|
1879
3870
|
"API_REFERENCE.md",
|
|
1880
3871
|
"ARCHITECTURE.md",
|
|
@@ -1900,6 +3891,7 @@ const FILE_NAMES_TO_IGNORE = [
|
|
|
1900
3891
|
"FAQ.md",
|
|
1901
3892
|
"GOVERNANCE.md",
|
|
1902
3893
|
"INSTALL.md",
|
|
3894
|
+
"INSTRUCTIONS.md",
|
|
1903
3895
|
"ISSUE_TEMPLATE.md",
|
|
1904
3896
|
"LICENSE.md",
|
|
1905
3897
|
"LLMS.md",
|
|
@@ -1923,10 +3915,10 @@ const FILE_NAMES_TO_IGNORE = [
|
|
|
1923
3915
|
"RELEASING.md",
|
|
1924
3916
|
"RESEARCH.md",
|
|
1925
3917
|
"ROADMAP.md",
|
|
1926
|
-
"SKILL.md",
|
|
1927
|
-
"SPEC.md",
|
|
1928
3918
|
"SECURITY_POLICY.md",
|
|
1929
3919
|
"SECURITY.md",
|
|
3920
|
+
"SKILL.md",
|
|
3921
|
+
"SPEC.md",
|
|
1930
3922
|
"STYLE_GUIDE.md",
|
|
1931
3923
|
"SUPPORT.md",
|
|
1932
3924
|
"TESTING.md",
|
|
@@ -1936,6 +3928,21 @@ const FILE_NAMES_TO_IGNORE = [
|
|
|
1936
3928
|
"UPGRADE_NOTES.md",
|
|
1937
3929
|
"VERSIONING.md"
|
|
1938
3930
|
];
|
|
3931
|
+
const COMMENT_TERMS = [
|
|
3932
|
+
"commitlint",
|
|
3933
|
+
"JSDoc",
|
|
3934
|
+
"PHP",
|
|
3935
|
+
"PHPStan",
|
|
3936
|
+
"PHPUnit",
|
|
3937
|
+
"pnpm",
|
|
3938
|
+
"PostCSS",
|
|
3939
|
+
"SCSS",
|
|
3940
|
+
"Stylelint",
|
|
3941
|
+
"Symfony",
|
|
3942
|
+
"TOML",
|
|
3943
|
+
"TSDoc",
|
|
3944
|
+
"Vitest"
|
|
3945
|
+
];
|
|
1939
3946
|
const unicorn = async () => {
|
|
1940
3947
|
const { requiredAll: [pluginUnicorn] } = await resolvePackages(MODULES.unicorn);
|
|
1941
3948
|
if (!pluginUnicorn) return [];
|
|
@@ -1947,30 +3954,114 @@ const unicorn = async () => {
|
|
|
1947
3954
|
files: GLOB_SCRIPT_FILES,
|
|
1948
3955
|
rules: {
|
|
1949
3956
|
...pluginUnicorn.configs.recommended.rules,
|
|
1950
|
-
"
|
|
3957
|
+
"no-else-return": "off",
|
|
3958
|
+
"no-useless-concat": "off",
|
|
3959
|
+
"operator-assignment": "off",
|
|
3960
|
+
"unicorn/comment-content": ["error", { replacements: {
|
|
3961
|
+
[String.raw`\bapplication\b(?!/)`]: false,
|
|
3962
|
+
[String.raw`\bapplications\b`]: false,
|
|
3963
|
+
...Object.fromEntries(COMMENT_TERMS.map((term) => [String.raw`\b${term}\b`, {
|
|
3964
|
+
replacement: term,
|
|
3965
|
+
caseSensitive: false
|
|
3966
|
+
}]))
|
|
3967
|
+
} }],
|
|
3968
|
+
"unicorn/consistent-boolean-name": "off",
|
|
3969
|
+
"unicorn/consistent-class-member-order": ["error", { order: [
|
|
3970
|
+
"public-field",
|
|
3971
|
+
"static-field",
|
|
3972
|
+
"private-field",
|
|
3973
|
+
"static-block",
|
|
3974
|
+
"constructor",
|
|
3975
|
+
"public-method",
|
|
3976
|
+
"static-method",
|
|
3977
|
+
"private-method"
|
|
3978
|
+
] }],
|
|
3979
|
+
"unicorn/consistent-conditional-object-spread": ["error", "ternary"],
|
|
1951
3980
|
"unicorn/consistent-destructuring": "error",
|
|
3981
|
+
"func-style": "off",
|
|
3982
|
+
"unicorn/consistent-function-style": ["error", {
|
|
3983
|
+
defaultExport: "arrow-function",
|
|
3984
|
+
namedExports: "arrow-function",
|
|
3985
|
+
namedFunctions: "arrow-function",
|
|
3986
|
+
objectProperties: "arrow-function",
|
|
3987
|
+
reassignedVariables: "arrow-function",
|
|
3988
|
+
typedVariables: "arrow-function"
|
|
3989
|
+
}],
|
|
1952
3990
|
"unicorn/custom-error-definition": "error",
|
|
1953
3991
|
"unicorn/filename-case": ["error", {
|
|
1954
3992
|
case: "kebabCase",
|
|
1955
3993
|
ignore: FILE_NAMES_TO_IGNORE
|
|
1956
3994
|
}],
|
|
1957
3995
|
"unicorn/require-post-message-target-origin": "error",
|
|
3996
|
+
"unicorn/iteration-fallback-style": ["error", "fallback"],
|
|
3997
|
+
"unicorn/name-replacements": ["error", {
|
|
3998
|
+
replacements: {
|
|
3999
|
+
application: false,
|
|
4000
|
+
applications: false,
|
|
4001
|
+
repository: false
|
|
4002
|
+
},
|
|
4003
|
+
ignore: ["[Ii]nheritDoc", String.raw`\.dist$`]
|
|
4004
|
+
}],
|
|
4005
|
+
"unicorn/no-accidental-bitwise-operator": "off",
|
|
4006
|
+
"unicorn/no-array-front-mutation": "error",
|
|
4007
|
+
"unicorn/no-array-reduce": ["error", { allowSimpleOperations: false }],
|
|
4008
|
+
"unicorn/no-array-reverse": ["error", { allowExpressionStatement: false }],
|
|
4009
|
+
"unicorn/no-array-sort": ["error", { allowExpressionStatement: false }],
|
|
4010
|
+
"unicorn/no-barrel-files": "error",
|
|
4011
|
+
"unicorn/no-instanceof-builtins": ["error", { useErrorIsError: true }],
|
|
4012
|
+
"unicorn/no-invalid-file-input-accept": "error",
|
|
1958
4013
|
"unicorn/no-keyword-prefix": "error",
|
|
1959
|
-
"unicorn/no-
|
|
1960
|
-
"no-
|
|
4014
|
+
"unicorn/no-manually-wrapped-comments": "error",
|
|
4015
|
+
"unicorn/no-missing-local-resource": "error",
|
|
4016
|
+
"unicorn/no-negated-comparison": ["error", { checkLogicalExpressions: true }],
|
|
4017
|
+
"unicorn/no-null": ["error", { checkStrictEquality: true }],
|
|
4018
|
+
"unicorn/no-typeof-undefined": ["error", { checkGlobalVariables: true }],
|
|
4019
|
+
"unicorn/no-unsafe-dom-html": "error",
|
|
1961
4020
|
"unicorn/no-unused-properties": "error",
|
|
1962
|
-
"unicorn/
|
|
4021
|
+
"unicorn/numeric-separators-style": ["error", {
|
|
4022
|
+
binary: {
|
|
4023
|
+
minimumDigits: 9,
|
|
4024
|
+
groupLength: 8
|
|
4025
|
+
},
|
|
4026
|
+
hexadecimal: {
|
|
4027
|
+
minimumDigits: 3,
|
|
4028
|
+
groupLength: 2
|
|
4029
|
+
},
|
|
4030
|
+
number: {
|
|
4031
|
+
minimumDigits: 4,
|
|
4032
|
+
groupLength: 3,
|
|
4033
|
+
fractionGroupLength: 3
|
|
4034
|
+
},
|
|
4035
|
+
octal: {
|
|
4036
|
+
minimumDigits: 4,
|
|
4037
|
+
groupLength: 3
|
|
4038
|
+
}
|
|
4039
|
+
}],
|
|
4040
|
+
"unicorn/prefer-dispose": "error",
|
|
4041
|
+
"unicorn/prefer-error-is-error": "error",
|
|
4042
|
+
"unicorn/prefer-minimal-ternary": ["error", {
|
|
4043
|
+
checkComputedMemberAccess: true,
|
|
4044
|
+
checkVaryingBase: true
|
|
4045
|
+
}],
|
|
4046
|
+
"unicorn/prefer-queue-microtask": ["error", {
|
|
4047
|
+
checkSetImmediate: true,
|
|
4048
|
+
checkSetTimeout: true
|
|
4049
|
+
}],
|
|
4050
|
+
"unicorn/prefer-regexp-escape": "error",
|
|
4051
|
+
"unicorn/prefer-short-arrow-method": "error",
|
|
1963
4052
|
"unicorn/prefer-switch": "off",
|
|
1964
|
-
"unicorn/
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
"unicorn/text-encoding-identifier-case": ["error", { withDash: true }]
|
|
4053
|
+
"unicorn/require-css-escape": ["error", { checkAllSelectors: true }],
|
|
4054
|
+
"unicorn/string-content": ["error", { patterns: { "\\.\\.\\.": "…" } }],
|
|
4055
|
+
"unicorn/text-encoding-identifier-case": ["error", { withDash: true }],
|
|
4056
|
+
"unicorn/try-complexity": "error"
|
|
1969
4057
|
}
|
|
1970
4058
|
}];
|
|
1971
4059
|
};
|
|
1972
4060
|
//#endregion
|
|
1973
4061
|
//#region ../src/js/eslint/configs/overrides.ts
|
|
4062
|
+
/**
|
|
4063
|
+
* @internal @brnshkr/config/eslint
|
|
4064
|
+
*/
|
|
1974
4065
|
const jsOverrides = [{
|
|
1975
4066
|
name: buildConfigName(MAIN_SCOPES.OVERRIDES, `${MAIN_SCOPES.JAVASCRIPT}/scripts`),
|
|
1976
4067
|
files: ["scripts/**/*.?(c)js"],
|
|
@@ -2018,6 +4109,25 @@ const tsOverrides = isModuleEnabled(MODULES.typescript) ? [{
|
|
|
2018
4109
|
"import/no-named-as-default-member": "off"
|
|
2019
4110
|
}
|
|
2020
4111
|
}] : [];
|
|
4112
|
+
const importOverrides = isModuleEnabled(MODULES.import) && isModuleEnabled(MODULES.typescript) ? [{
|
|
4113
|
+
name: buildConfigName(MAIN_SCOPES.OVERRIDES, `${MAIN_SCOPES.IMPORT}/${MAIN_SCOPES.TYPESCRIPT}`),
|
|
4114
|
+
files: [GLOB_TS],
|
|
4115
|
+
rules: {
|
|
4116
|
+
"import/default": "off",
|
|
4117
|
+
"import/named": "off",
|
|
4118
|
+
"import/no-named-as-default-member": "off",
|
|
4119
|
+
"import/no-unresolved": "off"
|
|
4120
|
+
}
|
|
4121
|
+
}] : [];
|
|
4122
|
+
const buildTypeAwareImportOverrides = (typescriptOptions) => {
|
|
4123
|
+
const resolvedOptions = typeof typescriptOptions === "object" ? typescriptOptions : void 0;
|
|
4124
|
+
return isModuleEnabled(MODULES.import) && isModuleEnabled(MODULES.typescript) && (resolvedOptions?.typeAware ?? doesTsConfigExist(resolveTsConfigPath(resolvedOptions))) !== false ? [{
|
|
4125
|
+
name: buildConfigName(MAIN_SCOPES.OVERRIDES, `${MAIN_SCOPES.IMPORT}/type-aware`),
|
|
4126
|
+
files: [GLOB_TS],
|
|
4127
|
+
ignores: DEFAULT_TYPE_AWARE_IGNORES,
|
|
4128
|
+
rules: { "import/no-deprecated": "off" }
|
|
4129
|
+
}] : [];
|
|
4130
|
+
};
|
|
2021
4131
|
const testOverrides = isModuleEnabled(MODULES.test) ? [{
|
|
2022
4132
|
name: buildConfigName(MAIN_SCOPES.OVERRIDES, `${MAIN_SCOPES.TEST}/general`),
|
|
2023
4133
|
files: GLOB_TEST_FILES,
|
|
@@ -2028,46 +4138,62 @@ const unicornOverrides = isModuleEnabled(MODULES.unicorn) ? [{
|
|
|
2028
4138
|
files: [...GLOB_SCRIPT_FILES.map((glob) => `**/classes/${glob}`), ...GLOB_SCRIPT_FILES.map((glob) => `**/errors/${glob}`)],
|
|
2029
4139
|
rules: { "unicorn/filename-case": ["error", {
|
|
2030
4140
|
case: "pascalCase",
|
|
4141
|
+
checkDirectories: false,
|
|
2031
4142
|
ignore: FILE_NAMES_TO_IGNORE
|
|
2032
4143
|
}] }
|
|
2033
4144
|
}] : [];
|
|
2034
|
-
const jsdocOverrides = isModuleEnabled(MODULES.jsdoc) ? [
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
"
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
4145
|
+
const jsdocOverrides = isModuleEnabled(MODULES.jsdoc) ? [
|
|
4146
|
+
...isModuleEnabled(MODULES[packageOrganization]) ? [{
|
|
4147
|
+
name: buildConfigName(MAIN_SCOPES.OVERRIDES, `${MAIN_SCOPES.JSDOC}/internal-tag`),
|
|
4148
|
+
files: GLOB_SCRIPT_FILES,
|
|
4149
|
+
settings: { jsdoc: { structuredTags: { internal: {
|
|
4150
|
+
name: "text",
|
|
4151
|
+
type: false
|
|
4152
|
+
} } } },
|
|
4153
|
+
rules: { "jsdoc/empty-tags": "off" }
|
|
4154
|
+
}] : [],
|
|
4155
|
+
{
|
|
4156
|
+
name: buildConfigName(MAIN_SCOPES.OVERRIDES, `${MAIN_SCOPES.JSDOC}/${SUB_SCOPES.EXAMPLES}`),
|
|
4157
|
+
files: [GLOB_EXAMPLES],
|
|
4158
|
+
rules: {
|
|
4159
|
+
"no-console": "off",
|
|
4160
|
+
"no-undef": "off",
|
|
4161
|
+
"no-unused-expressions": "off",
|
|
4162
|
+
"no-unused-vars": "off",
|
|
4163
|
+
"node/no-missing-import": "off",
|
|
4164
|
+
"node/no-missing-require": "off",
|
|
4165
|
+
strict: "off",
|
|
4166
|
+
"import/no-unresolved": "off",
|
|
4167
|
+
"import/no-self-import": "off",
|
|
4168
|
+
"import/unambiguous": "off",
|
|
4169
|
+
"style/eol-last": "off",
|
|
4170
|
+
"style/no-multiple-empty-lines": "off",
|
|
4171
|
+
"ts/no-unused-expressions": "off",
|
|
4172
|
+
"ts/no-unused-vars": "off",
|
|
4173
|
+
"unicorn/no-null": "off",
|
|
4174
|
+
"unicorn/no-useless-undefined": "off",
|
|
4175
|
+
"unused/no-unused-imports": "off",
|
|
4176
|
+
"unused/no-unused-vars": "off"
|
|
4177
|
+
}
|
|
4178
|
+
},
|
|
4179
|
+
{
|
|
4180
|
+
name: buildConfigName(MAIN_SCOPES.OVERRIDES, `${MAIN_SCOPES.JSDOC}/default-expressions`),
|
|
4181
|
+
files: [
|
|
4182
|
+
"**/*.jsdoc-defaults.md/*.js",
|
|
4183
|
+
"**/*.jsdoc-params.md/*.js",
|
|
4184
|
+
"**/*.jsdoc-properties.md/*.js"
|
|
4185
|
+
],
|
|
4186
|
+
rules: {
|
|
4187
|
+
"no-empty-function": "off",
|
|
4188
|
+
"no-new": "off",
|
|
4189
|
+
strict: "off",
|
|
4190
|
+
"import/unambiguous": "off",
|
|
4191
|
+
"style/eol-last": "off",
|
|
4192
|
+
"style/no-extra-parens": "off",
|
|
4193
|
+
"style/semi": "off"
|
|
4194
|
+
}
|
|
2069
4195
|
}
|
|
2070
|
-
|
|
4196
|
+
] : [];
|
|
2071
4197
|
const svelteOverrides = isModuleEnabled(MODULES.svelte) ? [{
|
|
2072
4198
|
name: buildConfigName(MAIN_SCOPES.OVERRIDES, `${MAIN_SCOPES.SVELTE}/general`),
|
|
2073
4199
|
files: [GLOB_SVELTE],
|
|
@@ -2079,11 +4205,14 @@ const svelteOverrides = isModuleEnabled(MODULES.svelte) ? [{
|
|
|
2079
4205
|
"svelte/comment-directive": ["error", { reportUnusedDisableDirectives: true }],
|
|
2080
4206
|
"svelte/system": "error"
|
|
2081
4207
|
}
|
|
2082
|
-
}, {
|
|
4208
|
+
}, ...isModuleEnabled(MODULES.unicorn) ? [{
|
|
2083
4209
|
name: buildConfigName(MAIN_SCOPES.OVERRIDES, `${MAIN_SCOPES.SVELTE}/components`),
|
|
2084
4210
|
files: [`**/components/${GLOB_SVELTE}`],
|
|
2085
|
-
rules: { "unicorn/filename-case": ["error", {
|
|
2086
|
-
|
|
4211
|
+
rules: { "unicorn/filename-case": ["error", {
|
|
4212
|
+
case: "pascalCase",
|
|
4213
|
+
checkDirectories: false
|
|
4214
|
+
}] }
|
|
4215
|
+
}] : []] : [];
|
|
2087
4216
|
const tomlOverrides = isModuleEnabled(MODULES.toml) ? [{
|
|
2088
4217
|
name: buildConfigName(MAIN_SCOPES.OVERRIDES, `${MAIN_SCOPES.TOML}/general`),
|
|
2089
4218
|
files: [GLOB_TOML],
|
|
@@ -2096,10 +4225,16 @@ const yamlOverrides = isModuleEnabled(MODULES.yaml) ? [{
|
|
|
2096
4225
|
"no-irregular-whitespace": "off",
|
|
2097
4226
|
"no-unused-vars": "off"
|
|
2098
4227
|
}
|
|
4228
|
+
}, {
|
|
4229
|
+
name: buildConfigName(MAIN_SCOPES.OVERRIDES, `${MAIN_SCOPES.YAML}/extension`),
|
|
4230
|
+
files: GLOB_YAML_FIXED_EXTENSION_FILES,
|
|
4231
|
+
rules: { "yaml/file-extension": "off" }
|
|
2099
4232
|
}] : [];
|
|
2100
|
-
const overrides = () => [
|
|
4233
|
+
const overrides = (typescriptOptions) => [
|
|
2101
4234
|
...jsOverrides,
|
|
2102
4235
|
...tsOverrides,
|
|
4236
|
+
...importOverrides,
|
|
4237
|
+
...buildTypeAwareImportOverrides(typescriptOptions),
|
|
2103
4238
|
...testOverrides,
|
|
2104
4239
|
...unicornOverrides,
|
|
2105
4240
|
...jsdocOverrides,
|
|
@@ -2118,12 +4253,16 @@ const overrides = () => [
|
|
|
2118
4253
|
"import/no-default-export": "off",
|
|
2119
4254
|
"import/no-rename-default": "off",
|
|
2120
4255
|
"import/no-named-as-default-member": "off",
|
|
2121
|
-
"node/no-sync": "off"
|
|
4256
|
+
"node/no-sync": "off",
|
|
4257
|
+
"unicorn/no-barrel-files": "off"
|
|
2122
4258
|
}
|
|
2123
4259
|
}
|
|
2124
4260
|
];
|
|
2125
4261
|
//#endregion
|
|
2126
4262
|
//#region ../src/js/eslint/configs/perfectionist.ts
|
|
4263
|
+
/**
|
|
4264
|
+
* @internal @brnshkr/config/eslint
|
|
4265
|
+
*/
|
|
2127
4266
|
const perfectionist = async () => {
|
|
2128
4267
|
const { requiredAll: [pluginPerfectionist] } = await resolvePackages(MODULES.perfectionist);
|
|
2129
4268
|
if (!pluginPerfectionist) return [];
|
|
@@ -2145,6 +4284,9 @@ const perfectionist = async () => {
|
|
|
2145
4284
|
};
|
|
2146
4285
|
//#endregion
|
|
2147
4286
|
//#region ../src/js/eslint/configs/regexp.ts
|
|
4287
|
+
/**
|
|
4288
|
+
* @internal @brnshkr/config/eslint
|
|
4289
|
+
*/
|
|
2148
4290
|
const regexp = async () => {
|
|
2149
4291
|
const { requiredAll: [pluginRegExp] } = await resolvePackages(MODULES.regexp);
|
|
2150
4292
|
if (!pluginRegExp) return [];
|
|
@@ -2159,10 +4301,8 @@ const regexp = async () => {
|
|
|
2159
4301
|
"regexp/confusing-quantifier": "error",
|
|
2160
4302
|
"regexp/grapheme-string-literal": "error",
|
|
2161
4303
|
"regexp/letter-case": ["error", {
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
hexadecimalEscape: "lowercase",
|
|
2165
|
-
controlEscape: "uppercase"
|
|
4304
|
+
unicodeEscape: "uppercase",
|
|
4305
|
+
hexadecimalEscape: "uppercase"
|
|
2166
4306
|
}],
|
|
2167
4307
|
"regexp/no-control-character": "error",
|
|
2168
4308
|
"regexp/no-empty-alternative": "error",
|
|
@@ -2187,7 +4327,6 @@ const regexp = async () => {
|
|
|
2187
4327
|
"regexp/sort-character-class-elements": "error",
|
|
2188
4328
|
"regexp/unicode-escape": "error",
|
|
2189
4329
|
"regexp/unicode-property": ["error", {
|
|
2190
|
-
generalCategory: "never",
|
|
2191
4330
|
key: "short",
|
|
2192
4331
|
property: "long"
|
|
2193
4332
|
}]
|
|
@@ -2196,21 +4335,22 @@ const regexp = async () => {
|
|
|
2196
4335
|
};
|
|
2197
4336
|
//#endregion
|
|
2198
4337
|
//#region ../src/js/eslint/configs/style.ts
|
|
4338
|
+
/**
|
|
4339
|
+
* @internal @brnshkr/config/eslint
|
|
4340
|
+
*/
|
|
2199
4341
|
const style = async () => {
|
|
2200
4342
|
const { requiredAll: [pluginStyle] } = await resolvePackages(MODULES.style);
|
|
2201
4343
|
if (!pluginStyle) return [];
|
|
2202
4344
|
const styleConfig = pluginStyle.configs.customize({
|
|
2203
|
-
jsx: true,
|
|
2204
4345
|
semi: true,
|
|
2205
4346
|
indent: 2,
|
|
2206
4347
|
quotes: QUOTES,
|
|
2207
4348
|
quoteProps: "as-needed",
|
|
2208
4349
|
arrowParens: true,
|
|
2209
|
-
|
|
2210
|
-
braceStyle: "1tbs",
|
|
2211
|
-
commaDangle: "always-multiline"
|
|
4350
|
+
braceStyle: "1tbs"
|
|
2212
4351
|
});
|
|
2213
|
-
const
|
|
4352
|
+
const indentRuleEntry = styleConfig.rules?.["@stylistic/indent"];
|
|
4353
|
+
const indentRuleConfig = Array.isArray(indentRuleEntry) && isPlainObject(indentRuleEntry[2]) ? indentRuleEntry[2] : void 0;
|
|
2214
4354
|
return [{
|
|
2215
4355
|
name: buildConfigName(MAIN_SCOPES.STYLE, SUB_SCOPES.SETUP),
|
|
2216
4356
|
plugins: { style: pluginStyle }
|
|
@@ -2517,6 +4657,9 @@ const style = async () => {
|
|
|
2517
4657
|
};
|
|
2518
4658
|
//#endregion
|
|
2519
4659
|
//#region ../src/js/eslint/configs/svelte.ts
|
|
4660
|
+
/**
|
|
4661
|
+
* @internal @brnshkr/config/eslint
|
|
4662
|
+
*/
|
|
2520
4663
|
const extractRelevantConfig = (configs, key) => {
|
|
2521
4664
|
for (const config of configs) if (config.name === `svelte:${key}` && config.rules) return config;
|
|
2522
4665
|
throw new Error(`Expected key "${key}" to be contained in given config.`);
|
|
@@ -2557,14 +4700,11 @@ const svelte = async () => {
|
|
|
2557
4700
|
style: ["scss", null]
|
|
2558
4701
|
}],
|
|
2559
4702
|
"svelte/button-has-type": "error",
|
|
2560
|
-
"svelte/consistent-selector-style": ["error", {
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
"id"
|
|
2566
|
-
]
|
|
2567
|
-
}],
|
|
4703
|
+
"svelte/consistent-selector-style": ["error", { style: [
|
|
4704
|
+
"type",
|
|
4705
|
+
"class",
|
|
4706
|
+
"id"
|
|
4707
|
+
] }],
|
|
2568
4708
|
"svelte/derived-has-same-inputs-outputs": "error",
|
|
2569
4709
|
"svelte/experimental-require-strict-events": "error",
|
|
2570
4710
|
"svelte/experimental-require-slot-types": "error",
|
|
@@ -2578,25 +4718,29 @@ const svelte = async () => {
|
|
|
2578
4718
|
}],
|
|
2579
4719
|
"style/indent": "off",
|
|
2580
4720
|
"svelte/indent": ["error", { indent: 2 }],
|
|
2581
|
-
"svelte/max-attributes-per-line": ["error", {
|
|
2582
|
-
multiline: 1,
|
|
2583
|
-
singleline: 3
|
|
2584
|
-
}],
|
|
4721
|
+
"svelte/max-attributes-per-line": ["error", { singleline: 3 }],
|
|
2585
4722
|
"svelte/mustache-spacing": "error",
|
|
2586
4723
|
"svelte/no-add-event-listener": "error",
|
|
4724
|
+
"svelte/no-at-const-tags": "error",
|
|
2587
4725
|
"svelte/no-at-debug-tags": "error",
|
|
4726
|
+
"svelte/no-bind-value-on-checkable-inputs": "error",
|
|
4727
|
+
"svelte/no-conflicting-module-names": "error",
|
|
2588
4728
|
"svelte/no-extra-reactive-curlies": "error",
|
|
2589
4729
|
"svelte/no-ignored-unsubscribe": "error",
|
|
2590
|
-
"svelte/no-inline-styles": "error",
|
|
4730
|
+
"svelte/no-inline-styles": ["error", { allowTransitions: false }],
|
|
2591
4731
|
"svelte/no-inspect": "error",
|
|
4732
|
+
"svelte/no-nested-style-tag": "error",
|
|
2592
4733
|
"svelte/no-spaces-around-equal-signs-in-attribute": "error",
|
|
2593
4734
|
"svelte/no-target-blank": "error",
|
|
2594
4735
|
"svelte/no-top-level-browser-globals": "error",
|
|
2595
4736
|
"style/no-trailing-spaces": "off",
|
|
2596
4737
|
"svelte/no-trailing-spaces": "error",
|
|
4738
|
+
"svelte/no-unused-props": ["error", { checkImportedTypes: true }],
|
|
4739
|
+
"svelte/prefer-attribute-interpolation": "error",
|
|
2597
4740
|
"svelte/prefer-class-directive": "error",
|
|
2598
4741
|
"prefer-const": "off",
|
|
2599
4742
|
"svelte/prefer-const": "error",
|
|
4743
|
+
"svelte/prefer-derived-over-derived-by": "error",
|
|
2600
4744
|
"svelte/prefer-destructured-store-props": "error",
|
|
2601
4745
|
"svelte/prefer-style-directive": "error",
|
|
2602
4746
|
"svelte/require-event-prefix": "error",
|
|
@@ -2615,6 +4759,9 @@ const svelte = async () => {
|
|
|
2615
4759
|
};
|
|
2616
4760
|
//#endregion
|
|
2617
4761
|
//#region ../src/js/eslint/configs/test.ts
|
|
4762
|
+
/**
|
|
4763
|
+
* @internal @brnshkr/config/eslint
|
|
4764
|
+
*/
|
|
2618
4765
|
const test = async () => {
|
|
2619
4766
|
const { requiredAll: [pluginVitest] } = await resolvePackages(MODULES.test);
|
|
2620
4767
|
if (!pluginVitest) return [];
|
|
@@ -2654,7 +4801,7 @@ const test = async () => {
|
|
|
2654
4801
|
"test/prefer-hooks-on-top": "error",
|
|
2655
4802
|
"test/prefer-import-in-mock": "error",
|
|
2656
4803
|
"test/prefer-importing-vitest-globals": "error",
|
|
2657
|
-
"test/prefer-lowercase-title": "error",
|
|
4804
|
+
"test/prefer-lowercase-title": ["error", { ignore: ["describe"] }],
|
|
2658
4805
|
"test/prefer-mock-promise-shorthand": "error",
|
|
2659
4806
|
"test/prefer-snapshot-hint": "error",
|
|
2660
4807
|
"test/prefer-spy-on": "error",
|
|
@@ -2668,13 +4815,17 @@ const test = async () => {
|
|
|
2668
4815
|
"test/prefer-vi-mocked": "error",
|
|
2669
4816
|
"test/require-awaited-expect-poll": "error",
|
|
2670
4817
|
"test/require-hook": "error",
|
|
2671
|
-
"test/require-mock-type-parameters": "error",
|
|
4818
|
+
"test/require-mock-type-parameters": ["error", { checkImportFunctions: true }],
|
|
4819
|
+
"test/valid-expect": ["error", { alwaysAwait: true }],
|
|
2672
4820
|
"test/warn-todo": "error"
|
|
2673
4821
|
}
|
|
2674
4822
|
}];
|
|
2675
4823
|
};
|
|
2676
4824
|
//#endregion
|
|
2677
4825
|
//#region ../src/js/eslint/configs/toml.ts
|
|
4826
|
+
/**
|
|
4827
|
+
* @internal @brnshkr/config/eslint
|
|
4828
|
+
*/
|
|
2678
4829
|
const toml = async () => {
|
|
2679
4830
|
const { requiredAll: [pluginToml] } = await resolvePackages(MODULES.toml);
|
|
2680
4831
|
if (!pluginToml) return [];
|
|
@@ -2700,6 +4851,9 @@ const toml = async () => {
|
|
|
2700
4851
|
};
|
|
2701
4852
|
//#endregion
|
|
2702
4853
|
//#region ../src/js/eslint/configs/yaml.ts
|
|
4854
|
+
/**
|
|
4855
|
+
* @internal @brnshkr/config/eslint
|
|
4856
|
+
*/
|
|
2703
4857
|
const yaml = async () => {
|
|
2704
4858
|
const { requiredAll: [pluginYaml] } = await resolvePackages(MODULES.yaml);
|
|
2705
4859
|
if (!pluginYaml) return [];
|
|
@@ -2712,7 +4866,6 @@ const yaml = async () => {
|
|
|
2712
4866
|
language: "yaml/yaml",
|
|
2713
4867
|
rules: {
|
|
2714
4868
|
...renameRules(pluginYaml.configs.standard[2]?.rules, { yml: "yaml" }),
|
|
2715
|
-
"yaml/block-mapping-colon-indicator-newline": ["error", "never"],
|
|
2716
4869
|
"yaml/file-extension": "error",
|
|
2717
4870
|
"yaml/flow-mapping-curly-spacing": [
|
|
2718
4871
|
"error",
|
|
@@ -2720,23 +4873,21 @@ const yaml = async () => {
|
|
|
2720
4873
|
{ emptyObjects: "never" }
|
|
2721
4874
|
],
|
|
2722
4875
|
"yaml/indent": ["error", 2],
|
|
2723
|
-
"yaml/no-
|
|
2724
|
-
"yaml/
|
|
2725
|
-
"yaml/quotes": ["error", {
|
|
2726
|
-
avoidEscape: true,
|
|
2727
|
-
prefer: QUOTES
|
|
2728
|
-
}],
|
|
4876
|
+
"yaml/no-boolean-key": "error",
|
|
4877
|
+
"yaml/quotes": ["error", { prefer: QUOTES }],
|
|
2729
4878
|
"yaml/require-string-key": "error"
|
|
2730
4879
|
}
|
|
2731
4880
|
}];
|
|
2732
4881
|
};
|
|
2733
4882
|
//#endregion
|
|
2734
4883
|
//#region ../src/js/eslint/configs/index.ts
|
|
4884
|
+
/**
|
|
4885
|
+
* @internal @brnshkr/config/eslint
|
|
4886
|
+
*/
|
|
2735
4887
|
const configs = {
|
|
2736
4888
|
[packageOrganization]: builtinConfig[packageOrganization],
|
|
2737
4889
|
comments,
|
|
2738
4890
|
css,
|
|
2739
|
-
gitignore,
|
|
2740
4891
|
ignores,
|
|
2741
4892
|
import: imports,
|
|
2742
4893
|
javascript,
|
|
@@ -2757,12 +4908,49 @@ const configs = {
|
|
|
2757
4908
|
};
|
|
2758
4909
|
//#endregion
|
|
2759
4910
|
//#region ../src/js/eslint/index.ts
|
|
4911
|
+
/**
|
|
4912
|
+
* Build the `@brnshkr` ESLint flat config composer.
|
|
4913
|
+
*
|
|
4914
|
+
* Merges sensible defaults for every supported module (TypeScript, Svelte, JSDoc, etc.) with user
|
|
4915
|
+
* overrides and any additional flat configs. Modules whose optional peer dependency is not installed
|
|
4916
|
+
* are skipped automatically, so consumers only opt into what they use.
|
|
4917
|
+
*
|
|
4918
|
+
* @api
|
|
4919
|
+
*
|
|
4920
|
+
* @param optionsAndGlobalConfig per-module toggles and global flat-config fields (`files`,
|
|
4921
|
+
* `ignores`, `languageOptions`, etc.) merged with the defaults
|
|
4922
|
+
* @param additionalConfigs extra flat-config entries appended after the built-in ones
|
|
4923
|
+
*
|
|
4924
|
+
* @returns configured {@link FlatConfigComposer} that resolves to the final flat-config array
|
|
4925
|
+
*
|
|
4926
|
+
* @see https://github.com/brnshkr/config/blob/master/docs/js/eslint/index.md
|
|
4927
|
+
*
|
|
4928
|
+
* @example
|
|
4929
|
+
* ```js
|
|
4930
|
+
* import { getConfig } from '@brnshkr/config/eslint';
|
|
4931
|
+
*
|
|
4932
|
+
* getConfig(undefined);
|
|
4933
|
+
* getConfig({});
|
|
4934
|
+
*
|
|
4935
|
+
* getConfig({
|
|
4936
|
+
* svelte: false,
|
|
4937
|
+
* files: ['src/**'],
|
|
4938
|
+
* }, {
|
|
4939
|
+
* files: ['scripts/**'],
|
|
4940
|
+
* rules: { 'no-console': 'off' },
|
|
4941
|
+
* });
|
|
4942
|
+
*
|
|
4943
|
+
* getConfig(undefined, {
|
|
4944
|
+
* files: ['scripts/**'],
|
|
4945
|
+
* rules: { 'no-console': 'off' },
|
|
4946
|
+
* });
|
|
4947
|
+
* ```
|
|
4948
|
+
*/
|
|
2760
4949
|
const getConfig = (optionsAndGlobalConfig, ...additionalConfigs) => {
|
|
2761
4950
|
const resolvedOptions = {
|
|
2762
4951
|
[packageOrganization]: isModuleEnabledByDefault(MODULES[packageOrganization]),
|
|
2763
4952
|
comments: isModuleEnabledByDefault(MODULES.comments),
|
|
2764
4953
|
css: isModuleEnabledByDefault(MODULES.css),
|
|
2765
|
-
gitignore: isModuleEnabledByDefault(MODULES.gitignore),
|
|
2766
4954
|
import: isModuleEnabledByDefault(MODULES.import),
|
|
2767
4955
|
jsdoc: isModuleEnabledByDefault(MODULES.jsdoc),
|
|
2768
4956
|
json: isModuleEnabledByDefault(MODULES.json),
|
|
@@ -2781,8 +4969,7 @@ const getConfig = (optionsAndGlobalConfig, ...additionalConfigs) => {
|
|
|
2781
4969
|
};
|
|
2782
4970
|
setModuleEnabled(MODULES[packageOrganization], resolvedOptions[packageOrganization]);
|
|
2783
4971
|
setModuleEnabled(MODULES.comments, resolvedOptions.comments);
|
|
2784
|
-
setModuleEnabled(MODULES.css, resolvedOptions.css);
|
|
2785
|
-
setModuleEnabled(MODULES.gitignore, resolvedOptions.gitignore !== false);
|
|
4972
|
+
setModuleEnabled(MODULES.css, resolvedOptions.css !== false);
|
|
2786
4973
|
setModuleEnabled(MODULES.import, resolvedOptions.import);
|
|
2787
4974
|
setModuleEnabled(MODULES.jsdoc, resolvedOptions.jsdoc);
|
|
2788
4975
|
setModuleEnabled(MODULES.json, resolvedOptions.json);
|
|
@@ -2802,7 +4989,6 @@ const getConfig = (optionsAndGlobalConfig, ...additionalConfigs) => {
|
|
|
2802
4989
|
composer.append(...configsToAppend);
|
|
2803
4990
|
};
|
|
2804
4991
|
appendToComposer(configs.ignores(resolvedOptions.ignores));
|
|
2805
|
-
if (isModuleEnabled(MODULES.gitignore)) appendToComposer(configs.gitignore(typeof resolvedOptions.gitignore === "object" ? resolvedOptions.gitignore : { strict: false }));
|
|
2806
4992
|
appendToComposer(configs.javascript());
|
|
2807
4993
|
if (isModuleEnabled(MODULES.typescript)) appendToComposer(configs.typescript(typeof resolvedOptions.typescript === "object" ? resolvedOptions.typescript : void 0));
|
|
2808
4994
|
if (isModuleEnabled(MODULES.test)) appendToComposer(configs.test());
|
|
@@ -2819,12 +5005,17 @@ const getConfig = (optionsAndGlobalConfig, ...additionalConfigs) => {
|
|
|
2819
5005
|
if (isModuleEnabled(MODULES.json)) appendToComposer(configs.json());
|
|
2820
5006
|
if (isModuleEnabled(MODULES.toml)) appendToComposer(configs.toml());
|
|
2821
5007
|
if (isModuleEnabled(MODULES.yaml)) appendToComposer(configs.yaml());
|
|
2822
|
-
if (isModuleEnabled(MODULES.css)) appendToComposer(configs.css());
|
|
5008
|
+
if (isModuleEnabled(MODULES.css)) appendToComposer(configs.css(typeof resolvedOptions.css === "object" ? resolvedOptions.css : void 0));
|
|
2823
5009
|
if (isModuleEnabled(MODULES.markdown)) appendToComposer(configs.markdown(typeof resolvedOptions.markdown === "object" ? resolvedOptions.markdown : void 0));
|
|
2824
|
-
appendToComposer(configs.overrides());
|
|
5010
|
+
appendToComposer(configs.overrides(resolvedOptions.typescript));
|
|
2825
5011
|
appendToComposer(...getUserConfigs(resolvedOptions, additionalConfigs));
|
|
2826
5012
|
return composer;
|
|
2827
5013
|
};
|
|
5014
|
+
/**
|
|
5015
|
+
* Default-exported pre-built composer for direct re-export from an ESLint flat-config file.
|
|
5016
|
+
*
|
|
5017
|
+
* @api
|
|
5018
|
+
*/
|
|
2828
5019
|
var eslint_default = getConfig();
|
|
2829
5020
|
//#endregion
|
|
2830
5021
|
export { eslint_default as default, getConfig };
|