Skip to content

Commit

Permalink
feat: support [] while calling data methods
Browse files Browse the repository at this point in the history
fix: #145
  • Loading branch information
meixg committed Apr 15, 2022
1 parent 2011d4b commit f5d7181
Show file tree
Hide file tree
Showing 2 changed files with 161 additions and 4 deletions.
127 changes: 123 additions & 4 deletions src/runtime/san-ssr-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,125 @@ interface Computed {
[k: string]: (this: { data: SanSSRData }) => any
}

/**
* 字符串源码读取类,用于模板字符串解析过程
*
* @param {string} source 要读取的字符串
*/
class Walker {
source: string
len: number
index: number
constructor (source: string) {
this.source = source
this.len = this.source.length
this.index = 0
}

/**
* 向前读取符合规则的字符片段,并返回规则匹配结果
*
* @param reg 字符片段的正则表达式
* @param isMatchStart 是否必须匹配当前位置
* @return
*/
match (reg: RegExp, isMatchStart: boolean) {
reg.lastIndex = this.index

const match = reg.exec(this.source)
if (match && (!isMatchStart || this.index === match.index)) {
this.index = reg.lastIndex
return match
}
};
}

/**
* 读取ident
* 这里的 ident 指标识符(identifier),也就是通常意义上的变量名
* 这里默认的变量名规则为:由美元符号($)、数字、字母或者下划线(_)构成的字符串
*
* @inner
* @param walker 源码读取对象
* @return {string}
*/
function readIdent (walker: Walker) {
const match = walker.match(/\s*([$0-9a-z_]+)/ig, true)

if (!match) {
throw new Error('[SAN_SSR FATAL] expect an ident: ' + walker.source.slice(walker.index))
}

return match[1]
}

/**
* 读取字符串
*
* @param walker 源码读取对象
*/
function readString (walker: Walker) {
const startChar = walker.source.charAt(walker.index)
const index = walker.source.indexOf(startChar, walker.index + 1)

if (index === -1) {
throw new Error('[SAN_SSR FATAL] expect a string: ' + walker.source.slice(walker.index))
}

const value = walker.source.slice(walker.index + 1, index)
walker.index = index + 1

return value
}

/**
* 读取访问表达式
*
* @param walker 源码读取对象
* @return {Object}
*/
function readAccessor (walker: Walker) {
const firstSeg = readIdent(walker)

const result: (string | number)[] = [
firstSeg
]

while (walker.index < walker.len) {
switch (walker.source.charCodeAt(walker.index)) {
case 46: // .
walker.index++

// ident as string
result.push(readIdent(walker))
break

case 91: { // [
walker.index++
let currentCode = walker.source.charCodeAt(walker.index)
if (currentCode >= 48 && currentCode <= 57) { // 0-9
result.push(+(walker.match(/[0-9]+(\.[0-9]+)?/g, true)![0]))
} else if (currentCode === 34 || currentCode === 39) {
result.push(readString(walker))
} else {
throw new Error('[SAN_SSR FATAL] identifier is not support: ' + walker.source.slice(walker.index))
}
currentCode = walker.source.charCodeAt(walker.index)
if (currentCode !== 93) {
throw new Error('[SAN_SSR FATAL] expect ]: ' + walker.source.slice(walker.index))
}
walker.index++
break
}

default:
throw new Error('[SAN_SSR FATAL] expect . or [: ' + walker.source.slice(walker.index))
}
}

return result
}

/**
* SSR 期间的 Data 实现,替代 import('san').SanSSRData
*
Expand All @@ -29,12 +148,12 @@ export class SanSSRData {
if (arguments.length === 0) return this.data
if (this.computed[path]) return this.computed[path].call({ data: this })
return this.parseExpr(path).reduce(
(val: any, name: string) => val == null ? val : val[name],
(val: any, name: string | number) => val == null ? val : val[name],
this.data
)
}

set (path: string, value: string) {
set (path: string, value: any) {
const seq = this.parseExpr(path)
let parent = this.data
for (let i = 0; i < seq.length - 1; i++) {
Expand All @@ -54,7 +173,7 @@ export class SanSSRData {
if (value && value.splice) value.splice(index, 1)
}

parseExpr (expr: string): string[] {
return expr.split('.')
parseExpr (expr: string): (string|number)[] {
return readAccessor(new Walker(expr))
}
}
38 changes: 38 additions & 0 deletions test/unit/runtime/san-data.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,42 @@ describe('runtime/SanSSRData', () => {
expect(data.get('bar')).toBeUndefined()
})
})
describe('.parseExpr', () => {
it('should parse []', () => {
const data = new SanSSRData({}, {})
expect(data.parseExpr('data1.data3[0].age')).toEqual(['data1', 'data3', 0, 'age'])
expect(data.parseExpr('data1.data3["bbb"].age')).toEqual(['data1', 'data3', 'bbb', 'age'])
})
it('can use [] to get', () => {
const data = new SanSSRData({
data1: {
data2: {
schoolAge: 9
},
data3: [
{
age: 9
}
]
}
}, {})
expect(data.get('data1.data3[0].age')).toEqual(9)
})
it('can use [] to set', () => {
const data = new SanSSRData({
data1: {
data2: {
schoolAge: 9
},
data3: [
{
age: 9
}
]
}
}, {})
data.set('data1.data3[1]', { age: 10 })
expect(data.get('data1.data3').length).toEqual(2)
})
})
})

0 comments on commit f5d7181

Please sign in to comment.