最终,我们将会让这个测试通过
test('simple interpolation', () => {
const interpolationStr = '{{message}}'
const ast = baseParse(interpolationStr)
expect(ast.children[0]).toStrictEqual({
type: 'interpolation',
content: {
type: 'simple_expression',
content: 'message',
},
})
})
- 接收一个字符串
{{message}}
- 返回一个 ast
我们首先可以将代码的功能分割为多个模块,先快速通过测试
// parse.ts
// 导出函数
export function baseParse(content: string) {
const context = createContext(content)
return createRoot(parseChildren(context))
}
// 创建上下文
function createContext(content: string) {
return {
source: content,
}
}
// 创建 ast 根节点
function createRoot(children) {
return {
children,
}
}
// 创建 children
function parseChildren(context: { source: string }): any {
const nodes: any = []
let node
// 如果 context.source 以 {{ 开始
if (context.source.startsWith('{{')) {
node = parseInterpolation(context)
}
nodes.push(node)
return [node]
}
// 解析插值表达式
function parseInterpolation(context: { source: string }) {
return {
type: 'interpolation',
content: {
type: 'simple_expression',
content: 'message',
},
}
}
- 将功能进行分层
- 最终在
parseInterpolation
函数中进行解析插值
目前,我们需要将 {{message}}
中的 message
抽离出来,可以使用字符串的截取功能
// 将字符串截取为 message}}
const closeIndex = context.source.indexOf('}}', 2)
// 然后将字符串前面的 {{ 舍弃掉,我们将其称之为【推进】
context.source = context.source.slice(2)
// 获取到 {{}} 中间值的长度
const rawContentLength = closeIndex - 2
// 并将中间这个值获取出来
const content = context.source.slice(0, rawContentLength)
// 继续【推进】
context.source = context.source.slice(rawContentLength + 2)
- 最终,我们可以通过
content
来获取到值
在这一步,我们要将 {{
}}
抽离为具有语义化的字符串
const openDelimiter = '{{'
const closeDelimiter = '}}'
const closeIndex = context.source.indexOf(closeDelimiter, openDelimiter.length)
context.source = context.source.slice(openDelimiter.length)
const rawContentLength = closeIndex - closeDelimiter.length
const content = context.source.slice(0, rawContentLength)
context.source = context.source.slice(
rawContentLength + closeDelimiter.length
)
我们可以将推进的逻辑也抽离出来
const openDelimiter = '{{'
const closeDelimiter = '}}'
const closeIndex = context.source.indexOf(
closeDelimiter,
openDelimiter.length
)
+ advanceBy(context, openDelimiter.length)
- context.source = context.source.slice(openDelimiter.length)
const rawContentLength = closeIndex - openDelimiter.length
const content = context.source.slice(0, rawContentLength)
+ advanceBy(context, rawContentLength + closeDelimiter.length)
- context.source = context.source.slice(
- rawContentLength + closeDelimiter.length
- )
+ function advanceBy(context, length: number) {
+ context.source = context.source.slice(length)
+ }
// ast.ts
export const enum NodeType {
INTERPOLATION,
SIMPLE_EXPRESSION,
}
在这一块,我们需要修复一个边缘情况,在这里加入我们的插值表达式中存在空格,测试就会出现问题了,我们需要修复一下:
const openDelimiter = '{{'
const closeDelimiter = '}}'
const closeIndex = context.source.indexOf(closeDelimiter, openDelimiter.length)
context.source = context.source.slice(openDelimiter.length)
const rawContentLength = closeIndex - closeDelimiter.length
-const content = context.source.slice(0, rawContentLength)
+const rawContent = context.source.slice(0, rawContentLength)
+const content = rawContent.trim()
context.source = context.source.slice(
rawContentLength + closeDelimiter.length
)