Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit cc378e4

Browse files
author
realign
committedAug 7, 2019
first push
1 parent 322cb85 commit cc378e4

File tree

8 files changed

+500
-61
lines changed

8 files changed

+500
-61
lines changed
 

‎.eslintrc.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
module.exports = {
2+
"env": {
3+
// "browser": true,
4+
"es6": true,
5+
"node": true,
6+
},
7+
"globals": {
8+
"module": true,
9+
},
10+
"extends": "eslint:recommended",
11+
parser: "babel-eslint",
12+
"parserOptions": {
13+
"sourceType": "module",
14+
parser: "babel-eslint",
15+
},
16+
"rules": {
17+
"indent": [
18+
"error",
19+
4
20+
],
21+
"linebreak-style": [
22+
"error",
23+
"unix"
24+
],
25+
"quotes": [
26+
"error",
27+
"single"
28+
],
29+
"semi": [
30+
"error",
31+
"always"
32+
],
33+
// "comma-dangle": [
34+
// "error",
35+
// "always"
36+
// ],
37+
"no-console": "off",
38+
}
39+
};

‎.gitignore

Lines changed: 2 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,3 @@
1-
# Logs
2-
logs
3-
*.log
4-
npm-debug.log*
5-
yarn-debug.log*
6-
yarn-error.log*
1+
node_modules
72

8-
# Runtime data
9-
pids
10-
*.pid
11-
*.seed
12-
*.pid.lock
13-
14-
# Directory for instrumented libs generated by jscoverage/JSCover
15-
lib-cov
16-
17-
# Coverage directory used by tools like istanbul
18-
coverage
19-
20-
# nyc test coverage
21-
.nyc_output
22-
23-
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24-
.grunt
25-
26-
# Bower dependency directory (https://bower.io/)
27-
bower_components
28-
29-
# node-waf configuration
30-
.lock-wscript
31-
32-
# Compiled binary addons (https://nodejs.org/api/addons.html)
33-
build/Release
34-
35-
# Dependency directories
36-
node_modules/
37-
jspm_packages/
38-
39-
# TypeScript v1 declaration files
40-
typings/
41-
42-
# Optional npm cache directory
43-
.npm
44-
45-
# Optional eslint cache
46-
.eslintcache
47-
48-
# Optional REPL history
49-
.node_repl_history
50-
51-
# Output of 'npm pack'
52-
*.tgz
53-
54-
# Yarn Integrity file
55-
.yarn-integrity
56-
57-
# dotenv environment variables file
58-
.env
59-
60-
# next.js build output
61-
.next
3+
package-lock.json

‎README.md

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,37 @@
1-
# vue-sfc-descriptor-stringify
1+
# vue-sfc-descriptor-stringify
2+
3+
> Translate Vue SFC descriptor to String.
4+
> 将 vue-sfc 描述 转为 字符串。
5+
6+
## Scene
7+
8+
需要操作 `vue` 文件,进行 `属性` 或者 `内容` 的更改,类似 `babel`
9+
10+
## Usage
11+
12+
### Install
13+
14+
```bash
15+
# install
16+
$ npm i vue-sfc-descriptor-stringify -D
17+
```
18+
19+
### Call
20+
21+
```js
22+
const Stringify = require('vue-sfc-descriptor-stringify');
23+
24+
/**
25+
* @name Stringify
26+
* @param
27+
* sfcDescriptor 需要转换成 string 的 sfc
28+
* originSfcDescriptor 原始 sfc,如果没有对 sfcDescriptor 处理的话,跟 sfcDescriptor 是一样的
29+
* options 配置
30+
* indents 缩进
31+
* template: 2 // 默认
32+
* script: 0 // 默认
33+
* style: 0 // 默认
34+
* @returns String 转换之后的 vue-sfc 内容
35+
*/
36+
const str = Stringify(sfcDescriptor, originSfcDescriptor, options);
37+
```

‎__test__/index.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
const fs = require('fs');
2+
const _ = require('./../src/lib/util');
3+
const compiler = require('vue-template-compiler');
4+
const Stringify = require('../src/index');
5+
6+
const source = fs.readFileSync('./tpl/multiple-styles.vue', 'utf8');
7+
const descriptor = compiler.parseComponent(source) || {};
8+
const oriDescriptor = _.cloneDeep(descriptor);
9+
10+
descriptor.styles.forEach((item) => {
11+
if(!item.attrs) {
12+
item.attrs = {};
13+
}
14+
15+
item.attrs.lang = 'less';
16+
});
17+
18+
const result = Stringify(descriptor, oriDescriptor);
19+
20+
fs.writeFile('./tpl/multiple-styles_new.vue', result, (err) => {
21+
console.log(err);
22+
});

‎__test__/tpl/multiple-styles.vue

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
2+
<template>
3+
<div id="app">
4+
<el-menu
5+
router
6+
mode="horizontal"
7+
:default-active="activeIndex"
8+
:class="defaultClass">
9+
<el-menu-item index="/">
10+
物料堆页面
11+
<el-tooltip
12+
effect="light"
13+
placement="right-end">
14+
<div slot="content">
15+
<p class="m-custom-tips">
16+
1. 选择需要的区块,点击按钮添加到预览区<br>
17+
2. 拖拽预览区的区块,进行排序<br>
18+
3. 需要的区块添加完成,点击右上角进行其他信息编辑<br>
19+
4. 完善路由、文件路径等信息<br>
20+
5. 点击“开始创建”,完成后点击在编辑器中打开
21+
</p>
22+
</div>
23+
<i class="el-icon-question"></i>
24+
</el-tooltip>
25+
</el-menu-item>
26+
</el-menu>
27+
<div class="g-body">
28+
<router-view></router-view>
29+
</div>
30+
</div>
31+
</template>
32+
33+
34+
35+
<script>
36+
export default {
37+
name: 'app',
38+
data() {
39+
return {
40+
defaultClass: 'g-el-menu-fix-top',
41+
activeIndex: '/',
42+
};
43+
},
44+
}
45+
</script>
46+
47+
<style lang="scss" type="text/less">
48+
html,
49+
body,
50+
#app {
51+
position: relative;
52+
margin: 0;
53+
padding: 0;
54+
width: 100%;
55+
height: 100%;
56+
}
57+
#app {
58+
font-family: sans-serif, Helvetica, Arial;
59+
-webkit-font-smoothing: antialiased;
60+
-moz-osx-font-smoothing: grayscale;
61+
text-align: center;
62+
color: #2c3e50;
63+
margin-top: 0;
64+
}
65+
.el-menu.g-el-menu-fix-top {
66+
position: sticky;
67+
top: 0;
68+
z-index: 10;
69+
}
70+
.g-body {
71+
position: relative;
72+
height: calc(100% - 65px);
73+
}
74+
.m-custom-tips {
75+
margin: 0 4px;
76+
line-height: 20px;
77+
}
78+
79+
$scrollbar-w: 8px;
80+
$scrollbar-h: 8px;
81+
$scrollbar-color: #aaa;
82+
* {
83+
::-webkit-scrollbar {
84+
width: $scrollbar-w;
85+
height: $scrollbar-h;
86+
background: #efefef;
87+
}
88+
::-webkit-scrollbar-thumb {
89+
background: $scrollbar-color;
90+
border-radius: $scrollbar-w / 2;
91+
}
92+
}
93+
</style>

‎package.json

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{
2+
"name": "vue-sfc-descriptor-stringify",
3+
"version": "0.0.1",
4+
"description": "Translate vue sfc to string.",
5+
"main": "src/index.js",
6+
"scripts": {
7+
"test": "echo \"Error: no test specified\" && exit 1"
8+
},
9+
"devDependencies": {
10+
"babel-eslint": "^10.0.1",
11+
"body-parser": "^1.19.0",
12+
"eslint": "^5.16.0",
13+
"jest": "^22.3.0",
14+
"lodash": "^4.17.15",
15+
"n-s-logs": "0.2.0",
16+
"vue-template-compiler": "^2.5.13"
17+
},
18+
"dependencies": {
19+
"indent-string": "^3.2.0"
20+
},
21+
"repository": {
22+
"type": "git",
23+
"url": "git+https://github.com/ReAlign/vue-sfc-descriptor-stringify.git"
24+
},
25+
"keywords": [
26+
"vue",
27+
"sfc",
28+
"toString",
29+
"stringify"
30+
],
31+
"author": "ReAlign",
32+
"license": "MIT",
33+
"bugs": {
34+
"url": "https://github.com/ReAlign/vue-sfc-descriptor-stringify/issues"
35+
},
36+
"homepage": "https://github.com/ReAlign/vue-sfc-descriptor-stringify#readme"
37+
}

‎src/index.js

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
/**
2+
* @name vue-sfc-descriptor-stringify
3+
* @author ReAlign
4+
* @date 2019-08-07
5+
*/
6+
const _ = require('./lib/util');
7+
const indentString = require('indent-string');
8+
9+
const Stringify = {};
10+
11+
/**
12+
* @name 获取起始标签
13+
* @param block 模块
14+
*/
15+
Stringify._getOpenTag = (block = {}) => {
16+
const {
17+
type = '',
18+
attrs = {},
19+
} = block;
20+
21+
// 属性字符串
22+
let _attrsKVStr = Object.keys(attrs)
23+
// template -> script -> style
24+
.sort()
25+
// 填充:k=v
26+
.map(name => {
27+
const value = attrs[name];
28+
29+
return value === true ? name : `${name}="${value}"`;
30+
})
31+
// join by ' '
32+
.join(' ');
33+
_attrsKVStr = _attrsKVStr.length ? ` ${_attrsKVStr}` : _attrsKVStr;
34+
35+
return `<${type}${_attrsKVStr}>`;
36+
};
37+
38+
/**
39+
* @name 获取结束标签
40+
* @param block 模块
41+
*/
42+
Stringify._getCloseTag = (block = {}) => {
43+
const {
44+
type = '',
45+
} = block;
46+
47+
return `</${type}>\n`;
48+
};
49+
50+
/**
51+
* @name 根据类型查找配置
52+
* @param
53+
* type 类型
54+
* sfcWithConfig 数据源
55+
* opts 配置数据
56+
* index ${type} 的索引
57+
*/
58+
Stringify._$findConfigByType = (type = '', sfcWithConfig = {}, opts = {}) => {
59+
const {
60+
index = 0,
61+
} = opts;
62+
const typesIndex = {};
63+
let _conf = null;
64+
65+
sfcWithConfig.some(item => {
66+
if(typesIndex[item.type] === undefined) {
67+
typesIndex[item.type] = -1;
68+
}
69+
typesIndex[item.type]++;
70+
// 同类型 && 同下标
71+
const _flag = item.type === type && index === typesIndex[item.type];
72+
if(_flag) {
73+
_conf = item.__data__;
74+
}
75+
76+
return _flag;
77+
});
78+
// console.log(type, _conf);
79+
80+
return _conf;
81+
};
82+
83+
/**
84+
* @name 获取缩进配置
85+
* @param options 全局配置
86+
*/
87+
Stringify._$getIndents = (options = {}) => {
88+
const {
89+
indents = {},
90+
} = options;
91+
92+
return Object.assign({
93+
template: 2,
94+
script: 0,
95+
style: 0
96+
}, indents);
97+
};
98+
99+
100+
/**
101+
* @name SFC 基础解析
102+
* @param sfc sfc对象
103+
* @returns blockList
104+
*/
105+
Stringify._$baseParse = (sfc = {} /*, options = {} */) => {
106+
const {
107+
template,
108+
script,
109+
styles,
110+
customBlocks,
111+
} = sfc;
112+
113+
const blocks = [
114+
template,
115+
script,
116+
...styles,
117+
...customBlocks,
118+
];
119+
120+
return blocks
121+
// 过滤掉不存在的部分
122+
.filter(block => block !== null)
123+
// 根据原文件的位置排序
124+
.sort((a, b) => a.start - b.start)
125+
// 算出准确的源位置的块
126+
.map((block = {}) => {
127+
const openTag = Stringify._getOpenTag(block);
128+
const closeTag = Stringify._getCloseTag(block);
129+
130+
return Object.assign({}, block, {
131+
openTag,
132+
closeTag,
133+
134+
startOfOpenTag: Math.max(block.start - openTag.length, 0),
135+
endOfOpenTag: block.start,
136+
137+
startOfCloseTag: block.end,
138+
endOfCloseTag: block.end + closeTag.length
139+
});
140+
});
141+
};
142+
143+
/**
144+
* @name sfc转字符串
145+
* @param sfcDescriptor sfc 对象
146+
* @param options 全局配置
147+
* @param sfcWithConfig sfc 带有处理之后的配置项
148+
*/
149+
Stringify._$toStr = (sfcDescriptor = {}, options = {}, sfcWithConfig = {}) => {
150+
const newIndents = Stringify._$getIndents(options);
151+
const _sfcBlocks = Stringify._$baseParse(sfcDescriptor, options);
152+
153+
// 生成 sfc
154+
return _sfcBlocks.reduce((sfcCode, block) => {
155+
const cusData = Stringify._$findConfigByType(block.type, sfcWithConfig);
156+
const emptyLines = _.get(cusData, 'emptyLinesBefore', 0);
157+
const indent = _.get(newIndents, `${block.type}`, 0);
158+
159+
return [
160+
sfcCode,
161+
'\n'.repeat(emptyLines),
162+
block.openTag,
163+
indentString(block.content, indent),
164+
block.closeTag
165+
].join('');
166+
}, '');
167+
};
168+
169+
/**
170+
* @name 给原始sfc添加属性
171+
*/
172+
Stringify._$appendConfigOriginSFC = (originSfcDescriptor = {}, options = {}) => {
173+
const ori = Stringify._$baseParse(originSfcDescriptor, options);
174+
175+
ori.forEach((block, index, array) => {
176+
block.__data__ = {};
177+
178+
// 获取模块之前的空行
179+
const _getEmptyLinesBeforeBlock = (block, index, array) => {
180+
return index === 0
181+
? block.startOfOpenTag
182+
: block.startOfOpenTag - array[index - 1].endOfCloseTag;
183+
};
184+
block.__data__.emptyLinesBefore = _getEmptyLinesBeforeBlock(block, index, array);
185+
});
186+
187+
return ori;
188+
};
189+
190+
/**
191+
* @name VueSFC to String
192+
* @param
193+
* sfcDescriptor 需要转换成string 的 sfc
194+
* originSfcDescriptor 原始 sfc,如果没有对 sfc 处理的话,跟 sfcDescriptor 是一样的
195+
* options 配置
196+
* indents 缩进
197+
* template: 2,
198+
* script: 0,
199+
* style: 0,
200+
*/
201+
Stringify.toString = (sfcDescriptor = {}, originSfcDescriptor = {}, options = {}) => {
202+
const sfcWithConfig = Stringify._$appendConfigOriginSFC(originSfcDescriptor, options);
203+
// console.log(sfcWithConfig);
204+
return Stringify._$toStr(sfcDescriptor, options, sfcWithConfig);
205+
};
206+
207+
module.exports = Stringify.toString;

‎src/lib/util.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
const _ = {
2+
typeOf(o) {
3+
return o == null ? String(o) : ({}).toString.call(o).slice(8, -1).toLowerCase();
4+
},
5+
get(obj = {}, path = '', {
6+
defaVal
7+
} = {
8+
defaVal: ''
9+
}) {
10+
let _res = (Array.isArray(path) ?
11+
path :
12+
path.replace(/\[/g, '.').replace(/'|"|\]/g, '').split('.'))
13+
.reduce((total, curVal) => (total || {})[curVal], obj);
14+
15+
return _.typeOf(_res) === 'undefined' ? defaVal : _res;
16+
},
17+
cloneDeep(obj) {
18+
var cloneObj = function (obj) {
19+
var result = {},
20+
item, type;
21+
for (var i in obj) {
22+
item = obj[i];
23+
type = _.typeOf(item);
24+
if (type === 'object') {
25+
result[i] = cloneObj(item);
26+
} else if (type === 'array') {
27+
result[i] = cloneArray(item);
28+
} else {
29+
result[i] = item;
30+
}
31+
}
32+
return result;
33+
};
34+
var cloneArray = function (obj) {
35+
var result = [],
36+
item, type;
37+
for (var i = 0; i < obj.length; i++) {
38+
item = obj[i];
39+
type = _.typeOf(item);
40+
if (type === 'object') {
41+
result[i] = cloneObj(item);
42+
} else if (type === 'array') {
43+
result[i] = cloneArray(item);
44+
} else if (typeof item !== 'object') {
45+
result[i] = item;
46+
}
47+
}
48+
return result;
49+
};
50+
var type = _.typeOf(obj);
51+
switch (type) {
52+
53+
case 'object':
54+
return cloneObj(obj);
55+
case 'array':
56+
return cloneArray(obj);
57+
default:
58+
return obj;
59+
}
60+
}
61+
};
62+
63+
module.exports = _;

0 commit comments

Comments
 (0)
Please sign in to comment.