Skip to content

Commit

Permalink
feat: remove deprecated functions, test coverage 100%
Browse files Browse the repository at this point in the history
  • Loading branch information
JounQin committed Aug 11, 2019
1 parent 2a0a906 commit fd2694e
Show file tree
Hide file tree
Showing 13 changed files with 111 additions and 62 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ before_deploy:
- git checkout $TRAVIS_BRANCH
- cp -f README.md packages/eslint-mdx
- cp -f README.md packages/eslint-plugin-mdx
- git add --all && git commit -m "docs:\ update README" || echo 'nothing changed to commit'
- yarn run lerna-changelog

deploy:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ npm i -D @rxts/eslint-plugin-mdx

## Parser Options

1. `parser` (`string | Function`): Custom parser for ES syntax is supported, although `@typescript-eslint/parser` or `babel-eslint` will be detected automatically what means you actually do not need do this:
1. `parser` (`string | ParserConfig | ParserFn`): Custom parser for ES syntax is supported, although `@typescript-eslint/parser` or `babel-eslint` will be detected automatically what means you actually do not need do this:

```json
{
Expand Down
10 changes: 9 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,20 @@
],
"husky": {
"hooks": {
"pre-commit": "lint-staged",
"pre-commit": "lint-staged && yarn test -o",
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
},
"jest": {
"collectCoverage": true,
"coverageThreshold": {
"global": {
"branches": 100,
"functions": 100,
"lines": 100,
"statements": 100
}
},
"modulePathIgnorePatterns": [
"<rootDir>/packages/"
],
Expand Down
22 changes: 14 additions & 8 deletions packages/eslint-mdx/src/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ import {
} from './types'

import { Position } from 'unist'
import { AST } from 'eslint'
// `SourceLocation` is not exported from estree, but it is actually working
// eslint-disable-next-line import/named
import { SourceLocation } from 'estree'
import { AST } from 'eslint'

export const FALLBACK_PARSERS = [
'@typescript-eslint/parser',
Expand All @@ -36,20 +36,21 @@ export const normalizeParser = (parser?: ParserOptions['parser']) => {
}

if (typeof parser === 'object') {
parser = parser.parseForESLint || parser.parse
parser =
('parseForESLint' in parser && parser.parseForESLint) ||
('parse' in parser && parser.parse)
}

if (typeof parser !== 'function') {
throw new TypeError(
`Invalid custom parser for \`eslint-plugin-mdx\`: ${parser}`,
)
throw new TypeError(`Invalid custom parser for \`eslint-mdx\`: ${parser}`)
}
} else {
// try to load FALLBACK_PARSERS automatically
for (const fallback of FALLBACK_PARSERS) {
try {
// eslint-disable-next-line @typescript-eslint/no-require-imports,@typescript-eslint/no-var-requires
const fallbackParser = require(fallback)
/* istanbul ignore next */
parser = fallbackParser.parseForESLint || fallbackParser.parse
break
} catch (e) {}
Expand All @@ -63,11 +64,16 @@ export const normalizeParser = (parser?: ParserOptions['parser']) => {
return parser
}

export const normalizePosition = (position: Position) => {
export const normalizePosition = (
position: Position,
): Pick<AST.Program, 'loc' | 'range'> & {
start: number
end: number
} => {
const start = position.start.offset
const end = position.end.offset
return {
range: [start, end] as AST.Range,
range: [start, end],
loc: {
...position,
},
Expand All @@ -85,7 +91,7 @@ export interface BaseNode {
export function restoreNodeLocation<T extends BaseNode>(
node: T,
startLine: number,
offset = 0,
offset: number,
): T {
if (!node || !node.loc || !node.range) {
return node
Expand Down
6 changes: 5 additions & 1 deletion packages/eslint-mdx/src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,13 @@ export const LOC_ERROR_PROPERTIES = ['column', 'index', 'lineNumber'] as const
export const DEFAULT_EXTENSIONS: readonly string[] = ['.mdx']

export const DEFAULT_PARSER_OPTIONS: ParserOptions = {
comment: true,
ecmaFeatures: {
jsx: true,
},
ecmaVersion: new Date().getUTCFullYear() as Linter.ParserOptions['ecmaVersion'],
sourceType: 'module',
tokens: true,
}

export class Parser {
Expand Down Expand Up @@ -185,6 +187,7 @@ export class Parser {
this._options = options
}
const program = this._parser(code, options)
/* istanbul ignore next */
return ('ast' in program && program.ast
? program
: { ast: program }) as Linter.ESLintParseResult
Expand Down Expand Up @@ -282,12 +285,13 @@ export class Parser {
try {
program = this._eslintParse(value, options).ast
} catch (e) {
/* istanbul ignore if */
if (hasProperties<LocationError>(e, LOC_ERROR_PROPERTIES)) {
// should be handled by `_normalizeJsxNodes`, just for robustness
e.index += start
e.column = e.lineNumber > 1 ? e.column : e.column + loc.start.column
e.lineNumber += startLine
}

throw e
}

Expand Down
6 changes: 5 additions & 1 deletion packages/eslint-mdx/src/regexp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@ export const COMMENT_CONTENT_REGEX = new RegExp(

export const isOpenTag = (text: string) => OPEN_TAG_REGEX.test(text)
export const isCloseTag = (text: string) => CLOSE_TAG_REGEX.test(text)
export const isComment = (text: string) => COMMENT_REGEX.test(text)

// the following functions are only declared for robustness and should never be called
/* istanbul ignore next */
export const isOpenCloseTag = (text: string) => OPEN_CLOSE_TAG_REGEX.test(text)
// prettier-ignore
/* istanbul ignore next */
export const isSelfClosingTag = (text: string) => SELF_CLOSING_TAG_REGEX.test(text)
export const isComment = (text: string) => COMMENT_REGEX.test(text)
16 changes: 9 additions & 7 deletions packages/eslint-mdx/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ export type ParserFn = (
options: Linter.ParserOptions,
) => AST.Program | Linter.ESLintParseResult

export type ParserConfig =
| {
parseForESLint: ParserFn
}
| {
parse: ParserFn
}

export interface LocationError {
column?: number
index?: number
Expand All @@ -28,13 +36,7 @@ export interface LocationError {
export interface ParserOptions extends Linter.ParserOptions {
extensions?: string | string[]
filePath?: string
parser?:
| string
| {
parseForESLint: ParserFn
parse: ParserFn
}
| ParserFn
parser?: string | ParserConfig | ParserFn
}

export type Traverser = (node: Node, parent?: Parent) => void
Expand Down
30 changes: 0 additions & 30 deletions packages/eslint-plugin-mdx/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,5 @@
/* istanbul ignore file */

import { parse as oParse, parseForESLint as oParseForESLint } from 'eslint-mdx'

import * as configs from './configs'

import { Linter } from 'eslint'

export { configs }

export * from './rules'

const warn = () =>
console.error(
'parse from this plugin is deprecated, please use parser `eslint-mdx` directly',
)

/**
* @deprecated
*/
export const parse = (code: string, options?: Linter.ParserOptions) => {
warn()
return oParse(code, options)
}

/**
* @deprecated
*/
export const parseForESLint = (
code: string,
options?: Linter.ParserOptions,
) => {
warn()
oParseForESLint(code, options)
}
5 changes: 5 additions & 0 deletions test/__snapshots__/fixtures.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ exports[`fixtures should match all snapshots: no-jsx-html-comments.mdx 1`] = `
<header>Header{/** JSX HTML comment */}</header>
<main>Main Content</main>
<header>Header
{/** JSX
HTML comment */}</header>
<main>Main Content</main>
Inline <header>Header<!-- JSX HTML comment --></header>
<main>Main Content</main>
Expand Down
5 changes: 5 additions & 0 deletions test/fixtures/no-jsx-html-comments.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,10 @@
<header>Header<!-- JSX HTML comment --></header>
<main>Main Content</main>

<header>Header
<!-- JSX
HTML comment --></header>
<main>Main Content</main>

Inline <header>Header<!-- JSX HTML comment --></header>
<main>Main Content</main>
10 changes: 8 additions & 2 deletions test/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,17 @@ import { DEFAULT_PARSER_OPTIONS, ParserOptions } from 'eslint-mdx'

import { RuleTester } from 'eslint'

export function noop<T extends unknown[] = unknown[], R = unknown>(
..._args: T
): R {
return undefined
}

export const parser = require.resolve('eslint-mdx')

export const ruleTester = new RuleTester()

export const parserOptions: ParserOptions = {
...DEFAULT_PARSER_OPTIONS,
comment: true,
filePath: 'test.mdx',
tokens: true,
}
2 changes: 1 addition & 1 deletion test/no-unescaped-entities.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ ruleTester.run('no-unescaped-entities', noUnescapedEntities, {
],
},
{
code: 'Main <main> > </main>',
code: '<main>\n<section> > </section></main>',
parser,
parserOptions,
filename,
Expand Down
58 changes: 48 additions & 10 deletions test/parser.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
// eslint-disable-next-line @typescript-eslint/triple-slash-reference
/// <reference path="../packages/eslint-mdx/typings.d.ts" />

import { first, mdxProcessor, parser, normalizeParser } from 'eslint-mdx'
import {
first,
mdxProcessor,
parser,
normalizeParser,
ParserOptions,
} from 'eslint-mdx'
import { parse } from 'espree'

import { parserOptions } from './helper'
import { parserOptions, noop } from './helper'

import { Node } from 'unist'

Expand Down Expand Up @@ -99,28 +105,42 @@ describe('parser', () => {
})

it('should throw on invalid parser', () => {
;[
{
parse: null,
},
{
parseForEslint: null,
},
].forEach(p =>
expect(() =>
parser.parse('<header>Header</header>', {
...parserOptions,
parser: p as ParserOptions['parser'],
}),
).toThrow('Invalid custom parser for `eslint-mdx`:'),
)

expect(() =>
parser.parse('<header>Header</header>', {
...parserOptions,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
parser: {} as any,
parser: noop as ParserOptions['parser'],
}),
).toThrow('Invalid custom parser for `eslint-plugin-mdx`:')
).toThrow("Cannot use 'in' operator to search for 'ast' in undefined")
})

it('should work with valid custom parser', () => {
expect(() =>
parser.parse('<header>Header</header>', {
...parserOptions,
sourceType: null,
parser: 'babel-eslint',
}),
).not.toThrow()
})

it('should fallback to espree if no preferred parsers found', () => {
jest
.mock('@typescript-eslint/parser', jest.fn())
.mock('babel-eslint', jest.fn())
jest.mock('@typescript-eslint/parser', noop).mock('babel-eslint', noop)
expect(normalizeParser()).toBe(parse)
jest.unmock('@typescript-eslint/parser').unmock('babel-eslint')
})
Expand All @@ -129,12 +149,24 @@ describe('parser', () => {
expect(() =>
parser.parse("import A from 'a'\nimport A from 'a'", parserOptions),
).toThrow("unknown: Identifier 'A' has already been declared")
expect(() => parser.parse(`Header\n<>`, parserOptions)).toThrow(
expect(() => parser.parse('<header><>\n</header>', parserOptions)).toThrow(
'Expected corresponding closing tag for JSX fragment.',
)
expect(() => parser.parse('<h1></h2>', parserOptions)).toThrow(
"Expected corresponding JSX closing tag for 'h1'.",
)
expect(() => parser.parse('Header\n<>', parserOptions)).toThrow(
'JSX fragment has no corresponding closing tag.',
)
expect(() => parser.parse(`<main><</main>`, parserOptions)).toThrow(
expect(() => parser.parse('<main><</main>', parserOptions)).toThrow(
'Identifier expected.',
)
expect(() => parser.parse('<main>{<}</main>', parserOptions)).toThrow(
'Expression expected.',
)
expect(() =>
parser.parse('<main>\n<section><</section></main>', parserOptions),
).toThrow('Identifier expected.')
})

it('should not throw on adjacent JSX nodes', () => {
Expand All @@ -153,5 +185,11 @@ describe('parser', () => {
filePath: 'test.js',
}),
).not.toThrow()
expect(() =>
parser.parse('const a = {}', {
...parserOptions,
filePath: 'test.js',
}),
).not.toThrow()
})
})

0 comments on commit fd2694e

Please sign in to comment.