@aurelia/route-recognizer
Advanced tools
Comparing version 2.1.0-dev.202307101053 to 2.1.0-dev.202401061915
@@ -6,2 +6,19 @@ # Change Log | ||
<a name="2.0.0-beta.9"></a> | ||
# 2.0.0-beta.9 (2023-12-12) | ||
**Note:** Version bump only for package @aurelia/route-recognizer | ||
<a name="2.0.0-beta.8"></a> | ||
# 2.0.0-beta.8 (2023-07-24) | ||
### Bug Fixes: | ||
* **router-lite:** handling slash in parameter value (#1805) ([3fbb698](https://github.com/aurelia/aurelia/commit/3fbb698)) | ||
### Refactorings: | ||
* **ref:** deprecate view-model.ref and introduce component.ref (#1803) ([97e8dad](https://github.com/aurelia/aurelia/commit/97e8dad)) | ||
<a name="2.0.0-beta.7"></a> | ||
@@ -8,0 +25,0 @@ # 2.0.0-beta.7 (2023-06-16) |
@@ -10,3 +10,5 @@ export interface IConfigurableRoute<T> { | ||
readonly isStar: boolean; | ||
constructor(name: string, isOptional: boolean, isStar: boolean); | ||
readonly pattern: RegExp | null; | ||
constructor(name: string, isOptional: boolean, isStar: boolean, pattern: RegExp | null); | ||
satisfiesPattern(value: string): boolean; | ||
} | ||
@@ -13,0 +15,0 @@ export declare class ConfigurableRoute<T> implements IConfigurableRoute<T> { |
{ | ||
"name": "@aurelia/route-recognizer", | ||
"version": "2.1.0-dev.202307101053", | ||
"version": "2.1.0-dev.202401061915", | ||
"main": "dist/cjs/index.cjs", | ||
"module": "dist/esm/index.mjs", | ||
"exports": { | ||
"types": "./dist/types/index.d.ts", | ||
"require": "./dist/cjs/index.cjs", | ||
"import": "./dist/esm/index.mjs", | ||
"development": "./dist/esm/index.dev.mjs" | ||
".": { | ||
"types": "./dist/types/index.d.ts", | ||
"require": "./dist/cjs/index.cjs", | ||
"import": "./dist/esm/index.mjs", | ||
"development": "./dist/esm/index.dev.mjs" | ||
}, | ||
"./development": { | ||
"types": "./dist/types/index.d.ts", | ||
"require": "./dist/cjs/index.dev.cjs", | ||
"import": "./dist/esm/index.dev.mjs" | ||
} | ||
}, | ||
@@ -27,7 +34,3 @@ "types": "dist/types/index.d.ts", | ||
"files": [ | ||
"dist/**/*.cjs", | ||
"dist/**/*.dev.cjs.map", | ||
"dist/**/*.mjs", | ||
"dist/**/*.dev.mjs.map", | ||
"dist/**/*.d.ts", | ||
"dist", | ||
"src/**/*.ts", | ||
@@ -40,3 +43,3 @@ "README.md", | ||
"lint": "eslint --cache --ext .js,.ts src/", | ||
"lint:ci": "eslint --cache --ext .js,.ts --quiet --report-unused-disable-directives src/", | ||
"lint:ci": "eslint --ext .js,.ts --quiet --report-unused-disable-directives src/", | ||
"build": "rollup -c", | ||
@@ -55,3 +58,3 @@ "dev": "rollup -c -w", | ||
"devDependencies": { | ||
"typescript": "5.0.2" | ||
"typescript": "5.2.2" | ||
}, | ||
@@ -58,0 +61,0 @@ "engines": { |
103
src/index.ts
@@ -12,3 +12,10 @@ export interface IConfigurableRoute<T> { | ||
public readonly isStar: boolean, | ||
public readonly pattern: RegExp | null, | ||
){} | ||
public satisfiesPattern(value: string): boolean { | ||
if (this.pattern === null) return true; | ||
this.pattern.lastIndex = 0; | ||
return this.pattern.test(value); | ||
} | ||
} | ||
@@ -44,6 +51,14 @@ | ||
export class RecognizedRoute<T> { | ||
public readonly params: Readonly<Record<string, string | undefined>>; | ||
public constructor( | ||
public readonly endpoint: Endpoint<T>, | ||
public readonly params: Readonly<Record<string, string | undefined>>, | ||
) {} | ||
params: Readonly<Record<string, string | undefined>>, | ||
) { | ||
const $params: Record<string, string | undefined> = Object.create(null); | ||
for (const key in params) { | ||
const value = params[key]; | ||
$params[key] = value != null ? decodeURIComponent(value) : value; | ||
} | ||
this.params = Object.freeze($params); | ||
} | ||
} | ||
@@ -54,2 +69,5 @@ | ||
public endpoint: Endpoint<T>; | ||
private params: Record<string, string | undefined> | null = null; | ||
private isConstrained: boolean = false; | ||
private satisfiesConstraints: boolean | null = null; | ||
@@ -121,2 +139,5 @@ public constructor( | ||
chars.push(ch); | ||
this.isConstrained = this.isConstrained | ||
|| (stateToAdd as AnyState<T>).isDynamic | ||
&& ((stateToAdd as AnyState<T>).segment as DynamicSegment<T>)!.isConstrained; | ||
if ((stateToAdd as AnyState<T>).endpoint !== null) { | ||
@@ -132,3 +153,4 @@ this.endpoint = (stateToAdd as AnyState<T>).endpoint!; | ||
public finalize(): void { | ||
/** @internal */ | ||
public _finalize(): boolean { | ||
function collectSkippedStates( | ||
@@ -158,8 +180,15 @@ skippedStates: DynamicState<T>[], | ||
collectSkippedStates(this.skippedStates, this.head); | ||
if (!this.isConstrained) return true; | ||
this._getParams(); | ||
return this.satisfiesConstraints!; | ||
} | ||
public getParams(): Record<string, string | undefined> { | ||
/** @internal */ | ||
public _getParams(): Record<string, string | undefined> { | ||
let params = this.params; | ||
if (params != null) return params; | ||
const { states, chars, endpoint } = this; | ||
const params: Record<string, string | undefined> = {}; | ||
params = {}; | ||
this.satisfiesConstraints = true; | ||
// First initialize all properties with undefined so they all exist (even if they're not filled, e.g. non-matched optional params) | ||
@@ -173,12 +202,25 @@ for (const param of endpoint.params) { | ||
if (state.isDynamic) { | ||
const name = state.segment.name; | ||
const segment = state.segment; | ||
const name = segment.name; | ||
if (params[name] === void 0) { | ||
params[name] = chars[i]; | ||
} else { | ||
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands | ||
params[name] += chars[i]; | ||
} | ||
// check for constraint if this state's segment is constrained | ||
// and the state is the last dynamic state in a series of dynamic states. | ||
// null fallback is used, as a star segment can also be a dynamic segment, but without a pattern. | ||
const checkConstraint = state.isConstrained | ||
&& !Object.is(states[i + 1]?.segment, segment); | ||
if (!checkConstraint) continue; | ||
this.satisfiesConstraints = this.satisfiesConstraints && state.satisfiesConstraint(params[name]!); | ||
} | ||
} | ||
if(this.satisfiesConstraints) { | ||
this.params = params; | ||
} | ||
return params; | ||
@@ -315,3 +357,3 @@ } | ||
public getSolution(): Candidate<T> | null { | ||
const candidates = this.candidates.filter(hasEndpoint); | ||
const candidates = this.candidates.filter(x => hasEndpoint(x) && x._finalize()); | ||
if (candidates.length === 0) { | ||
@@ -321,6 +363,2 @@ return null; | ||
for (const candidate of candidates) { | ||
candidate.finalize(); | ||
} | ||
candidates.sort(compareChains); | ||
@@ -353,2 +391,4 @@ | ||
const routeParameterPattern = /^:(?<name>[^?\s{}]+)(?:\{\{(?<constraint>.+)\}\})?(?<optional>\?)?$/g; | ||
export class RouteRecognizer<T> { | ||
@@ -373,3 +413,3 @@ private readonly rootState: SeparatorState<T> = new State(null, null, '') as SeparatorState<T>; | ||
params = endpoint.params; | ||
// add residue iff the last parameter is not a star segment. | ||
// add residue iff the last parameter is not a star segment. | ||
if (addResidue && !(params[params.length - 1]?.isStar ?? false)) { | ||
@@ -402,7 +442,11 @@ endpoint.residualEndpoint = this.$add({ ...routeOrRoutes, path: `${routeOrRoutes.path}/*${RESIDUE}` }, true); | ||
case ':': { // route parameter | ||
const isOptional = part.endsWith('?'); | ||
const name = isOptional ? part.slice(1, -1) : part.slice(1); | ||
routeParameterPattern.lastIndex = 0; | ||
const match = routeParameterPattern.exec(part); | ||
const { name, optional } = match?.groups ?? {}; | ||
const isOptional = optional === '?'; | ||
if (name === RESIDUE) throw new Error(`Invalid parameter name; usage of the reserved parameter name '${RESIDUE}' is used.`); | ||
params.push(new Parameter(name, isOptional, false)); | ||
state = new DynamicSegment<T>(name, isOptional).appendTo(state); | ||
const constraint = match?.groups?.constraint; | ||
const pattern: RegExp | null = constraint != null ? new RegExp(constraint) : null; | ||
params.push(new Parameter(name, isOptional, false, pattern)); | ||
state = new DynamicSegment<T>(name, isOptional, pattern).appendTo(state); | ||
break; | ||
@@ -419,3 +463,3 @@ } | ||
} | ||
params.push(new Parameter(name, true, true)); | ||
params.push(new Parameter(name, true, true, null)); | ||
state = new StarSegment<T>(name, kind).appendTo(state); | ||
@@ -473,3 +517,3 @@ break; | ||
const { endpoint } = candidate; | ||
const params = candidate.getParams(); | ||
const params = candidate._getParams(); | ||
@@ -543,2 +587,3 @@ return new RecognizedRoute<T>(endpoint, params); | ||
public readonly length: number; | ||
public readonly isConstrained: boolean = false; | ||
@@ -556,2 +601,3 @@ public constructor( | ||
this.isOptional = segment.optional; | ||
this.isConstrained = segment.isConstrained; | ||
break; | ||
@@ -626,2 +672,8 @@ case SegmentKind.star: | ||
} | ||
public satisfiesConstraint(value: string): boolean { | ||
return this.isConstrained | ||
? (this.segment as DynamicSegment<T>).satisfiesPattern(value) | ||
: true; | ||
} | ||
} | ||
@@ -690,2 +742,3 @@ | ||
public get kind(): SegmentKind.dynamic { return SegmentKind.dynamic; } | ||
public readonly isConstrained: boolean; | ||
@@ -695,3 +748,7 @@ public constructor( | ||
public readonly optional: boolean, | ||
) {} | ||
public readonly pattern: RegExp | null, | ||
) { | ||
if (pattern === void 0) throw new Error(`Pattern is undefined`); | ||
this.isConstrained = pattern !== null; | ||
} | ||
@@ -714,2 +771,8 @@ public appendTo(state: AnyState<T>): DynamicState<T> { | ||
} | ||
public satisfiesPattern(value: string): boolean { | ||
if (this.pattern === null) return true; | ||
this.pattern.lastIndex = 0; | ||
return this.pattern.test(value); | ||
} | ||
} | ||
@@ -716,0 +779,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
223939
17
4101