Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/v2-tests-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ jobs:
matrix:
node: ['12', '14']
steps:
- name: Support longpaths
run: git config --system core.longpaths true
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node }}
uses: actions/setup-node@v2
Expand Down
30 changes: 30 additions & 0 deletions packages/docusaurus-utils/src/__tests__/docuHash.test.ts
Original file line number Diff line number Diff line change
@@ -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.
*/

import {docuHash} from '../docuHash';

describe('docuHash', () => {
test('docuHash works', () => {
const asserts: Record<string, string> = {
'': '-d41',
'/': 'index',
'/foo-bar': 'foo-bar-096',
'/foo/bar': 'foo-bar-1df',
'/endi/lie': 'endi-lie-9fa',
'/endi-lie': 'endi-lie-fd3',
'/yangshun/tay': 'yangshun-tay-48d',
'/yangshun-tay': 'yangshun-tay-f3b',
'/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar':
'foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo--d46',
'/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/test1-test2':
'foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-test-1-test--787',
};
Object.keys(asserts).forEach((file) => {
expect(docuHash(file)).toBe(asserts[file]);
});
});
});
33 changes: 0 additions & 33 deletions packages/docusaurus-utils/src/__tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
import path from 'path';
import {
fileToPath,
simpleHash,
docuHash,
genComponentName,
genChunkName,
idx,
Expand Down Expand Up @@ -68,37 +66,6 @@ describe('load utils', () => {
});
});

test('simpleHash', () => {
const asserts: Record<string, string> = {
'': 'd41',
'/foo-bar': '096',
'/foo/bar': '1df',
'/endi/lie': '9fa',
'/endi-lie': 'fd3',
'/yangshun/tay': '48d',
'/yangshun-tay': 'f3b',
};
Object.keys(asserts).forEach((file) => {
expect(simpleHash(file, 3)).toBe(asserts[file]);
});
});

test('docuHash', () => {
const asserts: Record<string, string> = {
'': '-d41',
'/': 'index',
'/foo-bar': 'foo-bar-096',
'/foo/bar': 'foo-bar-1df',
'/endi/lie': 'endi-lie-9fa',
'/endi-lie': 'endi-lie-fd3',
'/yangshun/tay': 'yangshun-tay-48d',
'/yangshun-tay': 'yangshun-tay-f3b',
};
Object.keys(asserts).forEach((file) => {
expect(docuHash(file)).toBe(asserts[file]);
});
});

test('fileToPath', () => {
const asserts: Record<string, string> = {
'index.md': '/',
Expand Down
82 changes: 82 additions & 0 deletions packages/docusaurus-utils/src/__tests__/pathUtils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/**
* 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 {simpleHash, isNameTooLong, shortName} from '../pathUtils';

describe('pathUtils', () => {
test('simpleHash', () => {
const asserts: Record<string, string> = {
'': 'd41',
'/foo-bar': '096',
'/foo/bar': '1df',
'/endi/lie': '9fa',
'/endi-lie': 'fd3',
'/yangshun/tay': '48d',
'/yangshun-tay': 'f3b',
'/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar':
'd46',
'/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/test1-test2':
'787',
};
Object.keys(asserts).forEach((file) => {
expect(simpleHash(file, 3)).toBe(asserts[file]);
});
});

test('isNameTooLong', () => {
const asserts: Record<string, boolean> = {
'': false,
'foo-bar-096': false,
'foo-bar-1df': false,
'endi-lie-9fa': false,
'endi-lie-fd3': false,
'yangshun-tay-48d': false,
'yangshun-tay-f3b': false,
'foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-d46': true,
'foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-test-1-test-2-787': true,
};
Object.keys(asserts).forEach((path) => {
expect(isNameTooLong(path)).toBe(asserts[path]);
});
});

describe('shortName', () => {
test('works', () => {
const asserts: Record<string, string> = {
'': '',
'foo-bar': 'foo-bar',
'endi-lie': 'endi-lie',
'yangshun-tay': 'yangshun-tay',
'foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar':
'foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-',
'foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-test-1-test-2':
'foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-test-1-test-',
};
Object.keys(asserts).forEach((file) => {
expect(shortName(file)).toBe(asserts[file]);
});
});

// Based on https://github.com/gatsbyjs/gatsby/pull/21518/files

const SHORT_PATH = `/short/path/without/trailing/slash`;
const VERY_LONG_PATH = `/${`x`.repeat(256)}/`;
const VERY_LONG_PATH_NON_LATIN = `/${`あ`.repeat(255)}/`;

it(`Truncates long paths correctly`, () => {
const truncatedPathLatin = shortName(VERY_LONG_PATH);
const truncatedPathNonLatin = shortName(VERY_LONG_PATH_NON_LATIN);
expect(truncatedPathLatin.length).toBeLessThanOrEqual(255);
expect(truncatedPathNonLatin.length).toBeLessThanOrEqual(255);
});

it(`Does not truncate short paths`, () => {
const truncatedPath = shortName(SHORT_PATH);
expect(truncatedPath).toEqual(SHORT_PATH);
});
});
});
28 changes: 28 additions & 0 deletions packages/docusaurus-utils/src/docuHash.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* 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 {kebabCase} from 'lodash';

import {shortName, isNameTooLong, simpleHash} from './pathUtils';

/**
* Given an input string, convert to kebab-case and append a hash.
* Avoid str collision.
* Also removes part of the string if its larger than the allowed
* filename per OS. Avoids ERRNAMETOOLONG error.
*/
export function docuHash(str: string): string {
if (str === '/') {
return 'index';
}
const shortHash = simpleHash(str, 3);
const parsedPath = `${kebabCase(str)}-${shortHash}`;
if (isNameTooLong(parsedPath)) {
return `${shortName(kebabCase(str))}-${shortHash}`;
}
return parsedPath;
}
22 changes: 5 additions & 17 deletions packages/docusaurus-utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import chalk from 'chalk';
import path from 'path';
import {createHash} from 'crypto';
import {camelCase, kebabCase, mapValues} from 'lodash';
import {camelCase, mapValues} from 'lodash';
import escapeStringRegexp from 'escape-string-regexp';
import fs from 'fs-extra';
import {URL} from 'url';
Expand All @@ -22,13 +22,17 @@ import {
import resolvePathnameUnsafe from 'resolve-pathname';

import {posixPath as posixPathImport} from './posixPath';
import {simpleHash} from './pathUtils';
import {docuHash} from './docuHash';

export const posixPath = posixPathImport;

export * from './codeTranslationsUtils';
export * from './markdownParser';
export * from './markdownLinks';
export * from './escapePath';
export * from './docuHash';
export {simpleHash} from './pathUtils';

const fileHash = new Map();
export async function generate(
Expand Down Expand Up @@ -98,22 +102,6 @@ export function encodePath(userpath: string): string {
.join('/');
}

export function simpleHash(str: string, length: number): string {
return createHash('md5').update(str).digest('hex').substr(0, length);
}

/**
* Given an input string, convert to kebab-case and append a hash.
* Avoid str collision.
*/
export function docuHash(str: string): string {
if (str === '/') {
return 'index';
}
const shortHash = simpleHash(str, 3);
return `${kebabCase(str)}-${shortHash}`;
}

/**
* Convert first string character to the upper case.
* E.g: docusaurus -> Docusaurus
Expand Down
48 changes: 48 additions & 0 deletions packages/docusaurus-utils/src/pathUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* 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.
*/

// Based on https://github.com/gatsbyjs/gatsby/pull/21518/files

import {createHash} from 'crypto';

// MacOS (APFS) and Windows (NTFS) filename length limit = 255 chars, Others = 255 bytes
const MAX_PATH_SEGMENT_CHARS = 255;
const MAX_PATH_SEGMENT_BYTES = 255;
// Space for appending things to the string like file extensions and so on
const SPACE_FOR_APPENDING = 10;

const isMacOs = process.platform === `darwin`;
const isWindows = process.platform === `win32`;

export const isNameTooLong = (str: string): boolean => {
return isMacOs || isWindows
? str.length + SPACE_FOR_APPENDING > MAX_PATH_SEGMENT_CHARS // MacOS (APFS) and Windows (NTFS) filename length limit (255 chars)
: Buffer.from(str).length + SPACE_FOR_APPENDING > MAX_PATH_SEGMENT_BYTES; // Other (255 bytes)
};

export const shortName = (str: string): string => {
if (isMacOs || isWindows) {
const overflowingChars = str.length - MAX_PATH_SEGMENT_CHARS;
return str.slice(
0,
str.length - overflowingChars - SPACE_FOR_APPENDING - 1,
);
}
const strBuffer = Buffer.from(str);
const overflowingBytes =
Buffer.byteLength(strBuffer) - MAX_PATH_SEGMENT_BYTES;
return strBuffer
.slice(
0,
Buffer.byteLength(strBuffer) - overflowingBytes - SPACE_FOR_APPENDING - 1,
)
.toString();
};

export function simpleHash(str: string, length: number): string {
return createHash('md5').update(str).digest('hex').substr(0, length);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
title: Markdown page example
---

# Markdown page example

You don't need React to write simple standalone pages.