feat: allow to set plugins for mdjs processing

This commit is contained in:
Thomas Allmer
2020-05-23 09:37:37 +02:00
parent c8ea754153
commit e1ed13d6c9
10 changed files with 142 additions and 63 deletions

View File

@@ -7,6 +7,8 @@ module.exports = {
'storybook-prebuilt/addon-docs/register.js',
'storybook-prebuilt/addon-knobs/register.js',
],
// this would disable the ids of headlines - you can also use it to add your own unified/remark plugins
// setupMdjsPlugins: plugins => plugins.filter(plugin => plugin.name !== 'mdSlug'),
rollup: config => {
config.plugins.push({
generateBundle() {

View File

@@ -1,6 +1,7 @@
#!/usr/bin/env node
/** @typedef {import('es-dev-server').Config} ServerConfig */
/** @typedef {import('@mdjs/core').MdjsProcessPlugin} MdjsProcessPlugin */
/* eslint-disable no-console, no-param-reassign */
const { createConfig, startServer } = require('es-dev-server');
@@ -8,14 +9,14 @@ const path = require('path');
const fs = require('fs');
const readCommandLineArgs = require('./readCommandLineArgs');
const mdjsToCsfTransformer = require('./transformers/mdjsToCsfTransformer');
const createMdjsToCsfTransformer = require('./transformers/createMdjsToCsfTransformer');
const createServeStorybookTransformer = require('./transformers/createServeStorybookTransformer');
const { mdxToCsfTransformer } = require('./transformers/mdxToCsfTransformer');
const toBrowserPath = require('../shared/toBrowserPath');
const getAssets = require('../shared/getAssets');
async function run() {
const config = /** @type {ServerConfig & { stories: string[], addons: string[], configDir: string}} */ (readCommandLineArgs());
const config = /** @type {ServerConfig & { stories: string[], addons: string[], configDir: string, setupMdjsPlugins: MdjsProcessPlugin[] }} */ (readCommandLineArgs());
const rootDir = config.rootDir ? path.resolve(process.cwd(), config.rootDir) : process.cwd();
const storybookConfigDir = config.configDir;
@@ -45,7 +46,9 @@ async function run() {
config.responseTransformers = [
...(config.responseTransformers || []),
mdjsToCsfTransformer,
createMdjsToCsfTransformer({
setupMdjsPlugins: config.setupMdjsPlugins,
}),
mdxToCsfTransformer,
createServeStorybookTransformer({
assets,

View File

@@ -87,6 +87,7 @@ module.exports = function readCommandLineArgs() {
configDir: storybookArgs['config-dir'],
stories: storybookArgs.stories,
addons: mainJs.addons || [],
setupMdjsPlugins: mainJs.setupMdjsPlugins,
// TODO: we should separate es dev server config and storybook config
// command line args read from regular es-dev-server

View File

@@ -0,0 +1,33 @@
const { mdjsToCsf } = require('storybook-addon-markdown-docs');
function createMdjsToCsfTransformer(options) {
/**
* @param {object} params
* @param {string} params.body
* @param {string} params.url
* @param {number} params.status
*/
return async function mdjsToCsfTransformer({ url, body, status }) {
if (status < 200 || status >= 300) {
return null;
}
const split = url.split('?');
const path = split[0].split('#')[0];
if (!path.endsWith('.md')) {
return null;
}
const searchParams = split[1];
if (!searchParams || !searchParams.startsWith('storybook-story')) {
return null;
}
const markdownStory = await mdjsToCsf(body, path, options);
return { body: markdownStory };
};
}
module.exports = createMdjsToCsfTransformer;

View File

@@ -1,31 +0,0 @@
const { mdjsToCsf } = require('storybook-addon-markdown-docs');
/**
* @param {object} params
* @param {string} params.body
* @param {string} params.url
* @param {number} params.status
*/
async function mdjsToCsfTransformer({ url, body, status }) {
if (status < 200 || status >= 300) {
return null;
}
const split = url.split('?');
const path = split[0].split('#')[0];
if (!path.endsWith('.md')) {
return null;
}
const searchParams = split[1];
if (!searchParams || !searchParams.startsWith('storybook-story')) {
return null;
}
const markdownStory = await mdjsToCsf(body, path);
return { body: markdownStory };
}
module.exports = mdjsToCsfTransformer;

View File

@@ -1,21 +1,10 @@
const { mdjsToCsf } = require('../../index');
module.exports = {
stories: ['../stories/*.stories.{js,md}'],
// this would disable the ids of headlines - you can also use it to add your own unified/remark plugins
// setupMdjsPlugins: plugins => plugins.filter(plugin => plugin.name !== 'mdSlug'),
esDevServer: {
open: true,
watch: true,
nodeResolve: true,
fileExtensions: ['.js', '.mjs', '.md'],
responseTransformers: [
async function mdToStory({ url, body }) {
const cleanURL = url.split('?')[0].split('#')[0];
if (cleanURL.endsWith('md')) {
const markdownStory = await mdjsToCsf(body, cleanURL);
return { body: markdownStory };
}
},
],
},
};

View File

@@ -7,10 +7,11 @@ const { jsxToJs } = require('./jsxToJs');
/**
* @param {string} markdown
* @param {string} filePath
* @param {object} options
* @returns {Promise<string>}
*/
async function mdjsToCsf(markdown, filePath) {
const markdownResult = await mdjsToMd(markdown);
async function mdjsToCsf(markdown, filePath, options = {}) {
const markdownResult = await mdjsToMd(markdown, { ...options, filePath });
const jsCode = renameDefaultExport(markdownResult.jsCode, filePath);
const storiesCode = createStoriesCode(markdownResult.stories);

View File

@@ -1,6 +1,7 @@
/** @typedef {import('@mdjs/core').Story} Story */
/** @typedef {import('@mdjs/core').MarkdownResult} MarkdownResult */
/** @typedef {import('@mdjs/core').ParseResult} ParseResult */
/** @typedef {import('@mdjs/core').MdjsProcessPlugin} MdjsProcessPlugin */
const unified = require('unified');
const markdown = require('remark-parse');
@@ -91,27 +92,69 @@ function transformPropsHook() {
}
/**
* @param {string} markdownText
* @returns {Promise<MarkdownResult>}
* @param {string} name
*/
async function mdjsToMd(markdownText) {
const parser = unified()
.use(markdown)
.use(mdjsParse)
.use(mdjsStoryParse, {
storyTag: name => `<Story name="${name}"></Story>`,
previewStoryTag: name => `<Preview><Story name="${name}"></Story></Preview>`,
})
.use(transformPropsHook)
.use(mdSlug)
.use(mdStringify, {
function storyTag(name) {
return `<Story name="${name}"></Story>`;
}
/**
* @param {string} name
*/
function previewStoryTag(name) {
return `<Preview><Story name="${name}"></Story></Preview>`;
}
/** @type {MdjsProcessPlugin[]} */
const mdjsToMdPlugins = [
{ name: 'markdown', plugin: markdown },
{ name: 'mdjsParse', plugin: mdjsParse },
{
name: 'mdjsStoryParse',
plugin: mdjsStoryParse,
options: {
storyTag,
previewStoryTag,
},
},
{ name: 'transformPropsHook', plugin: transformPropsHook },
{ name: 'mdSlug', plugin: mdSlug },
{
name: 'mdStringify',
plugin: mdStringify,
options: {
handlers: {
code,
image,
break: hardBreak,
thematicBreak,
},
});
},
},
];
/**
* @param {MdjsProcessPlugin[]} plugins
* @param {string=} filePath
*/
// eslint-disable-next-line no-unused-vars
function defaultSetupMdjsPlugins(plugins, filePath = '') {
return plugins;
}
/**
* @param {string} markdownText
* @returns {Promise<MarkdownResult>}
*/
async function mdjsToMd(
markdownText,
{ filePath = '', setupMdjsPlugins = defaultSetupMdjsPlugins } = {},
) {
const plugins = setupMdjsPlugins(mdjsToMdPlugins, filePath);
const parser = unified();
for (const pluginObj of plugins) {
parser.use(pluginObj.plugin, pluginObj.options);
}
/** @type {unknown} */
const parseResult = await parser.process(markdownText);
const result = /** @type {ParseResult} */ (parseResult);

View File

@@ -1,3 +1,5 @@
/** @typedef {import('@mdjs/core').MdjsProcessPlugin} MdjsProcessPlugin */
const chai = require('chai');
// @ts-ignore
const chaiSnapshot = require('mocha-chai-snapshot');
@@ -41,4 +43,30 @@ describe('mdjsToCsf', () => {
// @ts-ignore
expect(output).to.matchSnapshot(this);
});
it('passes on filePath and options to mdjsToMd', async () => {
let resultPlugins = [];
let resultFilePath;
const input = [
'```js script',
'export default { title: "My docs" }',
'```',
'',
'# Title',
].join('\n');
/**
* @param {MdjsProcessPlugin[]} plugins
* @param {string} filePath
*/
function setupMdjsPlugins(plugins, filePath) {
resultPlugins = plugins;
resultFilePath = filePath;
return plugins;
}
await mdjsToCsf(input, '/foo.js', {
setupMdjsPlugins,
});
expect(resultPlugins.length).to.equal(6);
expect(resultFilePath).to.equal('/foo.js');
});
});

View File

@@ -142,4 +142,14 @@ describe('mdjsToMd', () => {
},
]);
});
it('allows to configure unified plugins via setupMdjsPlugins(plugins, filePath)', async () => {
const outputWithSlug = await mdjsToMd('# Title');
expect(outputWithSlug.html).to.equal('<h1 id="title">Title</h1>\n');
const outputWithoutSlug = await mdjsToMd('# Title', {
setupMdjsPlugins: plugins => plugins.filter(plugin => plugin.name !== 'mdSlug'),
});
expect(outputWithoutSlug.html).to.equal('<h1>Title</h1>\n');
});
});