mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-11 16:17:25 +02:00
fix(v2): do not reveal environment file path (#1692)
* fix(v2): do not reveal environment file path * changelog * fix broken markdown linking
This commit is contained in:
parent
84b2270039
commit
a684806a0f
9 changed files with 48 additions and 41 deletions
|
@ -2,7 +2,8 @@
|
||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
- Shorter chunk naming for pages. Instead of absolute path, we use relative path from site directory
|
- Fix broken markdown linking replacement for mdx files
|
||||||
|
- Fix potential security vulnerability because we're exposing the directory structure of the host machine. Instead of absolute path, we use relative path from site directory. Resulting in shorter webpack chunk naming and smaller bundle size.
|
||||||
- Use contenthash instead of chunkhash for better long term caching
|
- Use contenthash instead of chunkhash for better long term caching
|
||||||
- Allow user to customize generated heading from MDX. Swizzle `@theme/Heading`
|
- Allow user to customize generated heading from MDX. Swizzle `@theme/Heading`
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ module.exports = function(context, opts) {
|
||||||
// Fetches blog contents and returns metadata for the necessary routes.
|
// Fetches blog contents and returns metadata for the necessary routes.
|
||||||
async loadContent() {
|
async loadContent() {
|
||||||
const {postsPerPage, include, routeBasePath} = options;
|
const {postsPerPage, include, routeBasePath} = options;
|
||||||
const {siteConfig} = context;
|
const {siteConfig, siteDir} = context;
|
||||||
const blogDir = contentPath;
|
const blogDir = contentPath;
|
||||||
|
|
||||||
if (!fs.existsSync(blogDir)) {
|
if (!fs.existsSync(blogDir)) {
|
||||||
|
@ -65,8 +65,9 @@ module.exports = function(context, opts) {
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
blogFiles.map(async relativeSource => {
|
blogFiles.map(async relativeSource => {
|
||||||
|
// Cannot use path.join() as it resolves '../' and removes the '@site'. Let webpack loader resolve it.
|
||||||
const source = path.join(blogDir, relativeSource);
|
const source = path.join(blogDir, relativeSource);
|
||||||
|
const aliasedSource = `@site/${path.relative(siteDir, source)}`;
|
||||||
const blogFileName = path.basename(relativeSource);
|
const blogFileName = path.basename(relativeSource);
|
||||||
// Extract, YYYY, MM, DD from the file name.
|
// Extract, YYYY, MM, DD from the file name.
|
||||||
const filePathDateArr = blogFileName.split('-');
|
const filePathDateArr = blogFileName.split('-');
|
||||||
|
@ -87,7 +88,7 @@ module.exports = function(context, opts) {
|
||||||
routeBasePath,
|
routeBasePath,
|
||||||
frontMatter.id || fileToUrl(blogFileName),
|
frontMatter.id || fileToUrl(blogFileName),
|
||||||
]),
|
]),
|
||||||
source,
|
source: aliasedSource,
|
||||||
description: frontMatter.description || excerpt,
|
description: frontMatter.description || excerpt,
|
||||||
date,
|
date,
|
||||||
tags: frontMatter.tags,
|
tags: frontMatter.tags,
|
||||||
|
|
|
@ -17,6 +17,7 @@ describe('loadDocs', () => {
|
||||||
url: 'https://docusaurus.io',
|
url: 'https://docusaurus.io',
|
||||||
};
|
};
|
||||||
const sidebarPath = path.join(siteDir, 'sidebars.json');
|
const sidebarPath = path.join(siteDir, 'sidebars.json');
|
||||||
|
const pluginPath = 'docs';
|
||||||
const plugin = pluginContentDocs(
|
const plugin = pluginContentDocs(
|
||||||
{
|
{
|
||||||
siteDir,
|
siteDir,
|
||||||
|
@ -28,7 +29,6 @@ describe('loadDocs', () => {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const {docs: docsMetadata} = await plugin.loadContent();
|
const {docs: docsMetadata} = await plugin.loadContent();
|
||||||
const docsDir = plugin.contentPath;
|
|
||||||
|
|
||||||
expect(docsMetadata.hello).toEqual({
|
expect(docsMetadata.hello).toEqual({
|
||||||
category: 'Guides',
|
category: 'Guides',
|
||||||
|
@ -37,7 +37,7 @@ describe('loadDocs', () => {
|
||||||
previous: 'foo/baz',
|
previous: 'foo/baz',
|
||||||
previous_title: 'baz',
|
previous_title: 'baz',
|
||||||
sidebar: 'docs',
|
sidebar: 'docs',
|
||||||
source: path.join(docsDir, 'hello.md'),
|
source: path.join('@site', pluginPath, 'hello.md'),
|
||||||
title: 'Hello, World !',
|
title: 'Hello, World !',
|
||||||
description: `Hi, Endilie here :)`,
|
description: `Hi, Endilie here :)`,
|
||||||
});
|
});
|
||||||
|
@ -49,7 +49,7 @@ describe('loadDocs', () => {
|
||||||
next_title: 'baz',
|
next_title: 'baz',
|
||||||
permalink: '/docs/foo/bar',
|
permalink: '/docs/foo/bar',
|
||||||
sidebar: 'docs',
|
sidebar: 'docs',
|
||||||
source: path.join(docsDir, 'foo', 'bar.md'),
|
source: path.join('@site', pluginPath, 'foo', 'bar.md'),
|
||||||
title: 'Bar',
|
title: 'Bar',
|
||||||
description: 'This is custom description',
|
description: 'This is custom description',
|
||||||
});
|
});
|
||||||
|
|
|
@ -15,36 +15,28 @@ describe('processMetadata', () => {
|
||||||
baseUrl: '/',
|
baseUrl: '/',
|
||||||
url: 'https://docusaurus.io',
|
url: 'https://docusaurus.io',
|
||||||
};
|
};
|
||||||
const docsDir = path.resolve(siteDir, 'docs');
|
const pluginPath = 'docs';
|
||||||
|
const docsDir = path.resolve(siteDir, pluginPath);
|
||||||
|
|
||||||
test('normal docs', async () => {
|
test('normal docs', async () => {
|
||||||
const sourceA = path.join('foo', 'bar.md');
|
const sourceA = path.join('foo', 'bar.md');
|
||||||
const sourceB = path.join('hello.md');
|
const sourceB = path.join('hello.md');
|
||||||
const dataA = await processMetadata(
|
|
||||||
sourceA,
|
const [dataA, dataB] = await Promise.all([
|
||||||
docsDir,
|
processMetadata(sourceA, docsDir, {}, siteConfig, pluginPath, siteDir),
|
||||||
{},
|
processMetadata(sourceB, docsDir, {}, siteConfig, pluginPath, siteDir),
|
||||||
siteConfig,
|
]);
|
||||||
'docs',
|
|
||||||
);
|
|
||||||
const dataB = await processMetadata(
|
|
||||||
sourceB,
|
|
||||||
docsDir,
|
|
||||||
{},
|
|
||||||
siteConfig,
|
|
||||||
'docs',
|
|
||||||
);
|
|
||||||
expect(dataA).toEqual({
|
expect(dataA).toEqual({
|
||||||
id: 'foo/bar',
|
id: 'foo/bar',
|
||||||
permalink: '/docs/foo/bar',
|
permalink: '/docs/foo/bar',
|
||||||
source: path.join(docsDir, sourceA),
|
source: path.join('@site', pluginPath, sourceA),
|
||||||
title: 'Bar',
|
title: 'Bar',
|
||||||
description: 'This is custom description',
|
description: 'This is custom description',
|
||||||
});
|
});
|
||||||
expect(dataB).toEqual({
|
expect(dataB).toEqual({
|
||||||
id: 'hello',
|
id: 'hello',
|
||||||
permalink: '/docs/hello',
|
permalink: '/docs/hello',
|
||||||
source: path.join(docsDir, sourceB),
|
source: path.join('@site', pluginPath, sourceB),
|
||||||
title: 'Hello, World !',
|
title: 'Hello, World !',
|
||||||
description: `Hi, Endilie here :)`,
|
description: `Hi, Endilie here :)`,
|
||||||
});
|
});
|
||||||
|
@ -52,11 +44,18 @@ describe('processMetadata', () => {
|
||||||
|
|
||||||
test('docs with custom permalink', async () => {
|
test('docs with custom permalink', async () => {
|
||||||
const source = path.join('permalink.md');
|
const source = path.join('permalink.md');
|
||||||
const data = await processMetadata(source, docsDir, {}, siteConfig, 'docs');
|
const data = await processMetadata(
|
||||||
|
source,
|
||||||
|
docsDir,
|
||||||
|
{},
|
||||||
|
siteConfig,
|
||||||
|
pluginPath,
|
||||||
|
siteDir,
|
||||||
|
);
|
||||||
expect(data).toEqual({
|
expect(data).toEqual({
|
||||||
id: 'permalink',
|
id: 'permalink',
|
||||||
permalink: '/docs/endiliey/permalink',
|
permalink: '/docs/endiliey/permalink',
|
||||||
source: path.join(docsDir, source),
|
source: path.join('@site', pluginPath, source),
|
||||||
title: 'Permalink',
|
title: 'Permalink',
|
||||||
description: 'This has a different permalink',
|
description: 'This has a different permalink',
|
||||||
});
|
});
|
||||||
|
|
|
@ -46,7 +46,7 @@ module.exports = function(context, opts) {
|
||||||
// Fetches blog contents and returns metadata for the contents.
|
// Fetches blog contents and returns metadata for the contents.
|
||||||
async loadContent() {
|
async loadContent() {
|
||||||
const {include, routeBasePath, sidebarPath} = options;
|
const {include, routeBasePath, sidebarPath} = options;
|
||||||
const {siteConfig} = context;
|
const {siteConfig, siteDir} = context;
|
||||||
const docsDir = contentPath;
|
const docsDir = contentPath;
|
||||||
|
|
||||||
if (!fs.existsSync(docsDir)) {
|
if (!fs.existsSync(docsDir)) {
|
||||||
|
@ -73,6 +73,7 @@ module.exports = function(context, opts) {
|
||||||
order,
|
order,
|
||||||
siteConfig,
|
siteConfig,
|
||||||
routeBasePath,
|
routeBasePath,
|
||||||
|
siteDir,
|
||||||
);
|
);
|
||||||
docs[metadata.id] = metadata;
|
docs[metadata.id] = metadata;
|
||||||
}),
|
}),
|
||||||
|
@ -177,6 +178,7 @@ module.exports = function(context, opts) {
|
||||||
loader: path.resolve(__dirname, './markdown/index.js'),
|
loader: path.resolve(__dirname, './markdown/index.js'),
|
||||||
options: {
|
options: {
|
||||||
siteConfig: context.siteConfig,
|
siteConfig: context.siteConfig,
|
||||||
|
siteDir: context.siteDir,
|
||||||
docsDir: globalContents.docsDir,
|
docsDir: globalContents.docsDir,
|
||||||
sourceToPermalink: globalContents.sourceToPermalink,
|
sourceToPermalink: globalContents.sourceToPermalink,
|
||||||
},
|
},
|
||||||
|
|
|
@ -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 {getOptions} = require('loader-utils');
|
const {getOptions} = require('loader-utils');
|
||||||
const {resolve} = require('url');
|
const {resolve} = require('url');
|
||||||
|
|
||||||
|
@ -13,7 +14,7 @@ module.exports = async function(fileString) {
|
||||||
const options = Object.assign({}, getOptions(this), {
|
const options = Object.assign({}, getOptions(this), {
|
||||||
filepath: this.resourcePath,
|
filepath: this.resourcePath,
|
||||||
});
|
});
|
||||||
const {docsDir, sourceToPermalink} = options;
|
const {docsDir, siteDir, sourceToPermalink} = options;
|
||||||
|
|
||||||
// Determine the source dir. e.g: /docs, /website/versioned_docs/version-1.0.0
|
// Determine the source dir. e.g: /docs, /website/versioned_docs/version-1.0.0
|
||||||
let sourceDir;
|
let sourceDir;
|
||||||
|
@ -37,15 +38,17 @@ module.exports = async function(fileString) {
|
||||||
// Replace inline-style links or reference-style links e.g:
|
// Replace inline-style links or reference-style links e.g:
|
||||||
// This is [Document 1](doc1.md) -> we replace this doc1.md with correct link
|
// This is [Document 1](doc1.md) -> we replace this doc1.md with correct link
|
||||||
// [doc1]: doc1.md -> we replace this doc1.md with correct link
|
// [doc1]: doc1.md -> we replace this doc1.md with correct link
|
||||||
const mdRegex = /(?:(?:\]\()|(?:\]:\s?))(?!https)([^'")\]\s>]+\.md)/g;
|
const mdRegex = /(?:(?:\]\()|(?:\]:\s?))(?!https)([^'")\]\s>]+\.mdx?)/g;
|
||||||
let mdMatch = mdRegex.exec(modifiedLine);
|
let mdMatch = mdRegex.exec(modifiedLine);
|
||||||
while (mdMatch !== null) {
|
while (mdMatch !== null) {
|
||||||
// Replace it to correct html link.
|
// Replace it to correct html link.
|
||||||
const mdLink = mdMatch[1];
|
const mdLink = mdMatch[1];
|
||||||
const targetSource = `${sourceDir}/${mdLink}`;
|
const targetSource = `${sourceDir}/${mdLink}`;
|
||||||
|
const aliasedSource = source =>
|
||||||
|
`@site/${path.relative(siteDir, source)}`;
|
||||||
const permalink =
|
const permalink =
|
||||||
sourceToPermalink[resolve(thisSource, mdLink)] ||
|
sourceToPermalink[aliasedSource(resolve(thisSource, mdLink))] ||
|
||||||
sourceToPermalink[targetSource];
|
sourceToPermalink[aliasedSource(targetSource)];
|
||||||
if (permalink) {
|
if (permalink) {
|
||||||
modifiedLine = modifiedLine.replace(mdLink, permalink);
|
modifiedLine = modifiedLine.replace(mdLink, permalink);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,12 +11,14 @@ const {parse, normalizeUrl} = require('@docusaurus/utils');
|
||||||
|
|
||||||
module.exports = async function processMetadata(
|
module.exports = async function processMetadata(
|
||||||
source,
|
source,
|
||||||
refDir,
|
docsDir,
|
||||||
order,
|
order,
|
||||||
siteConfig,
|
siteConfig,
|
||||||
docsBasePath,
|
docsBasePath,
|
||||||
|
siteDir,
|
||||||
) {
|
) {
|
||||||
const filepath = path.resolve(refDir, source);
|
const filepath = path.join(docsDir, source);
|
||||||
|
|
||||||
const fileString = await fs.readFile(filepath, 'utf-8');
|
const fileString = await fs.readFile(filepath, 'utf-8');
|
||||||
const {frontMatter: metadata = {}, excerpt} = parse(fileString);
|
const {frontMatter: metadata = {}, excerpt} = parse(fileString);
|
||||||
|
|
||||||
|
@ -24,6 +26,7 @@ module.exports = async function processMetadata(
|
||||||
if (!metadata.id) {
|
if (!metadata.id) {
|
||||||
metadata.id = path.basename(source, path.extname(source));
|
metadata.id = path.basename(source, path.extname(source));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (metadata.id.includes('/')) {
|
if (metadata.id.includes('/')) {
|
||||||
throw new Error('Document id cannot include "/".');
|
throw new Error('Document id cannot include "/".');
|
||||||
}
|
}
|
||||||
|
@ -45,9 +48,9 @@ module.exports = async function processMetadata(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The docs absolute file source.
|
// Cannot use path.join() as it resolves '../' and removes the '@site'. Let webpack loader resolve it.
|
||||||
// e.g: `/end/docs/hello.md` or `/end/website/versioned_docs/version-1.0.0/hello.md`
|
const aliasedPath = `@site/${path.relative(siteDir, filepath)}`;
|
||||||
metadata.source = path.join(refDir, source);
|
metadata.source = aliasedPath;
|
||||||
|
|
||||||
// Build the permalink.
|
// Build the permalink.
|
||||||
const {baseUrl} = siteConfig;
|
const {baseUrl} = siteConfig;
|
||||||
|
|
|
@ -47,10 +47,8 @@ module.exports = function(context, opts) {
|
||||||
|
|
||||||
return pagesFiles.map(relativeSource => {
|
return pagesFiles.map(relativeSource => {
|
||||||
const source = path.join(pagesDir, relativeSource);
|
const source = path.join(pagesDir, relativeSource);
|
||||||
const aliasedSource = path.join(
|
// Cannot use path.join() as it resolves '../' and removes the '@site'. Let webpack loader resolve it.
|
||||||
'@site',
|
const aliasedSource = `@site/${path.relative(siteDir, source)}`;
|
||||||
path.relative(siteDir, source),
|
|
||||||
);
|
|
||||||
const pathName = encodePath(fileToPath(relativeSource));
|
const pathName = encodePath(fileToPath(relativeSource));
|
||||||
// Default Language.
|
// Default Language.
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -61,7 +61,7 @@ my-website
|
||||||
### Project structure rundown
|
### Project structure rundown
|
||||||
|
|
||||||
- `/blog/` - Contains the blog markdown files. You can delete the directory if you do not want/need a blog. More details can be found in the [blog guide](blog.md).
|
- `/blog/` - Contains the blog markdown files. You can delete the directory if you do not want/need a blog. More details can be found in the [blog guide](blog.md).
|
||||||
- `/docs/` - Contains the markdown files for the docs. Customize the order of the docs sidebar in `sidebars.js`. More details can be found in the [docs guide](markdown-features).
|
- `/docs/` - Contains the markdown files for the docs. Customize the order of the docs sidebar in `sidebars.js`. More details can be found in the [docs guide](markdown-features.mdx).
|
||||||
- `/src/` - Non-documentation files like pages or custom React components. You don't have to strictly put your non-documentation files in here but putting them under a centralized directory makes it easier to specify in case you need to do some sort of linting/processing
|
- `/src/` - Non-documentation files like pages or custom React components. You don't have to strictly put your non-documentation files in here but putting them under a centralized directory makes it easier to specify in case you need to do some sort of linting/processing
|
||||||
- `/src/pages` - Any files within this directory will be converted into a website page. More details can be found in the [pages guide](creating-pages.md).
|
- `/src/pages` - Any files within this directory will be converted into a website page. More details can be found in the [pages guide](creating-pages.md).
|
||||||
- `/static/` - Static directory. Any contents inside here will be copied into the root of the final `build` directory.
|
- `/static/` - Static directory. Any contents inside here will be copied into the root of the final `build` directory.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue