eslint-plugin-absolute 0.2.2 → 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);
|
|
@@ -1114,7 +1276,7 @@ var sortExports = {
|
|
|
1114
1276
|
});
|
|
1115
1277
|
return unsorted ? messageId : null;
|
|
1116
1278
|
};
|
|
1117
|
-
const
|
|
1279
|
+
const hasForwardDependenciesInOrder = (items) => {
|
|
1118
1280
|
const exportNames = items.map((item) => item.name);
|
|
1119
1281
|
return items.some((item, idx) => {
|
|
1120
1282
|
const laterNames = new Set(exportNames.slice(idx + 1));
|
|
@@ -1130,6 +1292,23 @@ var sortExports = {
|
|
|
1130
1292
|
return false;
|
|
1131
1293
|
});
|
|
1132
1294
|
};
|
|
1295
|
+
const wouldCreateForwardDependencies = (items, sortedItems) => {
|
|
1296
|
+
const sortedIndices = new Map(sortedItems.map((item, idx) => [item.name, idx]));
|
|
1297
|
+
const exportNames = new Set(items.map((item) => item.name));
|
|
1298
|
+
return items.some((item) => {
|
|
1299
|
+
const itemIndex = sortedIndices.get(item.name);
|
|
1300
|
+
if (itemIndex === undefined) {
|
|
1301
|
+
return false;
|
|
1302
|
+
}
|
|
1303
|
+
const dependencies = getImmediateDependencyNames(item.node);
|
|
1304
|
+
for (const dependency of dependencies) {
|
|
1305
|
+
const dependencyIndex = exportNames.has(dependency) ? sortedIndices.get(dependency) : undefined;
|
|
1306
|
+
if (dependencyIndex !== undefined && itemIndex < dependencyIndex)
|
|
1307
|
+
return true;
|
|
1308
|
+
}
|
|
1309
|
+
return false;
|
|
1310
|
+
});
|
|
1311
|
+
};
|
|
1133
1312
|
const processExportBlock = (block) => {
|
|
1134
1313
|
if (block.length < minKeys) {
|
|
1135
1314
|
return;
|
|
@@ -1142,10 +1321,13 @@ var sortExports = {
|
|
|
1142
1321
|
if (!messageId) {
|
|
1143
1322
|
return;
|
|
1144
1323
|
}
|
|
1145
|
-
if (
|
|
1324
|
+
if (hasForwardDependenciesInOrder(items)) {
|
|
1146
1325
|
return;
|
|
1147
1326
|
}
|
|
1148
1327
|
const sortedItems = items.slice().sort(sortComparator);
|
|
1328
|
+
if (wouldCreateForwardDependencies(items, sortedItems)) {
|
|
1329
|
+
return;
|
|
1330
|
+
}
|
|
1149
1331
|
const expectedOrder = sortedItems.map((item) => item.name).join(", ");
|
|
1150
1332
|
const [firstNode] = block;
|
|
1151
1333
|
const lastNode = block[block.length - 1];
|
package/package.json
CHANGED
|
@@ -388,7 +388,7 @@ export const sortExports: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
388
388
|
return unsorted ? messageId : null;
|
|
389
389
|
};
|
|
390
390
|
|
|
391
|
-
const
|
|
391
|
+
const hasForwardDependenciesInOrder = (items: ExportItem[]) => {
|
|
392
392
|
const exportNames = items.map((item) => item.name);
|
|
393
393
|
return items.some((item, idx) => {
|
|
394
394
|
const laterNames = new Set(exportNames.slice(idx + 1));
|
|
@@ -406,6 +406,37 @@ export const sortExports: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
406
406
|
});
|
|
407
407
|
};
|
|
408
408
|
|
|
409
|
+
const wouldCreateForwardDependencies = (
|
|
410
|
+
items: ExportItem[],
|
|
411
|
+
sortedItems: ExportItem[]
|
|
412
|
+
) => {
|
|
413
|
+
const sortedIndices = new Map(
|
|
414
|
+
sortedItems.map((item, idx) => [item.name, idx])
|
|
415
|
+
);
|
|
416
|
+
const exportNames = new Set(items.map((item) => item.name));
|
|
417
|
+
|
|
418
|
+
return items.some((item) => {
|
|
419
|
+
const itemIndex = sortedIndices.get(item.name);
|
|
420
|
+
if (itemIndex === undefined) {
|
|
421
|
+
return false;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const dependencies = getImmediateDependencyNames(item.node);
|
|
425
|
+
for (const dependency of dependencies) {
|
|
426
|
+
const dependencyIndex = exportNames.has(dependency)
|
|
427
|
+
? sortedIndices.get(dependency)
|
|
428
|
+
: undefined;
|
|
429
|
+
if (
|
|
430
|
+
dependencyIndex !== undefined &&
|
|
431
|
+
itemIndex < dependencyIndex
|
|
432
|
+
)
|
|
433
|
+
return true;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
return false;
|
|
437
|
+
});
|
|
438
|
+
};
|
|
439
|
+
|
|
409
440
|
const processExportBlock = (
|
|
410
441
|
block: TSESTree.ExportNamedDeclaration[]
|
|
411
442
|
) => {
|
|
@@ -424,12 +455,16 @@ export const sortExports: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
424
455
|
return;
|
|
425
456
|
}
|
|
426
457
|
|
|
427
|
-
if (
|
|
458
|
+
if (hasForwardDependenciesInOrder(items)) {
|
|
428
459
|
return;
|
|
429
460
|
}
|
|
430
461
|
|
|
431
462
|
const sortedItems = items.slice().sort(sortComparator);
|
|
432
463
|
|
|
464
|
+
if (wouldCreateForwardDependencies(items, sortedItems)) {
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
|
|
433
468
|
const expectedOrder = sortedItems
|
|
434
469
|
.map((item) => item.name)
|
|
435
470
|
.join(", ");
|