@mionjs/devtools 0.8.0-alpha.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/LICENSE +21 -0
- package/build/eslint/cjs/index.cjs +41 -0
- package/build/eslint/cjs/index.cjs.map +1 -0
- package/build/eslint/cjs/package.json +1 -0
- package/build/eslint/cjs/src/eslint/index.cjs +13 -0
- package/build/eslint/cjs/src/eslint/index.cjs.map +1 -0
- package/build/eslint/cjs/src/eslint/index.d.ts +3 -0
- package/build/eslint/cjs/src/eslint/rules/enforce-type-imports.cjs +121 -0
- package/build/eslint/cjs/src/eslint/rules/enforce-type-imports.cjs.map +1 -0
- package/build/eslint/cjs/src/eslint/rules/enforce-type-imports.d.ts +7 -0
- package/build/eslint/cjs/src/eslint/rules/formatTypeNames.cjs +71 -0
- package/build/eslint/cjs/src/eslint/rules/formatTypeNames.cjs.map +1 -0
- package/build/eslint/cjs/src/eslint/rules/formatTypeNames.d.ts +3 -0
- package/build/eslint/cjs/src/eslint/rules/no-mixed-union-properties.cjs +220 -0
- package/build/eslint/cjs/src/eslint/rules/no-mixed-union-properties.cjs.map +1 -0
- package/build/eslint/cjs/src/eslint/rules/no-mixed-union-properties.d.ts +3 -0
- package/build/eslint/cjs/src/eslint/rules/no-type-imports.cjs +251 -0
- package/build/eslint/cjs/src/eslint/rules/no-type-imports.cjs.map +1 -0
- package/build/eslint/cjs/src/eslint/rules/no-type-imports.d.ts +3 -0
- package/build/eslint/cjs/src/eslint/rules/no-typeof-runtype.cjs +72 -0
- package/build/eslint/cjs/src/eslint/rules/no-typeof-runtype.cjs.map +1 -0
- package/build/eslint/cjs/src/eslint/rules/no-typeof-runtype.d.ts +3 -0
- package/build/eslint/cjs/src/eslint/rules/no-unreachable-union-types.cjs +220 -0
- package/build/eslint/cjs/src/eslint/rules/no-unreachable-union-types.cjs.map +1 -0
- package/build/eslint/cjs/src/eslint/rules/no-unreachable-union-types.d.ts +3 -0
- package/build/eslint/cjs/src/eslint/rules/no-vite-client.cjs +71 -0
- package/build/eslint/cjs/src/eslint/rules/no-vite-client.cjs.map +1 -0
- package/build/eslint/cjs/src/eslint/rules/no-vite-client.d.ts +4 -0
- package/build/eslint/cjs/src/eslint/rules/pure-functions.cjs +346 -0
- package/build/eslint/cjs/src/eslint/rules/pure-functions.cjs.map +1 -0
- package/build/eslint/cjs/src/eslint/rules/pure-functions.d.ts +4 -0
- package/build/eslint/cjs/src/eslint/rules/strong-typed-routes.cjs +328 -0
- package/build/eslint/cjs/src/eslint/rules/strong-typed-routes.cjs.map +1 -0
- package/build/eslint/cjs/src/eslint/rules/strong-typed-routes.d.ts +4 -0
- package/build/eslint/cjs/src/eslint/rules/type-formats-imports.cjs +52 -0
- package/build/eslint/cjs/src/eslint/rules/type-formats-imports.cjs.map +1 -0
- package/build/eslint/cjs/src/eslint/rules/type-formats-imports.d.ts +3 -0
- package/build/eslint/cjs/src/pureFns/purityRules.cjs +67 -0
- package/build/eslint/cjs/src/pureFns/purityRules.cjs.map +1 -0
- package/build/eslint/cjs/src/pureFns/purityRules.d.ts +4 -0
- package/build/eslint/esm/index.js +42 -0
- package/build/eslint/esm/index.js.map +1 -0
- package/build/eslint/esm/src/eslint/index.d.ts +3 -0
- package/build/eslint/esm/src/eslint/index.js +14 -0
- package/build/eslint/esm/src/eslint/index.js.map +1 -0
- package/build/eslint/esm/src/eslint/rules/enforce-type-imports.d.ts +7 -0
- package/build/eslint/esm/src/eslint/rules/enforce-type-imports.js +122 -0
- package/build/eslint/esm/src/eslint/rules/enforce-type-imports.js.map +1 -0
- package/build/eslint/esm/src/eslint/rules/formatTypeNames.d.ts +3 -0
- package/build/eslint/esm/src/eslint/rules/formatTypeNames.js +71 -0
- package/build/eslint/esm/src/eslint/rules/formatTypeNames.js.map +1 -0
- package/build/eslint/esm/src/eslint/rules/no-mixed-union-properties.d.ts +3 -0
- package/build/eslint/esm/src/eslint/rules/no-mixed-union-properties.js +221 -0
- package/build/eslint/esm/src/eslint/rules/no-mixed-union-properties.js.map +1 -0
- package/build/eslint/esm/src/eslint/rules/no-type-imports.d.ts +3 -0
- package/build/eslint/esm/src/eslint/rules/no-type-imports.js +252 -0
- package/build/eslint/esm/src/eslint/rules/no-type-imports.js.map +1 -0
- package/build/eslint/esm/src/eslint/rules/no-typeof-runtype.d.ts +3 -0
- package/build/eslint/esm/src/eslint/rules/no-typeof-runtype.js +73 -0
- package/build/eslint/esm/src/eslint/rules/no-typeof-runtype.js.map +1 -0
- package/build/eslint/esm/src/eslint/rules/no-unreachable-union-types.d.ts +3 -0
- package/build/eslint/esm/src/eslint/rules/no-unreachable-union-types.js +221 -0
- package/build/eslint/esm/src/eslint/rules/no-unreachable-union-types.js.map +1 -0
- package/build/eslint/esm/src/eslint/rules/no-vite-client.d.ts +4 -0
- package/build/eslint/esm/src/eslint/rules/no-vite-client.js +72 -0
- package/build/eslint/esm/src/eslint/rules/no-vite-client.js.map +1 -0
- package/build/eslint/esm/src/eslint/rules/pure-functions.d.ts +4 -0
- package/build/eslint/esm/src/eslint/rules/pure-functions.js +347 -0
- package/build/eslint/esm/src/eslint/rules/pure-functions.js.map +1 -0
- package/build/eslint/esm/src/eslint/rules/strong-typed-routes.d.ts +4 -0
- package/build/eslint/esm/src/eslint/rules/strong-typed-routes.js +329 -0
- package/build/eslint/esm/src/eslint/rules/strong-typed-routes.js.map +1 -0
- package/build/eslint/esm/src/eslint/rules/type-formats-imports.d.ts +3 -0
- package/build/eslint/esm/src/eslint/rules/type-formats-imports.js +53 -0
- package/build/eslint/esm/src/eslint/rules/type-formats-imports.js.map +1 -0
- package/build/eslint/esm/src/pureFns/purityRules.d.ts +4 -0
- package/build/eslint/esm/src/pureFns/purityRules.js +67 -0
- package/build/eslint/esm/src/pureFns/purityRules.js.map +1 -0
- package/build/vite-plugin/cjs/index.cjs +14 -0
- package/build/vite-plugin/cjs/index.cjs.map +1 -0
- package/build/vite-plugin/cjs/package.json +1 -0
- package/build/vite-plugin/cjs/src/pureFns/purityRules.cjs +65 -0
- package/build/vite-plugin/cjs/src/pureFns/purityRules.cjs.map +1 -0
- package/build/vite-plugin/cjs/src/pureFns/purityRules.d.ts +4 -0
- package/build/vite-plugin/cjs/src/vite-plugin/aotCacheGenerator.cjs +235 -0
- package/build/vite-plugin/cjs/src/vite-plugin/aotCacheGenerator.cjs.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/aotCacheGenerator.d.ts +23 -0
- package/build/vite-plugin/cjs/src/vite-plugin/aotDiskCache.cjs +134 -0
- package/build/vite-plugin/cjs/src/vite-plugin/aotDiskCache.cjs.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/aotDiskCache.d.ts +6 -0
- package/build/vite-plugin/cjs/src/vite-plugin/cjsPackageJsonPlugin.cjs +15 -0
- package/build/vite-plugin/cjs/src/vite-plugin/cjsPackageJsonPlugin.cjs.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/cjsPackageJsonPlugin.d.ts +2 -0
- package/build/vite-plugin/cjs/src/vite-plugin/constants.cjs +25 -0
- package/build/vite-plugin/cjs/src/vite-plugin/constants.cjs.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/constants.d.ts +10 -0
- package/build/vite-plugin/cjs/src/vite-plugin/extractPureFn.cjs +628 -0
- package/build/vite-plugin/cjs/src/vite-plugin/extractPureFn.cjs.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/extractPureFn.d.ts +13 -0
- package/build/vite-plugin/cjs/src/vite-plugin/index.cjs +14 -0
- package/build/vite-plugin/cjs/src/vite-plugin/index.cjs.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/index.d.ts +5 -0
- package/build/vite-plugin/cjs/src/vite-plugin/mionVitePlugin.cjs +404 -0
- package/build/vite-plugin/cjs/src/vite-plugin/mionVitePlugin.cjs.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/mionVitePlugin.d.ts +36 -0
- package/build/vite-plugin/cjs/src/vite-plugin/resolveModule.cjs +55 -0
- package/build/vite-plugin/cjs/src/vite-plugin/resolveModule.cjs.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/resolveModule.d.ts +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/transformers.cjs +165 -0
- package/build/vite-plugin/cjs/src/vite-plugin/transformers.cjs.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/transformers.d.ts +10 -0
- package/build/vite-plugin/cjs/src/vite-plugin/types.cjs +2 -0
- package/build/vite-plugin/cjs/src/vite-plugin/types.cjs.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/types.d.ts +57 -0
- package/build/vite-plugin/cjs/src/vite-plugin/virtual-modules.d.cjs +2 -0
- package/build/vite-plugin/cjs/src/vite-plugin/virtual-modules.d.cjs.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/virtualModule.cjs +68 -0
- package/build/vite-plugin/cjs/src/vite-plugin/virtualModule.cjs.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/virtualModule.d.ts +2 -0
- package/build/vite-plugin/esm/index.js +14 -0
- package/build/vite-plugin/esm/index.js.map +1 -0
- package/build/vite-plugin/esm/src/pureFns/purityRules.d.ts +4 -0
- package/build/vite-plugin/esm/src/pureFns/purityRules.js +65 -0
- package/build/vite-plugin/esm/src/pureFns/purityRules.js.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/aotCacheGenerator.d.ts +23 -0
- package/build/vite-plugin/esm/src/vite-plugin/aotCacheGenerator.js +235 -0
- package/build/vite-plugin/esm/src/vite-plugin/aotCacheGenerator.js.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/aotDiskCache.d.ts +6 -0
- package/build/vite-plugin/esm/src/vite-plugin/aotDiskCache.js +133 -0
- package/build/vite-plugin/esm/src/vite-plugin/aotDiskCache.js.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/cjsPackageJsonPlugin.d.ts +2 -0
- package/build/vite-plugin/esm/src/vite-plugin/cjsPackageJsonPlugin.js +15 -0
- package/build/vite-plugin/esm/src/vite-plugin/cjsPackageJsonPlugin.js.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/constants.d.ts +10 -0
- package/build/vite-plugin/esm/src/vite-plugin/constants.js +25 -0
- package/build/vite-plugin/esm/src/vite-plugin/constants.js.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/extractPureFn.d.ts +13 -0
- package/build/vite-plugin/esm/src/vite-plugin/extractPureFn.js +611 -0
- package/build/vite-plugin/esm/src/vite-plugin/extractPureFn.js.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/index.d.ts +5 -0
- package/build/vite-plugin/esm/src/vite-plugin/index.js +14 -0
- package/build/vite-plugin/esm/src/vite-plugin/index.js.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/mionVitePlugin.d.ts +36 -0
- package/build/vite-plugin/esm/src/vite-plugin/mionVitePlugin.js +387 -0
- package/build/vite-plugin/esm/src/vite-plugin/mionVitePlugin.js.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/resolveModule.d.ts +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/resolveModule.js +33 -0
- package/build/vite-plugin/esm/src/vite-plugin/resolveModule.js.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/transformers.d.ts +10 -0
- package/build/vite-plugin/esm/src/vite-plugin/transformers.js +148 -0
- package/build/vite-plugin/esm/src/vite-plugin/transformers.js.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/types.d.ts +57 -0
- package/build/vite-plugin/esm/src/vite-plugin/types.js +2 -0
- package/build/vite-plugin/esm/src/vite-plugin/types.js.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/virtual-modules.d.js +2 -0
- package/build/vite-plugin/esm/src/vite-plugin/virtual-modules.d.js.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/virtualModule.d.ts +2 -0
- package/build/vite-plugin/esm/src/vite-plugin/virtualModule.js +68 -0
- package/build/vite-plugin/esm/src/vite-plugin/virtualModule.js.map +1 -0
- package/package.json +94 -0
- package/src/vite-plugin/virtual-modules.d.ts +48 -0
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
import { AST_NODE_TYPES } from "@typescript-eslint/utils";
|
|
2
|
+
const ROUTER_FUNCTIONS = ["route", "middleFn", "headersFn"];
|
|
3
|
+
const HANDLER_TYPES = ["Handler", "HeaderHandler"];
|
|
4
|
+
function buildImportCache(program) {
|
|
5
|
+
const routerFunctions = /* @__PURE__ */ new Set();
|
|
6
|
+
const handlerTypes = /* @__PURE__ */ new Set();
|
|
7
|
+
for (const statement of program.body) {
|
|
8
|
+
if (statement.type === AST_NODE_TYPES.ImportDeclaration) {
|
|
9
|
+
const source = statement.source.value;
|
|
10
|
+
if (source === "@mionjs/router" || source === "@mionjs/router/") {
|
|
11
|
+
for (const specifier of statement.specifiers) {
|
|
12
|
+
if (specifier.type === AST_NODE_TYPES.ImportSpecifier && specifier.imported.type === AST_NODE_TYPES.Identifier) {
|
|
13
|
+
const name = specifier.imported.name;
|
|
14
|
+
if (ROUTER_FUNCTIONS.includes(name)) {
|
|
15
|
+
routerFunctions.add(name);
|
|
16
|
+
}
|
|
17
|
+
if (HANDLER_TYPES.includes(name)) {
|
|
18
|
+
handlerTypes.add(name);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return { routerFunctions, handlerTypes };
|
|
26
|
+
}
|
|
27
|
+
function getRouterFunctionName(node, importCache) {
|
|
28
|
+
if (node.callee.type !== AST_NODE_TYPES.Identifier) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
const functionName = node.callee.name;
|
|
32
|
+
if (importCache.routerFunctions.has(functionName)) {
|
|
33
|
+
return functionName;
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
function buildFunctionCache(program) {
|
|
38
|
+
const cache = /* @__PURE__ */ new Map();
|
|
39
|
+
for (const statement of program.body) {
|
|
40
|
+
if (statement.type === AST_NODE_TYPES.FunctionDeclaration && statement.id?.name) {
|
|
41
|
+
cache.set(statement.id.name, statement);
|
|
42
|
+
}
|
|
43
|
+
if (statement.type === AST_NODE_TYPES.VariableDeclaration) {
|
|
44
|
+
for (const declarator of statement.declarations) {
|
|
45
|
+
if (declarator.id.type === AST_NODE_TYPES.Identifier) {
|
|
46
|
+
if (declarator.init?.type === AST_NODE_TYPES.ArrowFunctionExpression || declarator.init?.type === AST_NODE_TYPES.FunctionExpression) {
|
|
47
|
+
cache.set(declarator.id.name, declarator.init);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return cache;
|
|
54
|
+
}
|
|
55
|
+
function getHandlerFunction(node, functionCache) {
|
|
56
|
+
const handlerIndex = 0;
|
|
57
|
+
if (node.arguments.length <= handlerIndex) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
const handlerArg = node.arguments[handlerIndex];
|
|
61
|
+
if (handlerArg.type === AST_NODE_TYPES.ArrowFunctionExpression || handlerArg.type === AST_NODE_TYPES.FunctionExpression) {
|
|
62
|
+
return handlerArg;
|
|
63
|
+
}
|
|
64
|
+
if (handlerArg.type === AST_NODE_TYPES.Identifier) {
|
|
65
|
+
return functionCache.get(handlerArg.name) ?? null;
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
function hasExplicitReturnType(func) {
|
|
70
|
+
return func.returnType !== void 0;
|
|
71
|
+
}
|
|
72
|
+
function isPrimitiveLiteral(node) {
|
|
73
|
+
if (node.type === AST_NODE_TYPES.Literal) {
|
|
74
|
+
const value = node.value;
|
|
75
|
+
if ("bigint" in node) {
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
return typeof value === "boolean" || typeof value === "string" || typeof value === "number" || value === null;
|
|
79
|
+
}
|
|
80
|
+
if (node.type === AST_NODE_TYPES.Identifier && node.name === "undefined") {
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
if (node.type === AST_NODE_TYPES.UnaryExpression && node.operator === "-" && node.argument.type === AST_NODE_TYPES.Literal) {
|
|
84
|
+
return typeof node.argument.value === "number";
|
|
85
|
+
}
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
function validateParameterTypes(func) {
|
|
89
|
+
const missingTypeParams = [];
|
|
90
|
+
const missingParamNodes = [];
|
|
91
|
+
for (let i = 1; i < func.params.length; i++) {
|
|
92
|
+
const param = func.params[i];
|
|
93
|
+
if (param.type === AST_NODE_TYPES.Identifier) {
|
|
94
|
+
if (!param.typeAnnotation) {
|
|
95
|
+
missingTypeParams.push(param.name);
|
|
96
|
+
missingParamNodes.push(param);
|
|
97
|
+
}
|
|
98
|
+
} else if (param.type === AST_NODE_TYPES.RestElement) {
|
|
99
|
+
if (param.argument.type === AST_NODE_TYPES.Identifier && !param.typeAnnotation) {
|
|
100
|
+
missingTypeParams.push(`...${param.argument.name}`);
|
|
101
|
+
missingParamNodes.push(param);
|
|
102
|
+
}
|
|
103
|
+
} else if (param.type === AST_NODE_TYPES.ArrayPattern || param.type === AST_NODE_TYPES.ObjectPattern) {
|
|
104
|
+
if (!param.typeAnnotation) {
|
|
105
|
+
missingTypeParams.push(`parameter ${i + 1}`);
|
|
106
|
+
missingParamNodes.push(param);
|
|
107
|
+
}
|
|
108
|
+
} else if (param.type === AST_NODE_TYPES.AssignmentPattern) {
|
|
109
|
+
const hasTypeAnnotationOnPattern = param.typeAnnotation !== void 0;
|
|
110
|
+
const hasTypeAnnotationOnLeft = param.left.type === AST_NODE_TYPES.Identifier && param.left.typeAnnotation !== void 0;
|
|
111
|
+
const hasTypeAnnotation = hasTypeAnnotationOnPattern || hasTypeAnnotationOnLeft;
|
|
112
|
+
const hasPrimitiveDefault = isPrimitiveLiteral(param.right);
|
|
113
|
+
if (!hasTypeAnnotation && !hasPrimitiveDefault) {
|
|
114
|
+
const paramName = param.left.type === AST_NODE_TYPES.Identifier ? param.left.name : `parameter ${i + 1}`;
|
|
115
|
+
missingTypeParams.push(paramName);
|
|
116
|
+
missingParamNodes.push(param);
|
|
117
|
+
}
|
|
118
|
+
} else if (!("typeAnnotation" in param) || !param.typeAnnotation) {
|
|
119
|
+
missingTypeParams.push(`parameter ${i + 1}`);
|
|
120
|
+
missingParamNodes.push(param);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
valid: missingTypeParams.length === 0,
|
|
125
|
+
missingTypeParams,
|
|
126
|
+
missingParamNodes
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function getParameterName(param) {
|
|
130
|
+
if (param.type === AST_NODE_TYPES.Identifier) {
|
|
131
|
+
return param.name;
|
|
132
|
+
} else if (param.type === AST_NODE_TYPES.RestElement && param.argument.type === AST_NODE_TYPES.Identifier) {
|
|
133
|
+
return `...${param.argument.name}`;
|
|
134
|
+
} else if (param.type === AST_NODE_TYPES.ArrayPattern) {
|
|
135
|
+
return "[...]";
|
|
136
|
+
} else if (param.type === AST_NODE_TYPES.ObjectPattern) {
|
|
137
|
+
return "{...}";
|
|
138
|
+
} else if (param.type === AST_NODE_TYPES.AssignmentPattern) {
|
|
139
|
+
if (param.left.type === AST_NODE_TYPES.Identifier) {
|
|
140
|
+
return param.left.name;
|
|
141
|
+
}
|
|
142
|
+
return "param";
|
|
143
|
+
}
|
|
144
|
+
return "param";
|
|
145
|
+
}
|
|
146
|
+
function getReturnTypeReportNode(func) {
|
|
147
|
+
if ((func.type === AST_NODE_TYPES.FunctionDeclaration || func.type === AST_NODE_TYPES.FunctionExpression) && func.id) {
|
|
148
|
+
return func.id;
|
|
149
|
+
}
|
|
150
|
+
return func;
|
|
151
|
+
}
|
|
152
|
+
function getHandlerTypeFromAnnotation(typeAnnotation, importCache) {
|
|
153
|
+
if (typeAnnotation.typeAnnotation.type === AST_NODE_TYPES.TSTypeReference) {
|
|
154
|
+
const typeName = typeAnnotation.typeAnnotation.typeName;
|
|
155
|
+
if (typeName.type === AST_NODE_TYPES.Identifier) {
|
|
156
|
+
const name = typeName.name;
|
|
157
|
+
if ((name === "Handler" || name === "HeaderHandler") && importCache.handlerTypes.has(name)) {
|
|
158
|
+
return name;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
function getHandlerTypeFromSatisfies(satisfiesExpression, importCache) {
|
|
165
|
+
if (satisfiesExpression.typeAnnotation.type === AST_NODE_TYPES.TSTypeReference) {
|
|
166
|
+
const typeName = satisfiesExpression.typeAnnotation.typeName;
|
|
167
|
+
if (typeName.type === AST_NODE_TYPES.Identifier) {
|
|
168
|
+
const name = typeName.name;
|
|
169
|
+
if ((name === "Handler" || name === "HeaderHandler") && importCache.handlerTypes.has(name)) {
|
|
170
|
+
return name;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
function getHandlerTypeFromJSDoc(node, context) {
|
|
177
|
+
const sourceCode = context.sourceCode;
|
|
178
|
+
const comments = sourceCode.getCommentsBefore(node);
|
|
179
|
+
for (const comment of comments) {
|
|
180
|
+
if (comment.type === "Block") {
|
|
181
|
+
const commentText = comment.value;
|
|
182
|
+
if (commentText.includes("@mion:route")) {
|
|
183
|
+
return "Handler";
|
|
184
|
+
}
|
|
185
|
+
if (commentText.includes("@mion:middleFn")) {
|
|
186
|
+
return "MiddleFnHandler";
|
|
187
|
+
}
|
|
188
|
+
if (commentText.includes("@mion:headersFn")) {
|
|
189
|
+
return "HeaderHandler";
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
const rule = {
|
|
196
|
+
meta: {
|
|
197
|
+
type: "problem",
|
|
198
|
+
docs: {
|
|
199
|
+
description: "Enforce explicit parameters and return type annotations for router handler functions"
|
|
200
|
+
},
|
|
201
|
+
messages: {
|
|
202
|
+
missingReturnType: "mion {{handlerType}}() handler must define a return type.",
|
|
203
|
+
missingParamTypes: 'mion parameter "{{paramName}}" must have an explicit type definition.',
|
|
204
|
+
missingReturnTypeRouter: "mion {{routerFunction}}() handler must define a return type.",
|
|
205
|
+
missingParamTypesRouter: 'mion parameter "{{paramName}}" must have an explicit type definition.'
|
|
206
|
+
},
|
|
207
|
+
schema: []
|
|
208
|
+
// No options
|
|
209
|
+
},
|
|
210
|
+
defaultOptions: [],
|
|
211
|
+
create(context) {
|
|
212
|
+
let importCache = null;
|
|
213
|
+
let functionCache = null;
|
|
214
|
+
return {
|
|
215
|
+
// Build caches once when we start processing the file
|
|
216
|
+
Program(node) {
|
|
217
|
+
importCache = buildImportCache(node);
|
|
218
|
+
functionCache = buildFunctionCache(node);
|
|
219
|
+
},
|
|
220
|
+
CallExpression(node) {
|
|
221
|
+
if (!importCache || !functionCache) return;
|
|
222
|
+
const functionName = getRouterFunctionName(node, importCache);
|
|
223
|
+
if (!functionName) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
const handlerFunc = getHandlerFunction(node, functionCache);
|
|
227
|
+
if (!handlerFunc) {
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
const hasReturnType = hasExplicitReturnType(handlerFunc);
|
|
231
|
+
const paramValidation = validateParameterTypes(handlerFunc);
|
|
232
|
+
if (!hasReturnType) {
|
|
233
|
+
context.report({
|
|
234
|
+
node: getReturnTypeReportNode(handlerFunc),
|
|
235
|
+
messageId: "missingReturnTypeRouter",
|
|
236
|
+
data: {
|
|
237
|
+
routerFunction: functionName
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
for (const paramNode of paramValidation.missingParamNodes) {
|
|
242
|
+
context.report({
|
|
243
|
+
node: paramNode,
|
|
244
|
+
messageId: "missingParamTypesRouter",
|
|
245
|
+
data: {
|
|
246
|
+
paramName: getParameterName(paramNode)
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
// Check variable declarations with type annotations or JSDoc tags
|
|
252
|
+
VariableDeclarator(node) {
|
|
253
|
+
if (!importCache) return;
|
|
254
|
+
if (node.id.type === AST_NODE_TYPES.Identifier) {
|
|
255
|
+
let handlerType = null;
|
|
256
|
+
if (node.id.typeAnnotation) {
|
|
257
|
+
handlerType = getHandlerTypeFromAnnotation(node.id.typeAnnotation, importCache);
|
|
258
|
+
}
|
|
259
|
+
if (!handlerType && node.parent?.type === AST_NODE_TYPES.VariableDeclaration) {
|
|
260
|
+
handlerType = getHandlerTypeFromJSDoc(node.parent, context);
|
|
261
|
+
}
|
|
262
|
+
if (handlerType && (node.init?.type === AST_NODE_TYPES.ArrowFunctionExpression || node.init?.type === AST_NODE_TYPES.FunctionExpression)) {
|
|
263
|
+
checkHandlerFunction(node.init, handlerType, context);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
},
|
|
267
|
+
// Check satisfies expressions
|
|
268
|
+
TSSatisfiesExpression(node) {
|
|
269
|
+
if (!importCache) return;
|
|
270
|
+
const handlerType = getHandlerTypeFromSatisfies(node, importCache);
|
|
271
|
+
if (handlerType && (node.expression.type === AST_NODE_TYPES.ArrowFunctionExpression || node.expression.type === AST_NODE_TYPES.FunctionExpression)) {
|
|
272
|
+
checkHandlerFunction(node.expression, handlerType, context);
|
|
273
|
+
}
|
|
274
|
+
},
|
|
275
|
+
// Check function declarations with JSDoc tags
|
|
276
|
+
FunctionDeclaration(node) {
|
|
277
|
+
const handlerType = getHandlerTypeFromJSDoc(node, context);
|
|
278
|
+
if (handlerType) {
|
|
279
|
+
checkHandlerFunction(node, handlerType, context);
|
|
280
|
+
}
|
|
281
|
+
},
|
|
282
|
+
// Check arrow functions and function expressions with JSDoc tags
|
|
283
|
+
ArrowFunctionExpression(node) {
|
|
284
|
+
const handlerType = getHandlerTypeFromJSDoc(node, context);
|
|
285
|
+
if (handlerType) {
|
|
286
|
+
checkHandlerFunction(node, handlerType, context);
|
|
287
|
+
}
|
|
288
|
+
},
|
|
289
|
+
FunctionExpression(node) {
|
|
290
|
+
const handlerType = getHandlerTypeFromJSDoc(node, context);
|
|
291
|
+
if (handlerType) {
|
|
292
|
+
checkHandlerFunction(node, handlerType, context);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
function handlerTypeToFunctionName(handlerType) {
|
|
299
|
+
if (handlerType === "HeaderHandler") return "headersFn";
|
|
300
|
+
if (handlerType === "MiddleFnHandler") return "middleFn";
|
|
301
|
+
return "route";
|
|
302
|
+
}
|
|
303
|
+
function checkHandlerFunction(func, handlerType, context) {
|
|
304
|
+
const hasReturnType = hasExplicitReturnType(func);
|
|
305
|
+
const paramValidation = validateParameterTypes(func);
|
|
306
|
+
const functionName = handlerTypeToFunctionName(handlerType);
|
|
307
|
+
if (!hasReturnType) {
|
|
308
|
+
context.report({
|
|
309
|
+
node: getReturnTypeReportNode(func),
|
|
310
|
+
messageId: "missingReturnType",
|
|
311
|
+
data: {
|
|
312
|
+
handlerType: functionName
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
for (const paramNode of paramValidation.missingParamNodes) {
|
|
317
|
+
context.report({
|
|
318
|
+
node: paramNode,
|
|
319
|
+
messageId: "missingParamTypes",
|
|
320
|
+
data: {
|
|
321
|
+
paramName: getParameterName(paramNode)
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
export {
|
|
327
|
+
rule as default
|
|
328
|
+
};
|
|
329
|
+
//# sourceMappingURL=strong-typed-routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"strong-typed-routes.js","sources":["../../../../../../src/eslint/rules/strong-typed-routes.ts"],"sourcesContent":["/* ########\n * 2024 mion\n * Author: Ma-jerez\n * License: MIT\n * The software is provided \"as is\", without warranty of any kind.\n * ######## */\n\nimport {TSESTree, TSESLint, AST_NODE_TYPES} from '@typescript-eslint/utils';\n\n// List of router functions that should have strongly typed handlers\nconst ROUTER_FUNCTIONS = ['route', 'middleFn', 'headersFn'] as const;\n// List of handler types that can be used with type annotations\nconst HANDLER_TYPES = ['Handler', 'HeaderHandler'] as const;\n\n/**\n * Cache for imports from @mionjs/router - computed once per file\n */\ninterface MionRouterImports {\n /** Set of function names imported from @mionjs/router (route, middleFn, headersFn) */\n routerFunctions: Set<string>;\n /** Set of type names imported from @mionjs/router (Handler, HeaderHandler) */\n handlerTypes: Set<string>;\n}\n\n/**\n * Builds a cache of all imports from @mionjs/router\n * This is called once per file in the Program visitor\n */\nfunction buildImportCache(program: TSESTree.Program): MionRouterImports {\n const routerFunctions = new Set<string>();\n const handlerTypes = new Set<string>();\n\n for (const statement of program.body) {\n if (statement.type === AST_NODE_TYPES.ImportDeclaration) {\n const source = statement.source.value;\n // Check for @mionjs/router package\n if (source === '@mionjs/router' || source === '@mionjs/router/') {\n for (const specifier of statement.specifiers) {\n if (\n specifier.type === AST_NODE_TYPES.ImportSpecifier &&\n specifier.imported.type === AST_NODE_TYPES.Identifier\n ) {\n const name = specifier.imported.name;\n if (ROUTER_FUNCTIONS.includes(name as (typeof ROUTER_FUNCTIONS)[number])) {\n routerFunctions.add(name);\n }\n if (HANDLER_TYPES.includes(name as (typeof HANDLER_TYPES)[number])) {\n handlerTypes.add(name);\n }\n }\n }\n }\n }\n }\n\n return {routerFunctions, handlerTypes};\n}\n\n/**\n * Checks if a call expression is calling router functions from @mionjs/router\n * Uses the cached import information for performance\n */\nfunction getRouterFunctionName(node: TSESTree.CallExpression, importCache: MionRouterImports): string | null {\n // Check if the callee is an identifier with one of the router function names\n if (node.callee.type !== AST_NODE_TYPES.Identifier) {\n return null;\n }\n\n const functionName = node.callee.name;\n\n // Check if this function is imported from @mionjs/router\n if (importCache.routerFunctions.has(functionName)) {\n return functionName;\n }\n\n return null;\n}\n\n/**\n * Cache for top-level function declarations and variable function expressions\n * This avoids iterating through the program body for each function reference\n */\ntype FunctionCache = Map<string, TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression | TSESTree.FunctionDeclaration>;\n\n/**\n * Builds a cache of all top-level function declarations and function variable assignments\n * This is called once per file in the Program visitor\n */\nfunction buildFunctionCache(program: TSESTree.Program): FunctionCache {\n const cache: FunctionCache = new Map();\n\n for (const statement of program.body) {\n // Check function declarations\n if (statement.type === AST_NODE_TYPES.FunctionDeclaration && statement.id?.name) {\n cache.set(statement.id.name, statement);\n }\n\n // Check variable declarations\n if (statement.type === AST_NODE_TYPES.VariableDeclaration) {\n for (const declarator of statement.declarations) {\n if (declarator.id.type === AST_NODE_TYPES.Identifier) {\n if (\n declarator.init?.type === AST_NODE_TYPES.ArrowFunctionExpression ||\n declarator.init?.type === AST_NODE_TYPES.FunctionExpression\n ) {\n cache.set(declarator.id.name, declarator.init);\n }\n }\n }\n }\n }\n\n return cache;\n}\n\n/**\n * Gets the handler function from a router function call\n * Uses the cached function information for performance\n */\nfunction getHandlerFunction(\n node: TSESTree.CallExpression,\n functionCache: FunctionCache\n): TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression | TSESTree.FunctionDeclaration | null {\n // Handler is always the first parameter for route, middleFn, and headersFn\n const handlerIndex = 0;\n\n if (node.arguments.length <= handlerIndex) {\n return null;\n }\n\n const handlerArg = node.arguments[handlerIndex];\n\n // Handle inline function expressions/arrow functions\n if (handlerArg.type === AST_NODE_TYPES.ArrowFunctionExpression || handlerArg.type === AST_NODE_TYPES.FunctionExpression) {\n return handlerArg;\n }\n\n // Handle function references (identifiers) - use cached function lookup\n if (handlerArg.type === AST_NODE_TYPES.Identifier) {\n return functionCache.get(handlerArg.name) ?? null;\n }\n\n return null;\n}\n\n/**\n * Checks if a function has an explicit return type annotation\n * @param func The function node\n * @returns True if it has explicit return type, false otherwise\n */\nfunction hasExplicitReturnType(\n func: TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression | TSESTree.FunctionDeclaration\n): boolean {\n return func.returnType !== undefined;\n}\n\n/**\n * Checks if a node represents a primitive literal value\n * @param node The node to check\n * @returns True if the node is a primitive literal (boolean, string, number, null, undefined, bigint)\n */\nfunction isPrimitiveLiteral(node: TSESTree.Node): boolean {\n // Check for boolean, string, number, null, or bigint literals\n if (node.type === AST_NODE_TYPES.Literal) {\n const value = node.value;\n // BigInt literals have a 'bigint' property and value is null\n if ('bigint' in node) {\n return true;\n }\n return typeof value === 'boolean' || typeof value === 'string' || typeof value === 'number' || value === null;\n }\n // Check for undefined identifier\n if (node.type === AST_NODE_TYPES.Identifier && node.name === 'undefined') {\n return true;\n }\n // Check for negative numbers (UnaryExpression with -)\n if (node.type === AST_NODE_TYPES.UnaryExpression && node.operator === '-' && node.argument.type === AST_NODE_TYPES.Literal) {\n return typeof node.argument.value === 'number';\n }\n return false;\n}\n\n/**\n * Checks if all parameters (except the first) have explicit type annotations\n * @param func The function node\n * @returns Object with validation results including the actual parameter nodes\n */\nfunction validateParameterTypes(\n func: TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression | TSESTree.FunctionDeclaration\n): {\n valid: boolean;\n missingTypeParams: string[];\n missingParamNodes: TSESTree.Parameter[];\n} {\n const missingTypeParams: string[] = [];\n const missingParamNodes: TSESTree.Parameter[] = [];\n\n // Skip the first parameter (context), check all others\n for (let i = 1; i < func.params.length; i++) {\n const param = func.params[i];\n\n if (param.type === AST_NODE_TYPES.Identifier) {\n if (!param.typeAnnotation) {\n missingTypeParams.push(param.name);\n missingParamNodes.push(param);\n }\n } else if (param.type === AST_NODE_TYPES.RestElement) {\n if (param.argument.type === AST_NODE_TYPES.Identifier && !param.typeAnnotation) {\n missingTypeParams.push(`...${param.argument.name}`);\n missingParamNodes.push(param);\n }\n } else if (param.type === AST_NODE_TYPES.ArrayPattern || param.type === AST_NODE_TYPES.ObjectPattern) {\n // For destructuring patterns, check if they have type annotations\n if (!param.typeAnnotation) {\n missingTypeParams.push(`parameter ${i + 1}`);\n missingParamNodes.push(param);\n }\n } else if (param.type === AST_NODE_TYPES.AssignmentPattern) {\n // For default parameters (e.g., `name = 'default'` or `name: string = 'default'`), check if:\n // 1. There's an explicit type annotation on the AssignmentPattern itself, OR\n // 2. There's an explicit type annotation on the left side (for `name: Type = value` pattern), OR\n // 3. The default value is a primitive literal (type can be inferred)\n const hasTypeAnnotationOnPattern = param.typeAnnotation !== undefined;\n const hasTypeAnnotationOnLeft =\n param.left.type === AST_NODE_TYPES.Identifier && param.left.typeAnnotation !== undefined;\n const hasTypeAnnotation = hasTypeAnnotationOnPattern || hasTypeAnnotationOnLeft;\n const hasPrimitiveDefault = isPrimitiveLiteral(param.right);\n\n if (!hasTypeAnnotation && !hasPrimitiveDefault) {\n // Need explicit type annotation since type cannot be inferred from default value\n const paramName = param.left.type === AST_NODE_TYPES.Identifier ? param.left.name : `parameter ${i + 1}`;\n missingTypeParams.push(paramName);\n missingParamNodes.push(param);\n }\n }\n // For other parameter types, we assume they need type annotations too\n else if (!('typeAnnotation' in param) || !param.typeAnnotation) {\n missingTypeParams.push(`parameter ${i + 1}`);\n missingParamNodes.push(param);\n }\n }\n\n return {\n valid: missingTypeParams.length === 0,\n missingTypeParams,\n missingParamNodes,\n };\n}\n\n/**\n * Extracts a readable parameter name from a parameter node\n */\nfunction getParameterName(param: TSESTree.Parameter): string {\n if (param.type === AST_NODE_TYPES.Identifier) {\n return param.name;\n } else if (param.type === AST_NODE_TYPES.RestElement && param.argument.type === AST_NODE_TYPES.Identifier) {\n return `...${param.argument.name}`;\n } else if (param.type === AST_NODE_TYPES.ArrayPattern) {\n return '[...]';\n } else if (param.type === AST_NODE_TYPES.ObjectPattern) {\n return '{...}';\n } else if (param.type === AST_NODE_TYPES.AssignmentPattern) {\n if (param.left.type === AST_NODE_TYPES.Identifier) {\n return param.left.name;\n }\n return 'param';\n }\n return 'param';\n}\n\n/**\n * Gets the node to report for missing return type\n * For arrow functions, this is the arrow token area; for regular functions, the function keyword\n */\nfunction getReturnTypeReportNode(\n func: TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression | TSESTree.FunctionDeclaration\n): TSESTree.Node {\n // For function declarations/expressions with an id, report on the id\n if ((func.type === AST_NODE_TYPES.FunctionDeclaration || func.type === AST_NODE_TYPES.FunctionExpression) && func.id) {\n return func.id;\n }\n // Otherwise report on the function itself (will highlight just the signature area)\n return func;\n}\n\n/**\n * Checks if a type annotation references Handler or HeaderHandler from @mionjs/router\n * Uses the cached import information for performance\n */\nfunction getHandlerTypeFromAnnotation(\n typeAnnotation: TSESTree.TSTypeAnnotation,\n importCache: MionRouterImports\n): 'Handler' | 'HeaderHandler' | null {\n if (typeAnnotation.typeAnnotation.type === AST_NODE_TYPES.TSTypeReference) {\n const typeName = typeAnnotation.typeAnnotation.typeName;\n if (typeName.type === AST_NODE_TYPES.Identifier) {\n const name = typeName.name;\n if ((name === 'Handler' || name === 'HeaderHandler') && importCache.handlerTypes.has(name)) {\n return name as 'Handler' | 'HeaderHandler';\n }\n }\n }\n return null;\n}\n\n/**\n * Checks if a satisfies expression references Handler or HeaderHandler from @mionjs/router\n * Uses the cached import information for performance\n */\nfunction getHandlerTypeFromSatisfies(\n satisfiesExpression: TSESTree.TSSatisfiesExpression,\n importCache: MionRouterImports\n): 'Handler' | 'HeaderHandler' | null {\n if (satisfiesExpression.typeAnnotation.type === AST_NODE_TYPES.TSTypeReference) {\n const typeName = satisfiesExpression.typeAnnotation.typeName;\n if (typeName.type === AST_NODE_TYPES.Identifier) {\n const name = typeName.name;\n if ((name === 'Handler' || name === 'HeaderHandler') && importCache.handlerTypes.has(name)) {\n return name as 'Handler' | 'HeaderHandler';\n }\n }\n }\n return null;\n}\n\n/**\n * Checks if a function has JSDoc tags indicating it should be type-checked\n * @param node The node to check for JSDoc comments\n * @param context The ESLint context\n * @returns The handler type if JSDoc tag is found, null otherwise\n */\nfunction getHandlerTypeFromJSDoc(\n node:\n | TSESTree.ArrowFunctionExpression\n | TSESTree.FunctionExpression\n | TSESTree.FunctionDeclaration\n | TSESTree.VariableDeclaration,\n context: TSESLint.RuleContext<any, any>\n): 'Handler' | 'HeaderHandler' | 'MiddleFnHandler' | null {\n const sourceCode = context.sourceCode;\n const comments = sourceCode.getCommentsBefore(node);\n\n for (const comment of comments) {\n if (comment.type === 'Block') {\n const commentText = comment.value;\n if (commentText.includes('@mion:route')) {\n return 'Handler';\n }\n if (commentText.includes('@mion:middleFn')) {\n return 'MiddleFnHandler';\n }\n if (commentText.includes('@mion:headersFn')) {\n return 'HeaderHandler';\n }\n }\n }\n return null;\n}\n\nconst rule: TSESLint.RuleModule<\n 'missingReturnType' | 'missingParamTypes' | 'missingReturnTypeRouter' | 'missingParamTypesRouter',\n []\n> = {\n meta: {\n type: 'problem',\n docs: {\n description: 'Enforce explicit parameters and return type annotations for router handler functions',\n },\n messages: {\n missingReturnType: 'mion {{handlerType}}() handler must define a return type.',\n missingParamTypes: 'mion parameter \"{{paramName}}\" must have an explicit type definition.',\n missingReturnTypeRouter: 'mion {{routerFunction}}() handler must define a return type.',\n missingParamTypesRouter: 'mion parameter \"{{paramName}}\" must have an explicit type definition.',\n },\n schema: [], // No options\n },\n defaultOptions: [],\n create(context) {\n // Caches built once per file for performance\n let importCache: MionRouterImports | null = null;\n let functionCache: FunctionCache | null = null;\n\n return {\n // Build caches once when we start processing the file\n Program(node: TSESTree.Program) {\n importCache = buildImportCache(node);\n functionCache = buildFunctionCache(node);\n },\n CallExpression(node: TSESTree.CallExpression) {\n if (!importCache || !functionCache) return;\n\n const functionName = getRouterFunctionName(node, importCache);\n if (!functionName) {\n return;\n }\n\n const handlerFunc = getHandlerFunction(node, functionCache);\n if (!handlerFunc) {\n return;\n }\n\n const hasReturnType = hasExplicitReturnType(handlerFunc);\n const paramValidation = validateParameterTypes(handlerFunc);\n\n // Report errors on specific nodes for better highlighting\n if (!hasReturnType) {\n context.report({\n node: getReturnTypeReportNode(handlerFunc),\n messageId: 'missingReturnTypeRouter',\n data: {\n routerFunction: functionName,\n },\n });\n }\n\n // Report each missing parameter type separately\n for (const paramNode of paramValidation.missingParamNodes) {\n context.report({\n node: paramNode,\n messageId: 'missingParamTypesRouter',\n data: {\n paramName: getParameterName(paramNode),\n },\n });\n }\n },\n // Check variable declarations with type annotations or JSDoc tags\n VariableDeclarator(node: TSESTree.VariableDeclarator) {\n if (!importCache) return;\n\n if (node.id.type === AST_NODE_TYPES.Identifier) {\n let handlerType: 'Handler' | 'HeaderHandler' | 'MiddleFnHandler' | null = null;\n\n // Check for type annotation\n if (node.id.typeAnnotation) {\n handlerType = getHandlerTypeFromAnnotation(node.id.typeAnnotation, importCache);\n }\n\n // Check for JSDoc tags on the variable declaration\n if (!handlerType && node.parent?.type === AST_NODE_TYPES.VariableDeclaration) {\n handlerType = getHandlerTypeFromJSDoc(node.parent, context);\n }\n\n if (\n handlerType &&\n (node.init?.type === AST_NODE_TYPES.ArrowFunctionExpression ||\n node.init?.type === AST_NODE_TYPES.FunctionExpression)\n ) {\n checkHandlerFunction(node.init, handlerType, context);\n }\n }\n },\n // Check satisfies expressions\n TSSatisfiesExpression(node: TSESTree.TSSatisfiesExpression) {\n if (!importCache) return;\n\n const handlerType = getHandlerTypeFromSatisfies(node, importCache);\n if (\n handlerType &&\n (node.expression.type === AST_NODE_TYPES.ArrowFunctionExpression ||\n node.expression.type === AST_NODE_TYPES.FunctionExpression)\n ) {\n checkHandlerFunction(node.expression, handlerType, context);\n }\n },\n // Check function declarations with JSDoc tags\n FunctionDeclaration(node: TSESTree.FunctionDeclaration) {\n const handlerType = getHandlerTypeFromJSDoc(node, context);\n if (handlerType) {\n checkHandlerFunction(node, handlerType, context);\n }\n },\n // Check arrow functions and function expressions with JSDoc tags\n ArrowFunctionExpression(node: TSESTree.ArrowFunctionExpression) {\n const handlerType = getHandlerTypeFromJSDoc(node, context);\n if (handlerType) {\n checkHandlerFunction(node, handlerType, context);\n }\n },\n FunctionExpression(node: TSESTree.FunctionExpression) {\n const handlerType = getHandlerTypeFromJSDoc(node, context);\n if (handlerType) {\n checkHandlerFunction(node, handlerType, context);\n }\n },\n };\n },\n};\n\n/**\n * Maps Handler/HeaderHandler/MiddleFnHandler type names to their corresponding router function names\n */\nfunction handlerTypeToFunctionName(handlerType: 'Handler' | 'HeaderHandler' | 'MiddleFnHandler'): string {\n if (handlerType === 'HeaderHandler') return 'headersFn';\n if (handlerType === 'MiddleFnHandler') return 'middleFn';\n return 'route';\n}\n\n/**\n * Helper function to check a handler function for type annotations\n * @param func The function node to check\n * @param handlerType The expected handler type\n * @param context The ESLint context\n */\nfunction checkHandlerFunction(\n func: TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression | TSESTree.FunctionDeclaration,\n handlerType: 'Handler' | 'HeaderHandler' | 'MiddleFnHandler',\n context: TSESLint.RuleContext<any, any>\n) {\n const hasReturnType = hasExplicitReturnType(func);\n const paramValidation = validateParameterTypes(func);\n const functionName = handlerTypeToFunctionName(handlerType);\n\n // Report errors on specific nodes for better highlighting\n if (!hasReturnType) {\n context.report({\n node: getReturnTypeReportNode(func),\n messageId: 'missingReturnType',\n data: {\n handlerType: functionName,\n },\n });\n }\n\n // Report each missing parameter type separately\n for (const paramNode of paramValidation.missingParamNodes) {\n context.report({\n node: paramNode,\n messageId: 'missingParamTypes',\n data: {\n paramName: getParameterName(paramNode),\n },\n });\n }\n}\n\nexport default rule;\n"],"names":[],"mappings":";AAUA,MAAM,mBAAmB,CAAC,SAAS,YAAY,WAAW;AAE1D,MAAM,gBAAgB,CAAC,WAAW,eAAe;AAgBjD,SAAS,iBAAiB,SAA8C;AACpE,QAAM,sCAAsB,IAAA;AAC5B,QAAM,mCAAmB,IAAA;AAEzB,aAAW,aAAa,QAAQ,MAAM;AAClC,QAAI,UAAU,SAAS,eAAe,mBAAmB;AACrD,YAAM,SAAS,UAAU,OAAO;AAEhC,UAAI,WAAW,oBAAoB,WAAW,mBAAmB;AAC7D,mBAAW,aAAa,UAAU,YAAY;AAC1C,cACI,UAAU,SAAS,eAAe,mBAClC,UAAU,SAAS,SAAS,eAAe,YAC7C;AACE,kBAAM,OAAO,UAAU,SAAS;AAChC,gBAAI,iBAAiB,SAAS,IAAyC,GAAG;AACtE,8BAAgB,IAAI,IAAI;AAAA,YAC5B;AACA,gBAAI,cAAc,SAAS,IAAsC,GAAG;AAChE,2BAAa,IAAI,IAAI;AAAA,YACzB;AAAA,UACJ;AAAA,QACJ;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ;AAEA,SAAO,EAAC,iBAAiB,aAAA;AAC7B;AAMA,SAAS,sBAAsB,MAA+B,aAA+C;AAEzG,MAAI,KAAK,OAAO,SAAS,eAAe,YAAY;AAChD,WAAO;AAAA,EACX;AAEA,QAAM,eAAe,KAAK,OAAO;AAGjC,MAAI,YAAY,gBAAgB,IAAI,YAAY,GAAG;AAC/C,WAAO;AAAA,EACX;AAEA,SAAO;AACX;AAYA,SAAS,mBAAmB,SAA0C;AAClE,QAAM,4BAA2B,IAAA;AAEjC,aAAW,aAAa,QAAQ,MAAM;AAElC,QAAI,UAAU,SAAS,eAAe,uBAAuB,UAAU,IAAI,MAAM;AAC7E,YAAM,IAAI,UAAU,GAAG,MAAM,SAAS;AAAA,IAC1C;AAGA,QAAI,UAAU,SAAS,eAAe,qBAAqB;AACvD,iBAAW,cAAc,UAAU,cAAc;AAC7C,YAAI,WAAW,GAAG,SAAS,eAAe,YAAY;AAClD,cACI,WAAW,MAAM,SAAS,eAAe,2BACzC,WAAW,MAAM,SAAS,eAAe,oBAC3C;AACE,kBAAM,IAAI,WAAW,GAAG,MAAM,WAAW,IAAI;AAAA,UACjD;AAAA,QACJ;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ;AAEA,SAAO;AACX;AAMA,SAAS,mBACL,MACA,eACoG;AAEpG,QAAM,eAAe;AAErB,MAAI,KAAK,UAAU,UAAU,cAAc;AACvC,WAAO;AAAA,EACX;AAEA,QAAM,aAAa,KAAK,UAAU,YAAY;AAG9C,MAAI,WAAW,SAAS,eAAe,2BAA2B,WAAW,SAAS,eAAe,oBAAoB;AACrH,WAAO;AAAA,EACX;AAGA,MAAI,WAAW,SAAS,eAAe,YAAY;AAC/C,WAAO,cAAc,IAAI,WAAW,IAAI,KAAK;AAAA,EACjD;AAEA,SAAO;AACX;AAOA,SAAS,sBACL,MACO;AACP,SAAO,KAAK,eAAe;AAC/B;AAOA,SAAS,mBAAmB,MAA8B;AAEtD,MAAI,KAAK,SAAS,eAAe,SAAS;AACtC,UAAM,QAAQ,KAAK;AAEnB,QAAI,YAAY,MAAM;AAClB,aAAO;AAAA,IACX;AACA,WAAO,OAAO,UAAU,aAAa,OAAO,UAAU,YAAY,OAAO,UAAU,YAAY,UAAU;AAAA,EAC7G;AAEA,MAAI,KAAK,SAAS,eAAe,cAAc,KAAK,SAAS,aAAa;AACtE,WAAO;AAAA,EACX;AAEA,MAAI,KAAK,SAAS,eAAe,mBAAmB,KAAK,aAAa,OAAO,KAAK,SAAS,SAAS,eAAe,SAAS;AACxH,WAAO,OAAO,KAAK,SAAS,UAAU;AAAA,EAC1C;AACA,SAAO;AACX;AAOA,SAAS,uBACL,MAKF;AACE,QAAM,oBAA8B,CAAA;AACpC,QAAM,oBAA0C,CAAA;AAGhD,WAAS,IAAI,GAAG,IAAI,KAAK,OAAO,QAAQ,KAAK;AACzC,UAAM,QAAQ,KAAK,OAAO,CAAC;AAE3B,QAAI,MAAM,SAAS,eAAe,YAAY;AAC1C,UAAI,CAAC,MAAM,gBAAgB;AACvB,0BAAkB,KAAK,MAAM,IAAI;AACjC,0BAAkB,KAAK,KAAK;AAAA,MAChC;AAAA,IACJ,WAAW,MAAM,SAAS,eAAe,aAAa;AAClD,UAAI,MAAM,SAAS,SAAS,eAAe,cAAc,CAAC,MAAM,gBAAgB;AAC5E,0BAAkB,KAAK,MAAM,MAAM,SAAS,IAAI,EAAE;AAClD,0BAAkB,KAAK,KAAK;AAAA,MAChC;AAAA,IACJ,WAAW,MAAM,SAAS,eAAe,gBAAgB,MAAM,SAAS,eAAe,eAAe;AAElG,UAAI,CAAC,MAAM,gBAAgB;AACvB,0BAAkB,KAAK,aAAa,IAAI,CAAC,EAAE;AAC3C,0BAAkB,KAAK,KAAK;AAAA,MAChC;AAAA,IACJ,WAAW,MAAM,SAAS,eAAe,mBAAmB;AAKxD,YAAM,6BAA6B,MAAM,mBAAmB;AAC5D,YAAM,0BACF,MAAM,KAAK,SAAS,eAAe,cAAc,MAAM,KAAK,mBAAmB;AACnF,YAAM,oBAAoB,8BAA8B;AACxD,YAAM,sBAAsB,mBAAmB,MAAM,KAAK;AAE1D,UAAI,CAAC,qBAAqB,CAAC,qBAAqB;AAE5C,cAAM,YAAY,MAAM,KAAK,SAAS,eAAe,aAAa,MAAM,KAAK,OAAO,aAAa,IAAI,CAAC;AACtG,0BAAkB,KAAK,SAAS;AAChC,0BAAkB,KAAK,KAAK;AAAA,MAChC;AAAA,IACJ,WAES,EAAE,oBAAoB,UAAU,CAAC,MAAM,gBAAgB;AAC5D,wBAAkB,KAAK,aAAa,IAAI,CAAC,EAAE;AAC3C,wBAAkB,KAAK,KAAK;AAAA,IAChC;AAAA,EACJ;AAEA,SAAO;AAAA,IACH,OAAO,kBAAkB,WAAW;AAAA,IACpC;AAAA,IACA;AAAA,EAAA;AAER;AAKA,SAAS,iBAAiB,OAAmC;AACzD,MAAI,MAAM,SAAS,eAAe,YAAY;AAC1C,WAAO,MAAM;AAAA,EACjB,WAAW,MAAM,SAAS,eAAe,eAAe,MAAM,SAAS,SAAS,eAAe,YAAY;AACvG,WAAO,MAAM,MAAM,SAAS,IAAI;AAAA,EACpC,WAAW,MAAM,SAAS,eAAe,cAAc;AACnD,WAAO;AAAA,EACX,WAAW,MAAM,SAAS,eAAe,eAAe;AACpD,WAAO;AAAA,EACX,WAAW,MAAM,SAAS,eAAe,mBAAmB;AACxD,QAAI,MAAM,KAAK,SAAS,eAAe,YAAY;AAC/C,aAAO,MAAM,KAAK;AAAA,IACtB;AACA,WAAO;AAAA,EACX;AACA,SAAO;AACX;AAMA,SAAS,wBACL,MACa;AAEb,OAAK,KAAK,SAAS,eAAe,uBAAuB,KAAK,SAAS,eAAe,uBAAuB,KAAK,IAAI;AAClH,WAAO,KAAK;AAAA,EAChB;AAEA,SAAO;AACX;AAMA,SAAS,6BACL,gBACA,aACkC;AAClC,MAAI,eAAe,eAAe,SAAS,eAAe,iBAAiB;AACvE,UAAM,WAAW,eAAe,eAAe;AAC/C,QAAI,SAAS,SAAS,eAAe,YAAY;AAC7C,YAAM,OAAO,SAAS;AACtB,WAAK,SAAS,aAAa,SAAS,oBAAoB,YAAY,aAAa,IAAI,IAAI,GAAG;AACxF,eAAO;AAAA,MACX;AAAA,IACJ;AAAA,EACJ;AACA,SAAO;AACX;AAMA,SAAS,4BACL,qBACA,aACkC;AAClC,MAAI,oBAAoB,eAAe,SAAS,eAAe,iBAAiB;AAC5E,UAAM,WAAW,oBAAoB,eAAe;AACpD,QAAI,SAAS,SAAS,eAAe,YAAY;AAC7C,YAAM,OAAO,SAAS;AACtB,WAAK,SAAS,aAAa,SAAS,oBAAoB,YAAY,aAAa,IAAI,IAAI,GAAG;AACxF,eAAO;AAAA,MACX;AAAA,IACJ;AAAA,EACJ;AACA,SAAO;AACX;AAQA,SAAS,wBACL,MAKA,SACsD;AACtD,QAAM,aAAa,QAAQ;AAC3B,QAAM,WAAW,WAAW,kBAAkB,IAAI;AAElD,aAAW,WAAW,UAAU;AAC5B,QAAI,QAAQ,SAAS,SAAS;AAC1B,YAAM,cAAc,QAAQ;AAC5B,UAAI,YAAY,SAAS,aAAa,GAAG;AACrC,eAAO;AAAA,MACX;AACA,UAAI,YAAY,SAAS,gBAAgB,GAAG;AACxC,eAAO;AAAA,MACX;AACA,UAAI,YAAY,SAAS,iBAAiB,GAAG;AACzC,eAAO;AAAA,MACX;AAAA,IACJ;AAAA,EACJ;AACA,SAAO;AACX;AAEA,MAAM,OAGF;AAAA,EACA,MAAM;AAAA,IACF,MAAM;AAAA,IACN,MAAM;AAAA,MACF,aAAa;AAAA,IAAA;AAAA,IAEjB,UAAU;AAAA,MACN,mBAAmB;AAAA,MACnB,mBAAmB;AAAA,MACnB,yBAAyB;AAAA,MACzB,yBAAyB;AAAA,IAAA;AAAA,IAE7B,QAAQ,CAAA;AAAA;AAAA,EAAC;AAAA,EAEb,gBAAgB,CAAA;AAAA,EAChB,OAAO,SAAS;AAEZ,QAAI,cAAwC;AAC5C,QAAI,gBAAsC;AAE1C,WAAO;AAAA;AAAA,MAEH,QAAQ,MAAwB;AAC5B,sBAAc,iBAAiB,IAAI;AACnC,wBAAgB,mBAAmB,IAAI;AAAA,MAC3C;AAAA,MACA,eAAe,MAA+B;AAC1C,YAAI,CAAC,eAAe,CAAC,cAAe;AAEpC,cAAM,eAAe,sBAAsB,MAAM,WAAW;AAC5D,YAAI,CAAC,cAAc;AACf;AAAA,QACJ;AAEA,cAAM,cAAc,mBAAmB,MAAM,aAAa;AAC1D,YAAI,CAAC,aAAa;AACd;AAAA,QACJ;AAEA,cAAM,gBAAgB,sBAAsB,WAAW;AACvD,cAAM,kBAAkB,uBAAuB,WAAW;AAG1D,YAAI,CAAC,eAAe;AAChB,kBAAQ,OAAO;AAAA,YACX,MAAM,wBAAwB,WAAW;AAAA,YACzC,WAAW;AAAA,YACX,MAAM;AAAA,cACF,gBAAgB;AAAA,YAAA;AAAA,UACpB,CACH;AAAA,QACL;AAGA,mBAAW,aAAa,gBAAgB,mBAAmB;AACvD,kBAAQ,OAAO;AAAA,YACX,MAAM;AAAA,YACN,WAAW;AAAA,YACX,MAAM;AAAA,cACF,WAAW,iBAAiB,SAAS;AAAA,YAAA;AAAA,UACzC,CACH;AAAA,QACL;AAAA,MACJ;AAAA;AAAA,MAEA,mBAAmB,MAAmC;AAClD,YAAI,CAAC,YAAa;AAElB,YAAI,KAAK,GAAG,SAAS,eAAe,YAAY;AAC5C,cAAI,cAAsE;AAG1E,cAAI,KAAK,GAAG,gBAAgB;AACxB,0BAAc,6BAA6B,KAAK,GAAG,gBAAgB,WAAW;AAAA,UAClF;AAGA,cAAI,CAAC,eAAe,KAAK,QAAQ,SAAS,eAAe,qBAAqB;AAC1E,0BAAc,wBAAwB,KAAK,QAAQ,OAAO;AAAA,UAC9D;AAEA,cACI,gBACC,KAAK,MAAM,SAAS,eAAe,2BAChC,KAAK,MAAM,SAAS,eAAe,qBACzC;AACE,iCAAqB,KAAK,MAAM,aAAa,OAAO;AAAA,UACxD;AAAA,QACJ;AAAA,MACJ;AAAA;AAAA,MAEA,sBAAsB,MAAsC;AACxD,YAAI,CAAC,YAAa;AAElB,cAAM,cAAc,4BAA4B,MAAM,WAAW;AACjE,YACI,gBACC,KAAK,WAAW,SAAS,eAAe,2BACrC,KAAK,WAAW,SAAS,eAAe,qBAC9C;AACE,+BAAqB,KAAK,YAAY,aAAa,OAAO;AAAA,QAC9D;AAAA,MACJ;AAAA;AAAA,MAEA,oBAAoB,MAAoC;AACpD,cAAM,cAAc,wBAAwB,MAAM,OAAO;AACzD,YAAI,aAAa;AACb,+BAAqB,MAAM,aAAa,OAAO;AAAA,QACnD;AAAA,MACJ;AAAA;AAAA,MAEA,wBAAwB,MAAwC;AAC5D,cAAM,cAAc,wBAAwB,MAAM,OAAO;AACzD,YAAI,aAAa;AACb,+BAAqB,MAAM,aAAa,OAAO;AAAA,QACnD;AAAA,MACJ;AAAA,MACA,mBAAmB,MAAmC;AAClD,cAAM,cAAc,wBAAwB,MAAM,OAAO;AACzD,YAAI,aAAa;AACb,+BAAqB,MAAM,aAAa,OAAO;AAAA,QACnD;AAAA,MACJ;AAAA,IAAA;AAAA,EAER;AACJ;AAKA,SAAS,0BAA0B,aAAsE;AACrG,MAAI,gBAAgB,gBAAiB,QAAO;AAC5C,MAAI,gBAAgB,kBAAmB,QAAO;AAC9C,SAAO;AACX;AAQA,SAAS,qBACL,MACA,aACA,SACF;AACE,QAAM,gBAAgB,sBAAsB,IAAI;AAChD,QAAM,kBAAkB,uBAAuB,IAAI;AACnD,QAAM,eAAe,0BAA0B,WAAW;AAG1D,MAAI,CAAC,eAAe;AAChB,YAAQ,OAAO;AAAA,MACX,MAAM,wBAAwB,IAAI;AAAA,MAClC,WAAW;AAAA,MACX,MAAM;AAAA,QACF,aAAa;AAAA,MAAA;AAAA,IACjB,CACH;AAAA,EACL;AAGA,aAAW,aAAa,gBAAgB,mBAAmB;AACvD,YAAQ,OAAO;AAAA,MACX,MAAM;AAAA,MACN,WAAW;AAAA,MACX,MAAM;AAAA,QACF,WAAW,iBAAiB,SAAS;AAAA,MAAA;AAAA,IACzC,CACH;AAAA,EACL;AACJ;"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { AST_NODE_TYPES } from "@typescript-eslint/utils";
|
|
2
|
+
import { FORMAT_TYPES_BY_PACKAGE } from "./formatTypeNames.js";
|
|
3
|
+
function getFormatTypePackage(source) {
|
|
4
|
+
for (const [pkg] of FORMAT_TYPES_BY_PACKAGE) {
|
|
5
|
+
if (source === pkg || source.startsWith(pkg + "/")) return pkg;
|
|
6
|
+
}
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
const rule = {
|
|
10
|
+
meta: {
|
|
11
|
+
type: "problem",
|
|
12
|
+
docs: {
|
|
13
|
+
description: "Disallow type-only imports for TypeFormat types. Type-only imports strip type metadata needed by Deepkit for runtime type reflection, causing silent validation/serialization failures."
|
|
14
|
+
},
|
|
15
|
+
messages: {
|
|
16
|
+
typeFormatsImports: 'Type "{{typeName}}" is imported as type-only from "{{source}}". Remove the "type" keyword from the import to preserve runtime type metadata for Deepkit reflection.'
|
|
17
|
+
},
|
|
18
|
+
schema: []
|
|
19
|
+
},
|
|
20
|
+
defaultOptions: [],
|
|
21
|
+
create(context) {
|
|
22
|
+
return {
|
|
23
|
+
ImportDeclaration(node) {
|
|
24
|
+
const source = node.source.value;
|
|
25
|
+
const matchedPkg = getFormatTypePackage(source);
|
|
26
|
+
if (!matchedPkg) return;
|
|
27
|
+
const formatTypes = FORMAT_TYPES_BY_PACKAGE.get(matchedPkg);
|
|
28
|
+
if (!formatTypes) return;
|
|
29
|
+
const isTypeOnlyImport = node.importKind === "type";
|
|
30
|
+
for (const specifier of node.specifiers) {
|
|
31
|
+
if (specifier.type !== AST_NODE_TYPES.ImportSpecifier) continue;
|
|
32
|
+
const importedName = specifier.imported.type === AST_NODE_TYPES.Identifier ? specifier.imported.name : specifier.imported.value;
|
|
33
|
+
if (!formatTypes.has(importedName)) continue;
|
|
34
|
+
const isTypeOnlySpecifier = specifier.importKind === "type";
|
|
35
|
+
if (isTypeOnlyImport || isTypeOnlySpecifier) {
|
|
36
|
+
context.report({
|
|
37
|
+
node: specifier,
|
|
38
|
+
messageId: "typeFormatsImports",
|
|
39
|
+
data: {
|
|
40
|
+
typeName: importedName,
|
|
41
|
+
source
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
export {
|
|
51
|
+
rule as default
|
|
52
|
+
};
|
|
53
|
+
//# sourceMappingURL=type-formats-imports.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type-formats-imports.js","sources":["../../../../../../src/eslint/rules/type-formats-imports.ts"],"sourcesContent":["/* ########\n * 2026 mion\n * Author: Ma-jerez\n * License: MIT\n * The software is provided \"as is\", without warranty of any kind.\n * ######## */\n\nimport {TSESTree, TSESLint, AST_NODE_TYPES} from '@typescript-eslint/utils';\nimport {FORMAT_TYPES_BY_PACKAGE} from './formatTypeNames.ts';\n\n/** Checks if source matches one of the format type packages (exact or subpath) */\nfunction getFormatTypePackage(source: string): string | null {\n for (const [pkg] of FORMAT_TYPES_BY_PACKAGE) {\n if (source === pkg || source.startsWith(pkg + '/')) return pkg;\n }\n return null;\n}\n\nconst rule: TSESLint.RuleModule<'typeFormatsImports', []> = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Disallow type-only imports for TypeFormat types. Type-only imports strip type metadata needed by Deepkit for runtime type reflection, causing silent validation/serialization failures.',\n },\n messages: {\n typeFormatsImports:\n 'Type \"{{typeName}}\" is imported as type-only from \"{{source}}\". Remove the \"type\" keyword from the import to preserve runtime type metadata for Deepkit reflection.',\n },\n schema: [],\n },\n defaultOptions: [],\n create(context) {\n return {\n ImportDeclaration(node: TSESTree.ImportDeclaration) {\n const source = node.source.value;\n const matchedPkg = getFormatTypePackage(source);\n if (!matchedPkg) return;\n\n const formatTypes = FORMAT_TYPES_BY_PACKAGE.get(matchedPkg);\n if (!formatTypes) return;\n\n const isTypeOnlyImport = node.importKind === 'type';\n\n for (const specifier of node.specifiers) {\n if (specifier.type !== AST_NODE_TYPES.ImportSpecifier) continue;\n\n const importedName =\n specifier.imported.type === AST_NODE_TYPES.Identifier\n ? specifier.imported.name\n : specifier.imported.value;\n\n if (!formatTypes.has(importedName)) continue;\n\n const isTypeOnlySpecifier = specifier.importKind === 'type';\n\n if (isTypeOnlyImport || isTypeOnlySpecifier) {\n context.report({\n node: specifier,\n messageId: 'typeFormatsImports',\n data: {\n typeName: importedName,\n source,\n },\n });\n }\n }\n },\n };\n },\n};\n\nexport default rule;\n"],"names":[],"mappings":";;AAWA,SAAS,qBAAqB,QAA+B;AACzD,aAAW,CAAC,GAAG,KAAK,yBAAyB;AACzC,QAAI,WAAW,OAAO,OAAO,WAAW,MAAM,GAAG,EAAG,QAAO;AAAA,EAC/D;AACA,SAAO;AACX;AAEA,MAAM,OAAsD;AAAA,EACxD,MAAM;AAAA,IACF,MAAM;AAAA,IACN,MAAM;AAAA,MACF,aACI;AAAA,IAAA;AAAA,IAER,UAAU;AAAA,MACN,oBACI;AAAA,IAAA;AAAA,IAER,QAAQ,CAAA;AAAA,EAAC;AAAA,EAEb,gBAAgB,CAAA;AAAA,EAChB,OAAO,SAAS;AACZ,WAAO;AAAA,MACH,kBAAkB,MAAkC;AAChD,cAAM,SAAS,KAAK,OAAO;AAC3B,cAAM,aAAa,qBAAqB,MAAM;AAC9C,YAAI,CAAC,WAAY;AAEjB,cAAM,cAAc,wBAAwB,IAAI,UAAU;AAC1D,YAAI,CAAC,YAAa;AAElB,cAAM,mBAAmB,KAAK,eAAe;AAE7C,mBAAW,aAAa,KAAK,YAAY;AACrC,cAAI,UAAU,SAAS,eAAe,gBAAiB;AAEvD,gBAAM,eACF,UAAU,SAAS,SAAS,eAAe,aACrC,UAAU,SAAS,OACnB,UAAU,SAAS;AAE7B,cAAI,CAAC,YAAY,IAAI,YAAY,EAAG;AAEpC,gBAAM,sBAAsB,UAAU,eAAe;AAErD,cAAI,oBAAoB,qBAAqB;AACzC,oBAAQ,OAAO;AAAA,cACX,MAAM;AAAA,cACN,WAAW;AAAA,cACX,MAAM;AAAA,gBACF,UAAU;AAAA,gBACV;AAAA,cAAA;AAAA,YACJ,CACH;AAAA,UACL;AAAA,QACJ;AAAA,MACJ;AAAA,IAAA;AAAA,EAER;AACJ;"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const ALLOWED_GLOBALS: Set<string>;
|
|
2
|
+
export declare const FORBIDDEN_IDENTIFIERS: Set<string>;
|
|
3
|
+
export declare const PURE_FN_CALL_NAMES: readonly ["pureServerFn", "registerPureFnFactory", "mapFrom"];
|
|
4
|
+
export declare const PURE_FN_SOURCE_PACKAGES: readonly ["@mionjs/core", "@mionjs/client"];
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
const ALLOWED_GLOBALS = /* @__PURE__ */ new Set([
|
|
2
|
+
// Value types
|
|
3
|
+
"undefined",
|
|
4
|
+
"null",
|
|
5
|
+
"NaN",
|
|
6
|
+
"Infinity",
|
|
7
|
+
"true",
|
|
8
|
+
"false",
|
|
9
|
+
// Built-in constructors/objects
|
|
10
|
+
"Object",
|
|
11
|
+
"Array",
|
|
12
|
+
"String",
|
|
13
|
+
"Number",
|
|
14
|
+
"Boolean",
|
|
15
|
+
"Math",
|
|
16
|
+
"JSON",
|
|
17
|
+
"Date",
|
|
18
|
+
"RegExp",
|
|
19
|
+
"Map",
|
|
20
|
+
"Set",
|
|
21
|
+
"WeakMap",
|
|
22
|
+
"WeakSet",
|
|
23
|
+
"Symbol",
|
|
24
|
+
"BigInt",
|
|
25
|
+
"Promise",
|
|
26
|
+
"Error",
|
|
27
|
+
"TypeError",
|
|
28
|
+
"RangeError",
|
|
29
|
+
"parseInt",
|
|
30
|
+
"parseFloat",
|
|
31
|
+
"isNaN",
|
|
32
|
+
"isFinite",
|
|
33
|
+
"encodeURIComponent",
|
|
34
|
+
"decodeURIComponent",
|
|
35
|
+
"encodeURI",
|
|
36
|
+
"decodeURI",
|
|
37
|
+
// Common safe globals
|
|
38
|
+
"console",
|
|
39
|
+
"globalThis",
|
|
40
|
+
"Bun"
|
|
41
|
+
]);
|
|
42
|
+
const FORBIDDEN_IDENTIFIERS = /* @__PURE__ */ new Set([
|
|
43
|
+
"eval",
|
|
44
|
+
"Function",
|
|
45
|
+
"fetch",
|
|
46
|
+
"setTimeout",
|
|
47
|
+
"setInterval",
|
|
48
|
+
"clearTimeout",
|
|
49
|
+
"clearInterval",
|
|
50
|
+
"process",
|
|
51
|
+
"window",
|
|
52
|
+
"document",
|
|
53
|
+
"global",
|
|
54
|
+
"require",
|
|
55
|
+
"XMLHttpRequest",
|
|
56
|
+
"WebSocket",
|
|
57
|
+
"localStorage",
|
|
58
|
+
"sessionStorage",
|
|
59
|
+
"indexedDB"
|
|
60
|
+
]);
|
|
61
|
+
const PURE_FN_SOURCE_PACKAGES = ["@mionjs/core", "@mionjs/client"];
|
|
62
|
+
export {
|
|
63
|
+
ALLOWED_GLOBALS,
|
|
64
|
+
FORBIDDEN_IDENTIFIERS,
|
|
65
|
+
PURE_FN_SOURCE_PACKAGES
|
|
66
|
+
};
|
|
67
|
+
//# sourceMappingURL=purityRules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"purityRules.js","sources":["../../../../../src/pureFns/purityRules.ts"],"sourcesContent":["/* ########\n * 2026 mion\n * Author: Ma-jerez\n * License: MIT\n * The software is provided \"as is\", without warranty of any kind.\n * ######## */\n\n/** Globals and built-ins that are allowed inside pure functions */\nexport const ALLOWED_GLOBALS = new Set([\n // Value types\n 'undefined',\n 'null',\n 'NaN',\n 'Infinity',\n 'true',\n 'false',\n // Built-in constructors/objects\n 'Object',\n 'Array',\n 'String',\n 'Number',\n 'Boolean',\n 'Math',\n 'JSON',\n 'Date',\n 'RegExp',\n 'Map',\n 'Set',\n 'WeakMap',\n 'WeakSet',\n 'Symbol',\n 'BigInt',\n 'Promise',\n 'Error',\n 'TypeError',\n 'RangeError',\n 'parseInt',\n 'parseFloat',\n 'isNaN',\n 'isFinite',\n 'encodeURIComponent',\n 'decodeURIComponent',\n 'encodeURI',\n 'decodeURI',\n // Common safe globals\n 'console',\n 'globalThis',\n 'Bun',\n]);\n\n/** Forbidden identifiers that indicate impure operations in pure functions */\nexport const FORBIDDEN_IDENTIFIERS = new Set([\n 'eval',\n 'Function',\n 'fetch',\n 'setTimeout',\n 'setInterval',\n 'clearTimeout',\n 'clearInterval',\n 'process',\n 'window',\n 'document',\n 'global',\n 'require',\n 'XMLHttpRequest',\n 'WebSocket',\n 'localStorage',\n 'sessionStorage',\n 'indexedDB',\n]);\n\n/** The target function names that purity validation applies to */\nexport const PURE_FN_CALL_NAMES = ['pureServerFn', 'registerPureFnFactory', 'mapFrom'] as const;\n\n/** The packages that export the target pure function APIs */\nexport const PURE_FN_SOURCE_PACKAGES = ['@mionjs/core', '@mionjs/client'] as const;\n"],"names":[],"mappings":"AAQO,MAAM,sCAAsB,IAAI;AAAA;AAAA,EAEnC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AACJ,CAAC;AAGM,MAAM,4CAA4B,IAAI;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACJ,CAAC;AAMM,MAAM,0BAA0B,CAAC,gBAAgB,gBAAgB;"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const src_vitePlugin_mionVitePlugin = require("./src/vite-plugin/mionVitePlugin.cjs");
|
|
4
|
+
const src_vitePlugin_cjsPackageJsonPlugin = require("./src/vite-plugin/cjsPackageJsonPlugin.cjs");
|
|
5
|
+
const src_vitePlugin_constants = require("./src/vite-plugin/constants.cjs");
|
|
6
|
+
exports.mionPlugin = src_vitePlugin_mionVitePlugin.mionVitePlugin;
|
|
7
|
+
exports.mionVitePlugin = src_vitePlugin_mionVitePlugin.mionVitePlugin;
|
|
8
|
+
exports.serverReady = src_vitePlugin_mionVitePlugin.serverReady;
|
|
9
|
+
exports.cjsPackageJsonPlugin = src_vitePlugin_cjsPackageJsonPlugin.cjsPackageJsonPlugin;
|
|
10
|
+
exports.VIRTUAL_AOT_JIT_FNS = src_vitePlugin_constants.VIRTUAL_AOT_JIT_FNS;
|
|
11
|
+
exports.VIRTUAL_AOT_PURE_FNS = src_vitePlugin_constants.VIRTUAL_AOT_PURE_FNS;
|
|
12
|
+
exports.VIRTUAL_AOT_ROUTER_CACHE = src_vitePlugin_constants.VIRTUAL_AOT_ROUTER_CACHE;
|
|
13
|
+
exports.VIRTUAL_PURE_FUNCTIONS = src_vitePlugin_constants.VIRTUAL_SERVER_PURE_FNS;
|
|
14
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"commonjs"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const ALLOWED_GLOBALS = /* @__PURE__ */ new Set([
|
|
4
|
+
// Value types
|
|
5
|
+
"undefined",
|
|
6
|
+
"null",
|
|
7
|
+
"NaN",
|
|
8
|
+
"Infinity",
|
|
9
|
+
"true",
|
|
10
|
+
"false",
|
|
11
|
+
// Built-in constructors/objects
|
|
12
|
+
"Object",
|
|
13
|
+
"Array",
|
|
14
|
+
"String",
|
|
15
|
+
"Number",
|
|
16
|
+
"Boolean",
|
|
17
|
+
"Math",
|
|
18
|
+
"JSON",
|
|
19
|
+
"Date",
|
|
20
|
+
"RegExp",
|
|
21
|
+
"Map",
|
|
22
|
+
"Set",
|
|
23
|
+
"WeakMap",
|
|
24
|
+
"WeakSet",
|
|
25
|
+
"Symbol",
|
|
26
|
+
"BigInt",
|
|
27
|
+
"Promise",
|
|
28
|
+
"Error",
|
|
29
|
+
"TypeError",
|
|
30
|
+
"RangeError",
|
|
31
|
+
"parseInt",
|
|
32
|
+
"parseFloat",
|
|
33
|
+
"isNaN",
|
|
34
|
+
"isFinite",
|
|
35
|
+
"encodeURIComponent",
|
|
36
|
+
"decodeURIComponent",
|
|
37
|
+
"encodeURI",
|
|
38
|
+
"decodeURI",
|
|
39
|
+
// Common safe globals
|
|
40
|
+
"console",
|
|
41
|
+
"globalThis",
|
|
42
|
+
"Bun"
|
|
43
|
+
]);
|
|
44
|
+
const FORBIDDEN_IDENTIFIERS = /* @__PURE__ */ new Set([
|
|
45
|
+
"eval",
|
|
46
|
+
"Function",
|
|
47
|
+
"fetch",
|
|
48
|
+
"setTimeout",
|
|
49
|
+
"setInterval",
|
|
50
|
+
"clearTimeout",
|
|
51
|
+
"clearInterval",
|
|
52
|
+
"process",
|
|
53
|
+
"window",
|
|
54
|
+
"document",
|
|
55
|
+
"global",
|
|
56
|
+
"require",
|
|
57
|
+
"XMLHttpRequest",
|
|
58
|
+
"WebSocket",
|
|
59
|
+
"localStorage",
|
|
60
|
+
"sessionStorage",
|
|
61
|
+
"indexedDB"
|
|
62
|
+
]);
|
|
63
|
+
exports.ALLOWED_GLOBALS = ALLOWED_GLOBALS;
|
|
64
|
+
exports.FORBIDDEN_IDENTIFIERS = FORBIDDEN_IDENTIFIERS;
|
|
65
|
+
//# sourceMappingURL=purityRules.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"purityRules.cjs","sources":["../../../../../src/pureFns/purityRules.ts"],"sourcesContent":["/* ########\n * 2026 mion\n * Author: Ma-jerez\n * License: MIT\n * The software is provided \"as is\", without warranty of any kind.\n * ######## */\n\n/** Globals and built-ins that are allowed inside pure functions */\nexport const ALLOWED_GLOBALS = new Set([\n // Value types\n 'undefined',\n 'null',\n 'NaN',\n 'Infinity',\n 'true',\n 'false',\n // Built-in constructors/objects\n 'Object',\n 'Array',\n 'String',\n 'Number',\n 'Boolean',\n 'Math',\n 'JSON',\n 'Date',\n 'RegExp',\n 'Map',\n 'Set',\n 'WeakMap',\n 'WeakSet',\n 'Symbol',\n 'BigInt',\n 'Promise',\n 'Error',\n 'TypeError',\n 'RangeError',\n 'parseInt',\n 'parseFloat',\n 'isNaN',\n 'isFinite',\n 'encodeURIComponent',\n 'decodeURIComponent',\n 'encodeURI',\n 'decodeURI',\n // Common safe globals\n 'console',\n 'globalThis',\n 'Bun',\n]);\n\n/** Forbidden identifiers that indicate impure operations in pure functions */\nexport const FORBIDDEN_IDENTIFIERS = new Set([\n 'eval',\n 'Function',\n 'fetch',\n 'setTimeout',\n 'setInterval',\n 'clearTimeout',\n 'clearInterval',\n 'process',\n 'window',\n 'document',\n 'global',\n 'require',\n 'XMLHttpRequest',\n 'WebSocket',\n 'localStorage',\n 'sessionStorage',\n 'indexedDB',\n]);\n\n/** The target function names that purity validation applies to */\nexport const PURE_FN_CALL_NAMES = ['pureServerFn', 'registerPureFnFactory', 'mapFrom'] as const;\n\n/** The packages that export the target pure function APIs */\nexport const PURE_FN_SOURCE_PACKAGES = ['@mionjs/core', '@mionjs/client'] as const;\n"],"names":[],"mappings":";;AAQO,MAAM,sCAAsB,IAAI;AAAA;AAAA,EAEnC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AACJ,CAAC;AAGM,MAAM,4CAA4B,IAAI;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACJ,CAAC;;;"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const ALLOWED_GLOBALS: Set<string>;
|
|
2
|
+
export declare const FORBIDDEN_IDENTIFIERS: Set<string>;
|
|
3
|
+
export declare const PURE_FN_CALL_NAMES: readonly ["pureServerFn", "registerPureFnFactory", "mapFrom"];
|
|
4
|
+
export declare const PURE_FN_SOURCE_PACKAGES: readonly ["@mionjs/core", "@mionjs/client"];
|