eslint-plugin-absolute 0.2.3 → 0.2.4

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/dist/index.js CHANGED
@@ -186,6 +186,16 @@ var explicitObjectTypes = {
186
186
 
187
187
  // src/rules/sort-keys-fixable.ts
188
188
  var SORT_BEFORE = -1;
189
+ var PURE_CONSTRUCTORS = new Set(["Date"]);
190
+ var PURE_GLOBAL_FUNCTIONS = new Set(["Boolean", "Number", "String"]);
191
+ var PURE_MEMBER_METHODS = new Set([
192
+ "getDay",
193
+ "getHours",
194
+ "getMilliseconds",
195
+ "getMinutes",
196
+ "getSeconds",
197
+ "padStart"
198
+ ]);
189
199
  var hasDuplicateNames = (names) => {
190
200
  const seen = new Set;
191
201
  const nonNullNames = names.flatMap((name) => name === null ? [] : [name]);
@@ -197,73 +207,54 @@ var hasDuplicateNames = (names) => {
197
207
  }
198
208
  return false;
199
209
  };
200
- var isSafeStaticTemplate = (node) => node.expressions.length === 0;
201
- var isSafeArrayElement = (node) => {
202
- if (!node || node.type === "SpreadElement") {
203
- return false;
204
- }
205
- return isSafeToReorderExpression(node);
206
- };
207
- var isSafeObjectProperty = (property) => {
208
- if (property.type !== "Property" || property.computed || property.kind !== "init") {
209
- return false;
210
- }
211
- if (property.key.type !== "Identifier" && property.key.type !== "Literal") {
212
- return false;
213
- }
214
- if (property.method) {
215
- return true;
216
- }
217
- return isSafeToReorderExpression(property.value);
218
- };
219
- var isSafeToReorderExpression = (node) => {
220
- if (!node || node.type === "PrivateIdentifier") {
221
- return false;
222
- }
223
- switch (node.type) {
224
- case "Identifier":
225
- case "Literal":
226
- case "ThisExpression":
227
- case "FunctionExpression":
228
- case "ArrowFunctionExpression":
229
- case "ClassExpression":
230
- return true;
231
- case "TemplateLiteral":
232
- return isSafeStaticTemplate(node);
233
- case "UnaryExpression":
234
- return isSafeToReorderExpression(node.argument);
235
- case "ArrayExpression":
236
- return node.elements.every(isSafeArrayElement);
237
- case "ObjectExpression":
238
- return node.properties.every(isSafeObjectProperty);
239
- default:
240
- return false;
241
- }
242
- };
243
- var isSafeJSXAttributeValue = (value) => {
244
- if (value === null) {
245
- return true;
246
- }
247
- if (value.type === "Literal") {
248
- return true;
249
- }
250
- if (value.type !== "JSXExpressionContainer") {
251
- return false;
252
- }
253
- if (value.expression.type === "JSXEmptyExpression") {
254
- return false;
255
- }
256
- return isSafeToReorderExpression(value.expression);
257
- };
258
210
  var sortKeysFixable = {
259
211
  create(context) {
260
212
  const { sourceCode } = context;
261
213
  const [option] = context.options;
214
+ const topLevelBindings = new Map;
215
+ const pureFunctionCache = new Map;
216
+ const pureFunctionInProgress = new Set;
262
217
  const order = option && option.order ? option.order : "asc";
263
218
  const caseSensitive = option && typeof option.caseSensitive === "boolean" ? option.caseSensitive : false;
264
219
  const natural = option && typeof option.natural === "boolean" ? option.natural : false;
265
220
  const minKeys = option && typeof option.minKeys === "number" ? option.minKeys : 2;
266
221
  const variablesBeforeFunctions = option && typeof option.variablesBeforeFunctions === "boolean" ? option.variablesBeforeFunctions : false;
222
+ for (const statement of sourceCode.ast.body) {
223
+ if (statement.type === "ImportDeclaration" && statement.specifiers.length > 0) {
224
+ for (const specifier of statement.specifiers) {
225
+ topLevelBindings.set(specifier.local.name, {
226
+ kind: "import"
227
+ });
228
+ }
229
+ continue;
230
+ }
231
+ if (statement.type === "FunctionDeclaration" && statement.id) {
232
+ topLevelBindings.set(statement.id.name, {
233
+ kind: "function",
234
+ node: statement
235
+ });
236
+ continue;
237
+ }
238
+ if (statement.type !== "VariableDeclaration" || statement.kind !== "const") {
239
+ continue;
240
+ }
241
+ for (const declaration of statement.declarations) {
242
+ if (declaration.id.type !== "Identifier" || !declaration.init) {
243
+ continue;
244
+ }
245
+ if (declaration.init.type === "ArrowFunctionExpression" || declaration.init.type === "FunctionExpression") {
246
+ topLevelBindings.set(declaration.id.name, {
247
+ kind: "function",
248
+ node: declaration.init
249
+ });
250
+ continue;
251
+ }
252
+ topLevelBindings.set(declaration.id.name, {
253
+ kind: "value",
254
+ node: declaration.init
255
+ });
256
+ }
257
+ }
267
258
  const compareKeys = (keyLeft, keyRight) => {
268
259
  let left = keyLeft;
269
260
  let right = keyRight;
@@ -278,6 +269,177 @@ var sortKeysFixable = {
278
269
  }
279
270
  return left.localeCompare(right);
280
271
  };
272
+ const getStaticMemberName = (memberExpression) => {
273
+ if (!memberExpression.computed && memberExpression.property.type === "Identifier") {
274
+ return memberExpression.property.name;
275
+ }
276
+ if (memberExpression.computed && memberExpression.property.type === "Literal" && typeof memberExpression.property.value === "string") {
277
+ return memberExpression.property.value;
278
+ }
279
+ return null;
280
+ };
281
+ const isStableIdentifier = (name, stableLocals) => {
282
+ if (stableLocals.has(name)) {
283
+ return true;
284
+ }
285
+ const binding = topLevelBindings.get(name);
286
+ if (!binding) {
287
+ return false;
288
+ }
289
+ if (binding.kind === "import") {
290
+ return true;
291
+ }
292
+ if (binding.kind === "value") {
293
+ return isPureRuntimeExpression(binding.node, stableLocals);
294
+ }
295
+ return false;
296
+ };
297
+ const isPureTopLevelFunction = (functionNode) => {
298
+ const cached = pureFunctionCache.get(functionNode);
299
+ if (cached !== undefined) {
300
+ return cached;
301
+ }
302
+ if (pureFunctionInProgress.has(functionNode)) {
303
+ return false;
304
+ }
305
+ pureFunctionInProgress.add(functionNode);
306
+ const stableLocals = new Set;
307
+ for (const parameter of functionNode.params) {
308
+ if (parameter.type === "Identifier") {
309
+ stableLocals.add(parameter.name);
310
+ }
311
+ }
312
+ let isPure = true;
313
+ const checkExpression = (expression) => isPureRuntimeExpression(expression, stableLocals);
314
+ if (functionNode.body.type === "BlockStatement") {
315
+ for (const statement of functionNode.body.body) {
316
+ if (statement.type === "ReturnStatement") {
317
+ if (statement.argument && !checkExpression(statement.argument)) {
318
+ isPure = false;
319
+ }
320
+ continue;
321
+ }
322
+ if (statement.type === "VariableDeclaration" && statement.kind === "const") {
323
+ for (const declaration of statement.declarations) {
324
+ if (declaration.id.type !== "Identifier" || !declaration.init || !checkExpression(declaration.init)) {
325
+ isPure = false;
326
+ break;
327
+ }
328
+ stableLocals.add(declaration.id.name);
329
+ }
330
+ if (!isPure) {
331
+ break;
332
+ }
333
+ continue;
334
+ }
335
+ isPure = false;
336
+ break;
337
+ }
338
+ } else {
339
+ isPure = checkExpression(functionNode.body);
340
+ }
341
+ pureFunctionInProgress.delete(functionNode);
342
+ pureFunctionCache.set(functionNode, isPure);
343
+ return isPure;
344
+ };
345
+ const isPureRuntimeExpression = (node, stableLocals) => {
346
+ if (!node || node.type === "PrivateIdentifier") {
347
+ return false;
348
+ }
349
+ switch (node.type) {
350
+ case "Identifier":
351
+ return isStableIdentifier(node.name, stableLocals);
352
+ case "Literal":
353
+ case "FunctionExpression":
354
+ case "ArrowFunctionExpression":
355
+ case "ClassExpression":
356
+ return true;
357
+ case "ThisExpression":
358
+ return stableLocals.has("this");
359
+ case "TemplateLiteral":
360
+ return node.expressions.every((expression) => isPureRuntimeExpression(expression, stableLocals));
361
+ case "UnaryExpression":
362
+ return isPureRuntimeExpression(node.argument, stableLocals);
363
+ case "BinaryExpression":
364
+ case "LogicalExpression":
365
+ return isPureRuntimeExpression(node.left, stableLocals) && isPureRuntimeExpression(node.right, stableLocals);
366
+ case "ConditionalExpression":
367
+ return isPureRuntimeExpression(node.test, stableLocals) && isPureRuntimeExpression(node.consequent, stableLocals) && isPureRuntimeExpression(node.alternate, stableLocals);
368
+ case "ArrayExpression":
369
+ return node.elements.every((element) => {
370
+ if (!element || element.type === "SpreadElement") {
371
+ return false;
372
+ }
373
+ return isPureRuntimeExpression(element, stableLocals);
374
+ });
375
+ case "ObjectExpression":
376
+ return node.properties.every((property) => {
377
+ if (property.type !== "Property" || property.computed || property.kind !== "init") {
378
+ return false;
379
+ }
380
+ if (property.key.type !== "Identifier" && property.key.type !== "Literal") {
381
+ return false;
382
+ }
383
+ if (property.method) {
384
+ return true;
385
+ }
386
+ return isPureRuntimeExpression(property.value, stableLocals);
387
+ });
388
+ case "MemberExpression":
389
+ return isPureRuntimeExpression(node.object, stableLocals) && (!node.computed || isPureRuntimeExpression(node.property, stableLocals));
390
+ case "NewExpression":
391
+ return node.callee.type === "Identifier" && PURE_CONSTRUCTORS.has(node.callee.name) && node.arguments.every((argument) => {
392
+ if (argument.type === "SpreadElement") {
393
+ return false;
394
+ }
395
+ return isPureRuntimeExpression(argument, stableLocals);
396
+ });
397
+ case "CallExpression": {
398
+ const argsArePure = node.arguments.every((argument) => {
399
+ if (argument.type === "SpreadElement") {
400
+ return false;
401
+ }
402
+ return isPureRuntimeExpression(argument, stableLocals);
403
+ });
404
+ if (!argsArePure) {
405
+ return false;
406
+ }
407
+ if (node.callee.type === "Identifier") {
408
+ if (PURE_GLOBAL_FUNCTIONS.has(node.callee.name)) {
409
+ return true;
410
+ }
411
+ const binding = topLevelBindings.get(node.callee.name);
412
+ return binding?.kind === "function" && isPureTopLevelFunction(binding.node);
413
+ }
414
+ if (node.callee.type !== "MemberExpression") {
415
+ return false;
416
+ }
417
+ const memberName = getStaticMemberName(node.callee);
418
+ if (!memberName || !PURE_MEMBER_METHODS.has(memberName)) {
419
+ return false;
420
+ }
421
+ return isPureRuntimeExpression(node.callee.object, stableLocals);
422
+ }
423
+ default:
424
+ return false;
425
+ }
426
+ };
427
+ const isSafeToReorderExpression = (node) => isPureRuntimeExpression(node, new Set);
428
+ const isSafeJSXAttributeValue = (value) => {
429
+ if (value === null) {
430
+ return true;
431
+ }
432
+ if (value.type === "Literal") {
433
+ return true;
434
+ }
435
+ if (value.type !== "JSXExpressionContainer") {
436
+ return false;
437
+ }
438
+ if (value.expression.type === "JSXEmptyExpression") {
439
+ return false;
440
+ }
441
+ return isSafeToReorderExpression(value.expression);
442
+ };
281
443
  const isFunctionProperty = (prop) => {
282
444
  const { value } = prop;
283
445
  return Boolean(value) && (value.type === "FunctionExpression" || value.type === "ArrowFunctionExpression" || prop.method === true);
@@ -1140,13 +1302,9 @@ var sortExports = {
1140
1302
  }
1141
1303
  const dependencies = getImmediateDependencyNames(item.node);
1142
1304
  for (const dependency of dependencies) {
1143
- if (!exportNames.has(dependency)) {
1144
- continue;
1145
- }
1146
- const dependencyIndex = sortedIndices.get(dependency);
1147
- if (dependencyIndex !== undefined && itemIndex < dependencyIndex) {
1305
+ const dependencyIndex = exportNames.has(dependency) ? sortedIndices.get(dependency) : undefined;
1306
+ if (dependencyIndex !== undefined && itemIndex < dependencyIndex)
1148
1307
  return true;
1149
- }
1150
1308
  }
1151
1309
  return false;
1152
1310
  });
package/package.json CHANGED
@@ -30,5 +30,5 @@
30
30
  "typecheck": "bun run tsc --noEmit"
31
31
  },
32
32
  "type": "module",
33
- "version": "0.2.3"
33
+ "version": "0.2.4"
34
34
  }
@@ -423,14 +423,14 @@ export const sortExports: TSESLint.RuleModule<MessageIds, Options> = {
423
423
 
424
424
  const dependencies = getImmediateDependencyNames(item.node);
425
425
  for (const dependency of dependencies) {
426
- if (!exportNames.has(dependency)) {
427
- continue;
428
- }
429
-
430
- const dependencyIndex = sortedIndices.get(dependency);
431
- if (dependencyIndex !== undefined && itemIndex < dependencyIndex) {
426
+ const dependencyIndex = exportNames.has(dependency)
427
+ ? sortedIndices.get(dependency)
428
+ : undefined;
429
+ if (
430
+ dependencyIndex !== undefined &&
431
+ itemIndex < dependencyIndex
432
+ )
432
433
  return true;
433
- }
434
434
  }
435
435
 
436
436
  return false;