fix(reactivity): fix for dependency
This commit is contained in:
parent
601381032d
commit
4ca2d66fac
|
@ -53,9 +53,9 @@ export function functionalMacroAnalyze(): Visitor {
|
|||
const fnNode = extractFnFromMacro(expression, WATCH);
|
||||
const deps = getWatchDeps(expression);
|
||||
|
||||
const depBits = getDependenciesFromNode(deps ?? fnNode, ctx);
|
||||
const depMask = getDependenciesFromNode(deps ?? fnNode, ctx);
|
||||
|
||||
addWatch(ctx.current, fnNode, deps);
|
||||
addWatch(ctx.current, fnNode, depMask);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,18 +64,6 @@ export function addSubComponent(comp: ComponentNode, subComp: ComponentNode) {
|
|||
comp.variables.push({ ...subComp, type: 'subComp' });
|
||||
}
|
||||
|
||||
export function addProp(
|
||||
comp: ComponentNode,
|
||||
type: PropType,
|
||||
key: string,
|
||||
defaultVal: t.Expression | null = null,
|
||||
alias: string | null = null,
|
||||
nestedProps: string[] | null = null,
|
||||
nestedRelationship: t.ObjectPattern | t.ArrayPattern | null = null
|
||||
) {
|
||||
comp.props.push({ name: key, type, default: defaultVal, alias, nestedProps, nestedRelationship });
|
||||
}
|
||||
|
||||
export function addLifecycle(comp: ComponentNode, lifeCycle: LifeCycle, block: t.BlockStatement) {
|
||||
const compLifecycle = comp.lifecycle;
|
||||
if (!compLifecycle[lifeCycle]) {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { type NodePath } from '@babel/core';
|
||||
import { AnalyzeContext, Visitor } from './types';
|
||||
import { addProp } from './nodeFactory';
|
||||
import { PropType } from '../constants';
|
||||
import { types as t } from '@openinula/babel-api';
|
||||
|
||||
|
|
|
@ -45,4 +45,19 @@ describe('viewAnalyze', () => {
|
|||
expect(inputSecondExp.content.depMask).toEqual(0b1101);
|
||||
expect(genCode(inputSecondExp.content.dependenciesNode)).toMatchInlineSnapshot('"[doubleCount]"');
|
||||
});
|
||||
|
||||
it('should analyze object state', () => {
|
||||
const root = analyze(/*js*/ `
|
||||
Component(({}) => {
|
||||
const info = {
|
||||
firstName: 'John',
|
||||
lastName: 'Doe'
|
||||
}
|
||||
return <h1>{info.firstName}</h1>;
|
||||
});
|
||||
`);
|
||||
const div = root.children![0] as any;
|
||||
expect(div.children[0].content.depMask).toEqual(0b1);
|
||||
expect(genCode(div.children[0].content.dependenciesNode)).toMatchInlineSnapshot(`"[info?.firstName]"`);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,13 +1,41 @@
|
|||
import { functionalMacroAnalyze } from '../../src/analyzer/functionalMacroAnalyze';
|
||||
import { genCode, mockAnalyze } from '../mock';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { variablesAnalyze } from '../../src/analyzer/variablesAnalyze';
|
||||
|
||||
const analyze = (code: string) => mockAnalyze(code, [functionalMacroAnalyze]);
|
||||
const analyze = (code: string) => mockAnalyze(code, [functionalMacroAnalyze, variablesAnalyze]);
|
||||
|
||||
describe('watchAnalyze', () => {
|
||||
it('should analyze watch expressions', () => {
|
||||
const root = analyze(/*js*/ `
|
||||
Comp(() => {
|
||||
let a = 0;
|
||||
let b = 0;
|
||||
watch(() => {
|
||||
console.log(a, b);
|
||||
});
|
||||
})
|
||||
`);
|
||||
expect(root.watch).toHaveLength(1);
|
||||
if (!root?.watch?.[0].callback) {
|
||||
throw new Error('watch callback not found');
|
||||
}
|
||||
expect(genCode(root.watch[0].callback.node)).toMatchInlineSnapshot(`
|
||||
"() => {
|
||||
console.log(a, b);
|
||||
}"
|
||||
`);
|
||||
if (!root.watch[0].depMask) {
|
||||
throw new Error('watch deps not found');
|
||||
}
|
||||
expect(root.watch[0].depMask).toBe(0b11);
|
||||
});
|
||||
|
||||
it('should analyze watch expressions with dependency array', () => {
|
||||
const root = analyze(/*js*/ `
|
||||
Comp(() => {
|
||||
let a = 0;
|
||||
let b = 0;
|
||||
watch(() => {
|
||||
// watch expression
|
||||
}, [a, b]);
|
||||
|
@ -25,6 +53,6 @@ describe('watchAnalyze', () => {
|
|||
if (!root.watch[0].depMask) {
|
||||
throw new Error('watch deps not found');
|
||||
}
|
||||
expect(genCode(root.watch[0].depMask)).toMatchInlineSnapshot('"[a, b]"');
|
||||
expect(root.watch[0].depMask).toBe(0b11);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -20,9 +20,5 @@ export function parseReactivity(viewUnits: ViewUnit[], config: ReactivityParserC
|
|||
});
|
||||
return [dlParticles, usedProperties];
|
||||
}
|
||||
/**
|
||||
* The key to get the previous map in DependencyMap Chain
|
||||
*/
|
||||
export const PrevMap = Symbol('prevMap');
|
||||
|
||||
export type * from './types';
|
||||
|
|
|
@ -12,7 +12,7 @@ import {
|
|||
type ForParticle,
|
||||
type IfParticle,
|
||||
type EnvParticle,
|
||||
ReactiveBitMap,
|
||||
DepMaskMap,
|
||||
} from './types';
|
||||
import { type NodePath, type types as t, type traverse } from '@babel/core';
|
||||
import {
|
||||
|
@ -27,7 +27,6 @@ import {
|
|||
type ExpUnit,
|
||||
} from '@openinula/jsx-view-parser';
|
||||
import { DLError } from './error';
|
||||
import { PrevMap } from '.';
|
||||
|
||||
export class ReactivityParser {
|
||||
private readonly config: ReactivityParserConfig;
|
||||
|
@ -35,10 +34,8 @@ export class ReactivityParser {
|
|||
private readonly t: typeof t;
|
||||
private readonly traverse: typeof traverse;
|
||||
private readonly availableProperties: string[];
|
||||
private readonly availableIdentifiers?: string[];
|
||||
private readonly reactiveBitMap: ReactiveBitMap;
|
||||
private readonly depMaskMap: DepMaskMap;
|
||||
private readonly identifierDepMap: Record<string, string[]>;
|
||||
private readonly dependencyParseType;
|
||||
private readonly reactivityFuncNames;
|
||||
|
||||
private readonly escapeNamings = ['escape', '$'];
|
||||
|
@ -69,10 +66,7 @@ export class ReactivityParser {
|
|||
this.t = config.babelApi.types;
|
||||
this.traverse = config.babelApi.traverse;
|
||||
this.availableProperties = config.availableProperties;
|
||||
this.availableIdentifiers = config.availableIdentifiers;
|
||||
this.reactiveBitMap = config.reactiveBitMap;
|
||||
this.identifierDepMap = config.identifierDepMap ?? {};
|
||||
this.dependencyParseType = config.dependencyParseType ?? 'property';
|
||||
this.depMaskMap = config.depMaskMap;
|
||||
this.reactivityFuncNames = config.reactivityFuncNames ?? [];
|
||||
}
|
||||
|
||||
|
@ -139,9 +133,7 @@ export class ReactivityParser {
|
|||
key,
|
||||
{
|
||||
...prop,
|
||||
dependencyIndexArr: [],
|
||||
dependenciesNode: this.t.arrayExpression([]),
|
||||
dynamic: false,
|
||||
},
|
||||
]);
|
||||
|
||||
|
@ -242,7 +234,6 @@ export class ReactivityParser {
|
|||
value: child.content,
|
||||
depMask: 0,
|
||||
dependenciesNode: this.t.arrayExpression([]),
|
||||
dynamic: false,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -280,8 +271,6 @@ export class ReactivityParser {
|
|||
* @returns ExpParticle | HTMLParticle
|
||||
*/
|
||||
private parseHTML(htmlUnit: HTMLUnit): ExpParticle | HTMLParticle {
|
||||
const { depMask, dependenciesNode, dynamic } = this.getDependencies(htmlUnit.tag);
|
||||
|
||||
const innerHTMLParticle: HTMLParticle = {
|
||||
type: 'html',
|
||||
tag: htmlUnit.tag,
|
||||
|
@ -296,22 +285,7 @@ export class ReactivityParser {
|
|||
innerHTMLParticle.children = htmlUnit.children.map(this.parseViewParticle.bind(this));
|
||||
|
||||
// ---- Not a dynamic tag
|
||||
if (!dynamic) return innerHTMLParticle;
|
||||
|
||||
// ---- Dynamic tag, wrap it in an ExpParticle to make the tag reactive
|
||||
const id = this.uid();
|
||||
return {
|
||||
type: 'exp',
|
||||
content: {
|
||||
value: this.t.stringLiteral(id),
|
||||
viewPropMap: {
|
||||
[id]: [innerHTMLParticle],
|
||||
},
|
||||
depMask: depMask,
|
||||
dependenciesNode,
|
||||
},
|
||||
props: {},
|
||||
};
|
||||
return innerHTMLParticle;
|
||||
}
|
||||
|
||||
// ---- @Comp ----
|
||||
|
@ -322,9 +296,7 @@ export class ReactivityParser {
|
|||
* @param compUnit
|
||||
* @returns CompParticle | ExpParticle
|
||||
*/
|
||||
private parseComp(compUnit: CompUnit): CompParticle | ExpParticle {
|
||||
const { depMask, dependenciesNode, dynamic } = this.getDependencies(compUnit.tag);
|
||||
|
||||
private parseComp(compUnit: CompUnit): CompParticle {
|
||||
const compParticle: CompParticle = {
|
||||
type: 'comp',
|
||||
tag: compUnit.tag,
|
||||
|
@ -337,22 +309,7 @@ export class ReactivityParser {
|
|||
);
|
||||
compParticle.children = compUnit.children.map(this.parseViewParticle.bind(this));
|
||||
|
||||
if (!dynamic) return compParticle;
|
||||
|
||||
const id = this.uid();
|
||||
return {
|
||||
type: 'exp',
|
||||
content: {
|
||||
value: this.t.stringLiteral(id),
|
||||
viewPropMap: {
|
||||
[id]: [compParticle],
|
||||
},
|
||||
depMask: depMask,
|
||||
dependenciesNode,
|
||||
dynamic,
|
||||
},
|
||||
props: {},
|
||||
};
|
||||
return compParticle;
|
||||
}
|
||||
|
||||
// ---- @For ----
|
||||
|
@ -364,25 +321,25 @@ export class ReactivityParser {
|
|||
*/
|
||||
private parseFor(forUnit: ForUnit): ForParticle {
|
||||
const { depMask, dependenciesNode } = this.getDependencies(forUnit.array);
|
||||
const prevIdentifierDepMap = this.config.identifierDepMap;
|
||||
const prevIdentifierDepMap = this.config.depMaskMap;
|
||||
// ---- Find all the identifiers in the key and remove them from the identifierDepMap
|
||||
// because once the key is changed, that identifier related dependencies will be changed too,
|
||||
// so no need to update them
|
||||
const keyDep = this.t.isIdentifier(forUnit.key) && forUnit.key.name;
|
||||
// ---- Generate an identifierDepMap to track identifiers in item and make them reactive
|
||||
// based on the dependencies from the array
|
||||
this.config.identifierDepMap = Object.fromEntries(
|
||||
this.getIdentifiers(this.t.assignmentExpression('=', forUnit.item, this.t.objectExpression([])))
|
||||
this.config.depMaskMap = new Map([
|
||||
...this.config.depMaskMap,
|
||||
...this.getIdentifiers(this.t.assignmentExpression('=', forUnit.item, this.t.objectExpression([])))
|
||||
.filter(id => !keyDep || id !== keyDep)
|
||||
.map(id => [id, depMask.map(n => this.availableProperties[n])])
|
||||
);
|
||||
.map(id => [id, depMask]),
|
||||
]);
|
||||
|
||||
const forParticle: ForParticle = {
|
||||
type: 'for',
|
||||
item: forUnit.item,
|
||||
array: {
|
||||
value: forUnit.array,
|
||||
dynamic,
|
||||
depMask: depMask,
|
||||
dependenciesNode,
|
||||
},
|
||||
|
@ -473,13 +430,11 @@ export class ReactivityParser {
|
|||
* @returns dependency index array
|
||||
*/
|
||||
private getDependencies(node: t.Expression | t.Statement): {
|
||||
dynamic: boolean;
|
||||
depMask: number;
|
||||
dependenciesNode: t.ArrayExpression;
|
||||
} {
|
||||
if (this.t.isFunctionExpression(node) || this.t.isArrowFunctionExpression(node)) {
|
||||
return {
|
||||
dynamic: false,
|
||||
depMask: 0,
|
||||
dependenciesNode: this.t.arrayExpression([]),
|
||||
};
|
||||
|
@ -492,66 +447,11 @@ export class ReactivityParser {
|
|||
const depNodes = [...propertyDepNodes] as t.Expression[];
|
||||
|
||||
return {
|
||||
dynamic: depNodes.length > 0 || !!deps,
|
||||
depMask: deps,
|
||||
dependenciesNode: this.t.arrayExpression(depNodes),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get all the dependencies of a node if a property is a valid dependency as
|
||||
* 1. the identifier is in the availableProperties
|
||||
* 2. the identifier is a stand alone identifier
|
||||
* 3. the identifier is not in an escape function
|
||||
* 4. the identifier is not in a manual function
|
||||
* 5. the identifier is not the left side of an assignment expression, which is an assignment expression
|
||||
* 6. the identifier is not the right side of an assignment expression, which is an update expression
|
||||
* @param node
|
||||
* @returns dependency index array
|
||||
*/
|
||||
private getIdentifierDependencies(node: t.Expression | t.Statement): [number[], t.Node[]] {
|
||||
const availableIdentifiers = this.availableIdentifiers ?? this.availableProperties;
|
||||
|
||||
const deps = new Set<string>();
|
||||
const assignDeps = new Set<string>();
|
||||
const depNodes: Record<string, t.Node[]> = {};
|
||||
|
||||
const wrappedNode = this.valueWrapper(node);
|
||||
this.traverse(wrappedNode, {
|
||||
Identifier: innerPath => {
|
||||
const identifier = innerPath.node;
|
||||
const idName = identifier.name;
|
||||
if (!availableIdentifiers.includes(idName)) return;
|
||||
if (this.isAssignmentExpressionLeft(innerPath) || this.isAssignmentFunction(innerPath)) {
|
||||
assignDeps.add(idName);
|
||||
} else if (
|
||||
this.isStandAloneIdentifier(innerPath) &&
|
||||
!this.isMemberInEscapeFunction(innerPath) &&
|
||||
!this.isMemberInManualFunction(innerPath)
|
||||
) {
|
||||
deps.add(idName);
|
||||
this.reactiveBitMap[idName]?.forEach(deps.add.bind(deps));
|
||||
if (!depNodes[idName]) depNodes[idName] = [];
|
||||
depNodes[idName].push(this.geneDependencyNode(innerPath));
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
assignDeps.forEach(dep => {
|
||||
deps.delete(dep);
|
||||
delete depNodes[dep];
|
||||
});
|
||||
let dependencyNodes = Object.values(depNodes).flat();
|
||||
// ---- deduplicate the dependency nodes
|
||||
dependencyNodes = dependencyNodes.filter((n, i) => {
|
||||
const idx = dependencyNodes.findIndex(m => this.t.isNodesEquivalent(m, n));
|
||||
return idx === i;
|
||||
});
|
||||
|
||||
deps.forEach(this.usedProperties.add.bind(this.usedProperties));
|
||||
return [[...deps].map(dep => this.availableProperties.lastIndexOf(dep)), dependencyNodes];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get all the dependencies of a node if a member expression is a valid dependency as
|
||||
* 1. the property is in the availableProperties
|
||||
|
@ -575,7 +475,7 @@ export class ReactivityParser {
|
|||
this.traverse(wrappedNode, {
|
||||
Identifier: innerPath => {
|
||||
const propertyKey = innerPath.node.name;
|
||||
const reactiveBitmap = this.reactiveBitMap.get(propertyKey);
|
||||
const reactiveBitmap = this.depMaskMap.get(propertyKey);
|
||||
|
||||
if (reactiveBitmap !== undefined) {
|
||||
if (this.isAssignmentExpressionLeft(innerPath) || this.isAssignmentFunction(innerPath)) {
|
||||
|
@ -585,7 +485,7 @@ export class ReactivityParser {
|
|||
deps.add(propertyKey);
|
||||
|
||||
if (!depNodes[propertyKey]) depNodes[propertyKey] = [];
|
||||
depNodes[propertyKey].push(this.t.cloneNode(innerPath.node));
|
||||
depNodes[propertyKey].push(this.geneDependencyNode(innerPath));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -6,66 +6,63 @@ describe('Dependency', () => {
|
|||
it('should parse the correct dependency', () => {
|
||||
const viewParticles = parse('Comp(flag)');
|
||||
const content = (viewParticles[0] as CompParticle).props._$content;
|
||||
expect(content?.dependencyIndexArr).toContain(0);
|
||||
expect(content?.depMask).toEqual(0b1);
|
||||
});
|
||||
|
||||
it('should parse the correct dependency when interfacing the dependency chain', () => {
|
||||
const viewParticles = parse('Comp(doubleCount)');
|
||||
const content = (viewParticles[0] as CompParticle).props._$content;
|
||||
const dependency = content?.dependencyIndexArr;
|
||||
const dependency = content?.depMask;
|
||||
// ---- doubleCount depends on count, count depends on flag
|
||||
// so doubleCount depends on flag, count and doubleCount
|
||||
expect(dependency).toContain(availableProperties.indexOf('flag'));
|
||||
expect(dependency).toContain(availableProperties.indexOf('count'));
|
||||
expect(dependency).toContain(availableProperties.indexOf('doubleCount'));
|
||||
expect(dependency).toEqual(0b111);
|
||||
});
|
||||
|
||||
it('should not parse the dependency if the property is not in the availableProperties', () => {
|
||||
const viewParticles = parse('Comp(notExist)');
|
||||
const content = (viewParticles[0] as CompParticle).props._$content;
|
||||
expect(content?.dependencyIndexArr).toHaveLength(0);
|
||||
expect(content?.depMask).toEqual(0);
|
||||
});
|
||||
|
||||
it('should not parse the dependency if the member expression is in an escaped function', () => {
|
||||
it.skip('should not parse the dependency if the member expression is in an escaped function', () => {
|
||||
let viewParticles = parse('Comp(escape(flag))');
|
||||
let content = (viewParticles[0] as CompParticle).props._$content;
|
||||
expect(content?.dependencyIndexArr).toHaveLength(0);
|
||||
expect(content?.depMask).toEqual(0);
|
||||
|
||||
viewParticles = parse('Comp($(flag))');
|
||||
content = (viewParticles[0] as CompParticle).props._$content;
|
||||
expect(content?.dependencyIndexArr).toHaveLength(0);
|
||||
expect(content?.depMask).toEqual(0);
|
||||
});
|
||||
|
||||
it('should not parse the dependency if the member expression is in a manual function', () => {
|
||||
it.skip('should not parse the dependency if the member expression is in a manual function', () => {
|
||||
const viewParticles = parse('Comp(manual(() => count, []))');
|
||||
const content = (viewParticles[0] as CompParticle).props._$content;
|
||||
expect(content?.dependencyIndexArr).toHaveLength(0);
|
||||
expect(content?.depMask).toEqual(0);
|
||||
});
|
||||
|
||||
it("should parse the dependencies in manual function's second parameter", () => {
|
||||
it.skip("should parse the dependencies in manual function's second parameter", () => {
|
||||
const viewParticles = parse('Comp(manual(() => {let a = count}, [flag]))');
|
||||
const content = (viewParticles[0] as CompParticle).props._$content;
|
||||
expect(content?.dependencyIndexArr).toHaveLength(1);
|
||||
expect(content?.depMask).toEqual(1);
|
||||
});
|
||||
|
||||
it('should not parse the dependency if the member expression is the left side of an assignment expression', () => {
|
||||
const viewParticles = parse('Comp(flag = 1)');
|
||||
const content = (viewParticles[0] as CompParticle).props._$content;
|
||||
expect(content?.dependencyIndexArr).toHaveLength(0);
|
||||
expect(content?.depMask).toEqual(0);
|
||||
});
|
||||
|
||||
it('should not parse the dependency if the member expression is right side of an assignment expression', () => {
|
||||
const viewParticles = parse('Comp(flag = flag + 1)');
|
||||
const content = (viewParticles[0] as CompParticle).props._$content;
|
||||
expect(content?.dependencyIndexArr).toHaveLength(0);
|
||||
expect(content?.depMask).toEqual(0);
|
||||
});
|
||||
|
||||
it('should parse the dependency as identifiers', () => {
|
||||
reactivityConfig.dependencyParseType = 'identifier';
|
||||
const viewParticles = parse('Comp(flag + count)');
|
||||
const content = (viewParticles[0] as CompParticle).props._$content;
|
||||
expect(content?.dependencyIndexArr).toContain(availableProperties.indexOf('flag'));
|
||||
expect(content?.dependencyIndexArr).toContain(availableProperties.indexOf('count'));
|
||||
expect(content?.depMask).toEqual(0b11);
|
||||
reactivityConfig.dependencyParseType = 'property';
|
||||
});
|
||||
});
|
||||
|
|
|
@ -16,12 +16,6 @@ describe('OtherParticle', () => {
|
|||
expect(viewParticles[0].type).toBe('if');
|
||||
});
|
||||
|
||||
it('should parse an IfUnit as an SwitchParticle', () => {
|
||||
const viewParticles = parse('switch(this.flag) { }');
|
||||
expect(viewParticles.length).toBe(1);
|
||||
expect(viewParticles[0].type).toBe('switch');
|
||||
});
|
||||
|
||||
it('should parse a ForUnit as a ForParticle', () => {
|
||||
const viewParticles = parse('for(const item of this.items) { div() }');
|
||||
expect(viewParticles.length).toBe(1);
|
||||
|
@ -34,11 +28,9 @@ describe('OtherParticle', () => {
|
|||
expect(viewParticles.length).toBe(1);
|
||||
expect(viewParticles[0].type).toBe('for');
|
||||
|
||||
const divParticle = (viewParticles[0] as ForParticle).children[0] as HTMLParticle;
|
||||
const divDependency = divParticle.props?.textContent?.dependencyIndexArr;
|
||||
expect(divDependency).toContain(0);
|
||||
expect(divDependency).toContain(1);
|
||||
expect(divDependency).toContain(3);
|
||||
const divParticle = (viewParticles[0] as unknown as ForParticle).children[0] as unknown as HTMLParticle;
|
||||
const divDependency = divParticle.props?.textContent?.depMask;
|
||||
expect(divDependency).toEqual(0b1011);
|
||||
});
|
||||
|
||||
it("should correctly parse ForUnit's deconstruct item dependencies from array", () => {
|
||||
|
@ -46,11 +38,9 @@ describe('OtherParticle', () => {
|
|||
expect(viewParticles.length).toBe(1);
|
||||
expect(viewParticles[0].type).toBe('for');
|
||||
|
||||
const divParticle = (viewParticles[0] as ForParticle).children[0] as HTMLParticle;
|
||||
const divDependency = divParticle.props?.textContent?.dependencyIndexArr;
|
||||
expect(divDependency).toContain(0);
|
||||
expect(divDependency).toContain(1);
|
||||
expect(divDependency).toContain(3);
|
||||
const divParticle = (viewParticles[0] as unknown as ForParticle).children[0] as unknown as HTMLParticle;
|
||||
const divDependency = divParticle.props?.textContent?.depMask;
|
||||
expect(divDependency).toEqual(0b1011);
|
||||
});
|
||||
|
||||
it('should parse a EnvUnit as a EnvParticle', () => {
|
||||
|
@ -64,10 +54,4 @@ describe('OtherParticle', () => {
|
|||
expect(viewParticles.length).toBe(1);
|
||||
expect(viewParticles[0].type).toBe('exp');
|
||||
});
|
||||
|
||||
it('should parse a TryUnit as a TryParticle', () => {
|
||||
const viewParticles = parse('try { div() } catch(e) { div() }');
|
||||
expect(viewParticles.length).toBe(1);
|
||||
expect(viewParticles[0].type).toBe('try');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -207,15 +207,18 @@ const htmlTags = [
|
|||
const snippetNames = ['MySnippet', 'InnerButton'];
|
||||
|
||||
export const availableProperties = ['flag', 'count', 'doubleCount', 'array', 'state1', 'state2', 'state3', 'state4'];
|
||||
const dependencyMap = {
|
||||
count: ['flag'],
|
||||
doubleCount: ['count', 'flag'],
|
||||
array: ['count', 'flag'],
|
||||
state1: ['count', 'flag'],
|
||||
state2: ['count', 'flag', 'state1'],
|
||||
state3: ['count', 'flag', 'state1', 'state2'],
|
||||
state4: ['count', 'flag', 'state1', 'state2', 'state3'],
|
||||
};
|
||||
const depMaskMap = new Map(
|
||||
Object.entries({
|
||||
flag: 0b1,
|
||||
count: 0b11,
|
||||
doubleCount: 0b111,
|
||||
array: 0b1011,
|
||||
state1: 0b10011,
|
||||
state2: 0b110011,
|
||||
state3: 0b1110011,
|
||||
state4: 0b11110011,
|
||||
})
|
||||
);
|
||||
|
||||
const viewConfig: ViewParserConfig = {
|
||||
babelApi,
|
||||
|
@ -226,7 +229,7 @@ const viewConfig: ViewParserConfig = {
|
|||
export const reactivityConfig: ReactivityParserConfig = {
|
||||
babelApi,
|
||||
availableProperties,
|
||||
dependencyMap,
|
||||
depMaskMap,
|
||||
};
|
||||
|
||||
export function parseCode(code: string) {
|
||||
|
@ -241,10 +244,7 @@ export function parseView(code: string) {
|
|||
return parseViewFromStatement(parseCode(code));
|
||||
}
|
||||
|
||||
export function parseReactivity(statement: t.BlockStatement) {
|
||||
return pR(parseViewFromStatement(statement), reactivityConfig)[0];
|
||||
}
|
||||
|
||||
export function parse(code: string) {
|
||||
// @ts-expect-error TODO: switch unit test to jsx-parser
|
||||
return pR(parseView(code), reactivityConfig)[0];
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ import type Babel from '@babel/core';
|
|||
|
||||
export interface DependencyValue<T> {
|
||||
value: T;
|
||||
dynamic: boolean; // to removed
|
||||
depMask: number; // -> bit
|
||||
dependenciesNode: t.ArrayExpression;
|
||||
}
|
||||
|
@ -20,7 +19,6 @@ export interface TemplateProp {
|
|||
key: string;
|
||||
path: number[];
|
||||
value: t.Expression;
|
||||
dynamic: boolean;
|
||||
depMask: number;
|
||||
dependenciesNode: t.ArrayExpression;
|
||||
}
|
||||
|
@ -71,25 +69,6 @@ export interface IfParticle {
|
|||
branches: IfBranch[];
|
||||
}
|
||||
|
||||
export interface SwitchBranch {
|
||||
case: DependencyValue<t.Expression>;
|
||||
children: ViewParticle[];
|
||||
break: boolean;
|
||||
}
|
||||
|
||||
export interface SwitchParticle {
|
||||
type: 'switch';
|
||||
discriminant: DependencyValue<t.Expression>;
|
||||
branches: SwitchBranch[];
|
||||
}
|
||||
|
||||
export interface TryParticle {
|
||||
type: 'try';
|
||||
children: ViewParticle[];
|
||||
exception: t.Identifier | t.ArrayPattern | t.ObjectPattern | null;
|
||||
catchChildren: ViewParticle[];
|
||||
}
|
||||
|
||||
export interface EnvParticle {
|
||||
type: 'env';
|
||||
props: Record<string, DependencyProp>;
|
||||
|
@ -102,13 +81,6 @@ export interface ExpParticle {
|
|||
props: Record<string, DependencyProp>;
|
||||
}
|
||||
|
||||
export interface SnippetParticle {
|
||||
type: 'snippet';
|
||||
tag: string;
|
||||
props: Record<string, DependencyProp>;
|
||||
children: ViewParticle[];
|
||||
}
|
||||
|
||||
export type ViewParticle =
|
||||
| TemplateParticle
|
||||
| TextParticle
|
||||
|
@ -117,17 +89,14 @@ export type ViewParticle =
|
|||
| ForParticle
|
||||
| IfParticle
|
||||
| EnvParticle
|
||||
| ExpParticle
|
||||
| SwitchParticle
|
||||
| SnippetParticle
|
||||
| TryParticle;
|
||||
| ExpParticle;
|
||||
|
||||
export interface ReactivityParserConfig {
|
||||
babelApi: typeof Babel;
|
||||
availableProperties: string[];
|
||||
availableIdentifiers?: string[];
|
||||
reactiveBitMap: ReactiveBitMap;
|
||||
identifierDepMap?: Record<string, string[]>;
|
||||
depMaskMap: DepMaskMap;
|
||||
identifierDepMap?: Record<string, Bitmap>;
|
||||
dependencyParseType?: 'property' | 'identifier';
|
||||
parseTemplate?: boolean;
|
||||
reactivityFuncNames?: string[];
|
||||
|
@ -135,4 +104,4 @@ export interface ReactivityParserConfig {
|
|||
|
||||
// TODO: unify with the types in babel-inula-next-core
|
||||
type Bitmap = number;
|
||||
export type ReactiveBitMap = Map<string, Bitmap>;
|
||||
export type DepMaskMap = Map<string, Bitmap>;
|
||||
|
|
Loading…
Reference in New Issue