diff --git a/packages/docusaurus-plugin-client-redirects/src/__tests__/collectRedirects.test.ts b/packages/docusaurus-plugin-client-redirects/src/__tests__/collectRedirects.test.ts index e303f7289b92..11b0d3455061 100644 --- a/packages/docusaurus-plugin-client-redirects/src/__tests__/collectRedirects.test.ts +++ b/packages/docusaurus-plugin-client-redirects/src/__tests__/collectRedirects.test.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import {removeTrailingSlash} from '@docusaurus/utils'; +import {removeTrailingSlash} from '@docusaurus/utils-common'; import {normalizePluginOptions} from '@docusaurus/utils-validation'; import collectRedirects from '../collectRedirects'; import {validateOptions} from '../options'; diff --git a/packages/docusaurus-plugin-client-redirects/src/collectRedirects.ts b/packages/docusaurus-plugin-client-redirects/src/collectRedirects.ts index 92a29a6ed307..1e3ac44e4405 100644 --- a/packages/docusaurus-plugin-client-redirects/src/collectRedirects.ts +++ b/packages/docusaurus-plugin-client-redirects/src/collectRedirects.ts @@ -7,8 +7,11 @@ import _ from 'lodash'; import logger from '@docusaurus/logger'; -import {addTrailingSlash, removeTrailingSlash} from '@docusaurus/utils'; -import {applyTrailingSlash} from '@docusaurus/utils-common'; +import { + applyTrailingSlash, + addTrailingSlash, + removeTrailingSlash, +} from '@docusaurus/utils-common'; import { createFromExtensionsRedirects, createToExtensionsRedirects, diff --git a/packages/docusaurus-plugin-client-redirects/src/extensionRedirects.ts b/packages/docusaurus-plugin-client-redirects/src/extensionRedirects.ts index 86fa4aeb48de..3fc48cdfad49 100644 --- a/packages/docusaurus-plugin-client-redirects/src/extensionRedirects.ts +++ b/packages/docusaurus-plugin-client-redirects/src/extensionRedirects.ts @@ -9,7 +9,7 @@ import { addTrailingSlash, removeSuffix, removeTrailingSlash, -} from '@docusaurus/utils'; +} from '@docusaurus/utils-common'; import type {RedirectItem} from './types'; const ExtensionAdditionalMessage = diff --git a/packages/docusaurus-plugin-client-redirects/src/index.ts b/packages/docusaurus-plugin-client-redirects/src/index.ts index 5b64560bac04..e7aa97961f37 100644 --- a/packages/docusaurus-plugin-client-redirects/src/index.ts +++ b/packages/docusaurus-plugin-client-redirects/src/index.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import {removePrefix, addLeadingSlash} from '@docusaurus/utils'; +import {addLeadingSlash, removePrefix} from '@docusaurus/utils-common'; import collectRedirects from './collectRedirects'; import writeRedirectFiles, { toRedirectFiles, diff --git a/packages/docusaurus-plugin-content-docs/package.json b/packages/docusaurus-plugin-content-docs/package.json index d7099a125e60..e8349e4fb455 100644 --- a/packages/docusaurus-plugin-content-docs/package.json +++ b/packages/docusaurus-plugin-content-docs/package.json @@ -41,6 +41,7 @@ "@docusaurus/module-type-aliases": "3.0.0", "@docusaurus/types": "3.0.0", "@docusaurus/utils": "3.0.0", + "@docusaurus/utils-common": "3.0.0", "@docusaurus/utils-validation": "3.0.0", "@types/react-router-config": "^5.0.7", "combine-promises": "^1.1.0", diff --git a/packages/docusaurus-plugin-content-docs/src/sidebars/generator.ts b/packages/docusaurus-plugin-content-docs/src/sidebars/generator.ts index 5a6b325449b1..0edf909bb9c8 100644 --- a/packages/docusaurus-plugin-content-docs/src/sidebars/generator.ts +++ b/packages/docusaurus-plugin-content-docs/src/sidebars/generator.ts @@ -8,7 +8,7 @@ import path from 'path'; import _ from 'lodash'; import logger from '@docusaurus/logger'; -import {addTrailingSlash} from '@docusaurus/utils'; +import {addTrailingSlash} from '@docusaurus/utils-common'; import {createDocsByIdIndex, toCategoryIndexMatcherParam} from '../docs'; import type { SidebarItemDoc, diff --git a/packages/docusaurus-plugin-content-docs/src/slug.ts b/packages/docusaurus-plugin-content-docs/src/slug.ts index 130c7a63b1f5..65887029e8bd 100644 --- a/packages/docusaurus-plugin-content-docs/src/slug.ts +++ b/packages/docusaurus-plugin-content-docs/src/slug.ts @@ -5,12 +5,8 @@ * LICENSE file in the root directory of this source tree. */ -import { - addLeadingSlash, - addTrailingSlash, - isValidPathname, - resolvePathname, -} from '@docusaurus/utils'; +import {isValidPathname, resolvePathname} from '@docusaurus/utils'; +import {addLeadingSlash, addTrailingSlash} from '@docusaurus/utils-common'; import { DefaultNumberPrefixParser, stripPathNumberPrefixes, diff --git a/packages/docusaurus-utils-common/src/__tests__/applyTrailingSlash.test.ts b/packages/docusaurus-utils-common/src/__tests__/applyTrailingSlash.test.ts index 4096b8273fca..c86013f7fd37 100644 --- a/packages/docusaurus-utils-common/src/__tests__/applyTrailingSlash.test.ts +++ b/packages/docusaurus-utils-common/src/__tests__/applyTrailingSlash.test.ts @@ -6,7 +6,10 @@ */ import applyTrailingSlash, { + addTrailingSlash, type ApplyTrailingSlashParams, + addLeadingSlash, + removeTrailingSlash, } from '../applyTrailingSlash'; function params( @@ -176,3 +179,30 @@ describe('applyTrailingSlash', () => { ).toBe('https://xyz.com/abc/?search#anchor'); }); }); + +describe('addTrailingSlash', () => { + it('is no-op for path with trailing slash', () => { + expect(addTrailingSlash('/abcd/')).toBe('/abcd/'); + }); + it('adds / for path without trailing slash', () => { + expect(addTrailingSlash('/abcd')).toBe('/abcd/'); + }); +}); + +describe('addLeadingSlash', () => { + it('is no-op for path with leading slash', () => { + expect(addLeadingSlash('/abc')).toBe('/abc'); + }); + it('adds / for path without leading slash', () => { + expect(addLeadingSlash('abc')).toBe('/abc'); + }); +}); + +describe('removeTrailingSlash', () => { + it('is no-op for path without trailing slash', () => { + expect(removeTrailingSlash('/abcd')).toBe('/abcd'); + }); + it('removes / for path with trailing slash', () => { + expect(removeTrailingSlash('/abcd/')).toBe('/abcd'); + }); +}); diff --git a/packages/docusaurus-utils-common/src/__tests__/stringUtils.test.ts b/packages/docusaurus-utils-common/src/__tests__/stringUtils.test.ts new file mode 100644 index 000000000000..9d01520300bf --- /dev/null +++ b/packages/docusaurus-utils-common/src/__tests__/stringUtils.test.ts @@ -0,0 +1,55 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import {addPrefix, addSuffix, removePrefix, removeSuffix} from '../stringUtils'; + +describe('removePrefix', () => { + it("is no-op when prefix doesn't exist", () => { + expect(removePrefix('abcdef', 'ijk')).toBe('abcdef'); + expect(removePrefix('abcdef', 'def')).toBe('abcdef'); + expect(removePrefix('abcdef', '')).toBe('abcdef'); + }); + it('removes prefix', () => { + expect(removePrefix('prefix', 'pre')).toBe('fix'); + }); +}); + +describe('removeSuffix', () => { + it("is no-op when suffix doesn't exist", () => { + expect(removeSuffix('abcdef', 'ijk')).toBe('abcdef'); + expect(removeSuffix('abcdef', 'abc')).toBe('abcdef'); + expect(removeSuffix('abcdef', '')).toBe('abcdef'); + }); + it('removes suffix', () => { + expect(removeSuffix('abcdef', 'ef')).toBe('abcd'); + }); + it('removes empty suffix', () => { + expect(removeSuffix('abcdef', '')).toBe('abcdef'); + }); +}); + +describe('addPrefix', () => { + it('is no-op when prefix already exists', () => { + expect(addPrefix('abcdef', 'abc')).toBe('abcdef'); + expect(addPrefix('abc', '')).toBe('abc'); + expect(addPrefix('', '')).toBe(''); + }); + it('adds prefix', () => { + expect(addPrefix('def', 'abc')).toBe('abcdef'); + }); +}); + +describe('addSuffix', () => { + it('is no-op when suffix already exists', () => { + expect(addSuffix('abcdef', 'def')).toBe('abcdef'); + expect(addSuffix('abc', '')).toBe('abc'); + expect(addSuffix('', '')).toBe(''); + }); + it('adds suffix', () => { + expect(addSuffix('abc', 'def')).toBe('abcdef'); + }); +}); diff --git a/packages/docusaurus-utils-common/src/applyTrailingSlash.ts b/packages/docusaurus-utils-common/src/applyTrailingSlash.ts index d8faa439cc5b..f83e5ce9c076 100644 --- a/packages/docusaurus-utils-common/src/applyTrailingSlash.ts +++ b/packages/docusaurus-utils-common/src/applyTrailingSlash.ts @@ -5,6 +5,7 @@ * LICENSE file in the root directory of this source tree. */ +import {addPrefix, removeSuffix} from './stringUtils'; import type {DocusaurusConfig} from '@docusaurus/types'; export type ApplyTrailingSlashParams = Pick< @@ -12,6 +13,10 @@ export type ApplyTrailingSlashParams = Pick< 'trailingSlash' | 'baseUrl' >; +export function addTrailingSlash(str: string): string { + return str.endsWith('/') ? str : `${str}/`; +} + // Trailing slash handling depends in some site configuration options export default function applyTrailingSlash( path: string, @@ -24,13 +29,6 @@ export default function applyTrailingSlash( return path; } - // TODO deduplicate: also present in @docusaurus/utils - function addTrailingSlash(str: string): string { - return str.endsWith('/') ? str : `${str}/`; - } - function removeTrailingSlash(str: string): string { - return str.endsWith('/') ? str.slice(0, -1) : str; - } function handleTrailingSlash(str: string, trailing: boolean): string { return trailing ? addTrailingSlash(str) : removeTrailingSlash(str); } @@ -55,3 +53,13 @@ export default function applyTrailingSlash( return path.replace(pathname, newPathname); } + +/** Appends a leading slash to `str`, if one doesn't exist. */ +export function addLeadingSlash(str: string): string { + return addPrefix(str, '/'); +} + +/** Removes the trailing slash from `str`. */ +export function removeTrailingSlash(str: string): string { + return removeSuffix(str, '/'); +} diff --git a/packages/docusaurus-utils-common/src/index.ts b/packages/docusaurus-utils-common/src/index.ts index 17b69e0727f0..0c4aae428147 100644 --- a/packages/docusaurus-utils-common/src/index.ts +++ b/packages/docusaurus-utils-common/src/index.ts @@ -11,6 +11,10 @@ export const blogPostContainerID = '__blog-post-container'; export { default as applyTrailingSlash, + addTrailingSlash, + addLeadingSlash, + removeTrailingSlash, type ApplyTrailingSlashParams, } from './applyTrailingSlash'; +export {addPrefix, removeSuffix, addSuffix, removePrefix} from './stringUtils'; export {getErrorCausalChain} from './errorUtils'; diff --git a/packages/docusaurus-utils-common/src/stringUtils.ts b/packages/docusaurus-utils-common/src/stringUtils.ts new file mode 100644 index 000000000000..29dea13c3210 --- /dev/null +++ b/packages/docusaurus-utils-common/src/stringUtils.ts @@ -0,0 +1,30 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/** Adds a given string prefix to `str`. */ +export function addPrefix(str: string, prefix: string): string { + return str.startsWith(prefix) ? str : `${prefix}${str}`; +} + +/** Removes a given string suffix from `str`. */ +export function removeSuffix(str: string, suffix: string): string { + if (suffix === '') { + // str.slice(0, 0) is "" + return str; + } + return str.endsWith(suffix) ? str.slice(0, -suffix.length) : str; +} + +/** Adds a given string suffix to `str`. */ +export function addSuffix(str: string, suffix: string): string { + return str.endsWith(suffix) ? str : `${str}${suffix}`; +} + +/** Removes a given string prefix from `str`. */ +export function removePrefix(str: string, prefix: string): string { + return str.startsWith(prefix) ? str.slice(prefix.length) : str; +} diff --git a/packages/docusaurus-utils-validation/package.json b/packages/docusaurus-utils-validation/package.json index f37c918de7b5..7a90a9aa891a 100644 --- a/packages/docusaurus-utils-validation/package.json +++ b/packages/docusaurus-utils-validation/package.json @@ -20,6 +20,7 @@ "dependencies": { "@docusaurus/logger": "3.0.0", "@docusaurus/utils": "3.0.0", + "@docusaurus/utils-common": "3.0.0", "joi": "^17.9.2", "js-yaml": "^4.1.0", "tslib": "^2.6.0" diff --git a/packages/docusaurus-utils-validation/src/validationSchemas.ts b/packages/docusaurus-utils-validation/src/validationSchemas.ts index 2e624458f5bc..69b4ff5c9996 100644 --- a/packages/docusaurus-utils-validation/src/validationSchemas.ts +++ b/packages/docusaurus-utils-validation/src/validationSchemas.ts @@ -5,12 +5,8 @@ * LICENSE file in the root directory of this source tree. */ -import { - isValidPathname, - DEFAULT_PLUGIN_ID, - type Tag, - addLeadingSlash, -} from '@docusaurus/utils'; +import {isValidPathname, DEFAULT_PLUGIN_ID, type Tag} from '@docusaurus/utils'; +import {addLeadingSlash} from '@docusaurus/utils-common'; import Joi from './Joi'; import {JoiFrontMatter} from './JoiFrontMatter'; diff --git a/packages/docusaurus-utils/package.json b/packages/docusaurus-utils/package.json index b3fa5dee583d..9f7adc9cbe3b 100644 --- a/packages/docusaurus-utils/package.json +++ b/packages/docusaurus-utils/package.json @@ -19,6 +19,7 @@ "license": "MIT", "dependencies": { "@docusaurus/logger": "3.0.0", + "@docusaurus/utils-common": "3.0.0", "@svgr/webpack": "^6.5.1", "escape-string-regexp": "^4.0.0", "file-loader": "^6.2.0", diff --git a/packages/docusaurus-utils/src/__tests__/jsUtils.test.ts b/packages/docusaurus-utils/src/__tests__/jsUtils.test.ts index 9ba72646964c..fb3750e0eae7 100644 --- a/packages/docusaurus-utils/src/__tests__/jsUtils.test.ts +++ b/packages/docusaurus-utils/src/__tests__/jsUtils.test.ts @@ -7,34 +7,7 @@ import {jest} from '@jest/globals'; import _ from 'lodash'; -import { - removeSuffix, - removePrefix, - mapAsyncSequential, - findAsyncSequential, -} from '../jsUtils'; - -describe('removeSuffix', () => { - it("is no-op when suffix doesn't exist", () => { - expect(removeSuffix('abcdef', 'ijk')).toBe('abcdef'); - expect(removeSuffix('abcdef', 'abc')).toBe('abcdef'); - expect(removeSuffix('abcdef', '')).toBe('abcdef'); - }); - it('removes suffix', () => { - expect(removeSuffix('abcdef', 'ef')).toBe('abcd'); - }); -}); - -describe('removePrefix', () => { - it("is no-op when prefix doesn't exist", () => { - expect(removePrefix('abcdef', 'ijk')).toBe('abcdef'); - expect(removePrefix('abcdef', 'def')).toBe('abcdef'); - expect(removePrefix('abcdef', '')).toBe('abcdef'); - }); - it('removes prefix', () => { - expect(removePrefix('prefix', 'pre')).toBe('fix'); - }); -}); +import {mapAsyncSequential, findAsyncSequential} from '../jsUtils'; describe('mapAsyncSequential', () => { function sleep(timeout: number): Promise { diff --git a/packages/docusaurus-utils/src/__tests__/urlUtils.test.ts b/packages/docusaurus-utils/src/__tests__/urlUtils.test.ts index 7ed1cbbf9cea..1f63138d9780 100644 --- a/packages/docusaurus-utils/src/__tests__/urlUtils.test.ts +++ b/packages/docusaurus-utils/src/__tests__/urlUtils.test.ts @@ -10,9 +10,6 @@ import { getEditUrl, fileToPath, isValidPathname, - addTrailingSlash, - addLeadingSlash, - removeTrailingSlash, resolvePathname, encodePath, buildSshUrl, @@ -207,33 +204,6 @@ describe('isValidPathname', () => { }); }); -describe('addTrailingSlash', () => { - it('is no-op for path with trailing slash', () => { - expect(addTrailingSlash('/abcd/')).toBe('/abcd/'); - }); - it('adds / for path without trailing slash', () => { - expect(addTrailingSlash('/abcd')).toBe('/abcd/'); - }); -}); - -describe('addLeadingSlash', () => { - it('is no-op for path with leading slash', () => { - expect(addLeadingSlash('/abc')).toBe('/abc'); - }); - it('adds / for path without leading slash', () => { - expect(addLeadingSlash('abc')).toBe('/abc'); - }); -}); - -describe('removeTrailingSlash', () => { - it('is no-op for path without trailing slash', () => { - expect(removeTrailingSlash('/abcd')).toBe('/abcd'); - }); - it('removes / for path with trailing slash', () => { - expect(removeTrailingSlash('/abcd/')).toBe('/abcd'); - }); -}); - describe('parseURLPath', () => { it('parse and resolve pathname', () => { expect(parseURLPath('')).toEqual({ diff --git a/packages/docusaurus-utils/src/globUtils.ts b/packages/docusaurus-utils/src/globUtils.ts index 777eb84ccc50..72b65d75d7f9 100644 --- a/packages/docusaurus-utils/src/globUtils.ts +++ b/packages/docusaurus-utils/src/globUtils.ts @@ -9,8 +9,7 @@ import path from 'path'; import Micromatch from 'micromatch'; // Note: Micromatch is used by Globby -import {addSuffix} from './jsUtils'; - +import {addSuffix} from '@docusaurus/utils-common'; /** A re-export of the globby instance. */ export {default as Globby} from 'globby'; diff --git a/packages/docusaurus-utils/src/index.ts b/packages/docusaurus-utils/src/index.ts index 2a47cfb6140a..405da5258dd4 100644 --- a/packages/docusaurus-utils/src/index.ts +++ b/packages/docusaurus-utils/src/index.ts @@ -35,12 +35,7 @@ export { getPluginI18nPath, localizePath, } from './i18nUtils'; -export { - removeSuffix, - removePrefix, - mapAsyncSequential, - findAsyncSequential, -} from './jsUtils'; +export {mapAsyncSequential, findAsyncSequential} from './jsUtils'; export { normalizeUrl, getEditUrl, @@ -50,9 +45,6 @@ export { resolvePathname, parseURLPath, serializeURLPath, - addLeadingSlash, - addTrailingSlash, - removeTrailingSlash, hasSSHProtocol, buildHttpsUrl, buildSshUrl, diff --git a/packages/docusaurus-utils/src/jsUtils.ts b/packages/docusaurus-utils/src/jsUtils.ts index 2540d4166d69..240798b7ef82 100644 --- a/packages/docusaurus-utils/src/jsUtils.ts +++ b/packages/docusaurus-utils/src/jsUtils.ts @@ -5,30 +5,6 @@ * LICENSE file in the root directory of this source tree. */ -/** Adds a given string prefix to `str`. */ -export function addPrefix(str: string, prefix: string): string { - return str.startsWith(prefix) ? str : `${prefix}${str}`; -} - -/** Adds a given string suffix to `str`. */ -export function addSuffix(str: string, suffix: string): string { - return str.endsWith(suffix) ? str : `${str}${suffix}`; -} - -/** Removes a given string suffix from `str`. */ -export function removeSuffix(str: string, suffix: string): string { - if (suffix === '') { - // str.slice(0, 0) is "" - return str; - } - return str.endsWith(suffix) ? str.slice(0, -suffix.length) : str; -} - -/** Removes a given string prefix from `str`. */ -export function removePrefix(str: string, prefix: string): string { - return str.startsWith(prefix) ? str.slice(prefix.length) : str; -} - /** * `Array#map` for async operations where order matters. * @param array The array to traverse. diff --git a/packages/docusaurus-utils/src/urlUtils.ts b/packages/docusaurus-utils/src/urlUtils.ts index 8a7af4aa4b6d..ddfc04272c36 100644 --- a/packages/docusaurus-utils/src/urlUtils.ts +++ b/packages/docusaurus-utils/src/urlUtils.ts @@ -6,7 +6,6 @@ */ import resolvePathnameUnsafe from 'resolve-pathname'; -import {addPrefix, addSuffix, removeSuffix} from './jsUtils'; /** * Much like `path.join`, but much better. Takes an array of URL segments, and @@ -232,22 +231,6 @@ export function resolvePathname(to: string, from?: string): string { return resolvePathnameUnsafe(to, from); } -/** Appends a leading slash to `str`, if one doesn't exist. */ -export function addLeadingSlash(str: string): string { - return addPrefix(str, '/'); -} - -// TODO deduplicate: also present in @docusaurus/utils-common -/** Appends a trailing slash to `str`, if one doesn't exist. */ -export function addTrailingSlash(str: string): string { - return addSuffix(str, '/'); -} - -/** Removes the trailing slash from `str`. */ -export function removeTrailingSlash(str: string): string { - return removeSuffix(str, '/'); -} - /** Constructs an SSH URL that can be used to push to GitHub. */ export function buildSshUrl( githubHost: string, diff --git a/packages/docusaurus/package.json b/packages/docusaurus/package.json index 104ebb888fbc..6d47141a33c2 100644 --- a/packages/docusaurus/package.json +++ b/packages/docusaurus/package.json @@ -69,8 +69,8 @@ "del": "^6.1.1", "detect-port": "^1.5.1", "escape-html": "^1.0.3", - "eval": "^0.1.8", "eta": "^2.2.0", + "eval": "^0.1.8", "file-loader": "^6.2.0", "fs-extra": "^11.1.1", "html-minifier-terser": "^7.2.0", diff --git a/packages/docusaurus/src/server/brokenLinks.ts b/packages/docusaurus/src/server/brokenLinks.ts index e3ad16926030..9e940f7b8c6b 100644 --- a/packages/docusaurus/src/server/brokenLinks.ts +++ b/packages/docusaurus/src/server/brokenLinks.ts @@ -9,13 +9,12 @@ import _ from 'lodash'; import logger from '@docusaurus/logger'; import {matchRoutes as reactRouterMatchRoutes} from 'react-router-config'; import { - addTrailingSlash, parseURLPath, - removeTrailingSlash, serializeURLPath, flattenRoutes, type URLPath, } from '@docusaurus/utils'; +import {addTrailingSlash, removeTrailingSlash} from '@docusaurus/utils-common'; import type {RouteConfig, ReportingSeverity} from '@docusaurus/types'; function matchRoutes(routeConfig: RouteConfig[], pathname: string) { diff --git a/packages/docusaurus/src/server/configValidation.ts b/packages/docusaurus/src/server/configValidation.ts index eafabc3cedad..cf2d16aa59f5 100644 --- a/packages/docusaurus/src/server/configValidation.ts +++ b/packages/docusaurus/src/server/configValidation.ts @@ -9,11 +9,13 @@ import { DEFAULT_PARSE_FRONT_MATTER, DEFAULT_STATIC_DIR_NAME, DEFAULT_I18N_DIR_NAME, - addLeadingSlash, - addTrailingSlash, - removeTrailingSlash, } from '@docusaurus/utils'; import {Joi, printWarning} from '@docusaurus/utils-validation'; +import { + addTrailingSlash, + addLeadingSlash, + removeTrailingSlash, +} from '@docusaurus/utils-common'; import type { DocusaurusConfig, I18nConfig,