@blumintinc/eslint-plugin-blumint 1.20.18 → 1.20.19

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 CHANGED
@@ -223,7 +223,7 @@ function noFrontendImportsFromFunctionsPatterns(pattern) {
223
223
  module.exports = {
224
224
  meta: {
225
225
  name: '@blumintinc/eslint-plugin-blumint',
226
- version: '1.20.18',
226
+ version: '1.20.19',
227
227
  },
228
228
  parseOptions: {
229
229
  ecmaVersion: 2020,
@@ -1,11 +1,51 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.preferCloneDeep = void 0;
7
+ const path_1 = __importDefault(require("path"));
4
8
  const utils_1 = require("@typescript-eslint/utils");
5
9
  const createRule_1 = require("../utils/createRule");
6
10
  const CLONE_DEEP_NAME = 'cloneDeep';
7
11
  const CLONE_DEEP_MODULE = 'functions/src/util/cloneDeep';
12
+ const CLONE_DEEP_TARGET = 'src/util/cloneDeep';
13
+ const FUNCTIONS_TIER_SEGMENT = '/functions/src/';
14
+ const FUNCTIONS_ROOT_SEGMENT = '/functions/';
8
15
  const INDENT_STEP = ' ';
16
+ const toPosixPath = (filePath) => filePath.replace(/\\/g, '/');
17
+ const ensureRelativeSpecifier = (specifier) => specifier.startsWith('.') ? specifier : `./${specifier}`;
18
+ const isWindowsDrivePath = (filePath) => /^[A-Za-z]:[\\/]/.test(filePath);
19
+ const isValidRelativePath = (relativePath) => relativePath !== '' &&
20
+ !path_1.default.isAbsolute(relativePath) &&
21
+ !isWindowsDrivePath(relativePath);
22
+ /**
23
+ * The helper lives in one place but the two TypeScript tiers reach it
24
+ * differently: the root tsconfig maps `functions/*` through `paths`, so files
25
+ * outside `functions/` resolve the bare specifier, while `functions/tsconfig.json`
26
+ * is rooted at `functions/` and declares no `paths`, leaving backend files able
27
+ * to reach a sibling util only by relative path. A single hardcoded specifier
28
+ * therefore emits an unresolvable import for every backend fix (#1389).
29
+ *
30
+ * Returns null when no correct specifier exists, which makes the caller decline
31
+ * the fix rather than write an import that cannot resolve.
32
+ */
33
+ function buildCloneDeepSpecifier(sourceFilePath, cwd) {
34
+ const absoluteFilename = toPosixPath(path_1.default.isAbsolute(sourceFilePath)
35
+ ? sourceFilePath
36
+ : path_1.default.join(cwd, sourceFilePath));
37
+ const tierIndex = absoluteFilename.indexOf(FUNCTIONS_TIER_SEGMENT);
38
+ if (tierIndex === -1) {
39
+ return CLONE_DEEP_MODULE;
40
+ }
41
+ const functionsRoot = absoluteFilename.slice(0, tierIndex + FUNCTIONS_ROOT_SEGMENT.length);
42
+ const targetPath = path_1.default.join(functionsRoot, CLONE_DEEP_TARGET);
43
+ const relativePath = path_1.default.relative(path_1.default.dirname(absoluteFilename), targetPath);
44
+ if (!isValidRelativePath(relativePath)) {
45
+ return null;
46
+ }
47
+ return ensureRelativeSpecifier(toPosixPath(relativePath));
48
+ }
9
49
  /**
10
50
  * Only BluMint's own `cloneDeep` accepts an overrides argument, so an existing
11
51
  * binding coming from anywhere else (notably `lodash`) must not be reused by the
@@ -44,6 +84,8 @@ exports.preferCloneDeep = (0, createRule_1.createRule)({
44
84
  // Track processed nodes to avoid duplicate reports
45
85
  const processedNodes = new Set();
46
86
  const sourceCode = context.sourceCode;
87
+ const cwd = typeof context.getCwd === 'function' ? context.getCwd() : process.cwd();
88
+ const cloneDeepSpecifier = buildCloneDeepSpecifier(context.getFilename(), cwd);
47
89
  function normalizedTextOf(node) {
48
90
  return sourceCode.getText(node).replace(/\s+/g, '');
49
91
  }
@@ -324,7 +366,8 @@ exports.preferCloneDeep = (0, createRule_1.createRule)({
324
366
  /**
325
367
  * Returns the fixes required for `cloneDeep` to resolve, an empty list when
326
368
  * it already does, or null when a conflicting binding of that name exists —
327
- * shadowing it would silently call something else.
369
+ * shadowing it would silently call something else — or when no import
370
+ * specifier that resolves from this file can be derived.
328
371
  */
329
372
  function buildImportFixes(fixer, scope) {
330
373
  const existing = utils_1.ASTUtils.findVariable(scope, CLONE_DEEP_NAME);
@@ -360,7 +403,12 @@ exports.preferCloneDeep = (0, createRule_1.createRule)({
360
403
  const lastSpecifier = namedSpecifiers[namedSpecifiers.length - 1];
361
404
  return [fixer.insertTextAfter(lastSpecifier, `, ${CLONE_DEEP_NAME}`)];
362
405
  }
363
- const importText = `import { ${CLONE_DEEP_NAME} } from '${CLONE_DEEP_MODULE}';\n`;
406
+ // Reusing an existing import needs no specifier of its own, so only a
407
+ // freshly written import depends on one being derivable.
408
+ if (cloneDeepSpecifier === null) {
409
+ return null;
410
+ }
411
+ const importText = `import { ${CLONE_DEEP_NAME} } from '${cloneDeepSpecifier}';\n`;
364
412
  const [firstImport] = importDeclarations;
365
413
  if (firstImport) {
366
414
  return [fixer.insertTextBefore(firstImport, importText)];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blumintinc/eslint-plugin-blumint",
3
- "version": "1.20.18",
3
+ "version": "1.20.19",
4
4
  "description": "Custom eslint rules for use within BluMint",
5
5
  "author": {
6
6
  "name": "Brodie McGuire",
@@ -1,4 +1,18 @@
1
1
  [
2
+ {
3
+ "version": "1.20.19",
4
+ "date": "2026-07-29T18:36:35.802Z",
5
+ "rules": [
6
+ {
7
+ "name": "prefer-clone-deep",
8
+ "changeType": "fix",
9
+ "issues": [
10
+ 1389
11
+ ],
12
+ "summary": "derive autofix import specifier from file tier (closes #1389)"
13
+ }
14
+ ]
15
+ },
2
16
  {
3
17
  "version": "1.20.18",
4
18
  "date": "2026-07-29T17:40:51.418Z",