chore(v2): fix windows Jest tests (#3959)

* test(v2): Fix docusaurus-utils tests for windows

* test(v2): Fix plugin-client-redirects test

- add the posixPath in writeRedirectsFiles.ts

* test(v2): Fix plugin-content-pages test

add posixPath in test and index

* test(v2): add window test configuration

 - add the window test configuration in nodejs-windows.yml

* test(v2): revert plugin-content-pages test fix

* test(v2): Fix mdx-loader/transformImage test

* test(v2): add cleanPath in transformImage test

* fix version path tests for windows

* make versionMetadata test work on Windows

* try to fix posix/win32 path issues

* attempt to fix windows test

* try to make source alias less win32 sensitive

* try to make source alias less win32 sensitive

* try to make source alias less win32 sensitive

* try to make source alias less win32 sensitive

* try to make source alias less win32 sensitive

* try to make source alias less win32 sensitive

* specific jest config for windows

* attempt to fix windows testing issue

* attempt to fix windows testing issue

* attempt to fix windows testing issue

* attempt to fix windows testing issue

* attempt to fix windows testing issue

* attempt to fix windows testing issue

* attempt to fix windows testing issue

* remove bad cleanPath fn

* try to fix windows tests

* try to fix windows tests

* blog: try to fix windows tests by using same logic as on docs plugin

* try to fix windows tests

* try to fix windows tests

* try to fix windows tests

* try to fix windows tests

* improve the Github CI setup for windows: make jobs run in parallel

* revert GH action change

Co-authored-by: Sachin Kumar Rajput <skr571999@gmail.com>
This commit is contained in:
Sébastien Lorber 2020-12-28 19:50:12 +01:00 committed by GitHub
parent 863a4d85d3
commit 141d062c3b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 189 additions and 131 deletions

View file

@ -29,6 +29,8 @@ jobs:
- name: Docusaurus 1 Build - name: Docusaurus 1 Build
if: steps.filter.outputs.v1 == 'true' if: steps.filter.outputs.v1 == 'true'
run: yarn build:v1 run: yarn build:v1
- name: Docusaurus Jest Tests
run: yarn test
- name: Docusaurus 2 Build - name: Docusaurus 2 Build
run: yarn build:v2 run: yarn build:v2
env: env:

View file

@ -7,6 +7,14 @@
const path = require('path'); const path = require('path');
const isWin = process.platform === 'win32';
const windowsSpecificIgnorePatterns = [
// v1 is legacy, not really worth it to invest in making its tests work on Windows
'/packages/docusaurus-1.x',
'/packages/docusaurus-init-1.x',
];
const ignorePatterns = [ const ignorePatterns = [
'/node_modules/', '/node_modules/',
'__fixtures__', '__fixtures__',
@ -19,7 +27,7 @@ const ignorePatterns = [
'/packages/docusaurus-theme-classic/lib', '/packages/docusaurus-theme-classic/lib',
'/packages/docusaurus-theme-classic/lib-next', '/packages/docusaurus-theme-classic/lib-next',
'/packages/docusaurus-migrate/lib', '/packages/docusaurus-migrate/lib',
]; ].concat(isWin ? windowsSpecificIgnorePatterns : []);
module.exports = { module.exports = {
rootDir: path.resolve(__dirname), rootDir: path.resolve(__dirname),

View file

@ -11,7 +11,7 @@ const url = require('url');
const fs = require('fs-extra'); const fs = require('fs-extra');
const escapeHtml = require('escape-html'); const escapeHtml = require('escape-html');
const {getFileLoaderUtils} = require('@docusaurus/core/lib/webpack/utils'); const {getFileLoaderUtils} = require('@docusaurus/core/lib/webpack/utils');
const {posixPath} = require('@docusaurus/utils'); const {posixPath, toMessageRelativeFilePath} = require('@docusaurus/utils');
const { const {
loaders: {inlineMarkdownImageFileLoader}, loaders: {inlineMarkdownImageFileLoader},
@ -37,19 +37,13 @@ const createJSX = (node, pathUrl) => {
} }
}; };
// Needed to throw errors with computer-agnostic path messages
// Absolute paths are too dependant of user FS
function toRelativePath(filePath) {
return path.relative(process.cwd(), filePath);
}
async function ensureImageFileExist(imagePath, sourceFilePath) { async function ensureImageFileExist(imagePath, sourceFilePath) {
const imageExists = await fs.exists(imagePath); const imageExists = await fs.exists(imagePath);
if (!imageExists) { if (!imageExists) {
throw new Error( throw new Error(
`Image ${toRelativePath(imagePath)} used in ${toRelativePath( `Image ${toMessageRelativeFilePath(
sourceFilePath, imagePath,
)} not found.`, )} used in ${toMessageRelativeFilePath(sourceFilePath)} not found.`,
); );
} }
} }
@ -57,7 +51,9 @@ async function ensureImageFileExist(imagePath, sourceFilePath) {
async function processImageNode(node, {filePath, staticDir}) { async function processImageNode(node, {filePath, staticDir}) {
if (!node.url) { if (!node.url) {
throw new Error( throw new Error(
`Markdown image url is mandatory. filePath=${toRelativePath(filePath)}`, `Markdown image url is mandatory. filePath=${toMessageRelativeFilePath(
filePath,
)}`,
); );
} }

View file

@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
const {posixPath} = require('@docusaurus/utils'); const {toMessageRelativeFilePath, posixPath} = require('@docusaurus/utils');
const visit = require('unist-util-visit'); const visit = require('unist-util-visit');
const path = require('path'); const path = require('path');
@ -19,19 +19,13 @@ const {
loaders: {inlineMarkdownLinkFileLoader}, loaders: {inlineMarkdownLinkFileLoader},
} = getFileLoaderUtils(); } = getFileLoaderUtils();
// Needed to throw errors with computer-agnostic path messages
// Absolute paths are too dependant of user FS
function toRelativePath(filePath) {
return path.relative(process.cwd(), filePath);
}
async function ensureAssetFileExist(fileSystemAssetPath, sourceFilePath) { async function ensureAssetFileExist(fileSystemAssetPath, sourceFilePath) {
const assetExists = await fs.exists(fileSystemAssetPath); const assetExists = await fs.exists(fileSystemAssetPath);
if (!assetExists) { if (!assetExists) {
throw new Error( throw new Error(
`Asset ${toRelativePath(fileSystemAssetPath)} used in ${toRelativePath( `Asset ${toMessageRelativeFilePath(
sourceFilePath, fileSystemAssetPath,
)} not found.`, )} used in ${toMessageRelativeFilePath(sourceFilePath)} not found.`,
); );
} }
} }
@ -111,7 +105,7 @@ async function processLinkNode({node, _index, _parent, filePath, staticDir}) {
const line = const line =
(node.position && node.position.start && node.position.start.line) || '?'; (node.position && node.position.start && node.position.start.line) || '?';
throw new Error( throw new Error(
`Markdown link url is mandatory. filePath=${toRelativePath( `Markdown link url is mandatory. filePath=${toMessageRelativeFilePath(
filePath, filePath,
)}, title=${title}, line=${line}`, )}, title=${title}, line=${line}`,
); );

View file

@ -65,7 +65,7 @@ describe('loadBlog', () => {
'https://github.com/facebook/docusaurus/edit/master/website-1x/blog/date-matter.md', 'https://github.com/facebook/docusaurus/edit/master/website-1x/blog/date-matter.md',
permalink: '/blog/date-matter', permalink: '/blog/date-matter',
readingTime: 0.02, readingTime: 0.02,
source: path.join('@site', pluginPath, 'date-matter.md'), source: path.posix.join('@site', pluginPath, 'date-matter.md'),
title: 'date-matter', title: 'date-matter',
description: `date inside front matter`, description: `date inside front matter`,
date: new Date('2019-01-01'), date: new Date('2019-01-01'),
@ -87,10 +87,10 @@ describe('loadBlog', () => {
'https://github.com/facebook/docusaurus/edit/master/website-1x/i18n/en/docusaurus-plugin-content-blog/2018-12-14-Happy-First-Birthday-Slash.md', 'https://github.com/facebook/docusaurus/edit/master/website-1x/i18n/en/docusaurus-plugin-content-blog/2018-12-14-Happy-First-Birthday-Slash.md',
permalink: '/blog/2018/12/14/Happy-First-Birthday-Slash', permalink: '/blog/2018/12/14/Happy-First-Birthday-Slash',
readingTime: 0.015, readingTime: 0.015,
source: path.join( source: path.posix.join(
'@site', '@site',
// pluginPath, // pluginPath,
path.join('i18n', 'en', 'docusaurus-plugin-content-blog'), path.posix.join('i18n', 'en', 'docusaurus-plugin-content-blog'),
'2018-12-14-Happy-First-Birthday-Slash.md', '2018-12-14-Happy-First-Birthday-Slash.md',
), ),
title: 'Happy 1st Birthday Slash! (translated)', title: 'Happy 1st Birthday Slash! (translated)',
@ -112,7 +112,7 @@ describe('loadBlog', () => {
'https://github.com/facebook/docusaurus/edit/master/website-1x/blog/complex-slug.md', 'https://github.com/facebook/docusaurus/edit/master/website-1x/blog/complex-slug.md',
permalink: '/blog/hey/my super path/héllô', permalink: '/blog/hey/my super path/héllô',
readingTime: 0.015, readingTime: 0.015,
source: path.join('@site', pluginPath, 'complex-slug.md'), source: path.posix.join('@site', pluginPath, 'complex-slug.md'),
title: 'Complex Slug', title: 'Complex Slug',
description: `complex url slug`, description: `complex url slug`,
prevItem: undefined, prevItem: undefined,
@ -133,7 +133,7 @@ describe('loadBlog', () => {
'https://github.com/facebook/docusaurus/edit/master/website-1x/blog/simple-slug.md', 'https://github.com/facebook/docusaurus/edit/master/website-1x/blog/simple-slug.md',
permalink: '/blog/simple/slug', permalink: '/blog/simple/slug',
readingTime: 0.015, readingTime: 0.015,
source: path.join('@site', pluginPath, 'simple-slug.md'), source: path.posix.join('@site', pluginPath, 'simple-slug.md'),
title: 'Simple Slug', title: 'Simple Slug',
description: `simple url slug`, description: `simple url slug`,
prevItem: undefined, prevItem: undefined,
@ -162,7 +162,7 @@ describe('loadBlog', () => {
'website-blog-without-date', 'website-blog-without-date',
); );
const blogPosts = await getBlogPosts(siteDir); const blogPosts = await getBlogPosts(siteDir);
const noDateSource = path.join('@site', pluginPath, 'no date.md'); const noDateSource = path.posix.join('@site', pluginPath, 'no date.md');
const noDateSourceBirthTime = ( const noDateSourceBirthTime = (
await fs.stat(noDateSource.replace('@site', siteDir)) await fs.stat(noDateSource.replace('@site', siteDir))
).birthtime; ).birthtime;

View file

@ -21,7 +21,7 @@ const blogPosts: BlogPost[] = [
id: 'Happy 1st Birthday Slash!', id: 'Happy 1st Birthday Slash!',
metadata: { metadata: {
permalink: '/blog/2018/12/14/Happy-First-Birthday-Slash', permalink: '/blog/2018/12/14/Happy-First-Birthday-Slash',
source: path.join( source: path.posix.join(
'@site', '@site',
pluginDir, pluginDir,
'2018-12-14-Happy-First-Birthday-Slash.md', '2018-12-14-Happy-First-Birthday-Slash.md',

View file

@ -9,6 +9,7 @@ import fs from 'fs-extra';
import globby from 'globby'; import globby from 'globby';
import chalk from 'chalk'; import chalk from 'chalk';
import path from 'path'; import path from 'path';
import {resolve} from 'url';
import readingTime from 'reading-time'; import readingTime from 'reading-time';
import {Feed} from 'feed'; import {Feed} from 'feed';
import { import {
@ -240,13 +241,18 @@ export function linkify({
while (mdMatch !== null) { while (mdMatch !== null) {
const mdLink = mdMatch[1]; const mdLink = mdMatch[1];
const aliasedPostSource = `@site/${path.relative(
siteDir, const aliasedSource = (source: string) =>
path.resolve(folderPath, mdLink), aliasedSitePath(source, siteDir);
)}`;
const blogPost: BlogPost | undefined = const blogPost: BlogPost | undefined =
blogPostsBySource[aliasedPostSource]; blogPostsBySource[aliasedSource(resolve(filePath, mdLink))] ||
blogPostsBySource[
aliasedSource(`${contentPaths.contentPathLocalized}/${mdLink}`)
] ||
blogPostsBySource[
aliasedSource(`${contentPaths.contentPath}/${mdLink}`)
];
if (blogPost) { if (blogPost) {
modifiedLine = modifiedLine.replace( modifiedLine = modifiedLine.replace(

View file

@ -14,6 +14,7 @@ import {
aliasedSitePath, aliasedSitePath,
getPluginI18nPath, getPluginI18nPath,
reportMessage, reportMessage,
posixPath,
} from '@docusaurus/utils'; } from '@docusaurus/utils';
import { import {
STATIC_DIR_NAME, STATIC_DIR_NAME,
@ -84,7 +85,7 @@ export default function pluginContentBlog(
); );
const dataDir = path.join(pluginDataDirRoot, pluginId); const dataDir = path.join(pluginDataDirRoot, pluginId);
const aliasedSource = (source: string) => const aliasedSource = (source: string) =>
`~blog/${path.relative(pluginDataDirRoot, source)}`; `~blog/${posixPath(path.relative(pluginDataDirRoot, source))}`;
let blogPosts: BlogPost[] = []; let blogPosts: BlogPost[] = [];

View file

@ -20,6 +20,7 @@ import {LoadContext} from '@docusaurus/types';
import {DEFAULT_PLUGIN_ID} from '@docusaurus/core/lib/constants'; import {DEFAULT_PLUGIN_ID} from '@docusaurus/core/lib/constants';
import {DEFAULT_OPTIONS} from '../options'; import {DEFAULT_OPTIONS} from '../options';
import {Optional} from 'utility-types'; import {Optional} from 'utility-types';
import {posixPath} from '@docusaurus/utils';
const fixtureDir = path.join(__dirname, '__fixtures__'); const fixtureDir = path.join(__dirname, '__fixtures__');
@ -89,10 +90,10 @@ function createTestUtils({
lastUpdatedAt: undefined, lastUpdatedAt: undefined,
sidebar_label: undefined, sidebar_label: undefined,
editUrl: undefined, editUrl: undefined,
source: path.join( source: path.posix.join(
'@site', '@site',
path.relative(siteDir, versionMetadata.docsDirPath), posixPath(path.relative(siteDir, versionMetadata.docsDirPath)),
docFileSource, posixPath(docFileSource),
), ),
...expectedMetadata, ...expectedMetadata,
}); });

View file

@ -140,11 +140,15 @@ describe('empty/no docs website', () => {
pluginContentDocs( pluginContentDocs(
context, context,
normalizePluginOptions(OptionsSchema, { normalizePluginOptions(OptionsSchema, {
path: '/path/does/not/exist/', path: `path/doesnt/exist`,
}), }),
), ),
).toThrowErrorMatchingInlineSnapshot( ).toThrowError(
`"The docs folder does not exist for version [current]. A docs folder is expected to be found at /path/does/not/exist"`, `The docs folder does not exist for version [current]. A docs folder is expected to be found at ${
process.platform === 'win32'
? 'path\\doesnt\\exist'
: 'path/doesnt/exist'
}`,
); );
}); });
}); });
@ -248,9 +252,9 @@ describe('simple website', () => {
permalink: '/docs/foo/bazSlug.html', permalink: '/docs/foo/bazSlug.html',
}, },
sidebar: 'docs', sidebar: 'docs',
source: path.join( source: path.posix.join(
'@site', '@site',
path.relative(siteDir, currentVersion.docsDirPath), posixPath(path.relative(siteDir, currentVersion.docsDirPath)),
'hello.md', 'hello.md',
), ),
title: 'Hello, World !', title: 'Hello, World !',
@ -270,9 +274,9 @@ describe('simple website', () => {
permalink: '/docs/foo/bar', permalink: '/docs/foo/bar',
slug: '/foo/bar', slug: '/foo/bar',
sidebar: 'docs', sidebar: 'docs',
source: path.join( source: path.posix.join(
'@site', '@site',
path.relative(siteDir, currentVersion.docsDirPath), posixPath(path.relative(siteDir, currentVersion.docsDirPath)),
'foo', 'foo',
'bar.md', 'bar.md',
), ),
@ -418,9 +422,9 @@ describe('versioned website', () => {
isDocsHomePage: false, isDocsHomePage: false,
permalink: '/docs/next/foo/barSlug', permalink: '/docs/next/foo/barSlug',
slug: '/foo/barSlug', slug: '/foo/barSlug',
source: path.join( source: path.posix.join(
'@site', '@site',
path.relative(siteDir, currentVersion.docsDirPath), posixPath(path.relative(siteDir, currentVersion.docsDirPath)),
'foo', 'foo',
'bar.md', 'bar.md',
), ),
@ -440,9 +444,9 @@ describe('versioned website', () => {
isDocsHomePage: true, isDocsHomePage: true,
permalink: '/docs/next/', permalink: '/docs/next/',
slug: '/', slug: '/',
source: path.join( source: path.posix.join(
'@site', '@site',
path.relative(siteDir, currentVersion.docsDirPath), posixPath(path.relative(siteDir, currentVersion.docsDirPath)),
'hello.md', 'hello.md',
), ),
title: 'hello', title: 'hello',
@ -461,9 +465,9 @@ describe('versioned website', () => {
isDocsHomePage: true, isDocsHomePage: true,
permalink: '/docs/', permalink: '/docs/',
slug: '/', slug: '/',
source: path.join( source: path.posix.join(
'@site', '@site',
path.relative(siteDir, version101.docsDirPath), posixPath(path.relative(siteDir, version101.docsDirPath)),
'hello.md', 'hello.md',
), ),
title: 'hello', title: 'hello',
@ -482,9 +486,9 @@ describe('versioned website', () => {
isDocsHomePage: false, isDocsHomePage: false,
permalink: '/docs/1.0.0/foo/baz', permalink: '/docs/1.0.0/foo/baz',
slug: '/foo/baz', slug: '/foo/baz',
source: path.join( source: path.posix.join(
'@site', '@site',
path.relative(siteDir, version100.docsDirPath), posixPath(path.relative(siteDir, version100.docsDirPath)),
'foo', 'foo',
'baz.md', 'baz.md',
), ),
@ -629,13 +633,6 @@ describe('versioned website (community)', () => {
isDocsHomePage: false, isDocsHomePage: false,
permalink: '/community/next/team', permalink: '/community/next/team',
slug: '/team', slug: '/team',
/*
source: path.join(
'@site',
path.relative(siteDir, currentVersion.docsDirPath),
'team.md',
),
*/
source: source:
'@site/i18n/en/docusaurus-plugin-content-docs-community/current/team.md', '@site/i18n/en/docusaurus-plugin-content-docs-community/current/team.md',
title: 'Team title translated', title: 'Team title translated',
@ -650,9 +647,9 @@ describe('versioned website (community)', () => {
isDocsHomePage: false, isDocsHomePage: false,
permalink: '/community/team', permalink: '/community/team',
slug: '/team', slug: '/team',
source: path.join( source: path.posix.join(
'@site', '@site',
path.relative(siteDir, version100.docsDirPath), posixPath(path.relative(siteDir, version100.docsDirPath)),
'team.md', 'team.md',
), ),
title: 'team', title: 'team',

View file

@ -24,30 +24,30 @@ const DefaultI18N: I18n = {
}; };
describe('version paths', () => { describe('version paths', () => {
test('getVersionedDocsDirPath', () => { test('getVersionsFilePath', () => {
expect(getVersionsFilePath('someSiteDir', DEFAULT_PLUGIN_ID)).toBe( expect(getVersionsFilePath('someSiteDir', DEFAULT_PLUGIN_ID)).toBe(
'someSiteDir/versions.json', `someSiteDir${path.sep}versions.json`,
); );
expect(getVersionsFilePath('otherSite/dir', 'pluginId')).toBe( expect(getVersionsFilePath('otherSite/dir', 'pluginId')).toBe(
'otherSite/dir/pluginId_versions.json', `otherSite${path.sep}dir${path.sep}pluginId_versions.json`,
); );
}); });
test('getVersionedDocsDirPath', () => { test('getVersionedDocsDirPath', () => {
expect(getVersionedDocsDirPath('someSiteDir', DEFAULT_PLUGIN_ID)).toBe( expect(getVersionedDocsDirPath('someSiteDir', DEFAULT_PLUGIN_ID)).toBe(
'someSiteDir/versioned_docs', `someSiteDir${path.sep}versioned_docs`,
); );
expect(getVersionedDocsDirPath('otherSite/dir', 'pluginId')).toBe( expect(getVersionedDocsDirPath('otherSite/dir', 'pluginId')).toBe(
'otherSite/dir/pluginId_versioned_docs', `otherSite${path.sep}dir${path.sep}pluginId_versioned_docs`,
); );
}); });
test('getVersionedSidebarsDirPath', () => { test('getVersionedSidebarsDirPath', () => {
expect(getVersionedSidebarsDirPath('someSiteDir', DEFAULT_PLUGIN_ID)).toBe( expect(getVersionedSidebarsDirPath('someSiteDir', DEFAULT_PLUGIN_ID)).toBe(
'someSiteDir/versioned_sidebars', `someSiteDir${path.sep}versioned_sidebars`,
); );
expect(getVersionedSidebarsDirPath('otherSite/dir', 'pluginId')).toBe( expect(getVersionedSidebarsDirPath('otherSite/dir', 'pluginId')).toBe(
'otherSite/dir/pluginId_versioned_sidebars', `otherSite${path.sep}dir${path.sep}pluginId_versioned_sidebars`,
); );
}); });
}); });

View file

@ -16,6 +16,7 @@ import {
docuHash, docuHash,
aliasedSitePath, aliasedSitePath,
reportMessage, reportMessage,
posixPath,
} from '@docusaurus/utils'; } from '@docusaurus/utils';
import {LoadContext, Plugin, RouteConfig} from '@docusaurus/types'; import {LoadContext, Plugin, RouteConfig} from '@docusaurus/types';
@ -66,7 +67,7 @@ export default function pluginContentDocs(
); );
const dataDir = path.join(pluginDataDirRoot, pluginId); const dataDir = path.join(pluginDataDirRoot, pluginId);
const aliasedSource = (source: string) => const aliasedSource = (source: string) =>
`~docs/${path.relative(pluginDataDirRoot, source)}`; `~docs/${posixPath(path.relative(pluginDataDirRoot, source))}`;
return { return {
name: 'docusaurus-plugin-content-docs', name: 'docusaurus-plugin-content-docs',

View file

@ -5,7 +5,6 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
import path from 'path';
import {resolve} from 'url'; import {resolve} from 'url';
import { import {
DocsMarkdownOption, DocsMarkdownOption,
@ -13,6 +12,7 @@ import {
BrokenMarkdownLink, BrokenMarkdownLink,
} from '../types'; } from '../types';
import {getDocsDirPaths} from '../versions'; import {getDocsDirPaths} from '../versions';
import {aliasedSitePath} from '@docusaurus/utils';
function getVersion(filePath: string, options: DocsMarkdownOption) { function getVersion(filePath: string, options: DocsMarkdownOption) {
const versionFound = options.versionsMetadata.find((version) => const versionFound = options.versionsMetadata.find((version) =>
@ -58,7 +58,7 @@ function replaceMarkdownLinks(
const mdLink = mdMatch[1]; const mdLink = mdMatch[1];
const aliasedSource = (source: string) => const aliasedSource = (source: string) =>
`@site/${path.relative(siteDir, source)}`; aliasedSitePath(source, siteDir);
const permalink = const permalink =
sourceToPermalink[aliasedSource(resolve(filePath, mdLink))] || sourceToPermalink[aliasedSource(resolve(filePath, mdLink))] ||

View file

@ -316,13 +316,21 @@ function createVersionMetadata({
} }
function checkVersionMetadataPaths({ function checkVersionMetadataPaths({
versionName, versionMetadata,
docsDirPath, context,
sidebarFilePath, }: {
}: VersionMetadata) { versionMetadata: VersionMetadata;
context: Pick<LoadContext, 'siteDir'>;
}) {
const {versionName, docsDirPath, sidebarFilePath} = versionMetadata;
const {siteDir} = context;
if (!fs.existsSync(docsDirPath)) { if (!fs.existsSync(docsDirPath)) {
throw new Error( throw new Error(
`The docs folder does not exist for version [${versionName}]. A docs folder is expected to be found at ${docsDirPath}`, `The docs folder does not exist for version [${versionName}]. A docs folder is expected to be found at ${path.relative(
siteDir,
docsDirPath,
)}`,
); );
} }
@ -457,7 +465,9 @@ export function readVersionsMetadata({
options, options,
}), }),
); );
versionsMetadata.forEach(checkVersionMetadataPaths); versionsMetadata.forEach((versionMetadata) =>
checkVersionMetadataPaths({versionMetadata, context}),
);
return versionsMetadata; return versionsMetadata;
} }

View file

@ -28,37 +28,47 @@ describe('docusaurus-plugin-content-pages', () => {
{ {
type: 'jsx', type: 'jsx',
permalink: '/', permalink: '/',
source: path.join('@site', pluginPath, 'index.js'), source: path.posix.join('@site', pluginPath, 'index.js'),
}, },
{ {
type: 'jsx', type: 'jsx',
permalink: '/typescript', permalink: '/typescript',
source: path.join('@site', pluginPath, 'typescript.tsx'), source: path.posix.join('@site', pluginPath, 'typescript.tsx'),
}, },
{ {
type: 'mdx', type: 'mdx',
permalink: '/hello/', permalink: '/hello/',
source: path.join('@site', pluginPath, 'hello', 'index.md'), source: path.posix.join('@site', pluginPath, 'hello', 'index.md'),
}, },
{ {
type: 'mdx', type: 'mdx',
permalink: '/hello/mdxPage', permalink: '/hello/mdxPage',
source: path.join('@site', pluginPath, 'hello', 'mdxPage.mdx'), source: path.posix.join('@site', pluginPath, 'hello', 'mdxPage.mdx'),
}, },
{ {
type: 'jsx', type: 'jsx',
permalink: '/hello/translatedJs', permalink: '/hello/translatedJs',
source: path.join('@site', pluginPath, 'hello', 'translatedJs.js'), source: path.posix.join(
'@site',
pluginPath,
'hello',
'translatedJs.js',
),
}, },
{ {
type: 'mdx', type: 'mdx',
permalink: '/hello/translatedMd', permalink: '/hello/translatedMd',
source: path.join('@site', pluginPath, 'hello', 'translatedMd.md'), source: path.posix.join(
'@site',
pluginPath,
'hello',
'translatedMd.md',
),
}, },
{ {
type: 'jsx', type: 'jsx',
permalink: '/hello/world', permalink: '/hello/world',
source: path.join('@site', pluginPath, 'hello', 'world.js'), source: path.posix.join('@site', pluginPath, 'hello', 'world.js'),
}, },
]); ]);
}); });
@ -81,7 +91,7 @@ describe('docusaurus-plugin-content-pages', () => {
); );
const pagesMetadatas = (await plugin.loadContent?.())!; const pagesMetadatas = (await plugin.loadContent?.())!;
const frTranslationsPath = path.join( const frTranslationsPath = path.posix.join(
'@site', '@site',
'i18n', 'i18n',
'fr', 'fr',
@ -92,37 +102,37 @@ describe('docusaurus-plugin-content-pages', () => {
{ {
type: 'jsx', type: 'jsx',
permalink: '/', permalink: '/',
source: path.join('@site', pluginPath, 'index.js'), source: path.posix.join('@site', pluginPath, 'index.js'),
}, },
{ {
type: 'jsx', type: 'jsx',
permalink: '/typescript', permalink: '/typescript',
source: path.join('@site', pluginPath, 'typescript.tsx'), source: path.posix.join('@site', pluginPath, 'typescript.tsx'),
}, },
{ {
type: 'mdx', type: 'mdx',
permalink: '/hello/', permalink: '/hello/',
source: path.join('@site', pluginPath, 'hello', 'index.md'), source: path.posix.join('@site', pluginPath, 'hello', 'index.md'),
}, },
{ {
type: 'mdx', type: 'mdx',
permalink: '/hello/mdxPage', permalink: '/hello/mdxPage',
source: path.join('@site', pluginPath, 'hello', 'mdxPage.mdx'), source: path.posix.join('@site', pluginPath, 'hello', 'mdxPage.mdx'),
}, },
{ {
type: 'jsx', type: 'jsx',
permalink: '/hello/translatedJs', permalink: '/hello/translatedJs',
source: path.join(frTranslationsPath, 'hello', 'translatedJs.js'), source: path.posix.join(frTranslationsPath, 'hello', 'translatedJs.js'),
}, },
{ {
type: 'mdx', type: 'mdx',
permalink: '/hello/translatedMd', permalink: '/hello/translatedMd',
source: path.join(frTranslationsPath, 'hello', 'translatedMd.md'), source: path.posix.join(frTranslationsPath, 'hello', 'translatedMd.md'),
}, },
{ {
type: 'jsx', type: 'jsx',
permalink: '/hello/world', permalink: '/hello/world',
source: path.join('@site', pluginPath, 'hello', 'world.js'), source: path.posix.join('@site', pluginPath, 'hello', 'world.js'),
}, },
]); ]);
}); });

View file

@ -6,7 +6,7 @@
*/ */
import {LoadContext, Plugin} from '@docusaurus/types'; import {LoadContext, Plugin} from '@docusaurus/types';
import {docuHash, normalizeUrl} from '@docusaurus/utils'; import {docuHash, normalizeUrl, posixPath} from '@docusaurus/utils';
import path from 'path'; import path from 'path';
export default function pluginDebug({ export default function pluginDebug({
@ -18,7 +18,7 @@ export default function pluginDebug({
'docusaurus-plugin-debug', 'docusaurus-plugin-debug',
); );
const aliasedSource = (source: string) => const aliasedSource = (source: string) =>
`~debug/${path.relative(pluginDataDirRoot, source)}`; `~debug/${posixPath(path.relative(pluginDataDirRoot, source))}`;
return { return {
name: 'docusaurus-plugin-debug', name: 'docusaurus-plugin-debug',

View file

@ -34,15 +34,21 @@ module.exports = {
{ {
docs: { docs: {
// ... // ...
remarkPlugins: [[require('@docusaurus/remark-plugin-npm2yarn'), {sync: true}]], remarkPlugins: [
[require('@docusaurus/remark-plugin-npm2yarn'), {sync: true}],
],
}, },
blog: { blog: {
// ... // ...
remarkPlugins: [[require('@docusaurus/remark-plugin-npm2yarn'), {sync: true}]], remarkPlugins: [
[require('@docusaurus/remark-plugin-npm2yarn'), {sync: true}],
],
}, },
pages: { pages: {
// ... // ...
remarkPlugins: [[require('@docusaurus/remark-plugin-npm2yarn'), {sync: true}]], remarkPlugins: [
[require('@docusaurus/remark-plugin-npm2yarn'), {sync: true}],
],
}, },
// ... // ...
}, },
@ -54,6 +60,6 @@ module.exports = {
## Options ## Options
| Property | Type | Default | Description | | Property | Type | Default | Description |
|----------|-----------|---------|---------------------------------------------------------------------------------------------------------------------------| | --- | --- | --- | --- |
| `sync` | `boolean` | `false` | Syncing tab choices (yarn and npm). See https://v2.docusaurus.io/docs/markdown-features/#syncing-tab-choices for details. | | `sync` | `boolean` | `false` | Syncing tab choices (yarn and npm). See https://v2.docusaurus.io/docs/markdown-features/#syncing-tab-choices for details. |

View file

@ -45,7 +45,9 @@ describe('load utils', () => {
'user/docs/test.md': '@site/../docs/test.md', 'user/docs/test.md': '@site/../docs/test.md',
}; };
Object.keys(asserts).forEach((file) => { Object.keys(asserts).forEach((file) => {
expect(aliasedSitePath(file, 'user/website')).toBe(asserts[file]); expect(posixPath(aliasedSitePath(file, 'user/website'))).toBe(
asserts[file],
);
}); });
}); });
@ -521,15 +523,15 @@ describe('removePrefix', () => {
describe('getFilePathForRoutePath', () => { describe('getFilePathForRoutePath', () => {
test('works for /', () => { test('works for /', () => {
expect(getFilePathForRoutePath('/')).toEqual('/index.html'); expect(posixPath(getFilePathForRoutePath('/'))).toEqual('/index.html');
}); });
test('works for /somePath', () => { test('works for /somePath', () => {
expect(getFilePathForRoutePath('/somePath')).toEqual( expect(posixPath(getFilePathForRoutePath('/somePath'))).toEqual(
'/somePath/index.html', '/somePath/index.html',
); );
}); });
test('works for /somePath/', () => { test('works for /somePath/', () => {
expect(getFilePathForRoutePath('/somePath/')).toEqual( expect(posixPath(getFilePathForRoutePath('/somePath/'))).toEqual(
'/somePath/index.html', '/somePath/index.html',
); );
}); });

View file

@ -140,6 +140,17 @@ export function posixPath(str: string): string {
return str.replace(/\\/g, '/'); return str.replace(/\\/g, '/');
} }
// When you want to display a path in a message/warning/error,
// it's more convenient to:
// - make it relative to cwd()
// - convert to posix (ie not using windows \ path separator)
// This way, Jest tests can run more reliably on any computer/CI
// on both Unix/Windows
// For Windows users this is not perfect (as they see / instead of \) but it's probably good enough
export function toMessageRelativeFilePath(filePath: string) {
return posixPath(path.relative(process.cwd(), filePath));
}
const chunkNameCache = new Map(); const chunkNameCache = new Map();
/** /**
* Generate unique chunk name given a module path. * Generate unique chunk name given a module path.
@ -365,7 +376,7 @@ export function normalizeUrl(rawUrls: string[]): string {
* Example: some/path/to/website/docs/foo.md -> @site/docs/foo.md * Example: some/path/to/website/docs/foo.md -> @site/docs/foo.md
*/ */
export function aliasedSitePath(filePath: string, siteDir: string): string { export function aliasedSitePath(filePath: string, siteDir: string): string {
const relativePath = path.relative(siteDir, filePath); const relativePath = posixPath(path.relative(siteDir, filePath));
// Cannot use path.join() as it resolves '../' and removes // Cannot use path.join() as it resolves '../' and removes
// the '@site'. Let webpack loader resolve it. // the '@site'. Let webpack loader resolve it.
return `@site/${relativePath}`; return `@site/${relativePath}`;

View file

@ -147,7 +147,7 @@ describe('localizePath', () => {
}, },
options: {localizePath: true}, options: {localizePath: true},
}), }),
).toEqual(`/baseFsPath${path.sep}fr${path.sep}`); ).toEqual(`${path.sep}baseFsPath${path.sep}fr${path.sep}`);
}); });
test('should localize path for default locale, if requested', () => { test('should localize path for default locale, if requested', () => {

View file

@ -11,6 +11,7 @@ import path from 'path';
import {DocusaurusConfig} from '@docusaurus/types'; import {DocusaurusConfig} from '@docusaurus/types';
import {CONFIG_FILE_NAME} from '../constants'; import {CONFIG_FILE_NAME} from '../constants';
import {validateConfig} from './configValidation'; import {validateConfig} from './configValidation';
import {toMessageRelativeFilePath} from '@docusaurus/utils';
export default function loadConfig(siteDir: string): DocusaurusConfig { export default function loadConfig(siteDir: string): DocusaurusConfig {
// TODO temporary undocumented env variable: we should be able to use a cli option instead! // TODO temporary undocumented env variable: we should be able to use a cli option instead!
@ -21,8 +22,7 @@ export default function loadConfig(siteDir: string): DocusaurusConfig {
if (!fs.existsSync(configPath)) { if (!fs.existsSync(configPath)) {
throw new Error( throw new Error(
`${CONFIG_FILE_NAME} not found at ${path.relative( `${CONFIG_FILE_NAME} not found at ${toMessageRelativeFilePath(
process.cwd(),
configPath, configPath,
)}`, )}`,
); );

View file

@ -28,21 +28,19 @@ async function createTmpSiteDir() {
async function createTmpTranslationFile( async function createTmpTranslationFile(
content: TranslationFileContent | null, content: TranslationFileContent | null,
) { ) {
const file = await tmp.file({ const filePath = await tmp.tmpName({
prefix: 'jest-createTmpTranslationFile', prefix: 'jest-createTmpTranslationFile',
postfix: '.json', postfix: '.json',
}); });
// null means we don't want a file, but tmp.file() creates an empty file :( // null means we don't want a file, just a filename
if (content === null) { if (content !== null) {
await fs.unlink(file.path); await fs.writeFile(filePath, JSON.stringify(content, null, 2));
} else {
await fs.writeFile(file.path, JSON.stringify(content, null, 2));
} }
return { return {
filePath: file.path, filePath,
readFile: async () => JSON.parse(await fs.readFile(file.path, 'utf8')), readFile: async () => JSON.parse(await fs.readFile(filePath, 'utf8')),
}; };
} }

View file

@ -9,7 +9,7 @@ import fs from 'fs-extra';
import {InitPlugin} from '../plugins/init'; import {InitPlugin} from '../plugins/init';
import {mapValues, difference} from 'lodash'; import {mapValues, difference} from 'lodash';
import {TranslationFileContent, TranslationFile} from '@docusaurus/types'; import {TranslationFileContent, TranslationFile} from '@docusaurus/types';
import {getPluginI18nPath} from '@docusaurus/utils'; import {getPluginI18nPath, toMessageRelativeFilePath} from '@docusaurus/utils';
import * as Joi from 'joi'; import * as Joi from 'joi';
import chalk from 'chalk'; import chalk from 'chalk';
@ -128,8 +128,10 @@ Maybe you should remove them?
console.log( console.log(
`${Object.keys(mergedContent) `${Object.keys(mergedContent)
.length.toString() .length.toString()
.padStart(3, ' ')} translations written at ${path.relative( .padStart(
process.cwd(), 3,
' ',
)} translations will be written at ${toMessageRelativeFilePath(
filePath, filePath,
)}`, )}`,
); );

View file

@ -14,6 +14,7 @@ import {TranslationFileContent, TranslationMessage} from '@docusaurus/types';
import globby from 'globby'; import globby from 'globby';
import nodePath from 'path'; import nodePath from 'path';
import {InitPlugin} from '../plugins/init'; import {InitPlugin} from '../plugins/init';
import {posixPath} from '@docusaurus/utils';
// We only support extracting source code translations from these kind of files // We only support extracting source code translations from these kind of files
const TranslatableSourceCodeExtension = new Set([ const TranslatableSourceCodeExtension = new Set([
@ -40,7 +41,13 @@ async function getSourceCodeFilePaths(
plugins.map((plugin) => plugin.getPathsToWatch?.() ?? []), plugins.map((plugin) => plugin.getPathsToWatch?.() ?? []),
); );
const filePaths = await globby(allPathsToWatch); // Required for Windows support, as paths using \ should not be used by globby
// (also using the windows hard drive prefix like c: is not a good idea)
const allRelativePosixPathsToWatch = allPathsToWatch.map((path) =>
posixPath(nodePath.relative(process.cwd(), path)),
);
const filePaths = await globby(allRelativePosixPathsToWatch);
return filePaths.filter(isTranslatableSourceCodePath); return filePaths.filter(isTranslatableSourceCodePath);
} }

View file

@ -15,6 +15,7 @@ import {
} from '../base'; } from '../base';
import * as utils from '../utils'; import * as utils from '../utils';
import {mapValues} from 'lodash'; import {mapValues} from 'lodash';
import {posixPath} from '@docusaurus/utils';
describe('babel transpilation exclude logic', () => { describe('babel transpilation exclude logic', () => {
test('always transpile client dir files', () => { test('always transpile client dir files', () => {
@ -70,7 +71,7 @@ describe('getDocusaurusAliases()', () => {
// using relative paths makes tests work everywhere // using relative paths makes tests work everywhere
const relativeDocusaurusAliases = mapValues( const relativeDocusaurusAliases = mapValues(
getDocusaurusAliases(), getDocusaurusAliases(),
(aliasValue) => path.relative(__dirname, aliasValue), (aliasValue) => posixPath(path.relative(__dirname, aliasValue)),
); );
expect(relativeDocusaurusAliases).toMatchSnapshot(); expect(relativeDocusaurusAliases).toMatchSnapshot();
}); });

View file

@ -135,7 +135,7 @@ describe('extending generated webpack config', () => {
describe('getFileLoaderUtils()', () => { describe('getFileLoaderUtils()', () => {
test('plugin svgo/removeViewBox should be disabled', () => { test('plugin svgo/removeViewBox should be disabled', () => {
const use = getFileLoaderUtils().rules.svg().use; const {use} = getFileLoaderUtils().rules.svg();
expect(use).toContainEqual( expect(use).toContainEqual(
expect.objectContaining({ expect.objectContaining({
loader: '@svgr/webpack', loader: '@svgr/webpack',

View file

@ -5,6 +5,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
const path = require('path');
const rule = require('..'); const rule = require('..');
const {ruleName, messages} = rule; const {ruleName, messages} = rule;
@ -12,7 +13,7 @@ const {ruleName, messages} = rule;
testStylelintRule( testStylelintRule(
{ {
// Relative to repo root. // Relative to repo root.
plugins: ['./packages/stylelint-copyright'], plugins: [path.join(process.cwd(), 'packages', 'stylelint-copyright')],
rules: { rules: {
[ruleName]: true, [ruleName]: true,
}, },

View file

@ -51,9 +51,11 @@ First, modify your `docusaurus.config.js` and add the required params:
| `baseUrl` | Base URL for your project. For projects hosted on GitHub pages, it follows the format "/_projectName_/". For https://github.com/facebook/docusaurus, `baseUrl` is `/docusaurus/`. | | `baseUrl` | Base URL for your project. For projects hosted on GitHub pages, it follows the format "/_projectName_/". For https://github.com/facebook/docusaurus, `baseUrl` is `/docusaurus/`. |
:::info :::info
In case you want to use your custom domain for GitHub Pages, create a `CNAME` file in the `static` directory. Anything within the `static` directory will be copied to the root of the `build` directory for deployment. In case you want to use your custom domain for GitHub Pages, create a `CNAME` file in the `static` directory. Anything within the `static` directory will be copied to the root of the `build` directory for deployment.
You may refer to GitHub Pages' documentation [User, Organization, and Project Pages](https://help.github.com/en/articles/user-organization-and-project-pages) for more details. You may refer to GitHub Pages' documentation [User, Organization, and Project Pages](https://help.github.com/en/articles/user-organization-and-project-pages) for more details.
::: :::
Example: Example:
@ -94,20 +96,20 @@ Optional parameters, also set as environment variables:
GitHub enterprise installations should work in the same manner as github.com; you only need to set the organization's GitHub Enterprise host as an environment veriable: GitHub enterprise installations should work in the same manner as github.com; you only need to set the organization's GitHub Enterprise host as an environment veriable:
| Name | Description | | Name | Description |
| --- | --- | | ------------- | ----------------------------------------------- |
| `GITHUB_HOST` | The domain name of your GitHub enterprise site. | | `GITHUB_HOST` | The domain name of your GitHub enterprise site. |
### Deploy ### Deploy
Finally, to deploy your site to GitHub Pages, run: Finally, to deploy your site to GitHub Pages, run:
<Tabs <Tabs
defaultValue="bash" defaultValue="bash"
values={[ values={[
{ label: 'Bash', value: 'bash' }, { label: 'Bash', value: 'bash' },
{ label: 'Windows', value: 'windows' }, { label: 'Windows', value: 'windows' },
{ label: 'PowerShell', value: 'powershell' } { label: 'PowerShell', value: 'powershell' }
]}> ]}>
<TabItem value="bash"> <TabItem value="bash">

View file

@ -78,7 +78,9 @@ module.exports = {
``` ```
:::note :::note
Shorthand notation relies on the iteration order of JavaScript object keys for the category name. When using this notation, keep in mind that EcmaScript does not guarantee `Object.keys({a,b}) === ['a','b']`, yet this is generally true. Shorthand notation relies on the iteration order of JavaScript object keys for the category name. When using this notation, keep in mind that EcmaScript does not guarantee `Object.keys({a,b}) === ['a','b']`, yet this is generally true.
::: :::
## Using multiple sidebars ## Using multiple sidebars