Skip to content

Commit

Permalink
refactor: unify error handling behavior (#6755)
Browse files Browse the repository at this point in the history
* refactor: unify error handling behavior

* revert

* normalize paths

* test...

* change

* does this work...

* fix...

* maybe fix

* maybe fix

* fix

* fix...
  • Loading branch information
Josh-Cena authored Feb 25, 2022
1 parent dcbf9f6 commit f903422
Show file tree
Hide file tree
Showing 58 changed files with 1,123 additions and 299 deletions.
8 changes: 4 additions & 4 deletions admin/scripts/generateExamples.mjs
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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.

Original file line number Diff line number Diff line change
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.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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);
});
});
Original file line number Diff line number Diff line change
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
Loading

0 comments on commit f903422

Please sign in to comment.