@prairielearn/eslint-plugin 2.0.9 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.mocharc.cjs +3 -0
- package/CHANGELOG.md +6 -0
- package/dist/index.d.ts +3 -12
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/rules/aws-client-mandatory-config.d.ts +2 -6
- package/dist/rules/aws-client-mandatory-config.js +15 -3
- package/dist/rules/aws-client-mandatory-config.js.map +1 -1
- package/dist/rules/aws-client-shared-config.d.ts +2 -6
- package/dist/rules/aws-client-shared-config.js +30 -7
- package/dist/rules/aws-client-shared-config.js.map +1 -1
- package/dist/rules/jsx-no-dollar-interpolation.d.ts +13 -0
- package/dist/rules/jsx-no-dollar-interpolation.js +60 -0
- package/dist/rules/jsx-no-dollar-interpolation.js.map +1 -0
- package/dist/tests/aws-client-mandatory-config.test.d.ts +1 -0
- package/dist/tests/aws-client-mandatory-config.test.js +29 -0
- package/dist/tests/aws-client-mandatory-config.test.js.map +1 -0
- package/dist/tests/aws-client-shared-config.test.d.ts +1 -0
- package/dist/tests/aws-client-shared-config.test.js +40 -0
- package/dist/tests/aws-client-shared-config.test.js.map +1 -0
- package/dist/tests/jsx-no-dollar-interpolation.test.d.ts +1 -0
- package/dist/tests/jsx-no-dollar-interpolation.test.js +58 -0
- package/dist/tests/jsx-no-dollar-interpolation.test.js.map +1 -0
- package/package.json +24 -2
- package/src/index.ts +2 -0
- package/src/rules/aws-client-mandatory-config.ts +19 -6
- package/src/rules/aws-client-shared-config.ts +37 -10
- package/src/rules/jsx-no-dollar-interpolation.ts +61 -0
- package/src/tests/aws-client-mandatory-config.test.ts +30 -0
- package/src/tests/aws-client-shared-config.test.ts +41 -0
- package/src/tests/jsx-no-dollar-interpolation.test.ts +59 -0
package/.mocharc.cjs
ADDED
package/CHANGELOG.md
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,14 +1,5 @@
|
|
|
1
1
|
export declare const rules: {
|
|
2
|
-
'aws-client-mandatory-config':
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
NewExpression(node: any): void;
|
|
6
|
-
};
|
|
7
|
-
};
|
|
8
|
-
'aws-client-shared-config': {
|
|
9
|
-
create(context: any): {
|
|
10
|
-
ImportDeclaration(node: any): void;
|
|
11
|
-
NewExpression(node: any): void;
|
|
12
|
-
};
|
|
13
|
-
};
|
|
2
|
+
'aws-client-mandatory-config': import("@typescript-eslint/utils/ts-eslint").RuleModule<"missingConfig", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
3
|
+
'aws-client-shared-config': import("@typescript-eslint/utils/ts-eslint").RuleModule<"improperConfig" | "unknownConfig", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
4
|
+
'jsx-no-dollar-interpolation': import("@typescript-eslint/utils/ts-eslint").RuleModule<"dollarInterpolationNotAllowed", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
14
5
|
};
|
package/dist/index.js
CHANGED
|
@@ -3,8 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.rules = void 0;
|
|
4
4
|
const aws_client_mandatory_config_js_1 = require("./rules/aws-client-mandatory-config.js");
|
|
5
5
|
const aws_client_shared_config_js_1 = require("./rules/aws-client-shared-config.js");
|
|
6
|
+
const jsx_no_dollar_interpolation_js_1 = require("./rules/jsx-no-dollar-interpolation.js");
|
|
6
7
|
exports.rules = {
|
|
7
8
|
'aws-client-mandatory-config': aws_client_mandatory_config_js_1.default,
|
|
8
9
|
'aws-client-shared-config': aws_client_shared_config_js_1.default,
|
|
10
|
+
'jsx-no-dollar-interpolation': jsx_no_dollar_interpolation_js_1.default,
|
|
9
11
|
};
|
|
10
12
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,2FAA8E;AAC9E,qFAAwE;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,2FAA8E;AAC9E,qFAAwE;AACxE,2FAA8E;AAEjE,QAAA,KAAK,GAAG;IACnB,6BAA6B,EAAE,wCAAwB;IACvD,0BAA0B,EAAE,qCAAqB;IACjD,6BAA6B,EAAE,wCAAwB;CACxD,CAAC","sourcesContent":["import awsClientMandatoryConfig from './rules/aws-client-mandatory-config.js';\nimport awsClientSharedConfig from './rules/aws-client-shared-config.js';\nimport jsxNoDollarInterpolation from './rules/jsx-no-dollar-interpolation.js';\n\nexport const rules = {\n 'aws-client-mandatory-config': awsClientMandatoryConfig,\n 'aws-client-shared-config': awsClientSharedConfig,\n 'jsx-no-dollar-interpolation': jsxNoDollarInterpolation,\n};\n"]}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
1
2
|
/**
|
|
2
3
|
* This rule enforces that we always explicitly provide a config to AWS clients.
|
|
3
4
|
* This helps ensure that we always construct a client with a specific region.
|
|
@@ -5,10 +6,5 @@
|
|
|
5
6
|
* This rules works in tandem with `aws-client-shared-config` to ensure that
|
|
6
7
|
* we're properly configuring AWS SDK clients.
|
|
7
8
|
*/
|
|
8
|
-
declare const _default:
|
|
9
|
-
create(context: any): {
|
|
10
|
-
ImportDeclaration(node: any): void;
|
|
11
|
-
NewExpression(node: any): void;
|
|
12
|
-
};
|
|
13
|
-
};
|
|
9
|
+
declare const _default: ESLintUtils.RuleModule<"missingConfig", [], unknown, ESLintUtils.RuleListener>;
|
|
14
10
|
export default _default;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
3
4
|
const utils_js_1 = require("../utils.js");
|
|
4
5
|
/**
|
|
5
6
|
* This rule enforces that we always explicitly provide a config to AWS clients.
|
|
@@ -8,7 +9,15 @@ const utils_js_1 = require("../utils.js");
|
|
|
8
9
|
* This rules works in tandem with `aws-client-shared-config` to ensure that
|
|
9
10
|
* we're properly configuring AWS SDK clients.
|
|
10
11
|
*/
|
|
11
|
-
exports.default = {
|
|
12
|
+
exports.default = utils_1.ESLintUtils.RuleCreator.withoutDocs({
|
|
13
|
+
meta: {
|
|
14
|
+
type: 'problem',
|
|
15
|
+
messages: {
|
|
16
|
+
missingConfig: '{{clientName}} must be constructed with a config object.',
|
|
17
|
+
},
|
|
18
|
+
schema: [],
|
|
19
|
+
},
|
|
20
|
+
defaultOptions: [],
|
|
12
21
|
create(context) {
|
|
13
22
|
const awsClientImports = new Set();
|
|
14
23
|
return {
|
|
@@ -24,7 +33,10 @@ exports.default = {
|
|
|
24
33
|
if (node.arguments.length === 0) {
|
|
25
34
|
context.report({
|
|
26
35
|
node,
|
|
27
|
-
|
|
36
|
+
messageId: 'missingConfig',
|
|
37
|
+
data: {
|
|
38
|
+
clientName: node.callee.name,
|
|
39
|
+
},
|
|
28
40
|
});
|
|
29
41
|
return;
|
|
30
42
|
}
|
|
@@ -32,5 +44,5 @@ exports.default = {
|
|
|
32
44
|
},
|
|
33
45
|
};
|
|
34
46
|
},
|
|
35
|
-
};
|
|
47
|
+
});
|
|
36
48
|
//# sourceMappingURL=aws-client-mandatory-config.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aws-client-mandatory-config.js","sourceRoot":"","sources":["../../src/rules/aws-client-mandatory-config.ts"],"names":[],"mappings":";;AAAA,0CAAqE;AAErE;;;;;;GAMG;AACH,kBAAe;
|
|
1
|
+
{"version":3,"file":"aws-client-mandatory-config.js","sourceRoot":"","sources":["../../src/rules/aws-client-mandatory-config.ts"],"names":[],"mappings":";;AAAA,oDAAuD;AAEvD,0CAAqE;AAErE;;;;;;GAMG;AACH,kBAAe,mBAAW,CAAC,WAAW,CAAC,WAAW,CAAC;IACjD,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,QAAQ,EAAE;YACR,aAAa,EAAE,0DAA0D;SAC1E;QACD,MAAM,EAAE,EAAE;KACX;IACD,cAAc,EAAE,EAAE;IAClB,MAAM,CAAC,OAAO;QACZ,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAAE,CAAC;QAEnC,OAAO;YACL,iCAAiC;YACjC,iBAAiB,CAAC,IAAI;gBACpB,MAAM,WAAW,GAAG,IAAA,iDAAsC,EAAC,IAAI,CAAC,CAAC;gBACjE,WAAW,CAAC,OAAO,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,gBAAgB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC;YACxE,CAAC;YACD,aAAa,CAAC,IAAI;gBAChB,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,YAAY,IAAI,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;oBAChF,gEAAgE;oBAChE,uDAAuD;oBACvD,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;wBAChC,OAAO,CAAC,MAAM,CAAC;4BACb,IAAI;4BACJ,SAAS,EAAE,eAAe;4BAC1B,IAAI,EAAE;gCACJ,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI;6BAC7B;yBACF,CAAC,CAAC;wBACH,OAAO;oBACT,CAAC;gBACH,CAAC;YACH,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC,CAAC","sourcesContent":["import { ESLintUtils } from '@typescript-eslint/utils';\n\nimport { getAwsClientNamesFromImportDeclaration } from '../utils.js';\n\n/**\n * This rule enforces that we always explicitly provide a config to AWS clients.\n * This helps ensure that we always construct a client with a specific region.\n *\n * This rules works in tandem with `aws-client-shared-config` to ensure that\n * we're properly configuring AWS SDK clients.\n */\nexport default ESLintUtils.RuleCreator.withoutDocs({\n meta: {\n type: 'problem',\n messages: {\n missingConfig: '{{clientName}} must be constructed with a config object.',\n },\n schema: [],\n },\n defaultOptions: [],\n create(context) {\n const awsClientImports = new Set();\n\n return {\n // Handle `import ...` statements\n ImportDeclaration(node) {\n const clientNames = getAwsClientNamesFromImportDeclaration(node);\n clientNames.forEach((clientName) => awsClientImports.add(clientName));\n },\n NewExpression(node) {\n if (node.callee.type === 'Identifier' && awsClientImports.has(node.callee.name)) {\n // We're constructing an AWS client. Ensure that the call has at\n // least one argument corresponding to a config object.\n if (node.arguments.length === 0) {\n context.report({\n node,\n messageId: 'missingConfig',\n data: {\n clientName: node.callee.name,\n },\n });\n return;\n }\n }\n },\n };\n },\n});\n"]}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
1
2
|
/**
|
|
2
3
|
* This ESLint rules enforces that we always provide a "shared" config to AWS
|
|
3
4
|
* clients.
|
|
@@ -16,10 +17,5 @@
|
|
|
16
17
|
* This rules works in tandem with `aws-client-mandatory-config` to ensure that
|
|
17
18
|
* we're properly configuring AWS SDK clients.
|
|
18
19
|
*/
|
|
19
|
-
declare const _default:
|
|
20
|
-
create(context: any): {
|
|
21
|
-
ImportDeclaration(node: any): void;
|
|
22
|
-
NewExpression(node: any): void;
|
|
23
|
-
};
|
|
24
|
-
};
|
|
20
|
+
declare const _default: ESLintUtils.RuleModule<"improperConfig" | "unknownConfig", [], unknown, ESLintUtils.RuleListener>;
|
|
25
21
|
export default _default;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
3
4
|
const utils_js_1 = require("../utils.js");
|
|
4
5
|
/**
|
|
5
6
|
* This ESLint rules enforces that we always provide a "shared" config to AWS
|
|
@@ -19,7 +20,16 @@ const utils_js_1 = require("../utils.js");
|
|
|
19
20
|
* This rules works in tandem with `aws-client-mandatory-config` to ensure that
|
|
20
21
|
* we're properly configuring AWS SDK clients.
|
|
21
22
|
*/
|
|
22
|
-
exports.default = {
|
|
23
|
+
exports.default = utils_1.ESLintUtils.RuleCreator.withoutDocs({
|
|
24
|
+
meta: {
|
|
25
|
+
type: 'problem',
|
|
26
|
+
messages: {
|
|
27
|
+
improperConfig: 'Config for {{clientName}} must be obtained by calling {{desiredConfigFunctionName}}().',
|
|
28
|
+
unknownConfig: 'Unknown config provided to AWS client.',
|
|
29
|
+
},
|
|
30
|
+
schema: [],
|
|
31
|
+
},
|
|
32
|
+
defaultOptions: [],
|
|
23
33
|
create(context) {
|
|
24
34
|
const awsClientImports = new Set();
|
|
25
35
|
return {
|
|
@@ -46,17 +56,26 @@ exports.default = {
|
|
|
46
56
|
if (configArgument.type !== 'CallExpression') {
|
|
47
57
|
context.report({
|
|
48
58
|
node,
|
|
49
|
-
|
|
59
|
+
messageId: 'improperConfig',
|
|
60
|
+
data: {
|
|
61
|
+
clientName: node.callee.name,
|
|
62
|
+
desiredConfigFunctionName,
|
|
63
|
+
},
|
|
50
64
|
});
|
|
51
65
|
return;
|
|
52
66
|
}
|
|
53
67
|
// Handle member calls to the function.
|
|
54
|
-
if (configArgument.callee.type === 'MemberExpression'
|
|
68
|
+
if (configArgument.callee.type === 'MemberExpression' &&
|
|
69
|
+
configArgument.callee.property.type === 'Identifier') {
|
|
55
70
|
const functionName = configArgument.callee.property.name;
|
|
56
71
|
if (functionName !== desiredConfigFunctionName) {
|
|
57
72
|
context.report({
|
|
58
73
|
node,
|
|
59
|
-
|
|
74
|
+
messageId: 'improperConfig',
|
|
75
|
+
data: {
|
|
76
|
+
clientName: node.callee.name,
|
|
77
|
+
desiredConfigFunctionName,
|
|
78
|
+
},
|
|
60
79
|
});
|
|
61
80
|
}
|
|
62
81
|
return;
|
|
@@ -66,18 +85,22 @@ exports.default = {
|
|
|
66
85
|
if (functionName !== desiredConfigFunctionName) {
|
|
67
86
|
context.report({
|
|
68
87
|
node,
|
|
69
|
-
|
|
88
|
+
messageId: 'improperConfig',
|
|
89
|
+
data: {
|
|
90
|
+
clientName: node.callee.name,
|
|
91
|
+
desiredConfigFunctionName,
|
|
92
|
+
},
|
|
70
93
|
});
|
|
71
94
|
}
|
|
72
95
|
return;
|
|
73
96
|
}
|
|
74
97
|
context.report({
|
|
75
98
|
node,
|
|
76
|
-
|
|
99
|
+
messageId: 'unknownConfig',
|
|
77
100
|
});
|
|
78
101
|
}
|
|
79
102
|
},
|
|
80
103
|
};
|
|
81
104
|
},
|
|
82
|
-
};
|
|
105
|
+
});
|
|
83
106
|
//# sourceMappingURL=aws-client-shared-config.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aws-client-shared-config.js","sourceRoot":"","sources":["../../src/rules/aws-client-shared-config.ts"],"names":[],"mappings":";;AAAA,0CAAqE;AAErE;;;;;;;;;;;;;;;;;GAiBG;AACH,kBAAe;
|
|
1
|
+
{"version":3,"file":"aws-client-shared-config.js","sourceRoot":"","sources":["../../src/rules/aws-client-shared-config.ts"],"names":[],"mappings":"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","sourcesContent":["import { ESLintUtils } from '@typescript-eslint/utils';\n\nimport { getAwsClientNamesFromImportDeclaration } from '../utils.js';\n\n/**\n * This ESLint rules enforces that we always provide a \"shared\" config to AWS\n * clients.\n *\n * This rule is extremely opinionated: it checks that the first argument to an\n * AWS client constructor consists of a function call to a function named\n * `makeAwsClientConfig()` (or `makeS3ClientConfig()` for S3 clients). This\n * is our convention to ensure that all clients will reuse credential providers,\n * which is important for ensuring that we don't overload IMDS with requests\n * for credentials if we construct a lot of clients in rapid succession.\n *\n * This is perhaps less than ideal, but the risk of misconfiguring a client is\n * high enough that we err towards being extremely prescriptive about how we\n * configure them.\n *\n * This rules works in tandem with `aws-client-mandatory-config` to ensure that\n * we're properly configuring AWS SDK clients.\n */\nexport default ESLintUtils.RuleCreator.withoutDocs({\n meta: {\n type: 'problem',\n messages: {\n improperConfig:\n 'Config for {{clientName}} must be obtained by calling {{desiredConfigFunctionName}}().',\n unknownConfig: 'Unknown config provided to AWS client.',\n },\n schema: [],\n },\n defaultOptions: [],\n create(context) {\n const awsClientImports = new Set<string>();\n\n return {\n // Handle `import ...` statements\n ImportDeclaration(node) {\n const clientNames = getAwsClientNamesFromImportDeclaration(node);\n clientNames.forEach((clientName) => awsClientImports.add(clientName));\n },\n NewExpression(node) {\n if (node.callee.type === 'Identifier' && awsClientImports.has(node.callee.name)) {\n // We're constructing an AWS client. Ensure that the first argument\n // comes from one of our config providers.\n\n if (node.arguments.length === 0) {\n // There is no argument to check. If the `aws-client-mandatory-config`\n // rule is enabled, it will catch this case.\n return;\n }\n\n let desiredConfigFunctionName = 'makeAwsClientConfig';\n\n // Special-case: S3 client.\n if (node.callee.name === 'S3Client' || node.callee.name === 'S3') {\n desiredConfigFunctionName = 'makeS3ClientConfig';\n }\n\n const configArgument = node.arguments[0];\n if (configArgument.type !== 'CallExpression') {\n context.report({\n node,\n messageId: 'improperConfig',\n data: {\n clientName: node.callee.name,\n desiredConfigFunctionName,\n },\n });\n return;\n }\n\n // Handle member calls to the function.\n if (\n configArgument.callee.type === 'MemberExpression' &&\n configArgument.callee.property.type === 'Identifier'\n ) {\n const functionName = configArgument.callee.property.name;\n if (functionName !== desiredConfigFunctionName) {\n context.report({\n node,\n messageId: 'improperConfig',\n data: {\n clientName: node.callee.name,\n desiredConfigFunctionName,\n },\n });\n }\n return;\n }\n\n if (configArgument.callee.type === 'Identifier') {\n const functionName = configArgument.callee.name;\n if (functionName !== desiredConfigFunctionName) {\n context.report({\n node,\n messageId: 'improperConfig',\n data: {\n clientName: node.callee.name,\n desiredConfigFunctionName,\n },\n });\n }\n return;\n }\n\n context.report({\n node,\n messageId: 'unknownConfig',\n });\n }\n },\n };\n },\n});\n"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
+
/**
|
|
3
|
+
* This rule will report things that look like template string interpolations
|
|
4
|
+
* that were improperly converted to JSX. For example, the following code will
|
|
5
|
+
* trigger an error:
|
|
6
|
+
*
|
|
7
|
+
* ```tsx
|
|
8
|
+
* const a = <div>${message}</div>;
|
|
9
|
+
* const b = <div>$ {message}</div>;
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
declare const _default: ESLintUtils.RuleModule<"dollarInterpolationNotAllowed", [], unknown, ESLintUtils.RuleListener>;
|
|
13
|
+
export default _default;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
4
|
+
/**
|
|
5
|
+
* This rule will report things that look like template string interpolations
|
|
6
|
+
* that were improperly converted to JSX. For example, the following code will
|
|
7
|
+
* trigger an error:
|
|
8
|
+
*
|
|
9
|
+
* ```tsx
|
|
10
|
+
* const a = <div>${message}</div>;
|
|
11
|
+
* const b = <div>$ {message}</div>;
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
exports.default = utils_1.ESLintUtils.RuleCreator.withoutDocs({
|
|
15
|
+
meta: {
|
|
16
|
+
type: 'problem',
|
|
17
|
+
messages: {
|
|
18
|
+
dollarInterpolationNotAllowed: 'Interpolation with a dollar sign is not allowed in JSX.',
|
|
19
|
+
},
|
|
20
|
+
schema: [],
|
|
21
|
+
},
|
|
22
|
+
defaultOptions: [],
|
|
23
|
+
create(context) {
|
|
24
|
+
return {
|
|
25
|
+
JSXElement(node) {
|
|
26
|
+
node.children.forEach((child, index) => {
|
|
27
|
+
// Skip the first child since it can't be preceded by a JSXText node.
|
|
28
|
+
if (index === 0)
|
|
29
|
+
return;
|
|
30
|
+
// Skip anything that's not a JSXExpressionContainer.
|
|
31
|
+
if (child.type !== 'JSXExpressionContainer')
|
|
32
|
+
return;
|
|
33
|
+
const previousChild = node.children[index - 1];
|
|
34
|
+
// Skip nodes that aren't preceded by a JSXText node.
|
|
35
|
+
if (previousChild.type !== 'JSXText')
|
|
36
|
+
return;
|
|
37
|
+
// Skip nodes that aren't preceded by a dollar sign.
|
|
38
|
+
if (!previousChild.value.trimEnd().endsWith('$'))
|
|
39
|
+
return;
|
|
40
|
+
// Determine the range of characters that should be reported. We
|
|
41
|
+
// include the dollar sign, any following whitespace, and the
|
|
42
|
+
// expression container.
|
|
43
|
+
const start = context.sourceCode.getIndexFromLoc(previousChild.loc.start);
|
|
44
|
+
const lastIndex = previousChild.value.lastIndexOf('$');
|
|
45
|
+
const dollarStart = start + lastIndex;
|
|
46
|
+
const dollarStartLoc = context.sourceCode.getLocFromIndex(dollarStart);
|
|
47
|
+
context.report({
|
|
48
|
+
node,
|
|
49
|
+
loc: {
|
|
50
|
+
start: dollarStartLoc,
|
|
51
|
+
end: child.loc.end,
|
|
52
|
+
},
|
|
53
|
+
messageId: 'dollarInterpolationNotAllowed',
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
//# sourceMappingURL=jsx-no-dollar-interpolation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsx-no-dollar-interpolation.js","sourceRoot":"","sources":["../../src/rules/jsx-no-dollar-interpolation.ts"],"names":[],"mappings":";;AAAA,oDAAuD;AAEvD;;;;;;;;;GASG;AACH,kBAAe,mBAAW,CAAC,WAAW,CAAC,WAAW,CAAC;IACjD,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,QAAQ,EAAE;YACR,6BAA6B,EAAE,yDAAyD;SACzF;QACD,MAAM,EAAE,EAAE;KACX;IACD,cAAc,EAAE,EAAE;IAElB,MAAM,CAAC,OAAO;QACZ,OAAO;YACL,UAAU,CAAC,IAAI;gBACb,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;oBACrC,qEAAqE;oBACrE,IAAI,KAAK,KAAK,CAAC;wBAAE,OAAO;oBAExB,qDAAqD;oBACrD,IAAI,KAAK,CAAC,IAAI,KAAK,wBAAwB;wBAAE,OAAO;oBAEpD,MAAM,aAAa,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;oBAE/C,qDAAqD;oBACrD,IAAI,aAAa,CAAC,IAAI,KAAK,SAAS;wBAAE,OAAO;oBAE7C,oDAAoD;oBACpD,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC;wBAAE,OAAO;oBAEzD,gEAAgE;oBAChE,6DAA6D;oBAC7D,wBAAwB;oBACxB,MAAM,KAAK,GAAG,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;oBAC1E,MAAM,SAAS,GAAG,aAAa,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;oBACvD,MAAM,WAAW,GAAG,KAAK,GAAG,SAAS,CAAC;oBACtC,MAAM,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,WAAW,CAAC,CAAC;oBAEvE,OAAO,CAAC,MAAM,CAAC;wBACb,IAAI;wBACJ,GAAG,EAAE;4BACH,KAAK,EAAE,cAAc;4BACrB,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG;yBACnB;wBACD,SAAS,EAAE,+BAA+B;qBAC3C,CAAC,CAAC;gBACL,CAAC,CAAC,CAAC;YACL,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC,CAAC","sourcesContent":["import { ESLintUtils } from '@typescript-eslint/utils';\n\n/**\n * This rule will report things that look like template string interpolations\n * that were improperly converted to JSX. For example, the following code will\n * trigger an error:\n *\n * ```tsx\n * const a = <div>${message}</div>;\n * const b = <div>$ {message}</div>;\n * ```\n */\nexport default ESLintUtils.RuleCreator.withoutDocs({\n meta: {\n type: 'problem',\n messages: {\n dollarInterpolationNotAllowed: 'Interpolation with a dollar sign is not allowed in JSX.',\n },\n schema: [],\n },\n defaultOptions: [],\n\n create(context) {\n return {\n JSXElement(node) {\n node.children.forEach((child, index) => {\n // Skip the first child since it can't be preceded by a JSXText node.\n if (index === 0) return;\n\n // Skip anything that's not a JSXExpressionContainer.\n if (child.type !== 'JSXExpressionContainer') return;\n\n const previousChild = node.children[index - 1];\n\n // Skip nodes that aren't preceded by a JSXText node.\n if (previousChild.type !== 'JSXText') return;\n\n // Skip nodes that aren't preceded by a dollar sign.\n if (!previousChild.value.trimEnd().endsWith('$')) return;\n\n // Determine the range of characters that should be reported. We\n // include the dollar sign, any following whitespace, and the\n // expression container.\n const start = context.sourceCode.getIndexFromLoc(previousChild.loc.start);\n const lastIndex = previousChild.value.lastIndexOf('$');\n const dollarStart = start + lastIndex;\n const dollarStartLoc = context.sourceCode.getLocFromIndex(dollarStart);\n\n context.report({\n node,\n loc: {\n start: dollarStartLoc,\n end: child.loc.end,\n },\n messageId: 'dollarInterpolationNotAllowed',\n });\n });\n },\n };\n },\n});\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const rule_tester_1 = require("@typescript-eslint/rule-tester");
|
|
4
|
+
const aws_client_mandatory_config_1 = require("../rules/aws-client-mandatory-config");
|
|
5
|
+
rule_tester_1.RuleTester.afterAll = after;
|
|
6
|
+
const ruleTester = new rule_tester_1.RuleTester();
|
|
7
|
+
ruleTester.run('aws-client-mandatory-config', aws_client_mandatory_config_1.default, {
|
|
8
|
+
valid: [
|
|
9
|
+
{
|
|
10
|
+
code: "import { S3 } from '@aws-sdk/client-s3'; new S3({ region: 'us-east-2' })",
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
code: "import { S3Client } from '@aws-sdk/client-s3'; new S3Client({ region: 'us-east-2' })",
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
code: "import { EC2 } from '@aws-sdk/client-ec2'; new EC2({ region: 'us-east-2' })",
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
code: "import { EC2Client } from '@aws-sdk/client-ec2'; new EC2Client({ region: 'us-east-2' })",
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
invalid: [
|
|
23
|
+
{
|
|
24
|
+
code: "import { S3 } from '@aws-sdk/client-s3'; new S3()",
|
|
25
|
+
errors: [{ messageId: 'missingConfig' }],
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
});
|
|
29
|
+
//# sourceMappingURL=aws-client-mandatory-config.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aws-client-mandatory-config.test.js","sourceRoot":"","sources":["../../src/tests/aws-client-mandatory-config.test.ts"],"names":[],"mappings":";;AAAA,gEAA4D;AAE5D,sFAAwD;AAExD,wBAAU,CAAC,QAAQ,GAAG,KAAK,CAAC;AAE5B,MAAM,UAAU,GAAG,IAAI,wBAAU,EAAE,CAAC;AAEpC,UAAU,CAAC,GAAG,CAAC,6BAA6B,EAAE,qCAAI,EAAE;IAClD,KAAK,EAAE;QACL;YACE,IAAI,EAAE,0EAA0E;SACjF;QACD;YACE,IAAI,EAAE,sFAAsF;SAC7F;QACD;YACE,IAAI,EAAE,6EAA6E;SACpF;QACD;YACE,IAAI,EAAE,yFAAyF;SAChG;KACF;IACD,OAAO,EAAE;QACP;YACE,IAAI,EAAE,mDAAmD;YACzD,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,eAAe,EAAE,CAAC;SACzC;KACF;CACF,CAAC,CAAC","sourcesContent":["import { RuleTester } from '@typescript-eslint/rule-tester';\n\nimport rule from '../rules/aws-client-mandatory-config';\n\nRuleTester.afterAll = after;\n\nconst ruleTester = new RuleTester();\n\nruleTester.run('aws-client-mandatory-config', rule, {\n valid: [\n {\n code: \"import { S3 } from '@aws-sdk/client-s3'; new S3({ region: 'us-east-2' })\",\n },\n {\n code: \"import { S3Client } from '@aws-sdk/client-s3'; new S3Client({ region: 'us-east-2' })\",\n },\n {\n code: \"import { EC2 } from '@aws-sdk/client-ec2'; new EC2({ region: 'us-east-2' })\",\n },\n {\n code: \"import { EC2Client } from '@aws-sdk/client-ec2'; new EC2Client({ region: 'us-east-2' })\",\n },\n ],\n invalid: [\n {\n code: \"import { S3 } from '@aws-sdk/client-s3'; new S3()\",\n errors: [{ messageId: 'missingConfig' }],\n },\n ],\n});\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const rule_tester_1 = require("@typescript-eslint/rule-tester");
|
|
4
|
+
const aws_client_shared_config_1 = require("../rules/aws-client-shared-config");
|
|
5
|
+
rule_tester_1.RuleTester.afterAll = after;
|
|
6
|
+
const ruleTester = new rule_tester_1.RuleTester();
|
|
7
|
+
ruleTester.run('aws-client-shared-config', aws_client_shared_config_1.default, {
|
|
8
|
+
valid: [
|
|
9
|
+
{
|
|
10
|
+
code: "import { S3 } from '@aws-sdk/client-s3'; new S3(makeS3ClientConfig());",
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
code: "import { S3Client } from '@aws-sdk/client-s3'; new S3Client(makeS3ClientConfig());",
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
code: "import { EC2 } from '@aws-sdk/client-ec2'; new EC2(makeAwsClientConfig());",
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
code: "import { EC2Client } from '@aws-sdk/client-ec2'; new EC2Client(makeAwsClientConfig());",
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
code: "import { EC2 } from '@aws-sdk/client-ec2'; new EC2(aws.makeAwsClientConfig());",
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
invalid: [
|
|
26
|
+
{
|
|
27
|
+
code: "import { S3 } from '@aws-sdk/client-s3'; new S3({ region: 'us-east-2' });",
|
|
28
|
+
errors: [{ messageId: 'improperConfig' }],
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
code: "import { EC2 } from '@aws-sdk/client-ec2'; new EC2({ region: 'us-east-2' });",
|
|
32
|
+
errors: [{ messageId: 'improperConfig' }],
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
code: "import { S3 } from '@aws-sdk/client-s3'; new S3(wrongFunction());",
|
|
36
|
+
errors: [{ messageId: 'improperConfig' }],
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
});
|
|
40
|
+
//# sourceMappingURL=aws-client-shared-config.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aws-client-shared-config.test.js","sourceRoot":"","sources":["../../src/tests/aws-client-shared-config.test.ts"],"names":[],"mappings":";;AAAA,gEAA4D;AAE5D,gFAAqD;AAErD,wBAAU,CAAC,QAAQ,GAAG,KAAK,CAAC;AAE5B,MAAM,UAAU,GAAG,IAAI,wBAAU,EAAE,CAAC;AAEpC,UAAU,CAAC,GAAG,CAAC,0BAA0B,EAAE,kCAAI,EAAE;IAC/C,KAAK,EAAE;QACL;YACE,IAAI,EAAE,wEAAwE;SAC/E;QACD;YACE,IAAI,EAAE,oFAAoF;SAC3F;QACD;YACE,IAAI,EAAE,4EAA4E;SACnF;QACD;YACE,IAAI,EAAE,wFAAwF;SAC/F;QACD;YACE,IAAI,EAAE,gFAAgF;SACvF;KACF;IACD,OAAO,EAAE;QACP;YACE,IAAI,EAAE,2EAA2E;YACjF,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,gBAAgB,EAAE,CAAC;SAC1C;QACD;YACE,IAAI,EAAE,8EAA8E;YACpF,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,gBAAgB,EAAE,CAAC;SAC1C;QACD;YACE,IAAI,EAAE,mEAAmE;YACzE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,gBAAgB,EAAE,CAAC;SAC1C;KACF;CACF,CAAC,CAAC","sourcesContent":["import { RuleTester } from '@typescript-eslint/rule-tester';\n\nimport rule from '../rules/aws-client-shared-config';\n\nRuleTester.afterAll = after;\n\nconst ruleTester = new RuleTester();\n\nruleTester.run('aws-client-shared-config', rule, {\n valid: [\n {\n code: \"import { S3 } from '@aws-sdk/client-s3'; new S3(makeS3ClientConfig());\",\n },\n {\n code: \"import { S3Client } from '@aws-sdk/client-s3'; new S3Client(makeS3ClientConfig());\",\n },\n {\n code: \"import { EC2 } from '@aws-sdk/client-ec2'; new EC2(makeAwsClientConfig());\",\n },\n {\n code: \"import { EC2Client } from '@aws-sdk/client-ec2'; new EC2Client(makeAwsClientConfig());\",\n },\n {\n code: \"import { EC2 } from '@aws-sdk/client-ec2'; new EC2(aws.makeAwsClientConfig());\",\n },\n ],\n invalid: [\n {\n code: \"import { S3 } from '@aws-sdk/client-s3'; new S3({ region: 'us-east-2' });\",\n errors: [{ messageId: 'improperConfig' }],\n },\n {\n code: \"import { EC2 } from '@aws-sdk/client-ec2'; new EC2({ region: 'us-east-2' });\",\n errors: [{ messageId: 'improperConfig' }],\n },\n {\n code: \"import { S3 } from '@aws-sdk/client-s3'; new S3(wrongFunction());\",\n errors: [{ messageId: 'improperConfig' }],\n },\n ],\n});\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const rule_tester_1 = require("@typescript-eslint/rule-tester");
|
|
4
|
+
const jsx_no_dollar_interpolation_1 = require("../rules/jsx-no-dollar-interpolation");
|
|
5
|
+
rule_tester_1.RuleTester.afterAll = after;
|
|
6
|
+
const ruleTester = new rule_tester_1.RuleTester({
|
|
7
|
+
languageOptions: {
|
|
8
|
+
parserOptions: {
|
|
9
|
+
ecmaFeatures: {
|
|
10
|
+
jsx: true,
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
ruleTester.run('jsx-no-dollar-interpolation', jsx_no_dollar_interpolation_1.default, {
|
|
16
|
+
valid: [
|
|
17
|
+
{
|
|
18
|
+
code: '<div>hello</div>',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
code: '<div>100$</div>',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
code: '<div>$100</div>',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
code: '<div>$</div>',
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
invalid: [
|
|
31
|
+
{
|
|
32
|
+
// eslint-disable-next-line no-template-curly-in-string
|
|
33
|
+
code: '<div>${message}</div>',
|
|
34
|
+
errors: [
|
|
35
|
+
{
|
|
36
|
+
messageId: 'dollarInterpolationNotAllowed',
|
|
37
|
+
line: 1,
|
|
38
|
+
column: 6,
|
|
39
|
+
endLine: 1,
|
|
40
|
+
endColumn: 16,
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
code: '<div>$ {message}</div>',
|
|
46
|
+
errors: [
|
|
47
|
+
{
|
|
48
|
+
messageId: 'dollarInterpolationNotAllowed',
|
|
49
|
+
line: 1,
|
|
50
|
+
column: 6,
|
|
51
|
+
endLine: 1,
|
|
52
|
+
endColumn: 17,
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
});
|
|
58
|
+
//# sourceMappingURL=jsx-no-dollar-interpolation.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsx-no-dollar-interpolation.test.js","sourceRoot":"","sources":["../../src/tests/jsx-no-dollar-interpolation.test.ts"],"names":[],"mappings":";;AAAA,gEAA4D;AAE5D,sFAAwD;AAExD,wBAAU,CAAC,QAAQ,GAAG,KAAK,CAAC;AAE5B,MAAM,UAAU,GAAG,IAAI,wBAAU,CAAC;IAChC,eAAe,EAAE;QACf,aAAa,EAAE;YACb,YAAY,EAAE;gBACZ,GAAG,EAAE,IAAI;aACV;SACF;KACF;CACF,CAAC,CAAC;AAEH,UAAU,CAAC,GAAG,CAAC,6BAA6B,EAAE,qCAAI,EAAE;IAClD,KAAK,EAAE;QACL;YACE,IAAI,EAAE,kBAAkB;SACzB;QACD;YACE,IAAI,EAAE,iBAAiB;SACxB;QACD;YACE,IAAI,EAAE,iBAAiB;SACxB;QACD;YACE,IAAI,EAAE,cAAc;SACrB;KACF;IACD,OAAO,EAAE;QACP;YACE,uDAAuD;YACvD,IAAI,EAAE,uBAAuB;YAC7B,MAAM,EAAE;gBACN;oBACE,SAAS,EAAE,+BAA+B;oBAC1C,IAAI,EAAE,CAAC;oBACP,MAAM,EAAE,CAAC;oBACT,OAAO,EAAE,CAAC;oBACV,SAAS,EAAE,EAAE;iBACd;aACF;SACF;QACD;YACE,IAAI,EAAE,wBAAwB;YAC9B,MAAM,EAAE;gBACN;oBACE,SAAS,EAAE,+BAA+B;oBAC1C,IAAI,EAAE,CAAC;oBACP,MAAM,EAAE,CAAC;oBACT,OAAO,EAAE,CAAC;oBACV,SAAS,EAAE,EAAE;iBACd;aACF;SACF;KACF;CACF,CAAC,CAAC","sourcesContent":["import { RuleTester } from '@typescript-eslint/rule-tester';\n\nimport rule from '../rules/jsx-no-dollar-interpolation';\n\nRuleTester.afterAll = after;\n\nconst ruleTester = new RuleTester({\n languageOptions: {\n parserOptions: {\n ecmaFeatures: {\n jsx: true,\n },\n },\n },\n});\n\nruleTester.run('jsx-no-dollar-interpolation', rule, {\n valid: [\n {\n code: '<div>hello</div>',\n },\n {\n code: '<div>100$</div>',\n },\n {\n code: '<div>$100</div>',\n },\n {\n code: '<div>$</div>',\n },\n ],\n invalid: [\n {\n // eslint-disable-next-line no-template-curly-in-string\n code: '<div>${message}</div>',\n errors: [\n {\n messageId: 'dollarInterpolationNotAllowed',\n line: 1,\n column: 6,\n endLine: 1,\n endColumn: 16,\n },\n ],\n },\n {\n code: '<div>$ {message}</div>',\n errors: [\n {\n messageId: 'dollarInterpolationNotAllowed',\n line: 1,\n column: 6,\n endLine: 1,\n endColumn: 17,\n },\n ],\n },\n ],\n});\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prairielearn/eslint-plugin",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -9,11 +9,33 @@
|
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"build": "tsc",
|
|
12
|
-
"dev": "tsc --watch --preserveWatchOutput"
|
|
12
|
+
"dev": "tsc --watch --preserveWatchOutput",
|
|
13
|
+
"test": "c8 mocha src/**/*.test.ts"
|
|
13
14
|
},
|
|
14
15
|
"devDependencies": {
|
|
15
16
|
"@prairielearn/tsconfig": "^0.0.0",
|
|
17
|
+
"@types/chai": "^5.0.1",
|
|
18
|
+
"@types/mocha": "^10.0.10",
|
|
16
19
|
"@types/node": "^20.17.23",
|
|
20
|
+
"@typescript-eslint/rule-tester": "^8.26.0",
|
|
21
|
+
"c8": "^10.1.3",
|
|
22
|
+
"chai": "^5.2.0",
|
|
23
|
+
"mocha": "^11.1.0",
|
|
24
|
+
"tsx": "^4.19.3",
|
|
17
25
|
"typescript": "^5.8.2"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@typescript-eslint/utils": "^8.26.0"
|
|
29
|
+
},
|
|
30
|
+
"c8": {
|
|
31
|
+
"reporter": [
|
|
32
|
+
"html",
|
|
33
|
+
"text-summary",
|
|
34
|
+
"cobertura"
|
|
35
|
+
],
|
|
36
|
+
"all": true,
|
|
37
|
+
"include": [
|
|
38
|
+
"src/**"
|
|
39
|
+
]
|
|
18
40
|
}
|
|
19
41
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import awsClientMandatoryConfig from './rules/aws-client-mandatory-config.js';
|
|
2
2
|
import awsClientSharedConfig from './rules/aws-client-shared-config.js';
|
|
3
|
+
import jsxNoDollarInterpolation from './rules/jsx-no-dollar-interpolation.js';
|
|
3
4
|
|
|
4
5
|
export const rules = {
|
|
5
6
|
'aws-client-mandatory-config': awsClientMandatoryConfig,
|
|
6
7
|
'aws-client-shared-config': awsClientSharedConfig,
|
|
8
|
+
'jsx-no-dollar-interpolation': jsxNoDollarInterpolation,
|
|
7
9
|
};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
+
|
|
1
3
|
import { getAwsClientNamesFromImportDeclaration } from '../utils.js';
|
|
2
4
|
|
|
3
5
|
/**
|
|
@@ -7,24 +9,35 @@ import { getAwsClientNamesFromImportDeclaration } from '../utils.js';
|
|
|
7
9
|
* This rules works in tandem with `aws-client-shared-config` to ensure that
|
|
8
10
|
* we're properly configuring AWS SDK clients.
|
|
9
11
|
*/
|
|
10
|
-
export default {
|
|
11
|
-
|
|
12
|
+
export default ESLintUtils.RuleCreator.withoutDocs({
|
|
13
|
+
meta: {
|
|
14
|
+
type: 'problem',
|
|
15
|
+
messages: {
|
|
16
|
+
missingConfig: '{{clientName}} must be constructed with a config object.',
|
|
17
|
+
},
|
|
18
|
+
schema: [],
|
|
19
|
+
},
|
|
20
|
+
defaultOptions: [],
|
|
21
|
+
create(context) {
|
|
12
22
|
const awsClientImports = new Set();
|
|
13
23
|
|
|
14
24
|
return {
|
|
15
25
|
// Handle `import ...` statements
|
|
16
|
-
ImportDeclaration(node
|
|
26
|
+
ImportDeclaration(node) {
|
|
17
27
|
const clientNames = getAwsClientNamesFromImportDeclaration(node);
|
|
18
28
|
clientNames.forEach((clientName) => awsClientImports.add(clientName));
|
|
19
29
|
},
|
|
20
|
-
NewExpression(node
|
|
30
|
+
NewExpression(node) {
|
|
21
31
|
if (node.callee.type === 'Identifier' && awsClientImports.has(node.callee.name)) {
|
|
22
32
|
// We're constructing an AWS client. Ensure that the call has at
|
|
23
33
|
// least one argument corresponding to a config object.
|
|
24
34
|
if (node.arguments.length === 0) {
|
|
25
35
|
context.report({
|
|
26
36
|
node,
|
|
27
|
-
|
|
37
|
+
messageId: 'missingConfig',
|
|
38
|
+
data: {
|
|
39
|
+
clientName: node.callee.name,
|
|
40
|
+
},
|
|
28
41
|
});
|
|
29
42
|
return;
|
|
30
43
|
}
|
|
@@ -32,4 +45,4 @@ export default {
|
|
|
32
45
|
},
|
|
33
46
|
};
|
|
34
47
|
},
|
|
35
|
-
};
|
|
48
|
+
});
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
+
|
|
1
3
|
import { getAwsClientNamesFromImportDeclaration } from '../utils.js';
|
|
2
4
|
|
|
3
5
|
/**
|
|
@@ -18,17 +20,27 @@ import { getAwsClientNamesFromImportDeclaration } from '../utils.js';
|
|
|
18
20
|
* This rules works in tandem with `aws-client-mandatory-config` to ensure that
|
|
19
21
|
* we're properly configuring AWS SDK clients.
|
|
20
22
|
*/
|
|
21
|
-
export default {
|
|
22
|
-
|
|
23
|
+
export default ESLintUtils.RuleCreator.withoutDocs({
|
|
24
|
+
meta: {
|
|
25
|
+
type: 'problem',
|
|
26
|
+
messages: {
|
|
27
|
+
improperConfig:
|
|
28
|
+
'Config for {{clientName}} must be obtained by calling {{desiredConfigFunctionName}}().',
|
|
29
|
+
unknownConfig: 'Unknown config provided to AWS client.',
|
|
30
|
+
},
|
|
31
|
+
schema: [],
|
|
32
|
+
},
|
|
33
|
+
defaultOptions: [],
|
|
34
|
+
create(context) {
|
|
23
35
|
const awsClientImports = new Set<string>();
|
|
24
36
|
|
|
25
37
|
return {
|
|
26
38
|
// Handle `import ...` statements
|
|
27
|
-
ImportDeclaration(node
|
|
39
|
+
ImportDeclaration(node) {
|
|
28
40
|
const clientNames = getAwsClientNamesFromImportDeclaration(node);
|
|
29
41
|
clientNames.forEach((clientName) => awsClientImports.add(clientName));
|
|
30
42
|
},
|
|
31
|
-
NewExpression(node
|
|
43
|
+
NewExpression(node) {
|
|
32
44
|
if (node.callee.type === 'Identifier' && awsClientImports.has(node.callee.name)) {
|
|
33
45
|
// We're constructing an AWS client. Ensure that the first argument
|
|
34
46
|
// comes from one of our config providers.
|
|
@@ -50,18 +62,29 @@ export default {
|
|
|
50
62
|
if (configArgument.type !== 'CallExpression') {
|
|
51
63
|
context.report({
|
|
52
64
|
node,
|
|
53
|
-
|
|
65
|
+
messageId: 'improperConfig',
|
|
66
|
+
data: {
|
|
67
|
+
clientName: node.callee.name,
|
|
68
|
+
desiredConfigFunctionName,
|
|
69
|
+
},
|
|
54
70
|
});
|
|
55
71
|
return;
|
|
56
72
|
}
|
|
57
73
|
|
|
58
74
|
// Handle member calls to the function.
|
|
59
|
-
if (
|
|
75
|
+
if (
|
|
76
|
+
configArgument.callee.type === 'MemberExpression' &&
|
|
77
|
+
configArgument.callee.property.type === 'Identifier'
|
|
78
|
+
) {
|
|
60
79
|
const functionName = configArgument.callee.property.name;
|
|
61
80
|
if (functionName !== desiredConfigFunctionName) {
|
|
62
81
|
context.report({
|
|
63
82
|
node,
|
|
64
|
-
|
|
83
|
+
messageId: 'improperConfig',
|
|
84
|
+
data: {
|
|
85
|
+
clientName: node.callee.name,
|
|
86
|
+
desiredConfigFunctionName,
|
|
87
|
+
},
|
|
65
88
|
});
|
|
66
89
|
}
|
|
67
90
|
return;
|
|
@@ -72,7 +95,11 @@ export default {
|
|
|
72
95
|
if (functionName !== desiredConfigFunctionName) {
|
|
73
96
|
context.report({
|
|
74
97
|
node,
|
|
75
|
-
|
|
98
|
+
messageId: 'improperConfig',
|
|
99
|
+
data: {
|
|
100
|
+
clientName: node.callee.name,
|
|
101
|
+
desiredConfigFunctionName,
|
|
102
|
+
},
|
|
76
103
|
});
|
|
77
104
|
}
|
|
78
105
|
return;
|
|
@@ -80,10 +107,10 @@ export default {
|
|
|
80
107
|
|
|
81
108
|
context.report({
|
|
82
109
|
node,
|
|
83
|
-
|
|
110
|
+
messageId: 'unknownConfig',
|
|
84
111
|
});
|
|
85
112
|
}
|
|
86
113
|
},
|
|
87
114
|
};
|
|
88
115
|
},
|
|
89
|
-
};
|
|
116
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* This rule will report things that look like template string interpolations
|
|
5
|
+
* that were improperly converted to JSX. For example, the following code will
|
|
6
|
+
* trigger an error:
|
|
7
|
+
*
|
|
8
|
+
* ```tsx
|
|
9
|
+
* const a = <div>${message}</div>;
|
|
10
|
+
* const b = <div>$ {message}</div>;
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
export default ESLintUtils.RuleCreator.withoutDocs({
|
|
14
|
+
meta: {
|
|
15
|
+
type: 'problem',
|
|
16
|
+
messages: {
|
|
17
|
+
dollarInterpolationNotAllowed: 'Interpolation with a dollar sign is not allowed in JSX.',
|
|
18
|
+
},
|
|
19
|
+
schema: [],
|
|
20
|
+
},
|
|
21
|
+
defaultOptions: [],
|
|
22
|
+
|
|
23
|
+
create(context) {
|
|
24
|
+
return {
|
|
25
|
+
JSXElement(node) {
|
|
26
|
+
node.children.forEach((child, index) => {
|
|
27
|
+
// Skip the first child since it can't be preceded by a JSXText node.
|
|
28
|
+
if (index === 0) return;
|
|
29
|
+
|
|
30
|
+
// Skip anything that's not a JSXExpressionContainer.
|
|
31
|
+
if (child.type !== 'JSXExpressionContainer') return;
|
|
32
|
+
|
|
33
|
+
const previousChild = node.children[index - 1];
|
|
34
|
+
|
|
35
|
+
// Skip nodes that aren't preceded by a JSXText node.
|
|
36
|
+
if (previousChild.type !== 'JSXText') return;
|
|
37
|
+
|
|
38
|
+
// Skip nodes that aren't preceded by a dollar sign.
|
|
39
|
+
if (!previousChild.value.trimEnd().endsWith('$')) return;
|
|
40
|
+
|
|
41
|
+
// Determine the range of characters that should be reported. We
|
|
42
|
+
// include the dollar sign, any following whitespace, and the
|
|
43
|
+
// expression container.
|
|
44
|
+
const start = context.sourceCode.getIndexFromLoc(previousChild.loc.start);
|
|
45
|
+
const lastIndex = previousChild.value.lastIndexOf('$');
|
|
46
|
+
const dollarStart = start + lastIndex;
|
|
47
|
+
const dollarStartLoc = context.sourceCode.getLocFromIndex(dollarStart);
|
|
48
|
+
|
|
49
|
+
context.report({
|
|
50
|
+
node,
|
|
51
|
+
loc: {
|
|
52
|
+
start: dollarStartLoc,
|
|
53
|
+
end: child.loc.end,
|
|
54
|
+
},
|
|
55
|
+
messageId: 'dollarInterpolationNotAllowed',
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
},
|
|
61
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { RuleTester } from '@typescript-eslint/rule-tester';
|
|
2
|
+
|
|
3
|
+
import rule from '../rules/aws-client-mandatory-config';
|
|
4
|
+
|
|
5
|
+
RuleTester.afterAll = after;
|
|
6
|
+
|
|
7
|
+
const ruleTester = new RuleTester();
|
|
8
|
+
|
|
9
|
+
ruleTester.run('aws-client-mandatory-config', rule, {
|
|
10
|
+
valid: [
|
|
11
|
+
{
|
|
12
|
+
code: "import { S3 } from '@aws-sdk/client-s3'; new S3({ region: 'us-east-2' })",
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
code: "import { S3Client } from '@aws-sdk/client-s3'; new S3Client({ region: 'us-east-2' })",
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
code: "import { EC2 } from '@aws-sdk/client-ec2'; new EC2({ region: 'us-east-2' })",
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
code: "import { EC2Client } from '@aws-sdk/client-ec2'; new EC2Client({ region: 'us-east-2' })",
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
invalid: [
|
|
25
|
+
{
|
|
26
|
+
code: "import { S3 } from '@aws-sdk/client-s3'; new S3()",
|
|
27
|
+
errors: [{ messageId: 'missingConfig' }],
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { RuleTester } from '@typescript-eslint/rule-tester';
|
|
2
|
+
|
|
3
|
+
import rule from '../rules/aws-client-shared-config';
|
|
4
|
+
|
|
5
|
+
RuleTester.afterAll = after;
|
|
6
|
+
|
|
7
|
+
const ruleTester = new RuleTester();
|
|
8
|
+
|
|
9
|
+
ruleTester.run('aws-client-shared-config', rule, {
|
|
10
|
+
valid: [
|
|
11
|
+
{
|
|
12
|
+
code: "import { S3 } from '@aws-sdk/client-s3'; new S3(makeS3ClientConfig());",
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
code: "import { S3Client } from '@aws-sdk/client-s3'; new S3Client(makeS3ClientConfig());",
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
code: "import { EC2 } from '@aws-sdk/client-ec2'; new EC2(makeAwsClientConfig());",
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
code: "import { EC2Client } from '@aws-sdk/client-ec2'; new EC2Client(makeAwsClientConfig());",
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
code: "import { EC2 } from '@aws-sdk/client-ec2'; new EC2(aws.makeAwsClientConfig());",
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
invalid: [
|
|
28
|
+
{
|
|
29
|
+
code: "import { S3 } from '@aws-sdk/client-s3'; new S3({ region: 'us-east-2' });",
|
|
30
|
+
errors: [{ messageId: 'improperConfig' }],
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
code: "import { EC2 } from '@aws-sdk/client-ec2'; new EC2({ region: 'us-east-2' });",
|
|
34
|
+
errors: [{ messageId: 'improperConfig' }],
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
code: "import { S3 } from '@aws-sdk/client-s3'; new S3(wrongFunction());",
|
|
38
|
+
errors: [{ messageId: 'improperConfig' }],
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { RuleTester } from '@typescript-eslint/rule-tester';
|
|
2
|
+
|
|
3
|
+
import rule from '../rules/jsx-no-dollar-interpolation';
|
|
4
|
+
|
|
5
|
+
RuleTester.afterAll = after;
|
|
6
|
+
|
|
7
|
+
const ruleTester = new RuleTester({
|
|
8
|
+
languageOptions: {
|
|
9
|
+
parserOptions: {
|
|
10
|
+
ecmaFeatures: {
|
|
11
|
+
jsx: true,
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
ruleTester.run('jsx-no-dollar-interpolation', rule, {
|
|
18
|
+
valid: [
|
|
19
|
+
{
|
|
20
|
+
code: '<div>hello</div>',
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
code: '<div>100$</div>',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
code: '<div>$100</div>',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
code: '<div>$</div>',
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
invalid: [
|
|
33
|
+
{
|
|
34
|
+
// eslint-disable-next-line no-template-curly-in-string
|
|
35
|
+
code: '<div>${message}</div>',
|
|
36
|
+
errors: [
|
|
37
|
+
{
|
|
38
|
+
messageId: 'dollarInterpolationNotAllowed',
|
|
39
|
+
line: 1,
|
|
40
|
+
column: 6,
|
|
41
|
+
endLine: 1,
|
|
42
|
+
endColumn: 16,
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
code: '<div>$ {message}</div>',
|
|
48
|
+
errors: [
|
|
49
|
+
{
|
|
50
|
+
messageId: 'dollarInterpolationNotAllowed',
|
|
51
|
+
line: 1,
|
|
52
|
+
column: 6,
|
|
53
|
+
endLine: 1,
|
|
54
|
+
endColumn: 17,
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
});
|