Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
".": "./dist/index.mjs",
"./package.json": "./package.json"
},
"main": "./dist/index.mjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.mts",
"files": [
"dist"
Expand Down
7 changes: 3 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,13 @@ export const plugin: ESLint.Plugin = {
},
}

const allRuleEntries: Array<[string, Linter.RuleEntry]> = Object.keys(rules)
.map(ruleName => [`md-style/${ruleName}`, 'error'])

const recommendedRules: Partial<Linter.RulesRecord> = {
'md-style/valid-heading-anchor': 'error',
'md-style/space-around-inline-element': 'error',
}
const allRules: Partial<Linter.RulesRecord> = Object.fromEntries(allRuleEntries)
const allRules: Partial<Linter.RulesRecord>
= Object.fromEntries(Object.keys(rules)
.map(ruleName => [`md-style/${ruleName}`, 'error']))

interface PluginConfigMap {
recommended: Linter.Config
Expand Down
4 changes: 3 additions & 1 deletion src/rules/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import spaceAroundInlineElement from './space-around-inline-element/index'
import spaceAroundNumber from './space-around-number'
import spaceAroundWord from './space-around-word'
import validHeadingAnchor from './valid-heading-anchor/index'

export const rules = {
'space-around-inline-element': spaceAroundInlineElement,
'valid-heading-anchor': validHeadingAnchor,
'space-around-number': spaceAroundNumber,
'space-around-word': spaceAroundWord,
'valid-heading-anchor': validHeadingAnchor,
}
2 changes: 1 addition & 1 deletion src/rules/space-around-inline-element/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { InvalidTestCase, ValidTestCase } from 'eslint-vitest-rule-tester'
import markdown from '@eslint/markdown'
import { run } from 'eslint-vitest-rule-tester'
import { INLINE_SPACE_MESSAGE_IDS as MESSAGE_IDS } from '@/utils/inline-element'
import { MESSAGE_IDS } from '@/rules/space-around-inline-element'
import rule, { RULE_NAME } from './index'

const valid: ValidTestCase[] = [
Expand Down
94 changes: 51 additions & 43 deletions src/rules/space-around-inline-element/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,19 @@ import type { RuleContext, ValueOf } from '@/types'
import type { InlineElement } from '@/types/inline-element'
import { createRule } from '@/utils'
import { getNodeContext, getNodePosition, isNestedInlineElement } from '@/utils/ast'
import { INLINE_SPACE_MESSAGE_IDS as MESSAGE_IDS, validateSpace } from '@/utils/inline-element'
import { validateSpace } from '@/utils/inline-element'
import { getSpaceContext } from '@/utils/space'

export const RULE_NAME = 'space-around-inline-element'

export const MESSAGE_IDS = {
missingSpaceBefore: 'missingSpaceBefore',
missingSpaceAfter: 'missingSpaceAfter',
multipleSpacesBefore: 'multipleSpacesBefore',
multipleSpacesAfter: 'multipleSpacesAfter',
multipleSpacesAfterPunctuation: 'multipleSpacesAfterPunctuation',
unexpectedSpaceBefore: 'unexpectedSpaceBefore',
unexpectedSpaceAfter: 'unexpectedSpaceAfter',
} as const
type MessageIds = ValueOf<typeof MESSAGE_IDS>
type Options = []

Expand All @@ -18,6 +26,47 @@ const BEFORE_INLINE_ELEMENT_MESSAGE_IDS = new Set<MessageIds>([
MESSAGE_IDS.unexpectedSpaceBefore,
])

export default createRule<Options, MessageIds>({
name: RULE_NAME,
meta: {
type: 'layout',
docs: {
description: 'Enforce spacing around Markdown inline elements.',
},
messages: {
missingSpaceBefore: 'A space is required before the inline element.',
missingSpaceAfter: 'A space is required after the inline element.',
multipleSpacesBefore: 'Use exactly one space before the inline element.',
multipleSpacesAfter: 'Use exactly one space after the inline element.',
multipleSpacesAfterPunctuation: 'Use one space after punctuation.',
unexpectedSpaceBefore: 'Do not add a space between punctuation and the inline element.',
unexpectedSpaceAfter: 'Do not add a space between the inline element and punctuation.',
},
fixable: 'whitespace',
schema: [],
},
defaultOptions: [],
create(context) {
return {
link(node: Link) {
checkInlineElement(context, node)
},
image(node: Image) {
checkInlineElement(context, node)
},
inlineCode(node: InlineCode) {
checkInlineElement(context, node)
},
emphasis(node: Emphasis) {
checkInlineElement(context, node)
},
strong(node: Strong) {
checkInlineElement(context, node)
},
}
},
})

/**
* Checks one selected inline element and reports the fix range around it.
*/
Expand Down Expand Up @@ -63,44 +112,3 @@ function checkInlineElement(context: RuleContext<MessageIds, Options>, node: Inl
})
}
}

export default createRule<Options, MessageIds>({
name: RULE_NAME,
meta: {
type: 'layout',
docs: {
description: 'Enforce spacing around Markdown inline elements.',
},
messages: {
missingSpaceBefore: 'A space is required before the inline element.',
missingSpaceAfter: 'A space is required after the inline element.',
multipleSpacesBefore: 'Use exactly one space before the inline element.',
multipleSpacesAfter: 'Use exactly one space after the inline element.',
multipleSpacesAfterPunctuation: 'Use one space after punctuation.',
unexpectedSpaceBefore: 'Do not add a space between punctuation and the inline element.',
unexpectedSpaceAfter: 'Do not add a space between the inline element and punctuation.',
},
fixable: 'whitespace',
schema: [],
},
defaultOptions: [],
create(context) {
return {
link(node: Link) {
checkInlineElement(context, node)
},
image(node: Image) {
checkInlineElement(context, node)
},
inlineCode(node: InlineCode) {
checkInlineElement(context, node)
},
emphasis(node: Emphasis) {
checkInlineElement(context, node)
},
strong(node: Strong) {
checkInlineElement(context, node)
},
}
},
})
76 changes: 76 additions & 0 deletions src/rules/space-around-number/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import type { InvalidTestCase, ValidTestCase } from 'eslint-vitest-rule-tester'
import markdown from '@eslint/markdown'
import { run } from 'eslint-vitest-rule-tester'
import { SPACE_MESSAGE_IDS as MESSAGE_IDS } from '@/utils/space'
import rule, { RULE_NAME } from './index'

const valid: ValidTestCase[] = [
{
description: 'number between chinese text with spaces',
code: '支持 123 个规则',
},
{
description: 'decimal and percent number between chinese text with spaces',
code: '提升 4.0% 效率',
},
{
description: 'number adjacent to punctuation is allowed',
code: '版本:2.0,现已发布。',
},
]

const invalid: InvalidTestCase[] = [
{
description: 'reports a missing space before a number after CJK text',
code: '支持123 个规则',
output: '支持 123 个规则',
errors: [{ messageId: MESSAGE_IDS.missingSpaceBefore }],
},
{
description: 'reports a missing space after a number before CJK text',
code: '支持 123个规则',
output: '支持 123 个规则',
errors: [{ messageId: MESSAGE_IDS.missingSpaceAfter }],
},
{
description: 'reports missing spaces on both sides of an embedded number',
code: '支持123个规则',
output: '支持 123 个规则',
errors: [{ messageId: MESSAGE_IDS.missingSpacesAround }],
},
{
description: 'reports missing spaces around decimal percent numbers',
code: '提升4.0%效率',
output: '提升 4.0% 效率',
errors: [{ messageId: MESSAGE_IDS.missingSpacesAround }],
},
{
description: 'reports an unexpected space before a number after CJK text',
code: '支持 123 个规则',
output: '支持 123 个规则',
errors: [{ messageId: MESSAGE_IDS.unexpectedSpaceBefore }],
},
{
description: 'reports an unexpected space after a number before CJK text',
code: '支持 123 个规则',
output: '支持 123 个规则',
errors: [{ messageId: MESSAGE_IDS.unexpectedSpaceAfter }],
},
{
description: 'reports unexpected spaces on both sides of a number',
code: '支持 123 个规则',
output: '支持 123 个规则',
errors: [{ messageId: MESSAGE_IDS.unexpectedSpaceAround }],
},
]

run({
name: RULE_NAME,
rule,
valid,
invalid,
configs: {
plugins: { markdown },
language: 'markdown/gfm',
},
})
49 changes: 49 additions & 0 deletions src/rules/space-around-number/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import type { Text } from 'mdast'
import type { ValueOf } from '@/types'
import type { SPACE_MESSAGE_IDS as MESSAGE_IDS } from '@/utils/space'
import { createRule } from '@/utils'
import { fixBoundarySpace, getBoundarySpaceMessageId, isNumberType } from '@/utils/text'

export const RULE_NAME = 'space-around-number'

type MessageIds = ValueOf<typeof MESSAGE_IDS>
type Options = []

export default createRule<Options, MessageIds>({
name: RULE_NAME,
meta: {
type: 'layout',
docs: {
description: 'Enforce a single space between CJK characters and numbers.',
},
messages: {
missingSpaceBefore: 'Add a space before the number.',
missingSpaceAfter: 'Add a space after the number.',
missingSpacesAround: 'Add spaces before and after the number.',
unexpectedSpaceBefore: 'Remove the unexpected space before the number.',
unexpectedSpaceAfter: 'Remove the unexpected space after the number.',
unexpectedSpaceAround: 'Remove the unexpected spaces around the number.',
},
fixable: 'whitespace',
schema: [],
},
defaultOptions: [],
create(context) {
return {
text(node: Text) {
const { fixed, missingBefore, missingAfter, unexpectedBefore, unexpectedAfter } = fixBoundarySpace(node, isNumberType)

if (fixed === node.value)
return

context.report({
node,
messageId: getBoundarySpaceMessageId({ missingBefore, missingAfter, unexpectedBefore, unexpectedAfter }),
fix(fixer) {
return fixer.replaceText(node, fixed)
},
})
},
}
},
})
3 changes: 2 additions & 1 deletion src/rules/space-around-word/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type { InvalidTestCase, ValidTestCase } from 'eslint-vitest-rule-tester'
import markdown from '@eslint/markdown'
import { run } from 'eslint-vitest-rule-tester'
import rule, { MESSAGE_IDS, RULE_NAME } from './index'
import { SPACE_MESSAGE_IDS as MESSAGE_IDS } from '@/utils/space'
import rule, { RULE_NAME } from './index'

const valid: ValidTestCase[] = [
{
Expand Down
Loading
Loading