Skip to content

Commit

Permalink
Move prependBaseUrlToPathname to @php-wasm/scopes
Browse files Browse the repository at this point in the history
  • Loading branch information
bgrgicak committed Nov 6, 2024
1 parent bc73a97 commit 9190c11
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 104 deletions.
68 changes: 67 additions & 1 deletion packages/php-wasm/scopes/src/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { getURLScope, isURLScoped, removeURLScope, setURLScope } from './index';
import {
getURLScope,
isURLScoped,
removeURLScope,
setURLScope,
prependBaseUrlToPathname,
} from './index';

describe('getURLScope', () => {
it('should return the scope from a scoped URL', () => {
Expand Down Expand Up @@ -56,3 +62,63 @@ describe('setURLScope', () => {
expect(setURLScope(url, null).href).toBe('http://localhost/index.php');
});
});

describe('prependBaseUrlToPathname', () => {
it('should preserve full base url when relative url is provided', () => {
expect(
prependBaseUrlToPathname(
'/wp-admin/index.php',
new URL('http://localhost/scope:123/')
)
).toBe('http://localhost/scope:123/wp-admin/index.php');
});
it('should preserve full base url when relative reference is provided', () => {
expect(
prependBaseUrlToPathname(
'index.php',
new URL('http://localhost/scope:123/')
)
).toBe('http://localhost/scope:123/index.php');
});
it('should preserve full base url when relative current directory reference is provided', () => {
expect(
prependBaseUrlToPathname(
'./test',
new URL('http://localhost/scope:123/')
)
).toBe('http://localhost/scope:123/test');
});

it('should preserve query params', () => {
expect(
prependBaseUrlToPathname(
'index.php?test=1',
new URL('http://localhost/scope:123/')
)
).toBe('http://localhost/scope:123/index.php?test=1');
});

it('should preserve relative url scope if it exists', () => {
expect(
prependBaseUrlToPathname(
'/scope:relative/index.php',
new URL('http://localhost/scope:123/')
)
).toBe('http://localhost/scope:relative/index.php');
});

it('should preserve base url subfolder', () => {
expect(
prependBaseUrlToPathname(
'index.php',
new URL('http://localhost/scope:123/subfolder/')
)
).toBe('http://localhost/scope:123/subfolder/index.php');
});

it('should return a unscoped url if base url is unscoped', () => {
expect(
prependBaseUrlToPathname('index.php', new URL('http://localhost/'))
).toBe('http://localhost/index.php');
});
});
66 changes: 65 additions & 1 deletion packages/php-wasm/scopes/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { joinPaths } from '@php-wasm/util';

/**
* Scopes are unique strings, like `my-site`, used to uniquely brand
* the outgoing HTTP traffic from each browser tab. This helps the
Expand All @@ -12,6 +14,25 @@
* For more information, see the README section on scopes.
*/

/**
* Checks if the URL pathname is scoped.
*
* @example
* ```js
* isUrlPathnameScoped('/scope:my-site/index.php');
* // true
*
* isUrlPathnameScoped('/index.php');
* // false
* ```
*
* @param url - The URL pathname to check.
* @returns `true` if the URL pathname is scoped, `false` otherwise.
*/
export function isUrlPathnameScoped(url: string): boolean {
return url.startsWith('/scope:');
}

/**
* Checks if the given URL contains scope information.
*
Expand All @@ -28,7 +49,7 @@
* @returns `true` if the URL contains scope information, `false` otherwise.
*/
export function isURLScoped(url: URL): boolean {
return url.pathname.startsWith(`/scope:`);
return isUrlPathnameScoped(url.pathname);
}

/**
Expand Down Expand Up @@ -115,3 +136,46 @@ export function removeURLScope(url: URL): URL {
newUrl.pathname = '/' + parts.slice(2).join('/');
return newUrl;
}

/**
* Prepends the base URL to a given pathname and maintains scope information.
*
* If the pathname is scoped, it will maintain scope information from the pathname.
* Otherwise, it will prepend the pathname with the base URL pathname to preserve the scope.
*
* If neither the pathname nor the base URL have scope information,
* the base URL will be prepended to the pathname and the result will be unscoped.
*
* @example
* ```js
* prependBaseUrlToPathname('/scope:pathname/index.php', new URL('http://localhost/scope:base/'));
* // 'http://localhost/scope:pathname/index.php'
*
* prependBaseUrlToPathname('/index.php', new URL('http://localhost/scope:base/'));
* // 'http://localhost/scope:base/index.php'
* ```
*
* @param pathname - The pathname to prepend the base URL to.
* @param baseUrl - The base URL to use to prepend to the pathname.
* @returns The absolute URL.
*/
export function prependBaseUrlToPathname(
pathname: string,
baseUrl: URL
): string {
/**
* Each Playground URL must have a scope to correctly resolve the current site.
*
* If a scope is provided in the relative URL, we need to preserve it.
*
* If the scope is not provided in the relative URL,
* we need to use the base URL pathname.
*
* We include the full base URL pathname in case it has subfolders in addition to the scope.
* Base URLs can have subfolders in multi-sites that use subfolders instead of subdomains.
*/
if (isUrlPathnameScoped(pathname)) {
return baseUrl.origin + pathname;
}
return baseUrl.origin + joinPaths(baseUrl.pathname, pathname);
}
13 changes: 10 additions & 3 deletions packages/php-wasm/universal/src/lib/php-request-handler.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { joinPaths } from '@php-wasm/util';
import { prependBaseUrlToPathname } from '@php-wasm/scopes';
import {
ensurePathPrefix,
toRelativeUrl,
removePathPrefix,
DEFAULT_BASE_URL,
toAbsoluteUrl,
isAbsoluteUrl,
} from './urls';
import { PHP, PHPExecutionFailureError, normalizeHeaders } from './php';
Expand Down Expand Up @@ -546,8 +546,15 @@ export class PHPRequestHandler {
response.headers['location']
) {
response.headers['location'] = response.headers['location'].map(
(location) =>
toAbsoluteUrl(location, new URL(this.#ABSOLUTE_URL))
(location) => {
if (isAbsoluteUrl(location)) {
return location;
}
return prependBaseUrlToPathname(
location,
new URL(this.#ABSOLUTE_URL)
);
}
);
}
return response;
Expand Down
64 changes: 0 additions & 64 deletions packages/php-wasm/universal/src/lib/urls.spec.ts

This file was deleted.

35 changes: 0 additions & 35 deletions packages/php-wasm/universal/src/lib/urls.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { joinPaths } from '@php-wasm/util';

/**
* The default base used to convert a path into the URL object.
*/
Expand Down Expand Up @@ -73,36 +71,3 @@ export function ensurePathPrefix(path: string, prefix: string): string {
export function isAbsoluteUrl(url: string): boolean {
return url.startsWith('http://') || url.startsWith('https://');
}

/**
* Returns a absolute URL for the provided URL.
*
* If the provided URL is relative, it will be prepended with the base URL.
* If a absolute URL is provided, it will return the provided URL and ignore
* the base URL.
*
* @param url - The URL to convert to an absolute URL.
* @param baseUrl - The base URL to use to convert the relative URL to an absolute URL.
* @returns The absolute URL.
*/
export function toAbsoluteUrl(url: string, baseUrl: URL): string {
if (isAbsoluteUrl(url)) {
return url;
}

/**
* Each Playground URL must have a scope to correctly resolve the current site.
*
* If a scope is provided in the relative URL, we need to preserve it.
*
* If the scope is not provided in the relative URL,
* we need to use the base URL pathname.
*
* We include the full base URL pathname in case it has subfolders in addition to the scope.
* Base URLs can have subfolders in multi-sites that use subfolders instead of subdomains.
*/
if (url.startsWith('/scope:')) {
return baseUrl.origin + url;
}
return baseUrl.origin + joinPaths(baseUrl.pathname, url);
}

0 comments on commit 9190c11

Please sign in to comment.