next-mdx-remote-client
Version:
A wrapper of the `@mdx-js/mdx` for the `nextjs` applications in order to load MDX content. It is a fork of `next-mdx-remote`.
134 lines (116 loc) • 3.87 kB
text/typescript
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*
* reference to https://github.com/hashicorp/next-mdx-remote/blob/main/src/format-mdx-error.ts
*
* improved the types
*/
import { codeFrameColumns } from "@babel/code-frame";
type Position = {
start: {
line: number;
column: number;
};
};
/**
* Attempt to parse position information from an error message originating from the MDX compiler.
*
* @param message error message
* @returns the position of the error indicator
*/
function parsePositionInformationFromErrorMessage(message: string): Position | undefined {
const positionInfoPattern = /\d+:\d+(-\d+:\d+)/g;
const match = message.match(positionInfoPattern);
if (!match) return;
// take the last match, that seems to be the most reliable source of the error.
const lastMatch = match.slice(-1)[0];
const [line, column] = lastMatch.split("-")[0].split(":");
return {
start: {
line: Number.parseInt(line, 10),
column: Number.parseInt(column, 10),
},
};
}
type PositionedError = Error & {
position?: Position;
};
/**
* prints a nicely formatted error message from an error caught during MDX compilation.
*
* @param error Error caught from the mdx compiler
* @param source Raw MDX string
* @returns Error
*/
export function createFormattedMDXError(error: PositionedError, source: string): Error {
const position = error.position ?? parsePositionInformationFromErrorMessage(error.message);
const codeFrames = position
? codeFrameColumns(
source,
{
start: {
line: position.start.line,
column: position.start.column,
},
},
{ linesAbove: 2, linesBelow: 2 },
)
: "";
const formattedError = new Error(`[next-mdx-remote-client] error compiling MDX:
${error?.message}
${codeFrames ? "\n" + codeFrames + "\n" : ""}
More information: https://mdxjs.com/docs/troubleshooting-mdx`);
formattedError.stack = "";
return formattedError;
}
/**
* Copyright (c) @talatkuyuk AKA @ipikuka
* SPDX-License-Identifier: MPL-2.0
*/
export type VfileDataIntoScope =
| true // all fields
| string // one specific field
| { name: string; as: string } // one specific field but change the key as
| Array<string | { name: string; as: string }>; // more than one field
/**
* copies some fields of vfile.data into scope by mutating the scope
* pay attention that it provides reference copy for objects (including arrays)
*
* @param data vfile.data from copied
* @param vfileDataIntoScope refers the fields of vfile.data, some or all (if true)
* @param scope an object to copied in a mutable way
*/
export function passVfileDataIntoScope(
data: Record<string, unknown>,
vfileDataIntoScope: VfileDataIntoScope,
scope: Record<string, unknown>,
): undefined {
if (typeof vfileDataIntoScope === "boolean") {
Object.entries(data).forEach(([key, value]) => {
scope[key] = value;
});
} else if (typeof vfileDataIntoScope === "string") {
scope[vfileDataIntoScope] = data[vfileDataIntoScope];
} else if (Array.isArray(vfileDataIntoScope)) {
vfileDataIntoScope.forEach((field) => {
if (typeof field === "string") {
scope[field] = data[field];
} else if (
// type-coverage:ignore-next-line
Object.prototype.hasOwnProperty.call(field, "name") &&
// type-coverage:ignore-next-line
Object.prototype.hasOwnProperty.call(field, "as")
) {
scope[field.as] = data[field.name];
}
});
} else if (
// type-coverage:ignore-next-line
Object.prototype.hasOwnProperty.call(vfileDataIntoScope, "name") &&
// type-coverage:ignore-next-line
Object.prototype.hasOwnProperty.call(vfileDataIntoScope, "as")
) {
scope[vfileDataIntoScope.as] = data[vfileDataIntoScope.name];
}
}