Skip to content

Commit

Permalink
Refactor code-style
Browse files Browse the repository at this point in the history
  • Loading branch information
wooorm committed Sep 20, 2023
1 parent 2a07c07 commit 57b2aaf
Show file tree
Hide file tree
Showing 4 changed files with 150 additions and 110 deletions.
118 changes: 73 additions & 45 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -1,89 +1,117 @@
/**
* @typedef {import('mdast').Root} Root
* @typedef {import('mdast').Image} Image
* @typedef {import('mdast').Link} Link
*
* @typedef {import('mdast').Root} Root
* @typedef {import('mdast').RootContent} RootContent
*/

/**
* @typedef Options
* Configuration (optional).
* @property {Array<string> | undefined} [imageExtensions]
* File extensions (without dot) to treat as images.
* @property {ReadonlyArray<string> | null | undefined} [imageExtensions]
* File extensions (without dot) to treat as images (default:
* `defaultImageExtensions`).
*/

import {collapseWhiteSpace} from 'collapse-white-space'
import isUrl from 'is-url'
import {position} from 'unist-util-position'
import {visitParents} from 'unist-util-visit-parents'
import {is} from 'unist-util-is'

const isImgPath = (/** @type {string} */ value) =>
value.startsWith('/') || value.startsWith('./') || value.startsWith('../')

/**
* Extensions recognized as images by default
*/
export const defaultImageExtensions = [
'svg',
'png',
'jpg',
'jpeg',
'gif',
'webp',
'avif'
]
/** @type {Readonly<Options>} */
const emptyOptions = {}

/**
* Plugin to add a simpler image syntax.
* Add a simpler image syntax.
*
* @type {import('unified').Plugin<[Options?]|void[], Root>}
* @param {Readonly<Options> | null | undefined} [options]
* Configuration (optional).
* @returns
* Transform.
*/
export default function remarkImages({
imageExtensions = defaultImageExtensions
} = {}) {
const imgExtRegex = new RegExp(`\\.(${imageExtensions.join('|')})$`)
const isImgExt = (/** @type {string} */ value) => imgExtRegex.test(value)
export default function remarkImages(options) {
const settings = options || emptyOptions
const imageExtensions = settings.imageExtensions || defaultImageExtensions
const imageExtensionRegex = new RegExp(`\\.(${imageExtensions.join('|')})$`)

return (tree) => {
visitParents(tree, 'text', (node, parents) => {
const value = String(node.value).trim()
/**
* Transform.
*
* @param {Root} tree
* Tree.
* @returns {undefined}
* Nothing.
*/
return function (tree) {
visitParents(tree, 'text', function (node, parents) {
const value = collapseWhiteSpace(node.value, {
style: 'html',
trim: true
})

if ((isUrl(value) || isImgPath(value)) && isImgExt(value)) {
if (
// Cannot contain whitespace (collapsed, so there can only be spaces):
!value.includes(' ') &&
// Looks like a URL or path:
(isUrl(value) ||
value.startsWith('/') ||
value.startsWith('./') ||
value.startsWith('../')) &&
// Ends in known extension:
imageExtensionRegex.test(value)
) {
let interactive = false
let length = parents.length
const parent = parents[length - 1]
const siblings = parent.children
// @ts-expect-error: too many possible parents.
const index = siblings.indexOf(node)

// Check if we’re in interactive content.
while (length--) {
if (is(parents[length], ['link', 'linkReference'])) {
const parent = parents[length]
if (parent.type === 'link' || parent.type === 'linkReference') {
interactive = true
break
}
}

/** @type {Image} */
const image = {
/** @type {Image | Link} */
let replacement = {
type: 'image',
url: value,
title: null,
alt: '',
position: node.position
position: position(node)
}
/** @type {Image|Link} */
let next = image

// Add a link if we’re not already in one.
if (!interactive) {
next = {
replacement = {
type: 'link',
url: value,
title: null,
children: [image],
position: node.position
children: [replacement],
position: position(node)
}
}

siblings[index] = next
const parent = parents[parents.length - 1]
/** @type {Array<RootContent>} */
const siblings = parent.children
siblings[siblings.indexOf(node)] = replacement
}
})
}
}

/**
* Extensions recognized as images by default.
*
* @type {ReadonlyArray<string>}
*/
export const defaultImageExtensions = [
'avif',
'gif',
'jpeg',
'jpg',
'png',
'svg',
'webp'
]
9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@
],
"dependencies": {
"@types/mdast": "^4.0.0",
"collapse-white-space": "^2.0.0",
"is-url": "^1.0.0",
"unified": "^11.0.0",
"unist-util-is": "^6.0.0",
"unist-util-position": "^5.0.0",
"unist-util-visit-parents": "^6.0.0"
},
"devDependencies": {
Expand Down Expand Up @@ -87,6 +87,9 @@
"strict": true
},
"xo": {
"prettier": true
"prettier": true,
"rules": {
"unicorn/prefer-at": "off"
}
}
}
2 changes: 1 addition & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ Configuration (optional).

###### `options.imageExtensions`

List of file extensions recognized as images (`Array.<string>?`, default
List of file extensions recognized as images (`Array<string>?`, default
[`defaultImageExtensions`](#defaultimageextensions)).

### `defaultImageExtensions`
Expand Down
Loading

0 comments on commit 57b2aaf

Please sign in to comment.