import path from 'path' ; import type { Rule } from 'eslint' ; import type { ParserOptions } from 'eslint-mdx' ; import { DEFAULT_EXTENSIONS , MARKDOWN_EXTENSIONS , getPhysicalFilename , getRemarkProcessor , } from 'eslint-mdx' ; import { createSyncFn } from 'synckit' ; import type { FrozenProcessor } from 'unified' ; import type { VFile , VFileOptions } from 'vfile' ; import vfile from 'vfile' ; import type { VFileMessage } from 'vfile-message' ; import type { RemarkLintMessage } from './types' ; const workerPath = require . resolve ( '../worker' ) ; const lazyRemark = { get processSync ( ) { return createSyncFn ( workerPath ) as ( fileOptions : VFileOptions , physicalFilename : string , isMdx : boolean ) => { messages : VFile [ 'messages' ] ; content : string ; } ; } , } ; const brokenCache = new WeakMap < FrozenProcessor , true > ( ) ; export const remark = { meta : { type : 'layout' , docs : { description : 'Linter integration with remark plugins' , category : 'Stylistic Issues' , recommended : true , } , fixable : 'code' , } , create ( context ) { const filename = context . getFilename ( ) ; const extname = path . extname ( filename ) ; const sourceCode = context . getSourceCode ( ) ; const options = context . parserOptions as ParserOptions ; const isMdx = [ ... DEFAULT_EXTENSIONS , ... ( options . extensions || [ ] ) , ] . includes ( extname ) ; const isMarkdown = [ ... MARKDOWN_EXTENSIONS , ... ( options . markdownExtensions || [ ] ) , ] . includes ( extname ) ; return { Program ( node ) { if ( ! isMdx && ! isMarkdown ) { return ; } const physicalFilename = getPhysicalFilename ( filename ) ; const sourceText = sourceCode . getText ( node ) ; const remarkProcessor = getRemarkProcessor ( physicalFilename , isMdx ) ; const fileOptions = { path : physicalFilename , contents : sourceText , } ; const file = vfile ( fileOptions ) ; let fixedText ; let broken = brokenCache . get ( remarkProcessor ) ; if ( broken ) { const { messages , content } = lazyRemark . processSync ( fileOptions , physicalFilename , isMdx ) ; file . messages = messages ; fixedText = content ; } else { try { remarkProcessor . processSync ( file ) ; } catch ( err ) { if ( ( err as Error ) . message === '`processSync` finished async. Use `process` instead' ) { brokenCache . set ( remarkProcessor , ( broken = true ) ) ; const { messages , content } = lazyRemark . processSync ( fileOptions , physicalFilename , isMdx ) ; file . messages = messages ; fixedText = content ; } else if ( ! file . messages . includes ( err as VFileMessage ) ) { file . message ( err ) . fatal = true ; } } } if ( ! broken ) { fixedText = file . toString ( ) ; } let fixed = 0 ; for ( const { source , reason , ruleId , fatal , location : { start , end } , } of file . messages ) { const severity = fatal ? 2 : fatal == null ? 0 : 1 ; if ( ! severity ) { continue ; } const message = { reason , source , ruleId , severity , } ; context . report ( { message : JSON . stringify ( message ) , loc : { start : { ... start , column : start . column - 1 , } , end : { ... end , column : end . column - 1 , } , } , node , fix : fixedText === sourceText ? null : ( ) => fixed ++ ? null : { range : [ 0 , sourceText . length ] , text : fixedText , } , } ) ; } } , } ; } , } ;