-
Notifications
You must be signed in to change notification settings - Fork 20
/
san-file-parser.ts
126 lines (116 loc) · 4.55 KB
/
san-file-parser.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
/**
* San 文件解析器
*
* 从 .san 文件源码,得到它里面的 San 信息,产出 JSSanSourceFile。
* JSSanSourceFile 包含了若干个 JSComponentInfo 和一个 entryComponentInfo。
*/
import { ExpressionStatement, MethodDefinition, ObjectExpression, CallExpression, Node } from 'estree'
import { JavaScriptSanParser } from './javascript-san-parser'
import assert from 'assert'
import { isClass, getConstructor, addStringPropertyForObject, assertObjectExpression, isCallExpression, isObjectExpression, findDefaultExport } from '../ast/js-ast-util'
import { JSSanSourceFile } from '../models/san-source-file'
export class SanFileParser {
private readonly parser: JavaScriptSanParser
constructor (
public readonly scriptContent: string,
public readonly templateContent: string,
private readonly filePath: string
) {
this.parser = new JavaScriptSanParser(filePath, scriptContent, 'module')
}
parse (): JSSanSourceFile {
const expr = findDefaultExport(this.parser.root)
assert(expr, 'default export not found')
// export default { inited() {} }
if (isObjectExpression(expr)) this.expandToSanComponent(expr)
// defineComponent({}) -> defineComponent({ template: `${templateContent}` })
this.insertTemplate(expr)
return this.parser.parse()
}
/**
* 把简写的 san options 替换为 san Component。
* { inited(){} } -> require('san').defineComponent({ inited(){} })
*/
private expandToSanComponent (options: ObjectExpression) {
const defineComponent: CallExpression = {
type: 'CallExpression',
callee: {
type: 'MemberExpression',
object: {
type: 'CallExpression',
callee: { type: 'Identifier', name: 'require' },
arguments: [{ type: 'Literal', value: 'san', raw: "'san'" }],
optional: false
},
property: { type: 'Identifier', name: 'defineComponent' },
computed: false,
optional: false
},
arguments: [{ ...options }],
optional: false
}
Object.assign(options, defineComponent)
}
/**
* 把模板字符串插入到 san 组件定义中
* - 情况一:defineComponent({ inited(){} }) -> defineComponent({ inited(){}, template: '<div>...</div>' })
* - 情况二:class XComponent { constructor() {} } -> class XComponent { constructor() { this.template='<div>...</div>' } }
*/
private insertTemplate (expr: Node) {
if (isCallExpression(expr)) {
assert(expr.arguments[0], 'cannot parse san script')
assertObjectExpression(expr.arguments[0])
addStringPropertyForObject(expr.arguments[0], 'template', this.templateContent)
} else if (isClass(expr)) {
let fn = getConstructor(expr)
if (!fn) {
fn = this.createEmptyConstructor()
expr.body.body.push(fn)
}
fn.value.body.body.push(this.createTemplateAssignmentExpression())
} else {
throw new Error('entry component not found')
}
}
private createEmptyConstructor (): MethodDefinition {
return {
type: 'MethodDefinition',
kind: 'constructor',
static: false,
computed: false,
key: { type: 'Identifier', name: 'constructor' },
value: {
type: 'FunctionExpression',
id: null,
generator: false,
async: false,
params: [],
body: { type: 'BlockStatement', body: [] }
}
}
}
/**
* 创建给 this.template 赋值为 this.templateContent 的表达式
*/
private createTemplateAssignmentExpression (): ExpressionStatement {
return {
type: 'ExpressionStatement',
expression: {
type: 'AssignmentExpression',
operator: '=',
left: {
type: 'MemberExpression',
object: { type: 'ThisExpression' },
property: { type: 'Identifier', name: 'template' },
computed: false,
optional: false
},
right: {
type: 'Literal',
value: this.templateContent,
raw: JSON.stringify(this.templateContent)
}
}
}
}
}