mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-19 03:57:01 +02:00
fix(utils): properly escape Windows paths (#6190)
* fix(utils): properly escape Windows paths * Use in more places * Escape path in test * Fix snapshot * Better comment * Fix tests
This commit is contained in:
parent
6716548b87
commit
f02fefb5b7
8 changed files with 38 additions and 37 deletions
|
@ -11,7 +11,6 @@ import mdx from 'remark-mdx';
|
||||||
import vfile from 'to-vfile';
|
import vfile from 'to-vfile';
|
||||||
import plugin from '../index';
|
import plugin from '../index';
|
||||||
import headings from '../../headings/index';
|
import headings from '../../headings/index';
|
||||||
import {posixPath} from '@docusaurus/utils';
|
|
||||||
|
|
||||||
const processFixture = async (name, options) => {
|
const processFixture = async (name, options) => {
|
||||||
const filePath = path.join(__dirname, `__fixtures__/${name}.md`);
|
const filePath = path.join(__dirname, `__fixtures__/${name}.md`);
|
||||||
|
@ -24,7 +23,8 @@ const processFixture = async (name, options) => {
|
||||||
|
|
||||||
return result
|
return result
|
||||||
.toString()
|
.toString()
|
||||||
.replace(new RegExp(posixPath(process.cwd()), 'g'), '[CWD]');
|
.replace(/\\\\/g, '/')
|
||||||
|
.replace(new RegExp(process.cwd().replace(/\\/g, '/'), 'g'), '[CWD]');
|
||||||
};
|
};
|
||||||
|
|
||||||
const staticDirs = [
|
const staticDirs = [
|
||||||
|
|
|
@ -11,7 +11,6 @@ import mdx from 'remark-mdx';
|
||||||
import vfile from 'to-vfile';
|
import vfile from 'to-vfile';
|
||||||
import plugin from '..';
|
import plugin from '..';
|
||||||
import transformImage from '../../transformImage';
|
import transformImage from '../../transformImage';
|
||||||
import {posixPath} from '@docusaurus/utils';
|
|
||||||
|
|
||||||
const processFixture = async (name: string, options?) => {
|
const processFixture = async (name: string, options?) => {
|
||||||
const filePath = path.join(__dirname, `__fixtures__/${name}.md`);
|
const filePath = path.join(__dirname, `__fixtures__/${name}.md`);
|
||||||
|
@ -33,7 +32,8 @@ const processFixture = async (name: string, options?) => {
|
||||||
|
|
||||||
return result
|
return result
|
||||||
.toString()
|
.toString()
|
||||||
.replace(new RegExp(posixPath(process.cwd()), 'g'), '[CWD]');
|
.replace(/\\\\/g, '/')
|
||||||
|
.replace(new RegExp(process.cwd().replace(/\\/g, '/'), 'g'), '[CWD]');
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('transformAsset plugin', () => {
|
describe('transformAsset plugin', () => {
|
||||||
|
|
|
@ -11,9 +11,9 @@
|
||||||
* For example, this would fail due to unescaped \: `<img src={require('${filePath}')} />`
|
* For example, this would fail due to unescaped \: `<img src={require('${filePath}')} />`
|
||||||
* But this would work: `<img src={require('${escapePath(filePath)}')} />`
|
* But this would work: `<img src={require('${escapePath(filePath)}')} />`
|
||||||
*
|
*
|
||||||
* Workaround for issue in posixPath, maybe we won't need it anymore soon?
|
* posixPath can't be used in all cases, because forward slashes are only valid
|
||||||
* https://github.com/facebook/docusaurus/issues/4730#issuecomment-833530370
|
* Windows paths when they don't contain non-ascii characters, and posixPath
|
||||||
* https://github.com/sindresorhus/slash/pull/16#issuecomment-833528479
|
* doesn't escape those that fail to be converted.
|
||||||
*/
|
*/
|
||||||
export function escapePath(str: string): string {
|
export function escapePath(str: string): string {
|
||||||
const escaped = JSON.stringify(str);
|
const escaped = JSON.stringify(str);
|
||||||
|
|
|
@ -7,18 +7,20 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert Windows backslash paths to posix style paths.
|
* Convert Windows backslash paths to posix style paths.
|
||||||
* E.g: endi\\lie -> endi/lie
|
* E.g: endi\lie -> endi/lie
|
||||||
*
|
*
|
||||||
* Looks like this code was originally copied from https://github.com/sindresorhus/slash/blob/main/index.js
|
* Returns original path if the posix counterpart is not valid Windows path.
|
||||||
|
* This makes the legacy code that uses posixPath safe; but also makes it less
|
||||||
|
* useful when you actually want a path with forward slashes (e.g. for URL)
|
||||||
*
|
*
|
||||||
|
* Adopted from https://github.com/sindresorhus/slash/blob/main/index.js
|
||||||
*/
|
*/
|
||||||
export function posixPath(str: string): string {
|
export function posixPath(str: string): string {
|
||||||
const isExtendedLengthPath = /^\\\\\?\\/.test(str);
|
const isExtendedLengthPath = /^\\\\\?\\/.test(str);
|
||||||
|
|
||||||
// TODO not sure why we need this
|
// Forward slashes are only valid Windows paths when they don't contain non-ascii characters.
|
||||||
// See https://github.com/sindresorhus/slash/pull/16#issuecomment-833528479
|
// eslint-disable-next-line no-control-regex
|
||||||
// See https://github.com/facebook/docusaurus/issues/4730#issuecomment-833530370
|
const hasNonAscii = /[^\u0000-\u0080]+/.test(str);
|
||||||
const hasNonAscii = /[^\u0000-\u0080]+/.test(str); // eslint-disable-line
|
|
||||||
|
|
||||||
if (isExtendedLengthPath || hasNonAscii) {
|
if (isExtendedLengthPath || hasNonAscii) {
|
||||||
return str;
|
return str;
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
import type {RuleSetRule} from 'webpack';
|
import type {RuleSetRule} from 'webpack';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import {posixPath} from './posixPath';
|
import {escapePath} from './escapePath';
|
||||||
import {
|
import {
|
||||||
WEBPACK_URL_LOADER_LIMIT,
|
WEBPACK_URL_LOADER_LIMIT,
|
||||||
OUTPUT_STATIC_ASSETS_DIR_NAME,
|
OUTPUT_STATIC_ASSETS_DIR_NAME,
|
||||||
|
@ -61,12 +61,12 @@ export function getFileLoaderUtils(): FileLoaderUtils {
|
||||||
// Maybe with the ideal image plugin, all md images should be "ideal"?
|
// Maybe with the ideal image plugin, all md images should be "ideal"?
|
||||||
// This is used to force url-loader+file-loader on markdown images
|
// This is used to force url-loader+file-loader on markdown images
|
||||||
// https://webpack.js.org/concepts/loaders/#inline
|
// https://webpack.js.org/concepts/loaders/#inline
|
||||||
inlineMarkdownImageFileLoader: `!${posixPath(
|
inlineMarkdownImageFileLoader: `!${escapePath(
|
||||||
require.resolve('url-loader'),
|
require.resolve('url-loader'),
|
||||||
)}?limit=${urlLoaderLimit}&name=${fileLoaderFileName(
|
)}?limit=${urlLoaderLimit}&name=${fileLoaderFileName(
|
||||||
'images',
|
'images',
|
||||||
)}&fallback=${posixPath(require.resolve('file-loader'))}!`,
|
)}&fallback=${escapePath(require.resolve('file-loader'))}!`,
|
||||||
inlineMarkdownLinkFileLoader: `!${posixPath(
|
inlineMarkdownLinkFileLoader: `!${escapePath(
|
||||||
require.resolve('file-loader'),
|
require.resolve('file-loader'),
|
||||||
)}?name=${fileLoaderFileName('files')}!`,
|
)}?name=${fileLoaderFileName('files')}!`,
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,19 +4,19 @@ exports[`loadRoutes flat route config 1`] = `
|
||||||
Object {
|
Object {
|
||||||
"registry": Object {
|
"registry": Object {
|
||||||
"component---theme-blog-list-pagea-6-a-7ba": Object {
|
"component---theme-blog-list-pagea-6-a-7ba": Object {
|
||||||
"loader": "() => import(/* webpackChunkName: 'component---theme-blog-list-pagea-6-a-7ba' */ \\"@theme/BlogListPage\\")",
|
"loader": "() => import(/* webpackChunkName: 'component---theme-blog-list-pagea-6-a-7ba' */ '@theme/BlogListPage')",
|
||||||
"modulePath": "@theme/BlogListPage",
|
"modulePath": "@theme/BlogListPage",
|
||||||
},
|
},
|
||||||
"content---blog-0-b-4-09e": Object {
|
"content---blog-0-b-4-09e": Object {
|
||||||
"loader": "() => import(/* webpackChunkName: 'content---blog-0-b-4-09e' */ \\"blog/2018-12-14-Happy-First-Birthday-Slash.md?truncated=true\\")",
|
"loader": "() => import(/* webpackChunkName: 'content---blog-0-b-4-09e' */ 'blog/2018-12-14-Happy-First-Birthday-Slash.md?truncated=true')",
|
||||||
"modulePath": "blog/2018-12-14-Happy-First-Birthday-Slash.md?truncated=true",
|
"modulePath": "blog/2018-12-14-Happy-First-Birthday-Slash.md?truncated=true",
|
||||||
},
|
},
|
||||||
"content---blog-7-b-8-fd9": Object {
|
"content---blog-7-b-8-fd9": Object {
|
||||||
"loader": "() => import(/* webpackChunkName: 'content---blog-7-b-8-fd9' */ \\"blog/2018-12-14-Happy-First-Birthday-Slash.md\\")",
|
"loader": "() => import(/* webpackChunkName: 'content---blog-7-b-8-fd9' */ 'blog/2018-12-14-Happy-First-Birthday-Slash.md')",
|
||||||
"modulePath": "blog/2018-12-14-Happy-First-Birthday-Slash.md",
|
"modulePath": "blog/2018-12-14-Happy-First-Birthday-Slash.md",
|
||||||
},
|
},
|
||||||
"metadata---blog-0-b-6-74c": Object {
|
"metadata---blog-0-b-6-74c": Object {
|
||||||
"loader": "() => import(/* webpackChunkName: 'metadata---blog-0-b-6-74c' */ \\"blog-2018-12-14-happy-first-birthday-slash-d2c.json\\")",
|
"loader": "() => import(/* webpackChunkName: 'metadata---blog-0-b-6-74c' */ 'blog-2018-12-14-happy-first-birthday-slash-d2c.json')",
|
||||||
"modulePath": "blog-2018-12-14-happy-first-birthday-slash-d2c.json",
|
"modulePath": "blog-2018-12-14-happy-first-birthday-slash-d2c.json",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -62,31 +62,31 @@ exports[`loadRoutes nested route config 1`] = `
|
||||||
Object {
|
Object {
|
||||||
"registry": Object {
|
"registry": Object {
|
||||||
"component---theme-doc-item-178-a40": Object {
|
"component---theme-doc-item-178-a40": Object {
|
||||||
"loader": "() => import(/* webpackChunkName: 'component---theme-doc-item-178-a40' */ \\"@theme/DocItem\\")",
|
"loader": "() => import(/* webpackChunkName: 'component---theme-doc-item-178-a40' */ '@theme/DocItem')",
|
||||||
"modulePath": "@theme/DocItem",
|
"modulePath": "@theme/DocItem",
|
||||||
},
|
},
|
||||||
"component---theme-doc-page-1-be-9be": Object {
|
"component---theme-doc-page-1-be-9be": Object {
|
||||||
"loader": "() => import(/* webpackChunkName: 'component---theme-doc-page-1-be-9be' */ \\"@theme/DocPage\\")",
|
"loader": "() => import(/* webpackChunkName: 'component---theme-doc-page-1-be-9be' */ '@theme/DocPage')",
|
||||||
"modulePath": "@theme/DocPage",
|
"modulePath": "@theme/DocPage",
|
||||||
},
|
},
|
||||||
"content---docs-foo-baz-8-ce-61e": Object {
|
"content---docs-foo-baz-8-ce-61e": Object {
|
||||||
"loader": "() => import(/* webpackChunkName: 'content---docs-foo-baz-8-ce-61e' */ \\"docs/foo/baz.md\\")",
|
"loader": "() => import(/* webpackChunkName: 'content---docs-foo-baz-8-ce-61e' */ 'docs/foo/baz.md')",
|
||||||
"modulePath": "docs/foo/baz.md",
|
"modulePath": "docs/foo/baz.md",
|
||||||
},
|
},
|
||||||
"content---docs-helloaff-811": Object {
|
"content---docs-helloaff-811": Object {
|
||||||
"loader": "() => import(/* webpackChunkName: 'content---docs-helloaff-811' */ \\"docs/hello.md\\")",
|
"loader": "() => import(/* webpackChunkName: 'content---docs-helloaff-811' */ 'docs/hello.md')",
|
||||||
"modulePath": "docs/hello.md",
|
"modulePath": "docs/hello.md",
|
||||||
},
|
},
|
||||||
"docsMetadata---docs-routef-34-881": Object {
|
"docsMetadata---docs-routef-34-881": Object {
|
||||||
"loader": "() => import(/* webpackChunkName: 'docsMetadata---docs-routef-34-881' */ \\"docs-b5f.json\\")",
|
"loader": "() => import(/* webpackChunkName: 'docsMetadata---docs-routef-34-881' */ 'docs-b5f.json')",
|
||||||
"modulePath": "docs-b5f.json",
|
"modulePath": "docs-b5f.json",
|
||||||
},
|
},
|
||||||
"metadata---docs-foo-baz-2-cf-fa7": Object {
|
"metadata---docs-foo-baz-2-cf-fa7": Object {
|
||||||
"loader": "() => import(/* webpackChunkName: 'metadata---docs-foo-baz-2-cf-fa7' */ \\"docs-foo-baz-dd9.json\\")",
|
"loader": "() => import(/* webpackChunkName: 'metadata---docs-foo-baz-2-cf-fa7' */ 'docs-foo-baz-dd9.json')",
|
||||||
"modulePath": "docs-foo-baz-dd9.json",
|
"modulePath": "docs-foo-baz-dd9.json",
|
||||||
},
|
},
|
||||||
"metadata---docs-hello-956-741": Object {
|
"metadata---docs-hello-956-741": Object {
|
||||||
"loader": "() => import(/* webpackChunkName: 'metadata---docs-hello-956-741' */ \\"docs-hello-da2.json\\")",
|
"loader": "() => import(/* webpackChunkName: 'metadata---docs-hello-956-741' */ 'docs-hello-da2.json')",
|
||||||
"modulePath": "docs-hello-da2.json",
|
"modulePath": "docs-hello-da2.json",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -146,7 +146,7 @@ exports[`loadRoutes route config with empty (but valid) path string 1`] = `
|
||||||
Object {
|
Object {
|
||||||
"registry": Object {
|
"registry": Object {
|
||||||
"component---hello-world-jse-0-f-b6c": Object {
|
"component---hello-world-jse-0-f-b6c": Object {
|
||||||
"loader": "() => import(/* webpackChunkName: 'component---hello-world-jse-0-f-b6c' */ \\"hello/world.js\\")",
|
"loader": "() => import(/* webpackChunkName: 'component---hello-world-jse-0-f-b6c' */ 'hello/world.js')",
|
||||||
"modulePath": "hello/world.js",
|
"modulePath": "hello/world.js",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
import {
|
import {
|
||||||
generate,
|
generate,
|
||||||
|
escapePath,
|
||||||
DEFAULT_BUILD_DIR_NAME,
|
DEFAULT_BUILD_DIR_NAME,
|
||||||
DEFAULT_CONFIG_FILE_NAME,
|
DEFAULT_CONFIG_FILE_NAME,
|
||||||
GENERATED_FILES_DIR_NAME,
|
GENERATED_FILES_DIR_NAME,
|
||||||
|
@ -323,8 +324,7 @@ export async function load(
|
||||||
`export default [\n${clientModules
|
`export default [\n${clientModules
|
||||||
// import() is async so we use require() because client modules can have
|
// import() is async so we use require() because client modules can have
|
||||||
// CSS and the order matters for loading CSS.
|
// CSS and the order matters for loading CSS.
|
||||||
// We need to JSON.stringify so that if its on windows, backslash are escaped.
|
.map((module) => ` require('${escapePath(module)}'),`)
|
||||||
.map((module) => ` require(${JSON.stringify(module)}),`)
|
|
||||||
.join('\n')}\n];\n`,
|
.join('\n')}\n];\n`,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -343,10 +343,9 @@ ${Object.keys(registry)
|
||||||
.sort()
|
.sort()
|
||||||
.map(
|
.map(
|
||||||
(key) =>
|
(key) =>
|
||||||
// We need to JSON.stringify so that if its on windows, backslash are escaped.
|
` '${key}': [${registry[key].loader}, '${escapePath(
|
||||||
` '${key}': [${registry[key].loader}, ${JSON.stringify(
|
|
||||||
registry[key].modulePath,
|
registry[key].modulePath,
|
||||||
)}, require.resolveWeak(${JSON.stringify(registry[key].modulePath)})],`,
|
)}', require.resolveWeak('${escapePath(registry[key].modulePath)}')],`,
|
||||||
)
|
)
|
||||||
.join('\n')}};\n`,
|
.join('\n')}};\n`,
|
||||||
);
|
);
|
||||||
|
|
|
@ -10,6 +10,7 @@ import {
|
||||||
normalizeUrl,
|
normalizeUrl,
|
||||||
removeSuffix,
|
removeSuffix,
|
||||||
simpleHash,
|
simpleHash,
|
||||||
|
escapePath,
|
||||||
} from '@docusaurus/utils';
|
} from '@docusaurus/utils';
|
||||||
import {has, isPlainObject, isString} from 'lodash';
|
import {has, isPlainObject, isString} from 'lodash';
|
||||||
import {stringify} from 'querystring';
|
import {stringify} from 'querystring';
|
||||||
|
@ -232,10 +233,9 @@ function genRouteChunkNames(
|
||||||
if (isModule(value)) {
|
if (isModule(value)) {
|
||||||
const modulePath = getModulePath(value);
|
const modulePath = getModulePath(value);
|
||||||
const chunkName = genChunkName(modulePath, prefix, name);
|
const chunkName = genChunkName(modulePath, prefix, name);
|
||||||
// We need to JSON.stringify so that if its on windows, backslashes are escaped.
|
const loader = `() => import(/* webpackChunkName: '${chunkName}' */ '${escapePath(
|
||||||
const loader = `() => import(/* webpackChunkName: '${chunkName}' */ ${JSON.stringify(
|
|
||||||
modulePath,
|
modulePath,
|
||||||
)})`;
|
)}')`;
|
||||||
|
|
||||||
registry[chunkName] = {
|
registry[chunkName] = {
|
||||||
loader,
|
loader,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue