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": { "devDependencies": {
"@antfu/eslint-config": "2.26.0", "@antfu/eslint-config": "2.26.0",
"@types/buffer-from": "^1.1.3",
"@types/codemirror": "^5.60.15", "@types/codemirror": "^5.60.15",
"@types/marked": "^4.0.0", "@types/crypto-js": "^4.2.2",
"@types/node": "^22.4.1", "@types/node": "^22.4.1",
"@types/uuid": "^10.0.0",
"@unocss/eslint-plugin": "^0.62.2", "@unocss/eslint-plugin": "^0.62.2",
"@vitejs/plugin-vue": "^5.1.2", "@vitejs/plugin-vue": "^5.1.2",
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.20",
@ -913,7 +915,6 @@
}, },
"node_modules/@clack/prompts/node_modules/is-unicode-supported": { "node_modules/@clack/prompts/node_modules/is-unicode-supported": {
"version": "1.3.0", "version": "1.3.0",
"dev": true,
"inBundle": true, "inBundle": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@ -2229,6 +2230,15 @@
"vue": "^2.7.0 || ^3.0.0" "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": { "node_modules/@types/codemirror": {
"version": "5.60.15", "version": "5.60.15",
"resolved": "https://registry.npmmirror.com/@types/codemirror/-/codemirror-5.60.15.tgz", "resolved": "https://registry.npmmirror.com/@types/codemirror/-/codemirror-5.60.15.tgz",
@ -2238,6 +2248,12 @@
"@types/tern": "*" "@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": { "node_modules/@types/eslint": {
"version": "9.6.1", "version": "9.6.1",
"resolved": "https://registry.npmmirror.com/@types/eslint/-/eslint-9.6.1.tgz", "resolved": "https://registry.npmmirror.com/@types/eslint/-/eslint-9.6.1.tgz",
@ -2273,12 +2289,6 @@
"@types/lodash": "*" "@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": { "node_modules/@types/mdast": {
"version": "3.0.15", "version": "3.0.15",
"resolved": "https://registry.npmmirror.com/@types/mdast/-/mdast-3.0.15.tgz", "resolved": "https://registry.npmmirror.com/@types/mdast/-/mdast-3.0.15.tgz",
@ -2318,6 +2328,12 @@
"integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
"dev": true "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": { "node_modules/@types/web-bluetooth": {
"version": "0.0.20", "version": "0.0.20",
"resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz", "resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz",

View File

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

View File

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

View File

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

View File

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

View File

@ -8,7 +8,7 @@ const service = axios.create({
service.interceptors.request.use( service.interceptors.request.use(
(config) => { (config) => {
if (/^(?:post|put|delete)$/i.test(config.method)) { if (/^(?:post|put|delete)$/i.test(`${config.method}`)) {
if (config.data && config.data.upload) { if (config.data && config.data.upload) {
config.headers[`Content-Type`] = `multipart/form-data` 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 * as tokenTools from '@/utils/tokenTools'
import { giteeConfig, githubConfig } from '@/config' import { giteeConfig, githubConfig } from '@/config'
function getConfig(useDefault, platform) { function getConfig(useDefault: boolean, platform: string) {
if (useDefault) { if (useDefault) {
// load default config file // load default config file
const config = platform === `github` ? githubConfig : giteeConfig const config = platform === `github` ? githubConfig : giteeConfig
@ -29,7 +29,7 @@ function getConfig(useDefault, platform) {
} }
// load configuration from localStorage // load configuration from localStorage
const customConfig = JSON.parse(localStorage.getItem(`${platform}Config`)) const customConfig = JSON.parse(localStorage.getItem(`${platform}Config`)!)
// split username/repo // split username/repo
const repoUrl = customConfig.repo const repoUrl = customConfig.repo
@ -62,7 +62,7 @@ function getDir() {
* @param {string} filename * @param {string} filename
* @returns {string} `时间戳+uuid` * @returns {string} `时间戳+uuid`
*/ */
function getDateFilename(filename) { function getDateFilename(filename: string) {
const currentTimestamp = new Date().getTime() const currentTimestamp = new Date().getTime()
const fileSuffix = filename.split(`.`)[1] const fileSuffix = filename.split(`.`)[1]
return `${currentTimestamp}-${uuidv4()}.${fileSuffix}` return `${currentTimestamp}-${uuidv4()}.${fileSuffix}`
@ -72,7 +72,7 @@ function getDateFilename(filename) {
// GitHub File Upload // GitHub File Upload
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
async function ghFileUpload(content, filename) { async function ghFileUpload(content: string, filename: string) {
const useDefault = localStorage.getItem(`imgHost`) === `default` const useDefault = localStorage.getItem(`imgHost`) === `default`
const { username, repo, branch, accessToken } = getConfig( const { username, repo, branch, accessToken } = getConfig(
useDefault, useDefault,
@ -81,7 +81,18 @@ async function ghFileUpload(content, filename) {
const dir = getDir() const dir = getDir()
const url = `https://api.github.com/repos/${username}/${repo}/contents/${dir}/` const url = `https://api.github.com/repos/${username}/${repo}/contents/${dir}/`
const dateFilename = getDateFilename(filename) 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, url: url + dateFilename,
method: `put`, method: `put`,
headers: { headers: {
@ -95,7 +106,7 @@ async function ghFileUpload(content, filename) {
}) })
const githubResourceUrl = `raw.githubusercontent.com/${username}/${repo}/${branch}/` const githubResourceUrl = `raw.githubusercontent.com/${username}/${repo}/${branch}/`
const cdnResourceUrl = `fastly.jsdelivr.net/gh/${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 return useDefault
? res.content.download_url.replace(githubResourceUrl, cdnResourceUrl) ? res.content.download_url.replace(githubResourceUrl, cdnResourceUrl)
: res.content.download_url : res.content.download_url
@ -105,13 +116,24 @@ async function ghFileUpload(content, filename) {
// Gitee File Upload // Gitee File Upload
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
async function giteeUpload(content, filename) { async function giteeUpload(content: any, filename: string) {
const useDefault = localStorage.getItem(`imgHost`) === `default` const useDefault = localStorage.getItem(`imgHost`) === `default`
const { username, repo, branch, accessToken } = getConfig(useDefault, `gitee`) const { username, repo, branch, accessToken } = getConfig(useDefault, `gitee`)
const dir = getDir() const dir = getDir()
const dateFilename = getDateFilename(filename) const dateFilename = getDateFilename(filename)
const url = `https://gitee.com/api/v5/repos/${username}/${repo}/contents/${dir}/${dateFilename}` 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, url,
method: `POST`, method: `POST`,
data: { data: {
@ -129,7 +151,10 @@ async function giteeUpload(content, filename) {
// Qiniu File Upload // 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 policy = JSON.stringify(putPolicy)
const encoded = base64encode(utf16to8(policy)) const encoded = base64encode(utf16to8(policy))
const hash = CryptoJS.HmacSHA1(encoded, secretKey) const hash = CryptoJS.HmacSHA1(encoded, secretKey)
@ -137,9 +162,9 @@ function getQiniuToken(accessKey, secretKey, putPolicy) {
return `${accessKey}:${safe64(encodedSigned)}:${encoded}` return `${accessKey}:${safe64(encodedSigned)}:${encoded}`
} }
async function qiniuUpload(file) { async function qiniuUpload(file: File) {
const { accessKey, secretKey, bucket, region, path, domain } = JSON.parse( const { accessKey, secretKey, bucket, region, path, domain } = JSON.parse(
localStorage.getItem(`qiniuConfig`), localStorage.getItem(`qiniuConfig`)!,
) )
const token = getQiniuToken(accessKey, secretKey, { const token = getQiniuToken(accessKey, secretKey, {
scope: bucket, scope: bucket,
@ -167,10 +192,10 @@ async function qiniuUpload(file) {
// AliOSS File Upload // AliOSS File Upload
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
async function aliOSSFileUpload(file) { async function aliOSSFileUpload(file: File) {
const dateFilename = getDateFilename(file.name) const dateFilename = getDateFilename(file.name)
const { region, bucket, accessKeyId, accessKeySecret, useSSL, cdnHost, path } 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 dir = path ? `${path}/${dateFilename}` : dateFilename
const secure = useSSL === undefined || useSSL const secure = useSSL === undefined || useSSL
const protocol = secure ? `https` : `http` const protocol = secure ? `https` : `http`
@ -195,10 +220,10 @@ async function aliOSSFileUpload(file) {
// TxCOS File Upload // TxCOS File Upload
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
async function txCOSFileUpload(file) { async function txCOSFileUpload(file: File) {
const dateFilename = getDateFilename(file.name) const dateFilename = getDateFilename(file.name)
const { secretId, secretKey, bucket, region, path, cdnHost } = JSON.parse( const { secretId, secretKey, bucket, region, path, cdnHost } = JSON.parse(
localStorage.getItem(`txCOSConfig`), localStorage.getItem(`txCOSConfig`)!,
) )
const cos = new COS({ const cos = new COS({
SecretId: secretId, SecretId: secretId,
@ -235,13 +260,13 @@ async function txCOSFileUpload(file) {
// Minio File Upload // Minio File Upload
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
async function minioFileUpload(content, filename) { async function minioFileUpload(content: string, filename: string) {
const dateFilename = getDateFilename(filename) const dateFilename = getDateFilename(filename)
const { endpoint, port, useSSL, bucket, accessKey, secretKey } = JSON.parse( const { endpoint, port, useSSL, bucket, accessKey, secretKey } = JSON.parse(
localStorage.getItem(`minioConfig`), localStorage.getItem(`minioConfig`)!,
) )
const buffer = Buffer(content, `base64`) const buffer = Buffer(content, `base64`)
const conf = { const conf: Minio.ClientOptions = {
endPoint: endpoint, endPoint: endpoint,
useSSL, useSSL,
accessKey, accessKey,
@ -278,7 +303,7 @@ async function minioFileUpload(content, filename) {
// formCustom File Upload // formCustom File Upload
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
async function formCustomUpload(content, file) { async function formCustomUpload(content: string, file: File) {
const str = ` const str = `
async (CUSTOM_ARG) => { async (CUSTOM_ARG) => {
${localStorage.getItem(`formCustomConfig`)} ${localStorage.getItem(`formCustomConfig`)}
@ -304,16 +329,18 @@ async function formCustomUpload(content, file) {
errCb: reject, // 上传失败调用的函数 errCb: reject, // 上传失败调用的函数
} }
// eslint-disable-next-line no-eval // eslint-disable-next-line no-eval
eval(str)(exportObj).catch((err) => { eval(str)(exportObj).catch((err: any) => {
console.error(err) console.error(err)
reject(err) reject(err)
}) })
}) })
} }
function fileUpload(content, file) { function fileUpload(content: string, file: File) {
const imgHost = localStorage.getItem(`imgHost`) const imgHost = localStorage.getItem(`imgHost`)
!imgHost && localStorage.setItem(`imgHost`, `default`) if (!imgHost) {
localStorage.setItem(`imgHost`, `default`)
}
switch (imgHost) { switch (imgHost) {
case `aliOSS`: case `aliOSS`:
return aliOSSFileUpload(file) 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 prettierPluginBabel from 'prettier/plugins/babel'
import * as prettierPluginEstree from 'prettier/plugins/estree' import * as prettierPluginEstree from 'prettier/plugins/estree'
import * as prettierPluginCss from 'prettier/plugins/postcss' import * as prettierPluginCss from 'prettier/plugins/postcss'
import type { PropertiesHyphen } from 'csstype'
import { prefix } from '@/config' import { prefix } from '@/config'
import type { Block, Inline, Theme } from '@/types'
export function addPrefix(str) { export function addPrefix(str: string) {
return `${prefix}__${str}` 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 newTheme = JSON.parse(JSON.stringify(theme))
const { fontSize, color } = options const { fontSize, color } = options
if (fontSize) { if (fontSize) {
for (let i = 1; i <= 4; i++) { for (let i = 1; i <= 4; i++) {
const v = newTheme.block[`h${i}`][`font-size`] 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) { if (color) {
newTheme.base[`--md-primary-color`] = 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 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) => { keys.forEach((key) => {
if (source[key]) { if (source[key]) {
target[key] = Object.assign(target[key] || {}, 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`, `h1`,
`h2`, `h2`,
`h3`, `h3`,
@ -52,7 +57,7 @@ export function customCssWithTemplate(jsonString, color, theme) {
`ul`, `ul`,
`ol`, `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.block, jsonString, blockKeys)
mergeProperties(newTheme.inline, jsonString, inlineKeys) mergeProperties(newTheme.inline, jsonString, inlineKeys)
@ -65,16 +70,16 @@ export function customCssWithTemplate(jsonString, color, theme) {
* @param {string} css - CSS * @param {string} css - CSS
* @returns {object} - JSON CSS * @returns {object} - JSON CSS
*/ */
export function css2json(css) { export function css2json(css: string): Partial<Record<Block | Inline, PropertiesHyphen>> {
// 去除所有 CSS 注释 // 去除所有 CSS 注释
css = css.replace(/\/\*[\s\S]*?\*\//g, ``) css = css.replace(/\/\*[\s\S]*?\*\//g, ``)
const json = {} const json: Partial<Record<Block | Inline, PropertiesHyphen>> = {}
// 辅助函数:将声明数组转换为对象 // 辅助函数:将声明数组转换为对象
const toObject = array => const toObject = (array: any[]) =>
array.reduce((obj, item) => { array.reduce<{ [k: string]: string }>((obj, item) => {
const [property, value] = item.split(`:`).map(part => part.trim()) const [property, value] = item.split(`:`).map((part: string) => part.trim())
if (property) if (property)
obj[property] = value obj[property] = value
return obj return obj
@ -93,7 +98,7 @@ export function css2json(css) {
// 获取选择器并去除空格 // 获取选择器并去除空格
const selectors = css.substring(0, lbracket) const selectors = css.substring(0, lbracket)
.split(`,`) .split(`,`)
.map(selector => selector.trim()) .map(selector => selector.trim()) as (Block | Inline)[]
const declarationObj = toObject(declarations) const declarationObj = toObject(declarations)
@ -106,6 +111,8 @@ export function css2json(css) {
css = css.slice(rbracket + 1).trim() css = css.slice(rbracket + 1).trim()
} }
console.log(`json`, json)
return json return json
} }
@ -115,7 +122,7 @@ export function css2json(css) {
* @param {'markdown' | 'css'} [type] - 使'markdown' * @param {'markdown' | 'css'} [type] - 使'markdown'
* @returns {Promise<string>} - * @returns {Promise<string>} -
*/ */
export async function formatDoc(content, type = `markdown`) { export async function formatDoc(content: string, type: `markdown` | `css` = `markdown`) {
const plugins = { const plugins = {
markdown: [prettierPluginMarkdown, prettierPluginBabel, prettierPluginEstree], markdown: [prettierPluginMarkdown, prettierPluginBabel, prettierPluginEstree],
css: [prettierPluginCss], css: [prettierPluginCss],
@ -132,7 +139,7 @@ export async function formatDoc(content, type = `markdown`) {
* Markdown * Markdown
* @param {string} doc - * @param {string} doc -
*/ */
export function downloadMD(doc) { export function downloadMD(doc: string) {
const downLink = document.createElement(`a`) const downLink = document.createElement(`a`)
downLink.download = `content.md` downLink.download = `content.md`
@ -149,7 +156,7 @@ export function downloadMD(doc) {
* HTML * HTML
*/ */
export function exportHTML() { export function exportHTML() {
const element = document.querySelector(`#output`) const element = document.querySelector(`#output`)!
setStyles(element) setStyles(element)
const htmlStr = element.innerHTML const htmlStr = element.innerHTML
@ -166,14 +173,14 @@ export function exportHTML() {
downLink.click() downLink.click()
document.body.removeChild(downLink) document.body.removeChild(downLink)
function setStyles(element) { function setStyles(element: Element) {
/** /**
* DOM * DOM
* @param {DOM } element DOM * @param {DOM } element DOM
* @param {} excludes 使 * @param {} excludes 使
* @returns * @returns
*/ */
function getElementStyles(element, excludes = [`width`, `height`]) { function getElementStyles(element: Element, excludes = [`width`, `height`]) {
const styles = getComputedStyle(element, null) const styles = getComputedStyle(element, null)
return Object.entries(styles) return Object.entries(styles)
.filter( .filter(
@ -188,15 +195,13 @@ export function exportHTML() {
case isCode(element): case isCode(element):
case isSpan(element): case isSpan(element):
element.setAttribute(`style`, getElementStyles(element)) element.setAttribute(`style`, getElementStyles(element))
// eslint-disable-next-line no-fallthrough
default:
} }
if (element.children.length) { if (element.children.length) {
Array.from(element.children).forEach(child => setStyles(child)) Array.from(element.children).forEach(child => setStyles(child))
} }
// 判断是否是包裹代码块的 pre 元素 // 判断是否是包裹代码块的 pre 元素
function isPre(element) { function isPre(element: Element) {
return ( return (
element.tagName === `PRE` element.tagName === `PRE`
&& Array.from(element.classList).includes(`code__pre`) && Array.from(element.classList).includes(`code__pre`)
@ -204,16 +209,19 @@ export function exportHTML() {
} }
// 判断是否是包裹代码块的 code 元素 // 判断是否是包裹代码块的 code 元素
function isCode(element) { function isCode(element: Element | null) {
if (element == null) {
return false
}
return element.tagName === `CODE` return element.tagName === `CODE`
} }
// 判断是否是包裹代码字符的 span 元素 // 判断是否是包裹代码字符的 span 元素
function isSpan(element) { function isSpan(element: Element) {
return ( return (
element.tagName === `SPAN` element.tagName === `SPAN`
&& (isCode(element.parentElement) && (isCode(element.parentElement)
|| isCode(element.parentElement.parentElement)) || isCode((element.parentElement!).parentElement))
) )
} }
} }
@ -228,7 +236,7 @@ export function exportHTML() {
* @param {number} options.cols - * @param {number} options.cols -
* @returns {string} Markdown * @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 = `` let table = ``
for (let i = 0; i < rows + 2; ++i) { for (let i = 0; i < rows + 2; ++i) {
table += `| ` table += `| `
@ -244,16 +252,16 @@ export function createTable({ data, rows, cols }) {
return table return table
} }
export function toBase64(file) { export function toBase64(file: Blob) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const reader = new FileReader() const reader = new FileReader()
reader.readAsDataURL(file) reader.readAsDataURL(file)
reader.onload = () => resolve(reader.result.split(`,`).pop()) reader.onload = () => resolve((reader.result as string).split(`,`).pop())
reader.onerror = error => reject(error) reader.onerror = error => reject(error)
}) })
} }
export function checkImage(file) { export function checkImage(file: File) {
// 检查文件名后缀 // 检查文件名后缀
const isValidSuffix = /\.(?:gif|jpe?g|png)$/i.test(file.name) const isValidSuffix = /\.(?:gif|jpe?g|png)$/i.test(file.name)
if (!isValidSuffix) { if (!isValidSuffix) {
@ -277,27 +285,27 @@ export function checkImage(file) {
/** /**
* *
* @param {*} str * @param {string} str
* @returns string * @returns string
*/ */
export function removeLeft(str) { export function removeLeft(str: string) {
const lines = str.split(`\n`) const lines = str.split(`\n`)
// 获取应该删除的空白符数量 // 获取应该删除的空白符数量
const minSpaceNum = lines const minSpaceNum = lines
.filter(item => item.trim()) .filter(item => item.trim())
.map(item => item.match(/(^\s+)?/)[0].length) .map(item => (item.match(/(^\s+)?/)!)[0].length)
.sort((a, b) => a - b)[0] .sort((a, b) => a - b)[0]
// 删除空白符 // 删除空白符
return lines.map(item => item.slice(minSpaceNum)).join(`\n`) return lines.map(item => item.slice(minSpaceNum)).join(`\n`)
} }
export function solveWeChatImage() { export function solveWeChatImage() {
const clipboardDiv = document.getElementById(`output`) const clipboardDiv = document.getElementById(`output`)!
const images = clipboardDiv.getElementsByTagName(`img`) const images = clipboardDiv.getElementsByTagName(`img`)
for (let i = 0; i < images.length; i++) { for (let i = 0; i < images.length; i++) {
const image = images[i] const image = images[i]
const width = image.getAttribute(`width`) const width = image.getAttribute(`width`)!
const height = image.getAttribute(`height`) const height = image.getAttribute(`height`)!
image.removeAttribute(`width`) image.removeAttribute(`width`)
image.removeAttribute(`height`) image.removeAttribute(`height`)
image.style.width = width image.style.width = width
@ -305,7 +313,7 @@ export function solveWeChatImage() {
} }
} }
export function mergeCss(html) { export function mergeCss(html: string) {
return juice(html, { return juice(html, {
inlinePseudoElements: true, inlinePseudoElements: true,
preserveImportant: true, preserveImportant: true,

View File

@ -109,13 +109,13 @@ export function initRenderer(opts: IOpts) {
return footnoteIndex return footnoteIndex
} }
function reset(newOpts: IOpts): void { function reset(newOpts: Partial<IOpts>): void {
footnotes.length = 0 footnotes.length = 0
footnoteIndex = 0 footnoteIndex = 0
setOptions(newOpts) setOptions(newOpts)
} }
function setOptions(newOpts: IOpts): void { function setOptions(newOpts: Partial<IOpts>): void {
opts = { ...opts, ...newOpts } opts = { ...opts, ...newOpts }
styleMapping = buildTheme(opts) styleMapping = buildTheme(opts)
} }
@ -197,7 +197,7 @@ export function initRenderer(opts: IOpts) {
}, },
image({ href, title, text }: Tokens.Image): string { 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 figureStyles = styles(`figure`)
const imgStyles = styles(`image`) const imgStyles = styles(`image`)
return `<figure ${figureStyles}><img ${imgStyles} src="${href}" title="${title}" alt="${text}"/>${subText}</figure>` 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 = `` let out = ``
const len = str.length const len = str.length
@ -22,7 +22,7 @@ export function utf16to8(str) {
return out return out
} }
export function utf8to16(str) { export function utf8to16(str: string) {
let out = `` let out = ``
let i = 0 let i = 0
const len = str.length const len = str.length
@ -196,7 +196,7 @@ const base64DecodeChars = [
-1, -1,
] ]
export function base64encode(str) { export function base64encode(str: string) {
let out = `` let out = ``
let i = 0 let i = 0
const len = str.length const len = str.length
@ -232,7 +232,7 @@ export function base64encode(str) {
return out return out
} }
export function base64decode(str) { export function base64decode(str: string) {
let c1, c2, c3, c4 let c1, c2, c3, c4
let i = 0 let i = 0
const len = str.length const len = str.length
@ -283,7 +283,7 @@ export function base64decode(str) {
return out return out
} }
export function safe64(base64) { export function safe64(base64: string) {
base64 = base64.replace(/\+/g, `-`) base64 = base64.replace(/\+/g, `-`)
base64 = base64.replace(/\//g, `_`) base64 = base64.replace(/\//g, `_`)
return base64 return base64