Skip to content

Commit

Permalink
chore(mdx-loader): migrate package to TypeScript (#5347)
Browse files Browse the repository at this point in the history
* Polish code style

Signed-off-by: Josh-Cena <[email protected]>

* Partly done migration

Signed-off-by: Josh-Cena <[email protected]>

* Complete typing

Signed-off-by: Josh-Cena <[email protected]>

* Fix tests

Signed-off-by: Josh-Cena <[email protected]>

* A-ha

Signed-off-by: Josh-Cena <[email protected]>

* Cleanup

Signed-off-by: Josh-Cena <[email protected]>

* Fix error

Signed-off-by: Josh-Cena <[email protected]>

* Cleanup

Signed-off-by: Josh-Cena <[email protected]>
  • Loading branch information
Josh-Cena authored Aug 12, 2021
1 parent ac4a253 commit 3fc4793
Show file tree
Hide file tree
Showing 27 changed files with 346 additions and 288 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ module.exports = {
// Ignore certain webpack alias because it can't be resolved
'import/no-unresolved': [
ERROR,
{ignore: ['^@theme', '^@docusaurus', '^@generated']},
{ignore: ['^@theme', '^@docusaurus', '^@generated', 'unist', 'mdast']},
],
'import/extensions': OFF,
'header/header': [
Expand Down
8 changes: 6 additions & 2 deletions packages/docusaurus-mdx-loader/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
"name": "@docusaurus/mdx-loader",
"version": "2.0.0-beta.4",
"description": "Docusaurus Loader for MDX",
"main": "src/index.js",
"types": "src/index.d.ts",
"main": "lib/index.js",
"types": "src/types.d.ts",
"publishConfig": {
"access": "public"
},
Expand Down Expand Up @@ -39,6 +39,10 @@
},
"devDependencies": {
"@docusaurus/types": "2.0.0-beta.4",
"@types/escape-html": "^1.0.1",
"@types/mdast": "^3.0.7",
"@types/stringify-object": "^3.3.1",
"@types/unist": "^2.0.6",
"remark": "^12.0.0",
"remark-mdx": "^1.6.21",
"to-vfile": "^6.0.0",
Expand Down
19 changes: 0 additions & 19 deletions packages/docusaurus-mdx-loader/src/index.d.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,36 +5,45 @@
* LICENSE file in the root directory of this source tree.
*/

const {readFile} = require('fs-extra');
const mdx = require('@mdx-js/mdx');
const chalk = require('chalk');
const emoji = require('remark-emoji');
const {
import {readFile} from 'fs-extra';
import mdx from '@mdx-js/mdx';
import chalk from 'chalk';
import emoji from 'remark-emoji';
import {
parseFrontMatter,
parseMarkdownContentTitle,
} = require('@docusaurus/utils');
const stringifyObject = require('stringify-object');
const headings = require('./remark/headings');
const toc = require('./remark/toc');
const unwrapMdxCodeBlocks = require('./remark/unwrapMdxCodeBlocks');
const transformImage = require('./remark/transformImage');
const transformLinks = require('./remark/transformLinks');
const {escapePath} = require('@docusaurus/utils');
const {getFileLoaderUtils} = require('@docusaurus/core/lib/webpack/utils');
escapePath,
} from '@docusaurus/utils';
import stringifyObject from 'stringify-object';
import headings from './remark/headings';
import toc from './remark/toc';
import unwrapMdxCodeBlocks from './remark/unwrapMdxCodeBlocks';
import transformImage from './remark/transformImage';
import transformLinks from './remark/transformLinks';
import {getFileLoaderUtils} from '@docusaurus/core/lib/webpack/utils';
import type {RemarkAndRehypePluginOptions} from '@docusaurus/mdx-loader';

// TODO temporary until Webpack5 export this type
// see https://github.com/webpack/webpack/issues/11630
interface Loader extends Function {
(this: any, source: string): Promise<string | Buffer | void | undefined>;
}

const {
loaders: {inlineMarkdownImageFileLoader},
} = getFileLoaderUtils();

const DEFAULT_OPTIONS = {
const DEFAULT_OPTIONS: RemarkAndRehypePluginOptions = {
rehypePlugins: [],
remarkPlugins: [unwrapMdxCodeBlocks, emoji, headings, toc],
beforeDefaultRemarkPlugins: [],
beforeDefaultRehypePlugins: [],
};

// When this throws, it generally means that there's no metadata file associated with this MDX document
// It can happen when using MDX partials (usually starting with _)
// That's why it's important to provide the "isMDXPartial" function in config
async function readMetadataPath(metadataPath) {
async function readMetadataPath(metadataPath: string) {
try {
return await readFile(metadataPath, 'utf8');
} catch (e) {
Expand All @@ -48,15 +57,14 @@ async function readMetadataPath(metadataPath) {
// We don't do that for all frontMatters, only for the configured keys
// {image: "./myImage.png"} => {image: require("./myImage.png")}
function createFrontMatterAssetsExportCode(
filePath,
frontMatter,
frontMatterAssetKeys = [],
frontMatter: Record<string, unknown>,
frontMatterAssetKeys: string[] = [],
) {
if (frontMatterAssetKeys.length === 0) {
return 'undefined';
}

function createFrontMatterAssetRequireCode(value) {
function createFrontMatterAssetRequireCode(value: unknown) {
// Only process string values starting with ./
// We could enhance this logic and check if file exists on disc?
if (typeof value === 'string' && value.startsWith('./')) {
Expand Down Expand Up @@ -84,7 +92,7 @@ function createFrontMatterAssetsExportCode(
return exportValue;
}

module.exports = async function docusaurusMdxLoader(fileString) {
const docusaurusMdxLoader: Loader = async function (fileString) {
const callback = this.async();
const filePath = this.resourcePath;
const reqOptions = this.getOptions() || {};
Expand Down Expand Up @@ -122,35 +130,25 @@ module.exports = async function docusaurusMdxLoader(fileString) {
return callback(err);
}

let exportStr = ``;
exportStr += `\nexport const frontMatter = ${stringifyObject(frontMatter)};`;
exportStr += `\nexport const frontMatterAssets = ${createFrontMatterAssetsExportCode(
filePath,
let exportStr = `
export const frontMatter = ${stringifyObject(frontMatter)};
export const frontMatterAssets = ${createFrontMatterAssetsExportCode(
frontMatter,
reqOptions.frontMatterAssetKeys,
)};`;
exportStr += `\nexport const contentTitle = ${stringifyObject(
contentTitle,
)};`;
)};
export const contentTitle = ${stringifyObject(contentTitle)};`;

// MDX partials are MDX files starting with _ or in a folder starting with _
// Partial are not expected to have an associated metadata file or frontmatter
const isMDXPartial = options.isMDXPartial
? options.isMDXPartial(filePath)
: false;
const isMDXPartial = options.isMDXPartial && options.isMDXPartial(filePath);

if (isMDXPartial && hasFrontMatter) {
const errorMessage = `Docusaurus MDX partial files should not contain FrontMatter.
Those partial files use the _ prefix as a convention by default, but this is configurable.
File at ${filePath} contains FrontMatter that will be ignored: \n${JSON.stringify(
frontMatter,
null,
2,
)}`;

if (options.isMDXPartialFrontMatterWarningDisabled === true) {
// no warning
} else {
File at ${filePath} contains FrontMatter that will be ignored:
${JSON.stringify(frontMatter, null, 2)}`;

if (!options.isMDXPartialFrontMatterWarningDisabled) {
const shouldError = process.env.NODE_ENV === 'test' || process.env.CI;
if (shouldError) {
return callback(new Error(errorMessage));
Expand All @@ -176,12 +174,14 @@ File at ${filePath} contains FrontMatter that will be ignored: \n${JSON.stringif
}

const code = `
import React from 'react';
import { mdx } from '@mdx-js/react';
import React from 'react';
import { mdx } from '@mdx-js/react';
${exportStr}
${result}
`;
${exportStr}
${result}
`;

return callback(null, code);
};

export default docusaurusMdxLoader;
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,25 @@

/* Based on remark-slug (https://github.com/remarkjs/remark-slug) and gatsby-remark-autolink-headers (https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby-remark-autolink-headers) */

const {parseMarkdownHeadingId} = require('@docusaurus/utils');
const visit = require('unist-util-visit');
const toString = require('mdast-util-to-string');
const slugs = require('github-slugger')();
import {parseMarkdownHeadingId} from '@docusaurus/utils';
import visit, {Visitor} from 'unist-util-visit';
import toString from 'mdast-util-to-string';
import Slugger from 'github-slugger';
import type {Transformer} from 'unified';
import type {Parent} from 'unist';
import type {Heading, Text} from 'mdast';

function headings() {
const transformer = (ast) => {
const slugs = new Slugger();

function headings(): Transformer {
const transformer: Transformer = (ast) => {
slugs.reset();

function visitor(headingNode) {
const data = headingNode.data || (headingNode.data = {}); // eslint-disable-line
const properties = data.hProperties || (data.hProperties = {});
const visitor: Visitor<Heading> = (headingNode) => {
const data = headingNode.data || (headingNode.data = {});
const properties = (data.hProperties || (data.hProperties = {})) as {
id: string;
};
let {id} = properties;

if (id) {
Expand All @@ -29,7 +36,7 @@ function headings() {
);
const heading = toString(
headingTextNodes.length > 0
? {children: headingTextNodes}
? ({children: headingTextNodes} as Parent)
: headingNode,
);

Expand All @@ -42,8 +49,9 @@ function headings() {
// When there's an id, it is always in the last child node
// Sometimes heading is in multiple "parts" (** syntax creates a child node):
// ## part1 *part2* part3 {#id}
const lastNode =
headingNode.children[headingNode.children.length - 1];
const lastNode = headingNode.children[
headingNode.children.length - 1
] as Text;

if (headingNode.children.length > 1) {
const lastNodeText = parseMarkdownHeadingId(lastNode.value).text;
Expand All @@ -63,12 +71,12 @@ function headings() {

data.id = id;
properties.id = id;
}
};

visit(ast, 'heading', visitor);
};

return transformer;
}

module.exports = headings;
export default headings;
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,42 @@
* LICENSE file in the root directory of this source tree.
*/

const {parse} = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const stringifyObject = require('stringify-object');
const search = require('./search');
import {parse, ParserOptions} from '@babel/parser';
import type {Identifier} from '@babel/types';
import traverse from '@babel/traverse';
import stringifyObject from 'stringify-object';
import search from './search';
import type {Plugin, Transformer} from 'unified';
import type {Node, Parent} from 'unist';
import type {Literal} from 'mdast';

const parseOptions = {
const parseOptions: ParserOptions = {
plugins: ['jsx'],
sourceType: 'module',
};
const isImport = (child) => child.type === 'import';
const hasImports = (index) => index > -1;
const isExport = (child) => child.type === 'export';

const isTarget = (child, name) => {
const isImport = (child: Node): child is Literal => child.type === 'import';
const hasImports = (index: number) => index > -1;
const isExport = (child: Node): child is Literal => child.type === 'export';

interface PluginOptions {
name?: string;
}

const isTarget = (child: Literal, name: string) => {
let found = false;
const ast = parse(child.value, parseOptions);
traverse(ast, {
VariableDeclarator: (path) => {
if (path.node.id.name === name) {
if ((path.node.id as Identifier).name === name) {
found = true;
}
},
});

return found;
};

const getOrCreateExistingTargetIndex = (children, name) => {
const getOrCreateExistingTargetIndex = (children: Node[], name: string) => {
let importsIndex = -1;
let targetIndex = -1;

Expand All @@ -58,12 +66,12 @@ const getOrCreateExistingTargetIndex = (children, name) => {
return targetIndex;
};

const plugin = (options = {}) => {
const plugin: Plugin<[PluginOptions?]> = (options = {}) => {
const name = options.name || 'toc';

const transformer = (node) => {
const transformer: Transformer = (node) => {
const headings = search(node);
const {children} = node;
const {children} = node as Parent<Literal>;
const targetIndex = getOrCreateExistingTargetIndex(children, name);

if (headings && headings.length) {
Expand All @@ -76,4 +84,4 @@ const plugin = (options = {}) => {
return transformer;
};

module.exports = plugin;
export default plugin;
Loading

0 comments on commit 3fc4793

Please sign in to comment.