@mrpalmer/eslint-plugin 1.0.3 → 1.1.1
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/lib/index.d.ts +4 -10
- package/lib/index.js +13 -0
- package/lib/rules/require-package-scope.d.ts +11 -0
- package/lib/rules/require-package-scope.js +54 -0
- package/lib/rules/shorten-paths.js +1 -1
- package/lib/rules/sort-imports.d.ts +2 -2
- package/lib/rules/sort-imports.js +4 -3
- package/lib/utils/import-path-options.js +1 -6
- package/lib/utils/ts-config.d.ts +1 -1
- package/lib/utils/ts-config.js +6 -4
- package/package.json +2 -1
package/lib/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { TSESLint } from '@typescript-eslint/utils';
|
|
2
|
+
type PluginRules = Record<string, TSESLint.RuleModule<string, readonly unknown[]>>;
|
|
2
3
|
declare const _default: {
|
|
3
4
|
configs: {
|
|
4
5
|
recommended: {
|
|
@@ -9,11 +10,7 @@ declare const _default: {
|
|
|
9
10
|
name: string;
|
|
10
11
|
version: string;
|
|
11
12
|
};
|
|
12
|
-
rules:
|
|
13
|
-
'shorten-paths': TSESLint.RuleModule<"shortenImport" | "shortenExport", import("./rules/shorten-paths.js").Options, unknown, TSESLint.RuleListener>;
|
|
14
|
-
'sort-imports': TSESLint.RuleModule<"error" | "order" | "noLineBetweenImports", [import("./rules/sort-imports.js").Options], unknown, TSESLint.RuleListener>;
|
|
15
|
-
'sort-named': TSESLint.RuleModule<"order", [import("./rules/sort-named.js").Options], unknown, TSESLint.RuleListener>;
|
|
16
|
-
};
|
|
13
|
+
rules: PluginRules;
|
|
17
14
|
};
|
|
18
15
|
};
|
|
19
16
|
rules: {
|
|
@@ -22,15 +19,12 @@ declare const _default: {
|
|
|
22
19
|
'mrpalmer/sort-named': "warn";
|
|
23
20
|
};
|
|
24
21
|
};
|
|
22
|
+
json: TSESLint.FlatConfig.Config;
|
|
25
23
|
};
|
|
26
24
|
meta: {
|
|
27
25
|
name: string;
|
|
28
26
|
version: string;
|
|
29
27
|
};
|
|
30
|
-
rules:
|
|
31
|
-
'shorten-paths': TSESLint.RuleModule<"shortenImport" | "shortenExport", import("./rules/shorten-paths.js").Options, unknown, TSESLint.RuleListener>;
|
|
32
|
-
'sort-imports': TSESLint.RuleModule<"error" | "order" | "noLineBetweenImports", [import("./rules/sort-imports.js").Options], unknown, TSESLint.RuleListener>;
|
|
33
|
-
'sort-named': TSESLint.RuleModule<"order", [import("./rules/sort-named.js").Options], unknown, TSESLint.RuleListener>;
|
|
34
|
-
};
|
|
28
|
+
rules: PluginRules;
|
|
35
29
|
};
|
|
36
30
|
export default _default;
|
package/lib/index.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
import jsonPlugin from '@eslint/json';
|
|
1
2
|
import { meta } from './meta.js';
|
|
2
3
|
// rules
|
|
4
|
+
import requirePackageScope from './rules/require-package-scope.js';
|
|
3
5
|
import shortenPathsRule from './rules/shorten-paths.js';
|
|
4
6
|
import sortImportsRule from './rules/sort-imports.js';
|
|
5
7
|
import sortNamedRule from './rules/sort-named.js';
|
|
6
8
|
const rules = {
|
|
9
|
+
// @ts-expect-error -- require-package-scope is a JSON rule
|
|
10
|
+
'require-package-scope': requirePackageScope,
|
|
7
11
|
'shorten-paths': shortenPathsRule,
|
|
8
12
|
'sort-imports': sortImportsRule,
|
|
9
13
|
'sort-named': sortNamedRule,
|
|
@@ -23,9 +27,18 @@ const recommended = {
|
|
|
23
27
|
'mrpalmer/sort-named': 'warn',
|
|
24
28
|
},
|
|
25
29
|
};
|
|
30
|
+
const json = {
|
|
31
|
+
name: 'mrpalmer/json',
|
|
32
|
+
plugins: {
|
|
33
|
+
json: jsonPlugin,
|
|
34
|
+
mrpalmer: plugin,
|
|
35
|
+
},
|
|
36
|
+
language: 'json/json',
|
|
37
|
+
};
|
|
26
38
|
export default {
|
|
27
39
|
...plugin,
|
|
28
40
|
configs: {
|
|
29
41
|
recommended,
|
|
42
|
+
json,
|
|
30
43
|
},
|
|
31
44
|
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { JSONRuleDefinition } from '@eslint/json/types';
|
|
2
|
+
export type MessageIds = 'requirePackageScope' | 'forbidPackageScope';
|
|
3
|
+
export type RuleOptions = [{
|
|
4
|
+
scope: string | false;
|
|
5
|
+
}];
|
|
6
|
+
export type RequirePackageScopeRuleDefinition = JSONRuleDefinition<{
|
|
7
|
+
RuleOptions: RuleOptions;
|
|
8
|
+
MessageIds: MessageIds;
|
|
9
|
+
}>;
|
|
10
|
+
declare const rule: RequirePackageScopeRuleDefinition;
|
|
11
|
+
export default rule;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const rule = {
|
|
2
|
+
meta: {
|
|
3
|
+
type: 'suggestion',
|
|
4
|
+
docs: {
|
|
5
|
+
description: 'Require or forbid package scopes in package names.',
|
|
6
|
+
},
|
|
7
|
+
schema: [
|
|
8
|
+
{
|
|
9
|
+
type: 'object',
|
|
10
|
+
properties: {
|
|
11
|
+
scope: {
|
|
12
|
+
anyOf: [{ type: 'string' }, { type: 'boolean', enum: [false] }],
|
|
13
|
+
description: 'The required scope for package names, or false to forbid scopes.',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
additionalProperties: false,
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
defaultOptions: [{ scope: false }],
|
|
20
|
+
messages: {
|
|
21
|
+
requirePackageScope: 'Packages must use the "@{{scope}}" scope.',
|
|
22
|
+
forbidPackageScope: 'Packages must not use a scope.',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
create(context) {
|
|
26
|
+
const [{ scope }] = context.options;
|
|
27
|
+
return {
|
|
28
|
+
Document(node) {
|
|
29
|
+
if (node.body.type !== 'Object') {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const nameProperty = node.body.members.find((member) => member.name.type === 'String' && member.name.value === 'name');
|
|
33
|
+
if (nameProperty?.value.type !== 'String') {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const packageName = nameProperty.value.value;
|
|
37
|
+
if (scope === false && packageName.startsWith('@')) {
|
|
38
|
+
context.report({
|
|
39
|
+
node: nameProperty.value,
|
|
40
|
+
messageId: 'forbidPackageScope',
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
else if (scope && !packageName.startsWith(`@${scope}/`)) {
|
|
44
|
+
context.report({
|
|
45
|
+
node: nameProperty.value,
|
|
46
|
+
messageId: 'requirePackageScope',
|
|
47
|
+
data: { scope },
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
export default rule;
|
|
@@ -39,7 +39,7 @@ export default createRule({
|
|
|
39
39
|
},
|
|
40
40
|
],
|
|
41
41
|
create(context, options) {
|
|
42
|
-
const config = loadConfig(context.settings);
|
|
42
|
+
const config = loadConfig(context.settings, context.filename);
|
|
43
43
|
const absolutePath = path.resolve(process.cwd(), context.filename);
|
|
44
44
|
return {
|
|
45
45
|
'ImportDeclaration,ExportNamedDeclaration,ExportAllDeclaration'(node) {
|
|
@@ -11,8 +11,8 @@ interface PathGroup {
|
|
|
11
11
|
isSideEffect?: boolean;
|
|
12
12
|
}
|
|
13
13
|
export interface Options {
|
|
14
|
-
groups
|
|
15
|
-
pathGroups
|
|
14
|
+
groups?: ReadonlyArray<Arrayable<ImportType>>;
|
|
15
|
+
pathGroups?: PathGroup[];
|
|
16
16
|
}
|
|
17
17
|
type MessageId = 'error' | 'order' | 'noLineBetweenImports';
|
|
18
18
|
declare const _default: TSESLint.RuleModule<MessageId, [Options], unknown, TSESLint.RuleListener>;
|
|
@@ -533,15 +533,16 @@ export default createRule({
|
|
|
533
533
|
],
|
|
534
534
|
create(context, [options]) {
|
|
535
535
|
const settings = getSettings(context);
|
|
536
|
-
const tsConfigPaths = loadConfig(settings)?.config.compilerOptions?.paths ??
|
|
536
|
+
const tsConfigPaths = loadConfig(settings, context.filename)?.config.compilerOptions?.paths ??
|
|
537
|
+
{};
|
|
537
538
|
let ranks;
|
|
538
539
|
try {
|
|
539
|
-
const { pathGroups, maxPosition } = convertPathGroupsForRanks(options.pathGroups, Object.keys(tsConfigPaths));
|
|
540
|
+
const { pathGroups, maxPosition } = convertPathGroupsForRanks(options.pathGroups ?? [], Object.keys(tsConfigPaths));
|
|
540
541
|
const types = new Set(baseTypes);
|
|
541
542
|
pathGroups.forEach((group) => {
|
|
542
543
|
types.add(group.group);
|
|
543
544
|
});
|
|
544
|
-
const { groups, omittedTypes } = convertGroupsToRanks(['sideEffect', ...options.groups], types, settings['mrpalmer/coreModules']);
|
|
545
|
+
const { groups, omittedTypes } = convertGroupsToRanks(['sideEffect', ...(options.groups ?? defaultGroups)], types, settings['mrpalmer/coreModules']);
|
|
545
546
|
ranks = {
|
|
546
547
|
groups,
|
|
547
548
|
omittedTypes,
|
|
@@ -80,13 +80,8 @@ function findAliases({ filePath, originalImportPath, baseUrlAbsolutePath, paths,
|
|
|
80
80
|
}
|
|
81
81
|
return options;
|
|
82
82
|
}
|
|
83
|
-
const
|
|
84
|
-
'': ['.js', '.ts', '.jsx', '.tsx'],
|
|
85
|
-
'.js': ['.ts', '.js'],
|
|
86
|
-
};
|
|
83
|
+
const candidateExtensions = ['', '.js', '.jsx', '.ts', '.tsx'];
|
|
87
84
|
function fileExistsAtPath(filePath) {
|
|
88
|
-
const { ext } = path.parse(filePath);
|
|
89
|
-
const candidateExtensions = importExtensionMappings[ext] ?? [ext];
|
|
90
85
|
return candidateExtensions.some((ext) => fs.existsSync(filePath + ext));
|
|
91
86
|
}
|
|
92
87
|
function resolveAlias(importPath, matcher) {
|
package/lib/utils/ts-config.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createPathsMatcher } from 'get-tsconfig';
|
|
2
2
|
import type { PluginSettings } from '../types.js';
|
|
3
3
|
export { createPathsMatcher };
|
|
4
|
-
export declare function loadConfig(settings: PluginSettings): {
|
|
4
|
+
export declare function loadConfig(settings: PluginSettings, currentFilePath: string): {
|
|
5
5
|
path: string;
|
|
6
6
|
config: {
|
|
7
7
|
compilerOptions?: import("get-tsconfig").TsConfigJson.CompilerOptions | undefined;
|
package/lib/utils/ts-config.js
CHANGED
|
@@ -2,11 +2,13 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { createPathsMatcher, parseTsconfig } from 'get-tsconfig';
|
|
4
4
|
export { createPathsMatcher };
|
|
5
|
-
export function loadConfig(settings) {
|
|
6
|
-
const eslintConfigPath = findUp([
|
|
5
|
+
export function loadConfig(settings, currentFilePath) {
|
|
6
|
+
const eslintConfigPath = findUp(currentFilePath, [
|
|
7
7
|
'eslint.config.js',
|
|
8
8
|
'eslint.config.mjs',
|
|
9
9
|
'eslint.config.cjs',
|
|
10
|
+
'eslint.config.ts',
|
|
11
|
+
'eslint.config.mts',
|
|
10
12
|
]);
|
|
11
13
|
if (!eslintConfigPath || !fs.existsSync(eslintConfigPath)) {
|
|
12
14
|
return undefined;
|
|
@@ -21,8 +23,8 @@ export function loadConfig(settings) {
|
|
|
21
23
|
function getRelativeConfigFilePath(settings) {
|
|
22
24
|
return settings['mrpalmer/tsconfig'] ?? 'tsconfig.json';
|
|
23
25
|
}
|
|
24
|
-
function findUp(filenames) {
|
|
25
|
-
let directory =
|
|
26
|
+
function findUp(currentFilePath, filenames) {
|
|
27
|
+
let directory = currentFilePath;
|
|
26
28
|
const { root: stopAt } = path.parse(directory);
|
|
27
29
|
while (directory !== stopAt) {
|
|
28
30
|
const found = firstExistingPath(filenames, directory);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrpalmer/eslint-plugin",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Custom ESLint rules for Mike Palmer's projects",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"minimatch": "^10.0.1"
|
|
22
22
|
},
|
|
23
23
|
"peerDependencies": {
|
|
24
|
+
"@eslint/json": "^0.14.0",
|
|
24
25
|
"@typescript-eslint/utils": "^8.0.0",
|
|
25
26
|
"eslint": "^9.0.0"
|
|
26
27
|
},
|