@blumintinc/eslint-plugin-blumint 1.1.0 → 1.1.2
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.js +4 -1
- package/lib/rules/consistent-callback-naming.js +26 -17
- package/lib/rules/enforce-callable-types.js +4 -2
- package/lib/rules/enforce-identifiable-firestore-type.js +46 -5
- package/lib/rules/enforce-safe-stringify.d.ts +2 -0
- package/lib/rules/enforce-safe-stringify.js +64 -0
- package/lib/rules/extract-global-constants.js +27 -6
- package/lib/rules/global-const-style.js +39 -6
- package/lib/rules/require-dynamic-firebase-imports.js +2 -1
- package/lib/rules/require-usememo-object-literals.js +5 -0
- package/lib/utils/ASTHelpers.js +3 -0
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -32,10 +32,11 @@ const require_https_error_1 = __importDefault(require("./rules/require-https-err
|
|
|
32
32
|
const use_custom_router_1 = require("./rules/use-custom-router");
|
|
33
33
|
const require_image_overlayed_1 = __importDefault(require("./rules/require-image-overlayed"));
|
|
34
34
|
const require_usememo_object_literals_1 = require("./rules/require-usememo-object-literals");
|
|
35
|
+
const enforce_safe_stringify_1 = require("./rules/enforce-safe-stringify");
|
|
35
36
|
module.exports = {
|
|
36
37
|
meta: {
|
|
37
38
|
name: '@blumintinc/eslint-plugin-blumint',
|
|
38
|
-
version: '1.1.
|
|
39
|
+
version: '1.1.2',
|
|
39
40
|
},
|
|
40
41
|
parseOptions: {
|
|
41
42
|
ecmaVersion: 2020,
|
|
@@ -73,6 +74,7 @@ module.exports = {
|
|
|
73
74
|
'@blumintinc/blumint/use-custom-router': 'error',
|
|
74
75
|
'@blumintinc/blumint/require-image-overlayed': 'error',
|
|
75
76
|
'@blumintinc/blumint/require-usememo-object-literals': 'error',
|
|
77
|
+
'@blumintinc/blumint/enforce-safe-stringify': 'error',
|
|
76
78
|
},
|
|
77
79
|
},
|
|
78
80
|
},
|
|
@@ -106,6 +108,7 @@ module.exports = {
|
|
|
106
108
|
'use-custom-router': use_custom_router_1.useCustomRouter,
|
|
107
109
|
'require-image-overlayed': require_image_overlayed_1.default,
|
|
108
110
|
'require-usememo-object-literals': require_usememo_object_literals_1.requireUseMemoObjectLiterals,
|
|
111
|
+
'enforce-safe-stringify': enforce_safe_stringify_1.enforceStableStringify,
|
|
109
112
|
},
|
|
110
113
|
};
|
|
111
114
|
//# sourceMappingURL=index.js.map
|
|
@@ -11,28 +11,37 @@ module.exports = (0, createRule_1.createRule)({
|
|
|
11
11
|
fixable: 'code',
|
|
12
12
|
schema: [],
|
|
13
13
|
messages: {
|
|
14
|
-
callbackPropPrefix: 'Callback props must be prefixed with "on" (e.g., onClick, onChange)',
|
|
14
|
+
callbackPropPrefix: 'Callback props (function type props) must be prefixed with "on" (e.g., onClick, onChange)',
|
|
15
15
|
callbackFunctionPrefix: 'Callback functions should not use "handle" prefix, use descriptive verb phrases instead',
|
|
16
16
|
},
|
|
17
17
|
},
|
|
18
18
|
defaultOptions: [],
|
|
19
19
|
create(context) {
|
|
20
|
+
const parserServices = context.parserServices;
|
|
21
|
+
// Check if we have access to TypeScript services
|
|
22
|
+
if (!parserServices?.program || !parserServices?.esTreeNodeToTSNodeMap) {
|
|
23
|
+
throw new Error('You have to enable the `project` setting in parser options to use this rule');
|
|
24
|
+
}
|
|
25
|
+
const checker = parserServices.program.getTypeChecker();
|
|
26
|
+
function isFunctionType(node) {
|
|
27
|
+
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);
|
|
28
|
+
const type = checker.getTypeAtLocation(tsNode);
|
|
29
|
+
return type.getCallSignatures().length > 0;
|
|
30
|
+
}
|
|
20
31
|
return {
|
|
21
32
|
// Check JSX attributes for callback props
|
|
22
33
|
JSXAttribute(node) {
|
|
23
|
-
if (node.value &&
|
|
24
|
-
node.value.type === 'JSXExpressionContainer' &&
|
|
34
|
+
if (node.value?.type === 'JSXExpressionContainer' &&
|
|
25
35
|
node.value.expression.type === 'Identifier') {
|
|
26
|
-
const propName = node.name.name;
|
|
27
|
-
const valueName = node.value.expression.name;
|
|
36
|
+
const propName = node.name.type === 'JSXIdentifier' ? node.name.name : undefined;
|
|
28
37
|
// Skip React's built-in event handlers
|
|
29
|
-
if (propName
|
|
38
|
+
if (propName?.match(/^on[A-Z]/)) {
|
|
30
39
|
return;
|
|
31
40
|
}
|
|
32
|
-
// Check if
|
|
33
|
-
if (
|
|
34
|
-
|
|
35
|
-
|
|
41
|
+
// Check if the value is a function type
|
|
42
|
+
if (isFunctionType(node.value.expression) &&
|
|
43
|
+
propName &&
|
|
44
|
+
!propName.startsWith('on')) {
|
|
36
45
|
context.report({
|
|
37
46
|
node,
|
|
38
47
|
messageId: 'callbackPropPrefix',
|
|
@@ -47,8 +56,8 @@ module.exports = (0, createRule_1.createRule)({
|
|
|
47
56
|
},
|
|
48
57
|
// Check function declarations and variable declarations for callback functions
|
|
49
58
|
'FunctionDeclaration, VariableDeclarator'(node) {
|
|
50
|
-
const functionName = node.id?.name;
|
|
51
|
-
if (functionName && functionName.startsWith('handle')) {
|
|
59
|
+
const functionName = node.id?.type === 'Identifier' ? node.id.name : undefined;
|
|
60
|
+
if (functionName && functionName.startsWith('handle') && node.id) {
|
|
52
61
|
context.report({
|
|
53
62
|
node,
|
|
54
63
|
messageId: 'callbackFunctionPrefix',
|
|
@@ -61,18 +70,18 @@ module.exports = (0, createRule_1.createRule)({
|
|
|
61
70
|
});
|
|
62
71
|
}
|
|
63
72
|
},
|
|
64
|
-
// Check object
|
|
65
|
-
Property(node) {
|
|
66
|
-
if (node.
|
|
73
|
+
// Check class methods and object methods
|
|
74
|
+
'MethodDefinition, Property'(node) {
|
|
75
|
+
if (node.key.type === 'Identifier' &&
|
|
67
76
|
node.key.name &&
|
|
68
77
|
node.key.name.startsWith('handle')) {
|
|
78
|
+
const name = node.key.name;
|
|
69
79
|
context.report({
|
|
70
80
|
node: node.key,
|
|
71
81
|
messageId: 'callbackFunctionPrefix',
|
|
72
82
|
fix(fixer) {
|
|
73
83
|
// Remove 'handle' prefix and convert first character to lowercase
|
|
74
|
-
const newName =
|
|
75
|
-
node.key.name.slice(7);
|
|
84
|
+
const newName = name.slice(6).charAt(0).toLowerCase() + name.slice(7);
|
|
76
85
|
return fixer.replaceText(node.key, newName);
|
|
77
86
|
},
|
|
78
87
|
});
|
|
@@ -22,8 +22,10 @@ exports.enforceCallableTypes = (0, createRule_1.createRule)({
|
|
|
22
22
|
defaultOptions: [],
|
|
23
23
|
create(context) {
|
|
24
24
|
const filename = context.getFilename();
|
|
25
|
-
// Only apply to .f.ts files in the callable directory
|
|
26
|
-
if (!filename.endsWith('.f.ts') ||
|
|
25
|
+
// Only apply to .f.ts files in the callable directory, but ignore scripts directory
|
|
26
|
+
if (!filename.endsWith('.f.ts') ||
|
|
27
|
+
!filename.includes('/callable/') ||
|
|
28
|
+
filename.includes('/callable/scripts/')) {
|
|
27
29
|
return {};
|
|
28
30
|
}
|
|
29
31
|
let hasParamsExport = false;
|
|
@@ -64,23 +64,64 @@ exports.enforceIdentifiableFirestoreType = (0, createRule_1.createRule)({
|
|
|
64
64
|
if (node.id.name === folderName) {
|
|
65
65
|
hasExpectedType = true;
|
|
66
66
|
// Check if type extends Identifiable
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
const checkIdentifiableExtension = (type) => {
|
|
68
|
+
// Check for direct Identifiable extension
|
|
69
69
|
if (type.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
|
|
70
70
|
type.typeName.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
71
71
|
type.typeName.name === 'Identifiable') {
|
|
72
72
|
return true;
|
|
73
73
|
}
|
|
74
|
+
// Check intersection types
|
|
74
75
|
if (type.type === utils_1.AST_NODE_TYPES.TSIntersectionType) {
|
|
75
|
-
return type.types.some(
|
|
76
|
+
return type.types.some(checkIdentifiableExtension);
|
|
76
77
|
}
|
|
78
|
+
// Check generic type parameters
|
|
77
79
|
if (type.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
|
|
78
80
|
type.typeParameters?.params) {
|
|
79
|
-
return type.typeParameters.params.some(
|
|
81
|
+
return type.typeParameters.params.some(checkIdentifiableExtension);
|
|
80
82
|
}
|
|
81
83
|
return false;
|
|
82
84
|
};
|
|
83
|
-
|
|
85
|
+
// Check if type has id: string field
|
|
86
|
+
const checkIdField = (type) => {
|
|
87
|
+
// Check for id: string field in type literal
|
|
88
|
+
if (type.type === utils_1.AST_NODE_TYPES.TSTypeLiteral) {
|
|
89
|
+
return type.members.some((member) => member.type === utils_1.AST_NODE_TYPES.TSPropertySignature &&
|
|
90
|
+
member.key.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
91
|
+
member.key.name === 'id' &&
|
|
92
|
+
member.typeAnnotation?.typeAnnotation.type === utils_1.AST_NODE_TYPES.TSStringKeyword);
|
|
93
|
+
}
|
|
94
|
+
// Check intersection types
|
|
95
|
+
if (type.type === utils_1.AST_NODE_TYPES.TSIntersectionType) {
|
|
96
|
+
return type.types.some(checkIdField);
|
|
97
|
+
}
|
|
98
|
+
return false;
|
|
99
|
+
};
|
|
100
|
+
// Check if type is wrapped in a utility type
|
|
101
|
+
const isUtilityType = (type) => {
|
|
102
|
+
return (type.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
|
|
103
|
+
type.typeName.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
104
|
+
type.typeName.name === 'Resolve');
|
|
105
|
+
};
|
|
106
|
+
// Recursively check the type and its parameters
|
|
107
|
+
const checkType = (type) => {
|
|
108
|
+
// Check if type extends Identifiable
|
|
109
|
+
if (checkIdentifiableExtension(type)) {
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
// Check if type has id: string field (only for utility types)
|
|
113
|
+
if (isUtilityType(type) && checkIdField(type.typeParameters.params[0])) {
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
// Check if type is wrapped in a utility type
|
|
117
|
+
if (type.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
|
|
118
|
+
type.typeParameters?.params?.[0]) {
|
|
119
|
+
return checkType(type.typeParameters.params[0]);
|
|
120
|
+
}
|
|
121
|
+
// For direct type definitions, require extending Identifiable
|
|
122
|
+
return false;
|
|
123
|
+
};
|
|
124
|
+
typeHasIdentifiable = checkType(node.typeAnnotation);
|
|
84
125
|
}
|
|
85
126
|
},
|
|
86
127
|
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.enforceStableStringify = void 0;
|
|
4
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
5
|
+
const createRule_1 = require("../utils/createRule");
|
|
6
|
+
exports.enforceStableStringify = (0, createRule_1.createRule)({
|
|
7
|
+
name: 'enforce-safe-stringify',
|
|
8
|
+
meta: {
|
|
9
|
+
type: 'problem',
|
|
10
|
+
docs: {
|
|
11
|
+
description: 'Enforce using safe-stable-stringify instead of JSON.stringify',
|
|
12
|
+
recommended: 'error',
|
|
13
|
+
},
|
|
14
|
+
fixable: 'code',
|
|
15
|
+
schema: [],
|
|
16
|
+
messages: {
|
|
17
|
+
useStableStringify: 'Use safe-stable-stringify instead of JSON.stringify for safer serialization',
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
defaultOptions: [],
|
|
21
|
+
create(context) {
|
|
22
|
+
let hasStringifyImport = false;
|
|
23
|
+
return {
|
|
24
|
+
ImportDeclaration(node) {
|
|
25
|
+
if (node.source.value === 'safe-stable-stringify' &&
|
|
26
|
+
node.specifiers.some((specifier) => specifier.type === utils_1.AST_NODE_TYPES.ImportDefaultSpecifier &&
|
|
27
|
+
specifier.local.name === 'stringify')) {
|
|
28
|
+
hasStringifyImport = true;
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
MemberExpression(node) {
|
|
32
|
+
if (node.object.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
33
|
+
node.object.name === 'JSON' &&
|
|
34
|
+
node.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
35
|
+
node.property.name === 'stringify') {
|
|
36
|
+
context.report({
|
|
37
|
+
node,
|
|
38
|
+
messageId: 'useStableStringify',
|
|
39
|
+
fix(fixer) {
|
|
40
|
+
const fixes = [];
|
|
41
|
+
// Add import if not present
|
|
42
|
+
if (!hasStringifyImport) {
|
|
43
|
+
const program = context.getSourceCode().ast;
|
|
44
|
+
const firstImport = program.body.find((node) => node.type === utils_1.AST_NODE_TYPES.ImportDeclaration);
|
|
45
|
+
const importStatement = "import stringify from 'safe-stable-stringify';\n";
|
|
46
|
+
if (firstImport) {
|
|
47
|
+
fixes.push(fixer.insertTextBefore(firstImport, importStatement));
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
fixes.push(fixer.insertTextBefore(program.body[0], importStatement));
|
|
51
|
+
}
|
|
52
|
+
hasStringifyImport = true;
|
|
53
|
+
}
|
|
54
|
+
// Replace JSON.stringify with stringify
|
|
55
|
+
fixes.push(fixer.replaceText(node, 'stringify'));
|
|
56
|
+
return fixes;
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
//# sourceMappingURL=enforce-safe-stringify.js.map
|
|
@@ -3,6 +3,22 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.extractGlobalConstants = void 0;
|
|
4
4
|
const ASTHelpers_1 = require("../utils/ASTHelpers");
|
|
5
5
|
const createRule_1 = require("../utils/createRule");
|
|
6
|
+
function isInsideFunction(node) {
|
|
7
|
+
let current = node;
|
|
8
|
+
while (current) {
|
|
9
|
+
if (current.type === 'FunctionDeclaration' ||
|
|
10
|
+
current.type === 'FunctionExpression' ||
|
|
11
|
+
current.type === 'ArrowFunctionExpression') {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
current = current.parent;
|
|
15
|
+
}
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
function isFunctionDefinition(node) {
|
|
19
|
+
return (node?.type === 'FunctionExpression' ||
|
|
20
|
+
node?.type === 'ArrowFunctionExpression');
|
|
21
|
+
}
|
|
6
22
|
exports.extractGlobalConstants = (0, createRule_1.createRule)({
|
|
7
23
|
create(context) {
|
|
8
24
|
return {
|
|
@@ -10,13 +26,19 @@ exports.extractGlobalConstants = (0, createRule_1.createRule)({
|
|
|
10
26
|
if (node.kind !== 'const') {
|
|
11
27
|
return;
|
|
12
28
|
}
|
|
29
|
+
// Skip if any of the declarations are function definitions
|
|
30
|
+
if (node.declarations.some((d) => isFunctionDefinition(d.init))) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
13
33
|
const scope = context.getScope();
|
|
14
34
|
const hasDependencies = node.declarations.some((declaration) => declaration.init &&
|
|
15
35
|
ASTHelpers_1.ASTHelpers.declarationIncludesIdentifier(declaration.init));
|
|
36
|
+
// Only check function/block scoped constants without dependencies
|
|
16
37
|
if (!hasDependencies &&
|
|
17
|
-
(scope.type === 'function' || scope.type === 'block')
|
|
18
|
-
|
|
19
|
-
const constName = node.declarations[0].id
|
|
38
|
+
(scope.type === 'function' || scope.type === 'block') &&
|
|
39
|
+
isInsideFunction(node)) {
|
|
40
|
+
const constName = node.declarations[0].id
|
|
41
|
+
.name;
|
|
20
42
|
context.report({
|
|
21
43
|
node,
|
|
22
44
|
messageId: 'extractGlobalConstants',
|
|
@@ -34,8 +56,7 @@ exports.extractGlobalConstants = (0, createRule_1.createRule)({
|
|
|
34
56
|
const scope = context.getScope();
|
|
35
57
|
const hasDependencies = ASTHelpers_1.ASTHelpers.blockIncludesIdentifier(node.body);
|
|
36
58
|
if (!hasDependencies && scope.type === 'function') {
|
|
37
|
-
|
|
38
|
-
const funcName = node.id.name;
|
|
59
|
+
const funcName = node.id?.name;
|
|
39
60
|
context.report({
|
|
40
61
|
node,
|
|
41
62
|
messageId: 'extractGlobalConstants',
|
|
@@ -52,7 +73,7 @@ exports.extractGlobalConstants = (0, createRule_1.createRule)({
|
|
|
52
73
|
meta: {
|
|
53
74
|
type: 'suggestion',
|
|
54
75
|
docs: {
|
|
55
|
-
description: 'Extract constants
|
|
76
|
+
description: 'Extract static constants and functions to the global scope when possible',
|
|
56
77
|
recommended: 'error',
|
|
57
78
|
},
|
|
58
79
|
schema: [],
|
|
@@ -23,8 +23,27 @@ exports.default = (0, createRule_1.createRule)({
|
|
|
23
23
|
return {
|
|
24
24
|
VariableDeclaration(node) {
|
|
25
25
|
// Only check top-level const declarations
|
|
26
|
-
if (node.kind !== 'const'
|
|
27
|
-
|
|
26
|
+
if (node.kind !== 'const') {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
// Skip if not at program level
|
|
30
|
+
if (node.parent?.type !== utils_1.AST_NODE_TYPES.Program) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
// Skip if any declaration is a function component or arrow function
|
|
34
|
+
const shouldSkip = node.declarations.some(declaration => {
|
|
35
|
+
if (declaration.id.type !== utils_1.AST_NODE_TYPES.Identifier) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
const name = declaration.id.name;
|
|
39
|
+
const init = declaration.init;
|
|
40
|
+
return (
|
|
41
|
+
// Skip function components (uppercase name + arrow function)
|
|
42
|
+
(/^[A-Z]/.test(name) && init?.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression) ||
|
|
43
|
+
// Skip any arrow function
|
|
44
|
+
init?.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression);
|
|
45
|
+
});
|
|
46
|
+
if (shouldSkip) {
|
|
28
47
|
return;
|
|
29
48
|
}
|
|
30
49
|
node.declarations.forEach((declaration) => {
|
|
@@ -55,10 +74,24 @@ exports.default = (0, createRule_1.createRule)({
|
|
|
55
74
|
});
|
|
56
75
|
}
|
|
57
76
|
// Check for as const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
77
|
+
const isAsConstExpression = (node) => {
|
|
78
|
+
if (node.type === utils_1.AST_NODE_TYPES.TSAsExpression) {
|
|
79
|
+
return (node.typeAnnotation?.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
|
|
80
|
+
node.typeAnnotation?.typeName?.name === 'const');
|
|
81
|
+
}
|
|
82
|
+
return false;
|
|
83
|
+
};
|
|
84
|
+
const shouldHaveAsConst = (node) => {
|
|
85
|
+
// Skip if it's already an as const expression
|
|
86
|
+
if (isAsConstExpression(node)) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
// Check if it's a literal, array, or object that should have as const
|
|
90
|
+
return (node.type === utils_1.AST_NODE_TYPES.Literal ||
|
|
91
|
+
node.type === utils_1.AST_NODE_TYPES.ArrayExpression ||
|
|
92
|
+
node.type === utils_1.AST_NODE_TYPES.ObjectExpression);
|
|
93
|
+
};
|
|
94
|
+
if (shouldHaveAsConst(init)) {
|
|
62
95
|
context.report({
|
|
63
96
|
node: init,
|
|
64
97
|
messageId: 'asConst',
|
|
@@ -67,7 +67,8 @@ exports.default = (0, createRule_1.createRule)({
|
|
|
67
67
|
ImportDeclaration(node) {
|
|
68
68
|
const importSource = node.source.value;
|
|
69
69
|
if (typeof importSource === 'string' &&
|
|
70
|
-
isFirebaseImport(importSource)
|
|
70
|
+
isFirebaseImport(importSource) &&
|
|
71
|
+
!node.importKind?.includes('type')) {
|
|
71
72
|
context.report({
|
|
72
73
|
node,
|
|
73
74
|
messageId: 'requireDynamicImport',
|
|
@@ -23,6 +23,11 @@ exports.requireUseMemoObjectLiterals = (0, createRule_1.createRule)({
|
|
|
23
23
|
if (!node.value || node.value.type !== 'JSXExpressionContainer') {
|
|
24
24
|
return;
|
|
25
25
|
}
|
|
26
|
+
// Skip if the prop name is 'sx' or ends with 'Sx'
|
|
27
|
+
const propName = node.name.name;
|
|
28
|
+
if (typeof propName === 'string' && (propName === 'sx' || propName.endsWith('Sx'))) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
26
31
|
const { expression } = node.value;
|
|
27
32
|
// Check if the expression is an object or array literal
|
|
28
33
|
if ((expression.type === 'ObjectExpression' ||
|
package/lib/utils/ASTHelpers.js
CHANGED
|
@@ -75,6 +75,9 @@ class ASTHelpers {
|
|
|
75
75
|
}
|
|
76
76
|
return (this.declarationIncludesIdentifier(node.object) ||
|
|
77
77
|
this.declarationIncludesIdentifier(node.property));
|
|
78
|
+
case 'ImportExpression':
|
|
79
|
+
// Dynamic imports should be considered as having dependencies
|
|
80
|
+
return true;
|
|
78
81
|
case 'CallExpression':
|
|
79
82
|
case 'NewExpression':
|
|
80
83
|
// For function and constructor calls, we care about both the callee and the arguments.
|