Зачем вам нужен wepy для Vue
«Zuanzhuanzhuanzhuan» — это небольшая программа, разработанная нашей компанией с помощью wepy, которая по функциям очень похожа на APP и реализует большое количество функциональных страниц, а новому бизнес-проекту H5 часто требуются некоторые общедоступные страницы и функции в процессе разработки. Однако у нового проекта есть свои особенности: стоимость переразработки этих страниц очень высока, но конвертировать код апплета в VUE будет намного проще, поэтому такой инструмент конвертации необходим.
В этой статье вы познакомитесь с процессом синтаксического анализа и преобразования HTML, CSS и JavaScript в реальном бою.
Если вы нашли это полезным после прочтения, пожалуйста, поставьте лайк~
Обзор ТЧА
Полное название AST называется абстрактным синтаксическим деревом.В Интернете есть много объяснений концепции и демонстраций AST.На самом деле его можно сравнить с XML.В настоящее время многие популярные языки можно разобрать в синтаксическое дерево через AST, который также можно рассматривать как JSON. Эти языки включают, но не ограничиваются: CSS, HTML, JavaScript, PHP, Java, SQL и т. д., чтобы привести простой пример:
var a = 1;
Этот простой код JavaScript будет преобразован в «несколько сложное» синтаксическое дерево с помощью AST:
Это предложение представляет собой объявление переменной и присвоение на грамматическом уровне, поэтому родительский узел является узлом типа, тип которого является VariableDeclaration (объявление переменной).Содержимое объявления включает две части: идентификатор: a и начальное значение: 1
Это простое преобразование AST, вы можете пройтиastexplorerВизуально протестируйте больше кода.
В чем польза АСТ
AST может преобразовывать код в синтаксическое дерево JSON. На основе синтаксического дерева можно выполнять преобразование кода, замену и многие другие операции. Фактически, AST широко используется. Многие плагины, такие как less/sass, eslint и TypeScript, используются в нашем разработки осуществляются на основе АСТ.
Требования этой статьи также могут быть достигнуты заменой текста, но это требует большой регуляризации, и высок риск ошибок.Если вы используете AST, вы можете легко это сделать.
Принцип АСТ
Первая версия кода обработки AST разделена на следующие два шага:
лексический анализ
Лексический анализ разделит ваш код по-крупному, разделив его в соответствии с каждым написанным вами символом (он отбросит бесполезное содержимое, такое как комментарии, пробелы и т. д.), а затем разделит действительный код на токены один за другим.
Разбор
Затем AST обрабатывает и упаковывает эти токены в соответствии с определенными «правилами», которые различаются для каждого парсера, но делают примерно одно и то же, в том числе:
- Сопоставьте каждую лексему со встроенными грамматическими правилами синтаксического анализатора, такими как упомянутое выше var a = 1; этот код будет преобразован в тип VariableDeclaration.
- По грамматической структуре самого кода токены собираются в древовидную структуру.
Различные парсеры AST
Для каждого языка существует множество синтаксических анализаторов, способ их использования и результаты, которые они генерируют, различаются, и разработчики могут выбрать правильный синтаксический анализатор в соответствии со своими потребностями.
JavaScript
- Самым известным является babylon, потому что он является королевским парсером babel.Вообще, чаще используется библиотека AST JavaScript.
- acron:babylon является форком этой библиотеки
HTML
- htmlparser2: чаще используется
- parse5: не очень прост в использовании, он также должен взаимодействовать с библиотекой классов jsdom.
CSS
- cssom, csstree и т. д.
- less/sass
XML
- Xmldom
wepy для инструмента VUE
Затем мы начинаем настоящий бой.Технологии, которые мы используем для этого требования:
- node
- командир: используется для записи вызовов команд, связанных с командной строкой.
- fs-extra: обновленная версия библиотеки классов fs, которая в основном повышает удобство операций с файлами узлов и обеспечивает инкапсуляцию Promise.
- Xmldom: Анализ XML
- htmlparser2: анализировать HTML
- less: Parse css (все наши проекты объединены less, поэтому достаточно напрямую парсить less)
- babylon: парсинг JavaScript
- @babel/types: библиотека типов js для поиска, проверки и создания соответствующих узлов дерева кода.
- @babel/traverse: облегчает различные формы обхода синтаксического дерева JavaScript.
- @babel/template: распечатайте обработанное синтаксическое дерево в фиксированный шаблон
- @babel/generator: создание обработанного текстового содержимого JavaScript.
цель конверсии
Давайте сначала посмотрим на простое сравнение кода wepy и VUE:
//wepy版
<template>
<view class="userCard">
<view class="basic">
<view class="avatar">
<image src="{{info.portrait}}"></image>
</view>
<view class="info">
<view class="name">{{info.nickName}}</view>
<view class="label" wx:if="{{info.label}}">
<view class="label-text" wx:for="{{info.label}}">{{item}}</view>
</view>
<view class="onsale">在售宝贝{{sellingCount}}</view>
<view class="follow " @tap="follow">{{isFollow ? '取消关注' : '关注'}}</view>
</view>
</view>
</view>
</template>
<style lang="less" rel="stylesheet/less" scoped>
.userCard {
position:relative;
background: #FFFFFF;
box-shadow: 0 0 10rpx 0 rgba(162,167,182,0.31);
border-radius: 3rpx;
padding:20rpx;
position: relative;
}
/* css太多了,省略其他内容 */
</style>
<script>
import wepy from 'wepy'
export default class UserCard extends wepy.component {
props = {
info:{
type:Object,
default:{}
}
}
data = {
isFollow: false,
}
methods = {
async follow() {
await someHttpRequest() //请求某个接口
this.isFollow = !this.isFollow
this.$apply()
}
}
computed = {
sellingCount(){
return this.info.sellingCount || 1
}
}
onLoad(){
this.$log('view')
}
}
</script>
//VUE版
<template>
<div class="userCard">
<div class="basic">
<div class="avatar">
<img src="info.portrait"></img>
</view>
<view class="info">
<view class="name">{{info.nickName}}</view>
<view class="label" v-if="info.label">
<view class="label-text" v-for="(item,key) in info.label">{{item}}</view>
</view>
<view class="onsale">在售宝贝{{sellingCount}}</view>
<view class="follow " @click="follow">{{isFollow ? '取消关注' : '关注'}}</view>
</view>
</view>
</view>
</template>
<style lang="less" rel="stylesheet/less" scoped>
.userCard {
position:relative;
background: #FFFFFF;
box-shadow: 0 0 10rpx 0 rgba(162,167,182,0.31);
border-radius: 3*@px;
padding:20*@px;
position: relative;
}
/* css太多了,省略其他内容 */
</style>
<script>
export default {
props : {
info:{
type:Object,
default:{}
}
}
data(){
return {
isFollow: false,
}
}
methods : {
async follow() {
await someHttpRequest() //请求某个接口
this.isFollow = !this.isFollow
}
}
computed : {
sellingCount(){
return this.info.sellingCount || 1
}
}
created() {
this.$log('view')
}
}
</script>
Реализация кода конверсии
Давайте сначала напишем метод входа для чтения файла
const cwdPath = process.cwd()
const fse = require('fs-extra')
const convert = async function(filepath){
let fileText = await fse.readFile(filepath, 'utf-8');
fileHandle(fileText.toString(),filepath)
}
const fileHandle = async function(fileText,filepath){
//dosth...
}
convert(`${cwdPath}/demo.wpy`)
В функции fileHandle мы можем получить текстовое содержимое кода, сначала разберем его в XML, а шаблон, css и JavaScript разделим на три части. Некоторые студенты могут спросить, почему это не соответствует напрямую, потому что код разработчика может иметь много стилей, таких как стили из двух частей, и может быть много неожиданных ситуаций, которые нельзя учесть с помощью регуляризации, что также является смыслом использования AST .
//首先需要完成Xml解析及路径定义:
//初始化一个Xml解析器
let xmlParser = new XmlParser(),
//解析代码内容
xmlParserObj = xmlParser.parse(fileText),
//正则匹配产生文件名
filenameMatch = filepath.match(/([^\.|\/|\\]+)\.\w+$/),
//如果没有名字默认为blank
filename = filenameMatch.length > 1 ? filenameMatch[1] : 'blank',
//计算出模板文件存放目录dist的绝对地址
filedir = utils.createDistPath(filepath),
//最终产出文件地址
targetFilePath = `${filedir}/${filename}.vue`
//接下来创建目标目录
try {
fse.ensureDirSync(filedir)
}catch (e){
throw new Error(e)
}
//最后根据xml解析出来的节点类型进行不同处理
for(let i = 0 ;i < xmlParserObj.childNodes.length;i++){
let v = xmlParserObj.childNodes[i]
if(v.nodeName === 'style'){
typesHandler.style(v,filedir,filename,targetFilePath)
}
if(v.nodeName === 'template'){
typesHandler.template(v,filedir,filename,targetFilePath)
}
if(v.nodeName === 'script'){
typesHandler.script(v,filedir,filename,targetFilePath)
}
}
//XmlParser定义
const Xmldom = require('xmldom')
const utils = require('../utils')
class XmlParser extends Parser {
constructor(){
super()
}
createParser(){
return new Xmldom.DOMParser({errorHandler: {
warning (x) {
if (x.indexOf('missed value!!') > -1) {
// ignore warnings
} else
console.warn(x);
},
error (x) {
console.error(x);
}
}});
}
parse(fileText){
fileText = utils.replaceTagAndEventBind(fileText)
return this.createParser().parseFromString(fileText);
}
}
Логика обработки различных узлов определяется в объекте с именем typesHandler, Далее рассмотрим логику обработки различных типов фрагментов кода.
Из-за ограниченного места в этой статье перечислены только некоторые цели преобразования кода, которые на самом деле сложнее, чем эти.
Далее преобразуем код:
Обработка шаблона
цель конверсии
- Преобразование тега шаблона: преобразовать представление в div, преобразовать тег изображения в img
- Логическая оценка шаблона: преобразовать wx:if="{{info.label}}" в v-if="info.label"
- Цикл шаблона: wx:for="{{info.label}}" преобразуется в v-for="(item,key) в info.label"
- Привязка события: @tap="follow" преобразуется в @click="follow"
основной процесс
- Сначала разберите полученный целевой текст в синтаксическое дерево, затем выполните различные преобразования и, наконец, преобразуйте синтаксическое дерево в текст и запишите его в файл.
let templateContent = v.childNodes.toString(),
//初始化一个解析器
templateParser = new TemplateParser()
//生成语法树
templateParser.parse(templateContent).then((templateAst)=>{
//进行上述目标的转换
let convertedTemplate = templateConverter(templateAst)
//把语法树转成文本
templateConvertedString = templateParser.astToString(convertedTemplate)
templateConvertedString = `<template>\r\n${templateConvertedString}\r\n</template>\r\n`
fs.writeFile(targetFilePath,templateConvertedString, ()=>{
resolve()
});
}).catch((e)=>{
reject(e)
})
- TemplateParser — это простая библиотека классов обработки шаблонов AST, которую я инкапсулировал (поскольку используется библиотека классов htmlparser2, вызов метода этой библиотеки классов немного проблематичен), давайте посмотрим на код:
const Parser = require('./Parser') //基类
const htmlparser = require('htmlparser2') //html的AST类库
class TemplateParser extends Parser {
constructor(){
super()
}
/**
* HTML文本转AST方法
* @param scriptText
* @returns {Promise}
*/
parse(scriptText){
return new Promise((resolve, reject) => {
//先初始化一个domHandler
const handler = new htmlparser.DomHandler((error, dom)=>{
if (error) {
reject(error);
} else {
//在回调里拿到AST对象
resolve(dom);
}
});
//再初始化一个解析器
const parser = new htmlparser.Parser(handler);
//再通过write方法进行解析
parser.write(scriptText);
parser.end();
});
}
/**
* AST转文本方法
* @param ast
* @returns {string}
*/
astToString (ast) {
let str = '';
ast.forEach(item => {
if (item.type === 'text') {
str += item.data;
} else if (item.type === 'tag') {
str += '<' + item.name;
if (item.attribs) {
Object.keys(item.attribs).forEach(attr => {
str += ` ${attr}="${item.attribs[attr]}"`;
});
}
str += '>';
if (item.children && item.children.length) {
str += this.astToString(item.children);
}
str += `</${item.name}>`;
}
});
return str;
}
}
module.exports = TemplateParser
- 3. Далее мы рассмотрим конкретный процесс замены:
//html标签替换规则,可以添加更多
const tagConverterConfig = {
'view':'div',
'image':'img'
}
//属性替换规则,也可以加入更多
const attrConverterConfig = {
'wx:for':{
key:'v-for',
value:(str)=>{
return str.replace(/{{(.*)}}/,'(item,key) in $1')
}
},
'wx:if':{
key:'v-if',
value:(str)=>{
return str.replace(/{{(.*)}}/,'$1')
}
},
'@tap':{
key:'@click'
},
}
//替换入口方法
const templateConverter = function(ast){
for(let i = 0;i<ast.length;i++){
let node = ast[i]
//检测到是html节点
if(node.type === 'tag'){
//进行标签替换
if(tagConverterConfig[node.name]){
node.name = tagConverterConfig[node.name]
}
//进行属性替换
let attrs = {}
for(let k in node.attribs){
let target = attrConverterConfig[k]
if(target){
//分别替换属性名和属性值
attrs[target['key']] = target['value'] ?
target['value'](node.attribs[k]) :
node.attribs[k]
}else {
attrs[k] = node.attribs[k]
}
}
node.attribs = attrs
}
//因为是树状结构,所以需要进行递归
if(node.children){
templateConverter(node.children)
}
}
return ast
}
css обработка
цель конверсии
- заменить картинку на img
- Преобразовать единицу измерения rpx в *@px
основной процесс
- 1. Нам нужно деэкранировать текстовый код css, который мы получили первым, потому что в процессе разбора xml специальные символы в css были экранированы.Эта логика обработки очень проста, просто логика замены строки, поэтому инкапсуляция В utils инструментальный метод, в этой статье мы не будем вдаваться в подробности.
let styleText = utils.deEscape(v.childNodes.toString())
- 2. Судя по типу меньшего или обычного css по типу в атрибуте узла
if(v.attributes){
//检测css是哪种类型
for(let i in v.attributes){
let attr = v.attributes[i]
if(attr.name === 'lang'){
type = attr.value
}
}
}
- 3. Обработка меньшего содержания: используйте метод less.render() для преобразования less в css; если это css, просто обработайте styleText напрямую
less.render(styleText).then((output)=>{
//output是css内容对象
})
- 4. Сменить селектор изображения на img, и здесь нужно заменить еще теги, такие как текст, иконка, прокрутка-вид и т.д. Причины пробела повторяться не будут
const CSSOM = require('cssom') //css的AST解析器
const replaceTagClassName = function(replacedStyleText){
const replaceConfig = {}
//匹配标签选择器
const tagReg = /[^\.|#|\-|_](\b\w+\b)/g
//将css文本转换为语法树
const ast = CSSOM.parse(replacedStyleText),
styleRules = ast.cssRules
if(styleRules && styleRules.length){
//找到包含tag的className
styleRules.forEach(function(item){
//可能会有 view image {...}这多级选择器
let tags = item.selectorText.match(tagReg)
if(tags && tags.length){
let newName = ''
tags = tags.map((tag)=>{
tag = tag.trim()
if(tag === 'image')tag = 'img'
return tag
})
item.selectorText = tags.join(' ')
}
})
//使用toString方法可以把语法树转换为字符串
replacedStyleText = ast.toString()
}
return {replacedStyleText,replaceConfig}
}
- 5. Замените rpx на *@px
replacedStyleText = replacedStyleText.replace(/([\d\s]+)rpx/g,'$1*@px')
- 6. Запишите преобразованный код в файл
replacedStyleText = `<style scoped>\r\n${replacedStyleText}\r\n</style>\r\n`
fs.writeFile(targetFilePath,replacedStyleText,{
flag: 'a'
},()=>{
resolve()
});
Преобразование JavaScript
цель конверсии
- удалить ссылку на wepy
- Преобразование в запись объекта vue
- Удалить бесполезный код: this.$apply()
- корреспонденция жизненного цикла
основной процесс
Прежде чем понять, как конвертировать, давайте кратко разберемся с основным процессом конвертации JavaScript:
Заимствуя картинку у других авторов, видно, что процесс конвертации делится на три этапа: анализ -> конвертация -> генерация.
детали следующим образом:
- 1. Сначала преобразуйте узел xml в текст через toString
v.childNodes.toString()
- 2, а затем отмените побег (иначе сообщит об ошибке)
let javascriptContent = utils.deEscape(v.childNodes.toString())
- 3. Далее инициализируем парсер
let javascriptParser = new JavascriptParser()
Что инкапсулировано в этом парсере, смотрите в коде:
const Parser = require('./Parser') //基类
const babylon = require('babylon') //AST解析器
const generate = require('@babel/generator').default
const traverse = require('@babel/traverse').default
class JavascriptParser extends Parser {
constructor(){
super()
}
/**
* 解析前替换掉无用字符
* @param code
* @returns
*/
beforeParse(code){
return code.replace(/this\.\$apply\(\);?/gm,'').replace(/import\s+wepy\s+from\s+['"]wepy['"]/gm,'')
}
/**
* 文本内容解析成AST
* @param scriptText
* @returns {Promise}
*/
parse(scriptText){
return new Promise((resolve,reject)=>{
try {
const scriptParsed = babylon.parse(scriptText,{
sourceType:'module',
plugins: [
// "estree", //这个插件会导致解析的结果发生变化,因此去除,这本来是acron的插件
"jsx",
"flow",
"doExpressions",
"objectRestSpread",
"exportExtensions",
"classProperties",
"decorators",
"objectRestSpread",
"asyncGenerators",
"functionBind",
"functionSent",
"throwExpressions",
"templateInvalidEscapes"
]
})
resolve(scriptParsed)
}catch (e){
reject(e)
}
})
}
/**
* AST树遍历方法
* @param astObject
* @returns {*}
*/
traverse(astObject){
return traverse(astObject)
}
/**
* 模板或AST对象转文本方法
* @param astObject
* @param code
* @returns {*}
*/
generate(astObject,code){
const newScript = generate(astObject, {}, code)
return newScript
}
}
module.exports = JavascriptParser
Стоит отметить, что существует множество конфигураций плагинов для Babylon. Как их настроить, зависит от того, какой расширенный синтаксис используется в вашем коде. Для получения подробной информации обратитесь к документации или обрабатывайте в соответствии с сообщением об ошибке.
- 4. Перед парсингом можно удалить некоторый бесполезный код через метод beforeParse (эти коды обычно фиксированные, и их удобнее заменять сразу строками)
javascriptContent = javascriptParser.beforeParse(javascriptContent)
- 5, а затем разобрать текст в AST
javascriptParser.parse(javascriptContent)
- 6. Пройти через AST все дерево и выполнить различные преобразования кода
let {convertedJavascript,vistors} = componentConverter(javascriptAst)
ComponentConverter — это инкапсуляция метода преобразования.Процесс преобразования немного сложен.Давайте сначала разберемся с некоторыми понятиями.
Если мы получим объект AST, нам нужно сначала пройти его. Как его пройти? Если мы используем цикл или рекурсию для обхода такой сложной структуры JSON, это, несомненно, будет очень сложно, поэтому мы используем babel.traverseЭтот инструмент, документация:babel-traverse.
-
traverse принимает два параметра: объект AST и объект vistor.
-
vistor — это объект, который настраивает метод обхода
-
Существует два основных типа:
- Обход дерева: в основном за счет обработки обхода во время входа в узел и во время выхода из узла, а затем определить, какой тип узла выполнять соответствующую обработку после входа в узел
const componentVistor = {
enter(path) {
if (path.isIdentifier({ name: "n" })) {
path.node.name = "x";
}
},
exit(path){
//do sth
}
}
- Ход по типу: ход помогает найти все узлы соответствующего типа
const componentVistor = {
FunctionDeclaration(path) {
path.node.id.name = "x";
}
}
Код в этой статье в основном использует метод обхода дерева и выглядит следующим образом:
const componentVistor = {
enter(path) {
//判断如果是类属性
if (t.isClassProperty(path)) {
//根据不同类属性进行不同处理,把wepy的类属性写法提取出来,放到VUE模板中
switch (path.node.key.name){
case 'props':
vistors.props.handle(path.node.value)
break;
case 'data':
vistors.data.handle(path.node.value)
break;
case 'events':
vistors.events.handle(path.node.value)
break;
case 'computed':
vistors.computed.handle(path.node.value)
break;
case 'components':
vistors.components.handle(path.node.value)
break;
case 'watch':
vistors.watch.handle(path.node.value)
break;
case 'methods':
vistors.methods.handle(path.node.value)
break;
default:
console.info(path.node.key.name)
break;
}
}
//判断如果是类方法
if(t.isClassMethod(path)){
if(vistors.lifeCycle.is(path)){
vistors.lifeCycle.handle(path.node)
}else {
vistors.methods.handle(path.node)
}
}
}
}
Различные vistors в этой статье в основном делают одно, собирают различные атрибуты и методы класса, код базового класса:
class Vistor {
constructor() {
this.data = []
}
handle(path){
this.save(path)
}
save(path){
this.data.push(path)
}
getData(){
return this.data
}
}
module.exports = Vistor
Сюда нужно добавить@babel/typesЭта библиотека классов в основном предоставляет методы обнаружения, преобразования и генерации различных типов узлов в JavaScript AST, например:
//类型检测
if(t.isClassMethod(path)){
//如果是类方法
}
//创造一个对象节点
t.objectExpression(...)
Благодаря вышеописанной обработке мы собрали различные атрибуты и методы класса в wepy.Далее давайте посмотрим, как сгенерировать код записи vue.
- 7. Поместите преобразованное дерево AST в предопределенный шаблон шаблона.
convertedJavascript = componentTemplateBuilder(convertedJavascript,vistors)
Посмотрите, как определяется метод componentTemplateBuilder:
const componentTemplateBuilder = function(ast,vistors){
const buildRequire = template(componentTemplate);
ast = buildRequire({
PROPS: arrayToObject(vistors.props.getData()),
LIFECYCLE: arrayToObject(vistors.lifeCycle.getData()),
DATA: arrayToObject(vistors.data.getData()),
METHODS: arrayToObject(vistors.methods.getData()),
COMPUTED: arrayToObject(vistors.computed.getData()),
WATCH: arrayToObject(vistors.watch.getData()),
});
return ast
}
используется здесь@babel/templateОсновная функция этой библиотеки классов — собрать данные вашего кода в новый шаблон.Шаблон выглядит следующим образом:
const componentTemplate = `
export default {
data() {
return DATA
},
props:PROPS,
methods: METHODS,
computed: COMPUTED,
watch:WATCH,
}
`
* Жизненный цикл должен иметь дело с соответствующими отношениями, которые немного сложны и не будут описаны в этой статье.
- 8. Преобразуйте шаблон в текстовое содержимое и запишите его в файл
let codeText = `<script>\r\n${generate(convertedJavascript).code}\r\n</script>\r\n`
fs.writeFile(targetFilePath,codeText, ()=>{
resolve()
});
используется здесь@babel/generateБиблиотека классов, основная функция которой заключается в создании текстового формата из синтаксического дерева AST.
Код вышеуказанного процесса реализует общий процесс
const JavascriptParser = require('./lib/parser/JavascriptParser')
//先反转义
let javascriptContent = utils.deEscape(v.childNodes.toString()),
//初始化一个解析器
javascriptParser = new JavascriptParser()
//去除无用代码
javascriptContent = javascriptParser.beforeParse(javascriptContent)
//解析成AST
javascriptParser.parse(javascriptContent).then((javascriptAst)=>{
//进行代码转换
let {convertedJavascript,vistors} = componentConverter(javascriptAst)
//放到预先定义好的模板中
convertedJavascript = componentTemplateBuilder(convertedJavascript,vistors)
//生成文本并写入到文件
let codeText = `<script>\r\n${generate(convertedJavascript).code}\r\n</script>\r\n`
fs.writeFile(targetFilePath,codeText, ()=>{
resolve()
});
}).catch((e)=>{
reject(e)
})
Выше приведен процесс реализации основного кода инструмента wepy to VUE.
Я надеюсь, что благодаря этому примеру вы сможете понять, как выполнить точную обработку кода или преобразование синтаксиса с помощью AST.
Как сделать инструмент командной строки
Теперь, когда мы внедрили этот инструмент преобразования, мы надеемся предоставить разработчикам инструмент командной строки, который состоит из двух основных частей:
команда регистрации
- 1. Настройте раздел bin в package.json проекта.
{
"name": "@zz-vc/fancy-cli",
"bin": {
"fancy": "bin/fancy"
},
//其他配置
}
- 2. После написания кода npm публикует его
- 3. После того, как разработчик установит ваш плагин, вы можете запустить командную строку сfancy xxxxнепосредственно вызвать команду в виде
Написать код вызова команды
#!/usr/bin/env node
process.env.NODE_PATH = __dirname + '/../node_modules/'
const { resolve } = require('path')
const res = command => resolve(__dirname, './commands/', command)
const program = require('commander')
program
.version(require('../package').version )
program
.usage('<command>')
//注册convert命令
program
.command('convert <componentName>')
.description('convert a component,eg: fancy convert Tab.vue')
.alias('c')
.action((componentName) => {
let fn = require(res('convert'))
fn(componentName)
})
program.parse(process.argv)
if(!program.args.length){
program.help()
}
Код, соответствующий команде convert:
const cwdPath = process.cwd()
const convert = async function(filepath){
let fileText = await fse.readFile(filepath, 'utf-8');
fileHandle(fileText.toString(),filepath)
}
module.exports = function(fileName){
convert(`${cwdPath}/${fileName}`)
}
Код fileHandle уже упоминался в начале, для тех, кто забыл, вы можете прочитать его еще раз с самого начала, и вы можете связать воедино общую логику реализации этого инструмента.
Эпилог
На данный момент эта статья закончила, как написать инструмент командной строки из wepy в VUE через AST, надеюсь, она будет вам полезна.
самое главное: Наша компания Zhuanzhuan набирает несколько старших инженеров-разработчиков.Если вы заинтересованы в сотрудничестве со мной, отправьте свое резюме по адресуzhangsuoyong@zhuanzhuan.com
Пожалуйста, укажите источник и автора перепечатки: Zhang Suoyong@zhuanzhuan