From e1ed13d6c99bd2b9997f7bc3fb26cae7bd8c25a0 Mon Sep 17 00:00:00 2001 From: Thomas Allmer Date: Sat, 23 May 2020 09:37:37 +0200 Subject: [PATCH] feat: allow to set plugins for mdjs processing --- .../demoing-storybook/demo/.storybook/main.js | 2 + packages/demoing-storybook/src/start/cli.js | 9 ++- .../src/start/readCommandLineArgs.js | 1 + .../createMdjsToCsfTransformer.js | 33 +++++++++ .../transformers/mdjsToCsfTransformer.js | 31 -------- .../demo/.storybook/main.js | 15 +--- .../src/mdjsToCsf.js | 5 +- .../src/mdjsToMd.js | 71 +++++++++++++++---- .../test/mdjsToCsf.test.js | 28 ++++++++ .../test/mdjsToMd.test.js | 10 +++ 10 files changed, 142 insertions(+), 63 deletions(-) create mode 100644 packages/demoing-storybook/src/start/transformers/createMdjsToCsfTransformer.js delete mode 100644 packages/demoing-storybook/src/start/transformers/mdjsToCsfTransformer.js diff --git a/packages/demoing-storybook/demo/.storybook/main.js b/packages/demoing-storybook/demo/.storybook/main.js index f1b6ddc9..6c24f231 100644 --- a/packages/demoing-storybook/demo/.storybook/main.js +++ b/packages/demoing-storybook/demo/.storybook/main.js @@ -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() { diff --git a/packages/demoing-storybook/src/start/cli.js b/packages/demoing-storybook/src/start/cli.js index 118521e2..9ca03d9e 100755 --- a/packages/demoing-storybook/src/start/cli.js +++ b/packages/demoing-storybook/src/start/cli.js @@ -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, diff --git a/packages/demoing-storybook/src/start/readCommandLineArgs.js b/packages/demoing-storybook/src/start/readCommandLineArgs.js index 1a64c1b7..85cd5ef1 100644 --- a/packages/demoing-storybook/src/start/readCommandLineArgs.js +++ b/packages/demoing-storybook/src/start/readCommandLineArgs.js @@ -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 diff --git a/packages/demoing-storybook/src/start/transformers/createMdjsToCsfTransformer.js b/packages/demoing-storybook/src/start/transformers/createMdjsToCsfTransformer.js new file mode 100644 index 00000000..76fb0771 --- /dev/null +++ b/packages/demoing-storybook/src/start/transformers/createMdjsToCsfTransformer.js @@ -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; diff --git a/packages/demoing-storybook/src/start/transformers/mdjsToCsfTransformer.js b/packages/demoing-storybook/src/start/transformers/mdjsToCsfTransformer.js deleted file mode 100644 index deefd526..00000000 --- a/packages/demoing-storybook/src/start/transformers/mdjsToCsfTransformer.js +++ /dev/null @@ -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; diff --git a/packages/storybook-addon-markdown-docs/demo/.storybook/main.js b/packages/storybook-addon-markdown-docs/demo/.storybook/main.js index dfb2d426..66122240 100644 --- a/packages/storybook-addon-markdown-docs/demo/.storybook/main.js +++ b/packages/storybook-addon-markdown-docs/demo/.storybook/main.js @@ -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 }; - } - }, - ], }, }; diff --git a/packages/storybook-addon-markdown-docs/src/mdjsToCsf.js b/packages/storybook-addon-markdown-docs/src/mdjsToCsf.js index 0e1be141..fd1c001b 100644 --- a/packages/storybook-addon-markdown-docs/src/mdjsToCsf.js +++ b/packages/storybook-addon-markdown-docs/src/mdjsToCsf.js @@ -7,10 +7,11 @@ const { jsxToJs } = require('./jsxToJs'); /** * @param {string} markdown * @param {string} filePath + * @param {object} options * @returns {Promise} */ -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); diff --git a/packages/storybook-addon-markdown-docs/src/mdjsToMd.js b/packages/storybook-addon-markdown-docs/src/mdjsToMd.js index a7ea70c0..b85ce0c4 100644 --- a/packages/storybook-addon-markdown-docs/src/mdjsToMd.js +++ b/packages/storybook-addon-markdown-docs/src/mdjsToMd.js @@ -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} + * @param {string} name */ -async function mdjsToMd(markdownText) { - const parser = unified() - .use(markdown) - .use(mdjsParse) - .use(mdjsStoryParse, { - storyTag: name => ``, - previewStoryTag: name => ``, - }) - .use(transformPropsHook) - .use(mdSlug) - .use(mdStringify, { +function storyTag(name) { + return ``; +} + +/** + * @param {string} name + */ +function previewStoryTag(name) { + return ``; +} + +/** @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} + */ +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); diff --git a/packages/storybook-addon-markdown-docs/test/mdjsToCsf.test.js b/packages/storybook-addon-markdown-docs/test/mdjsToCsf.test.js index 1b87c1d1..ad233bed 100644 --- a/packages/storybook-addon-markdown-docs/test/mdjsToCsf.test.js +++ b/packages/storybook-addon-markdown-docs/test/mdjsToCsf.test.js @@ -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'); + }); }); diff --git a/packages/storybook-addon-markdown-docs/test/mdjsToMd.test.js b/packages/storybook-addon-markdown-docs/test/mdjsToMd.test.js index 52850a6e..9d051c2b 100644 --- a/packages/storybook-addon-markdown-docs/test/mdjsToMd.test.js +++ b/packages/storybook-addon-markdown-docs/test/mdjsToMd.test.js @@ -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('

Title

\n'); + + const outputWithoutSlug = await mdjsToMd('# Title', { + setupMdjsPlugins: plugins => plugins.filter(plugin => plugin.name !== 'mdSlug'), + }); + expect(outputWithoutSlug.html).to.equal('

Title

\n'); + }); });