markdown-to-file
Version:
Convert Markdown to PDF
293 lines (282 loc) • 7.95 kB
JavaScript
const fs = require(`fs`)
const path = require(`path`)
const pkg = require(`./package.json`)
let log = console.log
log = () => {}
const print = (...arg) => {
console.log(...arg)
}
global.SET = (key, val) => {
log(`SET`, key, val)
global[`${pkg.name}_${key}`] = val
return val
}
global.GET = (key) => {
return global[`${pkg.name}_${key}`]
}
global.SET(`pkg`, pkg)
const DeepProxy = require('proxy-deep')
let vscode = {}
try {
vscode = require('vscode')
global.SET(`isVsCode`, true)
} catch (error) {
let [input, type = `pdf`, ...arg] = process.argv.slice(2)
const cli = type ? parseArgv([`type=${type}`, ...arg]) : {}
Object.assign(cli, Object.entries(cli).forEach(([key, val]) =>{
;[
`convertOnSaveExclude`,
`styles`,
].includes(key) && (cli[key] = val.split(`,`))
}))
if(typeof(input) !== `string` || fs.existsSync(input) === false) {
print(`${pkg.name} v${pkg.version}`)
print(``)
print(`Converts Markdown files to pdf, html, png or jpeg files.`)
print(``)
print(`Usage: m2f <md> [type] [options...]`)
print(` md -- path to markdown file to convert`)
print(` type -- Output file format: pdf|html|jpeg|png|all`)
print(` options -- See: https://github.com/wll8/markdown-to-file`)
print(` Eg: `)
print(` m2f README.md`)
print(` m2f README.md html`)
print(` m2f README.md pdf outputDirectory=./mydoc/ markdown-it-include.enable=false`)
print(` m2f README.md styles=1.css,2.css`)
process.exit()
}
input = path.isAbsolute(input) ? input : `${process.cwd()}/${input}`
const properties = global.GET(`pkg`).contributes.configuration.properties
const defaultConfig = Object.entries(properties).reduce((acc, [key, val]) => {
acc = deepSet(acc, key, val.default)
return acc
}, {})
const userConfig = JSON.parse(JSON.stringify(defaultConfig))
Object.entries(cli).forEach(([key, val]) => {
deepSet(userConfig, `markdown-pdf.${key}`, val)
})
print(`options:`, cli)
global.SET(`defaultConfig`, defaultConfig)
global.SET(`userConfig`, userConfig)
// 在非 vscode 的环境中运行程序
vscode = {
// Uri: {
// // 获取某个文件的 file:// 协议地址
// file(file) {
// // return 'file:///d%3A/temp/readme_tmp.html'
// const fileUrl = encodeURI(`file:///${file}`.replace(/[\/\\]/g, `/`))
// log(`fileUrl`, fileUrl)
// return fileUrl
// },
// },
Uri: require(`vscode-uri`).URI,
workspace: {
getConfiguration(...arg) {
const [type] = arg
const config = global.GET(`userConfig`)[type]
return config || {}
},
},
window: {
withProgress(info, fn) {
print(`::withProgress`, info.title)
fn()
},
showWarningMessage(...arg) {
print(`::showWarningMessage`, ...arg)
return proxyObj(()=>{})
},
setStatusBarMessage(title, time) {
print(`::setStatusBarMessage`, title)
return proxyObj(()=>{})
},
activeTextEditor: {
document: {
uri: {
fsPath: input,
},
languageId: `markdown`,
getText() {
return require(`fs`).readFileSync(input, `utf8`)
},
},
},
},
commands: {
// 注册命令
registerCommand(out, fn) {
log(`::registerCommand`, out, fn)
;`extension.markdown-pdf.${cli.type}` === out && fn();
return ``
}
}
}
}
const vscode2 = proxyObj(vscode)
global.SET(`util.proxyObj`, proxyObj)
module.exports = vscode2
/**
* 代理一个对象
* [ ] fix: 对不存在的对象调用 toString 时会报错 TypeError: Cannot convert object to primitive value
* - 这是由于不存在的属性默认是一个 Proxy 对象, 而 String(Proxy) 出现了错误
* @param {*} obj
* @returns
*/
function proxyObj(obj) {
const newObj = new DeepProxy(obj, {
get(target, path, receiver) {
const val = Reflect.get(target, path, receiver)
if(typeof(val) !== `function`) {
proxyLog(`get`, [...this.path, path], {
getVal: val,
})
}
if(val === undefined) {
const fn = function (){}
let proxyFn
// fix: 不用使用 直接使用 this.nest 否则会报错 TypeError: Cannot convert a Symbol value to a string
// see: https://www.mattzeunert.com/2016/07/20/proxy-symbol-tostring.html
// see: https://gist.github.com/dotproto/f4bb65ad97fc90bb4217434f39297feb
fn[Symbol.toPrimitive] = function () { return ``}
proxyFn = new Proxy(fn, {
get : (target, name) => {
if (!(name in target)) {
return proxyFn
}
return target[name]
}
})
return proxyFn
} else {
// 是否是基本数据类型
const isBase = [
`string`,
`number`,
`bigint`,
`boolean`,
`null`,
`undefined`,
`symbol`,
].includes(typeof(val))
if(isBase) {
return val
} else {
// 只读属性不支持代理
if(isPropertyWritable(target, path)) {
return this.nest(val)
} else {
return val
}
}
}
},
apply(target, thisArg, argList) {
const fnRes = target(...argList) || (() => {})
proxyLog(`run`, this.path, {
fnArg: argList,
fnRes,
})
return fnRes
},
set(target, key, val) {
proxyLog(`set`, [...this.path, key], {
setVal: val,
})
target[key] = val
},
})
return newObj
}
/**
* 在代理函数中打印 log
* @param {*} type
* @param {*} path
* @param {*} param2
*/
function proxyLog(type, path, {
fnArg, // 函数参数
fnRes, // 函数返回值
getVal, // 属性值
setVal, // 设置值
}) {
try {
const pathStr = path.join(`.`)
if(type === `set`) {
log(`${type}> ${pathStr} setVal: ${setVal}`)
}
if(type === `get` && getVal !== undefined) {
log(`${type}> ${pathStr} getVal: ${getVal}`)
}
if(type === `run`) {
log(`${type}> ${pathStr} fnArg: ${fnArg.filter(item => (typeof(item) !== `function`)).join(`,`)}`)
}
{
const val = setVal || getVal || fnRes
typeof(val) !== `function` && log(`${type}Val>`, val);
}
} catch (error) {
// log(error)
}
}
/**
* 判断对象的属性是否可写
* @param {*} obj
* @param {*} prop
* @returns
*/
function isPropertyWritable(obj, prop) {
const value = obj[prop];
const sym = Symbol();
try {
obj[prop] = sym;
} catch(ex) {
// 解决在严格模式下报错问题
return false;
}
const isWritable = obj[prop] === sym;
obj[prop] = value; // 恢复原来的值
return isWritable;
}
/**
* 解析命令行参数
* @param {*} arr
* @returns
*/
function parseArgv(arr) {
return (arr || process.argv.slice(2)).reduce((acc, arg) => {
let [k, ...v] = arg.split(`=`)
v = v.join(`=`) // 把带有 = 的值合并为字符串
acc[k] = v === `` // 没有值时, 则表示为 true
? true
: (
/^(true|false)$/.test(v) // 转换指明的 true/false
? v === `true`
: (
/[\d|.]+/.test(v)
? (isNaN(Number(v)) ? v : Number(v)) // 如果转换为数字失败, 则使用原始字符
: v
)
)
return acc
}, {})
}
/**
* 深层设置对象值
* @param {*} object
* @param {*} keys
* @param {*} val
* @returns
*/
function deepSet(object, keys, val) {
keys = Array.isArray(keys) ? keys : keys
.replace(/\[/g, `.`)
.replace(/\]/g, ``)
.split(`.`)
if (keys.length > 1) {
object[keys[0]] = object[keys[0]] || {}
deepSet(object[keys[0]], keys.slice(1), val)
return object
}
object[keys[0]] = val
return object
}