@ms-cloudpack/eslint-plugin 0.3.12 → 0.3.14
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.map +1 -1
- package/lib/index.js +2 -0
- package/lib/index.js.map +1 -1
- package/lib/rules/no-test-exports.d.ts +4 -9
- package/lib/rules/no-test-exports.d.ts.map +1 -1
- package/lib/rules/no-test-exports.js +50 -45
- package/lib/rules/no-test-exports.js.map +1 -1
- package/lib/rules/no-unsupported-imports.d.ts +7 -9
- package/lib/rules/no-unsupported-imports.d.ts.map +1 -1
- package/lib/rules/no-unsupported-imports.js +33 -44
- package/lib/rules/no-unsupported-imports.js.map +1 -1
- package/lib/utils/createRule.d.ts +45 -0
- package/lib/utils/createRule.d.ts.map +1 -0
- package/lib/utils/createRule.js +31 -0
- package/lib/utils/createRule.js.map +1 -0
- package/package.json +3 -3
package/lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAQzD,QAAA,MAAM,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAW7B,CAAC;AAEF,SAAS,MAAM,CAAC"}
|
package/lib/index.js
CHANGED
|
@@ -3,6 +3,7 @@ const onboarding_1 = require("./configs/onboarding");
|
|
|
3
3
|
const onboarding_requiring_type_checking_1 = require("./configs/onboarding-requiring-type-checking");
|
|
4
4
|
const recommended_1 = require("./configs/recommended");
|
|
5
5
|
const recommended_requiring_type_checking_1 = require("./configs/recommended-requiring-type-checking");
|
|
6
|
+
const no_test_exports_1 = require("./rules/no-test-exports");
|
|
6
7
|
const no_unsupported_imports_1 = require("./rules/no-unsupported-imports");
|
|
7
8
|
const plugin = {
|
|
8
9
|
configs: {
|
|
@@ -12,6 +13,7 @@ const plugin = {
|
|
|
12
13
|
'recommended-requiring-type-checking': recommended_requiring_type_checking_1.recommendedRequiringTypeChecking,
|
|
13
14
|
},
|
|
14
15
|
rules: {
|
|
16
|
+
'no-test-exports': no_test_exports_1.rule,
|
|
15
17
|
'no-unsupported-imports': no_unsupported_imports_1.rule,
|
|
16
18
|
},
|
|
17
19
|
};
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,qDAAkD;AAClD,qGAA+F;AAC/F,uDAAoD;AACpD,uGAAiG;AACjG,2EAA8E;AAE9E,MAAM,MAAM,GAA2B;IACrC,OAAO,EAAE;QACP,UAAU,EAAV,uBAAU;QACV,oCAAoC,EAAE,oEAA+B;QACrE,WAAW,EAAX,yBAAW;QACX,qCAAqC,EAAE,sEAAgC;KACxE;IACD,KAAK,EAAE;QACL,wBAAwB,EAAE,6BAAoB;KAC/C;CACF,CAAC;AAEF,iBAAS,MAAM,CAAC","sourcesContent":["import type { TSESLint } from '@typescript-eslint/utils';\nimport { onboarding } from './configs/onboarding';\nimport { onboardingRequiringTypeChecking } from './configs/onboarding-requiring-type-checking';\nimport { recommended } from './configs/recommended';\nimport { recommendedRequiringTypeChecking } from './configs/recommended-requiring-type-checking';\nimport { rule as noUnsupportedImports } from './rules/no-unsupported-imports';\n\nconst plugin: TSESLint.Linter.Plugin = {\n configs: {\n onboarding,\n 'onboarding-requiring-type-checking': onboardingRequiringTypeChecking,\n recommended,\n 'recommended-requiring-type-checking': recommendedRequiringTypeChecking,\n },\n rules: {\n 'no-unsupported-imports': noUnsupportedImports,\n },\n};\n// This export format is required for ESLint plugins\nexport = plugin;\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,qDAAkD;AAClD,qGAA+F;AAC/F,uDAAoD;AACpD,uGAAiG;AACjG,6DAAgE;AAChE,2EAA8E;AAE9E,MAAM,MAAM,GAA2B;IACrC,OAAO,EAAE;QACP,UAAU,EAAV,uBAAU;QACV,oCAAoC,EAAE,oEAA+B;QACrE,WAAW,EAAX,yBAAW;QACX,qCAAqC,EAAE,sEAAgC;KACxE;IACD,KAAK,EAAE;QACL,iBAAiB,EAAE,sBAAa;QAChC,wBAAwB,EAAE,6BAAoB;KAC/C;CACF,CAAC;AAEF,iBAAS,MAAM,CAAC","sourcesContent":["import type { TSESLint } from '@typescript-eslint/utils';\nimport { onboarding } from './configs/onboarding';\nimport { onboardingRequiringTypeChecking } from './configs/onboarding-requiring-type-checking';\nimport { recommended } from './configs/recommended';\nimport { recommendedRequiringTypeChecking } from './configs/recommended-requiring-type-checking';\nimport { rule as noTestExports } from './rules/no-test-exports';\nimport { rule as noUnsupportedImports } from './rules/no-unsupported-imports';\n\nconst plugin: TSESLint.Linter.Plugin = {\n configs: {\n onboarding,\n 'onboarding-requiring-type-checking': onboardingRequiringTypeChecking,\n recommended,\n 'recommended-requiring-type-checking': recommendedRequiringTypeChecking,\n },\n rules: {\n 'no-test-exports': noTestExports,\n 'no-unsupported-imports': noUnsupportedImports,\n },\n};\n// This export format is required for ESLint plugins\nexport = plugin;\n"]}
|
|
@@ -1,23 +1,18 @@
|
|
|
1
|
-
import type { TSESLint } from '@typescript-eslint/utils';
|
|
2
|
-
/** Processed rule options */
|
|
3
|
-
type RuleOptions = {
|
|
4
|
-
testPathPatterns: RegExp[];
|
|
5
|
-
testExportNamePatterns: RegExp[];
|
|
6
|
-
};
|
|
7
1
|
/** Rule options as specified in the config */
|
|
8
|
-
export type
|
|
2
|
+
export type RuleOptions = {
|
|
9
3
|
testPathPatterns?: string[];
|
|
10
4
|
testExportNamePatterns?: string[];
|
|
11
5
|
maxDepth?: number;
|
|
12
6
|
};
|
|
13
7
|
export declare const defaultTestPaths: RegExp[];
|
|
14
8
|
export declare const defaultMaxDepth: number;
|
|
9
|
+
/** Error message IDs corresponding to `rule.meta.messages` */
|
|
15
10
|
export type MessageIds = 'invalidPath' | 'invalidName';
|
|
11
|
+
/** Error data `{{substitutions}}` used in messages */
|
|
16
12
|
export type ErrorData = {
|
|
17
13
|
extension: string;
|
|
18
14
|
invalidPath?: string;
|
|
19
15
|
invalidName?: string;
|
|
20
16
|
};
|
|
21
|
-
export declare const rule:
|
|
22
|
-
export {};
|
|
17
|
+
export declare const rule: import("../utils/createRule").RuleModule<RuleOptions, MessageIds>;
|
|
23
18
|
//# sourceMappingURL=no-test-exports.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-test-exports.d.ts","sourceRoot":"","sources":["../../src/rules/no-test-exports.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"no-test-exports.d.ts","sourceRoot":"","sources":["../../src/rules/no-test-exports.ts"],"names":[],"mappings":"AAQA,8CAA8C;AAC9C,MAAM,MAAM,WAAW,GAAG;IACxB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,sBAAsB,CAAC,EAAE,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAaF,eAAO,MAAM,gBAAgB,EAAE,MAAM,EAKpC,CAAC;AAGF,eAAO,MAAM,eAAe,QAAW,CAAC;AAExC,8DAA8D;AAC9D,MAAM,MAAM,UAAU,GAAG,aAAa,GAAG,aAAa,CAAC;AAEvD,sDAAsD;AACtD,MAAM,MAAM,SAAS,GAAG;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAIF,eAAO,MAAM,IAAI,mEAgFf,CAAC"}
|
|
@@ -6,6 +6,7 @@ const getImportPathLiteralNode_1 = require("../utils/getImportPathLiteralNode");
|
|
|
6
6
|
const extractIdentifiersFromExport_1 = require("../utils/testExports/extractIdentifiersFromExport");
|
|
7
7
|
const getRegexpsFromStrings_1 = require("../utils/getRegexpsFromStrings");
|
|
8
8
|
const isIncludedIndexFile_1 = require("../utils/testExports/isIncludedIndexFile");
|
|
9
|
+
const createRule_1 = require("../utils/createRule");
|
|
9
10
|
exports.defaultTestPaths = [
|
|
10
11
|
// Only match lowercase mock|test|fixture at the beginning of an import path segment to avoid false positives
|
|
11
12
|
/\/(__)?(mock|test|fixture)/,
|
|
@@ -15,22 +16,21 @@ exports.defaultTestPaths = [
|
|
|
15
16
|
// Only match lowercase mock|test|fixture at the beginning of an export name to avoid false positives
|
|
16
17
|
const defaultTestExportNames = [/^(mock|test|fixture)/, /Mock|Test|Fixture/];
|
|
17
18
|
exports.defaultMaxDepth = Infinity;
|
|
18
|
-
const separateFileMessage = 'use a separate index.mock.{{extension}}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
separateFileMessage,
|
|
22
|
-
invalidName: 'Export "{{invalidName}}" appears to be test-related and should not be exported from the index file; ' +
|
|
23
|
-
separateFileMessage,
|
|
24
|
-
};
|
|
25
|
-
exports.rule = {
|
|
26
|
-
defaultOptions: [],
|
|
19
|
+
const separateFileMessage = 'use a separate file such as index.mock.{{extension}} instead';
|
|
20
|
+
exports.rule = (0, createRule_1.createRule)({
|
|
21
|
+
name: 'no-test-exports',
|
|
27
22
|
meta: {
|
|
28
23
|
type: 'problem',
|
|
29
24
|
docs: {
|
|
30
25
|
description: 'ban test, mock, and fixture exports from index files',
|
|
31
26
|
recommended: 'recommended',
|
|
32
27
|
},
|
|
33
|
-
messages
|
|
28
|
+
messages: {
|
|
29
|
+
invalidPath: 'Contents of test-related path "{{invalidPath}}" should not be referenced from an index file; ' +
|
|
30
|
+
separateFileMessage,
|
|
31
|
+
invalidName: '"{{invalidName}}" appears to be test-related and should not be exported from an index file; ' +
|
|
32
|
+
separateFileMessage,
|
|
33
|
+
},
|
|
34
34
|
schema: [
|
|
35
35
|
{
|
|
36
36
|
type: 'object',
|
|
@@ -54,75 +54,80 @@ exports.rule = {
|
|
|
54
54
|
},
|
|
55
55
|
],
|
|
56
56
|
},
|
|
57
|
-
create: (
|
|
58
|
-
const options =
|
|
59
|
-
// eslint-disable-next-line etc/no-deprecated -- filename property may not exist yet?
|
|
60
|
-
const filename = context.filename || context.getFilename?.();
|
|
57
|
+
create: (ruleContext) => {
|
|
58
|
+
const options = ruleContext.options[0] || {};
|
|
61
59
|
const testPathPatterns = (0, getRegexpsFromStrings_1.getRegexpsFromStrings)(options.testPathPatterns, exports.defaultTestPaths);
|
|
62
60
|
const testExportNamePatterns = (0, getRegexpsFromStrings_1.getRegexpsFromStrings)(options.testExportNamePatterns, defaultTestExportNames);
|
|
63
61
|
const maxDepth = options.maxDepth ?? exports.defaultMaxDepth;
|
|
64
|
-
|
|
62
|
+
const extension = path.extname(ruleContext.filename).slice(1);
|
|
63
|
+
if (!(0, isIncludedIndexFile_1.isIncludedIndexFile)({ filename: ruleContext.filename, ignorePaths: testPathPatterns, maxDepth })) {
|
|
65
64
|
return {};
|
|
66
65
|
}
|
|
67
|
-
const
|
|
68
|
-
...context,
|
|
69
|
-
options: { testExportNamePatterns, testPathPatterns },
|
|
70
|
-
filename,
|
|
71
|
-
};
|
|
66
|
+
const context = { ruleContext, testExportNamePatterns, testPathPatterns, extension };
|
|
72
67
|
return {
|
|
73
68
|
// import foo from 'foo'
|
|
74
69
|
// import * as foo from 'foo'
|
|
75
70
|
// import { foo } from 'foo'
|
|
76
71
|
// import { foo as bar } from 'foo'
|
|
77
72
|
// import 'foo'
|
|
78
|
-
ImportDeclaration: (node) => checkNode(
|
|
73
|
+
ImportDeclaration: (node) => checkNode(context, node),
|
|
79
74
|
// import foo = require('foo')
|
|
80
75
|
TSImportEqualsDeclaration: (node) => {
|
|
81
76
|
// Skip "export import foo = require('foo')" because that will be handled by ExportNamedDeclaration
|
|
82
77
|
if (node.parent.type !== 'ExportNamedDeclaration') {
|
|
83
|
-
checkNode(
|
|
78
|
+
checkNode(context, node);
|
|
84
79
|
}
|
|
85
80
|
},
|
|
86
81
|
// export { foo }
|
|
87
|
-
ExportDeclaration: (node) => checkNode(ruleContext, node),
|
|
88
82
|
// export { foo } from 'foo'
|
|
89
83
|
// export { foo as bar } from 'foo'
|
|
90
84
|
// export import foo = require('foo')
|
|
91
85
|
// export const foo = 'foo';
|
|
92
86
|
// and other exported declarations
|
|
93
|
-
ExportNamedDeclaration: (node) => checkNode(
|
|
87
|
+
ExportNamedDeclaration: (node) => checkNode(context, node),
|
|
94
88
|
// export * from 'foo'
|
|
95
89
|
// export * as foo from 'foo'
|
|
96
|
-
ExportAllDeclaration: (node) => checkNode(
|
|
90
|
+
ExportAllDeclaration: (node) => checkNode(context, node),
|
|
97
91
|
};
|
|
98
92
|
},
|
|
99
|
-
};
|
|
93
|
+
});
|
|
94
|
+
/** Check an import or export node for violations. */
|
|
100
95
|
function checkNode(context, node) {
|
|
101
|
-
const
|
|
96
|
+
const hasInvalidPath = checkInvalidPath(context, node);
|
|
97
|
+
// Invalid path errors take precedence, to avoid spamming errors.
|
|
98
|
+
if (!hasInvalidPath && (node.type === 'ExportAllDeclaration' || node.type === 'ExportNamedDeclaration')) {
|
|
99
|
+
checkInvalidName(context, node);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Check an import or export for invalid paths.
|
|
104
|
+
* Returns true if a violation was found.
|
|
105
|
+
*/
|
|
106
|
+
function checkInvalidPath(context, node) {
|
|
107
|
+
const { ruleContext, testPathPatterns, extension } = context;
|
|
102
108
|
// If there's an import path, check it
|
|
103
109
|
const { importPath, pathNode } = (0, getImportPathLiteralNode_1.getImportPathLiteralNode)(node) || {};
|
|
104
|
-
if (importPath && pathNode &&
|
|
105
|
-
|
|
106
|
-
context.report({
|
|
110
|
+
if (importPath && pathNode && testPathPatterns.some((re) => re.test(importPath))) {
|
|
111
|
+
ruleContext.report({
|
|
107
112
|
node: pathNode,
|
|
108
113
|
messageId: 'invalidPath',
|
|
109
|
-
data,
|
|
114
|
+
data: { extension, invalidPath: importPath },
|
|
110
115
|
});
|
|
111
|
-
|
|
112
|
-
return;
|
|
116
|
+
return true;
|
|
113
117
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
/** Check an export for invalid names. */
|
|
121
|
+
function checkInvalidName(context, node) {
|
|
122
|
+
const { ruleContext, testExportNamePatterns, extension } = context;
|
|
123
|
+
const ids = (0, extractIdentifiersFromExport_1.extractIdentifiersFromExport)(node);
|
|
124
|
+
for (const id of ids) {
|
|
125
|
+
if (testExportNamePatterns.some((re) => re.test(id.name))) {
|
|
126
|
+
ruleContext.report({
|
|
127
|
+
node: id,
|
|
128
|
+
messageId: 'invalidName',
|
|
129
|
+
data: { extension, invalidName: id.name },
|
|
130
|
+
});
|
|
126
131
|
}
|
|
127
132
|
}
|
|
128
133
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-test-exports.js","sourceRoot":"","sources":["../../src/rules/no-test-exports.ts"],"names":[],"mappings":"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","sourcesContent":["import type { TSESLint, TSESTree } from '@typescript-eslint/utils';\nimport * as path from 'path';\nimport { getImportPathLiteralNode, type ImportLikeNode } from '../utils/getImportPathLiteralNode';\nimport { extractIdentifiersFromExport } from '../utils/testExports/extractIdentifiersFromExport';\nimport { getRegexpsFromStrings } from '../utils/getRegexpsFromStrings';\nimport { isIncludedIndexFile } from '../utils/testExports/isIncludedIndexFile';\n\n/** Processed rule options */\ntype RuleOptions = {\n testPathPatterns: RegExp[];\n testExportNamePatterns: RegExp[];\n};\n\n/** Rule options as specified in the config */\nexport type RawRuleOptions = Omit<Partial<RuleOptions>, 'testPathPatterns' | 'testExportNamePatterns'> & {\n testPathPatterns?: string[];\n testExportNamePatterns?: string[];\n maxDepth?: number;\n};\n\ntype RuleContext = Omit<RawRuleContext, 'options'> & {\n options: RuleOptions;\n filename: string;\n};\n\ntype RawRuleContext = TSESLint.RuleContext<MessageIds, RawRuleOptions[]>;\n\nexport const defaultTestPaths: RegExp[] = [\n // Only match lowercase mock|test|fixture at the beginning of an import path segment to avoid false positives\n /\\/(__)?(mock|test|fixture)/,\n /\\.(mock|test|fixture)s?(\\.\\w+)?$/,\n /Mock|Test|Fixture/,\n];\n// Only match lowercase mock|test|fixture at the beginning of an export name to avoid false positives\nconst defaultTestExportNames: RegExp[] = [/^(mock|test|fixture)/, /Mock|Test|Fixture/];\nexport const defaultMaxDepth = Infinity;\n\nexport type MessageIds = 'invalidPath' | 'invalidName';\n\nexport type ErrorData = {\n extension: string;\n invalidPath?: string;\n invalidName?: string;\n};\n\nconst separateFileMessage = 'use a separate index.mock.{{extension}} file instead';\n\nconst messages: Record<MessageIds, string> = {\n invalidPath:\n 'Contents of test-related path \"{{invalidPath}}\" should not be referenced from the index file; ' +\n separateFileMessage,\n invalidName:\n 'Export \"{{invalidName}}\" appears to be test-related and should not be exported from the index file; ' +\n separateFileMessage,\n};\n\nexport const rule: TSESLint.RuleModule<MessageIds, RawRuleOptions[]> = {\n defaultOptions: [],\n meta: {\n type: 'problem',\n docs: {\n description: 'ban test, mock, and fixture exports from index files',\n recommended: 'recommended',\n },\n messages,\n schema: [\n {\n type: 'object',\n properties: {\n testPathPatterns: {\n type: 'array',\n description:\n 'Ban exports from these paths (regex) from index files. Use \"...\" to include the default paths.',\n items: { type: 'string' },\n },\n testExportNamePatterns: {\n type: 'array',\n description:\n 'Ban exports with these names (regex) from index files. Use \"...\" to include the default names.',\n items: { type: 'string' },\n },\n maxDepth: {\n type: 'number',\n description: 'Maximum depth of index file (0-indexed; default Infinity)',\n },\n },\n additionalProperties: false,\n },\n ],\n },\n create: (context) => {\n const options = context.options?.[0] || {};\n // eslint-disable-next-line etc/no-deprecated -- filename property may not exist yet?\n const filename = context.filename || context.getFilename?.();\n const testPathPatterns = getRegexpsFromStrings(options.testPathPatterns, defaultTestPaths);\n const testExportNamePatterns = getRegexpsFromStrings(options.testExportNamePatterns, defaultTestExportNames);\n const maxDepth = options.maxDepth ?? defaultMaxDepth;\n\n if (!isIncludedIndexFile({ filename, ignorePaths: testPathPatterns, maxDepth })) {\n return {};\n }\n\n const ruleContext: RuleContext = {\n ...context,\n options: { testExportNamePatterns, testPathPatterns },\n filename,\n };\n\n return {\n // import foo from 'foo'\n // import * as foo from 'foo'\n // import { foo } from 'foo'\n // import { foo as bar } from 'foo'\n // import 'foo'\n ImportDeclaration: (node) => checkNode(ruleContext, node),\n // import foo = require('foo')\n TSImportEqualsDeclaration: (node) => {\n // Skip \"export import foo = require('foo')\" because that will be handled by ExportNamedDeclaration\n if (node.parent.type !== 'ExportNamedDeclaration') {\n checkNode(ruleContext, node);\n }\n },\n // export { foo }\n ExportDeclaration: (node) => checkNode(ruleContext, node),\n // export { foo } from 'foo'\n // export { foo as bar } from 'foo'\n // export import foo = require('foo')\n // export const foo = 'foo';\n // and other exported declarations\n ExportNamedDeclaration: (node) => checkNode(ruleContext, node),\n // export * from 'foo'\n // export * as foo from 'foo'\n ExportAllDeclaration: (node) => checkNode(ruleContext, node),\n };\n },\n};\n\nfunction checkNode(\n context: RuleContext,\n node: Exclude<ImportLikeNode, TSESTree.CallExpression | TSESTree.ImportExpression>,\n) {\n const extension = path.extname(context.filename).slice(1);\n\n // If there's an import path, check it\n const { importPath, pathNode } = getImportPathLiteralNode(node) || {};\n if (importPath && pathNode && context.options.testPathPatterns.some((re) => re.test(importPath))) {\n const data: ErrorData = { extension, invalidPath: importPath };\n context.report({\n node: pathNode,\n messageId: 'invalidPath',\n data,\n });\n // This takes precedence over export name checks\n return;\n }\n\n // Check exported names if relevant\n if (node.type === 'ExportAllDeclaration' || node.type === 'ExportNamedDeclaration') {\n const ids = extractIdentifiersFromExport(node);\n for (const id of ids) {\n if (context.options.testExportNamePatterns.some((re) => re.test(id.name))) {\n const data: ErrorData = { extension, invalidName: id.name };\n context.report({\n node: id,\n messageId: 'invalidName',\n data,\n });\n }\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"no-test-exports.js","sourceRoot":"","sources":["../../src/rules/no-test-exports.ts"],"names":[],"mappings":"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","sourcesContent":["import type { TSESTree } from '@typescript-eslint/utils';\nimport * as path from 'path';\nimport { getImportPathLiteralNode, type ImportLikeNode } from '../utils/getImportPathLiteralNode';\nimport { extractIdentifiersFromExport } from '../utils/testExports/extractIdentifiersFromExport';\nimport { getRegexpsFromStrings } from '../utils/getRegexpsFromStrings';\nimport { isIncludedIndexFile } from '../utils/testExports/isIncludedIndexFile';\nimport { type RuleContext, createRule } from '../utils/createRule';\n\n/** Rule options as specified in the config */\nexport type RuleOptions = {\n testPathPatterns?: string[];\n testExportNamePatterns?: string[];\n maxDepth?: number;\n};\n\ntype Context = {\n /** Original rule context */\n ruleContext: RuleContext<RuleOptions, MessageIds, ErrorData>;\n /** Processed test path patterns */\n testPathPatterns: RegExp[];\n /** Processed test export name patterns */\n testExportNamePatterns: RegExp[];\n /** Current filename extension (no leading .) */\n extension: string;\n};\n\nexport const defaultTestPaths: RegExp[] = [\n // Only match lowercase mock|test|fixture at the beginning of an import path segment to avoid false positives\n /\\/(__)?(mock|test|fixture)/,\n /\\.(mock|test|fixture)s?(\\.\\w+)?$/,\n /Mock|Test|Fixture/,\n];\n// Only match lowercase mock|test|fixture at the beginning of an export name to avoid false positives\nconst defaultTestExportNames: RegExp[] = [/^(mock|test|fixture)/, /Mock|Test|Fixture/];\nexport const defaultMaxDepth = Infinity;\n\n/** Error message IDs corresponding to `rule.meta.messages` */\nexport type MessageIds = 'invalidPath' | 'invalidName';\n\n/** Error data `{{substitutions}}` used in messages */\nexport type ErrorData = {\n extension: string;\n invalidPath?: string;\n invalidName?: string;\n};\n\nconst separateFileMessage = 'use a separate file such as index.mock.{{extension}} instead';\n\nexport const rule = createRule<RuleOptions, MessageIds, ErrorData>({\n name: 'no-test-exports',\n meta: {\n type: 'problem',\n docs: {\n description: 'ban test, mock, and fixture exports from index files',\n recommended: 'recommended',\n },\n messages: {\n invalidPath:\n 'Contents of test-related path \"{{invalidPath}}\" should not be referenced from an index file; ' +\n separateFileMessage,\n invalidName:\n '\"{{invalidName}}\" appears to be test-related and should not be exported from an index file; ' +\n separateFileMessage,\n },\n schema: [\n {\n type: 'object',\n properties: {\n testPathPatterns: {\n type: 'array',\n description:\n 'Ban exports from these paths (regex) from index files. Use \"...\" to include the default paths.',\n items: { type: 'string' },\n },\n testExportNamePatterns: {\n type: 'array',\n description:\n 'Ban exports with these names (regex) from index files. Use \"...\" to include the default names.',\n items: { type: 'string' },\n },\n maxDepth: {\n type: 'number',\n description: 'Maximum depth of index file (0-indexed; default Infinity)',\n },\n },\n additionalProperties: false,\n },\n ],\n },\n create: (ruleContext) => {\n const options = ruleContext.options[0] || {};\n const testPathPatterns = getRegexpsFromStrings(options.testPathPatterns, defaultTestPaths);\n const testExportNamePatterns = getRegexpsFromStrings(options.testExportNamePatterns, defaultTestExportNames);\n const maxDepth = options.maxDepth ?? defaultMaxDepth;\n const extension = path.extname(ruleContext.filename).slice(1);\n\n if (!isIncludedIndexFile({ filename: ruleContext.filename, ignorePaths: testPathPatterns, maxDepth })) {\n return {};\n }\n\n const context: Context = { ruleContext, testExportNamePatterns, testPathPatterns, extension };\n\n return {\n // import foo from 'foo'\n // import * as foo from 'foo'\n // import { foo } from 'foo'\n // import { foo as bar } from 'foo'\n // import 'foo'\n ImportDeclaration: (node) => checkNode(context, node),\n // import foo = require('foo')\n TSImportEqualsDeclaration: (node) => {\n // Skip \"export import foo = require('foo')\" because that will be handled by ExportNamedDeclaration\n if (node.parent.type !== 'ExportNamedDeclaration') {\n checkNode(context, node);\n }\n },\n // export { foo }\n // export { foo } from 'foo'\n // export { foo as bar } from 'foo'\n // export import foo = require('foo')\n // export const foo = 'foo';\n // and other exported declarations\n ExportNamedDeclaration: (node) => checkNode(context, node),\n // export * from 'foo'\n // export * as foo from 'foo'\n ExportAllDeclaration: (node) => checkNode(context, node),\n };\n },\n});\n\n/** Check an import or export node for violations. */\nfunction checkNode(\n context: Context,\n node: Exclude<ImportLikeNode, TSESTree.CallExpression | TSESTree.ImportExpression>,\n) {\n const hasInvalidPath = checkInvalidPath(context, node);\n // Invalid path errors take precedence, to avoid spamming errors.\n if (!hasInvalidPath && (node.type === 'ExportAllDeclaration' || node.type === 'ExportNamedDeclaration')) {\n checkInvalidName(context, node);\n }\n}\n\n/**\n * Check an import or export for invalid paths.\n * Returns true if a violation was found.\n */\nfunction checkInvalidPath(\n context: Context,\n node: Exclude<ImportLikeNode, TSESTree.CallExpression | TSESTree.ImportExpression>,\n): boolean {\n const { ruleContext, testPathPatterns, extension } = context;\n\n // If there's an import path, check it\n const { importPath, pathNode } = getImportPathLiteralNode(node) || {};\n\n if (importPath && pathNode && testPathPatterns.some((re) => re.test(importPath))) {\n ruleContext.report({\n node: pathNode,\n messageId: 'invalidPath',\n data: { extension, invalidPath: importPath },\n });\n return true;\n }\n\n return false;\n}\n\n/** Check an export for invalid names. */\nfunction checkInvalidName(\n context: Context,\n node: TSESTree.ExportAllDeclaration | TSESTree.ExportNamedDeclaration,\n): void {\n const { ruleContext, testExportNamePatterns, extension } = context;\n\n const ids = extractIdentifiersFromExport(node);\n\n for (const id of ids) {\n if (testExportNamePatterns.some((re) => re.test(id.name))) {\n ruleContext.report({\n node: id,\n messageId: 'invalidName',\n data: { extension, invalidName: id.name },\n });\n }\n }\n}\n"]}
|
|
@@ -1,16 +1,14 @@
|
|
|
1
|
-
import type { TSESLint } from '@typescript-eslint/utils';
|
|
2
|
-
/** Processed rule options */
|
|
3
|
-
type RuleOptions = {
|
|
4
|
-
ignorePatterns: RegExp[];
|
|
5
|
-
debug: boolean;
|
|
6
|
-
};
|
|
7
1
|
/** Rule options as specified in the config */
|
|
8
|
-
export type
|
|
2
|
+
export type RuleOptions = {
|
|
9
3
|
ignorePatterns?: string[];
|
|
4
|
+
debug?: boolean;
|
|
10
5
|
};
|
|
11
6
|
export type ErrorMessageIds = 'noExports' | 'noExportsLocal' | 'notExported' | 'notExportedLocal';
|
|
12
7
|
export type SuggestMessageIds = 'useTopLevel';
|
|
13
8
|
export type MessageIds = ErrorMessageIds | SuggestMessageIds;
|
|
14
|
-
export
|
|
15
|
-
|
|
9
|
+
export type ErrorData = {
|
|
10
|
+
packageName: string;
|
|
11
|
+
subPath?: string;
|
|
12
|
+
};
|
|
13
|
+
export declare const rule: import("../utils/createRule").RuleModule<RuleOptions, MessageIds>;
|
|
16
14
|
//# sourceMappingURL=no-unsupported-imports.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-unsupported-imports.d.ts","sourceRoot":"","sources":["../../src/rules/no-unsupported-imports.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"no-unsupported-imports.d.ts","sourceRoot":"","sources":["../../src/rules/no-unsupported-imports.ts"],"names":[],"mappings":"AAQA,8CAA8C;AAC9C,MAAM,MAAM,WAAW,GAAG;IACxB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAUF,MAAM,MAAM,eAAe,GAAG,WAAW,GAAG,gBAAgB,GAAG,aAAa,GAAG,kBAAkB,CAAC;AAClG,MAAM,MAAM,iBAAiB,GAAG,aAAa,CAAC;AAC9C,MAAM,MAAM,UAAU,GAAG,eAAe,GAAG,iBAAiB,CAAC;AAE7D,MAAM,MAAM,SAAS,GAAG;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAYF,eAAO,MAAM,IAAI,mEAkEf,CAAC"}
|
|
@@ -7,25 +7,14 @@ const parseImportIfRelevant_1 = require("../utils/unsupportedImports/parseImport
|
|
|
7
7
|
const pathSatisfiesAnyExport_1 = require("../utils/unsupportedImports/pathSatisfiesAnyExport");
|
|
8
8
|
const resolvePackageRoot_1 = require("../utils/unsupportedImports/resolvePackageRoot");
|
|
9
9
|
const getRegexpsFromStrings_1 = require("../utils/getRegexpsFromStrings");
|
|
10
|
-
const
|
|
11
|
-
ignorePatterns: [],
|
|
12
|
-
debug: false,
|
|
13
|
-
};
|
|
10
|
+
const createRule_1 = require("../utils/createRule");
|
|
14
11
|
const preferTopLevel = 'prefer importing directly from "{{packageName}}" if possible';
|
|
15
12
|
const noExports = '"{{packageName}}" doesn\'t have an exports map, so deep imports aren\'t allowed; ' + preferTopLevel;
|
|
16
13
|
const notExported = 'Path "{{subPath}}" is not exported by "{{packageName}}", according to its exports map; ' + preferTopLevel;
|
|
17
14
|
const localMessage = '(Consider updating "{{packageName}}" to export additional identifiers if needed, ' +
|
|
18
15
|
'or if deep imports are strictly necessary, add an exports map.)';
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
noExportsLocal: `${noExports}. ${localMessage}`,
|
|
22
|
-
notExported,
|
|
23
|
-
notExportedLocal: `${notExported}. ${localMessage}`,
|
|
24
|
-
useTopLevel: 'Import directly from "{{packageName}}", if possible',
|
|
25
|
-
};
|
|
26
|
-
const suggestMessageId = 'useTopLevel';
|
|
27
|
-
exports.rule = {
|
|
28
|
-
defaultOptions: [defaultOptions],
|
|
16
|
+
exports.rule = (0, createRule_1.createRule)({
|
|
17
|
+
name: 'no-unsupported-imports',
|
|
29
18
|
meta: {
|
|
30
19
|
type: 'problem',
|
|
31
20
|
docs: {
|
|
@@ -33,7 +22,13 @@ exports.rule = {
|
|
|
33
22
|
recommended: 'recommended',
|
|
34
23
|
},
|
|
35
24
|
hasSuggestions: true,
|
|
36
|
-
messages
|
|
25
|
+
messages: {
|
|
26
|
+
noExports,
|
|
27
|
+
noExportsLocal: `${noExports}. ${localMessage}`,
|
|
28
|
+
notExported,
|
|
29
|
+
notExportedLocal: `${notExported}. ${localMessage}`,
|
|
30
|
+
useTopLevel: 'Import directly from "{{packageName}}", if possible',
|
|
31
|
+
},
|
|
37
32
|
schema: [
|
|
38
33
|
{
|
|
39
34
|
type: 'object',
|
|
@@ -49,17 +44,12 @@ exports.rule = {
|
|
|
49
44
|
},
|
|
50
45
|
],
|
|
51
46
|
},
|
|
52
|
-
create: (
|
|
53
|
-
const options =
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
...options,
|
|
59
|
-
ignorePatterns: (0, getRegexpsFromStrings_1.getRegexpsFromStrings)(options.ignorePatterns),
|
|
60
|
-
},
|
|
61
|
-
// eslint-disable-next-line etc/no-deprecated -- filename property doesn't appear to exist yet...?
|
|
62
|
-
filename: context.getFilename(),
|
|
47
|
+
create: (ruleContext) => {
|
|
48
|
+
const options = ruleContext.options?.[0] || {};
|
|
49
|
+
const context = {
|
|
50
|
+
ruleContext,
|
|
51
|
+
debug: options.debug ?? false,
|
|
52
|
+
ignorePatterns: (0, getRegexpsFromStrings_1.getRegexpsFromStrings)(options.ignorePatterns),
|
|
63
53
|
};
|
|
64
54
|
return {
|
|
65
55
|
// import foo from 'foo'
|
|
@@ -67,29 +57,30 @@ exports.rule = {
|
|
|
67
57
|
// import { foo } from 'foo'
|
|
68
58
|
// import { foo as bar } from 'foo'
|
|
69
59
|
// import 'foo'
|
|
70
|
-
ImportDeclaration: (node) => checkImportOrExport(
|
|
60
|
+
ImportDeclaration: (node) => checkImportOrExport(context, node),
|
|
71
61
|
// await import('foo')
|
|
72
|
-
ImportExpression: (node) => checkImportOrExport(
|
|
62
|
+
ImportExpression: (node) => checkImportOrExport(context, node),
|
|
73
63
|
// import foo = require('foo')
|
|
74
64
|
TSImportEqualsDeclaration: (node) => {
|
|
75
65
|
// Skip "export import foo = require('foo')" because that will be handled by ExportNamedDeclaration
|
|
76
66
|
if (node.parent.type !== 'ExportNamedDeclaration') {
|
|
77
|
-
checkImportOrExport(
|
|
67
|
+
checkImportOrExport(context, node);
|
|
78
68
|
}
|
|
79
69
|
},
|
|
80
70
|
// export { foo } from 'foo'
|
|
81
71
|
// export { foo as bar } from 'foo'
|
|
82
|
-
ExportNamedDeclaration: (node) => checkImportOrExport(
|
|
72
|
+
ExportNamedDeclaration: (node) => checkImportOrExport(context, node),
|
|
83
73
|
// export * from 'foo'
|
|
84
74
|
// export * as foo from 'foo'
|
|
85
|
-
ExportAllDeclaration: (node) => checkImportOrExport(
|
|
75
|
+
ExportAllDeclaration: (node) => checkImportOrExport(context, node),
|
|
86
76
|
// require('foo')
|
|
87
77
|
// require.resolve('foo')
|
|
88
|
-
CallExpression: (node) => checkImportOrExport(
|
|
78
|
+
CallExpression: (node) => checkImportOrExport(context, node),
|
|
89
79
|
};
|
|
90
80
|
},
|
|
91
|
-
};
|
|
81
|
+
});
|
|
92
82
|
function checkImportOrExport(context, node) {
|
|
83
|
+
const { ruleContext } = context;
|
|
93
84
|
const { importPath, pathNode } = (0, getImportPathLiteralNode_1.getImportPathLiteralNode)(node) || {};
|
|
94
85
|
if (!(pathNode && importPath)) {
|
|
95
86
|
return;
|
|
@@ -100,7 +91,7 @@ function checkImportOrExport(context, node) {
|
|
|
100
91
|
}
|
|
101
92
|
// At this point, the import is probably invalid, so report it.
|
|
102
93
|
// (Might still be a false positive if it's a local package that hasn't been built yet.)
|
|
103
|
-
const { messageId,
|
|
94
|
+
const { messageId, data } = errorData;
|
|
104
95
|
// For named imports/exports or import *, suggest importing from the package root.
|
|
105
96
|
// This is just a suggestion, so it doesn't have to be correct, but we also want to avoid
|
|
106
97
|
// suggesting when it's likely to be misleading or might cause issues; for example:
|
|
@@ -121,14 +112,14 @@ function checkImportOrExport(context, node) {
|
|
|
121
112
|
// export { foo } from 'foo/bar'
|
|
122
113
|
node.type === 'ExportNamedDeclaration') {
|
|
123
114
|
suggestion = {
|
|
124
|
-
messageId:
|
|
115
|
+
messageId: 'useTopLevel',
|
|
125
116
|
data,
|
|
126
117
|
fix: (fixer) => fixer.replaceTextRange(
|
|
127
118
|
// Keep the quotes
|
|
128
119
|
[pathNode.range[0] + 1, pathNode.range[1] - 1], data.packageName),
|
|
129
120
|
};
|
|
130
121
|
}
|
|
131
|
-
|
|
122
|
+
ruleContext.report({
|
|
132
123
|
node: pathNode,
|
|
133
124
|
messageId,
|
|
134
125
|
data,
|
|
@@ -136,7 +127,7 @@ function checkImportOrExport(context, node) {
|
|
|
136
127
|
});
|
|
137
128
|
}
|
|
138
129
|
function checkImportPath(context, importPath) {
|
|
139
|
-
const { ignorePatterns } = context
|
|
130
|
+
const { ignorePatterns, debug, ruleContext } = context;
|
|
140
131
|
const importParts = (0, parseImportIfRelevant_1.parseImportIfRelevant)(importPath, ignorePatterns);
|
|
141
132
|
if (!importParts) {
|
|
142
133
|
// Import is relative, top-level, built-in, or ignored
|
|
@@ -144,7 +135,7 @@ function checkImportPath(context, importPath) {
|
|
|
144
135
|
}
|
|
145
136
|
const { packageName, subPath } = importParts;
|
|
146
137
|
// Find the root of the current file's package and read its package.json
|
|
147
|
-
const currentPkg = (0, getPackageInfo_1.getPackageInfo)(
|
|
138
|
+
const currentPkg = (0, getPackageInfo_1.getPackageInfo)(ruleContext.filename, debug);
|
|
148
139
|
if (!currentPkg || currentPkg.json.name === packageName) {
|
|
149
140
|
// - If package.json for the current file's package wasn't found, ignore the import
|
|
150
141
|
// (getPackageInfo already logged a debug message about it)
|
|
@@ -155,13 +146,13 @@ function checkImportPath(context, importPath) {
|
|
|
155
146
|
// (This resolution must be done in the context of the current package in case eslint is running
|
|
156
147
|
// in a different directory, e.g. monorepo root, where the package may not be installed or a
|
|
157
148
|
// different version may be installed.)
|
|
158
|
-
const resolvedPkgRoot = (0, resolvePackageRoot_1.resolvePackageRoot)(currentPkg.path, packageName,
|
|
149
|
+
const resolvedPkgRoot = (0, resolvePackageRoot_1.resolvePackageRoot)(currentPkg.path, packageName, debug);
|
|
159
150
|
if (!resolvedPkgRoot) {
|
|
160
151
|
// Maybe an optional dep?
|
|
161
152
|
return null;
|
|
162
153
|
}
|
|
163
154
|
// Read packageName's package.json
|
|
164
|
-
const importPkg = (0, getPackageInfo_1.getPackageInfo)(resolvedPkgRoot,
|
|
155
|
+
const importPkg = (0, getPackageInfo_1.getPackageInfo)(resolvedPkgRoot, debug);
|
|
165
156
|
if (!importPkg) {
|
|
166
157
|
return null;
|
|
167
158
|
}
|
|
@@ -169,16 +160,14 @@ function checkImportPath(context, importPath) {
|
|
|
169
160
|
// Deep imports are always an error if there's no exports map
|
|
170
161
|
return {
|
|
171
162
|
messageId: importPkg.isLocal ? 'noExportsLocal' : 'noExports',
|
|
172
|
-
|
|
173
|
-
packageName,
|
|
163
|
+
data: { subPath, packageName },
|
|
174
164
|
};
|
|
175
165
|
}
|
|
176
166
|
// Check if subPath matches any paths from exports (disregarding conditions)
|
|
177
167
|
if (!(0, pathSatisfiesAnyExport_1.pathSatisfiesAnyExport)(importPkg.json.exports, subPath)) {
|
|
178
168
|
return {
|
|
179
169
|
messageId: importPkg.isLocal ? 'notExportedLocal' : 'notExported',
|
|
180
|
-
|
|
181
|
-
packageName,
|
|
170
|
+
data: { subPath, packageName },
|
|
182
171
|
};
|
|
183
172
|
}
|
|
184
173
|
return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-unsupported-imports.js","sourceRoot":"","sources":["../../src/rules/no-unsupported-imports.ts"],"names":[],"mappings":"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","sourcesContent":["import type { TSESLint } from '@typescript-eslint/utils';\nimport { getImportPathLiteralNode, type ImportLikeNode } from '../utils/getImportPathLiteralNode';\nimport { getPackageInfo } from '../utils/getPackageInfo';\nimport { parseImportIfRelevant } from '../utils/unsupportedImports/parseImportIfRelevant';\nimport { pathSatisfiesAnyExport } from '../utils/unsupportedImports/pathSatisfiesAnyExport';\nimport { resolvePackageRoot } from '../utils/unsupportedImports/resolvePackageRoot';\nimport { getRegexpsFromStrings } from '../utils/getRegexpsFromStrings';\n\n/** Processed rule options */\ntype RuleOptions = {\n ignorePatterns: RegExp[];\n debug: boolean;\n};\n\n/** Rule options as specified in the config */\nexport type RawRuleOptions = Omit<Partial<RuleOptions>, 'ignorePatterns'> & {\n ignorePatterns?: string[];\n};\n\ntype RuleContext = Omit<RawRuleContext, 'options'> & {\n options: RuleOptions;\n filename: string;\n};\n\nexport type ErrorMessageIds = 'noExports' | 'noExportsLocal' | 'notExported' | 'notExportedLocal';\nexport type SuggestMessageIds = 'useTopLevel';\nexport type MessageIds = ErrorMessageIds | SuggestMessageIds;\n\ntype ErrorData = {\n packageName: string;\n subPath: string;\n};\n\ntype ErrorResult = ErrorData & {\n messageId: ErrorMessageIds;\n};\n\ntype RawRuleContext = TSESLint.RuleContext<MessageIds, RawRuleOptions[]>;\n\nconst defaultOptions: RawRuleOptions = {\n ignorePatterns: [],\n debug: false,\n};\n\nconst preferTopLevel = 'prefer importing directly from \"{{packageName}}\" if possible';\nconst noExports = '\"{{packageName}}\" doesn\\'t have an exports map, so deep imports aren\\'t allowed; ' + preferTopLevel;\nconst notExported =\n 'Path \"{{subPath}}\" is not exported by \"{{packageName}}\", according to its exports map; ' + preferTopLevel;\nconst localMessage =\n '(Consider updating \"{{packageName}}\" to export additional identifiers if needed, ' +\n 'or if deep imports are strictly necessary, add an exports map.)';\n\nconst messages: Record<MessageIds, string> = {\n noExports,\n noExportsLocal: `${noExports}. ${localMessage}`,\n notExported,\n notExportedLocal: `${notExported}. ${localMessage}`,\n useTopLevel: 'Import directly from \"{{packageName}}\", if possible',\n};\n\nconst suggestMessageId: SuggestMessageIds = 'useTopLevel';\n\nexport const rule: TSESLint.RuleModule<MessageIds, RawRuleOptions[]> = {\n defaultOptions: [defaultOptions],\n meta: {\n type: 'problem',\n docs: {\n description: 'ban importing from non-exported paths',\n recommended: 'recommended',\n },\n hasSuggestions: true,\n messages,\n schema: [\n {\n type: 'object',\n properties: {\n ignorePatterns: {\n type: 'array',\n description: 'Ignore imports matching these regular expression patterns.',\n items: { type: 'string' },\n },\n debug: { type: 'boolean' },\n },\n additionalProperties: false,\n },\n ],\n },\n create: (context) => {\n const options = context.options?.[0] || {};\n const ruleContext: RuleContext = {\n ...context,\n options: {\n debug: false,\n ...options,\n ignorePatterns: getRegexpsFromStrings(options.ignorePatterns),\n },\n // eslint-disable-next-line etc/no-deprecated -- filename property doesn't appear to exist yet...?\n filename: context.getFilename(),\n };\n\n return {\n // import foo from 'foo'\n // import * as foo from 'foo'\n // import { foo } from 'foo'\n // import { foo as bar } from 'foo'\n // import 'foo'\n ImportDeclaration: (node) => checkImportOrExport(ruleContext, node),\n // await import('foo')\n ImportExpression: (node) => checkImportOrExport(ruleContext, node),\n // import foo = require('foo')\n TSImportEqualsDeclaration: (node) => {\n // Skip \"export import foo = require('foo')\" because that will be handled by ExportNamedDeclaration\n if (node.parent.type !== 'ExportNamedDeclaration') {\n checkImportOrExport(ruleContext, node);\n }\n },\n // export { foo } from 'foo'\n // export { foo as bar } from 'foo'\n ExportNamedDeclaration: (node) => checkImportOrExport(ruleContext, node),\n // export * from 'foo'\n // export * as foo from 'foo'\n ExportAllDeclaration: (node) => checkImportOrExport(ruleContext, node),\n // require('foo')\n // require.resolve('foo')\n CallExpression: (node) => checkImportOrExport(ruleContext, node),\n };\n },\n};\n\nfunction checkImportOrExport(context: RuleContext, node: ImportLikeNode) {\n const { importPath, pathNode } = getImportPathLiteralNode(node) || {};\n if (!(pathNode && importPath)) {\n return;\n }\n\n const errorData = checkImportPath(context, importPath);\n if (!errorData) {\n return; // Import is fine\n }\n\n // At this point, the import is probably invalid, so report it.\n // (Might still be a false positive if it's a local package that hasn't been built yet.)\n const { messageId, ...data } = errorData;\n\n // For named imports/exports or import *, suggest importing from the package root.\n // This is just a suggestion, so it doesn't have to be correct, but we also want to avoid\n // suggesting when it's likely to be misleading or might cause issues; for example:\n // - Default imports may refer to an actual default export or the entire module\n // - Side effect imports are likely specific to a particular file\n // - Async import()\n // - export * from an entire package might export a lot more things\n // - require() could be used in many ways (would have to look at more context)\n // - require.resolve() is typically used to find the path to a specific file\n let suggestion: TSESLint.ReportSuggestionArray<SuggestMessageIds>[number] | undefined;\n if (\n // import { foo } from 'foo/bar'\n (node.type === 'ImportDeclaration' && node.specifiers[0]?.type === 'ImportSpecifier') ||\n // import * as foo from 'foo/bar'\n (node.type === 'ImportDeclaration' && node.specifiers[0]?.type === 'ImportNamespaceSpecifier') ||\n // import foo = require('foo/bar')\n node.type === 'TSImportEqualsDeclaration' ||\n // export { foo } from 'foo/bar'\n node.type === 'ExportNamedDeclaration'\n ) {\n suggestion = {\n messageId: suggestMessageId,\n data,\n fix: (fixer) =>\n fixer.replaceTextRange(\n // Keep the quotes\n [pathNode.range[0] + 1, pathNode.range[1] - 1],\n data.packageName,\n ),\n };\n }\n\n context.report({\n node: pathNode,\n messageId,\n data,\n suggest: suggestion ? [suggestion] : undefined,\n });\n}\n\nfunction checkImportPath(context: RuleContext, importPath: string): ErrorResult | null {\n const { ignorePatterns } = context.options;\n\n const importParts = parseImportIfRelevant(importPath, ignorePatterns);\n if (!importParts) {\n // Import is relative, top-level, built-in, or ignored\n return null;\n }\n const { packageName, subPath } = importParts;\n\n // Find the root of the current file's package and read its package.json\n const currentPkg = getPackageInfo(context.filename, context.options.debug);\n if (!currentPkg || currentPkg.json.name === packageName) {\n // - If package.json for the current file's package wasn't found, ignore the import\n // (getPackageInfo already logged a debug message about it)\n // - If the import is a self-reference, ignore it\n return null;\n }\n\n // Resolve packageName's top-level import so we can find the package.json.\n // (This resolution must be done in the context of the current package in case eslint is running\n // in a different directory, e.g. monorepo root, where the package may not be installed or a\n // different version may be installed.)\n const resolvedPkgRoot = resolvePackageRoot(currentPkg.path, packageName, context.options.debug);\n if (!resolvedPkgRoot) {\n // Maybe an optional dep?\n return null;\n }\n\n // Read packageName's package.json\n const importPkg = getPackageInfo(resolvedPkgRoot, context.options.debug);\n if (!importPkg) {\n return null;\n }\n\n if (!importPkg.json.exports) {\n // Deep imports are always an error if there's no exports map\n return {\n messageId: importPkg.isLocal ? 'noExportsLocal' : 'noExports',\n subPath: subPath,\n packageName,\n };\n }\n\n // Check if subPath matches any paths from exports (disregarding conditions)\n if (!pathSatisfiesAnyExport(importPkg.json.exports, subPath)) {\n return {\n messageId: importPkg.isLocal ? 'notExportedLocal' : 'notExported',\n subPath: subPath,\n packageName,\n };\n }\n return null;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"no-unsupported-imports.js","sourceRoot":"","sources":["../../src/rules/no-unsupported-imports.ts"],"names":[],"mappings":"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","sourcesContent":["import { getImportPathLiteralNode, type ImportLikeNode } from '../utils/getImportPathLiteralNode';\nimport { getPackageInfo } from '../utils/getPackageInfo';\nimport { parseImportIfRelevant } from '../utils/unsupportedImports/parseImportIfRelevant';\nimport { pathSatisfiesAnyExport } from '../utils/unsupportedImports/pathSatisfiesAnyExport';\nimport { resolvePackageRoot } from '../utils/unsupportedImports/resolvePackageRoot';\nimport { getRegexpsFromStrings } from '../utils/getRegexpsFromStrings';\nimport { createRule, type SuggestionDescriptor, type RuleContext, type ReportDescriptor } from '../utils/createRule';\n\n/** Rule options as specified in the config */\nexport type RuleOptions = {\n ignorePatterns?: string[];\n debug?: boolean;\n};\n\ntype Context = {\n /** Original rule context */\n ruleContext: RuleContext<RuleOptions, MessageIds, ErrorData>;\n /** Processed ignore patterns */\n ignorePatterns: RegExp[];\n debug: boolean;\n};\n\nexport type ErrorMessageIds = 'noExports' | 'noExportsLocal' | 'notExported' | 'notExportedLocal';\nexport type SuggestMessageIds = 'useTopLevel';\nexport type MessageIds = ErrorMessageIds | SuggestMessageIds;\n\nexport type ErrorData = {\n packageName: string;\n subPath?: string;\n};\n\ntype ErrorResult = Pick<ReportDescriptor<ErrorMessageIds, ErrorData>, 'messageId' | 'data'>;\n\nconst preferTopLevel = 'prefer importing directly from \"{{packageName}}\" if possible';\nconst noExports = '\"{{packageName}}\" doesn\\'t have an exports map, so deep imports aren\\'t allowed; ' + preferTopLevel;\nconst notExported =\n 'Path \"{{subPath}}\" is not exported by \"{{packageName}}\", according to its exports map; ' + preferTopLevel;\nconst localMessage =\n '(Consider updating \"{{packageName}}\" to export additional identifiers if needed, ' +\n 'or if deep imports are strictly necessary, add an exports map.)';\n\nexport const rule = createRule<RuleOptions, MessageIds, ErrorData>({\n name: 'no-unsupported-imports',\n meta: {\n type: 'problem',\n docs: {\n description: 'ban importing from non-exported paths',\n recommended: 'recommended',\n },\n hasSuggestions: true,\n messages: {\n noExports,\n noExportsLocal: `${noExports}. ${localMessage}`,\n notExported,\n notExportedLocal: `${notExported}. ${localMessage}`,\n useTopLevel: 'Import directly from \"{{packageName}}\", if possible',\n },\n schema: [\n {\n type: 'object',\n properties: {\n ignorePatterns: {\n type: 'array',\n description: 'Ignore imports matching these regular expression patterns.',\n items: { type: 'string' },\n },\n debug: { type: 'boolean' },\n },\n additionalProperties: false,\n },\n ],\n },\n create: (ruleContext) => {\n const options = ruleContext.options?.[0] || {};\n const context: Context = {\n ruleContext,\n debug: options.debug ?? false,\n ignorePatterns: getRegexpsFromStrings(options.ignorePatterns),\n };\n\n return {\n // import foo from 'foo'\n // import * as foo from 'foo'\n // import { foo } from 'foo'\n // import { foo as bar } from 'foo'\n // import 'foo'\n ImportDeclaration: (node) => checkImportOrExport(context, node),\n // await import('foo')\n ImportExpression: (node) => checkImportOrExport(context, node),\n // import foo = require('foo')\n TSImportEqualsDeclaration: (node) => {\n // Skip \"export import foo = require('foo')\" because that will be handled by ExportNamedDeclaration\n if (node.parent.type !== 'ExportNamedDeclaration') {\n checkImportOrExport(context, node);\n }\n },\n // export { foo } from 'foo'\n // export { foo as bar } from 'foo'\n ExportNamedDeclaration: (node) => checkImportOrExport(context, node),\n // export * from 'foo'\n // export * as foo from 'foo'\n ExportAllDeclaration: (node) => checkImportOrExport(context, node),\n // require('foo')\n // require.resolve('foo')\n CallExpression: (node) => checkImportOrExport(context, node),\n };\n },\n});\n\nfunction checkImportOrExport(context: Context, node: ImportLikeNode) {\n const { ruleContext } = context;\n\n const { importPath, pathNode } = getImportPathLiteralNode(node) || {};\n if (!(pathNode && importPath)) {\n return;\n }\n\n const errorData = checkImportPath(context, importPath);\n if (!errorData) {\n return; // Import is fine\n }\n\n // At this point, the import is probably invalid, so report it.\n // (Might still be a false positive if it's a local package that hasn't been built yet.)\n const { messageId, data } = errorData;\n\n // For named imports/exports or import *, suggest importing from the package root.\n // This is just a suggestion, so it doesn't have to be correct, but we also want to avoid\n // suggesting when it's likely to be misleading or might cause issues; for example:\n // - Default imports may refer to an actual default export or the entire module\n // - Side effect imports are likely specific to a particular file\n // - Async import()\n // - export * from an entire package might export a lot more things\n // - require() could be used in many ways (would have to look at more context)\n // - require.resolve() is typically used to find the path to a specific file\n let suggestion: SuggestionDescriptor<SuggestMessageIds, ErrorData> | undefined;\n if (\n // import { foo } from 'foo/bar'\n (node.type === 'ImportDeclaration' && node.specifiers[0]?.type === 'ImportSpecifier') ||\n // import * as foo from 'foo/bar'\n (node.type === 'ImportDeclaration' && node.specifiers[0]?.type === 'ImportNamespaceSpecifier') ||\n // import foo = require('foo/bar')\n node.type === 'TSImportEqualsDeclaration' ||\n // export { foo } from 'foo/bar'\n node.type === 'ExportNamedDeclaration'\n ) {\n suggestion = {\n messageId: 'useTopLevel',\n data,\n fix: (fixer) =>\n fixer.replaceTextRange(\n // Keep the quotes\n [pathNode.range[0] + 1, pathNode.range[1] - 1],\n data.packageName,\n ),\n };\n }\n\n ruleContext.report({\n node: pathNode,\n messageId,\n data,\n suggest: suggestion ? [suggestion] : undefined,\n });\n}\n\nfunction checkImportPath(context: Context, importPath: string): ErrorResult | null {\n const { ignorePatterns, debug, ruleContext } = context;\n\n const importParts = parseImportIfRelevant(importPath, ignorePatterns);\n if (!importParts) {\n // Import is relative, top-level, built-in, or ignored\n return null;\n }\n const { packageName, subPath } = importParts;\n\n // Find the root of the current file's package and read its package.json\n const currentPkg = getPackageInfo(ruleContext.filename, debug);\n if (!currentPkg || currentPkg.json.name === packageName) {\n // - If package.json for the current file's package wasn't found, ignore the import\n // (getPackageInfo already logged a debug message about it)\n // - If the import is a self-reference, ignore it\n return null;\n }\n\n // Resolve packageName's top-level import so we can find the package.json.\n // (This resolution must be done in the context of the current package in case eslint is running\n // in a different directory, e.g. monorepo root, where the package may not be installed or a\n // different version may be installed.)\n const resolvedPkgRoot = resolvePackageRoot(currentPkg.path, packageName, debug);\n if (!resolvedPkgRoot) {\n // Maybe an optional dep?\n return null;\n }\n\n // Read packageName's package.json\n const importPkg = getPackageInfo(resolvedPkgRoot, debug);\n if (!importPkg) {\n return null;\n }\n\n if (!importPkg.json.exports) {\n // Deep imports are always an error if there's no exports map\n return {\n messageId: importPkg.isLocal ? 'noExportsLocal' : 'noExports',\n data: { subPath, packageName },\n };\n }\n\n // Check if subPath matches any paths from exports (disregarding conditions)\n if (!pathSatisfiesAnyExport(importPkg.json.exports, subPath)) {\n return {\n messageId: importPkg.isLocal ? 'notExportedLocal' : 'notExported',\n data: { subPath, packageName },\n };\n }\n return null;\n}\n"]}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { ESLintUtils, type TSESLint, type TSESTree } from '@typescript-eslint/utils';
|
|
2
|
+
type Writable<T> = {
|
|
3
|
+
-readonly [K in keyof T]: T[K];
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
* Problem report descriptor with strongly typed error and suggestion data.
|
|
7
|
+
* (This assumes all error/suggestion messages share a common data type.)
|
|
8
|
+
*/
|
|
9
|
+
export type ReportDescriptor<TMessageIds extends string, TErrorData> = Omit<Writable<TSESLint.ReportDescriptor<TMessageIds>>, 'data' | 'node' | 'suggest'> & {
|
|
10
|
+
data: TErrorData;
|
|
11
|
+
node: TSESTree.Node;
|
|
12
|
+
suggest?: SuggestionDescriptor<TMessageIds, TErrorData>[] | null;
|
|
13
|
+
};
|
|
14
|
+
/** Fix suggestion with strongly typed data. */
|
|
15
|
+
export type SuggestionDescriptor<TMessageIds extends string, TErrorData> = Omit<ReportDescriptor<TMessageIds, TErrorData>, 'suggest' | 'node'>;
|
|
16
|
+
/** Fix suggestion output (for testing). */
|
|
17
|
+
export type SuggestionOutput<TMessageIds extends string, TErrorData> = Omit<TSESLint.SuggestionOutput<TMessageIds>, 'data'> & {
|
|
18
|
+
data?: TErrorData;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Rule context with strongly typed `report`.
|
|
22
|
+
* (This assumes all error/suggestion messages share a common data type.)
|
|
23
|
+
*/
|
|
24
|
+
export type RuleContext<TOptions, TMessageIds extends string, TErrorData> = Omit<TSESLint.RuleContext<TMessageIds, TOptions[]>, 'report'> & {
|
|
25
|
+
report: (descriptor: ReportDescriptor<TMessageIds, TErrorData>) => void;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Rule info with better {@link RuleContext} type for the `create` function.
|
|
29
|
+
* (This assumes all error/suggestion messages share a common data type.)
|
|
30
|
+
*/
|
|
31
|
+
type CreateRuleOptions<TOptions, TMessageIds extends string, TErrorData> = Omit<ESLintUtils.RuleWithMetaAndName<TOptions[], TMessageIds>, 'create' | 'defaultOptions'> & {
|
|
32
|
+
create(this: void, context: Readonly<RuleContext<TOptions, TMessageIds, TErrorData>>): TSESLint.RuleListener;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* This exported alias is just to avoid an error when exporting the `rule` returned from `createRule`
|
|
36
|
+
* ("the inferred type of 'rule' cannot be named without a reference to ...").
|
|
37
|
+
*/
|
|
38
|
+
export type RuleModule<TOptions, TMessageIds extends string> = ESLintUtils.RuleModule<TMessageIds, TOptions[], ESLintUtils.RuleListener>;
|
|
39
|
+
/**
|
|
40
|
+
* Create a lint rule, with strongly typed data in `context.report`.
|
|
41
|
+
* (This assumes all error/suggestion messages share a common data type.)
|
|
42
|
+
*/
|
|
43
|
+
export declare function createRule<TOptions, TMessageIds extends string, TErrorData>(params: CreateRuleOptions<TOptions, TMessageIds, TErrorData>): RuleModule<TOptions, TMessageIds>;
|
|
44
|
+
export {};
|
|
45
|
+
//# sourceMappingURL=createRule.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createRule.d.ts","sourceRoot":"","sources":["../../src/utils/createRule.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAE,KAAK,QAAQ,EAAE,KAAK,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAErF,KAAK,QAAQ,CAAC,CAAC,IAAI;IAAE,CAAC,UAAU,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAAE,CAAC;AAEtD;;;GAGG;AACH,MAAM,MAAM,gBAAgB,CAAC,WAAW,SAAS,MAAM,EAAE,UAAU,IAAI,IAAI,CAEzE,QAAQ,CAAC,QAAQ,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAC,EAChD,MAAM,GAAG,MAAM,GAAG,SAAS,CAC5B,GAAG;IACF,IAAI,EAAE,UAAU,CAAC;IAEjB,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,oBAAoB,CAAC,WAAW,EAAE,UAAU,CAAC,EAAE,GAAG,IAAI,CAAC;CAClE,CAAC;AAEF,+CAA+C;AAC/C,MAAM,MAAM,oBAAoB,CAAC,WAAW,SAAS,MAAM,EAAE,UAAU,IAAI,IAAI,CAC7E,gBAAgB,CAAC,WAAW,EAAE,UAAU,CAAC,EACzC,SAAS,GAAG,MAAM,CACnB,CAAC;AAEF,2CAA2C;AAC3C,MAAM,MAAM,gBAAgB,CAAC,WAAW,SAAS,MAAM,EAAE,UAAU,IAAI,IAAI,CACzE,QAAQ,CAAC,gBAAgB,CAAC,WAAW,CAAC,EACtC,MAAM,CACP,GAAG;IAAE,IAAI,CAAC,EAAE,UAAU,CAAA;CAAE,CAAC;AAE1B;;;GAGG;AACH,MAAM,MAAM,WAAW,CAAC,QAAQ,EAAE,WAAW,SAAS,MAAM,EAAE,UAAU,IAAI,IAAI,CAC9E,QAAQ,CAAC,WAAW,CAAC,WAAW,EAAE,QAAQ,EAAE,CAAC,EAC7C,QAAQ,CACT,GAAG;IACF,MAAM,EAAE,CAAC,UAAU,EAAE,gBAAgB,CAAC,WAAW,EAAE,UAAU,CAAC,KAAK,IAAI,CAAC;CACzE,CAAC;AAEF;;;GAGG;AACH,KAAK,iBAAiB,CAAC,QAAQ,EAAE,WAAW,SAAS,MAAM,EAAE,UAAU,IAAI,IAAI,CAC7E,WAAW,CAAC,mBAAmB,CAAC,QAAQ,EAAE,EAAE,WAAW,CAAC,EAMxD,QAAQ,GAAG,gBAAgB,CAC5B,GAAG;IACF,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,WAAW,CAAC,QAAQ,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC,GAAG,QAAQ,CAAC,YAAY,CAAC;CAC9G,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,UAAU,CAAC,QAAQ,EAAE,WAAW,SAAS,MAAM,IAAI,WAAW,CAAC,UAAU,CACnF,WAAW,EACX,QAAQ,EAAE,EACV,WAAW,CAAC,YAAY,CACzB,CAAC;AAMF;;;GAGG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,WAAW,SAAS,MAAM,EAAE,UAAU,EACzE,MAAM,EAAE,iBAAiB,CAAC,QAAQ,EAAE,WAAW,EAAE,UAAU,CAAC,GAC3D,UAAU,CAAC,QAAQ,EAAE,WAAW,CAAC,CAkBnC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createRule = void 0;
|
|
4
|
+
//
|
|
5
|
+
// This file contains wrappers for rule creation and related types.
|
|
6
|
+
//
|
|
7
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
8
|
+
const baseCreateRule = utils_1.ESLintUtils.RuleCreator((name) => `https://www.npmjs.com/package/@ms-cloudpack/eslint-plugin#${name}`);
|
|
9
|
+
/**
|
|
10
|
+
* Create a lint rule, with strongly typed data in `context.report`.
|
|
11
|
+
* (This assumes all error/suggestion messages share a common data type.)
|
|
12
|
+
*/
|
|
13
|
+
function createRule(params) {
|
|
14
|
+
return baseCreateRule({
|
|
15
|
+
...params,
|
|
16
|
+
defaultOptions: [],
|
|
17
|
+
create: (context) => {
|
|
18
|
+
const contextCopy = { ...context };
|
|
19
|
+
// Fill in properties that were added in eslint 8.40.0
|
|
20
|
+
/* eslint-disable etc/no-deprecated */
|
|
21
|
+
contextCopy.cwd ??= context.getCwd?.();
|
|
22
|
+
contextCopy.filename ??= context.getFilename?.();
|
|
23
|
+
contextCopy.physicalFilename ??= context.getPhysicalFilename?.();
|
|
24
|
+
contextCopy.sourceCode ??= context.getSourceCode?.();
|
|
25
|
+
/* eslint-enable etc/no-deprecated */
|
|
26
|
+
return params.create(contextCopy);
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
exports.createRule = createRule;
|
|
31
|
+
//# sourceMappingURL=createRule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createRule.js","sourceRoot":"","sources":["../../src/utils/createRule.ts"],"names":[],"mappings":";;;AAAA,EAAE;AACF,mEAAmE;AACnE,EAAE;AACF,oDAAqF;AAoErF,MAAM,cAAc,GAAG,mBAAW,CAAC,WAAW,CAC5C,CAAC,IAAI,EAAE,EAAE,CAAC,6DAA6D,IAAI,EAAE,CAC9E,CAAC;AAEF;;;GAGG;AACH,SAAgB,UAAU,CACxB,MAA4D;IAE5D,OAAO,cAAc,CAAC;QACpB,GAAG,MAAM;QACT,cAAc,EAAE,EAAE;QAClB,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE;YAClB,MAAM,WAAW,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC;YAEnC,sDAAsD;YACtD,sCAAsC;YACtC,WAAW,CAAC,GAAG,KAAK,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;YACvC,WAAW,CAAC,QAAQ,KAAK,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC;YACjD,WAAW,CAAC,gBAAgB,KAAK,OAAO,CAAC,mBAAmB,EAAE,EAAE,CAAC;YACjE,WAAW,CAAC,UAAU,KAAK,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC;YACrD,qCAAqC;YAErC,OAAO,MAAM,CAAC,MAAM,CAAC,WAA6D,CAAC,CAAC;QACtF,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AApBD,gCAoBC","sourcesContent":["//\n// This file contains wrappers for rule creation and related types.\n//\nimport { ESLintUtils, type TSESLint, type TSESTree } from '@typescript-eslint/utils';\n\ntype Writable<T> = { -readonly [K in keyof T]: T[K] };\n\n/**\n * Problem report descriptor with strongly typed error and suggestion data.\n * (This assumes all error/suggestion messages share a common data type.)\n */\nexport type ReportDescriptor<TMessageIds extends string, TErrorData> = Omit<\n // Make the descriptor writable for convenience (like conditionally adding a fix)\n Writable<TSESLint.ReportDescriptor<TMessageIds>>,\n 'data' | 'node' | 'suggest'\n> & {\n data: TErrorData;\n // have to re-declare `node` due to an oddity with union types (built-in ReportDescriptor is a union) and omit\n node: TSESTree.Node;\n suggest?: SuggestionDescriptor<TMessageIds, TErrorData>[] | null;\n};\n\n/** Fix suggestion with strongly typed data. */\nexport type SuggestionDescriptor<TMessageIds extends string, TErrorData> = Omit<\n ReportDescriptor<TMessageIds, TErrorData>,\n 'suggest' | 'node'\n>;\n\n/** Fix suggestion output (for testing). */\nexport type SuggestionOutput<TMessageIds extends string, TErrorData> = Omit<\n TSESLint.SuggestionOutput<TMessageIds>,\n 'data'\n> & { data?: TErrorData };\n\n/**\n * Rule context with strongly typed `report`.\n * (This assumes all error/suggestion messages share a common data type.)\n */\nexport type RuleContext<TOptions, TMessageIds extends string, TErrorData> = Omit<\n TSESLint.RuleContext<TMessageIds, TOptions[]>,\n 'report'\n> & {\n report: (descriptor: ReportDescriptor<TMessageIds, TErrorData>) => void;\n};\n\n/**\n * Rule info with better {@link RuleContext} type for the `create` function.\n * (This assumes all error/suggestion messages share a common data type.)\n */\ntype CreateRuleOptions<TOptions, TMessageIds extends string, TErrorData> = Omit<\n ESLintUtils.RuleWithMetaAndName<TOptions[], TMessageIds>,\n // Why remove defaultOptions (array): typescript-eslint merges this array with the original\n // options array and passes the merged array as the second argument to `create`. However,\n // this isn't reflected in the types, and its behavior is kind of weird (an empty `defaultOptions`\n // array will cause the provided options to be ignored). So remove it from the types\n // and fill in a default value in `createRule` instead.\n 'create' | 'defaultOptions'\n> & {\n create(this: void, context: Readonly<RuleContext<TOptions, TMessageIds, TErrorData>>): TSESLint.RuleListener;\n};\n\n/**\n * This exported alias is just to avoid an error when exporting the `rule` returned from `createRule`\n * (\"the inferred type of 'rule' cannot be named without a reference to ...\").\n */\nexport type RuleModule<TOptions, TMessageIds extends string> = ESLintUtils.RuleModule<\n TMessageIds,\n TOptions[],\n ESLintUtils.RuleListener\n>;\n\nconst baseCreateRule = ESLintUtils.RuleCreator(\n (name) => `https://www.npmjs.com/package/@ms-cloudpack/eslint-plugin#${name}`,\n);\n\n/**\n * Create a lint rule, with strongly typed data in `context.report`.\n * (This assumes all error/suggestion messages share a common data type.)\n */\nexport function createRule<TOptions, TMessageIds extends string, TErrorData>(\n params: CreateRuleOptions<TOptions, TMessageIds, TErrorData>,\n): RuleModule<TOptions, TMessageIds> {\n return baseCreateRule({\n ...params,\n defaultOptions: [],\n create: (context) => {\n const contextCopy = { ...context };\n\n // Fill in properties that were added in eslint 8.40.0\n /* eslint-disable etc/no-deprecated */\n contextCopy.cwd ??= context.getCwd?.();\n contextCopy.filename ??= context.getFilename?.();\n contextCopy.physicalFilename ??= context.getPhysicalFilename?.();\n contextCopy.sourceCode ??= context.getSourceCode?.();\n /* eslint-enable etc/no-deprecated */\n\n return params.create(contextCopy as RuleContext<TOptions, TMessageIds, TErrorData>);\n },\n });\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ms-cloudpack/eslint-plugin",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.14",
|
|
4
4
|
"description": "A set of ESLint rules for Cloudpack",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"@rnx-kit/eslint-plugin": "^0.6.0",
|
|
18
18
|
"@typescript-eslint/eslint-plugin": "^6.19.1",
|
|
19
19
|
"@typescript-eslint/parser": "^6.19.1",
|
|
20
|
+
"@typescript-eslint/utils": "^6.19.1",
|
|
20
21
|
"resolve": "^1.22.0"
|
|
21
22
|
},
|
|
22
23
|
"peerDependencies": {
|
|
@@ -26,8 +27,7 @@
|
|
|
26
27
|
"@ms-cloudpack/eslint-plugin-internal": "*",
|
|
27
28
|
"@ms-cloudpack/scripts": "*",
|
|
28
29
|
"@ms-cloudpack/test-utilities": "*",
|
|
29
|
-
"@typescript-eslint/rule-tester": "^6.
|
|
30
|
-
"@typescript-eslint/utils": "^6.7.0",
|
|
30
|
+
"@typescript-eslint/rule-tester": "^6.19.1",
|
|
31
31
|
"@types/resolve": "^1.20.2"
|
|
32
32
|
},
|
|
33
33
|
"files": [
|