Skip to content

Commit

Permalink
type: Modiy type errors.
Browse files Browse the repository at this point in the history
  • Loading branch information
jaywcjlove committed Oct 2, 2021
1 parent ff96d4d commit 3ca5ecd
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 99 deletions.
18 changes: 6 additions & 12 deletions package.json
Expand Up @@ -2,6 +2,7 @@
"name": "rehype-attr",
"version": "2.0.3",
"description": "New syntax to add attributes to Markdown.",
"homepage": "https://jaywcjlove.github.io/rehype-attr",
"author": "Kenny Wong <wowohoo@qq.com>",
"license": "MIT",
"sideEffects": false,
Expand Down Expand Up @@ -36,25 +37,18 @@
"unified"
],
"jest": {
"testMatch": [
"<rootDir>/test/*.{ts,tsx}"
],
"coverageReporters": [
"lcov",
"json-summary"
],
"collectCoverageFrom": [
"<rootDir>/src/*.{tsx,ts}"
],
"transformIgnorePatterns": [
"<rootDir>/node_modules/?!(.*)"
]
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"dependencies": {
"unified": "10.1.0"
"unified": "10.1.0",
"unist-util-visit": "4.1.0"
},
"devDependencies": {
"@types/jest": "27.0.1",
"rehype": "12.0.0",
"rehype-raw": "6.1.0",
"rehype-stringify": "9.0.2",
Expand Down
45 changes: 23 additions & 22 deletions src/index.ts
@@ -1,6 +1,6 @@
import { Plugin, Transformer } from 'unified';
import { Parent, NodeData, Node } from 'unist';
import visit from './visit';
import { Plugin } from 'unified';
import { Root, Element, Comment, Properties, Literal } from 'hast';
import { visit } from 'unist-util-visit';
import { propertiesHandle, nextChild, prevChild, getCommentObject } from './utils';

export type RehypeAttrsOptions = {
Expand Down Expand Up @@ -42,40 +42,41 @@ export type RehypeAttrsOptions = {
* <p title="Rehype Attrs">text</p>
* ```
*/
properties: 'data' | 'string' | 'attr'
properties: 'data' | 'string' | 'attr';
}

const defaultOptions: RehypeAttrsOptions = {
properties: 'data'
properties: 'data',
}

const rehypeAttrs: Plugin<[RehypeAttrsOptions?]> = (options): Transformer => {
const rehypeAttrs: Plugin<[RehypeAttrsOptions?], Root> = (options) => {
const opts = { ...defaultOptions, ...options }
return transformer;
function transformer(tree: Node<NodeData<Parent>>): void {
// ????? any
visit(tree as any, 'element', (node: NodeData<Parent>, index: number, parent: NodeData<Parent>) => {
const codeNode = node && node.children && Array.isArray(node.children) && node.children[0]
if (node.tagName === 'pre' && codeNode && codeNode.tagName === 'code' && Array.isArray(parent.children) && parent.children.length > 1) {
const child = prevChild(parent.children, index)
if (child) {
const attr = getCommentObject(child)
if (Object.keys(attr).length > 0) {
node.properties = { ...(node.properties as any), ...{ 'data-type': 'rehyp' } }
codeNode.properties = propertiesHandle(codeNode.properties, attr, opts.properties)
return (tree) => {
visit(tree, 'element', (node, index, parent) => {
if (node.tagName === 'pre' && node && Array.isArray(node.children) && parent && Array.isArray(parent.children) && parent.children.length > 1) {
const firstChild = node.children[0] as Element;
if (firstChild && firstChild.tagName === 'code' && typeof index === 'number') {
const child = prevChild(parent.children as Literal[], index);
if (child) {
const attr = getCommentObject(child);
if (Object.keys(attr).length > 0) {
node.properties = { ...node.properties, ...{ 'data-type': 'rehyp' } }
firstChild.properties = propertiesHandle(firstChild.properties, attr, opts.properties) as Properties
}
}
}
}
if (/^(em|strong|b|a|i|p|pre|kbd|blockquote|h(1|2|3|4|5|6)|code|table|img|del|ul|ol)$/.test(node.tagName as string) && Array.isArray(parent.children)) {

if (/^(em|strong|b|a|i|p|pre|kbd|blockquote|h(1|2|3|4|5|6)|code|table|img|del|ul|ol)$/.test(node.tagName) && parent && Array.isArray(parent.children) && typeof index === 'number') {
const child = nextChild(parent.children, index)
if (child) {
const attr = getCommentObject(child)
const attr = getCommentObject(child as Comment)
if (Object.keys(attr).length > 0) {
node.properties = propertiesHandle(node.properties as any, attr, opts.properties)
node.properties = propertiesHandle(node.properties, attr, opts.properties) as Properties
}
}
}
})
});
}
}

Expand Down
36 changes: 18 additions & 18 deletions src/utils.ts
@@ -1,48 +1,44 @@
import { Parent, NodeData } from 'unist';
import { Element, Comment, Literal, ElementContent, RootContent, Properties } from 'hast';
import { RehypeAttrsOptions } from './';

export const getURLParameters = (url: string): Record<string, string | number | boolean> =>
(url.match(/([^?=&]+)(=([^&]*))/g) || []).reduce(
(a: Record<string, string | number>, v: string) => (
// eslint-disable-next-line no-sequences
(a[v.slice(0, v.indexOf('='))] = v.slice(v.indexOf('=') + 1)), a
),
{},
);

type CommentData = {
type?: 'comment',
value?: string,
}

export const prevChild = (data: NodeData<Parent>[] = [], index: number): CommentData | undefined => {
export const prevChild = (data: Literal[] = [], index: number): Comment | undefined => {
let i = index;
while (i > -1) {
i--;
if (!data[i]) return
if ((data[i] && data[i].value && (data[i].value as string).replace(/(\n|\s)/g, '') !== '') || data[i].type !== 'text') {
if (!/^rehype:/.test(data[i].value as string) || (data[i].type as string) !== 'comment') return;
return data[i] as unknown as CommentData;
return data[i] as unknown as Comment;
}
}
return;
}

export const nextChild = (data: NodeData<Parent>[] = [], index: number, tagName?: string): CommentData | undefined => {
export const nextChild = (data: RootContent[] | ElementContent[] = [], index: number, tagName?: string): ElementContent | undefined => {
let i = index;
while (i < data.length) {
i++;
if (tagName) {
if (data[i] && data[i].value && (data[i].value as string).replace(/(\n|\s)/g, '') !== '' || data[i] && (data[i].type as string) === 'element') {
return data[i].tagName === tagName ? data[i] as unknown as CommentData : undefined
const element = data[i] as Literal & Element;
if (element && element.value && (element.value as string).replace(/(\n|\s)/g, '') !== '' || data[i] && (data[i].type as string) === 'element') {
return element.tagName === tagName ? element : undefined
}
} else {
if (!data[i] || (data[i].type !== 'text' && (data[i].type as string) !== 'comment') || (data[i].type == 'text' && (data[i].value as string).replace(/(\n|\s)/g, '') !== '')) return
if ((data[i].type as string) === 'comment') {
if (!/^rehype:/.test(data[i].value as string)) return;
const element = data[i] as ElementContent & Literal;
if (!element || (element.type !== 'text' && (element.type as string) !== 'comment') || (element.type === 'text' && (element.value as string).replace(/(\n|\s)/g, '') !== '')) return;
if ((element.type as string) === 'comment') {
if (!/^rehype:/.test(element.value as string)) return;
const nextNode = nextChild(data, i, 'pre')
if (nextNode) return;
return data[i] as unknown as CommentData;
return element;
}
}
}
Expand All @@ -55,7 +51,7 @@ export const nextChild = (data: NodeData<Parent>[] = [], index: number, tagName?
* @param index 当前数据所在的位置
* @returns 返回 当前参数数据 Object,`{}`
*/
export const getCommentObject = ({ value = '' }: CommentData): Record<string, string | number | boolean | null> => {
export const getCommentObject = ({ value = '' }: Comment): Properties => {
const param = getURLParameters(value.replace(/^rehype:/, ''));
Object.keys(param).forEach((keyName: string) => {
if (param[keyName] === 'true') {
Expand All @@ -71,7 +67,11 @@ export const getCommentObject = ({ value = '' }: CommentData): Record<string, st
return param;
}

export const propertiesHandle = (defaultAttrs?: Record<string, string> | null, attrs?: Record<string, string | number | boolean | null> | null, type?: RehypeAttrsOptions['properties']) => {
export type DataConfig = {
'data-config': Properties
}

export const propertiesHandle = (defaultAttrs?: Properties | null, attrs?: Properties, type?: RehypeAttrsOptions['properties']): Properties | DataConfig => {
if (type === 'string') {
return { ...defaultAttrs, 'data-config': JSON.stringify({ ...attrs, rehyp: true })}
} else if (type === 'attr') {
Expand Down
22 changes: 0 additions & 22 deletions src/visit.ts

This file was deleted.

35 changes: 10 additions & 25 deletions test/index.test.ts
@@ -1,4 +1,5 @@
import { unified, Plugin } from 'unified'
import { unified, Plugin } from 'unified';
import { Comment, Literal, ElementContent } from 'hast';
import { Parent, NodeData } from 'unist';
import { rehype } from 'rehype';
import gfm from 'remark-gfm';
Expand All @@ -8,7 +9,6 @@ import remarkParse from 'remark-parse';
import stringify from 'rehype-stringify';
import rehypeAttrs from '../src';
import * as utils from '../src/utils';
import visit from '../src/visit';

const mrkStr = "<!--rehype:title=Rehype Attrs-->\n```js\nconsole.log('')\n```"

Expand All @@ -30,40 +30,25 @@ describe('rehype-attr function test case', () => {
],
"data": { "quirksMode": false }
}
visit(node, 'element', (childNode, index, parent) => {
expect(/(del|p)/.test((childNode as any).tagName)).toBeTruthy()
expect(typeof childNode).toEqual('object')
expect(typeof index).toEqual('number')
expect(typeof parent).toEqual('object')
})
expect(visit(node)).toBeUndefined()
expect(visit(node, 'element')).toBeUndefined()
expect(visit(node, 'element', () => {})).toBeUndefined()
expect(visit({ type: 'root' }, 'element', () => {})).toBeUndefined()
expect(visit({ type: 'root', children: [ { type: 'element' }] }, 'element', () => {})).toBeUndefined()
expect(visit()).toBeUndefined()
expect(visit(undefined)).toBeUndefined()
expect(visit(undefined, undefined)).toBeUndefined()
expect(visit(undefined, undefined, undefined)).toBeUndefined()
});
it('getCommentObject', async () => {
expect(utils.getCommentObject({})).toEqual({ });
expect(utils.getCommentObject({ value: 'rehype:title=Rehype Attrs' })).toEqual({ title: 'Rehype Attrs' });
expect(utils.getCommentObject({} as Comment)).toEqual({ });
expect(utils.getCommentObject({ value: 'rehype:title=Rehype Attrs' } as Comment)).toEqual({ title: 'Rehype Attrs' });
});
it('prevChild', async () => {
expect(utils.prevChild(undefined, 0)).toBeUndefined()
expect(utils.prevChild(undefined, -1)).toBeUndefined()
expect(utils.prevChild([ { type: 'comment', value: 'rehype:title=Rehype Attrs' }, { type: 'text', value: '\n' } ], 1)).toEqual({ type: "comment", value: "rehype:title=Rehype Attrs" })
expect(utils.prevChild([ { type: 'comment', value: 'rehype:title=Rehype Attrs' }, { type: 'text' } ], 1)).toEqual({ type: "comment", value: "rehype:title=Rehype Attrs" })
expect(utils.prevChild([ { type: 'comment', value: 'rehype:title=Rehype Attrs' }, { type: 'text' } ] as Literal[], 1)).toEqual({ type: "comment", value: "rehype:title=Rehype Attrs" })
expect(utils.prevChild([ { type: 'text', value: '\n' }, { type: 'comment', value: 'rehype:title=Rehype Attrs' } ], 2)).toEqual({ type: "comment", value: "rehype:title=Rehype Attrs" })
});
it('nextChild', async () => {
expect(utils.nextChild(undefined, 0)).toBeUndefined()
expect(utils.nextChild(undefined, -1)).toBeUndefined()
expect(utils.nextChild([ { type: 'elment', value: 'rehype:title=Rehype Attrs' } ], 0)).toBeUndefined()
expect(utils.nextChild([ { type: 'text' }, { type: 'comment', value: 'rehype:title=Rehype Attrs' } ], 0)).toEqual({ type: "comment", value: "rehype:title=Rehype Attrs" })
expect(utils.nextChild([ { type: 'elment', value: 'rehype:title=Rehype Attrs' } ] as unknown as ElementContent[], 0)).toBeUndefined()
expect(utils.nextChild([ { type: 'text' }, { type: 'comment', value: 'rehype:title=Rehype Attrs' } ] as ElementContent[], 0)).toEqual({ type: "comment", value: "rehype:title=Rehype Attrs" })
expect(utils.nextChild([ { type: 'text', value: '\n' }, { type: 'comment', value: 'rehype:title=Rehype Attrs' } ], 0)).toEqual({ type: "comment", value: "rehype:title=Rehype Attrs" })
expect(utils.nextChild([ { type: 'text', value: '\n' }, { type: 'text', value: '' }, { type: 'element', tagName: 'pre' } ], 0, 'pre')).toEqual({ type: 'element', tagName: 'pre' })
expect(utils.nextChild([ { type: 'text', value: '\n' }, { type: 'text', value: '' }, { type: 'element', tagName: 'pre' } ] as ElementContent[], 0, 'pre')).toEqual({ type: 'element', tagName: 'pre' })
});
it('propertiesHandle', async () => {
expect(utils.propertiesHandle({}, {})).toEqual({
Expand Down Expand Up @@ -394,10 +379,10 @@ describe('rehype-attr test case', () => {
const pluginWithoutOptions: Plugin<void[]> = (options) => {
// expectType<void>(options)
}

const htmlStr = rehype()
.data('settings', { fragment: true })
.use(rehypeAttrs as any, { properties: 'attr' })
.use(rehypeAttrs, { properties: 'attr' })
.processSync(data.markdown)
.toString()
expect(htmlStr).toEqual(data.expected);
Expand Down

0 comments on commit 3ca5ecd

Please sign in to comment.