From fa5f758b540b736e0b339e93e5813df87442e8d2 Mon Sep 17 00:00:00 2001 From: pengzhanbo Date: Thu, 9 Feb 2023 23:04:33 +0800 Subject: [PATCH] =?UTF-8?q?refactor(plugin-auto-frontmatter):=20=E9=87=8D?= =?UTF-8?q?=E6=9E=84=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/plugin-auto-frontmatter/README.md | 22 ++++---- packages/plugin-auto-frontmatter/package.json | 7 +-- .../plugin-auto-frontmatter/src/node/env.d.ts | 17 ++----- .../src/node/plugin.ts | 50 ++++++++++--------- .../src/node/readFiles.ts | 14 ++++-- .../plugin-auto-frontmatter/src/node/utils.ts | 5 ++ .../src/shared/index.ts | 8 +-- pnpm-lock.yaml | 20 +++++++- 8 files changed, 82 insertions(+), 61 deletions(-) create mode 100644 packages/plugin-auto-frontmatter/src/node/utils.ts diff --git a/packages/plugin-auto-frontmatter/README.md b/packages/plugin-auto-frontmatter/README.md index 89b1925c..8002176c 100644 --- a/packages/plugin-auto-frontmatter/README.md +++ b/packages/plugin-auto-frontmatter/README.md @@ -30,13 +30,15 @@ export default { ### options -`{ glob?: string | string[]; formatter: Formatter }` +`{ include?: string | string[]; exclude?: string | string[]; formatter: Formatter }` -- `glob` - glob 匹配字符串或数组,匹配需要自动生成 `frontmatter` 的 md文件。 - 默认预设为 `['**/*.md', '!.vuepress/', '!node_modules/']`。 - 自定义匹配将被合并到预设配置中 - example: `['blog/**']` +- `include` + include 匹配字符串或数组,匹配需要自动生成 `frontmatter` 的 md文件。 + 默认预设为 `['**/*.md']`。 + +- `exclude` + exclude 排除不需要的文件 + 默认预设为: `['!.vuepress/', '!node_modules/']` - `formatter` 配置`frontmatter`每个字段的生成规则。 @@ -58,7 +60,7 @@ export default { > type FormatterArray = { - glob: string + include: string formatter: FormatterObject }[] @@ -82,7 +84,7 @@ export default { const formatterArr: Formatter = [ { // 更精细化的匹配某个 md文件,支持glob 匹配字符串 - glob: '**/{README,index}.md', + include: '**/{README,index}.md', // formatter 仅对 glob命中的文件有效 formatter: { home(value, matter, file) { @@ -92,8 +94,8 @@ export default { { // 通配,如果文件没有被其他精细glob命中, // 则使用 通配 formatter - // 如果是数组,必须有且用一个 glob为 * 的 项 - glob: '*', + // 如果是数组,必须有且用一个 include 为 * 的 项 + include: '*', formatter: { title(title) { return title || '默认标题' diff --git a/packages/plugin-auto-frontmatter/package.json b/packages/plugin-auto-frontmatter/package.json index 07514ce8..75993e93 100644 --- a/packages/plugin-auto-frontmatter/package.json +++ b/packages/plugin-auto-frontmatter/package.json @@ -31,10 +31,11 @@ "dependencies": { "@vuepress/core": "2.0.0-beta.60", "@vuepress/shared": "2.0.0-beta.60", - "@vuepress/utils": "2.0.0-beta.60", "chokidar": "^3.5.3", - "glob-to-regexp": "^0.4.1", - "gray-matter": "^4.0.3" + "create-filter": "^1.0.0", + "fast-glob": "^3.2.12", + "gray-matter": "^4.0.3", + "json2yaml": "^1.1.0" }, "publishConfig": { "access": "public" diff --git a/packages/plugin-auto-frontmatter/src/node/env.d.ts b/packages/plugin-auto-frontmatter/src/node/env.d.ts index 643357e3..5591cb30 100644 --- a/packages/plugin-auto-frontmatter/src/node/env.d.ts +++ b/packages/plugin-auto-frontmatter/src/node/env.d.ts @@ -1,16 +1,5 @@ -declare module 'glob-to-regexp' { - interface GlobToRegexp { - ( - glob: string, - options?: { - globstar?: boolean - extended?: boolean - flags?: string - } - ): RegExp - } +declare module 'json2yaml' { + const result: any - const globToRegexp: GlobToRegexp - - export default globToRegexp + export default result } diff --git a/packages/plugin-auto-frontmatter/src/node/plugin.ts b/packages/plugin-auto-frontmatter/src/node/plugin.ts index a00e9058..3857b6b8 100644 --- a/packages/plugin-auto-frontmatter/src/node/plugin.ts +++ b/packages/plugin-auto-frontmatter/src/node/plugin.ts @@ -1,8 +1,9 @@ +import fs from 'node:fs' import type { Plugin } from '@vuepress/core' -import { fs } from '@vuepress/utils' import chokidar from 'chokidar' -import globToRegexp from 'glob-to-regexp' +import { createFilter } from 'create-filter' import grayMatter from 'gray-matter' +import jsonToYaml from 'json2yaml' import type { AutoFrontmatterOptions, FormatterArray, @@ -10,30 +11,31 @@ import type { MarkdownFile, } from '../shared/index.js' import { readMarkdown, readMarkdownList } from './readFiles.js' +import { ensureArray } from './utils.js' export const autoFrontmatterPlugin = ({ - glob = '', + include = ['**/*.{md,MD}'], + exclude = ['.vuepress/**/*', 'node_modules'], formatter = {}, }: AutoFrontmatterOptions = {}): Plugin => { - glob = glob ? (Array.isArray(glob) ? glob : [glob]) : [] - glob = ['**/*.{md,MD}', '!.vuepress/', '!node_modules/', ...glob] + include = ensureArray(include) + exclude = ensureArray(exclude) + + const globFilter = createFilter(include, exclude, { resolve: false }) const matterFormatter: FormatterArray = Array.isArray(formatter) ? formatter - : [{ glob: '*', formatter }] + : [{ include: '*', formatter }] const globFormatter: FormatterObject = - matterFormatter.find(({ glob }) => glob === '*')?.formatter || {} + matterFormatter.find(({ include }) => include === '*')?.formatter || {} const otherFormatters = matterFormatter - .filter(({ glob }) => glob !== '*') - .map(({ glob, formatter }) => { + .filter(({ include }) => include !== '*') + .map(({ include, formatter }) => { return { - glob, - regexp: globToRegexp(glob, { - globstar: true, - extended: true, - }), + include, + filter: createFilter(include), formatter, } }) @@ -42,26 +44,27 @@ export const autoFrontmatterPlugin = ({ const { filepath, relativePath } = file const formatter = - otherFormatters.find(({ regexp }) => regexp.test(relativePath)) - ?.formatter || globFormatter + otherFormatters.find(({ filter }) => filter(relativePath))?.formatter || + globFormatter const { data, content } = grayMatter(file.content) Object.keys(formatter).forEach((key) => { const value = formatter[key](data[key], data, file) data[key] = value ?? data[key] }) - const newContent = grayMatter.stringify({ content }, data) + const yaml = jsonToYaml + .stringify(data) + .replace(/\n\s{2}/g, '\n') + .replace(/"/g, '') + const newContent = `${yaml}---\n${content}` - fs.writeFileSync(filepath, newContent) + fs.writeFileSync(filepath, newContent, 'utf-8') } return { name: '@vuepress-plume/vuepress-plugin-auto-frontmatter', onInitialized: async (app) => { - const markdownList = await readMarkdownList( - app.dir.source(), - glob as string[] - ) + const markdownList = await readMarkdownList(app.dir.source(), globFilter) markdownList.forEach((file) => formatMarkdown(file)) }, onWatched: async (app, watchers) => { @@ -72,8 +75,7 @@ export const autoFrontmatterPlugin = ({ }) watcher.on('add', (relativePath) => { - if ((glob as string[]).some((_) => !globToRegexp(_).test(relativePath))) - return + if (!globFilter(relativePath)) return formatMarkdown(readMarkdown(app.dir.source(), relativePath)) }) diff --git a/packages/plugin-auto-frontmatter/src/node/readFiles.ts b/packages/plugin-auto-frontmatter/src/node/readFiles.ts index 48c17505..69e730c2 100644 --- a/packages/plugin-auto-frontmatter/src/node/readFiles.ts +++ b/packages/plugin-auto-frontmatter/src/node/readFiles.ts @@ -1,18 +1,22 @@ -import { fs, globby, path } from '@vuepress/utils' +import fs from 'node:fs' +import path from 'node:path' +import fg from 'fast-glob' import type { MarkdownFile } from '../shared/index.js' type MarkdownFileList = MarkdownFile[] export const readMarkdownList = async ( sourceDir: string, - glob: string[] + filter: (id: string) => boolean ): Promise => { - const files: string[] = await globby(glob, { + const files: string[] = await fg(['**/*.md'], { cwd: sourceDir, - gitignore: true, + ignore: ['node_modules/', '.vuepress/'], }) - return files.map((file) => readMarkdown(sourceDir, file)) + return files + .filter((file) => filter(file)) + .map((file) => readMarkdown(sourceDir, file)) } export const readMarkdown = ( diff --git a/packages/plugin-auto-frontmatter/src/node/utils.ts b/packages/plugin-auto-frontmatter/src/node/utils.ts new file mode 100644 index 00000000..981a3c67 --- /dev/null +++ b/packages/plugin-auto-frontmatter/src/node/utils.ts @@ -0,0 +1,5 @@ +export function ensureArray(thing: T | T[] | null | undefined): T[] { + if (Array.isArray(thing)) return thing + if (thing === null || thing === undefined) return [] + return [thing] +} diff --git a/packages/plugin-auto-frontmatter/src/shared/index.ts b/packages/plugin-auto-frontmatter/src/shared/index.ts index a99a7560..c02c2680 100644 --- a/packages/plugin-auto-frontmatter/src/shared/index.ts +++ b/packages/plugin-auto-frontmatter/src/shared/index.ts @@ -15,15 +15,17 @@ export type FormatterObject = Record< > export type FormatterArray = { - glob: string + include: string formatter: FormatterObject }[] export interface AutoFrontmatterOptions { /** - * glob string + * FilterPattern */ - glob?: string | string[] + include?: string | string[] + + exclude?: string | string[] /** * { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 68e98a33..da4335b3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -102,15 +102,19 @@ importers: '@vuepress/shared': 2.0.0-beta.60 '@vuepress/utils': 2.0.0-beta.60 chokidar: ^3.5.3 - glob-to-regexp: ^0.4.1 + create-filter: ^1.0.0 + fast-glob: ^3.2.12 gray-matter: ^4.0.3 + json2yaml: ^1.1.0 dependencies: '@vuepress/core': 2.0.0-beta.60 '@vuepress/shared': 2.0.0-beta.60 '@vuepress/utils': 2.0.0-beta.60 chokidar: 3.5.3 - glob-to-regexp: 0.4.1 + create-filter: 1.0.0 + fast-glob: 3.2.12 gray-matter: 4.0.3 + json2yaml: 1.1.0 packages/plugin-baidu-tongji: specifiers: @@ -10156,6 +10160,14 @@ packages: /json-stringify-safe/5.0.1: resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} + /json2yaml/1.1.0: + resolution: {integrity: sha512-/xse+m0SlllfZahQrNOelmLrFNfeZv4QG0QKlvg7VsPSGIxpB3X+ggLkdffwmI1DdQ3o9XjZX+K+EOI1epdKgg==} + engines: {node: '>= 0.2.0'} + hasBin: true + dependencies: + remedial: 1.0.8 + dev: false + /json5/1.0.1: resolution: {integrity: sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==} hasBin: true @@ -13369,6 +13381,10 @@ packages: engines: {node: '>= 0.10'} dev: false + /remedial/1.0.8: + resolution: {integrity: sha512-/62tYiOe6DzS5BqVsNpH/nkGlX45C/Sp6V+NtiN6JQNS1Viay7cWkazmRkrQrdFj2eshDe96SIQNIoMxqhzBOg==} + dev: false + /remove-trailing-separator/1.1.0: resolution: {integrity: sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==} dev: false