@blumintinc/eslint-plugin-blumint 1.0.5 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +1 -0
  2. package/lib/index.js +44 -2
  3. package/lib/rules/consistent-callback-naming.d.ts +2 -0
  4. package/lib/rules/consistent-callback-naming.js +93 -0
  5. package/lib/rules/enforce-callable-types.d.ts +3 -0
  6. package/lib/rules/enforce-callable-types.js +97 -0
  7. package/lib/rules/enforce-callback-memo.d.ts +3 -0
  8. package/lib/rules/enforce-callback-memo.js +79 -0
  9. package/lib/rules/enforce-dynamic-firebase-imports.d.ts +4 -0
  10. package/lib/rules/enforce-dynamic-firebase-imports.js +49 -0
  11. package/lib/rules/enforce-identifiable-firestore-type.d.ts +3 -0
  12. package/lib/rules/enforce-identifiable-firestore-type.js +130 -0
  13. package/lib/rules/enforce-serializable-params.d.ts +9 -0
  14. package/lib/rules/enforce-serializable-params.js +127 -0
  15. package/lib/rules/extract-global-constants.js +27 -6
  16. package/lib/rules/global-const-style.d.ts +5 -0
  17. package/lib/rules/global-const-style.js +110 -0
  18. package/lib/rules/no-jsx-whitespace-literal.d.ts +1 -0
  19. package/lib/rules/no-jsx-whitespace-literal.js +34 -0
  20. package/lib/rules/no-unused-props.d.ts +6 -0
  21. package/lib/rules/no-unused-props.js +91 -0
  22. package/lib/rules/require-dynamic-firebase-imports.d.ts +6 -0
  23. package/lib/rules/require-dynamic-firebase-imports.js +85 -0
  24. package/lib/rules/require-https-error.d.ts +6 -0
  25. package/lib/rules/require-https-error.js +74 -0
  26. package/lib/rules/require-image-overlayed.d.ts +7 -0
  27. package/lib/rules/require-image-overlayed.js +80 -0
  28. package/lib/rules/require-memo.js +3 -3
  29. package/lib/rules/require-usememo-object-literals.d.ts +4 -0
  30. package/lib/rules/require-usememo-object-literals.js +58 -0
  31. package/lib/rules/use-custom-link.d.ts +1 -0
  32. package/lib/rules/use-custom-link.js +49 -0
  33. package/lib/rules/use-custom-router.d.ts +1 -0
  34. package/lib/rules/use-custom-router.js +65 -0
  35. package/lib/utils/ASTHelpers.js +5 -0
  36. package/lib/utils/graph/ClassGraphBuilder.js +4 -0
  37. package/lib/utils/graph/ClassGraphSorterReadability.js +8 -3
  38. package/package.json +2 -2
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const utils_1 = require("@typescript-eslint/utils");
4
+ const createRule_1 = require("../utils/createRule");
5
+ const NON_SERIALIZABLE_TYPES = new Set([
6
+ 'Date',
7
+ 'DocumentReference',
8
+ 'Timestamp',
9
+ 'Map',
10
+ 'Set',
11
+ 'Symbol',
12
+ 'Function',
13
+ 'undefined',
14
+ ]);
15
+ exports.default = (0, createRule_1.createRule)({
16
+ name: 'enforce-serializable-params',
17
+ meta: {
18
+ type: 'problem',
19
+ docs: {
20
+ description: 'Enforce serializable parameters in Firebase Callable/HTTPS Cloud Functions',
21
+ recommended: 'error',
22
+ },
23
+ schema: [
24
+ {
25
+ type: 'object',
26
+ properties: {
27
+ additionalNonSerializableTypes: {
28
+ type: 'array',
29
+ items: { type: 'string' },
30
+ },
31
+ functionTypes: {
32
+ type: 'array',
33
+ items: { type: 'string' },
34
+ default: ['CallableRequest'],
35
+ },
36
+ },
37
+ additionalProperties: false,
38
+ },
39
+ ],
40
+ messages: {
41
+ nonSerializableParam: 'Parameter type "{{ type }}" is not serializable',
42
+ nonSerializableProperty: 'Property "{{ prop }}" has non-serializable type "{{ type }}"',
43
+ },
44
+ },
45
+ defaultOptions: [
46
+ {
47
+ additionalNonSerializableTypes: [],
48
+ functionTypes: ['CallableRequest'],
49
+ },
50
+ ],
51
+ create(context, [options]) {
52
+ const allNonSerializableTypes = new Set([
53
+ ...NON_SERIALIZABLE_TYPES,
54
+ ...(options.additionalNonSerializableTypes || []),
55
+ ]);
56
+ const typeAliasMap = new Map();
57
+ function isNonSerializableType(typeName) {
58
+ return allNonSerializableTypes.has(typeName);
59
+ }
60
+ function checkTypeNode(node, propName) {
61
+ if (!node)
62
+ return;
63
+ switch (node.type) {
64
+ case utils_1.AST_NODE_TYPES.TSTypeReference: {
65
+ const typeName = node.typeName.name;
66
+ if (isNonSerializableType(typeName)) {
67
+ context.report({
68
+ node,
69
+ messageId: propName
70
+ ? 'nonSerializableProperty'
71
+ : 'nonSerializableParam',
72
+ data: {
73
+ type: typeName,
74
+ prop: propName,
75
+ },
76
+ });
77
+ }
78
+ // Check type parameters of generic types (like Array<T>)
79
+ if (node.typeParameters) {
80
+ node.typeParameters.params.forEach((param) => checkTypeNode(param, propName));
81
+ }
82
+ break;
83
+ }
84
+ case utils_1.AST_NODE_TYPES.TSArrayType:
85
+ checkTypeNode(node.elementType, propName);
86
+ break;
87
+ case utils_1.AST_NODE_TYPES.TSTypeAnnotation:
88
+ checkTypeNode(node.typeAnnotation, propName);
89
+ break;
90
+ case utils_1.AST_NODE_TYPES.TSTypeLiteral:
91
+ node.members.forEach((member) => {
92
+ if (member.type === utils_1.AST_NODE_TYPES.TSPropertySignature) {
93
+ const propertyName = member.key.name;
94
+ checkTypeNode(member.typeAnnotation, propertyName);
95
+ }
96
+ });
97
+ break;
98
+ case utils_1.AST_NODE_TYPES.TSUnionType:
99
+ node.types.forEach((type) => checkTypeNode(type, propName));
100
+ break;
101
+ }
102
+ }
103
+ return {
104
+ TSTypeAliasDeclaration(node) {
105
+ typeAliasMap.set(node.id.name, node);
106
+ },
107
+ TSTypeReference(node) {
108
+ const typeName = node.typeName.name;
109
+ if (options.functionTypes.includes(typeName) &&
110
+ node.typeParameters?.params[0]) {
111
+ const typeParam = node.typeParameters.params[0];
112
+ if (typeParam.type === utils_1.AST_NODE_TYPES.TSTypeReference) {
113
+ const referencedTypeName = typeParam.typeName.name;
114
+ const typeAlias = typeAliasMap.get(referencedTypeName);
115
+ if (typeAlias) {
116
+ checkTypeNode(typeAlias.typeAnnotation);
117
+ }
118
+ }
119
+ else {
120
+ checkTypeNode(typeParam);
121
+ }
122
+ }
123
+ },
124
+ };
125
+ },
126
+ });
127
+ //# sourceMappingURL=enforce-serializable-params.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
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
19
- const constName = node.declarations[0].id.name;
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
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
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/functions to the global scope when possible',
76
+ description: 'Extract static constants and functions to the global scope when possible',
56
77
  recommended: 'error',
57
78
  },
58
79
  schema: [],
@@ -0,0 +1,5 @@
1
+ import { TSESTree } from '@typescript-eslint/utils';
2
+ declare const _default: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"upperSnakeCase" | "asConst", never[], {
3
+ VariableDeclaration(node: TSESTree.VariableDeclaration): void;
4
+ }>;
5
+ export default _default;
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const utils_1 = require("@typescript-eslint/utils");
4
+ const createRule_1 = require("../utils/createRule");
5
+ const isUpperSnakeCase = (str) => /^[A-Z][A-Z0-9_]*$/.test(str);
6
+ exports.default = (0, createRule_1.createRule)({
7
+ name: 'global-const-style',
8
+ meta: {
9
+ type: 'suggestion',
10
+ docs: {
11
+ description: 'Enforce UPPER_SNAKE_CASE and as const for global static constants',
12
+ recommended: 'error',
13
+ },
14
+ fixable: 'code',
15
+ schema: [],
16
+ messages: {
17
+ upperSnakeCase: 'Global constants should be in UPPER_SNAKE_CASE',
18
+ asConst: 'Global constants should use "as const"',
19
+ },
20
+ },
21
+ defaultOptions: [],
22
+ create(context) {
23
+ return {
24
+ VariableDeclaration(node) {
25
+ // Only check top-level const declarations
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) {
47
+ return;
48
+ }
49
+ node.declarations.forEach((declaration) => {
50
+ // Skip destructuring patterns
51
+ if (declaration.id.type !== utils_1.AST_NODE_TYPES.Identifier) {
52
+ return;
53
+ }
54
+ const { name } = declaration.id;
55
+ const init = declaration.init;
56
+ // Skip if no initializer or if it's a dynamic value
57
+ if (!init ||
58
+ init.type === utils_1.AST_NODE_TYPES.CallExpression ||
59
+ init.type === utils_1.AST_NODE_TYPES.BinaryExpression) {
60
+ return;
61
+ }
62
+ // Check for UPPER_SNAKE_CASE
63
+ if (!isUpperSnakeCase(name)) {
64
+ context.report({
65
+ node: declaration.id,
66
+ messageId: 'upperSnakeCase',
67
+ fix(fixer) {
68
+ const newName = name
69
+ .replace(/([A-Z])/g, '_$1')
70
+ .toUpperCase()
71
+ .replace(/^_/, '');
72
+ return fixer.replaceText(declaration.id, newName);
73
+ },
74
+ });
75
+ }
76
+ // Check for as const
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)) {
95
+ context.report({
96
+ node: init,
97
+ messageId: 'asConst',
98
+ fix(fixer) {
99
+ const sourceCode = context.getSourceCode();
100
+ const initText = sourceCode.getText(init);
101
+ return fixer.replaceText(init, `${initText} as const`);
102
+ },
103
+ });
104
+ }
105
+ });
106
+ },
107
+ };
108
+ },
109
+ });
110
+ //# sourceMappingURL=global-const-style.js.map
@@ -0,0 +1 @@
1
+ export declare const noJsxWhitespaceLiteral: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"noWhitespaceLiteral", [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.noJsxWhitespaceLiteral = void 0;
4
+ const createRule_1 = require("../utils/createRule");
5
+ exports.noJsxWhitespaceLiteral = (0, createRule_1.createRule)({
6
+ name: 'no-jsx-whitespace-literal',
7
+ meta: {
8
+ type: 'suggestion',
9
+ docs: {
10
+ description: 'Disallow the use of {" "} elements in JSX code',
11
+ recommended: 'error',
12
+ },
13
+ schema: [],
14
+ messages: {
15
+ noWhitespaceLiteral: 'Avoid using {" "} for spacing in JSX. Use proper text nodes or CSS spacing instead.',
16
+ },
17
+ },
18
+ defaultOptions: [],
19
+ create(context) {
20
+ return {
21
+ JSXExpressionContainer(node) {
22
+ if (node.expression.type === 'Literal' &&
23
+ typeof node.expression.value === 'string' &&
24
+ node.expression.value.trim() === '') {
25
+ context.report({
26
+ node,
27
+ messageId: 'noWhitespaceLiteral',
28
+ });
29
+ }
30
+ },
31
+ };
32
+ },
33
+ });
34
+ //# sourceMappingURL=no-jsx-whitespace-literal.js.map
@@ -0,0 +1,6 @@
1
+ import { TSESTree } from '@typescript-eslint/utils';
2
+ export declare const noUnusedProps: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"unusedProp", never[], {
3
+ TSTypeAliasDeclaration(node: TSESTree.TSTypeAliasDeclaration): void;
4
+ VariableDeclaration(node: TSESTree.VariableDeclaration): void;
5
+ 'VariableDeclaration:exit'(node: never): void;
6
+ }>;
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.noUnusedProps = void 0;
4
+ const utils_1 = require("@typescript-eslint/utils");
5
+ const createRule_1 = require("../utils/createRule");
6
+ exports.noUnusedProps = (0, createRule_1.createRule)({
7
+ name: 'no-unused-props',
8
+ meta: {
9
+ type: 'problem',
10
+ docs: {
11
+ description: 'Detect unused props in React component type definitions',
12
+ recommended: 'error',
13
+ },
14
+ schema: [],
15
+ messages: {
16
+ unusedProp: 'Prop "{{propName}}" is defined in type but not used in component',
17
+ },
18
+ fixable: 'code',
19
+ },
20
+ defaultOptions: [],
21
+ create(context) {
22
+ const propsTypes = new Map();
23
+ const usedProps = new Map();
24
+ let currentComponent = null;
25
+ return {
26
+ TSTypeAliasDeclaration(node) {
27
+ if (node.id.name.endsWith('Props')) {
28
+ if (node.typeAnnotation.type === utils_1.AST_NODE_TYPES.TSTypeLiteral) {
29
+ const props = {};
30
+ node.typeAnnotation.members.forEach((member) => {
31
+ if (member.type === utils_1.AST_NODE_TYPES.TSPropertySignature &&
32
+ member.key.type === utils_1.AST_NODE_TYPES.Identifier) {
33
+ props[member.key.name] = member.key;
34
+ }
35
+ });
36
+ propsTypes.set(node.id.name, props);
37
+ }
38
+ }
39
+ },
40
+ VariableDeclaration(node) {
41
+ if (node.declarations.length === 1) {
42
+ const declaration = node.declarations[0];
43
+ if (declaration.init?.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression) {
44
+ const param = declaration.init.params[0];
45
+ if (param?.type === utils_1.AST_NODE_TYPES.ObjectPattern &&
46
+ param.typeAnnotation?.typeAnnotation.type ===
47
+ utils_1.AST_NODE_TYPES.TSTypeReference &&
48
+ param.typeAnnotation.typeAnnotation.typeName.type ===
49
+ utils_1.AST_NODE_TYPES.Identifier) {
50
+ const typeName = param.typeAnnotation.typeAnnotation.typeName.name;
51
+ if (typeName.endsWith('Props')) {
52
+ currentComponent = { node, typeName };
53
+ const used = new Set();
54
+ param.properties.forEach((prop) => {
55
+ if (prop.type === utils_1.AST_NODE_TYPES.Property &&
56
+ prop.key.type === utils_1.AST_NODE_TYPES.Identifier) {
57
+ used.add(prop.key.name);
58
+ }
59
+ });
60
+ usedProps.set(typeName, used);
61
+ }
62
+ }
63
+ }
64
+ }
65
+ },
66
+ 'VariableDeclaration:exit'(node) {
67
+ if (currentComponent?.node === node) {
68
+ const { typeName } = currentComponent;
69
+ const propsType = propsTypes.get(typeName);
70
+ const used = usedProps.get(typeName);
71
+ if (propsType && used) {
72
+ Object.keys(propsType).forEach((prop) => {
73
+ if (!used.has(prop)) {
74
+ context.report({
75
+ node: propsType[prop],
76
+ messageId: 'unusedProp',
77
+ data: { propName: prop },
78
+ });
79
+ }
80
+ });
81
+ }
82
+ // Reset state for this component
83
+ propsTypes.delete(typeName);
84
+ usedProps.delete(typeName);
85
+ currentComponent = null;
86
+ }
87
+ },
88
+ };
89
+ },
90
+ });
91
+ //# sourceMappingURL=no-unused-props.js.map
@@ -0,0 +1,6 @@
1
+ import { TSESTree } from '@typescript-eslint/utils';
2
+ export declare const RULE_NAME = "require-dynamic-firebase-imports";
3
+ declare const _default: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"requireDynamicImport", never[], {
4
+ ImportDeclaration(node: TSESTree.ImportDeclaration): void;
5
+ }>;
6
+ export default _default;
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RULE_NAME = void 0;
4
+ const createRule_1 = require("../utils/createRule");
5
+ exports.RULE_NAME = 'require-dynamic-firebase-imports';
6
+ exports.default = (0, createRule_1.createRule)({
7
+ name: exports.RULE_NAME,
8
+ meta: {
9
+ type: 'problem',
10
+ docs: {
11
+ description: 'Enforce dynamic imports for Firebase dependencies',
12
+ recommended: 'error',
13
+ },
14
+ fixable: 'code',
15
+ schema: [],
16
+ messages: {
17
+ requireDynamicImport: 'Firebase dependencies must be imported dynamically to reduce bundle size',
18
+ },
19
+ },
20
+ defaultOptions: [],
21
+ create(context) {
22
+ const isFirebaseImport = (source) => {
23
+ return (source.startsWith('firebase/') ||
24
+ source.includes('config/firebase-client'));
25
+ };
26
+ const createDynamicImport = (node) => {
27
+ const importSource = node.source.value;
28
+ const importSpecifiers = node.specifiers;
29
+ if (importSpecifiers.length === 0) {
30
+ // For side-effect imports like 'firebase/auth'
31
+ return `await import('${importSource}');`;
32
+ }
33
+ if (importSpecifiers.length === 1) {
34
+ const spec = importSpecifiers[0];
35
+ if (spec.type === 'ImportDefaultSpecifier') {
36
+ // For default imports
37
+ return `const ${spec.local.name} = (await import('${importSource}')).default;`;
38
+ }
39
+ if (spec.type === 'ImportSpecifier') {
40
+ // For single named import
41
+ const importedName = spec.imported.name;
42
+ const localName = spec.local.name;
43
+ if (importedName === localName) {
44
+ return `const { ${localName} } = await import('${importSource}');`;
45
+ }
46
+ return `const { ${importedName}: ${localName} } = await import('${importSource}');`;
47
+ }
48
+ }
49
+ // For multiple named imports
50
+ const importedModule = `await import('${importSource}')`;
51
+ const namedImports = importSpecifiers
52
+ .map((spec) => {
53
+ if (spec.type === 'ImportSpecifier') {
54
+ const importedName = spec.imported.name;
55
+ const localName = spec.local.name;
56
+ return importedName === localName
57
+ ? localName
58
+ : `${importedName}: ${localName}`;
59
+ }
60
+ return '';
61
+ })
62
+ .filter(Boolean)
63
+ .join(', ');
64
+ return `const { ${namedImports} } = ${importedModule};`;
65
+ };
66
+ return {
67
+ ImportDeclaration(node) {
68
+ const importSource = node.source.value;
69
+ if (typeof importSource === 'string' &&
70
+ isFirebaseImport(importSource) &&
71
+ !node.importKind?.includes('type')) {
72
+ context.report({
73
+ node,
74
+ messageId: 'requireDynamicImport',
75
+ fix(fixer) {
76
+ const dynamicImport = createDynamicImport(node);
77
+ return fixer.replaceText(node, dynamicImport);
78
+ },
79
+ });
80
+ }
81
+ },
82
+ };
83
+ },
84
+ });
85
+ //# sourceMappingURL=require-dynamic-firebase-imports.js.map
@@ -0,0 +1,6 @@
1
+ import { TSESTree } from '@typescript-eslint/utils';
2
+ declare const _default: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"useHttpsError" | "useProprietaryHttpsError", never[], {} | {
3
+ ImportDeclaration(node: TSESTree.ImportDeclaration): void;
4
+ ThrowStatement(node: TSESTree.ThrowStatement): void;
5
+ }>;
6
+ export = _default;
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ const utils_1 = require("@typescript-eslint/utils");
3
+ const createRule_1 = require("../utils/createRule");
4
+ module.exports = (0, createRule_1.createRule)({
5
+ name: 'require-https-error',
6
+ meta: {
7
+ type: 'problem',
8
+ docs: {
9
+ description: 'Enforce using proprietary HttpsError instead of throw new Error or firebase-admin HttpsError in functions/src',
10
+ recommended: 'error',
11
+ },
12
+ schema: [],
13
+ messages: {
14
+ useHttpsError: 'Use HttpsError instead of throw new Error in functions/src directory',
15
+ useProprietaryHttpsError: 'Use our proprietary HttpsError instead of firebase-admin HttpsError',
16
+ },
17
+ },
18
+ defaultOptions: [],
19
+ create(context) {
20
+ const filename = context.getFilename();
21
+ // Only apply rule to files in functions/src directory
22
+ if (!filename.includes('functions/src')) {
23
+ return {};
24
+ }
25
+ let hasFirebaseAdminImport = false;
26
+ let httpsIdentifier = null;
27
+ return {
28
+ ImportDeclaration(node) {
29
+ if (node.source.value === 'firebase-admin' ||
30
+ node.source.value === 'firebase-admin/lib/https-error') {
31
+ hasFirebaseAdminImport = true;
32
+ // Track the local name of the https import
33
+ const httpsSpecifier = node.specifiers.find((spec) => spec.type === utils_1.AST_NODE_TYPES.ImportSpecifier &&
34
+ spec.imported.name === 'https');
35
+ if (httpsSpecifier && 'local' in httpsSpecifier) {
36
+ httpsIdentifier = httpsSpecifier.local.name;
37
+ }
38
+ context.report({
39
+ node,
40
+ messageId: 'useProprietaryHttpsError',
41
+ });
42
+ }
43
+ },
44
+ ThrowStatement(node) {
45
+ const argument = node.argument;
46
+ if (argument &&
47
+ argument.type === utils_1.AST_NODE_TYPES.NewExpression &&
48
+ argument.callee) {
49
+ if (argument.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
50
+ argument.callee.name === 'Error') {
51
+ context.report({
52
+ node,
53
+ messageId: 'useHttpsError',
54
+ });
55
+ }
56
+ else if (hasFirebaseAdminImport &&
57
+ ((argument.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
58
+ argument.callee.name === 'HttpsError') ||
59
+ (argument.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
60
+ argument.callee.object.type === utils_1.AST_NODE_TYPES.Identifier &&
61
+ argument.callee.object.name === httpsIdentifier &&
62
+ argument.callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
63
+ argument.callee.property.name === 'HttpsError'))) {
64
+ context.report({
65
+ node,
66
+ messageId: 'useProprietaryHttpsError',
67
+ });
68
+ }
69
+ }
70
+ },
71
+ };
72
+ },
73
+ });
74
+ //# sourceMappingURL=require-https-error.js.map
@@ -0,0 +1,7 @@
1
+ declare const _default: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"useImageOverlayed", {
2
+ componentPath: string;
3
+ }[], {
4
+ JSXElement(node: any): void;
5
+ ImportDeclaration(node: any): void;
6
+ }>;
7
+ export = _default;