Skip to content

Commit

Permalink
feat(plugin-sfc): add sfc plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
meteorlxy committed Jun 23, 2022
1 parent 9e36bdc commit 1eb4e0a
Show file tree
Hide file tree
Showing 8 changed files with 235 additions and 0 deletions.
40 changes: 40 additions & 0 deletions packages/plugin-sfc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# @mdit-vue/plugin-sfc

[![npm](https://badgen.net/npm/v/@mdit-vue/plugin-sfc)](https://www.npmjs.com/package/@mdit-vue/plugin-sfc)

A [markdown-it](https://github.com/markdown-it/markdown-it) plugin to help transforming markdown to [Vue SFC](https://vuejs.org/guide/scaling-up/sfc.html).

- Extract all SFC blocks except `<template>` from rendered result to markdown-it `env.sfcBlocks`.
- Support extracting custom blocks.

## Install

```sh
npm i @mdit-vue/plugin-sfc
```

## Usage

This plugin will only take effects when the `html` option of markdown-it is enabled:

```ts
import MarkdownIt from 'markdown-it';
import { sfcPlugin } from '@mdit-vue/plugin-sfc';
import type { MarkdownItEnv } from '@mdit-vue/shared';

const md = MarkdownIt({ html: true }).use(sfcPlugin);
const env: MarkdownItEnv = {};

const rendered = md.render(
`\
# foo
<script>
console.log('bar')
</script>
`,
env,
);

const sfc = `<template>${rendered}</template>${env.sfcBlocks}`;
```
12 changes: 12 additions & 0 deletions packages/plugin-sfc/build.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { defineBuildConfig } from 'unbuild';

export default defineBuildConfig({
clean: true,
declaration: true,
entries: ['src/index'],
externals: ['markdown-it'],
rollup: {
emitCJS: true,
inlineDependencies: false,
},
});
48 changes: 48 additions & 0 deletions packages/plugin-sfc/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"name": "@mdit-vue/plugin-sfc",
"version": "0.0.0",
"description": "A markdown-it plugin to help transforming markdown tu vue sfc",
"keywords": [
"markdown-it",
"markdown-it-plugin",
"vue",
"sfc"
],
"homepage": "https://github.com/mdit-vue",
"bugs": {
"url": "https://github.com/mdit-vue/mdit-vue/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/mdit-vue/mdit-vue.git"
},
"license": "MIT",
"author": "meteorlxy <meteor.lxy@foxmail.com>",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
}
},
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"scripts": {
"build": "unbuild",
"test": "vitest"
},
"dependencies": {
"@mdit-vue/shared": "workspace:*",
"@types/markdown-it": "^12.2.3",
"markdown-it": "^13.0.1"
},
"devDependencies": {
"c8": "^7.11.3",
"unbuild": "^0.7.4",
"vitest": "^0.15.2"
},
"publishConfig": {
"access": "public"
}
}
2 changes: 2 additions & 0 deletions packages/plugin-sfc/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './sfc-plugin';
export * from './types';
40 changes: 40 additions & 0 deletions packages/plugin-sfc/src/sfc-plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type { MarkdownItEnv } from '@mdit-vue/shared';
import type { PluginWithOptions } from 'markdown-it';
import type { SfcPluginOptions } from './types';

/**
* Avoid rendering vue SFC script / style / custom blocks
*
* Extract them into env
*/
export const sfcPlugin: PluginWithOptions<SfcPluginOptions> = (
md,
{ customBlocks = [] }: SfcPluginOptions = {},
): void => {
// extract `<script>`, `<style>` and other user defined custom blocks
const sfcBlocks = Array.from(new Set(['script', 'style', ...customBlocks]));
const sfcBlocksRegexp = new RegExp(
`^<(${sfcBlocks.join('|')})(?=(\\s|>|$))`,
'i',
);

const rawRule = md.renderer.rules.html_block!;
md.renderer.rules.html_block = (
tokens,
idx,
options,
env: MarkdownItEnv,
self,
) => {
const content = tokens[idx].content;
const extractedSfcBlocks = env.sfcBlocks || (env.sfcBlocks = []);

// extract sfc blocks to env and do not render them
if (sfcBlocksRegexp.test(content.trim())) {
extractedSfcBlocks.push(content);
return '';
}

return rawRule(tokens, idx, options, env, self);
};
};
15 changes: 15 additions & 0 deletions packages/plugin-sfc/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* Options of @mdit-vue/plugin-sfc
*/
export interface SfcPluginOptions {
/**
* Custom blocks to be extracted
*/
customBlocks?: string[];
}

declare module '@mdit-vue/shared' {
interface MarkdownItEnv {
sfcBlocks?: string[];
}
}
61 changes: 61 additions & 0 deletions packages/plugin-sfc/tests/sfc-plugin.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import type { MarkdownItEnv } from '@mdit-vue/shared';
import MarkdownIt from 'markdown-it';
import { describe, expect, it } from 'vitest';
import { sfcPlugin } from '../src';

const source = `\
# hello vuepress
{{ msg }}
<docs>
extra hoisted tag
</docs>
<script>
export default {
setup() {
return {
msg: 'foobar'
}
}
}
</script>
<style lang="stylus">
.h1
red
</style>
`;

const extractedScript = source.replace(/^.*(<script>.*<\/script>).*$/s, '$1\n');
const extractedStyle = source.replace(/^.*(<style .*<\/style>).*$/s, '$1\n');
const extractedDocs = source.replace(/^.*(<docs>.*<\/docs>).*$/s, '$1\n');

describe('@mdit-vue/plugin-sfc > sfc-plugin', () => {
it('should hoist script and style tags', () => {
const md = MarkdownIt({ html: true }).use(sfcPlugin);
const env: MarkdownItEnv = {};

const rendered = md.render(source, env);

expect(env.sfcBlocks).toEqual([extractedScript, extractedStyle]);
expect(/<(script|style)\b/.test(rendered)).toBe(false);
});

it('should hoist docs tags correctly', () => {
const md = MarkdownIt({ html: true }).use(sfcPlugin, {
customBlocks: ['docs'],
});
const env: MarkdownItEnv = {};

const rendered = md.render(source, env);

expect(env.sfcBlocks).toEqual([
extractedDocs,
extractedScript,
extractedStyle,
]);
expect(/<(script|style|docs)\b/.test(rendered)).toBe(false);
});
});
17 changes: 17 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 1eb4e0a

Please sign in to comment.