@tanstack/eslint-plugin-router 1.154.7 → 1.161.2
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/cjs/index.cjs +4 -2
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/rules/route-param-names/constants.cjs +13 -0
- package/dist/cjs/rules/route-param-names/constants.cjs.map +1 -0
- package/dist/cjs/rules/route-param-names/constants.d.cts +23 -0
- package/dist/cjs/rules/route-param-names/route-param-names.rule.cjs +98 -0
- package/dist/cjs/rules/route-param-names/route-param-names.rule.cjs.map +1 -0
- package/dist/cjs/rules/route-param-names/route-param-names.rule.d.cts +4 -0
- package/dist/cjs/rules/route-param-names/route-param-names.utils.cjs +61 -0
- package/dist/cjs/rules/route-param-names/route-param-names.utils.cjs.map +1 -0
- package/dist/cjs/rules/route-param-names/route-param-names.utils.d.cts +43 -0
- package/dist/cjs/rules.cjs +3 -1
- package/dist/cjs/rules.cjs.map +1 -1
- package/dist/cjs/utils/detect-router-imports.cjs +2 -1
- package/dist/cjs/utils/detect-router-imports.cjs.map +1 -1
- package/dist/esm/index.js +4 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/rules/route-param-names/constants.d.ts +23 -0
- package/dist/esm/rules/route-param-names/constants.js +13 -0
- package/dist/esm/rules/route-param-names/constants.js.map +1 -0
- package/dist/esm/rules/route-param-names/route-param-names.rule.d.ts +4 -0
- package/dist/esm/rules/route-param-names/route-param-names.rule.js +98 -0
- package/dist/esm/rules/route-param-names/route-param-names.rule.js.map +1 -0
- package/dist/esm/rules/route-param-names/route-param-names.utils.d.ts +43 -0
- package/dist/esm/rules/route-param-names/route-param-names.utils.js +61 -0
- package/dist/esm/rules/route-param-names/route-param-names.utils.js.map +1 -0
- package/dist/esm/rules.js +3 -1
- package/dist/esm/rules.js.map +1 -1
- package/dist/esm/utils/detect-router-imports.js +2 -1
- package/dist/esm/utils/detect-router-imports.js.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/route-param-names.rule.test.ts +271 -0
- package/src/__tests__/route-param-names.utils.test.ts +174 -0
- package/src/index.ts +2 -0
- package/src/rules/route-param-names/constants.ts +36 -0
- package/src/rules/route-param-names/route-param-names.rule.ts +127 -0
- package/src/rules/route-param-names/route-param-names.utils.ts +122 -0
- package/src/rules.ts +2 -0
- package/src/utils/detect-router-imports.ts +2 -1
package/dist/cjs/index.cjs
CHANGED
|
@@ -11,7 +11,8 @@ Object.assign(plugin.configs, {
|
|
|
11
11
|
recommended: {
|
|
12
12
|
plugins: ["@tanstack/eslint-plugin-router"],
|
|
13
13
|
rules: {
|
|
14
|
-
"@tanstack/router/create-route-property-order": "warn"
|
|
14
|
+
"@tanstack/router/create-route-property-order": "warn",
|
|
15
|
+
"@tanstack/router/route-param-names": "error"
|
|
15
16
|
}
|
|
16
17
|
},
|
|
17
18
|
"flat/recommended": [
|
|
@@ -20,7 +21,8 @@ Object.assign(plugin.configs, {
|
|
|
20
21
|
"@tanstack/router": plugin
|
|
21
22
|
},
|
|
22
23
|
rules: {
|
|
23
|
-
"@tanstack/router/create-route-property-order": "warn"
|
|
24
|
+
"@tanstack/router/create-route-property-order": "warn",
|
|
25
|
+
"@tanstack/router/route-param-names": "error"
|
|
24
26
|
}
|
|
25
27
|
}
|
|
26
28
|
]
|
package/dist/cjs/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../../src/index.ts"],"sourcesContent":["import { rules } from './rules'\nimport type { ESLint, Linter } from 'eslint'\nimport type { RuleModule } from '@typescript-eslint/utils/ts-eslint'\n\ntype RuleKey = keyof typeof rules\n\ninterface Plugin extends Omit<ESLint.Plugin, 'rules'> {\n rules: Record<RuleKey, RuleModule<any, any, any>>\n configs: {\n recommended: ESLint.ConfigData\n 'flat/recommended': Array<Linter.FlatConfig>\n }\n}\n\nconst plugin: Plugin = {\n meta: {\n name: '@tanstack/eslint-plugin-router',\n },\n configs: {} as Plugin['configs'],\n rules,\n}\n\n// Assign configs here so we can reference `plugin`\nObject.assign(plugin.configs, {\n recommended: {\n plugins: ['@tanstack/eslint-plugin-router'],\n rules: {\n '@tanstack/router/create-route-property-order': 'warn',\n },\n },\n 'flat/recommended': [\n {\n plugins: {\n '@tanstack/router': plugin,\n },\n rules: {\n '@tanstack/router/create-route-property-order': 'warn',\n },\n },\n ],\n})\n\nexport default plugin\n"],"names":["rules"],"mappings":";;AAcA,MAAM,SAAiB;AAAA,EACrB,MAAM;AAAA,IACJ,MAAM;AAAA,EAAA;AAAA,EAER,SAAS,CAAA;AAAA,EAAC,OACVA,MAAAA;AACF;AAGA,OAAO,OAAO,OAAO,SAAS;AAAA,EAC5B,aAAa;AAAA,IACX,SAAS,CAAC,gCAAgC;AAAA,IAC1C,OAAO;AAAA,MACL,gDAAgD;AAAA,IAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../../src/index.ts"],"sourcesContent":["import { rules } from './rules'\nimport type { ESLint, Linter } from 'eslint'\nimport type { RuleModule } from '@typescript-eslint/utils/ts-eslint'\n\ntype RuleKey = keyof typeof rules\n\ninterface Plugin extends Omit<ESLint.Plugin, 'rules'> {\n rules: Record<RuleKey, RuleModule<any, any, any>>\n configs: {\n recommended: ESLint.ConfigData\n 'flat/recommended': Array<Linter.FlatConfig>\n }\n}\n\nconst plugin: Plugin = {\n meta: {\n name: '@tanstack/eslint-plugin-router',\n },\n configs: {} as Plugin['configs'],\n rules,\n}\n\n// Assign configs here so we can reference `plugin`\nObject.assign(plugin.configs, {\n recommended: {\n plugins: ['@tanstack/eslint-plugin-router'],\n rules: {\n '@tanstack/router/create-route-property-order': 'warn',\n '@tanstack/router/route-param-names': 'error',\n },\n },\n 'flat/recommended': [\n {\n plugins: {\n '@tanstack/router': plugin,\n },\n rules: {\n '@tanstack/router/create-route-property-order': 'warn',\n '@tanstack/router/route-param-names': 'error',\n },\n },\n ],\n})\n\nexport default plugin\n"],"names":["rules"],"mappings":";;AAcA,MAAM,SAAiB;AAAA,EACrB,MAAM;AAAA,IACJ,MAAM;AAAA,EAAA;AAAA,EAER,SAAS,CAAA;AAAA,EAAC,OACVA,MAAAA;AACF;AAGA,OAAO,OAAO,OAAO,SAAS;AAAA,EAC5B,aAAa;AAAA,IACX,SAAS,CAAC,gCAAgC;AAAA,IAC1C,OAAO;AAAA,MACL,gDAAgD;AAAA,MAChD,sCAAsC;AAAA,IAAA;AAAA,EACxC;AAAA,EAEF,oBAAoB;AAAA,IAClB;AAAA,MACE,SAAS;AAAA,QACP,oBAAoB;AAAA,MAAA;AAAA,MAEtB,OAAO;AAAA,QACL,gDAAgD;AAAA,QAChD,sCAAsC;AAAA,MAAA;AAAA,IACxC;AAAA,EACF;AAEJ,CAAC;;"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const pathAsFirstArgFunctions = [
|
|
4
|
+
"createFileRoute",
|
|
5
|
+
"createLazyFileRoute",
|
|
6
|
+
"createLazyRoute"
|
|
7
|
+
];
|
|
8
|
+
const pathAsPropertyFunctions = ["createRoute"];
|
|
9
|
+
const VALID_PARAM_NAME_REGEX = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
|
10
|
+
exports.VALID_PARAM_NAME_REGEX = VALID_PARAM_NAME_REGEX;
|
|
11
|
+
exports.pathAsFirstArgFunctions = pathAsFirstArgFunctions;
|
|
12
|
+
exports.pathAsPropertyFunctions = pathAsPropertyFunctions;
|
|
13
|
+
//# sourceMappingURL=constants.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.cjs","sources":["../../../../src/rules/route-param-names/constants.ts"],"sourcesContent":["/**\n * Functions where the path is passed as the first argument (string literal)\n * e.g., createFileRoute('/path/$param')(...)\n */\nexport const pathAsFirstArgFunctions = [\n 'createFileRoute',\n 'createLazyFileRoute',\n 'createLazyRoute',\n] as const\n\nexport type PathAsFirstArgFunction = (typeof pathAsFirstArgFunctions)[number]\n\n/**\n * Functions where the path is a property in the options object\n * e.g., createRoute({ path: '/path/$param' })\n */\nexport const pathAsPropertyFunctions = ['createRoute'] as const\n\nexport type PathAsPropertyFunction = (typeof pathAsPropertyFunctions)[number]\n\n/**\n * All route functions that need param name validation\n */\nexport const allRouteFunctions = [\n ...pathAsFirstArgFunctions,\n ...pathAsPropertyFunctions,\n] as const\n\nexport type RouteFunction = (typeof allRouteFunctions)[number]\n\n/**\n * Regex for valid JavaScript identifier (param name)\n * Must start with letter, underscore, or dollar sign\n * Can contain letters, numbers, underscores, or dollar signs\n */\nexport const VALID_PARAM_NAME_REGEX = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/\n"],"names":[],"mappings":";;AAIO,MAAM,0BAA0B;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AACF;AAQO,MAAM,0BAA0B,CAAC,aAAa;AAmB9C,MAAM,yBAAyB;;;;"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Functions where the path is passed as the first argument (string literal)
|
|
3
|
+
* e.g., createFileRoute('/path/$param')(...)
|
|
4
|
+
*/
|
|
5
|
+
export declare const pathAsFirstArgFunctions: readonly ["createFileRoute", "createLazyFileRoute", "createLazyRoute"];
|
|
6
|
+
export type PathAsFirstArgFunction = (typeof pathAsFirstArgFunctions)[number];
|
|
7
|
+
/**
|
|
8
|
+
* Functions where the path is a property in the options object
|
|
9
|
+
* e.g., createRoute({ path: '/path/$param' })
|
|
10
|
+
*/
|
|
11
|
+
export declare const pathAsPropertyFunctions: readonly ["createRoute"];
|
|
12
|
+
export type PathAsPropertyFunction = (typeof pathAsPropertyFunctions)[number];
|
|
13
|
+
/**
|
|
14
|
+
* All route functions that need param name validation
|
|
15
|
+
*/
|
|
16
|
+
export declare const allRouteFunctions: readonly ["createFileRoute", "createLazyFileRoute", "createLazyRoute", "createRoute"];
|
|
17
|
+
export type RouteFunction = (typeof allRouteFunctions)[number];
|
|
18
|
+
/**
|
|
19
|
+
* Regex for valid JavaScript identifier (param name)
|
|
20
|
+
* Must start with letter, underscore, or dollar sign
|
|
21
|
+
* Can contain letters, numbers, underscores, or dollar signs
|
|
22
|
+
*/
|
|
23
|
+
export declare const VALID_PARAM_NAME_REGEX: RegExp;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const utils = require("@typescript-eslint/utils");
|
|
4
|
+
const getDocsUrl = require("../../utils/get-docs-url.cjs");
|
|
5
|
+
const detectRouterImports = require("../../utils/detect-router-imports.cjs");
|
|
6
|
+
const routeParamNames_utils = require("./route-param-names.utils.cjs");
|
|
7
|
+
const constants = require("./constants.cjs");
|
|
8
|
+
const createRule = utils.ESLintUtils.RuleCreator(getDocsUrl.getDocsUrl);
|
|
9
|
+
const pathAsFirstArgSet = new Set(constants.pathAsFirstArgFunctions);
|
|
10
|
+
const pathAsPropertySet = new Set(constants.pathAsPropertyFunctions);
|
|
11
|
+
const name = "route-param-names";
|
|
12
|
+
const rule = createRule({
|
|
13
|
+
name,
|
|
14
|
+
meta: {
|
|
15
|
+
type: "problem",
|
|
16
|
+
docs: {
|
|
17
|
+
description: "Ensure route param names are valid JavaScript identifiers",
|
|
18
|
+
recommended: "error"
|
|
19
|
+
},
|
|
20
|
+
messages: {
|
|
21
|
+
invalidParamName: 'Invalid param name "{{paramName}}" in route path. Param names must be valid JavaScript identifiers (match /[a-zA-Z_$][a-zA-Z0-9_$]*/).'
|
|
22
|
+
},
|
|
23
|
+
schema: []
|
|
24
|
+
},
|
|
25
|
+
defaultOptions: [],
|
|
26
|
+
create: detectRouterImports.detectTanstackRouterImports((context, _, helpers) => {
|
|
27
|
+
function reportInvalidParams(node, path) {
|
|
28
|
+
const invalidParams = routeParamNames_utils.getInvalidParams(path);
|
|
29
|
+
for (const param of invalidParams) {
|
|
30
|
+
context.report({
|
|
31
|
+
node,
|
|
32
|
+
messageId: "invalidParamName",
|
|
33
|
+
data: { paramName: param.paramName }
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function getStringLiteralValue(node) {
|
|
38
|
+
if (node.type === utils.AST_NODE_TYPES.Literal && typeof node.value === "string") {
|
|
39
|
+
return node.value;
|
|
40
|
+
}
|
|
41
|
+
if (node.type === utils.AST_NODE_TYPES.TemplateLiteral && node.quasis.length === 1) {
|
|
42
|
+
const cooked = node.quasis[0]?.value.cooked;
|
|
43
|
+
if (cooked != null) {
|
|
44
|
+
return cooked;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
CallExpression(node) {
|
|
51
|
+
if (node.callee.type === utils.AST_NODE_TYPES.Identifier) {
|
|
52
|
+
const funcName = node.callee.name;
|
|
53
|
+
if (!helpers.isTanstackRouterImport(node.callee)) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (pathAsPropertySet.has(funcName)) {
|
|
57
|
+
const arg = node.arguments[0];
|
|
58
|
+
if (arg?.type === utils.AST_NODE_TYPES.ObjectExpression) {
|
|
59
|
+
for (const prop of arg.properties) {
|
|
60
|
+
if (prop.type === utils.AST_NODE_TYPES.Property) {
|
|
61
|
+
const isPathKey = prop.key.type === utils.AST_NODE_TYPES.Identifier && prop.key.name === "path" || prop.key.type === utils.AST_NODE_TYPES.Literal && prop.key.value === "path";
|
|
62
|
+
if (isPathKey) {
|
|
63
|
+
const pathValue = getStringLiteralValue(prop.value);
|
|
64
|
+
if (pathValue) {
|
|
65
|
+
reportInvalidParams(prop.value, pathValue);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (node.callee.type === utils.AST_NODE_TYPES.CallExpression) {
|
|
75
|
+
const innerCall = node.callee;
|
|
76
|
+
if (innerCall.callee.type === utils.AST_NODE_TYPES.Identifier) {
|
|
77
|
+
const funcName = innerCall.callee.name;
|
|
78
|
+
if (!helpers.isTanstackRouterImport(innerCall.callee)) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (pathAsFirstArgSet.has(funcName)) {
|
|
82
|
+
const pathArg = innerCall.arguments[0];
|
|
83
|
+
if (pathArg) {
|
|
84
|
+
const pathValue = getStringLiteralValue(pathArg);
|
|
85
|
+
if (pathValue) {
|
|
86
|
+
reportInvalidParams(pathArg, pathValue);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
})
|
|
95
|
+
});
|
|
96
|
+
exports.name = name;
|
|
97
|
+
exports.rule = rule;
|
|
98
|
+
//# sourceMappingURL=route-param-names.rule.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-param-names.rule.cjs","sources":["../../../../src/rules/route-param-names/route-param-names.rule.ts"],"sourcesContent":["import { AST_NODE_TYPES, ESLintUtils } from '@typescript-eslint/utils'\n\nimport { getDocsUrl } from '../../utils/get-docs-url'\nimport { detectTanstackRouterImports } from '../../utils/detect-router-imports'\nimport { getInvalidParams } from './route-param-names.utils'\nimport { pathAsFirstArgFunctions, pathAsPropertyFunctions } from './constants'\nimport type { TSESTree } from '@typescript-eslint/utils'\nimport type { ExtraRuleDocs } from '../../types'\n\nconst createRule = ESLintUtils.RuleCreator<ExtraRuleDocs>(getDocsUrl)\n\nconst pathAsFirstArgSet = new Set<string>(pathAsFirstArgFunctions)\nconst pathAsPropertySet = new Set<string>(pathAsPropertyFunctions)\n\nexport const name = 'route-param-names'\n\nexport const rule = createRule({\n name,\n meta: {\n type: 'problem',\n docs: {\n description: 'Ensure route param names are valid JavaScript identifiers',\n recommended: 'error',\n },\n messages: {\n invalidParamName:\n 'Invalid param name \"{{paramName}}\" in route path. Param names must be valid JavaScript identifiers (match /[a-zA-Z_$][a-zA-Z0-9_$]*/).',\n },\n schema: [],\n },\n defaultOptions: [],\n\n create: detectTanstackRouterImports((context, _, helpers) => {\n function reportInvalidParams(node: TSESTree.Node, path: string) {\n const invalidParams = getInvalidParams(path)\n\n for (const param of invalidParams) {\n context.report({\n node,\n messageId: 'invalidParamName',\n data: { paramName: param.paramName },\n })\n }\n }\n\n function getStringLiteralValue(node: TSESTree.Node): string | null {\n if (\n node.type === AST_NODE_TYPES.Literal &&\n typeof node.value === 'string'\n ) {\n return node.value\n }\n if (\n node.type === AST_NODE_TYPES.TemplateLiteral &&\n node.quasis.length === 1\n ) {\n const cooked = node.quasis[0]?.value.cooked\n if (cooked != null) {\n return cooked\n }\n }\n return null\n }\n\n return {\n CallExpression(node) {\n // Handle direct function call: createRoute({ path: '...' })\n if (node.callee.type === AST_NODE_TYPES.Identifier) {\n const funcName = node.callee.name\n\n // Skip if not imported from TanStack Router\n if (!helpers.isTanstackRouterImport(node.callee)) {\n return\n }\n\n // Case: createRoute({ path: '/path/$param' }) or createRoute({ 'path': '/path/$param' })\n if (pathAsPropertySet.has(funcName)) {\n const arg = node.arguments[0]\n if (arg?.type === AST_NODE_TYPES.ObjectExpression) {\n for (const prop of arg.properties) {\n if (prop.type === AST_NODE_TYPES.Property) {\n const isPathKey =\n (prop.key.type === AST_NODE_TYPES.Identifier &&\n prop.key.name === 'path') ||\n (prop.key.type === AST_NODE_TYPES.Literal &&\n prop.key.value === 'path')\n if (isPathKey) {\n const pathValue = getStringLiteralValue(prop.value)\n if (pathValue) {\n reportInvalidParams(prop.value, pathValue)\n }\n }\n }\n }\n }\n return\n }\n }\n\n // Handle curried function call: createFileRoute('/path')({ ... })\n if (node.callee.type === AST_NODE_TYPES.CallExpression) {\n const innerCall = node.callee\n\n if (innerCall.callee.type === AST_NODE_TYPES.Identifier) {\n const funcName = innerCall.callee.name\n\n // Skip if not imported from TanStack Router\n if (!helpers.isTanstackRouterImport(innerCall.callee)) {\n return\n }\n\n // Case: createFileRoute('/path/$param')(...) or similar\n if (pathAsFirstArgSet.has(funcName)) {\n const pathArg = innerCall.arguments[0]\n if (pathArg) {\n const pathValue = getStringLiteralValue(pathArg)\n if (pathValue) {\n reportInvalidParams(pathArg, pathValue)\n }\n }\n }\n }\n }\n },\n }\n }),\n})\n"],"names":["ESLintUtils","getDocsUrl","pathAsFirstArgFunctions","pathAsPropertyFunctions","detectTanstackRouterImports","getInvalidParams","AST_NODE_TYPES"],"mappings":";;;;;;;AASA,MAAM,aAAaA,MAAAA,YAAY,YAA2BC,qBAAU;AAEpE,MAAM,oBAAoB,IAAI,IAAYC,iCAAuB;AACjE,MAAM,oBAAoB,IAAI,IAAYC,iCAAuB;AAE1D,MAAM,OAAO;AAEb,MAAM,OAAO,WAAW;AAAA,EAC7B;AAAA,EACA,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aAAa;AAAA,MACb,aAAa;AAAA,IAAA;AAAA,IAEf,UAAU;AAAA,MACR,kBACE;AAAA,IAAA;AAAA,IAEJ,QAAQ,CAAA;AAAA,EAAC;AAAA,EAEX,gBAAgB,CAAA;AAAA,EAEhB,QAAQC,oBAAAA,4BAA4B,CAAC,SAAS,GAAG,YAAY;AAC3D,aAAS,oBAAoB,MAAqB,MAAc;AAC9D,YAAM,gBAAgBC,sBAAAA,iBAAiB,IAAI;AAE3C,iBAAW,SAAS,eAAe;AACjC,gBAAQ,OAAO;AAAA,UACb;AAAA,UACA,WAAW;AAAA,UACX,MAAM,EAAE,WAAW,MAAM,UAAA;AAAA,QAAU,CACpC;AAAA,MACH;AAAA,IACF;AAEA,aAAS,sBAAsB,MAAoC;AACjE,UACE,KAAK,SAASC,MAAAA,eAAe,WAC7B,OAAO,KAAK,UAAU,UACtB;AACA,eAAO,KAAK;AAAA,MACd;AACA,UACE,KAAK,SAASA,qBAAe,mBAC7B,KAAK,OAAO,WAAW,GACvB;AACA,cAAM,SAAS,KAAK,OAAO,CAAC,GAAG,MAAM;AACrC,YAAI,UAAU,MAAM;AAClB,iBAAO;AAAA,QACT;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,MACL,eAAe,MAAM;AAEnB,YAAI,KAAK,OAAO,SAASA,MAAAA,eAAe,YAAY;AAClD,gBAAM,WAAW,KAAK,OAAO;AAG7B,cAAI,CAAC,QAAQ,uBAAuB,KAAK,MAAM,GAAG;AAChD;AAAA,UACF;AAGA,cAAI,kBAAkB,IAAI,QAAQ,GAAG;AACnC,kBAAM,MAAM,KAAK,UAAU,CAAC;AAC5B,gBAAI,KAAK,SAASA,MAAAA,eAAe,kBAAkB;AACjD,yBAAW,QAAQ,IAAI,YAAY;AACjC,oBAAI,KAAK,SAASA,MAAAA,eAAe,UAAU;AACzC,wBAAM,YACH,KAAK,IAAI,SAASA,MAAAA,eAAe,cAChC,KAAK,IAAI,SAAS,UACnB,KAAK,IAAI,SAASA,MAAAA,eAAe,WAChC,KAAK,IAAI,UAAU;AACvB,sBAAI,WAAW;AACb,0BAAM,YAAY,sBAAsB,KAAK,KAAK;AAClD,wBAAI,WAAW;AACb,0CAAoB,KAAK,OAAO,SAAS;AAAA,oBAC3C;AAAA,kBACF;AAAA,gBACF;AAAA,cACF;AAAA,YACF;AACA;AAAA,UACF;AAAA,QACF;AAGA,YAAI,KAAK,OAAO,SAASA,MAAAA,eAAe,gBAAgB;AACtD,gBAAM,YAAY,KAAK;AAEvB,cAAI,UAAU,OAAO,SAASA,MAAAA,eAAe,YAAY;AACvD,kBAAM,WAAW,UAAU,OAAO;AAGlC,gBAAI,CAAC,QAAQ,uBAAuB,UAAU,MAAM,GAAG;AACrD;AAAA,YACF;AAGA,gBAAI,kBAAkB,IAAI,QAAQ,GAAG;AACnC,oBAAM,UAAU,UAAU,UAAU,CAAC;AACrC,kBAAI,SAAS;AACX,sBAAM,YAAY,sBAAsB,OAAO;AAC/C,oBAAI,WAAW;AACb,sCAAoB,SAAS,SAAS;AAAA,gBACxC;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IAAA;AAAA,EAEJ,CAAC;AACH,CAAC;;;"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
+
import { ExtraRuleDocs } from '../../types.cjs';
|
|
3
|
+
export declare const name = "route-param-names";
|
|
4
|
+
export declare const rule: ESLintUtils.RuleModule<string, readonly unknown[], ExtraRuleDocs, ESLintUtils.RuleListener>;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const constants = require("./constants.cjs");
|
|
4
|
+
function extractParamsFromSegment(segment) {
|
|
5
|
+
const params = [];
|
|
6
|
+
if (!segment || !segment.includes("$")) {
|
|
7
|
+
return params;
|
|
8
|
+
}
|
|
9
|
+
if (segment === "$" || segment === "{$}") {
|
|
10
|
+
return params;
|
|
11
|
+
}
|
|
12
|
+
if (segment.startsWith("$") && !segment.includes("{")) {
|
|
13
|
+
const paramName = segment.slice(1);
|
|
14
|
+
if (paramName) {
|
|
15
|
+
params.push({
|
|
16
|
+
fullParam: segment,
|
|
17
|
+
paramName,
|
|
18
|
+
isOptional: false,
|
|
19
|
+
isValid: constants.VALID_PARAM_NAME_REGEX.test(paramName)
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
return params;
|
|
23
|
+
}
|
|
24
|
+
const bracePattern = /\{(-?\$)([^}]*)\}/g;
|
|
25
|
+
let match;
|
|
26
|
+
while ((match = bracePattern.exec(segment)) !== null) {
|
|
27
|
+
const prefix = match[1];
|
|
28
|
+
const paramName = match[2];
|
|
29
|
+
if (!paramName) {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
const isOptional = prefix === "-$";
|
|
33
|
+
params.push({
|
|
34
|
+
fullParam: `${prefix}${paramName}`,
|
|
35
|
+
paramName,
|
|
36
|
+
isOptional,
|
|
37
|
+
isValid: constants.VALID_PARAM_NAME_REGEX.test(paramName)
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
return params;
|
|
41
|
+
}
|
|
42
|
+
function extractParamsFromPath(path) {
|
|
43
|
+
if (!path || !path.includes("$")) {
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
const segments = path.split("/");
|
|
47
|
+
const allParams = [];
|
|
48
|
+
for (const segment of segments) {
|
|
49
|
+
const params = extractParamsFromSegment(segment);
|
|
50
|
+
allParams.push(...params);
|
|
51
|
+
}
|
|
52
|
+
return allParams;
|
|
53
|
+
}
|
|
54
|
+
function getInvalidParams(path) {
|
|
55
|
+
const params = extractParamsFromPath(path);
|
|
56
|
+
return params.filter((p) => !p.isValid);
|
|
57
|
+
}
|
|
58
|
+
exports.extractParamsFromPath = extractParamsFromPath;
|
|
59
|
+
exports.extractParamsFromSegment = extractParamsFromSegment;
|
|
60
|
+
exports.getInvalidParams = getInvalidParams;
|
|
61
|
+
//# sourceMappingURL=route-param-names.utils.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-param-names.utils.cjs","sources":["../../../../src/rules/route-param-names/route-param-names.utils.ts"],"sourcesContent":["import { VALID_PARAM_NAME_REGEX } from './constants'\n\nexport interface ExtractedParam {\n /** The full param string including $ prefix (e.g., \"$userId\", \"-$optional\") */\n fullParam: string\n /** The param name without $ prefix (e.g., \"userId\", \"optional\") */\n paramName: string\n /** Whether this is an optional param (prefixed with -$) */\n isOptional: boolean\n /** Whether this param name is valid */\n isValid: boolean\n}\n\n/**\n * Extracts param names from a route path segment.\n *\n * Handles these patterns:\n * - $paramName -> extract \"paramName\"\n * - {$paramName} -> extract \"paramName\"\n * - prefix{$paramName}suffix -> extract \"paramName\"\n * - {-$paramName} -> extract \"paramName\" (optional)\n * - prefix{-$paramName}suffix -> extract \"paramName\" (optional)\n * - $ or {$} -> wildcard, skip validation\n */\nexport function extractParamsFromSegment(\n segment: string,\n): Array<ExtractedParam> {\n const params: Array<ExtractedParam> = []\n\n // Skip empty segments\n if (!segment || !segment.includes('$')) {\n return params\n }\n\n // Check for wildcard ($ alone or {$})\n if (segment === '$' || segment === '{$}') {\n return params // Wildcard, no param name to validate\n }\n\n // Pattern 1: Simple $paramName (entire segment starts with $)\n if (segment.startsWith('$') && !segment.includes('{')) {\n const paramName = segment.slice(1)\n if (paramName) {\n params.push({\n fullParam: segment,\n paramName,\n isOptional: false,\n isValid: VALID_PARAM_NAME_REGEX.test(paramName),\n })\n }\n return params\n }\n\n // Pattern 2: Braces pattern {$paramName} or {-$paramName} with optional prefix/suffix\n // Match patterns like: prefix{$param}suffix, {$param}, {-$param}\n const bracePattern = /\\{(-?\\$)([^}]*)\\}/g\n let match\n\n while ((match = bracePattern.exec(segment)) !== null) {\n const prefix = match[1] // \"$\" or \"-$\"\n const paramName = match[2] // The param name after $ or -$\n\n if (!paramName) {\n // This is a wildcard {$} or {-$}, skip\n continue\n }\n\n const isOptional = prefix === '-$'\n\n params.push({\n fullParam: `${prefix}${paramName}`,\n paramName,\n isOptional,\n isValid: VALID_PARAM_NAME_REGEX.test(paramName),\n })\n }\n\n return params\n}\n\n/**\n * Extracts all params from a route path.\n *\n * @param path - The route path (e.g., \"/users/$userId/posts/$postId\")\n * @returns Array of extracted params with validation info\n */\nexport function extractParamsFromPath(path: string): Array<ExtractedParam> {\n if (!path || !path.includes('$')) {\n return []\n }\n\n const segments = path.split('/')\n const allParams: Array<ExtractedParam> = []\n\n for (const segment of segments) {\n const params = extractParamsFromSegment(segment)\n allParams.push(...params)\n }\n\n return allParams\n}\n\n/**\n * Validates a single param name.\n *\n * @param paramName - The param name to validate (without $ prefix)\n * @returns Whether the param name is valid\n */\nexport function isValidParamName(paramName: string): boolean {\n return VALID_PARAM_NAME_REGEX.test(paramName)\n}\n\n/**\n * Gets all invalid params from a route path.\n *\n * @param path - The route path\n * @returns Array of invalid param info\n */\nexport function getInvalidParams(path: string): Array<ExtractedParam> {\n const params = extractParamsFromPath(path)\n return params.filter((p) => !p.isValid)\n}\n"],"names":["VALID_PARAM_NAME_REGEX"],"mappings":";;;AAwBO,SAAS,yBACd,SACuB;AACvB,QAAM,SAAgC,CAAA;AAGtC,MAAI,CAAC,WAAW,CAAC,QAAQ,SAAS,GAAG,GAAG;AACtC,WAAO;AAAA,EACT;AAGA,MAAI,YAAY,OAAO,YAAY,OAAO;AACxC,WAAO;AAAA,EACT;AAGA,MAAI,QAAQ,WAAW,GAAG,KAAK,CAAC,QAAQ,SAAS,GAAG,GAAG;AACrD,UAAM,YAAY,QAAQ,MAAM,CAAC;AACjC,QAAI,WAAW;AACb,aAAO,KAAK;AAAA,QACV,WAAW;AAAA,QACX;AAAA,QACA,YAAY;AAAA,QACZ,SAASA,UAAAA,uBAAuB,KAAK,SAAS;AAAA,MAAA,CAC/C;AAAA,IACH;AACA,WAAO;AAAA,EACT;AAIA,QAAM,eAAe;AACrB,MAAI;AAEJ,UAAQ,QAAQ,aAAa,KAAK,OAAO,OAAO,MAAM;AACpD,UAAM,SAAS,MAAM,CAAC;AACtB,UAAM,YAAY,MAAM,CAAC;AAEzB,QAAI,CAAC,WAAW;AAEd;AAAA,IACF;AAEA,UAAM,aAAa,WAAW;AAE9B,WAAO,KAAK;AAAA,MACV,WAAW,GAAG,MAAM,GAAG,SAAS;AAAA,MAChC;AAAA,MACA;AAAA,MACA,SAASA,UAAAA,uBAAuB,KAAK,SAAS;AAAA,IAAA,CAC/C;AAAA,EACH;AAEA,SAAO;AACT;AAQO,SAAS,sBAAsB,MAAqC;AACzE,MAAI,CAAC,QAAQ,CAAC,KAAK,SAAS,GAAG,GAAG;AAChC,WAAO,CAAA;AAAA,EACT;AAEA,QAAM,WAAW,KAAK,MAAM,GAAG;AAC/B,QAAM,YAAmC,CAAA;AAEzC,aAAW,WAAW,UAAU;AAC9B,UAAM,SAAS,yBAAyB,OAAO;AAC/C,cAAU,KAAK,GAAG,MAAM;AAAA,EAC1B;AAEA,SAAO;AACT;AAkBO,SAAS,iBAAiB,MAAqC;AACpE,QAAM,SAAS,sBAAsB,IAAI;AACzC,SAAO,OAAO,OAAO,CAAC,MAAM,CAAC,EAAE,OAAO;AACxC;;;;"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export interface ExtractedParam {
|
|
2
|
+
/** The full param string including $ prefix (e.g., "$userId", "-$optional") */
|
|
3
|
+
fullParam: string;
|
|
4
|
+
/** The param name without $ prefix (e.g., "userId", "optional") */
|
|
5
|
+
paramName: string;
|
|
6
|
+
/** Whether this is an optional param (prefixed with -$) */
|
|
7
|
+
isOptional: boolean;
|
|
8
|
+
/** Whether this param name is valid */
|
|
9
|
+
isValid: boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Extracts param names from a route path segment.
|
|
13
|
+
*
|
|
14
|
+
* Handles these patterns:
|
|
15
|
+
* - $paramName -> extract "paramName"
|
|
16
|
+
* - {$paramName} -> extract "paramName"
|
|
17
|
+
* - prefix{$paramName}suffix -> extract "paramName"
|
|
18
|
+
* - {-$paramName} -> extract "paramName" (optional)
|
|
19
|
+
* - prefix{-$paramName}suffix -> extract "paramName" (optional)
|
|
20
|
+
* - $ or {$} -> wildcard, skip validation
|
|
21
|
+
*/
|
|
22
|
+
export declare function extractParamsFromSegment(segment: string): Array<ExtractedParam>;
|
|
23
|
+
/**
|
|
24
|
+
* Extracts all params from a route path.
|
|
25
|
+
*
|
|
26
|
+
* @param path - The route path (e.g., "/users/$userId/posts/$postId")
|
|
27
|
+
* @returns Array of extracted params with validation info
|
|
28
|
+
*/
|
|
29
|
+
export declare function extractParamsFromPath(path: string): Array<ExtractedParam>;
|
|
30
|
+
/**
|
|
31
|
+
* Validates a single param name.
|
|
32
|
+
*
|
|
33
|
+
* @param paramName - The param name to validate (without $ prefix)
|
|
34
|
+
* @returns Whether the param name is valid
|
|
35
|
+
*/
|
|
36
|
+
export declare function isValidParamName(paramName: string): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Gets all invalid params from a route path.
|
|
39
|
+
*
|
|
40
|
+
* @param path - The route path
|
|
41
|
+
* @returns Array of invalid param info
|
|
42
|
+
*/
|
|
43
|
+
export declare function getInvalidParams(path: string): Array<ExtractedParam>;
|
package/dist/cjs/rules.cjs
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const createRoutePropertyOrder_rule = require("./rules/create-route-property-order/create-route-property-order.rule.cjs");
|
|
4
|
+
const routeParamNames_rule = require("./rules/route-param-names/route-param-names.rule.cjs");
|
|
4
5
|
const rules = {
|
|
5
|
-
[createRoutePropertyOrder_rule.name]: createRoutePropertyOrder_rule.rule
|
|
6
|
+
[createRoutePropertyOrder_rule.name]: createRoutePropertyOrder_rule.rule,
|
|
7
|
+
[routeParamNames_rule.name]: routeParamNames_rule.rule
|
|
6
8
|
};
|
|
7
9
|
exports.rules = rules;
|
|
8
10
|
//# sourceMappingURL=rules.cjs.map
|
package/dist/cjs/rules.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rules.cjs","sources":["../../src/rules.ts"],"sourcesContent":["import * as createRoutePropertyOrder from './rules/create-route-property-order/create-route-property-order.rule'\nimport type { ESLintUtils } from '@typescript-eslint/utils'\nimport type { ExtraRuleDocs } from './types'\n\nexport const rules: Record<\n string,\n ESLintUtils.RuleModule<\n string,\n ReadonlyArray<unknown>,\n ExtraRuleDocs,\n ESLintUtils.RuleListener\n >\n> = {\n [createRoutePropertyOrder.name]: createRoutePropertyOrder.rule,\n}\n"],"names":["createRoutePropertyOrder.name","createRoutePropertyOrder.rule"],"mappings":"
|
|
1
|
+
{"version":3,"file":"rules.cjs","sources":["../../src/rules.ts"],"sourcesContent":["import * as createRoutePropertyOrder from './rules/create-route-property-order/create-route-property-order.rule'\nimport * as routeParamNames from './rules/route-param-names/route-param-names.rule'\nimport type { ESLintUtils } from '@typescript-eslint/utils'\nimport type { ExtraRuleDocs } from './types'\n\nexport const rules: Record<\n string,\n ESLintUtils.RuleModule<\n string,\n ReadonlyArray<unknown>,\n ExtraRuleDocs,\n ESLintUtils.RuleListener\n >\n> = {\n [createRoutePropertyOrder.name]: createRoutePropertyOrder.rule,\n [routeParamNames.name]: routeParamNames.rule,\n}\n"],"names":["createRoutePropertyOrder.name","createRoutePropertyOrder.rule","routeParamNames.name","routeParamNames.rule"],"mappings":";;;;AAKO,MAAM,QAQT;AAAA,EACF,CAACA,8BAAAA,IAA6B,GAAGC,8BAAAA;AAAAA,EACjC,CAACC,yBAAoB,GAAGC,qBAAAA;AAC1B;;"}
|
|
@@ -24,7 +24,8 @@ function detectTanstackRouterImports(create) {
|
|
|
24
24
|
};
|
|
25
25
|
const detectionInstructions = {
|
|
26
26
|
ImportDeclaration(node) {
|
|
27
|
-
if (node.specifiers.length > 0 &&
|
|
27
|
+
if (node.specifiers.length > 0 && // `importKind` is parser-dependent and can be undefined (eg. Espree)
|
|
28
|
+
node.importKind !== "type" && node.source.value.startsWith("@tanstack/") && node.source.value.endsWith("-router")) {
|
|
28
29
|
tanstackRouterImportSpecifiers.push(...node.specifiers);
|
|
29
30
|
}
|
|
30
31
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detect-router-imports.cjs","sources":["../../../src/utils/detect-router-imports.ts"],"sourcesContent":["import { TSESTree } from '@typescript-eslint/utils'\nimport type { ESLintUtils, TSESLint } from '@typescript-eslint/utils'\n\ntype Create = Parameters<\n ReturnType<typeof ESLintUtils.RuleCreator>\n>[0]['create']\n\ntype Context = Parameters<Create>[0]\ntype Options = Parameters<Create>[1]\ntype Helpers = {\n isSpecificTanstackRouterImport: (\n node: TSESTree.Identifier,\n source: string,\n ) => boolean\n isTanstackRouterImport: (node: TSESTree.Identifier) => boolean\n}\n\ntype EnhancedCreate = (\n context: Context,\n options: Options,\n helpers: Helpers,\n) => ReturnType<Create>\n\nexport function detectTanstackRouterImports(create: EnhancedCreate): Create {\n return (context, optionsWithDefault) => {\n const tanstackRouterImportSpecifiers: Array<TSESTree.ImportClause> = []\n\n const helpers: Helpers = {\n isSpecificTanstackRouterImport(node, source) {\n return !!tanstackRouterImportSpecifiers.find((specifier) => {\n if (\n specifier.type === TSESTree.AST_NODE_TYPES.ImportSpecifier &&\n specifier.parent.type ===\n TSESTree.AST_NODE_TYPES.ImportDeclaration &&\n specifier.parent.source.value === source\n ) {\n return node.name === specifier.local.name\n }\n\n return false\n })\n },\n isTanstackRouterImport(node) {\n return !!tanstackRouterImportSpecifiers.find((specifier) => {\n if (specifier.type === TSESTree.AST_NODE_TYPES.ImportSpecifier) {\n return node.name === specifier.local.name\n }\n\n return false\n })\n },\n }\n\n const detectionInstructions: TSESLint.RuleListener = {\n ImportDeclaration(node) {\n if (\n node.specifiers.length > 0 &&\n node.importKind
|
|
1
|
+
{"version":3,"file":"detect-router-imports.cjs","sources":["../../../src/utils/detect-router-imports.ts"],"sourcesContent":["import { TSESTree } from '@typescript-eslint/utils'\nimport type { ESLintUtils, TSESLint } from '@typescript-eslint/utils'\n\ntype Create = Parameters<\n ReturnType<typeof ESLintUtils.RuleCreator>\n>[0]['create']\n\ntype Context = Parameters<Create>[0]\ntype Options = Parameters<Create>[1]\ntype Helpers = {\n isSpecificTanstackRouterImport: (\n node: TSESTree.Identifier,\n source: string,\n ) => boolean\n isTanstackRouterImport: (node: TSESTree.Identifier) => boolean\n}\n\ntype EnhancedCreate = (\n context: Context,\n options: Options,\n helpers: Helpers,\n) => ReturnType<Create>\n\nexport function detectTanstackRouterImports(create: EnhancedCreate): Create {\n return (context, optionsWithDefault) => {\n const tanstackRouterImportSpecifiers: Array<TSESTree.ImportClause> = []\n\n const helpers: Helpers = {\n isSpecificTanstackRouterImport(node, source) {\n return !!tanstackRouterImportSpecifiers.find((specifier) => {\n if (\n specifier.type === TSESTree.AST_NODE_TYPES.ImportSpecifier &&\n specifier.parent.type ===\n TSESTree.AST_NODE_TYPES.ImportDeclaration &&\n specifier.parent.source.value === source\n ) {\n return node.name === specifier.local.name\n }\n\n return false\n })\n },\n isTanstackRouterImport(node) {\n return !!tanstackRouterImportSpecifiers.find((specifier) => {\n if (specifier.type === TSESTree.AST_NODE_TYPES.ImportSpecifier) {\n return node.name === specifier.local.name\n }\n\n return false\n })\n },\n }\n\n const detectionInstructions: TSESLint.RuleListener = {\n ImportDeclaration(node) {\n if (\n node.specifiers.length > 0 &&\n // `importKind` is parser-dependent and can be undefined (eg. Espree)\n node.importKind !== 'type' &&\n node.source.value.startsWith('@tanstack/') &&\n node.source.value.endsWith('-router')\n ) {\n tanstackRouterImportSpecifiers.push(...node.specifiers)\n }\n },\n }\n\n // Call original rule definition\n const ruleInstructions = create(context, optionsWithDefault, helpers)\n const enhancedRuleInstructions: TSESLint.RuleListener = {}\n\n const allKeys = new Set(\n Object.keys(detectionInstructions).concat(Object.keys(ruleInstructions)),\n )\n\n // Iterate over ALL instructions keys so we can override original rule instructions\n // to prevent their execution if conditions to report errors are not met.\n allKeys.forEach((instruction) => {\n enhancedRuleInstructions[instruction] = (node) => {\n if (instruction in detectionInstructions) {\n detectionInstructions[instruction]?.(node)\n }\n\n const ruleFunction = ruleInstructions[instruction]\n if (ruleFunction !== undefined) {\n return ruleFunction(node)\n }\n\n return undefined\n }\n })\n\n return enhancedRuleInstructions\n }\n}\n"],"names":["TSESTree"],"mappings":";;;AAuBO,SAAS,4BAA4B,QAAgC;AAC1E,SAAO,CAAC,SAAS,uBAAuB;AACtC,UAAM,iCAA+D,CAAA;AAErE,UAAM,UAAmB;AAAA,MACvB,+BAA+B,MAAM,QAAQ;AAC3C,eAAO,CAAC,CAAC,+BAA+B,KAAK,CAAC,cAAc;AAC1D,cACE,UAAU,SAASA,MAAAA,SAAS,eAAe,mBAC3C,UAAU,OAAO,SACfA,MAAAA,SAAS,eAAe,qBAC1B,UAAU,OAAO,OAAO,UAAU,QAClC;AACA,mBAAO,KAAK,SAAS,UAAU,MAAM;AAAA,UACvC;AAEA,iBAAO;AAAA,QACT,CAAC;AAAA,MACH;AAAA,MACA,uBAAuB,MAAM;AAC3B,eAAO,CAAC,CAAC,+BAA+B,KAAK,CAAC,cAAc;AAC1D,cAAI,UAAU,SAASA,eAAS,eAAe,iBAAiB;AAC9D,mBAAO,KAAK,SAAS,UAAU,MAAM;AAAA,UACvC;AAEA,iBAAO;AAAA,QACT,CAAC;AAAA,MACH;AAAA,IAAA;AAGF,UAAM,wBAA+C;AAAA,MACnD,kBAAkB,MAAM;AACtB,YACE,KAAK,WAAW,SAAS;AAAA,QAEzB,KAAK,eAAe,UACpB,KAAK,OAAO,MAAM,WAAW,YAAY,KACzC,KAAK,OAAO,MAAM,SAAS,SAAS,GACpC;AACA,yCAA+B,KAAK,GAAG,KAAK,UAAU;AAAA,QACxD;AAAA,MACF;AAAA,IAAA;AAIF,UAAM,mBAAmB,OAAO,SAAS,oBAAoB,OAAO;AACpE,UAAM,2BAAkD,CAAA;AAExD,UAAM,UAAU,IAAI;AAAA,MAClB,OAAO,KAAK,qBAAqB,EAAE,OAAO,OAAO,KAAK,gBAAgB,CAAC;AAAA,IAAA;AAKzE,YAAQ,QAAQ,CAAC,gBAAgB;AAC/B,+BAAyB,WAAW,IAAI,CAAC,SAAS;AAChD,YAAI,eAAe,uBAAuB;AACxC,gCAAsB,WAAW,IAAI,IAAI;AAAA,QAC3C;AAEA,cAAM,eAAe,iBAAiB,WAAW;AACjD,YAAI,iBAAiB,QAAW;AAC9B,iBAAO,aAAa,IAAI;AAAA,QAC1B;AAEA,eAAO;AAAA,MACT;AAAA,IACF,CAAC;AAED,WAAO;AAAA,EACT;AACF;;"}
|
package/dist/esm/index.js
CHANGED
|
@@ -10,7 +10,8 @@ Object.assign(plugin.configs, {
|
|
|
10
10
|
recommended: {
|
|
11
11
|
plugins: ["@tanstack/eslint-plugin-router"],
|
|
12
12
|
rules: {
|
|
13
|
-
"@tanstack/router/create-route-property-order": "warn"
|
|
13
|
+
"@tanstack/router/create-route-property-order": "warn",
|
|
14
|
+
"@tanstack/router/route-param-names": "error"
|
|
14
15
|
}
|
|
15
16
|
},
|
|
16
17
|
"flat/recommended": [
|
|
@@ -19,7 +20,8 @@ Object.assign(plugin.configs, {
|
|
|
19
20
|
"@tanstack/router": plugin
|
|
20
21
|
},
|
|
21
22
|
rules: {
|
|
22
|
-
"@tanstack/router/create-route-property-order": "warn"
|
|
23
|
+
"@tanstack/router/create-route-property-order": "warn",
|
|
24
|
+
"@tanstack/router/route-param-names": "error"
|
|
23
25
|
}
|
|
24
26
|
}
|
|
25
27
|
]
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/index.ts"],"sourcesContent":["import { rules } from './rules'\nimport type { ESLint, Linter } from 'eslint'\nimport type { RuleModule } from '@typescript-eslint/utils/ts-eslint'\n\ntype RuleKey = keyof typeof rules\n\ninterface Plugin extends Omit<ESLint.Plugin, 'rules'> {\n rules: Record<RuleKey, RuleModule<any, any, any>>\n configs: {\n recommended: ESLint.ConfigData\n 'flat/recommended': Array<Linter.FlatConfig>\n }\n}\n\nconst plugin: Plugin = {\n meta: {\n name: '@tanstack/eslint-plugin-router',\n },\n configs: {} as Plugin['configs'],\n rules,\n}\n\n// Assign configs here so we can reference `plugin`\nObject.assign(plugin.configs, {\n recommended: {\n plugins: ['@tanstack/eslint-plugin-router'],\n rules: {\n '@tanstack/router/create-route-property-order': 'warn',\n },\n },\n 'flat/recommended': [\n {\n plugins: {\n '@tanstack/router': plugin,\n },\n rules: {\n '@tanstack/router/create-route-property-order': 'warn',\n },\n },\n ],\n})\n\nexport default plugin\n"],"names":[],"mappings":";AAcA,MAAM,SAAiB;AAAA,EACrB,MAAM;AAAA,IACJ,MAAM;AAAA,EAAA;AAAA,EAER,SAAS,CAAA;AAAA,EACT;AACF;AAGA,OAAO,OAAO,OAAO,SAAS;AAAA,EAC5B,aAAa;AAAA,IACX,SAAS,CAAC,gCAAgC;AAAA,IAC1C,OAAO;AAAA,MACL,gDAAgD;AAAA,IAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/index.ts"],"sourcesContent":["import { rules } from './rules'\nimport type { ESLint, Linter } from 'eslint'\nimport type { RuleModule } from '@typescript-eslint/utils/ts-eslint'\n\ntype RuleKey = keyof typeof rules\n\ninterface Plugin extends Omit<ESLint.Plugin, 'rules'> {\n rules: Record<RuleKey, RuleModule<any, any, any>>\n configs: {\n recommended: ESLint.ConfigData\n 'flat/recommended': Array<Linter.FlatConfig>\n }\n}\n\nconst plugin: Plugin = {\n meta: {\n name: '@tanstack/eslint-plugin-router',\n },\n configs: {} as Plugin['configs'],\n rules,\n}\n\n// Assign configs here so we can reference `plugin`\nObject.assign(plugin.configs, {\n recommended: {\n plugins: ['@tanstack/eslint-plugin-router'],\n rules: {\n '@tanstack/router/create-route-property-order': 'warn',\n '@tanstack/router/route-param-names': 'error',\n },\n },\n 'flat/recommended': [\n {\n plugins: {\n '@tanstack/router': plugin,\n },\n rules: {\n '@tanstack/router/create-route-property-order': 'warn',\n '@tanstack/router/route-param-names': 'error',\n },\n },\n ],\n})\n\nexport default plugin\n"],"names":[],"mappings":";AAcA,MAAM,SAAiB;AAAA,EACrB,MAAM;AAAA,IACJ,MAAM;AAAA,EAAA;AAAA,EAER,SAAS,CAAA;AAAA,EACT;AACF;AAGA,OAAO,OAAO,OAAO,SAAS;AAAA,EAC5B,aAAa;AAAA,IACX,SAAS,CAAC,gCAAgC;AAAA,IAC1C,OAAO;AAAA,MACL,gDAAgD;AAAA,MAChD,sCAAsC;AAAA,IAAA;AAAA,EACxC;AAAA,EAEF,oBAAoB;AAAA,IAClB;AAAA,MACE,SAAS;AAAA,QACP,oBAAoB;AAAA,MAAA;AAAA,MAEtB,OAAO;AAAA,QACL,gDAAgD;AAAA,QAChD,sCAAsC;AAAA,MAAA;AAAA,IACxC;AAAA,EACF;AAEJ,CAAC;"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Functions where the path is passed as the first argument (string literal)
|
|
3
|
+
* e.g., createFileRoute('/path/$param')(...)
|
|
4
|
+
*/
|
|
5
|
+
export declare const pathAsFirstArgFunctions: readonly ["createFileRoute", "createLazyFileRoute", "createLazyRoute"];
|
|
6
|
+
export type PathAsFirstArgFunction = (typeof pathAsFirstArgFunctions)[number];
|
|
7
|
+
/**
|
|
8
|
+
* Functions where the path is a property in the options object
|
|
9
|
+
* e.g., createRoute({ path: '/path/$param' })
|
|
10
|
+
*/
|
|
11
|
+
export declare const pathAsPropertyFunctions: readonly ["createRoute"];
|
|
12
|
+
export type PathAsPropertyFunction = (typeof pathAsPropertyFunctions)[number];
|
|
13
|
+
/**
|
|
14
|
+
* All route functions that need param name validation
|
|
15
|
+
*/
|
|
16
|
+
export declare const allRouteFunctions: readonly ["createFileRoute", "createLazyFileRoute", "createLazyRoute", "createRoute"];
|
|
17
|
+
export type RouteFunction = (typeof allRouteFunctions)[number];
|
|
18
|
+
/**
|
|
19
|
+
* Regex for valid JavaScript identifier (param name)
|
|
20
|
+
* Must start with letter, underscore, or dollar sign
|
|
21
|
+
* Can contain letters, numbers, underscores, or dollar signs
|
|
22
|
+
*/
|
|
23
|
+
export declare const VALID_PARAM_NAME_REGEX: RegExp;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const pathAsFirstArgFunctions = [
|
|
2
|
+
"createFileRoute",
|
|
3
|
+
"createLazyFileRoute",
|
|
4
|
+
"createLazyRoute"
|
|
5
|
+
];
|
|
6
|
+
const pathAsPropertyFunctions = ["createRoute"];
|
|
7
|
+
const VALID_PARAM_NAME_REGEX = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
|
8
|
+
export {
|
|
9
|
+
VALID_PARAM_NAME_REGEX,
|
|
10
|
+
pathAsFirstArgFunctions,
|
|
11
|
+
pathAsPropertyFunctions
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sources":["../../../../src/rules/route-param-names/constants.ts"],"sourcesContent":["/**\n * Functions where the path is passed as the first argument (string literal)\n * e.g., createFileRoute('/path/$param')(...)\n */\nexport const pathAsFirstArgFunctions = [\n 'createFileRoute',\n 'createLazyFileRoute',\n 'createLazyRoute',\n] as const\n\nexport type PathAsFirstArgFunction = (typeof pathAsFirstArgFunctions)[number]\n\n/**\n * Functions where the path is a property in the options object\n * e.g., createRoute({ path: '/path/$param' })\n */\nexport const pathAsPropertyFunctions = ['createRoute'] as const\n\nexport type PathAsPropertyFunction = (typeof pathAsPropertyFunctions)[number]\n\n/**\n * All route functions that need param name validation\n */\nexport const allRouteFunctions = [\n ...pathAsFirstArgFunctions,\n ...pathAsPropertyFunctions,\n] as const\n\nexport type RouteFunction = (typeof allRouteFunctions)[number]\n\n/**\n * Regex for valid JavaScript identifier (param name)\n * Must start with letter, underscore, or dollar sign\n * Can contain letters, numbers, underscores, or dollar signs\n */\nexport const VALID_PARAM_NAME_REGEX = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/\n"],"names":[],"mappings":"AAIO,MAAM,0BAA0B;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AACF;AAQO,MAAM,0BAA0B,CAAC,aAAa;AAmB9C,MAAM,yBAAyB;"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
+
import { ExtraRuleDocs } from '../../types.js';
|
|
3
|
+
export declare const name = "route-param-names";
|
|
4
|
+
export declare const rule: ESLintUtils.RuleModule<string, readonly unknown[], ExtraRuleDocs, ESLintUtils.RuleListener>;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { ESLintUtils, AST_NODE_TYPES } from "@typescript-eslint/utils";
|
|
2
|
+
import { getDocsUrl } from "../../utils/get-docs-url.js";
|
|
3
|
+
import { detectTanstackRouterImports } from "../../utils/detect-router-imports.js";
|
|
4
|
+
import { getInvalidParams } from "./route-param-names.utils.js";
|
|
5
|
+
import { pathAsFirstArgFunctions, pathAsPropertyFunctions } from "./constants.js";
|
|
6
|
+
const createRule = ESLintUtils.RuleCreator(getDocsUrl);
|
|
7
|
+
const pathAsFirstArgSet = new Set(pathAsFirstArgFunctions);
|
|
8
|
+
const pathAsPropertySet = new Set(pathAsPropertyFunctions);
|
|
9
|
+
const name = "route-param-names";
|
|
10
|
+
const rule = createRule({
|
|
11
|
+
name,
|
|
12
|
+
meta: {
|
|
13
|
+
type: "problem",
|
|
14
|
+
docs: {
|
|
15
|
+
description: "Ensure route param names are valid JavaScript identifiers",
|
|
16
|
+
recommended: "error"
|
|
17
|
+
},
|
|
18
|
+
messages: {
|
|
19
|
+
invalidParamName: 'Invalid param name "{{paramName}}" in route path. Param names must be valid JavaScript identifiers (match /[a-zA-Z_$][a-zA-Z0-9_$]*/).'
|
|
20
|
+
},
|
|
21
|
+
schema: []
|
|
22
|
+
},
|
|
23
|
+
defaultOptions: [],
|
|
24
|
+
create: detectTanstackRouterImports((context, _, helpers) => {
|
|
25
|
+
function reportInvalidParams(node, path) {
|
|
26
|
+
const invalidParams = getInvalidParams(path);
|
|
27
|
+
for (const param of invalidParams) {
|
|
28
|
+
context.report({
|
|
29
|
+
node,
|
|
30
|
+
messageId: "invalidParamName",
|
|
31
|
+
data: { paramName: param.paramName }
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function getStringLiteralValue(node) {
|
|
36
|
+
if (node.type === AST_NODE_TYPES.Literal && typeof node.value === "string") {
|
|
37
|
+
return node.value;
|
|
38
|
+
}
|
|
39
|
+
if (node.type === AST_NODE_TYPES.TemplateLiteral && node.quasis.length === 1) {
|
|
40
|
+
const cooked = node.quasis[0]?.value.cooked;
|
|
41
|
+
if (cooked != null) {
|
|
42
|
+
return cooked;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
CallExpression(node) {
|
|
49
|
+
if (node.callee.type === AST_NODE_TYPES.Identifier) {
|
|
50
|
+
const funcName = node.callee.name;
|
|
51
|
+
if (!helpers.isTanstackRouterImport(node.callee)) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (pathAsPropertySet.has(funcName)) {
|
|
55
|
+
const arg = node.arguments[0];
|
|
56
|
+
if (arg?.type === AST_NODE_TYPES.ObjectExpression) {
|
|
57
|
+
for (const prop of arg.properties) {
|
|
58
|
+
if (prop.type === AST_NODE_TYPES.Property) {
|
|
59
|
+
const isPathKey = prop.key.type === AST_NODE_TYPES.Identifier && prop.key.name === "path" || prop.key.type === AST_NODE_TYPES.Literal && prop.key.value === "path";
|
|
60
|
+
if (isPathKey) {
|
|
61
|
+
const pathValue = getStringLiteralValue(prop.value);
|
|
62
|
+
if (pathValue) {
|
|
63
|
+
reportInvalidParams(prop.value, pathValue);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (node.callee.type === AST_NODE_TYPES.CallExpression) {
|
|
73
|
+
const innerCall = node.callee;
|
|
74
|
+
if (innerCall.callee.type === AST_NODE_TYPES.Identifier) {
|
|
75
|
+
const funcName = innerCall.callee.name;
|
|
76
|
+
if (!helpers.isTanstackRouterImport(innerCall.callee)) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (pathAsFirstArgSet.has(funcName)) {
|
|
80
|
+
const pathArg = innerCall.arguments[0];
|
|
81
|
+
if (pathArg) {
|
|
82
|
+
const pathValue = getStringLiteralValue(pathArg);
|
|
83
|
+
if (pathValue) {
|
|
84
|
+
reportInvalidParams(pathArg, pathValue);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
})
|
|
93
|
+
});
|
|
94
|
+
export {
|
|
95
|
+
name,
|
|
96
|
+
rule
|
|
97
|
+
};
|
|
98
|
+
//# sourceMappingURL=route-param-names.rule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-param-names.rule.js","sources":["../../../../src/rules/route-param-names/route-param-names.rule.ts"],"sourcesContent":["import { AST_NODE_TYPES, ESLintUtils } from '@typescript-eslint/utils'\n\nimport { getDocsUrl } from '../../utils/get-docs-url'\nimport { detectTanstackRouterImports } from '../../utils/detect-router-imports'\nimport { getInvalidParams } from './route-param-names.utils'\nimport { pathAsFirstArgFunctions, pathAsPropertyFunctions } from './constants'\nimport type { TSESTree } from '@typescript-eslint/utils'\nimport type { ExtraRuleDocs } from '../../types'\n\nconst createRule = ESLintUtils.RuleCreator<ExtraRuleDocs>(getDocsUrl)\n\nconst pathAsFirstArgSet = new Set<string>(pathAsFirstArgFunctions)\nconst pathAsPropertySet = new Set<string>(pathAsPropertyFunctions)\n\nexport const name = 'route-param-names'\n\nexport const rule = createRule({\n name,\n meta: {\n type: 'problem',\n docs: {\n description: 'Ensure route param names are valid JavaScript identifiers',\n recommended: 'error',\n },\n messages: {\n invalidParamName:\n 'Invalid param name \"{{paramName}}\" in route path. Param names must be valid JavaScript identifiers (match /[a-zA-Z_$][a-zA-Z0-9_$]*/).',\n },\n schema: [],\n },\n defaultOptions: [],\n\n create: detectTanstackRouterImports((context, _, helpers) => {\n function reportInvalidParams(node: TSESTree.Node, path: string) {\n const invalidParams = getInvalidParams(path)\n\n for (const param of invalidParams) {\n context.report({\n node,\n messageId: 'invalidParamName',\n data: { paramName: param.paramName },\n })\n }\n }\n\n function getStringLiteralValue(node: TSESTree.Node): string | null {\n if (\n node.type === AST_NODE_TYPES.Literal &&\n typeof node.value === 'string'\n ) {\n return node.value\n }\n if (\n node.type === AST_NODE_TYPES.TemplateLiteral &&\n node.quasis.length === 1\n ) {\n const cooked = node.quasis[0]?.value.cooked\n if (cooked != null) {\n return cooked\n }\n }\n return null\n }\n\n return {\n CallExpression(node) {\n // Handle direct function call: createRoute({ path: '...' })\n if (node.callee.type === AST_NODE_TYPES.Identifier) {\n const funcName = node.callee.name\n\n // Skip if not imported from TanStack Router\n if (!helpers.isTanstackRouterImport(node.callee)) {\n return\n }\n\n // Case: createRoute({ path: '/path/$param' }) or createRoute({ 'path': '/path/$param' })\n if (pathAsPropertySet.has(funcName)) {\n const arg = node.arguments[0]\n if (arg?.type === AST_NODE_TYPES.ObjectExpression) {\n for (const prop of arg.properties) {\n if (prop.type === AST_NODE_TYPES.Property) {\n const isPathKey =\n (prop.key.type === AST_NODE_TYPES.Identifier &&\n prop.key.name === 'path') ||\n (prop.key.type === AST_NODE_TYPES.Literal &&\n prop.key.value === 'path')\n if (isPathKey) {\n const pathValue = getStringLiteralValue(prop.value)\n if (pathValue) {\n reportInvalidParams(prop.value, pathValue)\n }\n }\n }\n }\n }\n return\n }\n }\n\n // Handle curried function call: createFileRoute('/path')({ ... })\n if (node.callee.type === AST_NODE_TYPES.CallExpression) {\n const innerCall = node.callee\n\n if (innerCall.callee.type === AST_NODE_TYPES.Identifier) {\n const funcName = innerCall.callee.name\n\n // Skip if not imported from TanStack Router\n if (!helpers.isTanstackRouterImport(innerCall.callee)) {\n return\n }\n\n // Case: createFileRoute('/path/$param')(...) or similar\n if (pathAsFirstArgSet.has(funcName)) {\n const pathArg = innerCall.arguments[0]\n if (pathArg) {\n const pathValue = getStringLiteralValue(pathArg)\n if (pathValue) {\n reportInvalidParams(pathArg, pathValue)\n }\n }\n }\n }\n }\n },\n }\n }),\n})\n"],"names":[],"mappings":";;;;;AASA,MAAM,aAAa,YAAY,YAA2B,UAAU;AAEpE,MAAM,oBAAoB,IAAI,IAAY,uBAAuB;AACjE,MAAM,oBAAoB,IAAI,IAAY,uBAAuB;AAE1D,MAAM,OAAO;AAEb,MAAM,OAAO,WAAW;AAAA,EAC7B;AAAA,EACA,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aAAa;AAAA,MACb,aAAa;AAAA,IAAA;AAAA,IAEf,UAAU;AAAA,MACR,kBACE;AAAA,IAAA;AAAA,IAEJ,QAAQ,CAAA;AAAA,EAAC;AAAA,EAEX,gBAAgB,CAAA;AAAA,EAEhB,QAAQ,4BAA4B,CAAC,SAAS,GAAG,YAAY;AAC3D,aAAS,oBAAoB,MAAqB,MAAc;AAC9D,YAAM,gBAAgB,iBAAiB,IAAI;AAE3C,iBAAW,SAAS,eAAe;AACjC,gBAAQ,OAAO;AAAA,UACb;AAAA,UACA,WAAW;AAAA,UACX,MAAM,EAAE,WAAW,MAAM,UAAA;AAAA,QAAU,CACpC;AAAA,MACH;AAAA,IACF;AAEA,aAAS,sBAAsB,MAAoC;AACjE,UACE,KAAK,SAAS,eAAe,WAC7B,OAAO,KAAK,UAAU,UACtB;AACA,eAAO,KAAK;AAAA,MACd;AACA,UACE,KAAK,SAAS,eAAe,mBAC7B,KAAK,OAAO,WAAW,GACvB;AACA,cAAM,SAAS,KAAK,OAAO,CAAC,GAAG,MAAM;AACrC,YAAI,UAAU,MAAM;AAClB,iBAAO;AAAA,QACT;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,MACL,eAAe,MAAM;AAEnB,YAAI,KAAK,OAAO,SAAS,eAAe,YAAY;AAClD,gBAAM,WAAW,KAAK,OAAO;AAG7B,cAAI,CAAC,QAAQ,uBAAuB,KAAK,MAAM,GAAG;AAChD;AAAA,UACF;AAGA,cAAI,kBAAkB,IAAI,QAAQ,GAAG;AACnC,kBAAM,MAAM,KAAK,UAAU,CAAC;AAC5B,gBAAI,KAAK,SAAS,eAAe,kBAAkB;AACjD,yBAAW,QAAQ,IAAI,YAAY;AACjC,oBAAI,KAAK,SAAS,eAAe,UAAU;AACzC,wBAAM,YACH,KAAK,IAAI,SAAS,eAAe,cAChC,KAAK,IAAI,SAAS,UACnB,KAAK,IAAI,SAAS,eAAe,WAChC,KAAK,IAAI,UAAU;AACvB,sBAAI,WAAW;AACb,0BAAM,YAAY,sBAAsB,KAAK,KAAK;AAClD,wBAAI,WAAW;AACb,0CAAoB,KAAK,OAAO,SAAS;AAAA,oBAC3C;AAAA,kBACF;AAAA,gBACF;AAAA,cACF;AAAA,YACF;AACA;AAAA,UACF;AAAA,QACF;AAGA,YAAI,KAAK,OAAO,SAAS,eAAe,gBAAgB;AACtD,gBAAM,YAAY,KAAK;AAEvB,cAAI,UAAU,OAAO,SAAS,eAAe,YAAY;AACvD,kBAAM,WAAW,UAAU,OAAO;AAGlC,gBAAI,CAAC,QAAQ,uBAAuB,UAAU,MAAM,GAAG;AACrD;AAAA,YACF;AAGA,gBAAI,kBAAkB,IAAI,QAAQ,GAAG;AACnC,oBAAM,UAAU,UAAU,UAAU,CAAC;AACrC,kBAAI,SAAS;AACX,sBAAM,YAAY,sBAAsB,OAAO;AAC/C,oBAAI,WAAW;AACb,sCAAoB,SAAS,SAAS;AAAA,gBACxC;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IAAA;AAAA,EAEJ,CAAC;AACH,CAAC;"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export interface ExtractedParam {
|
|
2
|
+
/** The full param string including $ prefix (e.g., "$userId", "-$optional") */
|
|
3
|
+
fullParam: string;
|
|
4
|
+
/** The param name without $ prefix (e.g., "userId", "optional") */
|
|
5
|
+
paramName: string;
|
|
6
|
+
/** Whether this is an optional param (prefixed with -$) */
|
|
7
|
+
isOptional: boolean;
|
|
8
|
+
/** Whether this param name is valid */
|
|
9
|
+
isValid: boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Extracts param names from a route path segment.
|
|
13
|
+
*
|
|
14
|
+
* Handles these patterns:
|
|
15
|
+
* - $paramName -> extract "paramName"
|
|
16
|
+
* - {$paramName} -> extract "paramName"
|
|
17
|
+
* - prefix{$paramName}suffix -> extract "paramName"
|
|
18
|
+
* - {-$paramName} -> extract "paramName" (optional)
|
|
19
|
+
* - prefix{-$paramName}suffix -> extract "paramName" (optional)
|
|
20
|
+
* - $ or {$} -> wildcard, skip validation
|
|
21
|
+
*/
|
|
22
|
+
export declare function extractParamsFromSegment(segment: string): Array<ExtractedParam>;
|
|
23
|
+
/**
|
|
24
|
+
* Extracts all params from a route path.
|
|
25
|
+
*
|
|
26
|
+
* @param path - The route path (e.g., "/users/$userId/posts/$postId")
|
|
27
|
+
* @returns Array of extracted params with validation info
|
|
28
|
+
*/
|
|
29
|
+
export declare function extractParamsFromPath(path: string): Array<ExtractedParam>;
|
|
30
|
+
/**
|
|
31
|
+
* Validates a single param name.
|
|
32
|
+
*
|
|
33
|
+
* @param paramName - The param name to validate (without $ prefix)
|
|
34
|
+
* @returns Whether the param name is valid
|
|
35
|
+
*/
|
|
36
|
+
export declare function isValidParamName(paramName: string): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Gets all invalid params from a route path.
|
|
39
|
+
*
|
|
40
|
+
* @param path - The route path
|
|
41
|
+
* @returns Array of invalid param info
|
|
42
|
+
*/
|
|
43
|
+
export declare function getInvalidParams(path: string): Array<ExtractedParam>;
|