Repository: microsoft/playwright
File: packages/playwright-core/src/utils/isomorphic/urlMatch.ts (TypeScript)

Code before:
  return tokens.join('');
}

function isRegExp(obj: any): obj is RegExp {
  return obj instanceof RegExp || Object.prototype.toString.call(obj) === '[object RegExp]';
}

export type URLMatch = string | RegExp | ((url: URL) => boolean) | URLPattern;
// URLPattern is not in @types/node@18, so we polyfill it ourselves
export type URLPattern = {
  test(input: string | URL): boolean;
  hash: string;
  hostname: string;
  password: string;
  pathname: string;
  port: string;
  protocol: string;
  search: string;
  username: string;
};

// @ts-expect-error URLPattern is not in @types/node yet
// eslint-disable-next-line no-restricted-globals
export const isURLPattern = (v: unknown): v is URLPattern => typeof globalThis.URLPattern === 'function' && v instanceof globalThis.URLPattern;

export function serializeURLPattern(v: URLPattern) {
  return {
    hash: v.hash,
    hostname: v.hostname,
    password: v.password,
    pathname: v.pathname,
    port: v.port,
    protocol: v.protocol,
    search: v.search,
    username: v.username,
  };
}

function deserializeURLPattern(v: ReturnType<typeof serializeURLPattern>): URLPattern | undefined {
  // Client is on Node 24+ and can use URLPattern, Server is not. Let's match all URLs on the server, they'll be filtered again on the client.
  // @ts-expect-error URLPattern is not in @types/node yet
  // eslint-disable-next-line no-restricted-globals
  if (typeof globalThis.URLPattern !== 'function')
    return;

  // @ts-expect-error URLPattern is not in @types/node yet
  // eslint-disable-next-line no-restricted-globals
  return new globalThis.URLPattern({
    hash: v.hash,
    hostname: v.hostname,
    password: v.password,

Review comment (suggestion):
Let's instead export `serializeURLMatch()` and handle all cases right here.

Code after:
  return tokens.join('');
}

function isRegExp(obj: any): obj is RegExp {
  return obj instanceof RegExp || Object.prototype.toString.call(obj) === '[object RegExp]';
}

export type URLMatch = string | RegExp | ((url: URL) => boolean) | URLPattern;
// URLPattern is not in @types/node@18, so we polyfill it ourselves
export type URLPattern = {
  test(input: string | URL): boolean;
  hash: string;
  hostname: string;
  password: string;
  pathname: string;
  port: string;
  protocol: string;
  search: string;
  username: string;
};

// @ts-expect-error URLPattern is not in @types/node yet
// eslint-disable-next-line no-restricted-globals
export const isURLPattern = (v: unknown): v is URLPattern => typeof globalThis.URLPattern === 'function' && v instanceof globalThis.URLPattern;

export function serializeURLPattern(v: URLPattern) {
  return {
    hash: v.hash,
    hostname: v.hostname,
    password: v.password,
    pathname: v.pathname,
    port: v.port,
    protocol: v.protocol,
    search: v.search,
    username: v.username,
  };
}

export type SerializedURLMatch = { glob?: string, regexSource?: string, regexFlags?: string, urlPattern?: ReturnType<typeof serializeURLPattern> };

export function serializeURLMatch(match: URLMatch): SerializedURLMatch | undefined {
  if (isString(match))
    return { glob: match };
  if (isRegExp(match))
    return { regexSource: match.source, regexFlags: match.flags };
  if (isURLPattern(match))
    return { urlPattern: serializeURLPattern(match) };
  // Functions cannot be serialized
  return undefined;
}