Skip to content

Commit

Permalink
feat: write out reusable content as tags (#827)
Browse files Browse the repository at this point in the history
* feat: write out reusable content as tags

* fix: test
  • Loading branch information
kellyjosephprice authored Dec 6, 2023
1 parent 44e20c5 commit 5b3e0e2
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 30 deletions.
26 changes: 19 additions & 7 deletions __tests__/flavored-compilers/reusable-content.test.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,44 @@
import { mdast, md } from '../../index';

describe('reusable content compiler', () => {
it('writes an undefined reusable content block back to markdown', () => {
it('writes an undefined reusable content block as a tag', () => {
const doc = '<Undefined />';
const tree = mdast(doc);

expect(md(tree)).toMatch(doc);
});

it('writes a defined reusable content block back to markdown', () => {
const reusableContent = {
it('writes a defined reusable content block as a tag', () => {
const tags = {
Defined: '# Whoa',
};
const doc = '<Defined />';
const tree = mdast(doc, { reusableContent });
const tree = mdast(doc, { reusableContent: { tags } });

expect(tree.children[0].children[0].type).toBe('heading');
expect(md(tree)).toMatch(doc);
});

it('writes a defined reusable content block with multiple words back to markdown', () => {
const reusableContent = {
it('writes a defined reusable content block with multiple words as a tag', () => {
const tags = {
MyCustomComponent: '# Whoa',
};
const doc = '<MyCustomComponent />';
const tree = mdast(doc, { reusableContent });
const tree = mdast(doc, { reusableContent: { tags } });

expect(tree.children[0].children[0].type).toBe('heading');
expect(md(tree)).toMatch(doc);
});

describe('writeTags = false', () => {
it('writes a reusable content block as content', () => {
const tags = {
Defined: '# Whoa',
};
const doc = '<Defined />';
const string = md(doc, { reusableContent: { tags, writeTags: false } });

expect(string).toBe('# Whoa\n');
});
});
});
7 changes: 5 additions & 2 deletions __tests__/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ test('it should have the proper utils exports', () => {
compatibilityMode: false,
copyButtons: true,
correctnewlines: false,
disableReusableContent: false,
lazyImages: true,
markdownOptions: {
fences: true,
Expand All @@ -28,7 +27,11 @@ test('it should have the proper utils exports', () => {
paddedTable: true,
},
normalize: true,
reusableContent: {},
reusableContent: {
disabled: false,
tags: {},
writeTags: true,
},
safeMode: false,
settings: { position: false },
theme: 'light',
Expand Down
16 changes: 8 additions & 8 deletions __tests__/transformers/reusable-content.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { mdast } from '../../index';

describe('reusable content transfomer', () => {
it('should replace a reusable content block if the block is provided', () => {
const reusableContent = {
const tags = {
Test: `
# Test
Expand All @@ -17,15 +17,15 @@ Before
After
`;

const tree = mdast(md, { reusableContent });
const tree = mdast(md, { reusableContent: { tags } });

expect(tree.children[0].children[0].value).toBe('Before');
expect(tree.children[1]).toMatchSnapshot();
expect(tree.children[2].children[0].value).toBe('After');
});

it('should replace a reusable content block with multiple words if the block is provided', () => {
const reusableContent = {
const tags = {
MyCustomComponent: `
# Test
Expand All @@ -34,7 +34,7 @@ After
};
const md = '<MyCustomComponent />';

const tree = mdast(md, { reusableContent });
const tree = mdast(md, { reusableContent: { tags } });

expect(tree.children[0]).toMatchSnapshot();
});
Expand All @@ -48,22 +48,22 @@ After
});

it('does not expand reusable content recursively', () => {
const reusableContent = {
const tags = {
Test: '<Test />',
};
const md = '<Test />';
const tree = mdast(md, { reusableContent });
const tree = mdast(md, { reusableContent: { tags } });

expect(tree.children[0].children[0].type).toBe('reusable-content');
expect(tree.children[0].children[0].children).toStrictEqual([]);
});

it('does not replace reusable content if it is disabled', () => {
const reusableContent = {
const tags = {
Test: '<Test />',
};
const md = '<Test />';
const tree = mdast(md, { disableReusableContent: true, reusableContent });
const tree = mdast(md, { reusableContent: { tags, disabled: true } });

expect(tree.children[0].type).toBe('html');
expect(tree.children[0].value).toBe('<Test />');
Expand Down
12 changes: 7 additions & 5 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,15 @@ export const utils = {
* blocks recursively.
*/
const parseReusableContent = ({ reusableContent, ...opts }) => {
if (opts.disableReusableContent) return [null, opts];
if (reusableContent.disabled) return [{ disabled: true, writeTags: true }, opts];

const parsedReusableContent = Object.entries(reusableContent).reduce((memo, [name, content]) => {
const tags = Object.entries(reusableContent.tags).reduce((memo, [name, content]) => {
// eslint-disable-next-line no-use-before-define
memo[name] = mdast(content, opts).children;
return memo;
}, {});

return [parsedReusableContent, opts];
return [{ ...reusableContent, tags }, opts];
};

/**
Expand Down Expand Up @@ -290,10 +290,12 @@ export function astToPlainText(node, opts = {}) {
/**
* compile mdast to ReadMe-flavored markdown
*/
export function md(tree, opts = {}) {
if (!tree) return null;
export function md(treeOrString, opts = {}) {
if (!treeOrString) return null;
[, opts] = setup('', opts);

const tree = typeof treeOrString === 'string' ? mdast(treeOrString, opts) : treeOrString;

return processor(opts).use(remarkStringify, opts.markdownOptions).use(customCompilers).stringify(tree);
}

Expand Down
20 changes: 16 additions & 4 deletions options.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ const options = {
compatibilityMode: false,
copyButtons: true,
correctnewlines: false,
disableReusableContent: false,
markdownOptions: {
fences: true,
commonmark: true,
Expand All @@ -15,7 +14,11 @@ const options = {
},
normalize: true,
lazyImages: true,
reusableContent: {},
reusableContent: {
tags: {},
disabled: false,
writeTags: true,
},
safeMode: false,
settings: {
position: false,
Expand Down Expand Up @@ -77,8 +80,17 @@ const disableTokenizers = {
},
};

const parseOptions = (userOpts = {}) => {
let opts = { ...options, ...userOpts };
const parseOptions = ({ markdownOptions = {}, reusableContent = {}, settings = {}, ...userOpts } = {}) => {
let opts = {
...options,
markdownOptions: { ...options.markdownOptions, ...markdownOptions },
reusableContent: {
...options.reusableContent,
...reusableContent,
},
settings: { ...options.settings, ...settings },
...userOpts,
};

if (opts.disableTokenizers in disableTokenizers) {
opts = { ...opts, ...disableTokenizers[opts.disableTokenizers] };
Expand Down
5 changes: 4 additions & 1 deletion processor/compile/reusable-content.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { type } from '../transform/reusable-content';

export default function ReusableContentCompiler() {
const { writeTags = true } = this.data('reusableContent') || {};
const { Compiler } = this;
const { visitors } = Compiler.prototype;

visitors[type] = node => `<${node.tag} />`;
visitors[type] = function (node) {
return writeTags ? `<${node.tag} />` : this.block(node);
};
}
6 changes: 3 additions & 3 deletions processor/transform/reusable-content.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ export const type = 'reusable-content';
const regexp = /^\s*<(?<tag>[A-Z]\S+)\s*\/>\s*$/;

const reusableContentTransformer = function () {
const reusableContent = this.data('reusableContent');
if (!reusableContent) return () => undefined;
const { tags, disabled } = this.data('reusableContent');
if (disabled) return () => undefined;

return tree => {
visit(tree, 'html', (node, index, parent) => {
Expand All @@ -18,7 +18,7 @@ const reusableContentTransformer = function () {
const block = {
type,
tag,
children: tag in reusableContent ? reusableContent[tag] : [],
children: tag in tags ? tags[tag] : [],
};

parent.children[index] = block;
Expand Down

0 comments on commit 5b3e0e2

Please sign in to comment.