eslint-plugin-harlanzw 0.3.0 → 0.4.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.
- package/dist/index.mjs +539 -104
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { resolve } from 'node:path';
|
|
3
|
+
import process from 'node:process';
|
|
3
4
|
import { TextSourceCodeBase, ConfigCommentParser, Directive, VisitNodeStep } from '@eslint/plugin-kit';
|
|
4
5
|
import { AST_NODE_TYPES } from '@typescript-eslint/utils';
|
|
5
6
|
|
|
6
|
-
const version = "0.
|
|
7
|
+
const version = "0.4.0";
|
|
7
8
|
|
|
8
9
|
const STRENGTH_PATTERNS = {
|
|
9
10
|
strong: ["never", "must", "always", "under no circumstances", "absolutely", "required", "mandatory", "forbidden", "prohibited"],
|
|
@@ -1465,8 +1466,10 @@ const hasDocs = [
|
|
|
1465
1466
|
"link-require-descriptive-text",
|
|
1466
1467
|
"link-require-href",
|
|
1467
1468
|
"link-trailing-slash",
|
|
1469
|
+
"nuxt-no-random",
|
|
1468
1470
|
"nuxt-no-redundant-import-meta",
|
|
1469
1471
|
"nuxt-no-side-effects-in-setup",
|
|
1472
|
+
"nuxt-no-unsafe-date",
|
|
1470
1473
|
"nuxt-prefer-navigate-to-over-router-push-replace",
|
|
1471
1474
|
"nuxt-prefer-nuxt-link-over-router-link",
|
|
1472
1475
|
"use-composables-must-use-reactivity",
|
|
@@ -1633,6 +1636,57 @@ const VUE_REACTIVITY_APIS = /* @__PURE__ */ new Set([
|
|
|
1633
1636
|
"getCurrentScope",
|
|
1634
1637
|
"onScopeDispose"
|
|
1635
1638
|
]);
|
|
1639
|
+
const VUEUSE_REACTIVITY_APIS = /* @__PURE__ */ new Set([
|
|
1640
|
+
// Watch variants
|
|
1641
|
+
"whenever",
|
|
1642
|
+
"watchArray",
|
|
1643
|
+
"watchAtMost",
|
|
1644
|
+
"watchDebounced",
|
|
1645
|
+
"watchDeep",
|
|
1646
|
+
"watchIgnorable",
|
|
1647
|
+
"watchImmediate",
|
|
1648
|
+
"watchOnce",
|
|
1649
|
+
"watchPausable",
|
|
1650
|
+
"watchThrottled",
|
|
1651
|
+
"watchTriggerable",
|
|
1652
|
+
"watchWithFilter",
|
|
1653
|
+
"debouncedWatch",
|
|
1654
|
+
"throttledWatch",
|
|
1655
|
+
"until",
|
|
1656
|
+
// Computed variants
|
|
1657
|
+
"computedAsync",
|
|
1658
|
+
"computedEager",
|
|
1659
|
+
"computedInject",
|
|
1660
|
+
"computedWithControl",
|
|
1661
|
+
// Reactive utilities
|
|
1662
|
+
"reactiveComputed",
|
|
1663
|
+
"reactiveOmit",
|
|
1664
|
+
"reactivePick",
|
|
1665
|
+
"toReactive",
|
|
1666
|
+
// Ref utilities
|
|
1667
|
+
"controlledRef",
|
|
1668
|
+
"debouncedRef",
|
|
1669
|
+
"throttledRef",
|
|
1670
|
+
"refAutoReset",
|
|
1671
|
+
"refDebounced",
|
|
1672
|
+
"refDefault",
|
|
1673
|
+
"refThrottled",
|
|
1674
|
+
"refWithControl",
|
|
1675
|
+
"extendRef",
|
|
1676
|
+
"syncRef",
|
|
1677
|
+
"syncRefs",
|
|
1678
|
+
"templateRef",
|
|
1679
|
+
// State management
|
|
1680
|
+
"createGlobalState",
|
|
1681
|
+
"createInjectionState",
|
|
1682
|
+
"createSharedComposable",
|
|
1683
|
+
// Event/lifecycle hooks
|
|
1684
|
+
"onClickOutside",
|
|
1685
|
+
"onKeyStroke",
|
|
1686
|
+
"onLongPress",
|
|
1687
|
+
"onStartTyping",
|
|
1688
|
+
"createEventHook"
|
|
1689
|
+
]);
|
|
1636
1690
|
const SIDE_EFFECT_FUNCTIONS = /* @__PURE__ */ new Set([
|
|
1637
1691
|
// Timer functions
|
|
1638
1692
|
"setTimeout",
|
|
@@ -1744,10 +1798,99 @@ function trackVueImports(node, vueImports) {
|
|
|
1744
1798
|
});
|
|
1745
1799
|
}
|
|
1746
1800
|
}
|
|
1801
|
+
function trackNonVueImports(node, nonVueImports) {
|
|
1802
|
+
if (node.source.value !== "vue") {
|
|
1803
|
+
for (const spec of node.specifiers) {
|
|
1804
|
+
if (spec.type === "ImportSpecifier" && spec.imported.type === "Identifier") {
|
|
1805
|
+
nonVueImports.add(spec.imported.name);
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
function createReactivityChecker(vueImports, nonVueImports) {
|
|
1811
|
+
function isAutoImportedReactivityCall(node) {
|
|
1812
|
+
if (node.callee.type === "Identifier") {
|
|
1813
|
+
const name = node.callee.name;
|
|
1814
|
+
return (VUE_REACTIVITY_APIS.has(name) || VUEUSE_REACTIVITY_APIS.has(name)) && !nonVueImports.has(name);
|
|
1815
|
+
}
|
|
1816
|
+
return false;
|
|
1817
|
+
}
|
|
1818
|
+
function isReactiveLifecycleCall(node) {
|
|
1819
|
+
return node.callee.type === "Identifier" && /^tryOn[A-Z]/.test(node.callee.name);
|
|
1820
|
+
}
|
|
1821
|
+
function hasReactivityInExpression(expr) {
|
|
1822
|
+
if (!expr)
|
|
1823
|
+
return false;
|
|
1824
|
+
switch (expr.type) {
|
|
1825
|
+
case "CallExpression":
|
|
1826
|
+
if (isReactivityCall(expr, vueImports) || isAutoImportedReactivityCall(expr) || isComposableCall(expr) || isReactiveLifecycleCall(expr))
|
|
1827
|
+
return true;
|
|
1828
|
+
return expr.arguments.some((arg) => hasReactivityInArg(arg));
|
|
1829
|
+
case "NewExpression":
|
|
1830
|
+
return expr.arguments.some((arg) => hasReactivityInArg(arg));
|
|
1831
|
+
case "MemberExpression":
|
|
1832
|
+
return hasReactivityInExpression(expr.object);
|
|
1833
|
+
case "AssignmentExpression":
|
|
1834
|
+
return hasReactivityInExpression(expr.right);
|
|
1835
|
+
case "ObjectExpression":
|
|
1836
|
+
return expr.properties.some((prop) => prop.type === "Property" && hasReactivityInExpression(prop.value));
|
|
1837
|
+
case "ArrayExpression":
|
|
1838
|
+
return expr.elements.some((elem) => hasReactivityInExpression(elem));
|
|
1839
|
+
case "AwaitExpression":
|
|
1840
|
+
return hasReactivityInExpression(expr.argument);
|
|
1841
|
+
case "ConditionalExpression":
|
|
1842
|
+
return hasReactivityInExpression(expr.consequent) || hasReactivityInExpression(expr.alternate);
|
|
1843
|
+
case "LogicalExpression":
|
|
1844
|
+
return hasReactivityInExpression(expr.left) || hasReactivityInExpression(expr.right);
|
|
1845
|
+
default:
|
|
1846
|
+
return false;
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
function hasReactivityInArg(arg) {
|
|
1850
|
+
if (arg.type === "ArrowFunctionExpression" || arg.type === "FunctionExpression") {
|
|
1851
|
+
if (arg.body.type === "BlockStatement")
|
|
1852
|
+
return arg.body.body.some((stmt) => hasReactivityInStatement(stmt));
|
|
1853
|
+
return hasReactivityInExpression(arg.body);
|
|
1854
|
+
}
|
|
1855
|
+
if (arg.type === "SpreadElement")
|
|
1856
|
+
return hasReactivityInExpression(arg.argument);
|
|
1857
|
+
return hasReactivityInExpression(arg);
|
|
1858
|
+
}
|
|
1859
|
+
function hasReactivityInStatement(stmt) {
|
|
1860
|
+
if (!stmt)
|
|
1861
|
+
return false;
|
|
1862
|
+
switch (stmt.type) {
|
|
1863
|
+
case "ExpressionStatement":
|
|
1864
|
+
return hasReactivityInExpression(stmt.expression);
|
|
1865
|
+
case "VariableDeclaration":
|
|
1866
|
+
return stmt.declarations.some((decl) => hasReactivityInExpression(decl.init));
|
|
1867
|
+
case "ReturnStatement":
|
|
1868
|
+
return hasReactivityInExpression(stmt.argument);
|
|
1869
|
+
case "BlockStatement":
|
|
1870
|
+
return stmt.body.some((s) => hasReactivityInStatement(s));
|
|
1871
|
+
case "IfStatement":
|
|
1872
|
+
return hasReactivityInStatement(stmt.consequent) || (stmt.alternate ? hasReactivityInStatement(stmt.alternate) : false);
|
|
1873
|
+
case "WhileStatement":
|
|
1874
|
+
case "DoWhileStatement":
|
|
1875
|
+
return hasReactivityInStatement(stmt.body);
|
|
1876
|
+
case "ForStatement":
|
|
1877
|
+
case "ForInStatement":
|
|
1878
|
+
case "ForOfStatement":
|
|
1879
|
+
return hasReactivityInStatement(stmt.body);
|
|
1880
|
+
case "TryStatement":
|
|
1881
|
+
return hasReactivityInStatement(stmt.block) || (stmt.handler ? hasReactivityInStatement(stmt.handler.body) : false) || (stmt.finalizer ? hasReactivityInStatement(stmt.finalizer) : false);
|
|
1882
|
+
case "SwitchStatement":
|
|
1883
|
+
return stmt.cases.some((switchCase) => switchCase.consequent.some((s) => hasReactivityInStatement(s)));
|
|
1884
|
+
default:
|
|
1885
|
+
return false;
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
return { hasReactivityInStatement, hasReactivityInExpression };
|
|
1889
|
+
}
|
|
1747
1890
|
|
|
1748
|
-
const RULE_NAME$
|
|
1891
|
+
const RULE_NAME$l = "link-ascii-only";
|
|
1749
1892
|
const linkAsciiOnly = createEslintRule({
|
|
1750
|
-
name: RULE_NAME$
|
|
1893
|
+
name: RULE_NAME$l,
|
|
1751
1894
|
meta: {
|
|
1752
1895
|
type: "suggestion",
|
|
1753
1896
|
docs: {
|
|
@@ -1819,9 +1962,9 @@ const linkAsciiOnly = createEslintRule({
|
|
|
1819
1962
|
}
|
|
1820
1963
|
});
|
|
1821
1964
|
|
|
1822
|
-
const RULE_NAME$
|
|
1965
|
+
const RULE_NAME$k = "link-lowercase";
|
|
1823
1966
|
const linkLowercase = createEslintRule({
|
|
1824
|
-
name: RULE_NAME$
|
|
1967
|
+
name: RULE_NAME$k,
|
|
1825
1968
|
meta: {
|
|
1826
1969
|
type: "suggestion",
|
|
1827
1970
|
docs: {
|
|
@@ -1897,7 +2040,7 @@ const linkLowercase = createEslintRule({
|
|
|
1897
2040
|
}
|
|
1898
2041
|
});
|
|
1899
2042
|
|
|
1900
|
-
const RULE_NAME$
|
|
2043
|
+
const RULE_NAME$j = "link-no-double-slashes";
|
|
1901
2044
|
function fixDoubleSlashesInUrl(url) {
|
|
1902
2045
|
if (url.startsWith("//") || url.includes("://"))
|
|
1903
2046
|
return url;
|
|
@@ -1917,7 +2060,7 @@ function fixDoubleSlashesInUrl(url) {
|
|
|
1917
2060
|
return `${path.replace(/\/+/g, "/")}${search}${hash}`;
|
|
1918
2061
|
}
|
|
1919
2062
|
const linkNoDoubleSlashes = createEslintRule({
|
|
1920
|
-
name: RULE_NAME$
|
|
2063
|
+
name: RULE_NAME$j,
|
|
1921
2064
|
meta: {
|
|
1922
2065
|
type: "problem",
|
|
1923
2066
|
docs: {
|
|
@@ -1993,9 +2136,9 @@ const linkNoDoubleSlashes = createEslintRule({
|
|
|
1993
2136
|
}
|
|
1994
2137
|
});
|
|
1995
2138
|
|
|
1996
|
-
const RULE_NAME$
|
|
2139
|
+
const RULE_NAME$i = "link-no-underscores";
|
|
1997
2140
|
const linkNoUnderscores = createEslintRule({
|
|
1998
|
-
name: RULE_NAME$
|
|
2141
|
+
name: RULE_NAME$i,
|
|
1999
2142
|
meta: {
|
|
2000
2143
|
type: "suggestion",
|
|
2001
2144
|
docs: {
|
|
@@ -2069,9 +2212,9 @@ const linkNoUnderscores = createEslintRule({
|
|
|
2069
2212
|
}
|
|
2070
2213
|
});
|
|
2071
2214
|
|
|
2072
|
-
const RULE_NAME$
|
|
2215
|
+
const RULE_NAME$h = "link-no-whitespace";
|
|
2073
2216
|
const linkNoWhitespace = createEslintRule({
|
|
2074
|
-
name: RULE_NAME$
|
|
2217
|
+
name: RULE_NAME$h,
|
|
2075
2218
|
meta: {
|
|
2076
2219
|
type: "suggestion",
|
|
2077
2220
|
docs: {
|
|
@@ -2145,7 +2288,7 @@ const linkNoWhitespace = createEslintRule({
|
|
|
2145
2288
|
}
|
|
2146
2289
|
});
|
|
2147
2290
|
|
|
2148
|
-
const RULE_NAME$
|
|
2291
|
+
const RULE_NAME$g = "link-require-descriptive-text";
|
|
2149
2292
|
const BAD_LINK_TEXTS = /* @__PURE__ */ new Set([
|
|
2150
2293
|
"click here",
|
|
2151
2294
|
"click this",
|
|
@@ -2191,7 +2334,7 @@ function getVueLinkUrl(node) {
|
|
|
2191
2334
|
return null;
|
|
2192
2335
|
}
|
|
2193
2336
|
const linkRequireDescriptiveText = createEslintRule({
|
|
2194
|
-
name: RULE_NAME$
|
|
2337
|
+
name: RULE_NAME$g,
|
|
2195
2338
|
meta: {
|
|
2196
2339
|
type: "suggestion",
|
|
2197
2340
|
docs: {
|
|
@@ -2267,9 +2410,9 @@ const linkRequireDescriptiveText = createEslintRule({
|
|
|
2267
2410
|
}
|
|
2268
2411
|
});
|
|
2269
2412
|
|
|
2270
|
-
const RULE_NAME$
|
|
2413
|
+
const RULE_NAME$f = "link-require-href";
|
|
2271
2414
|
const linkRequireHref = createEslintRule({
|
|
2272
|
-
name: RULE_NAME$
|
|
2415
|
+
name: RULE_NAME$f,
|
|
2273
2416
|
meta: {
|
|
2274
2417
|
type: "problem",
|
|
2275
2418
|
docs: {
|
|
@@ -2334,12 +2477,12 @@ const linkRequireHref = createEslintRule({
|
|
|
2334
2477
|
}
|
|
2335
2478
|
});
|
|
2336
2479
|
|
|
2337
|
-
const RULE_NAME$
|
|
2480
|
+
const RULE_NAME$e = "link-trailing-slash";
|
|
2338
2481
|
function shouldSkipUrl(url) {
|
|
2339
2482
|
return url.startsWith("#") || url.includes(":") || url === "/" || url === "";
|
|
2340
2483
|
}
|
|
2341
2484
|
const linkTrailingSlash = createEslintRule({
|
|
2342
|
-
name: RULE_NAME$
|
|
2485
|
+
name: RULE_NAME$e,
|
|
2343
2486
|
meta: {
|
|
2344
2487
|
type: "suggestion",
|
|
2345
2488
|
docs: {
|
|
@@ -2454,9 +2597,9 @@ const linkTrailingSlash = createEslintRule({
|
|
|
2454
2597
|
}
|
|
2455
2598
|
});
|
|
2456
2599
|
|
|
2457
|
-
const RULE_NAME$
|
|
2600
|
+
const RULE_NAME$d = "nuxt-await-navigate-to";
|
|
2458
2601
|
const nuxtAwaitNavigateTo = createEslintRule({
|
|
2459
|
-
name: RULE_NAME$
|
|
2602
|
+
name: RULE_NAME$d,
|
|
2460
2603
|
meta: {
|
|
2461
2604
|
type: "problem",
|
|
2462
2605
|
docs: {
|
|
@@ -2505,9 +2648,188 @@ const nuxtAwaitNavigateTo = createEslintRule({
|
|
|
2505
2648
|
}
|
|
2506
2649
|
});
|
|
2507
2650
|
|
|
2508
|
-
const
|
|
2651
|
+
const CLIENT_LIFECYCLE_HOOKS = /* @__PURE__ */ new Set([
|
|
2652
|
+
"onMounted",
|
|
2653
|
+
"onBeforeMount",
|
|
2654
|
+
"onUpdated",
|
|
2655
|
+
"onBeforeUpdate",
|
|
2656
|
+
"onActivated",
|
|
2657
|
+
"onDeactivated"
|
|
2658
|
+
]);
|
|
2659
|
+
const DEFERRED_CALLBACK_FUNCTIONS = /* @__PURE__ */ new Set([
|
|
2660
|
+
"watch"
|
|
2661
|
+
]);
|
|
2662
|
+
const SERVER_HANDLER_FUNCTIONS = /* @__PURE__ */ new Set([
|
|
2663
|
+
"defineEventHandler",
|
|
2664
|
+
"defineCachedEventHandler",
|
|
2665
|
+
"defineNitroPlugin",
|
|
2666
|
+
"defineTask"
|
|
2667
|
+
]);
|
|
2668
|
+
function isNamedFunction(funcNode) {
|
|
2669
|
+
if (funcNode.type === "FunctionDeclaration")
|
|
2670
|
+
return true;
|
|
2671
|
+
const parent = funcNode.parent;
|
|
2672
|
+
if (!parent)
|
|
2673
|
+
return false;
|
|
2674
|
+
if (parent.type === "VariableDeclarator")
|
|
2675
|
+
return true;
|
|
2676
|
+
if (parent.type === "Property") {
|
|
2677
|
+
if (parent.key.type === "Identifier" && parent.key.name === "setup" && parent.parent?.type === "ObjectExpression" && parent.parent.parent?.type === "CallExpression" && parent.parent.parent.callee.type === "Identifier" && parent.parent.parent.callee.name === "defineComponent") {
|
|
2678
|
+
return false;
|
|
2679
|
+
}
|
|
2680
|
+
return true;
|
|
2681
|
+
}
|
|
2682
|
+
if (parent.type === "MethodDefinition")
|
|
2683
|
+
return true;
|
|
2684
|
+
if (parent.type === "AssignmentExpression" && parent.left.type === "Identifier")
|
|
2685
|
+
return true;
|
|
2686
|
+
return false;
|
|
2687
|
+
}
|
|
2688
|
+
function executedDuringSetup(node) {
|
|
2689
|
+
let current = node.parent;
|
|
2690
|
+
while (current) {
|
|
2691
|
+
if (current.type === "ArrowFunctionExpression" || current.type === "FunctionExpression" || current.type === "FunctionDeclaration") {
|
|
2692
|
+
if ((current.type === "ArrowFunctionExpression" || current.type === "FunctionExpression") && current.parent?.type === "CallExpression" && current.parent.callee.type === "Identifier") {
|
|
2693
|
+
const calleeName = current.parent.callee.name;
|
|
2694
|
+
if (CLIENT_LIFECYCLE_HOOKS.has(calleeName))
|
|
2695
|
+
return false;
|
|
2696
|
+
if (SERVER_HANDLER_FUNCTIONS.has(calleeName))
|
|
2697
|
+
return false;
|
|
2698
|
+
if (DEFERRED_CALLBACK_FUNCTIONS.has(calleeName)) {
|
|
2699
|
+
const args = current.parent.arguments;
|
|
2700
|
+
const argIndex = args.indexOf(current);
|
|
2701
|
+
if (argIndex > 0)
|
|
2702
|
+
return false;
|
|
2703
|
+
}
|
|
2704
|
+
}
|
|
2705
|
+
if (isNamedFunction(current))
|
|
2706
|
+
return false;
|
|
2707
|
+
}
|
|
2708
|
+
if (current.type === "Program")
|
|
2709
|
+
return true;
|
|
2710
|
+
current = current.parent;
|
|
2711
|
+
}
|
|
2712
|
+
return false;
|
|
2713
|
+
}
|
|
2714
|
+
function isClientGuardTest(test) {
|
|
2715
|
+
if (test.type === "MemberExpression" && test.object.type === "MetaProperty" && test.property.type === "Identifier" && test.property.name === "client") {
|
|
2716
|
+
return true;
|
|
2717
|
+
}
|
|
2718
|
+
if (test.type === "MemberExpression" && test.object.type === "Identifier" && test.object.name === "process" && test.property.type === "Identifier" && test.property.name === "client") {
|
|
2719
|
+
return true;
|
|
2720
|
+
}
|
|
2721
|
+
if (test.type === "BinaryExpression" && test.left.type === "UnaryExpression" && test.left.operator === "typeof" && test.left.argument.type === "Identifier" && test.left.argument.name === "window") {
|
|
2722
|
+
return true;
|
|
2723
|
+
}
|
|
2724
|
+
return false;
|
|
2725
|
+
}
|
|
2726
|
+
function isInsideClientGuard(node) {
|
|
2727
|
+
let parent = node.parent;
|
|
2728
|
+
while (parent) {
|
|
2729
|
+
if (parent.type === "IfStatement" && isClientGuardTest(parent.test))
|
|
2730
|
+
return isDescendantOfConsequent(node, parent);
|
|
2731
|
+
if (parent.type === "ConditionalExpression" && isClientGuardTest(parent.test)) {
|
|
2732
|
+
return isInConsequentBranch(node, parent);
|
|
2733
|
+
}
|
|
2734
|
+
parent = parent.parent;
|
|
2735
|
+
}
|
|
2736
|
+
return false;
|
|
2737
|
+
}
|
|
2738
|
+
function isDescendantOfConsequent(node, ifStmt) {
|
|
2739
|
+
let current = node;
|
|
2740
|
+
while (current && current !== ifStmt) {
|
|
2741
|
+
if (current === ifStmt.consequent)
|
|
2742
|
+
return true;
|
|
2743
|
+
current = current.parent;
|
|
2744
|
+
}
|
|
2745
|
+
return false;
|
|
2746
|
+
}
|
|
2747
|
+
function isInConsequentBranch(node, ternary) {
|
|
2748
|
+
let current = node;
|
|
2749
|
+
while (current && current !== ternary) {
|
|
2750
|
+
if (current === ternary.consequent)
|
|
2751
|
+
return true;
|
|
2752
|
+
if (current === ternary.alternate)
|
|
2753
|
+
return false;
|
|
2754
|
+
current = current.parent;
|
|
2755
|
+
}
|
|
2756
|
+
return false;
|
|
2757
|
+
}
|
|
2758
|
+
|
|
2759
|
+
const RULE_NAME$c = "nuxt-no-random";
|
|
2760
|
+
function isMathRandomCall(node) {
|
|
2761
|
+
return node.callee.type === "MemberExpression" && node.callee.object.type === "Identifier" && node.callee.object.name === "Math" && node.callee.property.type === "Identifier" && node.callee.property.name === "random";
|
|
2762
|
+
}
|
|
2763
|
+
function isCryptoRandomCall(node) {
|
|
2764
|
+
if (node.callee.type !== "MemberExpression" || node.callee.property.type !== "Identifier")
|
|
2765
|
+
return false;
|
|
2766
|
+
const method = node.callee.property.name;
|
|
2767
|
+
if (method !== "randomUUID" && method !== "getRandomValues")
|
|
2768
|
+
return false;
|
|
2769
|
+
const obj = node.callee.object;
|
|
2770
|
+
if (obj.type === "Identifier" && obj.name === "crypto")
|
|
2771
|
+
return true;
|
|
2772
|
+
if (obj.type === "MemberExpression" && obj.object.type === "Identifier" && (obj.object.name === "globalThis" || obj.object.name === "window") && obj.property.type === "Identifier" && obj.property.name === "crypto") {
|
|
2773
|
+
return true;
|
|
2774
|
+
}
|
|
2775
|
+
return false;
|
|
2776
|
+
}
|
|
2777
|
+
function reportRandom(context, node, template) {
|
|
2778
|
+
if (isMathRandomCall(node)) {
|
|
2779
|
+
context.report({ node, messageId: template ? "noMathRandomTemplate" : "noMathRandom" });
|
|
2780
|
+
} else if (isCryptoRandomCall(node)) {
|
|
2781
|
+
const method = node.callee.property;
|
|
2782
|
+
context.report({
|
|
2783
|
+
node,
|
|
2784
|
+
messageId: template ? "noCryptoRandomTemplate" : "noCryptoRandom",
|
|
2785
|
+
data: { method: `crypto.${method.name}` }
|
|
2786
|
+
});
|
|
2787
|
+
}
|
|
2788
|
+
}
|
|
2789
|
+
const nuxtNoRandom = createEslintRule({
|
|
2790
|
+
name: RULE_NAME$c,
|
|
2791
|
+
meta: {
|
|
2792
|
+
type: "problem",
|
|
2793
|
+
docs: {
|
|
2794
|
+
description: "disallow Math.random() and crypto random APIs in SSR-rendered code to prevent hydration mismatches"
|
|
2795
|
+
},
|
|
2796
|
+
schema: [],
|
|
2797
|
+
messages: {
|
|
2798
|
+
noMathRandom: "Math.random() produces different values on server and client, causing hydration mismatches. Move to onMounted() or guard with `if (import.meta.client)`.",
|
|
2799
|
+
noCryptoRandom: "{{method}}() produces different values on server and client, causing hydration mismatches. Move to onMounted() or guard with `if (import.meta.client)`.",
|
|
2800
|
+
noMathRandomTemplate: "Math.random() in templates produces different values on server and client, causing hydration mismatches. Move to a computed property backed by onMounted().",
|
|
2801
|
+
noCryptoRandomTemplate: "{{method}}() in templates produces different values on server and client, causing hydration mismatches. Move to a computed property backed by onMounted()."
|
|
2802
|
+
}
|
|
2803
|
+
},
|
|
2804
|
+
defaultOptions: [],
|
|
2805
|
+
create: (context) => {
|
|
2806
|
+
function checkScript(node) {
|
|
2807
|
+
if (!isMathRandomCall(node) && !isCryptoRandomCall(node))
|
|
2808
|
+
return;
|
|
2809
|
+
if (!executedDuringSetup(node))
|
|
2810
|
+
return;
|
|
2811
|
+
if (isInsideClientGuard(node))
|
|
2812
|
+
return;
|
|
2813
|
+
reportRandom(context, node, false);
|
|
2814
|
+
}
|
|
2815
|
+
if (isVueParser(context)) {
|
|
2816
|
+
return defineTemplateBodyVisitor(context, {
|
|
2817
|
+
// Template expressions — always execute during render
|
|
2818
|
+
CallExpression(node) {
|
|
2819
|
+
if (isMathRandomCall(node) || isCryptoRandomCall(node))
|
|
2820
|
+
reportRandom(context, node, true);
|
|
2821
|
+
}
|
|
2822
|
+
}, {
|
|
2823
|
+
CallExpression: checkScript
|
|
2824
|
+
});
|
|
2825
|
+
}
|
|
2826
|
+
return { CallExpression: checkScript };
|
|
2827
|
+
}
|
|
2828
|
+
});
|
|
2829
|
+
|
|
2830
|
+
const RULE_NAME$b = "nuxt-no-redundant-import-meta";
|
|
2509
2831
|
const nuxtNoRedundantImportMeta = createEslintRule({
|
|
2510
|
-
name: RULE_NAME$
|
|
2832
|
+
name: RULE_NAME$b,
|
|
2511
2833
|
meta: {
|
|
2512
2834
|
type: "problem",
|
|
2513
2835
|
docs: {
|
|
@@ -2544,7 +2866,7 @@ const nuxtNoRedundantImportMeta = createEslintRule({
|
|
|
2544
2866
|
}
|
|
2545
2867
|
});
|
|
2546
2868
|
|
|
2547
|
-
const RULE_NAME$
|
|
2869
|
+
const RULE_NAME$a = "nuxt-no-side-effects-in-async-data-handler";
|
|
2548
2870
|
const SIDE_EFFECT_PATTERNS = /* @__PURE__ */ new Set([
|
|
2549
2871
|
// Store/State mutations
|
|
2550
2872
|
"$patch",
|
|
@@ -2652,7 +2974,7 @@ function findSideEffectsInFunction(functionNode) {
|
|
|
2652
2974
|
return sideEffects;
|
|
2653
2975
|
}
|
|
2654
2976
|
const nuxtNoSideEffectsInAsyncDataHandler = createEslintRule({
|
|
2655
|
-
name: RULE_NAME$
|
|
2977
|
+
name: RULE_NAME$a,
|
|
2656
2978
|
meta: {
|
|
2657
2979
|
type: "problem",
|
|
2658
2980
|
docs: {
|
|
@@ -2743,9 +3065,9 @@ ${indent}${callOnceBlock}`)
|
|
|
2743
3065
|
}
|
|
2744
3066
|
});
|
|
2745
3067
|
|
|
2746
|
-
const RULE_NAME$
|
|
3068
|
+
const RULE_NAME$9 = "nuxt-no-side-effects-in-setup";
|
|
2747
3069
|
const nuxtNoSideEffectsInSetup = createEslintRule({
|
|
2748
|
-
name: RULE_NAME$
|
|
3070
|
+
name: RULE_NAME$9,
|
|
2749
3071
|
meta: {
|
|
2750
3072
|
type: "problem",
|
|
2751
3073
|
docs: {
|
|
@@ -2838,9 +3160,82 @@ ${indent}})`;
|
|
|
2838
3160
|
}
|
|
2839
3161
|
});
|
|
2840
3162
|
|
|
2841
|
-
const RULE_NAME$
|
|
3163
|
+
const RULE_NAME$8 = "nuxt-no-unsafe-date";
|
|
3164
|
+
function isDateNowCall(node) {
|
|
3165
|
+
return node.callee.type === "MemberExpression" && node.callee.object.type === "Identifier" && node.callee.object.name === "Date" && node.callee.property.type === "Identifier" && node.callee.property.name === "now";
|
|
3166
|
+
}
|
|
3167
|
+
function isDateFunctionCall(node) {
|
|
3168
|
+
return node.callee.type === "Identifier" && node.callee.name === "Date" && node.parent?.type !== "NewExpression";
|
|
3169
|
+
}
|
|
3170
|
+
function isNewDateCall(node) {
|
|
3171
|
+
return node.callee.type === "Identifier" && node.callee.name === "Date" && node.arguments.length === 0;
|
|
3172
|
+
}
|
|
3173
|
+
const nuxtNoUnsafeDate = createEslintRule({
|
|
3174
|
+
name: RULE_NAME$8,
|
|
3175
|
+
meta: {
|
|
3176
|
+
type: "problem",
|
|
3177
|
+
docs: {
|
|
3178
|
+
description: "disallow Date.now() and new Date() in SSR-rendered code to prevent hydration mismatches"
|
|
3179
|
+
},
|
|
3180
|
+
schema: [],
|
|
3181
|
+
messages: {
|
|
3182
|
+
noDateNow: "Date.now() returns different timestamps on server and client, causing hydration mismatches. Use the <NuxtTime> component or move to onMounted().",
|
|
3183
|
+
noNewDate: "new Date() returns different timestamps on server and client, causing hydration mismatches. Use the <NuxtTime> component or move to onMounted().",
|
|
3184
|
+
noDateCall: "Date() returns the current time as a string, which differs on server and client. Use the <NuxtTime> component or move to onMounted().",
|
|
3185
|
+
noDateNowTemplate: "Date.now() in templates returns different timestamps on server and client. Use the <NuxtTime> component instead.",
|
|
3186
|
+
noNewDateTemplate: "new Date() in templates returns different timestamps on server and client. Use the <NuxtTime> component instead.",
|
|
3187
|
+
noDateCallTemplate: "Date() in templates returns the current time, which differs on server and client. Use the <NuxtTime> component instead."
|
|
3188
|
+
}
|
|
3189
|
+
},
|
|
3190
|
+
defaultOptions: [],
|
|
3191
|
+
create: (context) => {
|
|
3192
|
+
function checkCallScript(node) {
|
|
3193
|
+
const isNow = isDateNowCall(node);
|
|
3194
|
+
const isCall = !isNow && isDateFunctionCall(node);
|
|
3195
|
+
if (!isNow && !isCall)
|
|
3196
|
+
return;
|
|
3197
|
+
if (!executedDuringSetup(node))
|
|
3198
|
+
return;
|
|
3199
|
+
if (isInsideClientGuard(node))
|
|
3200
|
+
return;
|
|
3201
|
+
context.report({ node, messageId: isNow ? "noDateNow" : "noDateCall" });
|
|
3202
|
+
}
|
|
3203
|
+
function checkNewScript(node) {
|
|
3204
|
+
if (!isNewDateCall(node))
|
|
3205
|
+
return;
|
|
3206
|
+
if (!executedDuringSetup(node))
|
|
3207
|
+
return;
|
|
3208
|
+
if (isInsideClientGuard(node))
|
|
3209
|
+
return;
|
|
3210
|
+
context.report({ node, messageId: "noNewDate" });
|
|
3211
|
+
}
|
|
3212
|
+
if (isVueParser(context)) {
|
|
3213
|
+
return defineTemplateBodyVisitor(context, {
|
|
3214
|
+
CallExpression(node) {
|
|
3215
|
+
if (isDateNowCall(node))
|
|
3216
|
+
context.report({ node, messageId: "noDateNowTemplate" });
|
|
3217
|
+
else if (isDateFunctionCall(node))
|
|
3218
|
+
context.report({ node, messageId: "noDateCallTemplate" });
|
|
3219
|
+
},
|
|
3220
|
+
NewExpression(node) {
|
|
3221
|
+
if (isNewDateCall(node))
|
|
3222
|
+
context.report({ node, messageId: "noNewDateTemplate" });
|
|
3223
|
+
}
|
|
3224
|
+
}, {
|
|
3225
|
+
CallExpression: checkCallScript,
|
|
3226
|
+
NewExpression: checkNewScript
|
|
3227
|
+
});
|
|
3228
|
+
}
|
|
3229
|
+
return {
|
|
3230
|
+
CallExpression: checkCallScript,
|
|
3231
|
+
NewExpression: checkNewScript
|
|
3232
|
+
};
|
|
3233
|
+
}
|
|
3234
|
+
});
|
|
3235
|
+
|
|
3236
|
+
const RULE_NAME$7 = "nuxt-prefer-navigate-to-over-router-push-replace";
|
|
2842
3237
|
const nuxtPreferNavigateToOverRouterPushReplace = createEslintRule({
|
|
2843
|
-
name: RULE_NAME$
|
|
3238
|
+
name: RULE_NAME$7,
|
|
2844
3239
|
meta: {
|
|
2845
3240
|
type: "suggestion",
|
|
2846
3241
|
docs: {
|
|
@@ -2903,9 +3298,9 @@ const nuxtPreferNavigateToOverRouterPushReplace = createEslintRule({
|
|
|
2903
3298
|
}
|
|
2904
3299
|
});
|
|
2905
3300
|
|
|
2906
|
-
const RULE_NAME$
|
|
3301
|
+
const RULE_NAME$6 = "nuxt-prefer-nuxt-link-over-router-link";
|
|
2907
3302
|
const nuxtPreferNuxtLinkOverRouterLink = createEslintRule({
|
|
2908
|
-
name: RULE_NAME$
|
|
3303
|
+
name: RULE_NAME$6,
|
|
2909
3304
|
meta: {
|
|
2910
3305
|
type: "suggestion",
|
|
2911
3306
|
docs: {
|
|
@@ -2976,9 +3371,9 @@ const nuxtPreferNuxtLinkOverRouterLink = createEslintRule({
|
|
|
2976
3371
|
}
|
|
2977
3372
|
});
|
|
2978
3373
|
|
|
2979
|
-
const RULE_NAME$
|
|
3374
|
+
const RULE_NAME$5 = "vue-no-faux-composables";
|
|
2980
3375
|
const vueNoFauxComposables = createEslintRule({
|
|
2981
|
-
name: RULE_NAME$
|
|
3376
|
+
name: RULE_NAME$5,
|
|
2982
3377
|
meta: {
|
|
2983
3378
|
type: "problem",
|
|
2984
3379
|
docs: {
|
|
@@ -2994,64 +3389,16 @@ const vueNoFauxComposables = createEslintRule({
|
|
|
2994
3389
|
const vueImports = /* @__PURE__ */ new Set();
|
|
2995
3390
|
const nonVueImports = /* @__PURE__ */ new Set();
|
|
2996
3391
|
const composableFunctions = /* @__PURE__ */ new Map();
|
|
2997
|
-
|
|
2998
|
-
if (node.callee.type === "Identifier") {
|
|
2999
|
-
const name = node.callee.name;
|
|
3000
|
-
return VUE_REACTIVITY_APIS.has(name) && !nonVueImports.has(name);
|
|
3001
|
-
}
|
|
3002
|
-
return false;
|
|
3003
|
-
}
|
|
3004
|
-
function hasReactivityInStatement(stmt) {
|
|
3005
|
-
if (!stmt)
|
|
3006
|
-
return false;
|
|
3007
|
-
switch (stmt.type) {
|
|
3008
|
-
case "ExpressionStatement":
|
|
3009
|
-
return hasReactivityInExpression(stmt.expression);
|
|
3010
|
-
case "VariableDeclaration":
|
|
3011
|
-
return stmt.declarations.some((decl) => hasReactivityInExpression(decl.init));
|
|
3012
|
-
case "ReturnStatement":
|
|
3013
|
-
return hasReactivityInExpression(stmt.argument);
|
|
3014
|
-
case "BlockStatement":
|
|
3015
|
-
return stmt.body.some((s) => hasReactivityInStatement(s));
|
|
3016
|
-
case "IfStatement":
|
|
3017
|
-
return hasReactivityInStatement(stmt.consequent) || (stmt.alternate ? hasReactivityInStatement(stmt.alternate) : false);
|
|
3018
|
-
case "WhileStatement":
|
|
3019
|
-
case "DoWhileStatement":
|
|
3020
|
-
return hasReactivityInStatement(stmt.body);
|
|
3021
|
-
case "ForStatement":
|
|
3022
|
-
case "ForInStatement":
|
|
3023
|
-
case "ForOfStatement":
|
|
3024
|
-
return hasReactivityInStatement(stmt.body);
|
|
3025
|
-
case "TryStatement":
|
|
3026
|
-
return hasReactivityInStatement(stmt.block) || (stmt.handler ? hasReactivityInStatement(stmt.handler.body) : false) || (stmt.finalizer ? hasReactivityInStatement(stmt.finalizer) : false);
|
|
3027
|
-
case "SwitchStatement":
|
|
3028
|
-
return stmt.cases.some((switchCase) => switchCase.consequent.some((s) => hasReactivityInStatement(s)));
|
|
3029
|
-
default:
|
|
3030
|
-
return false;
|
|
3031
|
-
}
|
|
3032
|
-
}
|
|
3033
|
-
function hasReactivityInExpression(expr) {
|
|
3034
|
-
if (!expr)
|
|
3035
|
-
return false;
|
|
3036
|
-
switch (expr.type) {
|
|
3037
|
-
case "CallExpression":
|
|
3038
|
-
if (isReactivityCall(expr, vueImports) || isAutoImportedReactivityCall(expr) || isComposableCall(expr))
|
|
3039
|
-
return true;
|
|
3040
|
-
return false;
|
|
3041
|
-
case "ObjectExpression":
|
|
3042
|
-
return expr.properties.some((prop) => prop.type === "Property" && hasReactivityInExpression(prop.value));
|
|
3043
|
-
case "ArrayExpression":
|
|
3044
|
-
return expr.elements.some((elem) => hasReactivityInExpression(elem));
|
|
3045
|
-
case "AwaitExpression":
|
|
3046
|
-
return hasReactivityInExpression(expr.argument);
|
|
3047
|
-
default:
|
|
3048
|
-
return false;
|
|
3049
|
-
}
|
|
3050
|
-
}
|
|
3392
|
+
const { hasReactivityInStatement, hasReactivityInExpression } = createReactivityChecker(vueImports, nonVueImports);
|
|
3051
3393
|
function checkFunctionForReactivity(functionNode, functionName) {
|
|
3052
|
-
if (!functionNode.body
|
|
3394
|
+
if (!functionNode.body)
|
|
3053
3395
|
return;
|
|
3054
|
-
|
|
3396
|
+
let hasReactivity;
|
|
3397
|
+
if (functionNode.body.type === "BlockStatement") {
|
|
3398
|
+
hasReactivity = functionNode.body.body.some((stmt) => hasReactivityInStatement(stmt));
|
|
3399
|
+
} else {
|
|
3400
|
+
hasReactivity = hasReactivityInExpression(functionNode.body);
|
|
3401
|
+
}
|
|
3055
3402
|
if (!hasReactivity) {
|
|
3056
3403
|
context.report({
|
|
3057
3404
|
node: functionNode,
|
|
@@ -3068,13 +3415,7 @@ const vueNoFauxComposables = createEslintRule({
|
|
|
3068
3415
|
},
|
|
3069
3416
|
ImportDeclaration(node) {
|
|
3070
3417
|
trackVueImports(node, vueImports);
|
|
3071
|
-
|
|
3072
|
-
for (const spec of node.specifiers) {
|
|
3073
|
-
if (spec.type === "ImportSpecifier" && spec.imported.type === "Identifier") {
|
|
3074
|
-
nonVueImports.add(spec.imported.name);
|
|
3075
|
-
}
|
|
3076
|
-
}
|
|
3077
|
-
}
|
|
3418
|
+
trackNonVueImports(node, nonVueImports);
|
|
3078
3419
|
},
|
|
3079
3420
|
"Program:exit": function() {
|
|
3080
3421
|
for (const [name, functionNode] of composableFunctions)
|
|
@@ -3098,9 +3439,9 @@ const vueNoFauxComposables = createEslintRule({
|
|
|
3098
3439
|
}
|
|
3099
3440
|
});
|
|
3100
3441
|
|
|
3101
|
-
const RULE_NAME$
|
|
3442
|
+
const RULE_NAME$4 = "vue-no-nested-reactivity";
|
|
3102
3443
|
const vueNoNestedReactivity = createEslintRule({
|
|
3103
|
-
name: RULE_NAME$
|
|
3444
|
+
name: RULE_NAME$4,
|
|
3104
3445
|
meta: {
|
|
3105
3446
|
type: "problem",
|
|
3106
3447
|
docs: {
|
|
@@ -3345,9 +3686,9 @@ const vueNoNestedReactivity = createEslintRule({
|
|
|
3345
3686
|
}
|
|
3346
3687
|
});
|
|
3347
3688
|
|
|
3348
|
-
const RULE_NAME$
|
|
3689
|
+
const RULE_NAME$3 = "vue-no-passing-refs-as-props";
|
|
3349
3690
|
const vueNoPassingRefsAsProps = createEslintRule({
|
|
3350
|
-
name: RULE_NAME$
|
|
3691
|
+
name: RULE_NAME$3,
|
|
3351
3692
|
meta: {
|
|
3352
3693
|
type: "problem",
|
|
3353
3694
|
docs: {
|
|
@@ -3454,9 +3795,9 @@ const vueNoReactiveDestructuring = createEslintRule({
|
|
|
3454
3795
|
}
|
|
3455
3796
|
});
|
|
3456
3797
|
|
|
3457
|
-
const RULE_NAME$
|
|
3798
|
+
const RULE_NAME$2 = "vue-no-ref-access-in-templates";
|
|
3458
3799
|
const vueNoRefAccessInTemplates = createEslintRule({
|
|
3459
|
-
name: RULE_NAME$
|
|
3800
|
+
name: RULE_NAME$2,
|
|
3460
3801
|
meta: {
|
|
3461
3802
|
type: "suggestion",
|
|
3462
3803
|
docs: {
|
|
@@ -3566,9 +3907,9 @@ const vueNoRefAccessInTemplates = createEslintRule({
|
|
|
3566
3907
|
}
|
|
3567
3908
|
});
|
|
3568
3909
|
|
|
3569
|
-
const RULE_NAME = "vue-no-torefs-on-props";
|
|
3910
|
+
const RULE_NAME$1 = "vue-no-torefs-on-props";
|
|
3570
3911
|
const vueNoTorefsOnProps = createEslintRule({
|
|
3571
|
-
name: RULE_NAME,
|
|
3912
|
+
name: RULE_NAME$1,
|
|
3572
3913
|
meta: {
|
|
3573
3914
|
type: "suggestion",
|
|
3574
3915
|
docs: {
|
|
@@ -3629,6 +3970,94 @@ const vueNoTorefsOnProps = createEslintRule({
|
|
|
3629
3970
|
}
|
|
3630
3971
|
});
|
|
3631
3972
|
|
|
3973
|
+
const RULE_NAME = "vue-require-composable-prefix";
|
|
3974
|
+
const vueRequireComposablePrefix = createEslintRule({
|
|
3975
|
+
name: RULE_NAME,
|
|
3976
|
+
meta: {
|
|
3977
|
+
type: "suggestion",
|
|
3978
|
+
docs: {
|
|
3979
|
+
description: "enforce use* prefix for functions that use Vue reactivity"
|
|
3980
|
+
},
|
|
3981
|
+
hasSuggestions: true,
|
|
3982
|
+
schema: [],
|
|
3983
|
+
messages: {
|
|
3984
|
+
requirePrefix: 'Function "{{name}}" uses Vue reactivity \u2014 consider renaming to "use{{Name}}"'
|
|
3985
|
+
}
|
|
3986
|
+
},
|
|
3987
|
+
defaultOptions: [],
|
|
3988
|
+
create: (context) => {
|
|
3989
|
+
const vueImports = /* @__PURE__ */ new Set();
|
|
3990
|
+
const nonVueImports = /* @__PURE__ */ new Set();
|
|
3991
|
+
const candidateFunctions = /* @__PURE__ */ new Map();
|
|
3992
|
+
const { hasReactivityInStatement, hasReactivityInExpression } = createReactivityChecker(vueImports, nonVueImports);
|
|
3993
|
+
function isExcludedName(name) {
|
|
3994
|
+
return /^define[A-Z]/.test(name) || name === "setup";
|
|
3995
|
+
}
|
|
3996
|
+
function checkFunctionForReactivity(functionNode, idNode, functionName) {
|
|
3997
|
+
if (!functionNode.body)
|
|
3998
|
+
return;
|
|
3999
|
+
let hasReactivity;
|
|
4000
|
+
if (functionNode.body.type === "BlockStatement") {
|
|
4001
|
+
hasReactivity = functionNode.body.body.some((stmt) => hasReactivityInStatement(stmt));
|
|
4002
|
+
} else {
|
|
4003
|
+
hasReactivity = hasReactivityInExpression(functionNode.body);
|
|
4004
|
+
}
|
|
4005
|
+
if (hasReactivity) {
|
|
4006
|
+
const capitalizedName = `${functionName.charAt(0).toUpperCase()}${functionName.slice(1)}`;
|
|
4007
|
+
const suggestedName = `use${capitalizedName}`;
|
|
4008
|
+
context.report({
|
|
4009
|
+
node: idNode,
|
|
4010
|
+
messageId: "requirePrefix",
|
|
4011
|
+
data: { name: functionName, Name: capitalizedName },
|
|
4012
|
+
suggest: [
|
|
4013
|
+
{
|
|
4014
|
+
messageId: "requirePrefix",
|
|
4015
|
+
data: { name: functionName, Name: capitalizedName },
|
|
4016
|
+
fix(fixer) {
|
|
4017
|
+
return fixer.replaceText(idNode, suggestedName);
|
|
4018
|
+
}
|
|
4019
|
+
}
|
|
4020
|
+
]
|
|
4021
|
+
});
|
|
4022
|
+
}
|
|
4023
|
+
}
|
|
4024
|
+
return {
|
|
4025
|
+
Program() {
|
|
4026
|
+
vueImports.clear();
|
|
4027
|
+
nonVueImports.clear();
|
|
4028
|
+
candidateFunctions.clear();
|
|
4029
|
+
},
|
|
4030
|
+
ImportDeclaration(node) {
|
|
4031
|
+
trackVueImports(node, vueImports);
|
|
4032
|
+
trackNonVueImports(node, nonVueImports);
|
|
4033
|
+
},
|
|
4034
|
+
"Program:exit": function() {
|
|
4035
|
+
for (const [name, { node, idNode }] of candidateFunctions)
|
|
4036
|
+
checkFunctionForReactivity(node, idNode, name);
|
|
4037
|
+
},
|
|
4038
|
+
FunctionDeclaration(node) {
|
|
4039
|
+
if (!node.id || isComposableName(node.id.name) || isExcludedName(node.id.name))
|
|
4040
|
+
return;
|
|
4041
|
+
if (node.parent.type === "Program" || node.parent.type === "ExportNamedDeclaration")
|
|
4042
|
+
candidateFunctions.set(node.id.name, { node, idNode: node.id });
|
|
4043
|
+
},
|
|
4044
|
+
VariableDeclarator(node) {
|
|
4045
|
+
if (node.id.type !== "Identifier" || isComposableName(node.id.name) || isExcludedName(node.id.name)) {
|
|
4046
|
+
return;
|
|
4047
|
+
}
|
|
4048
|
+
if (node.init?.type !== "FunctionExpression" && node.init?.type !== "ArrowFunctionExpression")
|
|
4049
|
+
return;
|
|
4050
|
+
const varDecl = node.parent;
|
|
4051
|
+
if (varDecl?.type !== "VariableDeclaration")
|
|
4052
|
+
return;
|
|
4053
|
+
if (varDecl.parent?.type !== "Program" && varDecl.parent?.type !== "ExportNamedDeclaration")
|
|
4054
|
+
return;
|
|
4055
|
+
candidateFunctions.set(node.id.name, { node: node.init, idNode: node.id });
|
|
4056
|
+
}
|
|
4057
|
+
};
|
|
4058
|
+
}
|
|
4059
|
+
});
|
|
4060
|
+
|
|
3632
4061
|
const plugin = {
|
|
3633
4062
|
meta: {
|
|
3634
4063
|
name: "harlanzw",
|
|
@@ -3648,9 +4077,11 @@ const plugin = {
|
|
|
3648
4077
|
"link-require-href": linkRequireHref,
|
|
3649
4078
|
"link-trailing-slash": linkTrailingSlash,
|
|
3650
4079
|
"nuxt-await-navigate-to": nuxtAwaitNavigateTo,
|
|
4080
|
+
"nuxt-no-random": nuxtNoRandom,
|
|
3651
4081
|
"nuxt-no-redundant-import-meta": nuxtNoRedundantImportMeta,
|
|
3652
4082
|
"nuxt-no-side-effects-in-async-data-handler": nuxtNoSideEffectsInAsyncDataHandler,
|
|
3653
4083
|
"nuxt-no-side-effects-in-setup": nuxtNoSideEffectsInSetup,
|
|
4084
|
+
"nuxt-no-unsafe-date": nuxtNoUnsafeDate,
|
|
3654
4085
|
"nuxt-prefer-navigate-to-over-router-push-replace": nuxtPreferNavigateToOverRouterPushReplace,
|
|
3655
4086
|
"nuxt-prefer-nuxt-link-over-router-link": nuxtPreferNuxtLinkOverRouterLink,
|
|
3656
4087
|
"prompt-ambiguous-quantifier": promptAmbiguousQuantifier,
|
|
@@ -3679,7 +4110,8 @@ const plugin = {
|
|
|
3679
4110
|
"vue-no-passing-refs-as-props": vueNoPassingRefsAsProps,
|
|
3680
4111
|
"vue-no-reactive-destructuring": vueNoReactiveDestructuring,
|
|
3681
4112
|
"vue-no-ref-access-in-templates": vueNoRefAccessInTemplates,
|
|
3682
|
-
"vue-no-torefs-on-props": vueNoTorefsOnProps
|
|
4113
|
+
"vue-no-torefs-on-props": vueNoTorefsOnProps,
|
|
4114
|
+
"vue-require-composable-prefix": vueRequireComposablePrefix
|
|
3683
4115
|
},
|
|
3684
4116
|
configs: {}
|
|
3685
4117
|
};
|
|
@@ -3776,9 +4208,11 @@ plugin.configs.nuxt = [
|
|
|
3776
4208
|
plugins: { harlanzw: plugin },
|
|
3777
4209
|
rules: {
|
|
3778
4210
|
"harlanzw/nuxt-await-navigate-to": "error",
|
|
4211
|
+
"harlanzw/nuxt-no-random": "error",
|
|
3779
4212
|
"harlanzw/nuxt-no-redundant-import-meta": "error",
|
|
3780
4213
|
"harlanzw/nuxt-no-side-effects-in-async-data-handler": "error",
|
|
3781
4214
|
"harlanzw/nuxt-no-side-effects-in-setup": "error",
|
|
4215
|
+
"harlanzw/nuxt-no-unsafe-date": "warn",
|
|
3782
4216
|
"harlanzw/nuxt-prefer-navigate-to-over-router-push-replace": "warn",
|
|
3783
4217
|
"harlanzw/nuxt-prefer-nuxt-link-over-router-link": "warn"
|
|
3784
4218
|
}
|
|
@@ -3795,7 +4229,8 @@ plugin.configs.vue = [
|
|
|
3795
4229
|
"harlanzw/vue-no-passing-refs-as-props": "error",
|
|
3796
4230
|
"harlanzw/vue-no-reactive-destructuring": "error",
|
|
3797
4231
|
"harlanzw/vue-no-ref-access-in-templates": "warn",
|
|
3798
|
-
"harlanzw/vue-no-torefs-on-props": "warn"
|
|
4232
|
+
"harlanzw/vue-no-torefs-on-props": "warn",
|
|
4233
|
+
"harlanzw/vue-require-composable-prefix": "warn"
|
|
3799
4234
|
}
|
|
3800
4235
|
}
|
|
3801
4236
|
];
|