From 92f874879d3ceb60032e4ebb1b4a2f9e7d422356 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Torres?= Date: Fri, 9 Jan 2026 13:43:59 +0100 Subject: [PATCH 1/2] refactor(create-docusaurus): replace lodash with native implementation --- packages/create-docusaurus/package.json | 1 - packages/create-docusaurus/src/index.ts | 15 ++++++++++---- .../src/__tests__/jsUtils.test.ts | 20 ++++++++++++++++++- packages/docusaurus-utils/src/index.ts | 2 +- packages/docusaurus-utils/src/jsUtils.ts | 11 ++++++++++ 5 files changed, 42 insertions(+), 7 deletions(-) diff --git a/packages/create-docusaurus/package.json b/packages/create-docusaurus/package.json index a865c1052a4d..179bd121f4a0 100755 --- a/packages/create-docusaurus/package.json +++ b/packages/create-docusaurus/package.json @@ -27,7 +27,6 @@ "commander": "^5.1.0", "execa": "^5.1.1", "fs-extra": "^11.1.1", - "lodash": "^4.17.21", "prompts": "^2.4.2", "semver": "^7.5.4", "supports-color": "^9.4.0", diff --git a/packages/create-docusaurus/src/index.ts b/packages/create-docusaurus/src/index.ts index ae9180746bc3..a1443d686541 100755 --- a/packages/create-docusaurus/src/index.ts +++ b/packages/create-docusaurus/src/index.ts @@ -8,7 +8,6 @@ import fs from 'fs-extra'; import {fileURLToPath} from 'url'; import path from 'path'; -import _ from 'lodash'; import {logger} from '@docusaurus/logger'; import execa from 'execa'; import prompts, {type Choice} from 'prompts'; @@ -16,7 +15,7 @@ import supportsColor from 'supports-color'; // TODO remove dependency on large @docusaurus/utils // would be better to have a new smaller @docusaurus/utils-cli package -import {askPreferredLanguage} from '@docusaurus/utils'; +import {askPreferredLanguage, kebabCase} from '@docusaurus/utils'; type LanguagesOptions = { javascript?: boolean; @@ -164,7 +163,15 @@ async function readTemplates(): Promise { ); // Classic should be first in list! - return _.sortBy(templates, (t) => t.name !== recommendedTemplate); + return templates.sort((a, b) => { + if (a.name === recommendedTemplate) { + return -1; + } + if (b.name === recommendedTemplate) { + return 1; + } + return 0; + }); } async function copyTemplate( @@ -562,7 +569,7 @@ export default async function init( // Update package.json info. try { await updatePkg(path.join(dest, 'package.json'), { - name: _.kebabCase(siteName), + name: kebabCase(siteName), version: '0.0.0', private: true, }); diff --git a/packages/docusaurus-utils/src/__tests__/jsUtils.test.ts b/packages/docusaurus-utils/src/__tests__/jsUtils.test.ts index fb3750e0eae7..405a87cb4f46 100644 --- a/packages/docusaurus-utils/src/__tests__/jsUtils.test.ts +++ b/packages/docusaurus-utils/src/__tests__/jsUtils.test.ts @@ -7,7 +7,7 @@ import {jest} from '@jest/globals'; import _ from 'lodash'; -import {mapAsyncSequential, findAsyncSequential} from '../jsUtils'; +import {mapAsyncSequential, findAsyncSequential, kebabCase} from '../jsUtils'; describe('mapAsyncSequential', () => { function sleep(timeout: number): Promise { @@ -80,3 +80,21 @@ describe('findAsyncSequential', () => { expect(timeTotal).toBeLessThan(1000); }); }); + +describe('kebabCase', () => { + it('conversion', () => { + const testCases: [string, string][] = [ + ['Foo Bar', 'foo-bar'], + ['fooBar', 'foo-bar'], + ['__FOO_BAR__', 'foo-bar'], + ['XMLHttpRequest', 'xml-http-request'], + ['sitemapXML', 'sitemap-xml'], + ['XMLHttp', 'xml-http'], + ['xml-http', 'xml-http'], + ]; + + testCases.forEach(([input, expected]) => { + expect(kebabCase(input)).toEqual(expected); + }); + }); +}); diff --git a/packages/docusaurus-utils/src/index.ts b/packages/docusaurus-utils/src/index.ts index bd93e1d19917..2f1c1d56172b 100644 --- a/packages/docusaurus-utils/src/index.ts +++ b/packages/docusaurus-utils/src/index.ts @@ -37,7 +37,7 @@ export { getPluginI18nPath, getLocaleConfig, } from './i18nUtils'; -export {mapAsyncSequential, findAsyncSequential} from './jsUtils'; +export {mapAsyncSequential, findAsyncSequential, kebabCase} from './jsUtils'; export { normalizeUrl, getEditUrl, diff --git a/packages/docusaurus-utils/src/jsUtils.ts b/packages/docusaurus-utils/src/jsUtils.ts index 240798b7ef82..8b37d79fe22c 100644 --- a/packages/docusaurus-utils/src/jsUtils.ts +++ b/packages/docusaurus-utils/src/jsUtils.ts @@ -43,3 +43,14 @@ export async function findAsyncSequential( } return undefined; } + +/** + * Converts a string to kebab-case. + * Mimics lodash's behavior. + */ +export function kebabCase(str: string): string { + const match = str.match( + /[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b|[_])|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g, + ); + return match ? match.map((x) => x.toLowerCase()).join('-') : ''; +} From 0fdc254652bf887f2b829b9c8d934526e4a088b8 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 22 Jan 2026 17:44:18 +0100 Subject: [PATCH 2/2] better integration --- .../src/__tests__/utils.test.ts | 42 +++++++++++++++++++ packages/create-docusaurus/src/index.ts | 5 ++- packages/create-docusaurus/src/utils.ts | 23 ++++++++++ .../src/__tests__/jsUtils.test.ts | 20 +-------- packages/docusaurus-utils/src/index.ts | 2 +- packages/docusaurus-utils/src/jsUtils.ts | 11 ----- 6 files changed, 70 insertions(+), 33 deletions(-) create mode 100644 packages/create-docusaurus/src/__tests__/utils.test.ts create mode 100644 packages/create-docusaurus/src/utils.ts diff --git a/packages/create-docusaurus/src/__tests__/utils.test.ts b/packages/create-docusaurus/src/__tests__/utils.test.ts new file mode 100644 index 000000000000..89fa327cecef --- /dev/null +++ b/packages/create-docusaurus/src/__tests__/utils.test.ts @@ -0,0 +1,42 @@ +/** + * 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 {siteNameToPackageName} from '../utils'; + +describe('siteNameToPackageName', () => { + it('converts simple cases', () => { + const testCases: [string, string][] = [ + ['Foo Bar', 'foo-bar'], + ['fooBar', 'foo-bar'], + ['__FOO_BAR__', 'foo-bar'], + ['XMLHttpRequest', 'xml-http-request'], + ['sitemapXML', 'sitemap-xml'], + ['XMLHttp', 'xml-http'], + ['xml-http', 'xml-http'], + ]; + + testCases.forEach(([input, expected]) => { + expect(siteNameToPackageName(input)).toEqual(expected); + }); + }); + + it('converts ñ', () => { + expect(siteNameToPackageName('mañanaFoo')).toEqual('ma-ana-foo'); + }); + + it('converts __', () => { + expect(siteNameToPackageName('foo__bar')).toEqual('foo-bar'); + }); + + it('skips 🔥', () => { + expect(siteNameToPackageName('🔥')).toEqual('🔥'); + }); + + it('skips !!!', () => { + expect(siteNameToPackageName('!!!')).toEqual('!!!'); + }); +}); diff --git a/packages/create-docusaurus/src/index.ts b/packages/create-docusaurus/src/index.ts index a1443d686541..76b357b708d5 100755 --- a/packages/create-docusaurus/src/index.ts +++ b/packages/create-docusaurus/src/index.ts @@ -15,7 +15,8 @@ import supportsColor from 'supports-color'; // TODO remove dependency on large @docusaurus/utils // would be better to have a new smaller @docusaurus/utils-cli package -import {askPreferredLanguage, kebabCase} from '@docusaurus/utils'; +import {askPreferredLanguage} from '@docusaurus/utils'; +import {siteNameToPackageName} from './utils.js'; type LanguagesOptions = { javascript?: boolean; @@ -569,7 +570,7 @@ export default async function init( // Update package.json info. try { await updatePkg(path.join(dest, 'package.json'), { - name: kebabCase(siteName), + name: siteNameToPackageName(siteName), version: '0.0.0', private: true, }); diff --git a/packages/create-docusaurus/src/utils.ts b/packages/create-docusaurus/src/utils.ts new file mode 100644 index 000000000000..29bf0485b1e0 --- /dev/null +++ b/packages/create-docusaurus/src/utils.ts @@ -0,0 +1,23 @@ +/** + * 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. + */ + +/** + * We use a simple kebab-case-like conversion + * It's not perfect, but good enough + * We don't want to depend on lodash in this package + * See https://github.com/facebook/docusaurus/pull/11653 + * @param siteName + */ +export function siteNameToPackageName(siteName: string): string { + const match = siteName.match( + /[A-Z]{2,}(?=[A-Z][a-z]+\d*|\b|_)|[A-Z]?[a-z]+\d*|[A-Z]|\d+/g, + ); + if (match) { + return match.map((x) => x.toLowerCase()).join('-'); + } + return siteName; +} diff --git a/packages/docusaurus-utils/src/__tests__/jsUtils.test.ts b/packages/docusaurus-utils/src/__tests__/jsUtils.test.ts index 405a87cb4f46..fb3750e0eae7 100644 --- a/packages/docusaurus-utils/src/__tests__/jsUtils.test.ts +++ b/packages/docusaurus-utils/src/__tests__/jsUtils.test.ts @@ -7,7 +7,7 @@ import {jest} from '@jest/globals'; import _ from 'lodash'; -import {mapAsyncSequential, findAsyncSequential, kebabCase} from '../jsUtils'; +import {mapAsyncSequential, findAsyncSequential} from '../jsUtils'; describe('mapAsyncSequential', () => { function sleep(timeout: number): Promise { @@ -80,21 +80,3 @@ describe('findAsyncSequential', () => { expect(timeTotal).toBeLessThan(1000); }); }); - -describe('kebabCase', () => { - it('conversion', () => { - const testCases: [string, string][] = [ - ['Foo Bar', 'foo-bar'], - ['fooBar', 'foo-bar'], - ['__FOO_BAR__', 'foo-bar'], - ['XMLHttpRequest', 'xml-http-request'], - ['sitemapXML', 'sitemap-xml'], - ['XMLHttp', 'xml-http'], - ['xml-http', 'xml-http'], - ]; - - testCases.forEach(([input, expected]) => { - expect(kebabCase(input)).toEqual(expected); - }); - }); -}); diff --git a/packages/docusaurus-utils/src/index.ts b/packages/docusaurus-utils/src/index.ts index 2f1c1d56172b..bd93e1d19917 100644 --- a/packages/docusaurus-utils/src/index.ts +++ b/packages/docusaurus-utils/src/index.ts @@ -37,7 +37,7 @@ export { getPluginI18nPath, getLocaleConfig, } from './i18nUtils'; -export {mapAsyncSequential, findAsyncSequential, kebabCase} from './jsUtils'; +export {mapAsyncSequential, findAsyncSequential} from './jsUtils'; export { normalizeUrl, getEditUrl, diff --git a/packages/docusaurus-utils/src/jsUtils.ts b/packages/docusaurus-utils/src/jsUtils.ts index 8b37d79fe22c..240798b7ef82 100644 --- a/packages/docusaurus-utils/src/jsUtils.ts +++ b/packages/docusaurus-utils/src/jsUtils.ts @@ -43,14 +43,3 @@ export async function findAsyncSequential( } return undefined; } - -/** - * Converts a string to kebab-case. - * Mimics lodash's behavior. - */ -export function kebabCase(str: string): string { - const match = str.match( - /[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b|[_])|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g, - ); - return match ? match.map((x) => x.toLowerCase()).join('-') : ''; -}