Skip to content

Commit

Permalink
Refactor to move implementation to lib/
Browse files Browse the repository at this point in the history
  • Loading branch information
wooorm committed Jan 25, 2023
1 parent 639fbab commit 2513bda
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 137 deletions.
141 changes: 4 additions & 137 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,140 +1,7 @@
/**
* @typedef {import('mdast').Root} Root
* @typedef {import('mdast').Content} Content
* @typedef {import('mdast').HTML} HTML
* @typedef {import('mdast-util-mdx-expression').MDXFlowExpression} MDXFlowExpression
* @typedef {import('mdast-util-mdx-expression').MDXTextExpression} MDXTextExpression
* @typedef {Root|Content} Node
*
* @typedef {string|number|boolean} MarkerParameterValue
* @typedef {Record<string, MarkerParameterValue>} MarkerParameters
*
* @typedef Mdx1CommentNode
* @property {'comment'} type
* @property {string} value
*
* @typedef Marker
* Comment marker.
* @property {string} name
* Name of marker.
* @property {string} attributes
* Value after name.
* @property {MarkerParameters|null} parameters
* Parsed attributes, with decimal numbers, `true`, and `false` are casted to
* numbers and booleans.
* @property {HTML|Mdx1CommentNode|MDXFlowExpression|MDXTextExpression} node
* Reference to given node.
* @typedef {import('./lib/index.js').Marker} Marker
* @typedef {import('./lib/index.js').MarkerParameterValue} MarkerParameterValue
* @typedef {import('./lib/index.js').MarkerParameters} MarkerParameters
*/

const commentExpression = /\s*([a-zA-Z\d-]+)(\s+([\s\S]*))?\s*/
const esCommentExpression = new RegExp(
'(\\s*\\/\\*' + commentExpression.source + '\\*\\/\\s*)'
)
const markerExpression = new RegExp(
'(\\s*<!--' + commentExpression.source + '-->\\s*)'
)

/**
* Parse a comment marker.
*
* @param {unknown} value
* `Node` to parse.
* @returns {Marker|null}
* Information, when applicable.
*/
export function commentMarker(value) {
if (
isNode(value) &&
(value.type === 'html' ||
// @ts-expect-error: MDX@1
value.type === 'comment' ||
value.type === 'mdxFlowExpression' ||
value.type === 'mdxTextExpression')
) {
let offset = 2
/** @type {RegExpMatchArray|null|undefined} */
let match

// @ts-expect-error: MDX@1
if (value.type === 'comment') {
// @ts-expect-error: MDX@1
match = value.value.match(commentExpression)
offset = 1
} else if (value.type === 'html') {
match = value.value.match(markerExpression)
} else if (
value.type === 'mdxFlowExpression' ||
value.type === 'mdxTextExpression'
) {
match = value.value.match(esCommentExpression)
}

if (match && match[0].length === value.value.length) {
const parameters = parseParameters(match[offset + 1] || '')

if (parameters) {
return {
name: match[offset],
attributes: (match[offset + 2] || '').trim(),
parameters,
node: value
}
}
}
}

return null
}

/**
* Parse `value` into an object.
*
* @param {string} value
* @returns {MarkerParameters|null}
*/
function parseParameters(value) {
/** @type {MarkerParameters} */
const parameters = {}

return value
.replace(
/\s+([-\w]+)(?:=(?:"((?:\\[\s\S]|[^"])+)"|'((?:\\[\s\S]|[^'])+)'|((?:\\[\s\S]|[^"'\s])+)))?/gi,
replacer
)
.replace(/\s+/g, '')
? null
: parameters

/**
* @param {string} _
* @param {string} $1
* @param {string} $2
* @param {string} $3
* @param {string} $4
*/
// eslint-disable-next-line max-params
function replacer(_, $1, $2, $3, $4) {
/** @type {MarkerParameterValue} */
let value = $2 || $3 || $4 || ''

if (value === 'true' || value === '') {
value = true
} else if (value === 'false') {
value = false
} else if (!Number.isNaN(Number(value))) {
value = Number(value)
}

parameters[$1] = value

return ''
}
}

/**
* @param {unknown} value
* @returns {value is Node}
*/
function isNode(value) {
return Boolean(value && typeof value === 'object' && 'type' in value)
}
export {commentMarker} from './lib/index.js'
140 changes: 140 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/**
* @typedef {import('mdast').Root} Root
* @typedef {import('mdast').Content} Content
* @typedef {import('mdast').HTML} HTML
* @typedef {import('mdast-util-mdx-expression').MDXFlowExpression} MDXFlowExpression
* @typedef {import('mdast-util-mdx-expression').MDXTextExpression} MDXTextExpression
* @typedef {Root|Content} Node
*
* @typedef {string|number|boolean} MarkerParameterValue
* @typedef {Record<string, MarkerParameterValue>} MarkerParameters
*
* @typedef Mdx1CommentNode
* @property {'comment'} type
* @property {string} value
*
* @typedef Marker
* Comment marker.
* @property {string} name
* Name of marker.
* @property {string} attributes
* Value after name.
* @property {MarkerParameters|null} parameters
* Parsed attributes, with decimal numbers, `true`, and `false` are casted to
* numbers and booleans.
* @property {HTML|Mdx1CommentNode|MDXFlowExpression|MDXTextExpression} node
* Reference to given node.
*/

const commentExpression = /\s*([a-zA-Z\d-]+)(\s+([\s\S]*))?\s*/
const esCommentExpression = new RegExp(
'(\\s*\\/\\*' + commentExpression.source + '\\*\\/\\s*)'
)
const markerExpression = new RegExp(
'(\\s*<!--' + commentExpression.source + '-->\\s*)'
)

/**
* Parse a comment marker.
*
* @param {unknown} value
* `Node` to parse.
* @returns {Marker|null}
* Information, when applicable.
*/
export function commentMarker(value) {
if (
isNode(value) &&
(value.type === 'html' ||
// @ts-expect-error: MDX@1
value.type === 'comment' ||
value.type === 'mdxFlowExpression' ||
value.type === 'mdxTextExpression')
) {
let offset = 2
/** @type {RegExpMatchArray|null|undefined} */
let match

// @ts-expect-error: MDX@1
if (value.type === 'comment') {
// @ts-expect-error: MDX@1
match = value.value.match(commentExpression)
offset = 1
} else if (value.type === 'html') {
match = value.value.match(markerExpression)
} else if (
value.type === 'mdxFlowExpression' ||
value.type === 'mdxTextExpression'
) {
match = value.value.match(esCommentExpression)
}

if (match && match[0].length === value.value.length) {
const parameters = parseParameters(match[offset + 1] || '')

if (parameters) {
return {
name: match[offset],
attributes: (match[offset + 2] || '').trim(),
parameters,
node: value
}
}
}
}

return null
}

/**
* Parse `value` into an object.
*
* @param {string} value
* @returns {MarkerParameters|null}
*/
function parseParameters(value) {
/** @type {MarkerParameters} */
const parameters = {}

return value
.replace(
/\s+([-\w]+)(?:=(?:"((?:\\[\s\S]|[^"])+)"|'((?:\\[\s\S]|[^'])+)'|((?:\\[\s\S]|[^"'\s])+)))?/gi,
replacer
)
.replace(/\s+/g, '')
? null
: parameters

/**
* @param {string} _
* @param {string} $1
* @param {string} $2
* @param {string} $3
* @param {string} $4
*/
// eslint-disable-next-line max-params
function replacer(_, $1, $2, $3, $4) {
/** @type {MarkerParameterValue} */
let value = $2 || $3 || $4 || ''

if (value === 'true' || value === '') {
value = true
} else if (value === 'false') {
value = false
} else if (!Number.isNaN(Number(value))) {
value = Number(value)
}

parameters[$1] = value

return ''
}
}

/**
* @param {unknown} value
* @returns {value is Node}
*/
function isNode(value) {
return Boolean(value && typeof value === 'object' && 'type' in value)
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"main": "index.js",
"types": "index.d.ts",
"files": [
"lib/",
"index.d.ts",
"index.js"
],
Expand Down

0 comments on commit 2513bda

Please sign in to comment.