chore: convert js to ts (#405)

This commit is contained in:
YangFong 2024-09-16 17:07:47 +08:00 committed by GitHub
parent d3a7d08f9d
commit b99ddef0ca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 175 additions and 121 deletions

32
package-lock.json generated
View File

@ -41,9 +41,11 @@
},
"devDependencies": {
"@antfu/eslint-config": "2.26.0",
"@types/buffer-from": "^1.1.3",
"@types/codemirror": "^5.60.15",
"@types/marked": "^4.0.0",
"@types/crypto-js": "^4.2.2",
"@types/node": "^22.4.1",
"@types/uuid": "^10.0.0",
"@unocss/eslint-plugin": "^0.62.2",
"@vitejs/plugin-vue": "^5.1.2",
"autoprefixer": "^10.4.20",
@ -913,7 +915,6 @@
},
"node_modules/@clack/prompts/node_modules/is-unicode-supported": {
"version": "1.3.0",
"dev": true,
"inBundle": true,
"license": "MIT",
"engines": {
@ -2229,6 +2230,15 @@
"vue": "^2.7.0 || ^3.0.0"
}
},
"node_modules/@types/buffer-from": {
"version": "1.1.3",
"resolved": "https://registry.npmmirror.com/@types/buffer-from/-/buffer-from-1.1.3.tgz",
"integrity": "sha512-2lq4YC9uLUMGHkl2IDtX4tCXSo2+hwMpOJcY1qiIk1kybc31rIlPyM1HCVJhkPFIo75a/pOVxqyvwuf5TpCG/w==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/codemirror": {
"version": "5.60.15",
"resolved": "https://registry.npmmirror.com/@types/codemirror/-/codemirror-5.60.15.tgz",
@ -2238,6 +2248,12 @@
"@types/tern": "*"
}
},
"node_modules/@types/crypto-js": {
"version": "4.2.2",
"resolved": "https://registry.npmmirror.com/@types/crypto-js/-/crypto-js-4.2.2.tgz",
"integrity": "sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==",
"dev": true
},
"node_modules/@types/eslint": {
"version": "9.6.1",
"resolved": "https://registry.npmmirror.com/@types/eslint/-/eslint-9.6.1.tgz",
@ -2273,12 +2289,6 @@
"@types/lodash": "*"
}
},
"node_modules/@types/marked": {
"version": "4.3.2",
"resolved": "https://registry.npmmirror.com/@types/marked/-/marked-4.3.2.tgz",
"integrity": "sha512-a79Yc3TOk6dGdituy8hmTTJXjOkZ7zsFYV10L337ttq/rec8lRMDBpV7fL3uLx6TgbFCa5DU/h8FmIBQPSbU0w==",
"dev": true
},
"node_modules/@types/mdast": {
"version": "3.0.15",
"resolved": "https://registry.npmmirror.com/@types/mdast/-/mdast-3.0.15.tgz",
@ -2318,6 +2328,12 @@
"integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
"dev": true
},
"node_modules/@types/uuid": {
"version": "10.0.0",
"resolved": "https://registry.npmmirror.com/@types/uuid/-/uuid-10.0.0.tgz",
"integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==",
"dev": true
},
"node_modules/@types/web-bluetooth": {
"version": "0.0.20",
"resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz",

View File

@ -50,9 +50,11 @@
},
"devDependencies": {
"@antfu/eslint-config": "2.26.0",
"@types/buffer-from": "^1.1.3",
"@types/codemirror": "^5.60.15",
"@types/marked": "^4.0.0",
"@types/crypto-js": "^4.2.2",
"@types/node": "^22.4.1",
"@types/uuid": "^10.0.0",
"@unocss/eslint-plugin": "^0.62.2",
"@vitejs/plugin-vue": "^5.1.2",
"autoprefixer": "^10.4.20",

View File

@ -1,6 +1,6 @@
import { toMerged } from 'es-toolkit'
import type { Theme } from '@/types'
import type { IConfigOption, Theme } from '@/types'
const defaultTheme: Theme = {
base: {
@ -342,7 +342,12 @@ const graceTheme = toMerged(defaultTheme, {
},
})
export const themeOptions = [
export const themeMap = {
default: defaultTheme,
grace: graceTheme,
}
export const themeOptions: IConfigOption<keyof typeof themeMap>[] = [
{
label: `经典`,
value: `default`,
@ -354,8 +359,3 @@ export const themeOptions = [
desc: ``,
},
]
export const themeMap = {
default: defaultTheme,
grace: graceTheme,
}

View File

@ -31,7 +31,7 @@ export const useStore = defineStore(`store`, () => {
const output = ref(``)
// 文本字体
const theme = useStorage(addPrefix(`theme`), themeOptions[0].value)
const theme = useStorage<keyof typeof themeMap>(addPrefix(`theme`), themeOptions[0].value)
// 文本字体
const fontFamily = useStorage(`fonts`, fontFamilyOptions[0].value)
// 文本大小
@ -46,15 +46,15 @@ export const useStore = defineStore(`store`, () => {
const fontSizeNumber = computed(() => fontSize.value.replace(`px`, ``))
// 内容编辑器编辑器
const editor = ref(null)
const editor = ref<CodeMirror.EditorFromTextArea | null>(null)
// 编辑区域内容
const editorContent = useStorage(`__editor_content`, DEFAULT_CONTENT)
// 格式化文档
const formatContent = () => {
formatDoc(editor.value.getValue()).then((doc) => {
editorContent.value = doc
editor.value.setValue(doc)
formatDoc((editor.value!).getValue()).then((doc) => {
editorContent.value = doc;
(editor.value!).setValue(doc)
})
}
@ -76,9 +76,9 @@ export const useStore = defineStore(`store`, () => {
}
// 自义定 CSS 编辑器
const cssEditor = ref(null)
const setCssEditorValue = (content) => {
cssEditor.value.setValue(content)
const cssEditor = ref<CodeMirror.EditorFromTextArea | null>(null)
const setCssEditorValue = (content: string) => {
(cssEditor.value!).setValue(content)
}
// 自定义 CSS 内容
const cssContent = useStorage(`__css_content`, DEFAULT_CSS_CONTENT)
@ -99,24 +99,24 @@ export const useStore = defineStore(`store`, () => {
})
const getCurrentTab = () => cssContentConfig.value.tabs.find((tab) => {
return tab.name === cssContentConfig.value.active
})
const tabChanged = (name) => {
})!
const tabChanged = (name: string) => {
cssContentConfig.value.active = name
const content = cssContentConfig.value.tabs.find((tab) => {
return tab.name === name
}).content
})!.content
setCssEditorValue(content)
}
// 重命名 css 方案
const renameTab = (name) => {
const tab = getCurrentTab()
const renameTab = (name: string) => {
const tab = getCurrentTab()!
tab.title = name
tab.name = name
cssContentConfig.value.active = name
}
const addCssContentTab = (name) => {
const addCssContentTab = (name: string) => {
cssContentConfig.value.tabs.push({
name,
title: name,
@ -125,20 +125,21 @@ export const useStore = defineStore(`store`, () => {
cssContentConfig.value.active = name
setCssEditorValue(DEFAULT_CSS_CONTENT)
}
const validatorTabName = (val) => {
const validatorTabName = (val: string) => {
return cssContentConfig.value.tabs.every(({ name }) => name !== val)
}
const renderer = initRenderer({
theme: customCssWithTemplate(css2json(getCurrentTab().content), primaryColor.value, customizeTheme(themeMap[theme.value], { fontSize: fontSizeNumber.value, color: primaryColor.value })),
fonts: fontFamily.value,
size: fontSizeNumber.value,
})
// 更新编辑器
const editorRefresh = () => {
codeThemeChange()
renderer.reset({ status: isCiteStatus.value, legend: legend.value })
let outputTemp = marked.parse(editor.value.getValue(0))
let outputTemp = marked.parse(editor.value!.getValue()) as string
// 去除第一行的 margin-top
outputTemp = outputTemp.replace(/(style=".*?)"/, `$1;margin-top: 0"`)
@ -173,7 +174,7 @@ export const useStore = defineStore(`store`, () => {
// 更新 CSS
const updateCss = () => {
const json = css2json(cssEditor.value.getValue())
const json = css2json(cssEditor.value!.getValue())
const newTheme = customCssWithTemplate(json, primaryColor.value, customizeTheme(themeMap[theme.value], { fontSize: fontSizeNumber.value, color: primaryColor.value }))
renderer.setOptions({
theme: newTheme,
@ -182,7 +183,7 @@ export const useStore = defineStore(`store`, () => {
}
// 初始化 CSS 编辑器
onMounted(() => {
const cssEditorDom = document.querySelector(`#cssEditor`)
const cssEditorDom = document.querySelector<HTMLTextAreaElement>(`#cssEditor`)!
cssEditorDom.value = getCurrentTab().content
const theme = isDark.value ? `darcula` : `xq-light`
cssEditor.value = markRaw(
@ -190,32 +191,32 @@ export const useStore = defineStore(`store`, () => {
mode: `css`,
theme,
lineNumbers: false,
styleActiveLine: true,
lineWrapping: true,
styleActiveLine: true,
matchBrackets: true,
autofocus: true,
extraKeys: {
[`${shiftKey}-${altKey}-F`]: function autoFormat(editor) {
[`${shiftKey}-${altKey}-F`]: function autoFormat(editor: CodeMirror.Editor) {
formatDoc(editor.getValue(), `css`).then((doc) => {
getCurrentTab().content = doc
editor.setValue(doc)
})
},
},
}),
} as never),
)
// 自动提示
cssEditor.value.on(`keyup`, (cm, e) => {
if ((e.keyCode >= 65 && e.keyCode <= 90) || e.keyCode === 189) {
cm.showHint(e)
(cm as any).showHint(e)
}
})
// 实时保存
cssEditor.value.on(`update`, () => {
updateCss()
getCurrentTab().content = cssEditor.value.getValue()
getCurrentTab().content = cssEditor.value!.getValue()
})
})
@ -249,25 +250,25 @@ export const useStore = defineStore(`store`, () => {
],
}
cssEditor.value.setValue(DEFAULT_CSS_CONTENT)
cssEditor.value!.setValue(DEFAULT_CSS_CONTENT)
updateCss()
editorRefresh()
}
// 为函数添加刷新编辑器的功能
const withAfterRefresh = fn => (...rest) => {
const withAfterRefresh = (fn: (...rest: any[]) => void) => (...rest: any[]) => {
fn(...rest)
editorRefresh()
}
const getTheme = (size, color) => {
const getTheme = (size: string, color: string) => {
const newTheme = themeMap[theme.value]
const fontSize = size.replace(`px`, ``)
return customCssWithTemplate(css2json(getCurrentTab().content), color, customizeTheme(newTheme, { fontSize, color }))
}
const themeChanged = withAfterRefresh((newTheme) => {
const themeChanged = withAfterRefresh((newTheme: keyof typeof themeMap) => {
renderer.setOptions({
theme: customCssWithTemplate(css2json(getCurrentTab().content), primaryColor.value, customizeTheme(themeMap[newTheme], { fontSize: fontSizeNumber.value })),
})
@ -320,12 +321,12 @@ export const useStore = defineStore(`store`, () => {
// 导出编辑器内容为 HTML并且下载到本地
const exportEditorContent2HTML = () => {
exportHTML()
document.querySelector(`#output`).innerHTML = output.value
document.querySelector(`#output`)!.innerHTML = output.value
}
// 导出编辑器内容到本地
const exportEditorContent2MD = () => {
downloadMD(editor.value.getValue())
downloadMD(editor.value!.getValue())
}
// 导入 Markdown 文档
@ -336,7 +337,7 @@ export const useStore = defineStore(`store`, () => {
input.name = `filename`
input.accept = `.md`
input.onchange = () => {
const file = input.files[0]
const file = input.files![0]
if (!file) {
return
}
@ -344,7 +345,7 @@ export const useStore = defineStore(`store`, () => {
const reader = new FileReader()
reader.readAsText(file)
reader.onload = (event) => {
editor.value.setValue(event.target.result)
(editor.value!).setValue((event.target !).result as string)
ElMessage.success(`文档导入成功`)
}
}
@ -374,7 +375,7 @@ export const useStore = defineStore(`store`, () => {
})
})
.catch(() => {
editor.value.focus()
(editor.value!).focus()
})
}

View File

@ -1,7 +1,7 @@
import type { PropertiesHyphen } from 'csstype'
type Block = `h1` | `h2` | `h3` | `h4` | `p` | `blockquote` | `blockquote_p` | `code_pre` | `code` | `image` | `ol` | `ul` | `footnotes` | `figure` | `hr`
type Inline = `listitem` | `codespan` | `link` | `wx_link` | `strong` | `table` | `thead` | `td` | `footnote` | `figcaption` | `em`
export type Block = `h1` | `h2` | `h3` | `h4` | `p` | `blockquote` | `blockquote_p` | `code_pre` | `code` | `image` | `ol` | `ul` | `footnotes` | `figure` | `hr`
export type Inline = `listitem` | `codespan` | `link` | `wx_link` | `strong` | `table` | `thead` | `td` | `footnote` | `figcaption` | `em`
interface CustomCSSProperties {
[`--md-primary-color`]?: string
@ -20,14 +20,14 @@ export interface IOpts {
theme: Theme
fonts: string
size: string
legend: string
status: boolean
legend?: string
status?: boolean
}
export type ThemeStyles = Record<Block | Inline, ExtendedProperties>
export interface IConfigOption {
export interface IConfigOption<VT = string> {
label: string
value: string
value: VT
desc: string
}

View File

@ -8,7 +8,7 @@ const service = axios.create({
service.interceptors.request.use(
(config) => {
if (/^(?:post|put|delete)$/i.test(config.method)) {
if (/^(?:post|put|delete)$/i.test(`${config.method}`)) {
if (config.data && config.data.upload) {
config.headers[`Content-Type`] = `multipart/form-data`
}

View File

@ -11,7 +11,7 @@ import { base64encode, safe64, utf16to8 } from '@/utils/tokenTools'
import * as tokenTools from '@/utils/tokenTools'
import { giteeConfig, githubConfig } from '@/config'
function getConfig(useDefault, platform) {
function getConfig(useDefault: boolean, platform: string) {
if (useDefault) {
// load default config file
const config = platform === `github` ? githubConfig : giteeConfig
@ -29,7 +29,7 @@ function getConfig(useDefault, platform) {
}
// load configuration from localStorage
const customConfig = JSON.parse(localStorage.getItem(`${platform}Config`))
const customConfig = JSON.parse(localStorage.getItem(`${platform}Config`)!)
// split username/repo
const repoUrl = customConfig.repo
@ -62,7 +62,7 @@ function getDir() {
* @param {string} filename
* @returns {string} `时间戳+uuid`
*/
function getDateFilename(filename) {
function getDateFilename(filename: string) {
const currentTimestamp = new Date().getTime()
const fileSuffix = filename.split(`.`)[1]
return `${currentTimestamp}-${uuidv4()}.${fileSuffix}`
@ -72,7 +72,7 @@ function getDateFilename(filename) {
// GitHub File Upload
// -----------------------------------------------------------------------
async function ghFileUpload(content, filename) {
async function ghFileUpload(content: string, filename: string) {
const useDefault = localStorage.getItem(`imgHost`) === `default`
const { username, repo, branch, accessToken } = getConfig(
useDefault,
@ -81,7 +81,18 @@ async function ghFileUpload(content, filename) {
const dir = getDir()
const url = `https://api.github.com/repos/${username}/${repo}/contents/${dir}/`
const dateFilename = getDateFilename(filename)
const res = await fetch({
const res = await fetch<{ content: {
download_url: string
} }, {
content: {
download_url: string
}
data: {
content: {
download_url: string
}
}
}>({
url: url + dateFilename,
method: `put`,
headers: {
@ -95,7 +106,7 @@ async function ghFileUpload(content, filename) {
})
const githubResourceUrl = `raw.githubusercontent.com/${username}/${repo}/${branch}/`
const cdnResourceUrl = `fastly.jsdelivr.net/gh/${username}/${repo}@${branch}/`
res.content = res.data?.content || res.content
res.content = res.data.content || res.content
return useDefault
? res.content.download_url.replace(githubResourceUrl, cdnResourceUrl)
: res.content.download_url
@ -105,13 +116,24 @@ async function ghFileUpload(content, filename) {
// Gitee File Upload
// -----------------------------------------------------------------------
async function giteeUpload(content, filename) {
async function giteeUpload(content: any, filename: string) {
const useDefault = localStorage.getItem(`imgHost`) === `default`
const { username, repo, branch, accessToken } = getConfig(useDefault, `gitee`)
const dir = getDir()
const dateFilename = getDateFilename(filename)
const url = `https://gitee.com/api/v5/repos/${username}/${repo}/contents/${dir}/${dateFilename}`
const res = await fetch({
const res = await fetch<{ content: {
download_url: string
} }, {
content: {
download_url: string
}
data: {
content: {
download_url: string
}
}
}>({
url,
method: `POST`,
data: {
@ -129,7 +151,10 @@ async function giteeUpload(content, filename) {
// Qiniu File Upload
// -----------------------------------------------------------------------
function getQiniuToken(accessKey, secretKey, putPolicy) {
function getQiniuToken(accessKey: string, secretKey: string, putPolicy: {
scope: string
deadline: number
}) {
const policy = JSON.stringify(putPolicy)
const encoded = base64encode(utf16to8(policy))
const hash = CryptoJS.HmacSHA1(encoded, secretKey)
@ -137,9 +162,9 @@ function getQiniuToken(accessKey, secretKey, putPolicy) {
return `${accessKey}:${safe64(encodedSigned)}:${encoded}`
}
async function qiniuUpload(file) {
async function qiniuUpload(file: File) {
const { accessKey, secretKey, bucket, region, path, domain } = JSON.parse(
localStorage.getItem(`qiniuConfig`),
localStorage.getItem(`qiniuConfig`)!,
)
const token = getQiniuToken(accessKey, secretKey, {
scope: bucket,
@ -167,10 +192,10 @@ async function qiniuUpload(file) {
// AliOSS File Upload
// -----------------------------------------------------------------------
async function aliOSSFileUpload(file) {
async function aliOSSFileUpload(file: File) {
const dateFilename = getDateFilename(file.name)
const { region, bucket, accessKeyId, accessKeySecret, useSSL, cdnHost, path }
= JSON.parse(localStorage.getItem(`aliOSSConfig`))
= JSON.parse(localStorage.getItem(`aliOSSConfig`)!)
const dir = path ? `${path}/${dateFilename}` : dateFilename
const secure = useSSL === undefined || useSSL
const protocol = secure ? `https` : `http`
@ -195,10 +220,10 @@ async function aliOSSFileUpload(file) {
// TxCOS File Upload
// -----------------------------------------------------------------------
async function txCOSFileUpload(file) {
async function txCOSFileUpload(file: File) {
const dateFilename = getDateFilename(file.name)
const { secretId, secretKey, bucket, region, path, cdnHost } = JSON.parse(
localStorage.getItem(`txCOSConfig`),
localStorage.getItem(`txCOSConfig`)!,
)
const cos = new COS({
SecretId: secretId,
@ -235,13 +260,13 @@ async function txCOSFileUpload(file) {
// Minio File Upload
// -----------------------------------------------------------------------
async function minioFileUpload(content, filename) {
async function minioFileUpload(content: string, filename: string) {
const dateFilename = getDateFilename(filename)
const { endpoint, port, useSSL, bucket, accessKey, secretKey } = JSON.parse(
localStorage.getItem(`minioConfig`),
localStorage.getItem(`minioConfig`)!,
)
const buffer = Buffer(content, `base64`)
const conf = {
const conf: Minio.ClientOptions = {
endPoint: endpoint,
useSSL,
accessKey,
@ -278,7 +303,7 @@ async function minioFileUpload(content, filename) {
// formCustom File Upload
// -----------------------------------------------------------------------
async function formCustomUpload(content, file) {
async function formCustomUpload(content: string, file: File) {
const str = `
async (CUSTOM_ARG) => {
${localStorage.getItem(`formCustomConfig`)}
@ -304,16 +329,18 @@ async function formCustomUpload(content, file) {
errCb: reject, // 上传失败调用的函数
}
// eslint-disable-next-line no-eval
eval(str)(exportObj).catch((err) => {
eval(str)(exportObj).catch((err: any) => {
console.error(err)
reject(err)
})
})
}
function fileUpload(content, file) {
function fileUpload(content: string, file: File) {
const imgHost = localStorage.getItem(`imgHost`)
!imgHost && localStorage.setItem(`imgHost`, `default`)
if (!imgHost) {
localStorage.setItem(`imgHost`, `default`)
}
switch (imgHost) {
case `aliOSS`:
return aliOSSFileUpload(file)

View File

@ -5,31 +5,36 @@ import * as prettierPluginMarkdown from 'prettier/plugins/markdown'
import * as prettierPluginBabel from 'prettier/plugins/babel'
import * as prettierPluginEstree from 'prettier/plugins/estree'
import * as prettierPluginCss from 'prettier/plugins/postcss'
import type { PropertiesHyphen } from 'csstype'
import { prefix } from '@/config'
import type { Block, Inline, Theme } from '@/types'
export function addPrefix(str) {
export function addPrefix(str: string) {
return `${prefix}__${str}`
}
export function customizeTheme(theme, options) {
export function customizeTheme(theme: Theme, options: {
fontSize?: string
color?: string
}) {
const newTheme = JSON.parse(JSON.stringify(theme))
const { fontSize, color } = options
if (fontSize) {
for (let i = 1; i <= 4; i++) {
const v = newTheme.block[`h${i}`][`font-size`]
newTheme.block[`h${i}`][`font-size`] = `${fontSize * Number.parseFloat(v)}px`
newTheme.block[`h${i}`][`font-size`] = `${Number(fontSize) * Number.parseFloat(v)}px`
}
}
if (color) {
newTheme.base[`--md-primary-color`] = color
}
return newTheme
return newTheme as Theme
}
export function customCssWithTemplate(jsonString, color, theme) {
export function customCssWithTemplate(jsonString: Partial<Record<Block | Inline, PropertiesHyphen>>, color: string, theme: Theme) {
const newTheme = customizeTheme(theme, { color })
const mergeProperties = (target, source, keys) => {
const mergeProperties = <T extends Block | Inline = Block>(target: Record<T, PropertiesHyphen>, source: Partial<Record<Block | Inline, PropertiesHyphen>>, keys: T[]) => {
keys.forEach((key) => {
if (source[key]) {
target[key] = Object.assign(target[key] || {}, source[key])
@ -37,7 +42,7 @@ export function customCssWithTemplate(jsonString, color, theme) {
})
}
const blockKeys = [
const blockKeys: Block[] = [
`h1`,
`h2`,
`h3`,
@ -52,7 +57,7 @@ export function customCssWithTemplate(jsonString, color, theme) {
`ul`,
`ol`,
]
const inlineKeys = [`strong`, `codespan`, `link`, `wx_link`, `listitem`]
const inlineKeys: Inline[] = [`strong`, `codespan`, `link`, `wx_link`, `listitem`]
mergeProperties(newTheme.block, jsonString, blockKeys)
mergeProperties(newTheme.inline, jsonString, inlineKeys)
@ -65,16 +70,16 @@ export function customCssWithTemplate(jsonString, color, theme) {
* @param {string} css - CSS
* @returns {object} - JSON CSS
*/
export function css2json(css) {
export function css2json(css: string): Partial<Record<Block | Inline, PropertiesHyphen>> {
// 去除所有 CSS 注释
css = css.replace(/\/\*[\s\S]*?\*\//g, ``)
const json = {}
const json: Partial<Record<Block | Inline, PropertiesHyphen>> = {}
// 辅助函数:将声明数组转换为对象
const toObject = array =>
array.reduce((obj, item) => {
const [property, value] = item.split(`:`).map(part => part.trim())
const toObject = (array: any[]) =>
array.reduce<{ [k: string]: string }>((obj, item) => {
const [property, value] = item.split(`:`).map((part: string) => part.trim())
if (property)
obj[property] = value
return obj
@ -93,7 +98,7 @@ export function css2json(css) {
// 获取选择器并去除空格
const selectors = css.substring(0, lbracket)
.split(`,`)
.map(selector => selector.trim())
.map(selector => selector.trim()) as (Block | Inline)[]
const declarationObj = toObject(declarations)
@ -106,6 +111,8 @@ export function css2json(css) {
css = css.slice(rbracket + 1).trim()
}
console.log(`json`, json)
return json
}
@ -115,7 +122,7 @@ export function css2json(css) {
* @param {'markdown' | 'css'} [type] - 使'markdown'
* @returns {Promise<string>} -
*/
export async function formatDoc(content, type = `markdown`) {
export async function formatDoc(content: string, type: `markdown` | `css` = `markdown`) {
const plugins = {
markdown: [prettierPluginMarkdown, prettierPluginBabel, prettierPluginEstree],
css: [prettierPluginCss],
@ -132,7 +139,7 @@ export async function formatDoc(content, type = `markdown`) {
* Markdown
* @param {string} doc -
*/
export function downloadMD(doc) {
export function downloadMD(doc: string) {
const downLink = document.createElement(`a`)
downLink.download = `content.md`
@ -149,7 +156,7 @@ export function downloadMD(doc) {
* HTML
*/
export function exportHTML() {
const element = document.querySelector(`#output`)
const element = document.querySelector(`#output`)!
setStyles(element)
const htmlStr = element.innerHTML
@ -166,14 +173,14 @@ export function exportHTML() {
downLink.click()
document.body.removeChild(downLink)
function setStyles(element) {
function setStyles(element: Element) {
/**
* DOM
* @param {DOM } element DOM
* @param {} excludes 使
* @returns
*/
function getElementStyles(element, excludes = [`width`, `height`]) {
function getElementStyles(element: Element, excludes = [`width`, `height`]) {
const styles = getComputedStyle(element, null)
return Object.entries(styles)
.filter(
@ -188,15 +195,13 @@ export function exportHTML() {
case isCode(element):
case isSpan(element):
element.setAttribute(`style`, getElementStyles(element))
// eslint-disable-next-line no-fallthrough
default:
}
if (element.children.length) {
Array.from(element.children).forEach(child => setStyles(child))
}
// 判断是否是包裹代码块的 pre 元素
function isPre(element) {
function isPre(element: Element) {
return (
element.tagName === `PRE`
&& Array.from(element.classList).includes(`code__pre`)
@ -204,16 +209,19 @@ export function exportHTML() {
}
// 判断是否是包裹代码块的 code 元素
function isCode(element) {
function isCode(element: Element | null) {
if (element == null) {
return false
}
return element.tagName === `CODE`
}
// 判断是否是包裹代码字符的 span 元素
function isSpan(element) {
function isSpan(element: Element) {
return (
element.tagName === `SPAN`
&& (isCode(element.parentElement)
|| isCode(element.parentElement.parentElement))
|| isCode((element.parentElement!).parentElement))
)
}
}
@ -228,7 +236,7 @@ export function exportHTML() {
* @param {number} options.cols -
* @returns {string} Markdown
*/
export function createTable({ data, rows, cols }) {
export function createTable({ data, rows, cols }: { data: { [k: string]: string }, rows: number, cols: number }) {
let table = ``
for (let i = 0; i < rows + 2; ++i) {
table += `| `
@ -244,16 +252,16 @@ export function createTable({ data, rows, cols }) {
return table
}
export function toBase64(file) {
export function toBase64(file: Blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = () => resolve(reader.result.split(`,`).pop())
reader.onload = () => resolve((reader.result as string).split(`,`).pop())
reader.onerror = error => reject(error)
})
}
export function checkImage(file) {
export function checkImage(file: File) {
// 检查文件名后缀
const isValidSuffix = /\.(?:gif|jpe?g|png)$/i.test(file.name)
if (!isValidSuffix) {
@ -277,27 +285,27 @@ export function checkImage(file) {
/**
*
* @param {*} str
* @param {string} str
* @returns string
*/
export function removeLeft(str) {
export function removeLeft(str: string) {
const lines = str.split(`\n`)
// 获取应该删除的空白符数量
const minSpaceNum = lines
.filter(item => item.trim())
.map(item => item.match(/(^\s+)?/)[0].length)
.map(item => (item.match(/(^\s+)?/)!)[0].length)
.sort((a, b) => a - b)[0]
// 删除空白符
return lines.map(item => item.slice(minSpaceNum)).join(`\n`)
}
export function solveWeChatImage() {
const clipboardDiv = document.getElementById(`output`)
const clipboardDiv = document.getElementById(`output`)!
const images = clipboardDiv.getElementsByTagName(`img`)
for (let i = 0; i < images.length; i++) {
const image = images[i]
const width = image.getAttribute(`width`)
const height = image.getAttribute(`height`)
const width = image.getAttribute(`width`)!
const height = image.getAttribute(`height`)!
image.removeAttribute(`width`)
image.removeAttribute(`height`)
image.style.width = width
@ -305,7 +313,7 @@ export function solveWeChatImage() {
}
}
export function mergeCss(html) {
export function mergeCss(html: string) {
return juice(html, {
inlinePseudoElements: true,
preserveImportant: true,

View File

@ -109,13 +109,13 @@ export function initRenderer(opts: IOpts) {
return footnoteIndex
}
function reset(newOpts: IOpts): void {
function reset(newOpts: Partial<IOpts>): void {
footnotes.length = 0
footnoteIndex = 0
setOptions(newOpts)
}
function setOptions(newOpts: IOpts): void {
function setOptions(newOpts: Partial<IOpts>): void {
opts = { ...opts, ...newOpts }
styleMapping = buildTheme(opts)
}
@ -197,7 +197,7 @@ export function initRenderer(opts: IOpts) {
},
image({ href, title, text }: Tokens.Image): string {
const subText = styledContent(`figcaption`, transform(opts.legend, text, title))
const subText = styledContent(`figcaption`, transform(opts.legend!, text, title))
const figureStyles = styles(`figure`)
const imgStyles = styles(`image`)
return `<figure ${figureStyles}><img ${imgStyles} src="${href}" title="${title}" alt="${text}"/>${subText}</figure>`

View File

@ -1,4 +1,4 @@
export function utf16to8(str) {
export function utf16to8(str: string) {
let out = ``
const len = str.length
@ -22,7 +22,7 @@ export function utf16to8(str) {
return out
}
export function utf8to16(str) {
export function utf8to16(str: string) {
let out = ``
let i = 0
const len = str.length
@ -196,7 +196,7 @@ const base64DecodeChars = [
-1,
]
export function base64encode(str) {
export function base64encode(str: string) {
let out = ``
let i = 0
const len = str.length
@ -232,7 +232,7 @@ export function base64encode(str) {
return out
}
export function base64decode(str) {
export function base64decode(str: string) {
let c1, c2, c3, c4
let i = 0
const len = str.length
@ -283,7 +283,7 @@ export function base64decode(str) {
return out
}
export function safe64(base64) {
export function safe64(base64: string) {
base64 = base64.replace(/\+/g, `-`)
base64 = base64.replace(/\//g, `_`)
return base64