From 22043c606b04f8c8b66ac817d56d92e78ee9d383 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Lorber?= Date: Tue, 2 Jun 2026 20:40:51 +0200 Subject: [PATCH 1/2] refactor(test): remove usage of useless `tmp-promise` dependency (#12106) --- .../babelTranslationsExtractor.test.ts | 124 ++++++++++-------- .../docusaurus-utils-validation/package.json | 3 - .../src/__tests__/tagsFile.test.ts | 53 +++++--- packages/docusaurus-utils/package.json | 3 +- .../src/__tests__/moduleUtils.test.ts | 17 ++- packages/docusaurus/package.json | 1 - .../__tests__/translations.test.ts | 55 +++++--- .../__tests__/translationsExtractor.test.ts | 18 +-- yarn.lock | 9 +- 9 files changed, 153 insertions(+), 130 deletions(-) diff --git a/packages/docusaurus-babel/src/__tests__/babelTranslationsExtractor.test.ts b/packages/docusaurus-babel/src/__tests__/babelTranslationsExtractor.test.ts index 030adcb9e261..b3b08839339f 100644 --- a/packages/docusaurus-babel/src/__tests__/babelTranslationsExtractor.test.ts +++ b/packages/docusaurus-babel/src/__tests__/babelTranslationsExtractor.test.ts @@ -7,7 +7,9 @@ import {describe, expect, it, vi} from 'vitest'; import fs from 'fs-extra'; -import tmp from 'tmp-promise'; +import {mkdtempDisposable, realpath} from 'node:fs/promises'; +import {tmpdir} from 'node:os'; +import {join} from 'node:path'; import {getBabelOptions} from '../utils'; import {extractSourceCodeFileTranslations} from '../babelTranslationsExtractor'; @@ -15,28 +17,34 @@ const TestBabelOptions = getBabelOptions({ isServer: true, }); -async function createTmpSourceCodeFile({ +async function tmpFile(name: string) { + const dir = await mkdtempDisposable( + join(await realpath(tmpdir()), 'docusaurus-tmp-'), + ); + return { + path: join(dir.path, name), + [Symbol.asyncDispose]: dir[Symbol.asyncDispose], + }; +} + +async function tmpSourceCodeFile({ extension, content, }: { extension: string; content: string; }) { - const file = await tmp.file({ - prefix: 'jest-createTmpSourceCodeFile', - postfix: `.${extension}`, - }); - + const file = await tmpFile(`sourceCode.${extension}`); await fs.writeFile(file.path, content); - return { - sourceCodeFilePath: file.path, + path: file.path, + [Symbol.asyncDispose]: file[Symbol.asyncDispose], }; } describe('extractSourceCodeFileTranslations', () => { it('throws for bad source code', async () => { - const {sourceCodeFilePath} = await createTmpSourceCodeFile({ + await using sourceCodeFile = await tmpSourceCodeFile({ extension: 'js', content: ` const default => { @@ -48,7 +56,7 @@ const default => { using error = vi.spyOn(console, 'error'); await expect( - extractSourceCodeFileTranslations(sourceCodeFilePath, TestBabelOptions), + extractSourceCodeFileTranslations(sourceCodeFile.path, TestBabelOptions), ).rejects.toThrow(); expect(error).toHaveBeenCalledWith( @@ -59,7 +67,7 @@ const default => { }); it('extracts nothing from untranslated source code', async () => { - const {sourceCodeFilePath} = await createTmpSourceCodeFile({ + await using sourceCodeFile = await tmpSourceCodeFile({ extension: 'js', content: ` const unrelated = 42; @@ -67,19 +75,19 @@ const unrelated = 42; }); const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( - sourceCodeFilePath, + sourceCodeFile.path, TestBabelOptions, ); expect(sourceCodeFileTranslations).toEqual({ - sourceCodeFilePath, + sourceCodeFilePath: sourceCodeFile.path, translations: {}, warnings: [], }); }); it('extracts from a translate() functions calls', async () => { - const {sourceCodeFilePath} = await createTmpSourceCodeFile({ + await using sourceCodeFile = await tmpSourceCodeFile({ extension: 'js', content: ` import {translate} from '@docusaurus/Translate'; @@ -97,12 +105,12 @@ export default function MyComponent() { }); const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( - sourceCodeFilePath, + sourceCodeFile.path, TestBabelOptions, ); expect(sourceCodeFileTranslations).toEqual({ - sourceCodeFilePath, + sourceCodeFilePath: sourceCodeFile.path, translations: { codeId: {message: 'code message', description: 'code description'}, codeId1: {message: 'codeId1'}, @@ -112,7 +120,7 @@ export default function MyComponent() { }); it('extracts from a components', async () => { - const {sourceCodeFilePath} = await createTmpSourceCodeFile({ + await using sourceCodeFile = await tmpSourceCodeFile({ extension: 'js', content: ` import Translate from '@docusaurus/Translate'; @@ -132,12 +140,12 @@ export default function MyComponent() { }); const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( - sourceCodeFilePath, + sourceCodeFile.path, TestBabelOptions, ); expect(sourceCodeFileTranslations).toEqual({ - sourceCodeFilePath, + sourceCodeFilePath: sourceCodeFile.path, translations: { codeId: {message: 'code message', description: 'code description'}, codeId1: {message: 'codeId1', description: 'description 2'}, @@ -147,7 +155,7 @@ export default function MyComponent() { }); it('extracts statically evaluable content', async () => { - const {sourceCodeFilePath} = await createTmpSourceCodeFile({ + await using sourceCodeFile = await tmpSourceCodeFile({ extension: 'js', content: ` import Translate, {translate} from '@docusaurus/Translate'; @@ -184,12 +192,12 @@ export default function MyComponent() { }); const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( - sourceCodeFilePath, + sourceCodeFile.path, TestBabelOptions, ); expect(sourceCodeFileTranslations).toEqual({ - sourceCodeFilePath, + sourceCodeFilePath: sourceCodeFile.path, translations: { 'prefix codeId comp': { message: 'prefix code message', @@ -208,7 +216,7 @@ export default function MyComponent() { }); it('extracts from TypeScript file', async () => { - const {sourceCodeFilePath} = await createTmpSourceCodeFile({ + await using sourceCodeFile = await tmpSourceCodeFile({ extension: 'tsx', content: ` import {translate} from '@docusaurus/Translate'; @@ -227,12 +235,12 @@ export default function MyComponent(props: ComponentProps) { }); const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( - sourceCodeFilePath, + sourceCodeFile.path, TestBabelOptions, ); expect(sourceCodeFileTranslations).toEqual({ - sourceCodeFilePath, + sourceCodeFilePath: sourceCodeFile.path, translations: { codeId: {message: 'code message', description: 'code description'}, 'code message 2': { @@ -245,7 +253,7 @@ export default function MyComponent(props: ComponentProps) { }); it('does not extract from functions that is not docusaurus provided', async () => { - const {sourceCodeFilePath} = await createTmpSourceCodeFile({ + await using sourceCodeFile = await tmpSourceCodeFile({ extension: 'js', content: ` import translate from 'a-lib'; @@ -258,19 +266,19 @@ export default function somethingElse() { }); const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( - sourceCodeFilePath, + sourceCodeFile.path, TestBabelOptions, ); expect(sourceCodeFileTranslations).toEqual({ - sourceCodeFilePath, + sourceCodeFilePath: sourceCodeFile.path, translations: {}, warnings: [], }); }); it('does not extract from functions that is internal', async () => { - const {sourceCodeFilePath} = await createTmpSourceCodeFile({ + await using sourceCodeFile = await tmpSourceCodeFile({ extension: 'js', content: ` function translate() { @@ -285,19 +293,19 @@ export default function somethingElse() { }); const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( - sourceCodeFilePath, + sourceCodeFile.path, TestBabelOptions, ); expect(sourceCodeFileTranslations).toEqual({ - sourceCodeFilePath, + sourceCodeFilePath: sourceCodeFile.path, translations: {}, warnings: [], }); }); it('recognizes aliased imports', async () => { - const {sourceCodeFilePath} = await createTmpSourceCodeFile({ + await using sourceCodeFile = await tmpSourceCodeFile({ extension: 'js', content: ` import Foo, {translate as bar} from '@docusaurus/Translate'; @@ -327,12 +335,12 @@ export default function () { }); const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( - sourceCodeFilePath, + sourceCodeFile.path, TestBabelOptions, ); expect(sourceCodeFileTranslations).toEqual({ - sourceCodeFilePath, + sourceCodeFilePath: sourceCodeFile.path, translations: { codeId: { description: 'code description', @@ -347,7 +355,7 @@ export default function () { }); it('recognizes aliased imports as string literal', async () => { - const {sourceCodeFilePath} = await createTmpSourceCodeFile({ + await using sourceCodeFile = await tmpSourceCodeFile({ extension: 'js', content: ` import {'translate' as bar} from '@docusaurus/Translate'; @@ -365,12 +373,12 @@ export default function () { }); const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( - sourceCodeFilePath, + sourceCodeFile.path, TestBabelOptions, ); expect(sourceCodeFileTranslations).toEqual({ - sourceCodeFilePath, + sourceCodeFilePath: sourceCodeFile.path, translations: { codeId1: { message: 'codeId1', @@ -381,7 +389,7 @@ export default function () { }); it('warns about id if no children', async () => { - const {sourceCodeFilePath} = await createTmpSourceCodeFile({ + await using sourceCodeFile = await tmpSourceCodeFile({ extension: 'js', content: ` import Translate from '@docusaurus/Translate'; @@ -395,24 +403,24 @@ export default function () { }); const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( - sourceCodeFilePath, + sourceCodeFile.path, TestBabelOptions, ); expect(sourceCodeFileTranslations).toEqual({ - sourceCodeFilePath, + sourceCodeFilePath: sourceCodeFile.path, translations: {}, warnings: [ ` without children must have id prop. Example: -File: ${sourceCodeFilePath} at line 6 +File: ${sourceCodeFile.path} at line 6 Full code: `, ], }); }); it('warns about dynamic id', async () => { - const {sourceCodeFilePath} = await createTmpSourceCodeFile({ + await using sourceCodeFile = await tmpSourceCodeFile({ extension: 'js', content: ` import Translate from '@docusaurus/Translate'; @@ -426,12 +434,12 @@ export default function () { }); const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( - sourceCodeFilePath, + sourceCodeFile.path, TestBabelOptions, ); expect(sourceCodeFileTranslations).toEqual({ - sourceCodeFilePath, + sourceCodeFilePath: sourceCodeFile.path, translations: { foo: { message: 'foo', @@ -441,14 +449,14 @@ export default function () { ` prop=id should be a statically evaluable object. Example: Message Dynamically constructed values are not allowed, because they prevent translations to be extracted. -File: ${sourceCodeFilePath} at line 6 +File: ${sourceCodeFile.path} at line 6 Full code: foo`, ], }); }); it('warns about dynamic children', async () => { - const {sourceCodeFilePath} = await createTmpSourceCodeFile({ + await using sourceCodeFile = await tmpSourceCodeFile({ extension: 'js', content: ` import Translate from '@docusaurus/Translate'; @@ -462,23 +470,23 @@ export default function () { }); const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( - sourceCodeFilePath, + sourceCodeFile.path, TestBabelOptions, ); expect(sourceCodeFileTranslations).toEqual({ - sourceCodeFilePath, + sourceCodeFilePath: sourceCodeFile.path, translations: {}, warnings: [ `Translate content could not be extracted. It has to be a static string and use optional but static props, like text. -File: ${sourceCodeFilePath} at line 6 +File: ${sourceCodeFile.path} at line 6 Full code: hhh`, ], }); }); it('warns about dynamic translate argument', async () => { - const {sourceCodeFilePath} = await createTmpSourceCodeFile({ + await using sourceCodeFile = await tmpSourceCodeFile({ extension: 'js', content: ` import {translate} from '@docusaurus/Translate'; @@ -488,25 +496,25 @@ translate(foo); }); const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( - sourceCodeFilePath, + sourceCodeFile.path, TestBabelOptions, ); expect(sourceCodeFileTranslations).toEqual({ - sourceCodeFilePath, + sourceCodeFilePath: sourceCodeFile.path, translations: {}, warnings: [ `translate() first arg should be a statically evaluable object. Example: translate({message: "text",id: "optional.id",description: "optional description"} Dynamically constructed values are not allowed, because they prevent translations to be extracted. -File: ${sourceCodeFilePath} at line 4 +File: ${sourceCodeFile.path} at line 4 Full code: translate(foo)`, ], }); }); it('warns about too many arguments', async () => { - const {sourceCodeFilePath} = await createTmpSourceCodeFile({ + await using sourceCodeFile = await tmpSourceCodeFile({ extension: 'js', content: ` import {translate} from '@docusaurus/Translate'; @@ -516,16 +524,16 @@ translate({message: 'a'}, {a: 1}, 2); }); const sourceCodeFileTranslations = await extractSourceCodeFileTranslations( - sourceCodeFilePath, + sourceCodeFile.path, TestBabelOptions, ); expect(sourceCodeFileTranslations).toEqual({ - sourceCodeFilePath, + sourceCodeFilePath: sourceCodeFile.path, translations: {}, warnings: [ `translate() function only takes 1 or 2 args -File: ${sourceCodeFilePath} at line 4 +File: ${sourceCodeFile.path} at line 4 Full code: translate({ message: 'a' }, { diff --git a/packages/docusaurus-utils-validation/package.json b/packages/docusaurus-utils-validation/package.json index 17965664d62c..75f34d4901bb 100644 --- a/packages/docusaurus-utils-validation/package.json +++ b/packages/docusaurus-utils-validation/package.json @@ -27,9 +27,6 @@ "lodash": "^4.17.21", "tslib": "^2.6.0" }, - "devDependencies": { - "tmp-promise": "^3.0.3" - }, "engines": { "node": ">=24.14" } diff --git a/packages/docusaurus-utils-validation/src/__tests__/tagsFile.test.ts b/packages/docusaurus-utils-validation/src/__tests__/tagsFile.test.ts index c87a08510dc9..46719d0bf877 100644 --- a/packages/docusaurus-utils-validation/src/__tests__/tagsFile.test.ts +++ b/packages/docusaurus-utils-validation/src/__tests__/tagsFile.test.ts @@ -8,7 +8,9 @@ import {describe, expect, it} from 'vitest'; import * as path from 'path'; import * as fs from 'fs-extra'; -import * as tmp from 'tmp-promise'; +import {mkdtempDisposable, realpath} from 'node:fs/promises'; +import {tmpdir} from 'node:os'; +import {join} from 'node:path'; import * as YAML from 'js-yaml'; import { ensureUniquePermalinks, @@ -291,25 +293,24 @@ describe('getTagsFile', () => { }: { filePath: string; tagsFileInput: TagsFileInput; - }): Promise<{dir: string}> { - async function createTmpDir() { - return ( - await tmp.dir({ - prefix: 'jest-createTmpSiteDir', - }) - ).path; - } - const contentPath = await createTmpDir(); + }): Promise<{dir: string} & AsyncDisposable> { + const tmpDir = await mkdtempDisposable( + join(await realpath(tmpdir()), 'docusaurus-tmp-'), + ); + const contentPath = tmpDir.path; const finalFilePath = path.join(contentPath, filePath); const fileContent = YAML.dump(tagsFileInput); await fs.writeFile(finalFilePath, fileContent); - return {dir: contentPath}; + return { + dir: contentPath, + [Symbol.asyncDispose]: tmpDir[Symbol.asyncDispose], + }; } type Params = Parameters[0]; it('reads tags file - regular', async () => { - const {dir} = await createTestTagsFile({ + await using tagsFile = await createTestTagsFile({ filePath: 'tags.yml', tagsFileInput: { tag1: {label: 'Tag1 Label'}, @@ -321,6 +322,7 @@ describe('getTagsFile', () => { }, }, }); + const {dir} = tagsFile; const params: Params = { contentPaths: {contentPath: dir, contentPathLocalized: dir}, @@ -349,12 +351,13 @@ describe('getTagsFile', () => { }); it('reads tags file - only keys', async () => { - const {dir} = await createTestTagsFile({ + await using tagsFile = await createTestTagsFile({ filePath: 'tags.yml', tagsFileInput: { tagKey: null, }, }); + const {dir} = tagsFile; const params: Params = { contentPaths: {contentPath: dir, contentPathLocalized: dir}, @@ -373,12 +376,13 @@ describe('getTagsFile', () => { }); it('reads tags file - tags option undefined', async () => { - const {dir} = await createTestTagsFile({ + await using tagsFile = await createTestTagsFile({ filePath: 'tags.yml', tagsFileInput: { tag: {label: 'tag label'}, }, }); + const {dir} = tagsFile; const params: Params = { contentPaths: {contentPath: dir, contentPathLocalized: dir}, @@ -397,10 +401,11 @@ describe('getTagsFile', () => { }); it('reads tags file - empty file', async () => { - const {dir} = await createTestTagsFile({ + await using tagsFile = await createTestTagsFile({ filePath: 'tags.yml', tagsFileInput: {}, }); + const {dir} = tagsFile; const params: Params = { contentPaths: {contentPath: dir, contentPathLocalized: dir}, @@ -411,19 +416,21 @@ describe('getTagsFile', () => { }); it('reads tags file - prioritizes reading from localized content path', async () => { - const {dir} = await createTestTagsFile({ + await using tagsFile = await createTestTagsFile({ filePath: 'tags.yml', tagsFileInput: { tag: {label: 'tag label'}, }, }); + const {dir} = tagsFile; - const {dir: dirLocalized} = await createTestTagsFile({ + await using tagsFileLocalized = await createTestTagsFile({ filePath: 'tags.yml', tagsFileInput: { tag: {label: 'tag label (localized)'}, }, }); + const {dir: dirLocalized} = tagsFileLocalized; const params: Params = { contentPaths: {contentPath: dir, contentPathLocalized: dirLocalized}, @@ -442,12 +449,13 @@ describe('getTagsFile', () => { }); it('reads tags file - custom tags file path', async () => { - const {dir} = await createTestTagsFile({ + await using tagsFile = await createTestTagsFile({ filePath: 'custom-tags-path.yml', tagsFileInput: { tag: {label: 'tag label'}, }, }); + const {dir} = tagsFile; const params: Params = { contentPaths: {contentPath: dir, contentPathLocalized: dir}, @@ -466,13 +474,14 @@ describe('getTagsFile', () => { }); it('throws if duplicate permalink', async () => { - const {dir} = await createTestTagsFile({ + await using tagsFile = await createTestTagsFile({ filePath: 'tags.yml', tagsFileInput: { tag1: {permalink: '/duplicate'}, tag2: {permalink: '/duplicate'}, }, }); + const {dir} = tagsFile; const params: Params = { contentPaths: {contentPath: dir, contentPathLocalized: dir}, @@ -500,12 +509,13 @@ describe('getTagsFile', () => { }); it('does not read tags file - tags option null/false', async () => { - const {dir} = await createTestTagsFile({ + await using tagsFile = await createTestTagsFile({ filePath: 'tags.yml', tagsFileInput: { tag: {label: 'tag label'}, }, }); + const {dir} = tagsFile; await expect( getTagsFile({ @@ -522,12 +532,13 @@ describe('getTagsFile', () => { }); it('does not read tags file - tags files has non-default name', async () => { - const {dir} = await createTestTagsFile({ + await using tagsFile = await createTestTagsFile({ filePath: 'bad-tags-file-name.yml', tagsFileInput: { tag: {label: 'tag label'}, }, }); + const {dir} = tagsFile; const params: Params = { contentPaths: {contentPath: dir, contentPathLocalized: dir}, diff --git a/packages/docusaurus-utils/package.json b/packages/docusaurus-utils/package.json index 9e005e8aa277..148038ef8746 100644 --- a/packages/docusaurus-utils/package.json +++ b/packages/docusaurus-utils/package.json @@ -47,7 +47,6 @@ "@types/dedent": "^0.7.0", "@types/micromatch": "^4.0.10", "@types/react-dom": "^19.2.3", - "dedent": "^0.7.0", - "tmp-promise": "^3.0.3" + "dedent": "^0.7.0" } } diff --git a/packages/docusaurus-utils/src/__tests__/moduleUtils.test.ts b/packages/docusaurus-utils/src/__tests__/moduleUtils.test.ts index 901cc0510d88..a67b58c399af 100644 --- a/packages/docusaurus-utils/src/__tests__/moduleUtils.test.ts +++ b/packages/docusaurus-utils/src/__tests__/moduleUtils.test.ts @@ -8,23 +8,21 @@ import {describe, expect, it} from 'vitest'; import fs from 'fs-extra'; import path from 'path'; -import tmp from 'tmp-promise'; +import {mkdtempDisposable, realpath} from 'node:fs/promises'; +import {join} from 'node:path'; +import {tmpdir} from 'node:os'; import dedent from 'dedent'; import {loadFreshModule} from '../moduleUtils'; async function createTmpDir() { - return ( - await tmp.dir({ - prefix: 'jest-tmp-moduleUtils-tests', - }) - ).path; + return mkdtempDisposable(join(await realpath(tmpdir()), 'docusaurus-tmp-')); } async function moduleGraphHelpers() { const dir = await createTmpDir(); async function fileHelper(name: string, initialContent?: string) { - const filePath = path.resolve(dir, name); + const filePath = path.resolve(dir.path, name); if (initialContent) { await fs.outputFile(filePath, initialContent); } @@ -35,7 +33,7 @@ async function moduleGraphHelpers() { }; } - return {fileHelper}; + return {fileHelper, [Symbol.asyncDispose]: dir[Symbol.asyncDispose]}; } async function loadModule(modulePath: string, withDefault: boolean) { @@ -136,7 +134,8 @@ describe('loadFreshModule', () => { describe('module graph', () => { it('can load and reload fresh module graph', async () => { - const {fileHelper} = await moduleGraphHelpers(); + await using helpers = await moduleGraphHelpers(); + const {fileHelper} = helpers; const dependency1 = await fileHelper( 'dependency1.js', diff --git a/packages/docusaurus/package.json b/packages/docusaurus/package.json index 9aecb364da9f..5b9ee6c0ac90 100644 --- a/packages/docusaurus/package.json +++ b/packages/docusaurus/package.json @@ -87,7 +87,6 @@ "@types/update-notifier": "^6.0.4", "@types/webpack-bundle-analyzer": "^4.7.0", "@types/webpack-env": "^1.18.8", - "tmp-promise": "^3.0.3", "tree-node-cli": "^1.6.0" }, "peerDependencies": { diff --git a/packages/docusaurus/src/server/translations/__tests__/translations.test.ts b/packages/docusaurus/src/server/translations/__tests__/translations.test.ts index 55a621da42ac..bd684d43ab28 100644 --- a/packages/docusaurus/src/server/translations/__tests__/translations.test.ts +++ b/packages/docusaurus/src/server/translations/__tests__/translations.test.ts @@ -8,7 +8,9 @@ import {describe, expect, it, vi} from 'vitest'; import fs from 'fs-extra'; import path from 'path'; -import tmp from 'tmp-promise'; +import {mkdtempDisposable, realpath} from 'node:fs/promises'; +import {join} from 'node:path'; +import {tmpdir} from 'node:os'; import { writePluginTranslations, writeCodeTranslations, @@ -26,17 +28,14 @@ import type { } from '@docusaurus/types'; async function createTmpSiteDir() { - const {path: siteDirPath} = await tmp.dir({ - prefix: 'jest-createTmpSiteDir', - }); - return siteDirPath; + return mkdtempDisposable(join(await realpath(tmpdir()), 'docusaurus-tmp-')); } async function createTmpTranslationFile( content: TranslationFileContent | null, ) { const siteDir = await createTmpSiteDir(); - const filePath = path.join(siteDir, 'i18n/en/code.json'); + const filePath = path.join(siteDir.path, 'i18n/en/code.json'); // null means we don't want a file, just a filename if (content !== null) { @@ -44,10 +43,11 @@ async function createTmpTranslationFile( } return { - localizationDir: path.join(siteDir, 'i18n/en'), + localizationDir: path.join(siteDir.path, 'i18n/en'), readFile() { return fs.readJSON(filePath); }, + [Symbol.asyncDispose]: siteDir[Symbol.asyncDispose], }; } @@ -55,7 +55,9 @@ describe('writeCodeTranslations', () => { it('creates new translation file', async () => { using info = vi.spyOn(console, 'info'); - const {localizationDir, readFile} = await createTmpTranslationFile(null); + await using tmpTranslationFile = await createTmpTranslationFile(null); + const {localizationDir, readFile} = tmpTranslationFile; + await writeCodeTranslations( {localizationDir}, { @@ -79,7 +81,9 @@ describe('writeCodeTranslations', () => { it('creates new translation file with prefix', async () => { using info = vi.spyOn(console, 'info'); - const {localizationDir, readFile} = await createTmpTranslationFile(null); + await using tmpTranslationFile = await createTmpTranslationFile(null); + const {localizationDir, readFile} = tmpTranslationFile; + await writeCodeTranslations( {localizationDir}, { @@ -105,11 +109,12 @@ describe('writeCodeTranslations', () => { it('appends missing translations', async () => { using info = vi.spyOn(console, 'info'); - const {localizationDir, readFile} = await createTmpTranslationFile({ + await using tmpTranslationFile = await createTmpTranslationFile({ key1: {message: 'key1 message'}, key2: {message: 'key2 message'}, key3: {message: 'key3 message'}, }); + const {localizationDir, readFile} = tmpTranslationFile; await writeCodeTranslations( {localizationDir}, @@ -136,9 +141,10 @@ describe('writeCodeTranslations', () => { it('appends missing.* translations with prefix', async () => { using info = vi.spyOn(console, 'info'); - const {localizationDir, readFile} = await createTmpTranslationFile({ + await using tmpTranslationFile = await createTmpTranslationFile({ key1: {message: 'key1 message'}, }); + const {localizationDir, readFile} = tmpTranslationFile; await writeCodeTranslations( {localizationDir}, @@ -163,9 +169,10 @@ describe('writeCodeTranslations', () => { it('overrides missing translations', async () => { using info = vi.spyOn(console, 'info'); - const {localizationDir, readFile} = await createTmpTranslationFile({ + await using tmpTranslationFile = await createTmpTranslationFile({ key1: {message: 'key1 message'}, }); + const {localizationDir, readFile} = tmpTranslationFile; await writeCodeTranslations( {localizationDir}, @@ -190,9 +197,10 @@ describe('writeCodeTranslations', () => { it('overrides missing translations with prefix', async () => { using info = vi.spyOn(console, 'info'); - const {localizationDir, readFile} = await createTmpTranslationFile({ + await using tmpTranslationFile = await createTmpTranslationFile({ key1: {message: 'key1 message'}, }); + const {localizationDir, readFile} = tmpTranslationFile; await writeCodeTranslations( {localizationDir}, @@ -218,11 +226,12 @@ describe('writeCodeTranslations', () => { it('always overrides message description', async () => { using info = vi.spyOn(console, 'info'); - const {localizationDir, readFile} = await createTmpTranslationFile({ + await using tmpTranslationFile = await createTmpTranslationFile({ key1: {message: 'key1 message', description: 'key1 desc'}, key2: {message: 'key2 message', description: 'key2 desc'}, key3: {message: 'key3 message', description: undefined}, }); + const {localizationDir, readFile} = tmpTranslationFile; await writeCodeTranslations( {localizationDir}, @@ -247,7 +256,8 @@ describe('writeCodeTranslations', () => { it('does not create empty translation files', async () => { using info = vi.spyOn(console, 'info'); - const {localizationDir, readFile} = await createTmpTranslationFile(null); + await using tmpTranslationFile = await createTmpTranslationFile(null); + const {localizationDir, readFile} = tmpTranslationFile; await writeCodeTranslations({localizationDir}, {}, {}); @@ -280,7 +290,8 @@ describe('writeCodeTranslations', () => { describe('writePluginTranslations', () => { it('writes plugin translations', async () => { - const localizationDir = await createTmpSiteDir(); + await using tmpDir = await createTmpSiteDir(); + const localizationDir = tmpDir.path; const filePath = path.join( localizationDir, @@ -314,7 +325,8 @@ describe('writePluginTranslations', () => { }); it('writes plugin translations consecutively with different options', async () => { - const localizationDir = await createTmpSiteDir(); + await using tmpDir = await createTmpSiteDir(); + const localizationDir = tmpDir.path; const filePath = path.join( localizationDir, @@ -387,7 +399,8 @@ describe('writePluginTranslations', () => { }); it('throws with explicit extension', async () => { - const localizationDir = await createTmpSiteDir(); + await using tmpDir = await createTmpSiteDir(); + const localizationDir = tmpDir.path; await expect(() => writePluginTranslations({ @@ -414,7 +427,8 @@ describe('writePluginTranslations', () => { describe('localizePluginTranslationFile', () => { it('does not localize if localized file does not exist', async () => { - const localizationDir = await createTmpSiteDir(); + await using tmpDir = await createTmpSiteDir(); + const localizationDir = tmpDir.path; const translationFile: TranslationFile = { path: 'my/translation/file', @@ -438,7 +452,8 @@ describe('localizePluginTranslationFile', () => { }); it('normalizes partially localized translation files', async () => { - const localizationDir = await createTmpSiteDir(); + await using tmpDir = await createTmpSiteDir(); + const localizationDir = tmpDir.path; await fs.outputJSON( path.join(localizationDir, 'my-plugin-name', 'my/translation/file.json'), diff --git a/packages/docusaurus/src/server/translations/__tests__/translationsExtractor.test.ts b/packages/docusaurus/src/server/translations/__tests__/translationsExtractor.test.ts index 48b349d2b0af..07b86767aa93 100644 --- a/packages/docusaurus/src/server/translations/__tests__/translationsExtractor.test.ts +++ b/packages/docusaurus/src/server/translations/__tests__/translationsExtractor.test.ts @@ -8,21 +8,21 @@ import {describe, expect, it, vi} from 'vitest'; import path from 'path'; import fs from 'fs-extra'; -import tmp from 'tmp-promise'; +import {mkdtempDisposable, realpath} from 'node:fs/promises'; +import {tmpdir} from 'node:os'; +import {join} from 'node:path'; import {SRC_DIR_NAME} from '@docusaurus/utils'; import {extractSiteSourceCodeTranslations} from '../translationsExtractor'; import type {InitializedPlugin, LoadedPlugin} from '@docusaurus/types'; async function createTmpDir() { - const {path: siteDirPath} = await tmp.dir({ - prefix: 'jest-createTmpSiteDir', - }); - return siteDirPath; + return mkdtempDisposable(join(await realpath(tmpdir()), 'docusaurus-tmp-')); } describe('extractSiteSourceCodeTranslations', () => { it('extracts translation from all plugins source code', async () => { - const siteDir = await createTmpDir(); + await using siteDirTmp = await createTmpDir(); + const siteDir = siteDirTmp.path; const siteComponentFile1 = path.join( siteDir, @@ -60,7 +60,8 @@ export default function MySiteComponent1() { }; } - const plugin1Dir = await createTmpDir(); + await using plugin1DirTmp = await createTmpDir(); + const plugin1Dir = plugin1DirTmp.path; const plugin1File1 = path.join(plugin1Dir, 'subpath', 'file1.jsx'); await fs.outputFile( plugin1File1, @@ -134,7 +135,8 @@ export default function MyComponent() { const plugin1 = createTestPlugin(plugin1Dir); - const plugin2Dir = await createTmpDir(); + await using plugin2DirTmp = await createTmpDir(); + const plugin2Dir = plugin2DirTmp.path; const plugin2File = path.join(plugin1Dir, 'subpath', 'file.tsx'); await fs.outputFile( plugin2File, diff --git a/yarn.lock b/yarn.lock index 9e7a63025a30..ea3cd1e92603 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17882,14 +17882,7 @@ tldts@^6.1.32: dependencies: tldts-core "^6.1.86" -tmp-promise@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/tmp-promise/-/tmp-promise-3.0.3.tgz#60a1a1cc98c988674fcbfd23b6e3367bdeac4ce7" - integrity sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ== - dependencies: - tmp "^0.2.0" - -tmp@^0.2.0, tmp@^0.2.1, tmp@^0.2.4, tmp@~0.2.1: +tmp@^0.2.1, tmp@^0.2.4, tmp@~0.2.1: version "0.2.7" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.7.tgz#26f4db11d1601ce8012dcb8a798ece1c06a99059" integrity sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw== From 9ab8633d5717295bd46b23eb21c5fbc3ce6598b8 Mon Sep 17 00:00:00 2001 From: vidigoat Date: Wed, 3 Jun 2026 00:16:31 +0530 Subject: [PATCH 2/2] docs(utils): fix incorrect encodePath JSDoc examples (#12088) --- packages/docusaurus-utils/src/urlUtils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/docusaurus-utils/src/urlUtils.ts b/packages/docusaurus-utils/src/urlUtils.ts index 3f3056abc8d3..48cf456eb794 100644 --- a/packages/docusaurus-utils/src/urlUtils.ts +++ b/packages/docusaurus-utils/src/urlUtils.ts @@ -138,8 +138,8 @@ export function fileToPath(file: string): string { * Similar to `encodeURI`, but uses `encodeURIComponent` and assumes there's no * query. * - * `encodeURI("/question?/answer")` => `"/question?/answer#section"`; - * `encodePath("/question?/answer#section")` => `"/question%3F/answer%23foo"` + * `encodeURI("/question?/answer")` => `"/question?/answer"`; + * `encodePath("/question?/answer#section")` => `"/question%3F/answer%23section"` */ export function encodePath(userPath: string): string { return userPath