Skip to content

Commit 0cde012

Browse files
committed
Refactor to move implementation to lib/
1 parent 6e72107 commit 0cde012

File tree

3 files changed

+275
-270
lines changed

3 files changed

+275
-270
lines changed

index.js

+4-270
Original file line numberDiff line numberDiff line change
@@ -1,270 +1,4 @@
1-
/**
2-
* @typedef {import('mdast').Link} Link
3-
* @typedef {import('mdast-util-from-markdown').CompileContext} CompileContext
4-
* @typedef {import('mdast-util-from-markdown').Extension} FromMarkdownExtension
5-
* @typedef {import('mdast-util-from-markdown').Transform} FromMarkdownTransform
6-
* @typedef {import('mdast-util-from-markdown').Handle} FromMarkdownHandle
7-
* @typedef {import('mdast-util-to-markdown/lib/types.js').Options} ToMarkdownExtension
8-
* @typedef {import('mdast-util-find-and-replace').ReplaceFunction} ReplaceFunction
9-
* @typedef {import('mdast-util-find-and-replace').RegExpMatchObject} RegExpMatchObject
10-
* @typedef {import('mdast').PhrasingContent} PhrasingContent
11-
*/
12-
13-
import {ccount} from 'ccount'
14-
import {findAndReplace} from 'mdast-util-find-and-replace'
15-
import {unicodePunctuation, unicodeWhitespace} from 'micromark-util-character'
16-
17-
const inConstruct = 'phrasing'
18-
const notInConstruct = ['autolink', 'link', 'image', 'label']
19-
20-
/** @type {FromMarkdownExtension} */
21-
export const gfmAutolinkLiteralFromMarkdown = {
22-
transforms: [transformGfmAutolinkLiterals],
23-
enter: {
24-
literalAutolink: enterLiteralAutolink,
25-
literalAutolinkEmail: enterLiteralAutolinkValue,
26-
literalAutolinkHttp: enterLiteralAutolinkValue,
27-
literalAutolinkWww: enterLiteralAutolinkValue
28-
},
29-
exit: {
30-
literalAutolink: exitLiteralAutolink,
31-
literalAutolinkEmail: exitLiteralAutolinkEmail,
32-
literalAutolinkHttp: exitLiteralAutolinkHttp,
33-
literalAutolinkWww: exitLiteralAutolinkWww
34-
}
35-
}
36-
37-
/** @type {ToMarkdownExtension} */
38-
export const gfmAutolinkLiteralToMarkdown = {
39-
unsafe: [
40-
{
41-
character: '@',
42-
before: '[+\\-.\\w]',
43-
after: '[\\-.\\w]',
44-
inConstruct,
45-
// @ts-expect-error: to do: use map.
46-
notInConstruct
47-
},
48-
{
49-
character: '.',
50-
before: '[Ww]',
51-
after: '[\\-.\\w]',
52-
inConstruct,
53-
// @ts-expect-error: to do: use map.
54-
notInConstruct
55-
},
56-
{
57-
character: ':',
58-
before: '[ps]',
59-
after: '\\/',
60-
inConstruct,
61-
// @ts-expect-error: to do: use map.
62-
notInConstruct
63-
}
64-
]
65-
}
66-
67-
/**
68-
* @this {CompileContext}
69-
* @type {FromMarkdownHandle}
70-
*/
71-
function enterLiteralAutolink(token) {
72-
this.enter({type: 'link', title: null, url: '', children: []}, token)
73-
}
74-
75-
/**
76-
* @this {CompileContext}
77-
* @type {FromMarkdownHandle}
78-
*/
79-
function enterLiteralAutolinkValue(token) {
80-
this.config.enter.autolinkProtocol.call(this, token)
81-
}
82-
83-
/**
84-
* @this {CompileContext}
85-
* @type {FromMarkdownHandle}
86-
*/
87-
function exitLiteralAutolinkHttp(token) {
88-
this.config.exit.autolinkProtocol.call(this, token)
89-
}
90-
91-
/**
92-
* @this {CompileContext}
93-
* @type {FromMarkdownHandle}
94-
*/
95-
function exitLiteralAutolinkWww(token) {
96-
this.config.exit.data.call(this, token)
97-
const node = /** @type {Link} */ (this.stack[this.stack.length - 1])
98-
node.url = 'http://' + this.sliceSerialize(token)
99-
}
100-
101-
/**
102-
* @this {CompileContext}
103-
* @type {FromMarkdownHandle}
104-
*/
105-
function exitLiteralAutolinkEmail(token) {
106-
this.config.exit.autolinkEmail.call(this, token)
107-
}
108-
109-
/**
110-
* @this {CompileContext}
111-
* @type {FromMarkdownHandle}
112-
*/
113-
function exitLiteralAutolink(token) {
114-
this.exit(token)
115-
}
116-
117-
/** @type {FromMarkdownTransform} */
118-
function transformGfmAutolinkLiterals(tree) {
119-
findAndReplace(
120-
tree,
121-
[
122-
[/(https?:\/\/|www(?=\.))([-.\w]+)([^ \t\r\n]*)/gi, findUrl],
123-
[/([-.\w+]+)@([-\w]+(?:\.[-\w]+)+)/g, findEmail]
124-
],
125-
{ignore: ['link', 'linkReference']}
126-
)
127-
}
128-
129-
/**
130-
* @type {ReplaceFunction}
131-
* @param {string} _
132-
* @param {string} protocol
133-
* @param {string} domain
134-
* @param {string} path
135-
* @param {RegExpMatchObject} match
136-
*/
137-
// eslint-disable-next-line max-params
138-
function findUrl(_, protocol, domain, path, match) {
139-
let prefix = ''
140-
141-
// Not an expected previous character.
142-
if (!previous(match)) {
143-
return false
144-
}
145-
146-
// Treat `www` as part of the domain.
147-
if (/^w/i.test(protocol)) {
148-
domain = protocol + domain
149-
protocol = ''
150-
prefix = 'http://'
151-
}
152-
153-
if (!isCorrectDomain(domain)) {
154-
return false
155-
}
156-
157-
const parts = splitUrl(domain + path)
158-
159-
if (!parts[0]) return false
160-
161-
/** @type {PhrasingContent} */
162-
const result = {
163-
type: 'link',
164-
title: null,
165-
url: prefix + protocol + parts[0],
166-
children: [{type: 'text', value: protocol + parts[0]}]
167-
}
168-
169-
if (parts[1]) {
170-
return [result, {type: 'text', value: parts[1]}]
171-
}
172-
173-
return result
174-
}
175-
176-
/**
177-
* @type {ReplaceFunction}
178-
* @param {string} _
179-
* @param {string} atext
180-
* @param {string} label
181-
* @param {RegExpMatchObject} match
182-
*/
183-
function findEmail(_, atext, label, match) {
184-
if (
185-
// Not an expected previous character.
186-
!previous(match, true) ||
187-
// Label ends in not allowed character.
188-
/[-\d_]$/.test(label)
189-
) {
190-
return false
191-
}
192-
193-
return {
194-
type: 'link',
195-
title: null,
196-
url: 'mailto:' + atext + '@' + label,
197-
children: [{type: 'text', value: atext + '@' + label}]
198-
}
199-
}
200-
201-
/**
202-
* @param {string} domain
203-
* @returns {boolean}
204-
*/
205-
function isCorrectDomain(domain) {
206-
const parts = domain.split('.')
207-
208-
if (
209-
parts.length < 2 ||
210-
(parts[parts.length - 1] &&
211-
(/_/.test(parts[parts.length - 1]) ||
212-
!/[a-zA-Z\d]/.test(parts[parts.length - 1]))) ||
213-
(parts[parts.length - 2] &&
214-
(/_/.test(parts[parts.length - 2]) ||
215-
!/[a-zA-Z\d]/.test(parts[parts.length - 2])))
216-
) {
217-
return false
218-
}
219-
220-
return true
221-
}
222-
223-
/**
224-
* @param {string} url
225-
* @returns {[string, string|undefined]}
226-
*/
227-
function splitUrl(url) {
228-
const trailExec = /[!"&'),.:;<>?\]}]+$/.exec(url)
229-
/** @type {number} */
230-
let closingParenIndex
231-
/** @type {number} */
232-
let openingParens
233-
/** @type {number} */
234-
let closingParens
235-
/** @type {string|undefined} */
236-
let trail
237-
238-
if (trailExec) {
239-
url = url.slice(0, trailExec.index)
240-
trail = trailExec[0]
241-
closingParenIndex = trail.indexOf(')')
242-
openingParens = ccount(url, '(')
243-
closingParens = ccount(url, ')')
244-
245-
while (closingParenIndex !== -1 && openingParens > closingParens) {
246-
url += trail.slice(0, closingParenIndex + 1)
247-
trail = trail.slice(closingParenIndex + 1)
248-
closingParenIndex = trail.indexOf(')')
249-
closingParens++
250-
}
251-
}
252-
253-
return [url, trail]
254-
}
255-
256-
/**
257-
* @param {RegExpMatchObject} match
258-
* @param {boolean} [email=false]
259-
* @returns {boolean}
260-
*/
261-
function previous(match, email) {
262-
const code = match.input.charCodeAt(match.index - 1)
263-
264-
return (
265-
(match.index === 0 ||
266-
unicodeWhitespace(code) ||
267-
unicodePunctuation(code)) &&
268-
(!email || code !== 47)
269-
)
270-
}
1+
export {
2+
gfmAutolinkLiteralFromMarkdown,
3+
gfmAutolinkLiteralToMarkdown
4+
} from './lib/index.js'

0 commit comments

Comments
 (0)