From 873a9a35f4ab5fd21f5147c563d86839b6a534d6 Mon Sep 17 00:00:00 2001 From: Fuma Nama Date: Mon, 4 Nov 2024 16:31:41 +0800 Subject: [PATCH] perf: parse matter only with files --- .changeset/eight-wombats-deliver.md | 5 ++++ src/check-external-url.ts | 14 +++++----- src/sample.ts | 22 ++++++++++++++-- src/scan.ts | 7 ----- src/validate.ts | 40 +++++++++++------------------ test/validate.test.ts | 18 ------------- 6 files changed, 48 insertions(+), 58 deletions(-) create mode 100644 .changeset/eight-wombats-deliver.md diff --git a/.changeset/eight-wombats-deliver.md b/.changeset/eight-wombats-deliver.md new file mode 100644 index 0000000..15df22a --- /dev/null +++ b/.changeset/eight-wombats-deliver.md @@ -0,0 +1,5 @@ +--- +'next-validate-link': patch +--- + +parse frontmatter only with files diff --git a/src/check-external-url.ts b/src/check-external-url.ts index 6a7aa6b..403835d 100644 --- a/src/check-external-url.ts +++ b/src/check-external-url.ts @@ -1,18 +1,20 @@ -export async function checkExternalUrl(url: string): Promise { +import type { ErrorReason } from './validate'; + +export async function checkExternalUrl( + url: string, +): Promise { const parsed = new URL(url); - if (parsed.hostname === 'localhost') return true; + if (parsed.hostname === 'localhost') return; const res = await fetch(parsed, { method: 'HEAD', }).catch(() => undefined); - if (!res) return false; + if (!res) return 'not-found'; if (!res.ok) { - if (res.status === 404) return false; + if (res.status === 404) return 'not-found'; console.warn(`${url} responded status ${res.status}, is it expected?`); } - - return true; } diff --git a/src/sample.ts b/src/sample.ts index e9fd0f1..d9a1b00 100644 --- a/src/sample.ts +++ b/src/sample.ts @@ -1,13 +1,31 @@ import path from 'node:path'; import fs from 'node:fs/promises'; +import FastGlob, { type Pattern } from 'fast-glob'; +import type { FileObject } from './validate'; +import matter from 'gray-matter'; -export async function readFileFromPath(file: string) { +export async function readFileFromPath(file: string): Promise< + FileObject & { + data?: object; + } +> { const content = await fs .readFile(path.resolve(file)) .then((res) => res.toString()); + const parsed = matter(content); + return { path: file, - content: content, + data: parsed.data, + content: parsed.content, }; } + +export async function readFiles( + patterns: Pattern | Pattern[], +): Promise { + const files = await FastGlob(patterns); + + return await Promise.all(files.map(readFileFromPath)); +} diff --git a/src/scan.ts b/src/scan.ts index cc6321f..2ea70f5 100644 --- a/src/scan.ts +++ b/src/scan.ts @@ -37,13 +37,6 @@ type UrlMeta = { const defaultMeta = {}; const defaultPopulate: PopulateParams[string] = [{}]; -const defaultPopulateOptional: PopulateParams[string] = [ - {}, - { - // case when it's empty - value: [], - }, -]; export async function scanURLs(options: ScanOptions): Promise { async function getFiles() { diff --git a/src/validate.ts b/src/validate.ts index ea25248..4d8b065 100644 --- a/src/validate.ts +++ b/src/validate.ts @@ -1,11 +1,10 @@ -import { readFile } from 'node:fs/promises'; import { remark } from 'remark'; import { visit } from 'unist-util-visit'; import type { ScanResult } from '@/scan'; -import path from 'node:path'; -import matter from 'gray-matter'; +import * as path from 'node:path'; import { checkExternalUrl } from './check-external-url'; import remarkGfm from 'remark-gfm'; +import { readFileFromPath } from './sample'; const processor = remark().use(remarkGfm); @@ -14,8 +13,8 @@ export type ValidateError = { detected: DetectedError[]; }; -type ErrorReason = 'not-found' | 'invalid-fragment' | 'invalid-query'; -type DetectedError = [ +export type ErrorReason = 'not-found' | 'invalid-fragment' | 'invalid-query'; +export type DetectedError = [ url: string, line: number, column: number, @@ -50,12 +49,12 @@ export type ValidateConfig = { checkExternal?: boolean; }; -type File = - | string - | { - path: string; - content: string; - }; +export type FileObject = { + path: string; + content: string; + + data?: object; +}; /** * Validate markdown files @@ -64,21 +63,14 @@ type File = * @param config - configurations */ export async function validateFiles( - files: File[], + files: (string | FileObject)[], config: ValidateConfig, ): Promise { const mdExtensions = ['.md', '.mdx']; - async function run(file: File): Promise { + async function run(file: string | FileObject): Promise { const finalFile = - typeof file === 'string' - ? { - path: file, - content: await readFile(file) - .then((res) => res.toString()) - .catch(() => ''), - } - : file; + typeof file === 'string' ? await readFileFromPath(file) : file; if (!mdExtensions.includes(path.extname(finalFile.path))) { console.warn( @@ -103,7 +95,7 @@ export async function validateMarkdown( content: string, config: ValidateConfig, ) { - const tree = processor.parse({ value: matter({ content }).content }); + const tree = processor.parse({ value: content }); const detected: DetectedError[] = []; const tasks: Promise[] = []; @@ -132,9 +124,7 @@ export async function detect( ): Promise { if (href.match(/https?:\/\//)) { if (config.checkExternal) { - const isValid = await checkExternalUrl(href); - - if (!isValid) return 'not-found'; + return await checkExternalUrl(href); } return; diff --git a/test/validate.test.ts b/test/validate.test.ts index 2480406..7a0689a 100644 --- a/test/validate.test.ts +++ b/test/validate.test.ts @@ -45,24 +45,6 @@ test('validate links: valid', async () => { ).lengthOf(0, 'no error'); }); -test('validate links: valid with frontmatter', async () => { - expect( - await validateFiles( - [ - { - path: 'a.md', - content: `--- -title: hello world [hello](/sfd) ---- - -[hello](/)`, - }, - ], - { scanned }, - ), - ).lengthOf(0, 'no error'); -}); - test('validate links: not found', async () => { expect( await validateFiles(