From 761c4e9ba3ab0769096e4ed239e2e4c3b6b25d00 Mon Sep 17 00:00:00 2001 From: Jeremy Roman Date: Thu, 2 Nov 2023 22:42:26 -0400 Subject: [PATCH] Apply syntax changes proposed in whatwg/urlpattern#179. --- src/url-pattern-parser.ts | 50 +++++++++++++++++++++++++-------------- src/url-pattern.ts | 46 +++++++++++++++++++++++++---------- 2 files changed, 66 insertions(+), 30 deletions(-) diff --git a/src/url-pattern-parser.ts b/src/url-pattern-parser.ts index d500e0a..355da7b 100644 --- a/src/url-pattern-parser.ts +++ b/src/url-pattern-parser.ts @@ -106,11 +106,8 @@ export class Parser { this.#changeState(State.HASH, /*skip=*/1); } else if (this.#isSearchPrefix()) { this.#changeState(State.SEARCH, /*skip=*/1); - this.#internalResult.hash = ''; } else { this.#changeState(State.PATHNAME, /*skip=*/0); - this.#internalResult.search = ''; - this.#internalResult.hash = ''; } continue; } @@ -147,17 +144,6 @@ export class Parser { switch (this.#state) { case State.INIT: if (this.#isProtocolSuffix()) { - // We are in absolute mode and we know values will not be inherited - // from a base URL. Therefore initialize the rest of the components - // to the empty string. - this.#internalResult.username = ''; - this.#internalResult.password = ''; - this.#internalResult.hostname = ''; - this.#internalResult.port = ''; - this.#internalResult.pathname = ''; - this.#internalResult.search = ''; - this.#internalResult.hash = ''; - // Update the state to expect the start of an absolute URL. this.#rewindAndSetState(State.PROTOCOL); } @@ -179,10 +165,6 @@ export class Parser { let nextState: State = State.PATHNAME; let skip: number = 1; - if (this.#shouldTreatAsStandardURL) { - this.#internalResult.pathname = '/'; - } - // If there are authority slashes, like `https://`, then // we must transition to the authority section of the URLPattern. if (this.#nextIsAuthoritySlashes()) { @@ -318,6 +300,13 @@ export class Parser { // This should not be reached. break; } + + if (this.#internalResult.hostname !== undefined && + this.#internalResult.port === undefined) { + // If the hostname is specified in a constructor string but the port is + // not, the default port is assumed to be meant. + this.#internalResult.port = ''; + } } } @@ -358,6 +347,31 @@ export class Parser { break; } + if (this.#state !== State.INIT && newState !== State.DONE) { + // If hostname, pathname or search is skipped but something appears after + // it, then it takes its default value (usually the empty string). + if ([State.PROTOCOL, State.AUTHORITY, State.USERNAME, State.PASSWORD] + .includes(this.#state) && + [State.PORT, State.PATHNAME, State.SEARCH, State.HASH] + .includes(newState)) { + this.#internalResult.hostname ??= ''; + } + if ([State.PROTOCOL, State.AUTHORITY, State.USERNAME, State.PASSWORD, + State.HOSTNAME, State.PORT] + .includes(this.#state) && + [State.SEARCH, State.HASH] + .includes(newState)) { + this.#internalResult.pathname ??= + (this.#shouldTreatAsStandardURL ? '/' : ''); + } + if ([State.PROTOCOL, State.AUTHORITY, State.USERNAME, State.PASSWORD, + State.HOSTNAME, State.PORT, State.PATHNAME] + .includes(this.#state) && + newState === State.HASH) { + this.#internalResult.search ??= ''; + } + } + this.#changeStateWithoutSettingComponent(newState, skip); } diff --git a/src/url-pattern.ts b/src/url-pattern.ts index 68d1e9f..66867a8 100644 --- a/src/url-pattern.ts +++ b/src/url-pattern.ts @@ -77,22 +77,44 @@ function processBaseURLString(input: string, isPattern: boolean) { function applyInit(o: URLPatternInit, init: URLPatternInit, isPattern: boolean): URLPatternInit { // If there is a baseURL we need to apply its component values first. The // rest of the URLPatternInit structure will then later override these - // values. Note, the baseURL will always set either an empty string or - // longer value for each considered component. We do not allow null strings - // to persist for these components past this phase since they should no - // longer be treated as wildcards. + // values. let baseURL; if (typeof init.baseURL === 'string') { try { baseURL = new URL(init.baseURL); - o.protocol = processBaseURLString(baseURL.protocol.substring(0, baseURL.protocol.length - 1), isPattern); - o.username = processBaseURLString(baseURL.username, isPattern); - o.password = processBaseURLString(baseURL.password, isPattern); - o.hostname = processBaseURLString(baseURL.hostname, isPattern); - o.port = processBaseURLString(baseURL.port, isPattern); - o.pathname = processBaseURLString(baseURL.pathname, isPattern); - o.search = processBaseURLString(baseURL.search.substring(1, baseURL.search.length), isPattern); - o.hash = processBaseURLString(baseURL.hash.substring(1, baseURL.hash.length), isPattern); + if (init.protocol === undefined) { + o.protocol = processBaseURLString(baseURL.protocol.substring(0, baseURL.protocol.length - 1), isPattern); + } + if (!isPattern && init.protocol === undefined && init.hostname === undefined && + init.port === undefined && init.username === undefined) { + o.username = processBaseURLString(baseURL.username, isPattern); + } + if (!isPattern && init.protocol === undefined && init.hostname === undefined && + init.port === undefined && init.username === undefined && + init.password === undefined) { + o.password = processBaseURLString(baseURL.password, isPattern); + } + if (init.protocol === undefined && init.hostname === undefined) { + o.hostname = processBaseURLString(baseURL.hostname, isPattern); + } + if (init.protocol === undefined && init.hostname === undefined && + init.port === undefined) { + o.port = processBaseURLString(baseURL.port, isPattern); + } + if (init.protocol === undefined && init.hostname === undefined && + init.port === undefined && init.pathname === undefined) { + o.pathname = processBaseURLString(baseURL.pathname, isPattern); + } + if (init.protocol === undefined && init.hostname === undefined && + init.port === undefined && init.pathname === undefined && + init.search === undefined) { + o.search = processBaseURLString(baseURL.search.substring(1, baseURL.search.length), isPattern); + } + if (init.protocol === undefined && init.hostname === undefined && + init.port === undefined && init.pathname === undefined && + init.search === undefined && init.hash === undefined) { + o.hash = processBaseURLString(baseURL.hash.substring(1, baseURL.hash.length), isPattern); + } } catch { throw new TypeError(`invalid baseURL '${init.baseURL}'.`); }