feat(plugin-md-power): add @[caniuse](feature) syntax supported
This commit is contained in:
parent
b77a6334d7
commit
53dfcb83b1
21
plugins/plugin-md-power/LICENSE
Normal file
21
plugins/plugin-md-power/LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (C) 2021 - PRESENT by pengzhanbo
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
69
plugins/plugin-md-power/package.json
Normal file
69
plugins/plugin-md-power/package.json
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
{
|
||||||
|
"name": "@vuepress-plume/plugin-md-power",
|
||||||
|
"type": "module",
|
||||||
|
"version": "1.0.0-rc.47",
|
||||||
|
"description": "The Plugin for VuePres 2 - markdown power",
|
||||||
|
"author": "pengzhanbo <volodymyr@foxmail.com>",
|
||||||
|
"license": "MIT",
|
||||||
|
"homepage": "https://github.com/pengzhanbo/vuepress-theme-plume#readme",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/pengzhanbo/vuepress-theme-plume.git",
|
||||||
|
"directory": "plugins/plugin-md-power"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/pengzhanbo/vuepress-theme-plume/issues"
|
||||||
|
},
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"types": "./lib/node/index.d.ts",
|
||||||
|
"import": "./lib/node/index.js"
|
||||||
|
},
|
||||||
|
"./client": {
|
||||||
|
"types": "./lib/client/index.d.ts",
|
||||||
|
"import": "./lib/client/index.js"
|
||||||
|
},
|
||||||
|
"./package.json": "./package.json"
|
||||||
|
},
|
||||||
|
"main": "lib/node/index.js",
|
||||||
|
"types": "./lib/node/index.d.ts",
|
||||||
|
"files": [
|
||||||
|
"lib"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"build": "pnpm run copy && pnpm run ts",
|
||||||
|
"clean": "rimraf --glob ./lib ./*.tsbuildinfo",
|
||||||
|
"copy": "cpx \"src/**/*.{d.ts,vue,css,scss,jpg,png}\" lib",
|
||||||
|
"ts": "tsc -b tsconfig.build.json"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@iconify/json": "^2",
|
||||||
|
"vuepress": "2.0.0-rc.9"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@iconify/json": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@iconify/utils": "^2.1.22",
|
||||||
|
"@vueuse/core": "^10.9.0",
|
||||||
|
"local-pkg": "^0.5.0",
|
||||||
|
"markdown-it-container": "^4.0.0",
|
||||||
|
"nanoid": "^5.0.6",
|
||||||
|
"vue": "^3.4.21"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@iconify/json": "^2.2.196",
|
||||||
|
"@types/markdown-it": "^13.0.7"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"keyword": [
|
||||||
|
"VuePress",
|
||||||
|
"vuepress plugin",
|
||||||
|
"markdown power",
|
||||||
|
"vuepress-plugin-md-power"
|
||||||
|
]
|
||||||
|
}
|
||||||
179
plugins/plugin-md-power/src/node/caniuse.ts
Normal file
179
plugins/plugin-md-power/src/node/caniuse.ts
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
/**
|
||||||
|
* @[caniuse embed{1,2,3,4}](feature_name)
|
||||||
|
* @[caniuse image](feature_name)
|
||||||
|
*/
|
||||||
|
import type { PluginWithOptions, Token } from 'markdown-it'
|
||||||
|
import type { RuleBlock } from 'markdown-it/lib/parser_block.js'
|
||||||
|
import type { Markdown } from 'vuepress/markdown'
|
||||||
|
import container from 'markdown-it-container'
|
||||||
|
import type { CanIUseMode, CanIUseOptions, CanIUseTokenMeta } from '../shared/index.js'
|
||||||
|
|
||||||
|
// @[caniuse]()
|
||||||
|
const minLength = 12
|
||||||
|
|
||||||
|
// char codes of '@[caniuse'
|
||||||
|
const START_CODES = [64, 91, 99, 97, 110, 105, 117, 115, 101]
|
||||||
|
|
||||||
|
// regexp to match the import syntax
|
||||||
|
const SYNTAX_RE = /^@\[caniuse(?:\s*?(embed|image)?(?:{([0-9,\-]*?)})?)\]\(([^)]*)\)/
|
||||||
|
|
||||||
|
function createCanIUseRuleBlock(defaultMode: CanIUseMode): RuleBlock {
|
||||||
|
return (state, startLine, endLine, silent) => {
|
||||||
|
const pos = state.bMarks[startLine] + state.tShift[startLine]
|
||||||
|
const max = state.eMarks[startLine]
|
||||||
|
|
||||||
|
// return false if the length is shorter than min length
|
||||||
|
if (pos + minLength > max)
|
||||||
|
return false
|
||||||
|
|
||||||
|
// check if it's matched the start
|
||||||
|
for (let i = 0; i < START_CODES.length; i += 1) {
|
||||||
|
if (state.src.charCodeAt(pos + i) !== START_CODES[i])
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if it's matched the syntax
|
||||||
|
const match = state.src.slice(pos, max).match(SYNTAX_RE)
|
||||||
|
if (!match)
|
||||||
|
return false
|
||||||
|
|
||||||
|
// return true as we have matched the syntax
|
||||||
|
if (silent)
|
||||||
|
return true
|
||||||
|
|
||||||
|
const [, mode, versions = '', feature] = match
|
||||||
|
|
||||||
|
const meta: CanIUseTokenMeta = {
|
||||||
|
feature,
|
||||||
|
mode: (mode as CanIUseMode) || defaultMode,
|
||||||
|
versions,
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = state.push('caniuse', '', 0)
|
||||||
|
|
||||||
|
token.meta = meta
|
||||||
|
token.map = [startLine, startLine + 1]
|
||||||
|
token.info = mode || defaultMode
|
||||||
|
|
||||||
|
state.line = startLine + 1
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveCanIUse({ feature, mode, versions }: CanIUseTokenMeta): string {
|
||||||
|
if (!feature)
|
||||||
|
return ''
|
||||||
|
|
||||||
|
if (mode === 'image') {
|
||||||
|
const link = 'https://caniuse.bitsofco.de/image/'
|
||||||
|
const alt = `Data on support for the ${feature} feature across the major browsers from caniuse.com`
|
||||||
|
return `<ClientOnly><p><picture>
|
||||||
|
<source type="image/webp" srcset="${link}${feature}.webp">
|
||||||
|
<source type="image/png" srcset="${link}${feature}.png">
|
||||||
|
<img src="${link}${feature}.jpg" alt="${alt}" width="100%">
|
||||||
|
</picture></p></ClientOnly>`
|
||||||
|
}
|
||||||
|
|
||||||
|
const periods = resolveVersions(versions)
|
||||||
|
const accessible = 'false'
|
||||||
|
const image = 'none'
|
||||||
|
const url = 'https://caniuse.bitsofco.de/embed/index.html'
|
||||||
|
const src = `${url}?feat=${feature}&periods=${periods}&accessible-colours=${accessible}&image-base=${image}`
|
||||||
|
|
||||||
|
return `<ClientOnly><div class="ciu_embed" style="margin:16px 0" data-feature="${feature}"><iframe src="${src}" frameborder="0" width="100%" height="400px" title="Can I use${feature}"></iframe></div></ClientOnly>`
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveVersions(versions: string): string {
|
||||||
|
if (!versions)
|
||||||
|
return 'future_1,current,past_1,past_2'
|
||||||
|
|
||||||
|
const list = versions
|
||||||
|
.split(',')
|
||||||
|
.map(v => Number(v.trim()))
|
||||||
|
.filter(v => !Number.isNaN(v) && v >= -5 && v <= 3)
|
||||||
|
|
||||||
|
list.push(0)
|
||||||
|
|
||||||
|
const uniq = [...new Set(list)].sort((a, b) => b - a)
|
||||||
|
const result: string[] = []
|
||||||
|
uniq.forEach((v) => {
|
||||||
|
if (v < 0)
|
||||||
|
result.push(`past_${Math.abs(v)}`)
|
||||||
|
if (v === 0)
|
||||||
|
result.push('current')
|
||||||
|
if (v > 0)
|
||||||
|
result.push(`future_${v}`)
|
||||||
|
})
|
||||||
|
return result.join(',')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @example
|
||||||
|
* ```md
|
||||||
|
* @[caniuse](feature_name)
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export const caniusePlugin: PluginWithOptions<CanIUseOptions> = (
|
||||||
|
md,
|
||||||
|
{ mode = 'embed' }: CanIUseOptions = {},
|
||||||
|
): void => {
|
||||||
|
md.block.ruler.before(
|
||||||
|
'import_code',
|
||||||
|
'caniuse',
|
||||||
|
createCanIUseRuleBlock(mode),
|
||||||
|
{
|
||||||
|
alt: ['paragraph', 'reference', 'blockquote', 'list'],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
md.renderer.rules.caniuse = (tokens, index) => {
|
||||||
|
const token = tokens[index]
|
||||||
|
|
||||||
|
const content = resolveCanIUse(token.meta)
|
||||||
|
token.content = content
|
||||||
|
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use caniuse plugin
|
||||||
|
*
|
||||||
|
* 兼容旧语法
|
||||||
|
* @example
|
||||||
|
* ```md
|
||||||
|
* :::caniuse <feature_name>
|
||||||
|
* :::
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function legacyCaniuse(
|
||||||
|
md: Markdown,
|
||||||
|
{ mode = 'embed' }: CanIUseOptions = {},
|
||||||
|
): void {
|
||||||
|
const modeMap: CanIUseMode[] = ['image', 'embed']
|
||||||
|
const isMode = (mode: CanIUseMode): boolean => modeMap.includes(mode)
|
||||||
|
|
||||||
|
mode = isMode(mode) ? mode : modeMap[0]
|
||||||
|
const type = 'caniuse'
|
||||||
|
const validateReg = new RegExp(`^${type}\\s+(.*)$`)
|
||||||
|
|
||||||
|
const validate = (info: string): boolean => {
|
||||||
|
return validateReg.test(info.trim())
|
||||||
|
}
|
||||||
|
|
||||||
|
const render = (tokens: Token[], index: number): string => {
|
||||||
|
const token = tokens[index]
|
||||||
|
if (token.nesting === 1) {
|
||||||
|
const info = token.info.trim().slice(type.length).trim() || ''
|
||||||
|
const feature = info.split(/\s+/)[0]
|
||||||
|
const versions = info.match(/\{(.*)\}/)?.[1] || ''
|
||||||
|
return feature ? resolveCanIUse({ feature, mode, versions }) : ''
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
md.use(container, type, { validate, render })
|
||||||
|
}
|
||||||
20
plugins/plugin-md-power/src/shared/caniuse.ts
Normal file
20
plugins/plugin-md-power/src/shared/caniuse.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
export type CanIUseMode = 'embed' | 'image'
|
||||||
|
|
||||||
|
export interface CanIUseTokenMeta {
|
||||||
|
feature: string
|
||||||
|
mode: CanIUseMode
|
||||||
|
versions: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CanIUseOptions {
|
||||||
|
/**
|
||||||
|
* 嵌入模式
|
||||||
|
*
|
||||||
|
* embed 通过iframe嵌入,提供可交互视图
|
||||||
|
*
|
||||||
|
* image 通过图片嵌入,静态
|
||||||
|
*
|
||||||
|
* @default 'embed'
|
||||||
|
*/
|
||||||
|
mode?: CanIUseMode
|
||||||
|
}
|
||||||
8
plugins/plugin-md-power/tsconfig.build.json
Normal file
8
plugins/plugin-md-power/tsconfig.build.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"extends": "../tsconfig.build.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": "./src",
|
||||||
|
"outDir": "./lib"
|
||||||
|
},
|
||||||
|
"include": ["./src"]
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user