@tanstack/eslint-plugin-query 5.95.2 → 5.96.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.
@@ -225,7 +225,9 @@ export declare const name_alias_4 = "no-unstable-deps";
225
225
 
226
226
  export declare const name_alias_5 = "no-void-query-fn";
227
227
 
228
- export declare const name_alias_6 = "stable-query-client";
228
+ export declare const name_alias_6 = "prefer-query-options";
229
+
230
+ export declare const name_alias_7 = "stable-query-client";
229
231
 
230
232
  export declare const NoRestDestructuringUtils: {
231
233
  isObjectRestDestructuring(node: TSESTree.Node): boolean;
@@ -243,13 +245,26 @@ declare const plugin: {
243
245
  recommended: {
244
246
  plugins: string[];
245
247
  rules: {
246
- '@tanstack/query/exhaustive-deps': "error";
247
- '@tanstack/query/no-rest-destructuring': "warn";
248
- '@tanstack/query/stable-query-client': "error";
249
- '@tanstack/query/no-unstable-deps': "error";
250
- '@tanstack/query/infinite-query-property-order': "error";
251
- '@tanstack/query/no-void-query-fn': "error";
252
- '@tanstack/query/mutation-property-order': "error";
248
+ readonly '@tanstack/query/exhaustive-deps': "error";
249
+ readonly '@tanstack/query/no-rest-destructuring': "warn";
250
+ readonly '@tanstack/query/stable-query-client': "error";
251
+ readonly '@tanstack/query/no-unstable-deps': "error";
252
+ readonly '@tanstack/query/infinite-query-property-order': "error";
253
+ readonly '@tanstack/query/no-void-query-fn': "error";
254
+ readonly '@tanstack/query/mutation-property-order': "error";
255
+ };
256
+ };
257
+ recommendedStrict: {
258
+ plugins: string[];
259
+ rules: {
260
+ readonly '@tanstack/query/prefer-query-options': "error";
261
+ readonly '@tanstack/query/exhaustive-deps': "error";
262
+ readonly '@tanstack/query/no-rest-destructuring': "warn";
263
+ readonly '@tanstack/query/stable-query-client': "error";
264
+ readonly '@tanstack/query/no-unstable-deps': "error";
265
+ readonly '@tanstack/query/infinite-query-property-order': "error";
266
+ readonly '@tanstack/query/no-void-query-fn': "error";
267
+ readonly '@tanstack/query/mutation-property-order': "error";
253
268
  };
254
269
  };
255
270
  'flat/recommended': {
@@ -258,13 +273,29 @@ declare const plugin: {
258
273
  '@tanstack/query': {};
259
274
  };
260
275
  rules: {
261
- '@tanstack/query/exhaustive-deps': "error";
262
- '@tanstack/query/no-rest-destructuring': "warn";
263
- '@tanstack/query/stable-query-client': "error";
264
- '@tanstack/query/no-unstable-deps': "error";
265
- '@tanstack/query/infinite-query-property-order': "error";
266
- '@tanstack/query/no-void-query-fn': "error";
267
- '@tanstack/query/mutation-property-order': "error";
276
+ readonly '@tanstack/query/exhaustive-deps': "error";
277
+ readonly '@tanstack/query/no-rest-destructuring': "warn";
278
+ readonly '@tanstack/query/stable-query-client': "error";
279
+ readonly '@tanstack/query/no-unstable-deps': "error";
280
+ readonly '@tanstack/query/infinite-query-property-order': "error";
281
+ readonly '@tanstack/query/no-void-query-fn': "error";
282
+ readonly '@tanstack/query/mutation-property-order': "error";
283
+ };
284
+ }[];
285
+ 'flat/recommended-strict': {
286
+ name: string;
287
+ plugins: {
288
+ '@tanstack/query': {};
289
+ };
290
+ rules: {
291
+ readonly '@tanstack/query/prefer-query-options': "error";
292
+ readonly '@tanstack/query/exhaustive-deps': "error";
293
+ readonly '@tanstack/query/no-rest-destructuring': "warn";
294
+ readonly '@tanstack/query/stable-query-client': "error";
295
+ readonly '@tanstack/query/no-unstable-deps': "error";
296
+ readonly '@tanstack/query/infinite-query-property-order': "error";
297
+ readonly '@tanstack/query/no-void-query-fn': "error";
298
+ readonly '@tanstack/query/mutation-property-order': "error";
268
299
  };
269
300
  }[];
270
301
  };
@@ -277,7 +308,9 @@ declare interface Plugin_2 extends Omit<ESLint.Plugin, 'rules'> {
277
308
  rules: Record<RuleKey, RuleModule<any, any, any>>;
278
309
  configs: {
279
310
  recommended: ESLint.ConfigData;
311
+ recommendedStrict: ESLint.ConfigData;
280
312
  'flat/recommended': Array<Linter.Config>;
313
+ 'flat/recommended-strict': Array<Linter.Config>;
281
314
  };
282
315
  }
283
316
  export { Plugin_2 as Plugin }
@@ -312,6 +345,10 @@ export declare const rule_alias_6: ESLintUtils.RuleModule<string, readonly unkno
312
345
  name: string;
313
346
  };
314
347
 
348
+ export declare const rule_alias_7: ESLintUtils.RuleModule<string, readonly unknown[], ExtraRuleDocs, ESLintUtils.RuleListener> & {
349
+ name: string;
350
+ };
351
+
315
352
  declare type RuleKey = keyof typeof rules;
316
353
 
317
354
  export declare const rules: Record<string, ESLintUtils.RuleModule<string, ReadonlyArray<unknown>, ExtraRuleDocs, ESLintUtils.RuleListener>>;
@@ -225,7 +225,9 @@ export declare const name_alias_4 = "no-unstable-deps";
225
225
 
226
226
  export declare const name_alias_5 = "no-void-query-fn";
227
227
 
228
- export declare const name_alias_6 = "stable-query-client";
228
+ export declare const name_alias_6 = "prefer-query-options";
229
+
230
+ export declare const name_alias_7 = "stable-query-client";
229
231
 
230
232
  export declare const NoRestDestructuringUtils: {
231
233
  isObjectRestDestructuring(node: TSESTree.Node): boolean;
@@ -243,13 +245,26 @@ declare const plugin: {
243
245
  recommended: {
244
246
  plugins: string[];
245
247
  rules: {
246
- '@tanstack/query/exhaustive-deps': "error";
247
- '@tanstack/query/no-rest-destructuring': "warn";
248
- '@tanstack/query/stable-query-client': "error";
249
- '@tanstack/query/no-unstable-deps': "error";
250
- '@tanstack/query/infinite-query-property-order': "error";
251
- '@tanstack/query/no-void-query-fn': "error";
252
- '@tanstack/query/mutation-property-order': "error";
248
+ readonly '@tanstack/query/exhaustive-deps': "error";
249
+ readonly '@tanstack/query/no-rest-destructuring': "warn";
250
+ readonly '@tanstack/query/stable-query-client': "error";
251
+ readonly '@tanstack/query/no-unstable-deps': "error";
252
+ readonly '@tanstack/query/infinite-query-property-order': "error";
253
+ readonly '@tanstack/query/no-void-query-fn': "error";
254
+ readonly '@tanstack/query/mutation-property-order': "error";
255
+ };
256
+ };
257
+ recommendedStrict: {
258
+ plugins: string[];
259
+ rules: {
260
+ readonly '@tanstack/query/prefer-query-options': "error";
261
+ readonly '@tanstack/query/exhaustive-deps': "error";
262
+ readonly '@tanstack/query/no-rest-destructuring': "warn";
263
+ readonly '@tanstack/query/stable-query-client': "error";
264
+ readonly '@tanstack/query/no-unstable-deps': "error";
265
+ readonly '@tanstack/query/infinite-query-property-order': "error";
266
+ readonly '@tanstack/query/no-void-query-fn': "error";
267
+ readonly '@tanstack/query/mutation-property-order': "error";
253
268
  };
254
269
  };
255
270
  'flat/recommended': {
@@ -258,13 +273,29 @@ declare const plugin: {
258
273
  '@tanstack/query': {};
259
274
  };
260
275
  rules: {
261
- '@tanstack/query/exhaustive-deps': "error";
262
- '@tanstack/query/no-rest-destructuring': "warn";
263
- '@tanstack/query/stable-query-client': "error";
264
- '@tanstack/query/no-unstable-deps': "error";
265
- '@tanstack/query/infinite-query-property-order': "error";
266
- '@tanstack/query/no-void-query-fn': "error";
267
- '@tanstack/query/mutation-property-order': "error";
276
+ readonly '@tanstack/query/exhaustive-deps': "error";
277
+ readonly '@tanstack/query/no-rest-destructuring': "warn";
278
+ readonly '@tanstack/query/stable-query-client': "error";
279
+ readonly '@tanstack/query/no-unstable-deps': "error";
280
+ readonly '@tanstack/query/infinite-query-property-order': "error";
281
+ readonly '@tanstack/query/no-void-query-fn': "error";
282
+ readonly '@tanstack/query/mutation-property-order': "error";
283
+ };
284
+ }[];
285
+ 'flat/recommended-strict': {
286
+ name: string;
287
+ plugins: {
288
+ '@tanstack/query': {};
289
+ };
290
+ rules: {
291
+ readonly '@tanstack/query/prefer-query-options': "error";
292
+ readonly '@tanstack/query/exhaustive-deps': "error";
293
+ readonly '@tanstack/query/no-rest-destructuring': "warn";
294
+ readonly '@tanstack/query/stable-query-client': "error";
295
+ readonly '@tanstack/query/no-unstable-deps': "error";
296
+ readonly '@tanstack/query/infinite-query-property-order': "error";
297
+ readonly '@tanstack/query/no-void-query-fn': "error";
298
+ readonly '@tanstack/query/mutation-property-order': "error";
268
299
  };
269
300
  }[];
270
301
  };
@@ -277,7 +308,9 @@ declare interface Plugin_2 extends Omit<ESLint.Plugin, 'rules'> {
277
308
  rules: Record<RuleKey, RuleModule<any, any, any>>;
278
309
  configs: {
279
310
  recommended: ESLint.ConfigData;
311
+ recommendedStrict: ESLint.ConfigData;
280
312
  'flat/recommended': Array<Linter.Config>;
313
+ 'flat/recommended-strict': Array<Linter.Config>;
281
314
  };
282
315
  }
283
316
  export { Plugin_2 as Plugin }
@@ -312,6 +345,10 @@ export declare const rule_alias_6: ESLintUtils.RuleModule<string, readonly unkno
312
345
  name: string;
313
346
  };
314
347
 
348
+ export declare const rule_alias_7: ESLintUtils.RuleModule<string, readonly unknown[], ExtraRuleDocs, ESLintUtils.RuleListener> & {
349
+ name: string;
350
+ };
351
+
315
352
  declare type RuleKey = keyof typeof rules;
316
353
 
317
354
  export declare const rules: Record<string, ESLintUtils.RuleModule<string, ReadonlyArray<unknown>, ExtraRuleDocs, ESLintUtils.RuleListener>>;
@@ -17,11 +17,11 @@ var ASTUtils = {
17
17
  isIdentifier(node) {
18
18
  return node.type === AST_NODE_TYPES.Identifier;
19
19
  },
20
- isIdentifierWithName(node, name8) {
21
- return ASTUtils.isIdentifier(node) && node.name === name8;
20
+ isIdentifierWithName(node, name9) {
21
+ return ASTUtils.isIdentifier(node) && node.name === name9;
22
22
  },
23
- isIdentifierWithOneOfNames(node, name8) {
24
- return ASTUtils.isIdentifier(node) && name8.includes(node.name);
23
+ isIdentifierWithOneOfNames(node, name9) {
24
+ return ASTUtils.isIdentifier(node) && name9.includes(node.name);
25
25
  },
26
26
  isProperty(node) {
27
27
  return node.type === AST_NODE_TYPES.Property;
@@ -395,8 +395,8 @@ var ExhaustiveDepsUtils = {
395
395
  const roots = /* @__PURE__ */ new Set();
396
396
  const paths = /* @__PURE__ */ new Set();
397
397
  const visitorKeys = sourceCode.visitorKeys;
398
- function addRoot(name8) {
399
- const cleaned = ExhaustiveDepsUtils.normalizeChain(name8);
398
+ function addRoot(name9) {
399
+ const cleaned = ExhaustiveDepsUtils.normalizeChain(name9);
400
400
  roots.add(cleaned);
401
401
  paths.add(cleaned);
402
402
  }
@@ -1327,6 +1327,260 @@ var rule7 = createPropertyOrderRule(
1327
1327
  sortRules2
1328
1328
  );
1329
1329
 
1330
+ // src/rules/prefer-query-options/prefer-query-options.rule.ts
1331
+ import { AST_NODE_TYPES as AST_NODE_TYPES9, ESLintUtils as ESLintUtils7 } from "@typescript-eslint/utils";
1332
+ var name8 = "prefer-query-options";
1333
+ var queryHooks2 = [
1334
+ "useQuery",
1335
+ "useInfiniteQuery",
1336
+ "useSuspenseQuery",
1337
+ "useSuspenseInfiniteQuery",
1338
+ "usePrefetchQuery",
1339
+ "usePrefetchInfiniteQuery"
1340
+ ];
1341
+ var queriesHooks = ["useQueries", "useSuspenseQueries"];
1342
+ var filterHooks = ["useIsFetching"];
1343
+ var queryClientOptionMethods = [
1344
+ "fetchQuery",
1345
+ "prefetchQuery",
1346
+ "fetchInfiniteQuery",
1347
+ "prefetchInfiniteQuery",
1348
+ "ensureQueryData",
1349
+ "ensureInfiniteQueryData"
1350
+ ];
1351
+ var queryClientQueryKeyMethods = [
1352
+ "getQueryData",
1353
+ "setQueryData",
1354
+ "getQueryState",
1355
+ "setQueryDefaults",
1356
+ "getQueryDefaults"
1357
+ ];
1358
+ var queryClientFilterMethods = [
1359
+ "invalidateQueries",
1360
+ "cancelQueries",
1361
+ "refetchQueries",
1362
+ "removeQueries",
1363
+ "resetQueries",
1364
+ "isFetching",
1365
+ "getQueriesData",
1366
+ "setQueriesData"
1367
+ ];
1368
+ var queryOptionsBuilders = ["queryOptions", "infiniteQueryOptions"];
1369
+ var createRule7 = ESLintUtils7.RuleCreator(getDocsUrl);
1370
+ var rule8 = createRule7({
1371
+ name: name8,
1372
+ meta: {
1373
+ type: "problem",
1374
+ docs: {
1375
+ description: "Prefer using queryOptions() to co-locate queryKey and queryFn",
1376
+ recommended: "strict"
1377
+ },
1378
+ messages: {
1379
+ preferQueryOptions: "Prefer using queryOptions() or infiniteQueryOptions() to co-locate queryKey and queryFn.",
1380
+ preferQueryOptionsQueryKey: "Prefer referencing a queryKey from a queryOptions() result instead of typing it manually."
1381
+ },
1382
+ schema: []
1383
+ },
1384
+ defaultOptions: [],
1385
+ create: detectTanstackQueryImports((context, _, helpers) => {
1386
+ function reportInlineQueryOptions(node) {
1387
+ if (ASTUtils.isObjectExpression(node) && hasInlineQueryOptions(node)) {
1388
+ context.report({
1389
+ node,
1390
+ messageId: "preferQueryOptions"
1391
+ });
1392
+ }
1393
+ }
1394
+ function reportInlineFilterQueryKey(node) {
1395
+ if (ASTUtils.isObjectExpression(node) && hasInlineFilterQueryKey(node)) {
1396
+ context.report({
1397
+ node,
1398
+ messageId: "preferQueryOptionsQueryKey"
1399
+ });
1400
+ }
1401
+ }
1402
+ return {
1403
+ CallExpression: (node) => {
1404
+ var _a;
1405
+ if (ASTUtils.isIdentifier(node.callee)) {
1406
+ const importedName = getTanstackImportName(
1407
+ context,
1408
+ helpers,
1409
+ node.callee
1410
+ );
1411
+ if (importedName === null) {
1412
+ return;
1413
+ }
1414
+ if (queryOptionsBuilders.includes(importedName)) {
1415
+ return;
1416
+ }
1417
+ const options2 = node.arguments[0];
1418
+ if (options2 === void 0) {
1419
+ return;
1420
+ }
1421
+ if (queryHooks2.includes(importedName)) {
1422
+ reportInlineQueryOptions(options2);
1423
+ return;
1424
+ }
1425
+ if (queriesHooks.includes(importedName) && ASTUtils.isObjectExpression(options2)) {
1426
+ const queries = (_a = ASTUtils.findPropertyWithIdentifierKey(
1427
+ options2.properties,
1428
+ "queries"
1429
+ )) == null ? void 0 : _a.value;
1430
+ if (queries !== void 0) {
1431
+ getQueryObjects(queries).forEach((query) => {
1432
+ reportInlineQueryOptions(query);
1433
+ });
1434
+ }
1435
+ return;
1436
+ }
1437
+ if (filterHooks.includes(importedName)) {
1438
+ reportInlineFilterQueryKey(options2);
1439
+ }
1440
+ return;
1441
+ }
1442
+ if (node.callee.type !== AST_NODE_TYPES9.MemberExpression || !ASTUtils.isIdentifier(node.callee.property) || !isTanstackQueryClient(node.callee.object, context, helpers)) {
1443
+ return;
1444
+ }
1445
+ const method = node.callee.property.name;
1446
+ const options = node.arguments[0];
1447
+ if (options === void 0) {
1448
+ return;
1449
+ }
1450
+ if (queryClientOptionMethods.includes(method)) {
1451
+ reportInlineQueryOptions(options);
1452
+ return;
1453
+ }
1454
+ if (queryClientQueryKeyMethods.includes(method) && isInlineArrayExpression(options)) {
1455
+ context.report({
1456
+ node: options,
1457
+ messageId: "preferQueryOptionsQueryKey"
1458
+ });
1459
+ return;
1460
+ }
1461
+ if (queryClientFilterMethods.includes(method)) {
1462
+ reportInlineFilterQueryKey(options);
1463
+ }
1464
+ }
1465
+ };
1466
+ })
1467
+ });
1468
+ function hasInlineQueryOptions(node) {
1469
+ return ASTUtils.findPropertyWithIdentifierKey(node.properties, "queryKey") !== void 0 || ASTUtils.findPropertyWithIdentifierKey(node.properties, "queryFn") !== void 0;
1470
+ }
1471
+ function hasInlineFilterQueryKey(node) {
1472
+ var _a;
1473
+ const queryKey = (_a = ASTUtils.findPropertyWithIdentifierKey(
1474
+ node.properties,
1475
+ "queryKey"
1476
+ )) == null ? void 0 : _a.value;
1477
+ return queryKey !== void 0 && isInlineArrayExpression(queryKey);
1478
+ }
1479
+ function isInlineArrayExpression(node) {
1480
+ return unwrapTypeAssertions(node).type === AST_NODE_TYPES9.ArrayExpression;
1481
+ }
1482
+ function getReturnedObjectExpressions(node) {
1483
+ if (ASTUtils.isObjectExpression(node)) {
1484
+ return [node];
1485
+ }
1486
+ if (node.type === AST_NODE_TYPES9.ArrowFunctionExpression || node.type === AST_NODE_TYPES9.FunctionExpression) {
1487
+ return getReturnedObjectExpressions(node.body);
1488
+ }
1489
+ if (node.type === AST_NODE_TYPES9.BlockStatement) {
1490
+ return node.body.flatMap((statement) => {
1491
+ if (statement.type === AST_NODE_TYPES9.ReturnStatement && statement.argument !== null) {
1492
+ return getReturnedObjectExpressions(statement.argument);
1493
+ }
1494
+ return [];
1495
+ });
1496
+ }
1497
+ if (node.type === AST_NODE_TYPES9.ConditionalExpression) {
1498
+ return [
1499
+ ...getReturnedObjectExpressions(node.consequent),
1500
+ ...getReturnedObjectExpressions(node.alternate)
1501
+ ];
1502
+ }
1503
+ if (node.type === AST_NODE_TYPES9.LogicalExpression) {
1504
+ return [
1505
+ ...getReturnedObjectExpressions(node.left),
1506
+ ...getReturnedObjectExpressions(node.right)
1507
+ ];
1508
+ }
1509
+ if (node.type === AST_NODE_TYPES9.SequenceExpression) {
1510
+ return node.expressions.flatMap(
1511
+ (expression) => getReturnedObjectExpressions(expression)
1512
+ );
1513
+ }
1514
+ return [];
1515
+ }
1516
+ function getQueryObjects(node) {
1517
+ if (node.type === AST_NODE_TYPES9.ArrayExpression) {
1518
+ return node.elements.flatMap((element) => {
1519
+ if (element !== null && ASTUtils.isObjectExpression(element)) {
1520
+ return [element];
1521
+ }
1522
+ return [];
1523
+ });
1524
+ }
1525
+ if (node.type === AST_NODE_TYPES9.CallExpression && node.callee.type === AST_NODE_TYPES9.MemberExpression && ASTUtils.isIdentifierWithName(node.callee.property, "map")) {
1526
+ const mapper = node.arguments[0];
1527
+ if ((mapper == null ? void 0 : mapper.type) === AST_NODE_TYPES9.ArrowFunctionExpression || (mapper == null ? void 0 : mapper.type) === AST_NODE_TYPES9.FunctionExpression) {
1528
+ return getReturnedObjectExpressions(mapper);
1529
+ }
1530
+ }
1531
+ return [];
1532
+ }
1533
+ function isTanstackQueryClient(node, context, helpers) {
1534
+ const source = resolveQueryClientSource(node, context);
1535
+ if (source.type === AST_NODE_TYPES9.CallExpression && ASTUtils.isIdentifier(source.callee)) {
1536
+ return getTanstackImportName(context, helpers, source.callee) === "useQueryClient";
1537
+ }
1538
+ if (source.type === AST_NODE_TYPES9.NewExpression && ASTUtils.isIdentifier(source.callee)) {
1539
+ return getTanstackImportName(context, helpers, source.callee) === "QueryClient";
1540
+ }
1541
+ return false;
1542
+ }
1543
+ function getTanstackImportName(context, helpers, node) {
1544
+ var _a, _b, _c;
1545
+ if (!helpers.isTanstackQueryImport(node)) {
1546
+ return null;
1547
+ }
1548
+ const definition = (_c = (_b = (_a = context.sourceCode.getScope(node).references.find((reference) => reference.identifier === node)) == null ? void 0 : _a.resolved) == null ? void 0 : _b.defs[0]) == null ? void 0 : _c.node;
1549
+ if ((definition == null ? void 0 : definition.type) !== AST_NODE_TYPES9.ImportSpecifier || definition.imported.type !== AST_NODE_TYPES9.Identifier) {
1550
+ return null;
1551
+ }
1552
+ return definition.imported.name;
1553
+ }
1554
+ function resolveQueryClientSource(node, context) {
1555
+ const visitedNodes = /* @__PURE__ */ new Set();
1556
+ while (!visitedNodes.has(node)) {
1557
+ visitedNodes.add(node);
1558
+ if (node.type === AST_NODE_TYPES9.ChainExpression) {
1559
+ node = node.expression;
1560
+ continue;
1561
+ }
1562
+ node = unwrapTypeAssertions(node);
1563
+ if (node.type !== AST_NODE_TYPES9.Identifier) {
1564
+ return node;
1565
+ }
1566
+ const expression = ASTUtils.getReferencedExpressionByIdentifier({
1567
+ context,
1568
+ node
1569
+ });
1570
+ if (expression === null) {
1571
+ return node;
1572
+ }
1573
+ node = expression;
1574
+ }
1575
+ return node;
1576
+ }
1577
+ function unwrapTypeAssertions(node) {
1578
+ while (node.type === AST_NODE_TYPES9.TSAsExpression || node.type === AST_NODE_TYPES9.TSSatisfiesExpression || node.type === AST_NODE_TYPES9.TSTypeAssertion) {
1579
+ node = node.expression;
1580
+ }
1581
+ return node;
1582
+ }
1583
+
1330
1584
  // src/rules.ts
1331
1585
  var rules = {
1332
1586
  [name]: rule,
@@ -1335,10 +1589,11 @@ var rules = {
1335
1589
  [name4]: rule4,
1336
1590
  [name5]: rule5,
1337
1591
  [name6]: rule6,
1338
- [name7]: rule7
1592
+ [name7]: rule7,
1593
+ [name8]: rule8
1339
1594
  };
1340
1595
 
1341
1596
  export {
1342
1597
  rules
1343
1598
  };
1344
- //# sourceMappingURL=chunk-T44AUBX4.js.map
1599
+ //# sourceMappingURL=chunk-N2NGQ5SW.js.map