Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: unify error handling behavior #6755

Merged
merged 11 commits into from Feb 25, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 4 additions & 4 deletions admin/scripts/generateExamples.mjs
Expand Up @@ -92,9 +92,9 @@ async function generateTemplateExample(template) {
);

console.log(`Generated example for template ${template}`);
} catch (error) {
} catch (err) {
console.error(`Failed to generated example for template ${template}`);
throw error;
throw err;
}
}

Expand All @@ -115,12 +115,12 @@ function updateStarters() {
console.log(`forcePushGitSubtree command: ${command}`);
shell.exec(command);
console.log('forcePushGitSubtree success!');
} catch (e) {
} catch (err) {
console.error(
`Can't force push to git subtree with command '${command}'`,
);
console.error(`If it's a permission problem, ask @slorber`);
console.error(e);
console.error(err);
}
console.log('');
}
Expand Down
1 change: 1 addition & 0 deletions jest.config.mjs
Expand Up @@ -53,4 +53,5 @@ export default {
location: {href: 'https://docusaurus.io'},
},
},
snapshotSerializers: ['<rootDir>/jest/snapshotPathNormalizer.js'],
};
174 changes: 174 additions & 0 deletions jest/snapshotPathNormalizer.js
@@ -0,0 +1,174 @@
/**
* 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.
*/
/* eslint-disable import/no-extraneous-dependencies */

// Forked from https://github.com/tribou/jest-serializer-path/blob/master/lib/index.js
// Added some project-specific handlers

const _ = require('lodash');
const {escapePath} = require('@docusaurus/utils');
const os = require('os');
const path = require('path');
const fs = require('fs');

module.exports = {
print(val, serialize) {
let normalizedValue = val;

if (_.isError(normalizedValue)) {
const message = normalizePaths(normalizedValue.message);
const error = new Error(message);

// Clone hidden props
const ownProps = Object.getOwnPropertyNames(error);
// eslint-disable-next-line no-restricted-syntax
for (const index in ownProps) {
if (Object.prototype.hasOwnProperty.call(ownProps, index)) {
const key = ownProps[index];

error[key] = normalizePaths(normalizedValue[key]);
}
}

// Clone normal props
// eslint-disable-next-line no-restricted-syntax
for (const index in normalizedValue) {
if (Object.prototype.hasOwnProperty.call(normalizedValue, index)) {
error[index] = normalizePaths(normalizedValue[index]);
}
}

normalizedValue = error;
} else if (typeof normalizedValue === 'object') {
normalizedValue = _.cloneDeep(normalizedValue);

Object.keys(normalizedValue).forEach((key) => {
normalizedValue[key] = normalizePaths(normalizedValue[key]);
});
} else {
normalizedValue = normalizePaths(normalizedValue);
}

return serialize(normalizedValue);
},
test(val) {
let has = false;

if (val && typeof val === 'object') {
// val.message is non-enumerable in an error
if (val.message && shouldUpdate(val.message)) {
has = true;
}

Object.keys(val).forEach((key) => {
if (shouldUpdate(val[key])) {
has = true;
}
});
} else if (shouldUpdate(val)) {
has = true;
}

return has;
},
normalizePaths,
getRealPath,
};

/**
* Normalize paths across platforms.
* Filters must be ran on all platforms to guard against false positives
*/
function normalizePaths(value) {
if (typeof value !== 'string') {
return value;
}

const cwd = process.cwd();
const cwdReal = getRealPath(cwd);
const tempDir = os.tmpdir();
const tempDirReal = getRealPath(tempDir);
const homeDir = os.homedir();
const homeDirReal = getRealPath(homeDir);

const homeRelativeToTemp = path.relative(tempDir, homeDir);
const homeRelativeToTempReal = path.relative(tempDirReal, homeDir);
const homeRealRelativeToTempReal = path.relative(tempDirReal, homeDirReal);
const homeRealRelativeToTemp = path.relative(tempDir, homeDirReal);

const runner = [
// Replace process.cwd with <PROJECT_ROOT>
(val) => val.split(cwdReal).join('<PROJECT_ROOT>'),
(val) => val.split(cwd).join('<PROJECT_ROOT>'),

// Replace home directory with <TEMP_DIR>
(val) => val.split(tempDirReal).join('<TEMP_DIR>'),
(val) => val.split(tempDir).join('<TEMP_DIR>'),

// Replace home directory with <HOME_DIR>
(val) => val.split(homeDirReal).join('<HOME_DIR>'),
(val) => val.split(homeDir).join('<HOME_DIR>'),

// handle HOME_DIR nested inside TEMP_DIR
(val) =>
val
.split(`<TEMP_DIR>${path.sep + homeRelativeToTemp}`)
.join('<HOME_DIR>'),
(val) =>
val
.split(`<TEMP_DIR>${path.sep + homeRelativeToTempReal}`)
.join('<HOME_DIR>'), // untested
(val) =>
val
.split(`<TEMP_DIR>${path.sep + homeRealRelativeToTempReal}`)
.join('<HOME_DIR>'),
(val) =>
val
.split(`<TEMP_DIR>${path.sep + homeRealRelativeToTemp}`)
.join('<HOME_DIR>'), // untested

// In case the CWD is escaped
(val) => val.split(escapePath(cwd)).join('<PROJECT_ROOT>'),

// Remove win32 drive letters, C:\ -> \
(val) => val.replace(/[a-zA-Z]:\\/g, '\\'),

// Remove duplicate backslashes created from escapePath
(val) => val.replace(/\\\\/g, '\\'),

// Convert win32 backslash's to forward slashes, \ -> /;
// ignore some that look like escape sequences.
(val) => val.replace(/\\(?!["])/g, '/'),
];

let result = value;
runner.forEach((current) => {
result = current(result);
});

return result;
}

function shouldUpdate(value) {
if (typeof value !== 'string') {
return false;
}

// return true if value is different from normalized value
return normalizePaths(value) !== value;
}

function getRealPath(pathname) {
try {
// eslint-disable-next-line no-restricted-properties
const realPath = fs.realpathSync(pathname);

return realPath;
} catch (error) {
return pathname;
}
}
7 changes: 3 additions & 4 deletions packages/docusaurus-mdx-loader/src/index.ts
Expand Up @@ -58,10 +58,9 @@ type Options = RemarkAndRehypePluginOptions & {
async function readMetadataPath(metadataPath: string) {
try {
return await fs.readFile(metadataPath, 'utf8');
} catch (e) {
throw new Error(
`MDX loader can't read MDX metadata file for path ${metadataPath}. Maybe the isMDXPartial option function was not provided?`,
);
} catch (err) {
logger.error`MDX loader can't read MDX metadata file path=${metadataPath}. Maybe the isMDXPartial option function was not provided?`;
throw err;
}
}

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Expand Up @@ -26,6 +26,11 @@ exports[`inline code should be escaped 1`] = `
value: '<code>&lt;div&gt;&lt;i&gt;Test&lt;/i&gt;&lt;/div&gt;</code>',
id: 'divitestidiv',
level: 2
},
{
value: '<code>&lt;div&gt;&lt;i&gt;Test&lt;/i&gt;&lt;/div&gt;</code>',
id: 'divitestidiv-1',
level: 2
}
];

Expand All @@ -38,6 +43,8 @@ exports[`inline code should be escaped 1`] = `
## \`<div> Test </div>\`

## \`<div><i>Test</i></div>\`

## [\`<div><i>Test</i></div>\`](/some/link)
"
`;

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@@ -1,5 +1,10 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`transformImage plugin does not choke on invalid image 1`] = `
"<img loading=\\"lazy\\" alt={\\"invalid image\\"} src={require(\\"!<PROJECT_ROOT>/node_modules/url-loader/dist/cjs.js?limit=10000&name=assets/images/[name]-[contenthash].[ext]&fallback=<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js!./static/invalid.png\\").default} />
"
`;

exports[`transformImage plugin fail if image does not exist 1`] = `"Image packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/__fixtures__/static/img/doesNotExist.png or packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/__fixtures__/static2/img/doesNotExist.png used in packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/__fixtures__/fail.md not found."`;

exports[`transformImage plugin fail if image relative path does not exist 1`] = `"Image packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/__fixtures__/notFound.png used in packages/docusaurus-mdx-loader/src/remark/transformImage/__tests__/__fixtures__/fail2.md not found."`;
Expand All @@ -14,27 +19,27 @@ exports[`transformImage plugin pathname protocol 1`] = `
exports[`transformImage plugin transform md images to <img /> 1`] = `
"![img](https://example.com/img.png)

<img loading=\\"lazy\\" src={require(\\"![CWD]/node_modules/url-loader/dist/cjs.js?limit=10000&name=assets/images/[name]-[contenthash].[ext]&fallback=[CWD]/node_modules/file-loader/dist/cjs.js!./static/img.png\\").default} width=\\"200\\" height=\\"200\\" />
<img loading=\\"lazy\\" src={require(\\"!<PROJECT_ROOT>/node_modules/url-loader/dist/cjs.js?limit=10000&name=assets/images/[name]-[contenthash].[ext]&fallback=<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js!./static/img.png\\").default} width=\\"200\\" height=\\"200\\" />

<img loading=\\"lazy\\" alt={\\"img\\"} src={require(\\"![CWD]/node_modules/url-loader/dist/cjs.js?limit=10000&name=assets/images/[name]-[contenthash].[ext]&fallback=[CWD]/node_modules/file-loader/dist/cjs.js!./static/img.png\\").default} width=\\"200\\" height=\\"200\\" />
<img loading=\\"lazy\\" alt={\\"img\\"} src={require(\\"!<PROJECT_ROOT>/node_modules/url-loader/dist/cjs.js?limit=10000&name=assets/images/[name]-[contenthash].[ext]&fallback=<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js!./static/img.png\\").default} width=\\"200\\" height=\\"200\\" />

<img loading=\\"lazy\\" alt={\\"img from second static folder\\"} src={require(\\"![CWD]/node_modules/url-loader/dist/cjs.js?limit=10000&name=assets/images/[name]-[contenthash].[ext]&fallback=[CWD]/node_modules/file-loader/dist/cjs.js!./static2/img2.png\\").default} width=\\"256\\" height=\\"82\\" />
<img loading=\\"lazy\\" alt={\\"img from second static folder\\"} src={require(\\"!<PROJECT_ROOT>/node_modules/url-loader/dist/cjs.js?limit=10000&name=assets/images/[name]-[contenthash].[ext]&fallback=<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js!./static2/img2.png\\").default} width=\\"256\\" height=\\"82\\" />

<img loading=\\"lazy\\" alt={\\"img from second static folder\\"} src={require(\\"![CWD]/node_modules/url-loader/dist/cjs.js?limit=10000&name=assets/images/[name]-[contenthash].[ext]&fallback=[CWD]/node_modules/file-loader/dist/cjs.js!./static2/img2.png\\").default} width=\\"256\\" height=\\"82\\" />
<img loading=\\"lazy\\" alt={\\"img from second static folder\\"} src={require(\\"!<PROJECT_ROOT>/node_modules/url-loader/dist/cjs.js?limit=10000&name=assets/images/[name]-[contenthash].[ext]&fallback=<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js!./static2/img2.png\\").default} width=\\"256\\" height=\\"82\\" />

<img loading=\\"lazy\\" alt={\\"img\\"} src={require(\\"![CWD]/node_modules/url-loader/dist/cjs.js?limit=10000&name=assets/images/[name]-[contenthash].[ext]&fallback=[CWD]/node_modules/file-loader/dist/cjs.js!./static/img.png\\").default} title=\\"Title\\" width=\\"200\\" height=\\"200\\" /> <img loading=\\"lazy\\" alt={\\"img\\"} src={require(\\"![CWD]/node_modules/url-loader/dist/cjs.js?limit=10000&name=assets/images/[name]-[contenthash].[ext]&fallback=[CWD]/node_modules/file-loader/dist/cjs.js!./static/img.png\\").default} width=\\"200\\" height=\\"200\\" />
<img loading=\\"lazy\\" alt={\\"img\\"} src={require(\\"!<PROJECT_ROOT>/node_modules/url-loader/dist/cjs.js?limit=10000&name=assets/images/[name]-[contenthash].[ext]&fallback=<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js!./static/img.png\\").default} title=\\"Title\\" width=\\"200\\" height=\\"200\\" /> <img loading=\\"lazy\\" alt={\\"img\\"} src={require(\\"!<PROJECT_ROOT>/node_modules/url-loader/dist/cjs.js?limit=10000&name=assets/images/[name]-[contenthash].[ext]&fallback=<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js!./static/img.png\\").default} width=\\"200\\" height=\\"200\\" />

<img loading=\\"lazy\\" alt={\\"img with &quot;quotes&quot;\\"} src={require(\\"![CWD]/node_modules/url-loader/dist/cjs.js?limit=10000&name=assets/images/[name]-[contenthash].[ext]&fallback=[CWD]/node_modules/file-loader/dist/cjs.js!./static/img.png\\").default} title=\\"&#39;Quoted&#39; title\\" width=\\"200\\" height=\\"200\\" />
<img loading=\\"lazy\\" alt={\\"img with &quot;quotes&quot;\\"} src={require(\\"!<PROJECT_ROOT>/node_modules/url-loader/dist/cjs.js?limit=10000&name=assets/images/[name]-[contenthash].[ext]&fallback=<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js!./static/img.png\\").default} title=\\"&#39;Quoted&#39; title\\" width=\\"200\\" height=\\"200\\" />

<img loading=\\"lazy\\" alt={\\"site alias\\"} src={require(\\"![CWD]/node_modules/url-loader/dist/cjs.js?limit=10000&name=assets/images/[name]-[contenthash].[ext]&fallback=[CWD]/node_modules/file-loader/dist/cjs.js!./static/img.png\\").default} width=\\"200\\" height=\\"200\\" />
<img loading=\\"lazy\\" alt={\\"site alias\\"} src={require(\\"!<PROJECT_ROOT>/node_modules/url-loader/dist/cjs.js?limit=10000&name=assets/images/[name]-[contenthash].[ext]&fallback=<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js!./static/img.png\\").default} width=\\"200\\" height=\\"200\\" />

<img loading=\\"lazy\\" alt={\\"img with hash\\"} src={require(\\"![CWD]/node_modules/url-loader/dist/cjs.js?limit=10000&name=assets/images/[name]-[contenthash].[ext]&fallback=[CWD]/node_modules/file-loader/dist/cjs.js!./static/img.png\\").default + '#light'} width=\\"200\\" height=\\"200\\" />
<img loading=\\"lazy\\" alt={\\"img with hash\\"} src={require(\\"![CWD]/node_modules/url-loader/dist/cjs.js?limit=10000&name=assets/images/[name]-[contenthash].[ext]&fallback=[CWD]/node_modules/file-loader/dist/cjs.js!./static/img.png\\").default + '#dark'} width=\\"200\\" height=\\"200\\" />
<img loading=\\"lazy\\" alt={\\"img with hash\\"} src={require(\\"!<PROJECT_ROOT>/node_modules/url-loader/dist/cjs.js?limit=10000&name=assets/images/[name]-[contenthash].[ext]&fallback=<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js!./static/img.png\\").default + '#light'} width=\\"200\\" height=\\"200\\" />
<img loading=\\"lazy\\" alt={\\"img with hash\\"} src={require(\\"!<PROJECT_ROOT>/node_modules/url-loader/dist/cjs.js?limit=10000&name=assets/images/[name]-[contenthash].[ext]&fallback=<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js!./static/img.png\\").default + '#dark'} width=\\"200\\" height=\\"200\\" />

<img loading=\\"lazy\\" alt={\\"img with query\\"} src={require(\\"![CWD]/node_modules/url-loader/dist/cjs.js?limit=10000&name=assets/images/[name]-[contenthash].[ext]&fallback=[CWD]/node_modules/file-loader/dist/cjs.js!./static/img.png?w=10\\").default} width=\\"200\\" height=\\"200\\" />
<img loading=\\"lazy\\" alt={\\"img with query\\"} src={require(\\"![CWD]/node_modules/url-loader/dist/cjs.js?limit=10000&name=assets/images/[name]-[contenthash].[ext]&fallback=[CWD]/node_modules/file-loader/dist/cjs.js!./static/img.png?w=10&h=10\\").default} width=\\"200\\" height=\\"200\\" />
<img loading=\\"lazy\\" alt={\\"img with query\\"} src={require(\\"!<PROJECT_ROOT>/node_modules/url-loader/dist/cjs.js?limit=10000&name=assets/images/[name]-[contenthash].[ext]&fallback=<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js!./static/img.png?w=10\\").default} width=\\"200\\" height=\\"200\\" />
<img loading=\\"lazy\\" alt={\\"img with query\\"} src={require(\\"!<PROJECT_ROOT>/node_modules/url-loader/dist/cjs.js?limit=10000&name=assets/images/[name]-[contenthash].[ext]&fallback=<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js!./static/img.png?w=10&h=10\\").default} width=\\"200\\" height=\\"200\\" />

<img loading=\\"lazy\\" alt={\\"img with both\\"} src={require(\\"![CWD]/node_modules/url-loader/dist/cjs.js?limit=10000&name=assets/images/[name]-[contenthash].[ext]&fallback=[CWD]/node_modules/file-loader/dist/cjs.js!./static/img.png?w=10&h=10\\").default + '#light'} width=\\"200\\" height=\\"200\\" />
<img loading=\\"lazy\\" alt={\\"img with both\\"} src={require(\\"!<PROJECT_ROOT>/node_modules/url-loader/dist/cjs.js?limit=10000&name=assets/images/[name]-[contenthash].[ext]&fallback=<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js!./static/img.png?w=10&h=10\\").default + '#light'} width=\\"200\\" height=\\"200\\" />

## Heading

Expand Down
Expand Up @@ -21,10 +21,7 @@ const processFixture = async (name, options) => {
.use(plugin, {...options, filePath})
.process(file);

return result
.toString()
.replace(/\\\\/g, '/')
.replace(new RegExp(process.cwd().replace(/\\/g, '/'), 'g'), '[CWD]');
return result.toString();
};

const staticDirs = [
Expand Down Expand Up @@ -60,4 +57,11 @@ describe('transformImage plugin', () => {
const result = await processFixture('pathname', {staticDirs});
expect(result).toMatchSnapshot();
});

test('does not choke on invalid image', async () => {
const errorMock = jest.spyOn(console, 'error').mockImplementation();
const result = await processFixture('invalid-img', {staticDirs});
expect(result).toMatchSnapshot();
expect(errorMock).toBeCalledTimes(1);
});
});
Expand Up @@ -66,9 +66,9 @@ async function toImageRequireNode(
if (size.height) {
height = ` height="${size.height}"`;
}
} catch (e) {
} catch (err) {
logger.error`The image at path=${imagePath} can't be read correctly. Please ensure it's a valid image.
${(e as Error).message}`;
${(err as Error).message}`;
}

Object.keys(jsxNode).forEach(
Expand Down