refactor(parse): use bitmap instead of dependency map
This commit is contained in:
parent
0dcad572f3
commit
601381032d
|
@ -19,6 +19,7 @@ import { addLifecycle, addWatch } from './nodeFactory';
|
||||||
import { types as t } from '@openinula/babel-api';
|
import { types as t } from '@openinula/babel-api';
|
||||||
import { ON_MOUNT, ON_UNMOUNT, WATCH, WILL_MOUNT, WILL_UNMOUNT } from '../constants';
|
import { ON_MOUNT, ON_UNMOUNT, WATCH, WILL_MOUNT, WILL_UNMOUNT } from '../constants';
|
||||||
import { extractFnFromMacro, getFnBody } from '../utils';
|
import { extractFnFromMacro, getFnBody } from '../utils';
|
||||||
|
import { getDependenciesFromNode } from './reactive/getDependencies';
|
||||||
|
|
||||||
function isLifeCycleName(name: string): name is LifeCycle {
|
function isLifeCycleName(name: string): name is LifeCycle {
|
||||||
return [WILL_MOUNT, ON_MOUNT, WILL_UNMOUNT, ON_UNMOUNT].includes(name);
|
return [WILL_MOUNT, ON_MOUNT, WILL_UNMOUNT, ON_UNMOUNT].includes(name);
|
||||||
|
@ -51,9 +52,9 @@ export function functionalMacroAnalyze(): Visitor {
|
||||||
if (calleeName === WATCH) {
|
if (calleeName === WATCH) {
|
||||||
const fnNode = extractFnFromMacro(expression, WATCH);
|
const fnNode = extractFnFromMacro(expression, WATCH);
|
||||||
const deps = getWatchDeps(expression);
|
const deps = getWatchDeps(expression);
|
||||||
if (!deps) {
|
|
||||||
// we auto collect the deps from the function body
|
const depBits = getDependenciesFromNode(deps ?? fnNode, ctx);
|
||||||
}
|
|
||||||
addWatch(ctx.current, fnNode, deps);
|
addWatch(ctx.current, fnNode, deps);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { NodePath, type types as t } from '@babel/core';
|
import { NodePath, type types as t } from '@babel/core';
|
||||||
import { ComponentNode, FunctionalExpression, LifeCycle, ReactiveVariable } from './types';
|
import type { ComponentNode, FunctionalExpression, LifeCycle, ReactiveVariable, Bitmap } from './types';
|
||||||
import { PropType } from '../constants';
|
import { PropType } from '../constants';
|
||||||
import { ViewParticle } from '@openinula/reactivity-parser';
|
import { ViewParticle } from '@openinula/reactivity-parser';
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ export function addProperty(comp: ComponentNode, name: string, value: t.Expressi
|
||||||
const bitmap = depBits ? depBits | bit : bit;
|
const bitmap = depBits ? depBits | bit : bit;
|
||||||
|
|
||||||
comp._reactiveBitMap.set(name, bitmap);
|
comp._reactiveBitMap.set(name, bitmap);
|
||||||
comp.variables.push({ name, value, isComputed: !!depBits, type: 'reactive', bitmap, level: comp.level });
|
comp.variables.push({ name, value, isComputed: !!depBits, type: 'reactive', depMask: bitmap, level: comp.level });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addMethod(comp: ComponentNode, name: string, value: FunctionalExpression) {
|
export function addMethod(comp: ComponentNode, name: string, value: FunctionalExpression) {
|
||||||
|
@ -87,13 +87,13 @@ export function addLifecycle(comp: ComponentNode, lifeCycle: LifeCycle, block: t
|
||||||
export function addWatch(
|
export function addWatch(
|
||||||
comp: ComponentNode,
|
comp: ComponentNode,
|
||||||
callback: NodePath<t.ArrowFunctionExpression> | NodePath<t.FunctionExpression>,
|
callback: NodePath<t.ArrowFunctionExpression> | NodePath<t.FunctionExpression>,
|
||||||
deps: NodePath<t.ArrayExpression> | null
|
depMask: Bitmap
|
||||||
) {
|
) {
|
||||||
// if watch not exist, create a new one
|
// if watch not exist, create a new one
|
||||||
if (!comp.watch) {
|
if (!comp.watch) {
|
||||||
comp.watch = [];
|
comp.watch = [];
|
||||||
}
|
}
|
||||||
comp.watch.push({ callback, deps });
|
comp.watch.push({ callback, depMask });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setViewChild(comp: ComponentNode, view: ViewParticle[], usedPropertySet: Set<string>) {
|
export function setViewChild(comp: ComponentNode, view: ViewParticle[], usedPropertySet: Set<string>) {
|
||||||
|
|
|
@ -26,14 +26,13 @@ import { reactivityFuncNames } from '../../const';
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function getDependenciesFromNode(
|
export function getDependenciesFromNode(
|
||||||
propertyKey: string,
|
|
||||||
path: NodePath<t.Expression | t.ClassDeclaration>,
|
path: NodePath<t.Expression | t.ClassDeclaration>,
|
||||||
{ current }: AnalyzeContext
|
{ current }: AnalyzeContext
|
||||||
) {
|
) {
|
||||||
// ---- Deps: console.log(count)
|
// ---- Deps: console.log(count)
|
||||||
let depsBit = 0;
|
let depMask = 0;
|
||||||
// ---- Assign deps: count = 1 or count++
|
// ---- Assign deps: count = 1 or count++
|
||||||
let assignDepBit = 0;
|
let assignDepMask = 0;
|
||||||
const depNodes: Record<string, t.Expression[]> = {};
|
const depNodes: Record<string, t.Expression[]> = {};
|
||||||
|
|
||||||
const visitor = (innerPath: NodePath<t.Identifier>) => {
|
const visitor = (innerPath: NodePath<t.Identifier>) => {
|
||||||
|
@ -42,9 +41,9 @@ export function getDependenciesFromNode(
|
||||||
|
|
||||||
if (reactiveBitmap !== undefined) {
|
if (reactiveBitmap !== undefined) {
|
||||||
if (isAssignmentExpressionLeft(innerPath) || isAssignmentFunction(innerPath)) {
|
if (isAssignmentExpressionLeft(innerPath) || isAssignmentFunction(innerPath)) {
|
||||||
assignDepBit |= reactiveBitmap;
|
assignDepMask |= reactiveBitmap;
|
||||||
} else {
|
} else {
|
||||||
depsBit |= reactiveBitmap;
|
depMask |= reactiveBitmap;
|
||||||
if (!depNodes[propertyKey]) depNodes[propertyKey] = [];
|
if (!depNodes[propertyKey]) depNodes[propertyKey] = [];
|
||||||
depNodes[propertyKey].push(t.cloneNode(innerPath.node));
|
depNodes[propertyKey].push(t.cloneNode(innerPath.node));
|
||||||
}
|
}
|
||||||
|
@ -61,11 +60,11 @@ export function getDependenciesFromNode(
|
||||||
// e.g. { console.log(count); count = 1 }
|
// e.g. { console.log(count); count = 1 }
|
||||||
// this will cause infinite loop
|
// this will cause infinite loop
|
||||||
// so we eliminate "count" from deps
|
// so we eliminate "count" from deps
|
||||||
if (assignDepBit & depsBit) {
|
if (assignDepMask & depMask) {
|
||||||
// TODO: I think we should throw an error here to indicate the user that there is a loop
|
// TODO: I think we should throw an error here to indicate the user that there is a loop
|
||||||
}
|
}
|
||||||
|
|
||||||
return depsBit;
|
return depMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -15,10 +15,10 @@
|
||||||
|
|
||||||
import { type NodePath, types as t } from '@babel/core';
|
import { type NodePath, types as t } from '@babel/core';
|
||||||
import { ON_MOUNT, ON_UNMOUNT, PropType, WILL_MOUNT, WILL_UNMOUNT } from '../constants';
|
import { ON_MOUNT, ON_UNMOUNT, PropType, WILL_MOUNT, WILL_UNMOUNT } from '../constants';
|
||||||
import { ViewParticle, PrevMap } from '@openinula/reactivity-parser';
|
import { ViewParticle } from '@openinula/reactivity-parser';
|
||||||
|
|
||||||
export type LifeCycle = typeof WILL_MOUNT | typeof ON_MOUNT | typeof WILL_UNMOUNT | typeof ON_UNMOUNT;
|
export type LifeCycle = typeof WILL_MOUNT | typeof ON_MOUNT | typeof WILL_UNMOUNT | typeof ON_UNMOUNT;
|
||||||
type Bitmap = number;
|
export type Bitmap = number;
|
||||||
|
|
||||||
export type FunctionalExpression = t.FunctionExpression | t.ArrowFunctionExpression;
|
export type FunctionalExpression = t.FunctionExpression | t.ArrowFunctionExpression;
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ export interface ReactiveVariable extends BaseVariable<t.Expression | null> {
|
||||||
* let age = 18; // age's bitmap is 0x0010
|
* let age = 18; // age's bitmap is 0x0010
|
||||||
* let greeting = `Hello, ${name}`; // greeting's bitmap is 0x0101
|
* let greeting = `Hello, ${name}`; // greeting's bitmap is 0x0101
|
||||||
*/
|
*/
|
||||||
bitmap: Bitmap;
|
depMask: Bitmap;
|
||||||
// need a flag for computed to gen a getter
|
// need a flag for computed to gen a getter
|
||||||
// watch is a static computed
|
// watch is a static computed
|
||||||
isComputed: boolean;
|
isComputed: boolean;
|
||||||
|
@ -93,8 +93,7 @@ export interface ComponentNode<Type = 'comp'> {
|
||||||
* The watch fn in the component
|
* The watch fn in the component
|
||||||
*/
|
*/
|
||||||
watch?: {
|
watch?: {
|
||||||
bit: Bitmap;
|
depMask: Bitmap;
|
||||||
deps: NodePath<t.ArrayExpression> | null;
|
|
||||||
callback: NodePath<t.ArrowFunctionExpression> | NodePath<t.FunctionExpression>;
|
callback: NodePath<t.ArrowFunctionExpression> | NodePath<t.FunctionExpression>;
|
||||||
}[];
|
}[];
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,7 +68,7 @@ export function variablesAnalyze(): Visitor {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
depBits = getDependenciesFromNode(id.node.name, init, ctx);
|
depBits = getDependenciesFromNode(init, ctx);
|
||||||
}
|
}
|
||||||
addProperty(ctx.current, id.node.name, init.node || null, depBits);
|
addProperty(ctx.current, id.node.name, init.node || null, depBits);
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ describe('analyze properties', () => {
|
||||||
const barVar = root.variables[1] as ReactiveVariable;
|
const barVar = root.variables[1] as ReactiveVariable;
|
||||||
expect(barVar.isComputed).toBe(true);
|
expect(barVar.isComputed).toBe(true);
|
||||||
expect(genCode(barVar.value)).toBe('foo');
|
expect(genCode(barVar.value)).toBe('foo');
|
||||||
expect(barVar.bitmap).toEqual(0b11);
|
expect(barVar.depMask).toEqual(0b11);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should analyze dependency from state in different shape', () => {
|
it('should analyze dependency from state in different shape', () => {
|
||||||
|
@ -68,7 +68,7 @@ describe('analyze properties', () => {
|
||||||
foo: foo ? a : b
|
foo: foo ? a : b
|
||||||
}"
|
}"
|
||||||
`);
|
`);
|
||||||
expect(barVar.bitmap).toEqual(0b1111);
|
expect(barVar.depMask).toEqual(0b1111);
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO:MOVE TO PROPS PLUGIN TEST
|
// TODO:MOVE TO PROPS PLUGIN TEST
|
||||||
|
@ -108,7 +108,7 @@ describe('analyze properties', () => {
|
||||||
expect(root.variables.length).toBe(1);
|
expect(root.variables.length).toBe(1);
|
||||||
const barVar = root.variables[0] as ReactiveVariable;
|
const barVar = root.variables[0] as ReactiveVariable;
|
||||||
expect(barVar.isComputed).toBe(false);
|
expect(barVar.isComputed).toBe(false);
|
||||||
expect(barVar.bitmap).toEqual(0b1);
|
expect(barVar.depMask).toEqual(0b1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -123,8 +123,8 @@ describe('analyze properties', () => {
|
||||||
})
|
})
|
||||||
`);
|
`);
|
||||||
expect(root.variables.length).toBe(2);
|
expect(root.variables.length).toBe(2);
|
||||||
expect(root.availableVariables[0].bitmap).toEqual(0b1);
|
expect(root.availableVariables[0].depMask).toEqual(0b1);
|
||||||
expect((root.variables[1] as SubCompVariable).ownAvailableVariables[0].bitmap).toBe(0b11);
|
expect((root.variables[1] as SubCompVariable).ownAvailableVariables[0].depMask).toBe(0b11);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should analyze dependency in parent', () => {
|
it('should analyze dependency in parent', () => {
|
||||||
|
@ -144,12 +144,12 @@ describe('analyze properties', () => {
|
||||||
`);
|
`);
|
||||||
const sonNode = root.variables[3] as SubCompVariable;
|
const sonNode = root.variables[3] as SubCompVariable;
|
||||||
// Son > middleName
|
// Son > middleName
|
||||||
expect(sonNode.ownAvailableVariables[0].bitmap).toBe(0b1111);
|
expect(sonNode.ownAvailableVariables[0].depMask).toBe(0b1111);
|
||||||
// Son > name
|
// Son > name
|
||||||
expect(sonNode.ownAvailableVariables[1].bitmap).toBe(0b11111);
|
expect(sonNode.ownAvailableVariables[1].depMask).toBe(0b11111);
|
||||||
const grandSonNode = sonNode.variables[2] as SubCompVariable;
|
const grandSonNode = sonNode.variables[2] as SubCompVariable;
|
||||||
// GrandSon > grandSonName
|
// GrandSon > grandSonName
|
||||||
expect(grandSonNode.ownAvailableVariables[0].bitmap).toBe(0b100001);
|
expect(grandSonNode.ownAvailableVariables[0].depMask).toBe(0b100001);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { variablesAnalyze } from '../../src/analyzer/variablesAnalyze';
|
import { variablesAnalyze } from '../../src/analyzer/variablesAnalyze';
|
||||||
import { propsAnalyze } from '../../src/analyzer/propsAnalyze';
|
|
||||||
import { ComponentNode } from '../../src/analyzer/types';
|
import { ComponentNode } from '../../src/analyzer/types';
|
||||||
import { viewAnalyze } from '../../src/analyzer/viewAnalyze';
|
import { viewAnalyze } from '../../src/analyzer/viewAnalyze';
|
||||||
import { genCode, mockAnalyze } from '../mock';
|
import { genCode, mockAnalyze } from '../mock';
|
||||||
|
@ -23,51 +22,27 @@ describe('viewAnalyze', () => {
|
||||||
});
|
});
|
||||||
`);
|
`);
|
||||||
const div = root.children![0] as any;
|
const div = root.children![0] as any;
|
||||||
expect(div.children[0].content.dependencyIndexArr).toMatchInlineSnapshot(`
|
expect(div.children[0].content.depMask).toEqual(0b11101);
|
||||||
[
|
|
||||||
4,
|
|
||||||
3,
|
|
||||||
2,
|
|
||||||
0,
|
|
||||||
]
|
|
||||||
`);
|
|
||||||
expect(genCode(div.children[0].content.dependenciesNode)).toMatchInlineSnapshot('"[doubleCount2]"');
|
expect(genCode(div.children[0].content.dependenciesNode)).toMatchInlineSnapshot('"[doubleCount2]"');
|
||||||
expect(div.props.className.dependencyIndexArr).toMatchInlineSnapshot(`
|
expect(div.props.className.depMask).toEqual(0b111);
|
||||||
[
|
|
||||||
1,
|
|
||||||
2,
|
|
||||||
0,
|
|
||||||
]
|
|
||||||
`);
|
|
||||||
expect(genCode(div.props.className.value)).toMatchInlineSnapshot('"className + count"');
|
expect(genCode(div.props.className.value)).toMatchInlineSnapshot('"className + count"');
|
||||||
|
|
||||||
// @ts-expect-error ignore ts here
|
// @ts-expect-error ignore ts here
|
||||||
const InputCompNode = (root.variables[3] as ComponentNode).value;
|
const InputCompNode = root.variables[5] as ComponentNode;
|
||||||
expect(InputCompNode.usedPropertySet).toMatchInlineSnapshot(`
|
expect(InputCompNode.usedPropertySet).toMatchInlineSnapshot(`
|
||||||
Set {
|
Set {
|
||||||
"count",
|
"count",
|
||||||
"doubleCount",
|
"doubleCount",
|
||||||
"name",
|
|
||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
// it's the {count}
|
// it's the {count}
|
||||||
const inputFirstExp = InputCompNode.children[0].children[0];
|
const inputFirstExp = InputCompNode.children![0].children[0];
|
||||||
expect(inputFirstExp.content.dependencyIndexArr).toMatchInlineSnapshot(`
|
expect(inputFirstExp.content.depMask).toEqual(0b100000);
|
||||||
[
|
|
||||||
5,
|
|
||||||
]
|
|
||||||
`);
|
|
||||||
expect(genCode(inputFirstExp.content.dependenciesNode)).toMatchInlineSnapshot('"[count]"');
|
expect(genCode(inputFirstExp.content.dependenciesNode)).toMatchInlineSnapshot('"[count]"');
|
||||||
|
|
||||||
// it's the {doubleCount}
|
// it's the {doubleCount}
|
||||||
const inputSecondExp = InputCompNode.children[0].children[1];
|
const inputSecondExp = InputCompNode.children[0].children[1];
|
||||||
expect(inputSecondExp.content.dependencyIndexArr).toMatchInlineSnapshot(`
|
expect(inputSecondExp.content.depMask).toEqual(0b1101);
|
||||||
[
|
|
||||||
3,
|
|
||||||
2,
|
|
||||||
0,
|
|
||||||
]
|
|
||||||
`);
|
|
||||||
expect(genCode(inputSecondExp.content.dependenciesNode)).toMatchInlineSnapshot('"[doubleCount]"');
|
expect(genCode(inputSecondExp.content.dependenciesNode)).toMatchInlineSnapshot('"[doubleCount]"');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -22,9 +22,9 @@ describe('watchAnalyze', () => {
|
||||||
// watch expression
|
// watch expression
|
||||||
}"
|
}"
|
||||||
`);
|
`);
|
||||||
if (!root.watch[0].deps) {
|
if (!root.watch[0].depMask) {
|
||||||
throw new Error('watch deps not found');
|
throw new Error('watch deps not found');
|
||||||
}
|
}
|
||||||
expect(genCode(root.watch[0].deps.node)).toMatchInlineSnapshot('"[a, b]"');
|
expect(genCode(root.watch[0].depMask)).toMatchInlineSnapshot('"[a, b]"');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -240,7 +240,7 @@ export class ReactivityParser {
|
||||||
key: 'value',
|
key: 'value',
|
||||||
path: [...path, idx],
|
path: [...path, idx],
|
||||||
value: child.content,
|
value: child.content,
|
||||||
depsBit: [],
|
depMask: 0,
|
||||||
dependenciesNode: this.t.arrayExpression([]),
|
dependenciesNode: this.t.arrayExpression([]),
|
||||||
dynamic: false,
|
dynamic: false,
|
||||||
});
|
});
|
||||||
|
@ -280,7 +280,7 @@ export class ReactivityParser {
|
||||||
* @returns ExpParticle | HTMLParticle
|
* @returns ExpParticle | HTMLParticle
|
||||||
*/
|
*/
|
||||||
private parseHTML(htmlUnit: HTMLUnit): ExpParticle | HTMLParticle {
|
private parseHTML(htmlUnit: HTMLUnit): ExpParticle | HTMLParticle {
|
||||||
const { depsBit, dependenciesNode } = this.getDependencies(htmlUnit.tag);
|
const { depMask, dependenciesNode, dynamic } = this.getDependencies(htmlUnit.tag);
|
||||||
|
|
||||||
const innerHTMLParticle: HTMLParticle = {
|
const innerHTMLParticle: HTMLParticle = {
|
||||||
type: 'html',
|
type: 'html',
|
||||||
|
@ -295,6 +295,9 @@ export class ReactivityParser {
|
||||||
|
|
||||||
innerHTMLParticle.children = htmlUnit.children.map(this.parseViewParticle.bind(this));
|
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
|
// ---- Dynamic tag, wrap it in an ExpParticle to make the tag reactive
|
||||||
const id = this.uid();
|
const id = this.uid();
|
||||||
return {
|
return {
|
||||||
|
@ -304,7 +307,7 @@ export class ReactivityParser {
|
||||||
viewPropMap: {
|
viewPropMap: {
|
||||||
[id]: [innerHTMLParticle],
|
[id]: [innerHTMLParticle],
|
||||||
},
|
},
|
||||||
depsBit,
|
depMask: depMask,
|
||||||
dependenciesNode,
|
dependenciesNode,
|
||||||
},
|
},
|
||||||
props: {},
|
props: {},
|
||||||
|
@ -320,7 +323,7 @@ export class ReactivityParser {
|
||||||
* @returns CompParticle | ExpParticle
|
* @returns CompParticle | ExpParticle
|
||||||
*/
|
*/
|
||||||
private parseComp(compUnit: CompUnit): CompParticle | ExpParticle {
|
private parseComp(compUnit: CompUnit): CompParticle | ExpParticle {
|
||||||
const { depsBit, dependenciesNode } = this.getDependencies(compUnit.tag);
|
const { depMask, dependenciesNode, dynamic } = this.getDependencies(compUnit.tag);
|
||||||
|
|
||||||
const compParticle: CompParticle = {
|
const compParticle: CompParticle = {
|
||||||
type: 'comp',
|
type: 'comp',
|
||||||
|
@ -344,7 +347,7 @@ export class ReactivityParser {
|
||||||
viewPropMap: {
|
viewPropMap: {
|
||||||
[id]: [compParticle],
|
[id]: [compParticle],
|
||||||
},
|
},
|
||||||
depsBit,
|
depMask: depMask,
|
||||||
dependenciesNode,
|
dependenciesNode,
|
||||||
dynamic,
|
dynamic,
|
||||||
},
|
},
|
||||||
|
@ -360,7 +363,7 @@ export class ReactivityParser {
|
||||||
* @returns ForParticle
|
* @returns ForParticle
|
||||||
*/
|
*/
|
||||||
private parseFor(forUnit: ForUnit): ForParticle {
|
private parseFor(forUnit: ForUnit): ForParticle {
|
||||||
const { depsBit, dependenciesNode } = this.getDependencies(forUnit.array);
|
const { depMask, dependenciesNode } = this.getDependencies(forUnit.array);
|
||||||
const prevIdentifierDepMap = this.config.identifierDepMap;
|
const prevIdentifierDepMap = this.config.identifierDepMap;
|
||||||
// ---- Find all the identifiers in the key and remove them from the identifierDepMap
|
// ---- 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,
|
// because once the key is changed, that identifier related dependencies will be changed too,
|
||||||
|
@ -371,7 +374,7 @@ export class ReactivityParser {
|
||||||
this.config.identifierDepMap = Object.fromEntries(
|
this.config.identifierDepMap = Object.fromEntries(
|
||||||
this.getIdentifiers(this.t.assignmentExpression('=', forUnit.item, this.t.objectExpression([])))
|
this.getIdentifiers(this.t.assignmentExpression('=', forUnit.item, this.t.objectExpression([])))
|
||||||
.filter(id => !keyDep || id !== keyDep)
|
.filter(id => !keyDep || id !== keyDep)
|
||||||
.map(id => [id, depsBit.map(n => this.availableProperties[n])])
|
.map(id => [id, depMask.map(n => this.availableProperties[n])])
|
||||||
);
|
);
|
||||||
|
|
||||||
const forParticle: ForParticle = {
|
const forParticle: ForParticle = {
|
||||||
|
@ -380,7 +383,7 @@ export class ReactivityParser {
|
||||||
array: {
|
array: {
|
||||||
value: forUnit.array,
|
value: forUnit.array,
|
||||||
dynamic,
|
dynamic,
|
||||||
depsBit,
|
depMask: depMask,
|
||||||
dependenciesNode,
|
dependenciesNode,
|
||||||
},
|
},
|
||||||
children: forUnit.children.map(this.parseViewParticle.bind(this)),
|
children: forUnit.children.map(this.parseViewParticle.bind(this)),
|
||||||
|
@ -470,12 +473,14 @@ export class ReactivityParser {
|
||||||
* @returns dependency index array
|
* @returns dependency index array
|
||||||
*/
|
*/
|
||||||
private getDependencies(node: t.Expression | t.Statement): {
|
private getDependencies(node: t.Expression | t.Statement): {
|
||||||
depsBit: number;
|
dynamic: boolean;
|
||||||
|
depMask: number;
|
||||||
dependenciesNode: t.ArrayExpression;
|
dependenciesNode: t.ArrayExpression;
|
||||||
} {
|
} {
|
||||||
if (this.t.isFunctionExpression(node) || this.t.isArrowFunctionExpression(node)) {
|
if (this.t.isFunctionExpression(node) || this.t.isArrowFunctionExpression(node)) {
|
||||||
return {
|
return {
|
||||||
depsBit: 0,
|
dynamic: false,
|
||||||
|
depMask: 0,
|
||||||
dependenciesNode: this.t.arrayExpression([]),
|
dependenciesNode: this.t.arrayExpression([]),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -487,7 +492,8 @@ export class ReactivityParser {
|
||||||
const depNodes = [...propertyDepNodes] as t.Expression[];
|
const depNodes = [...propertyDepNodes] as t.Expression[];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
depsBit: deps,
|
dynamic: depNodes.length > 0 || !!deps,
|
||||||
|
depMask: deps,
|
||||||
dependenciesNode: this.t.arrayExpression(depNodes),
|
dependenciesNode: this.t.arrayExpression(depNodes),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -559,10 +565,11 @@ export class ReactivityParser {
|
||||||
*/
|
*/
|
||||||
private getPropertyDependencies(node: t.Expression | t.Statement): [number, t.Node[]] {
|
private getPropertyDependencies(node: t.Expression | t.Statement): [number, t.Node[]] {
|
||||||
// ---- Deps: console.log(count)
|
// ---- Deps: console.log(count)
|
||||||
let depsBit = 0;
|
let depMask = 0;
|
||||||
// ---- Assign deps: count = 1 or count++
|
// ---- Assign deps: count = 1 or count++
|
||||||
let assignDepBit = 0;
|
let assignDepBit = 0;
|
||||||
const depNodes: Record<string, t.Node[]> = {};
|
const depNodes: Record<string, t.Node[]> = {};
|
||||||
|
const deps = new Set<string>();
|
||||||
|
|
||||||
const wrappedNode = this.valueWrapper(node);
|
const wrappedNode = this.valueWrapper(node);
|
||||||
this.traverse(wrappedNode, {
|
this.traverse(wrappedNode, {
|
||||||
|
@ -574,7 +581,9 @@ export class ReactivityParser {
|
||||||
if (this.isAssignmentExpressionLeft(innerPath) || this.isAssignmentFunction(innerPath)) {
|
if (this.isAssignmentExpressionLeft(innerPath) || this.isAssignmentFunction(innerPath)) {
|
||||||
assignDepBit |= reactiveBitmap;
|
assignDepBit |= reactiveBitmap;
|
||||||
} else {
|
} else {
|
||||||
depsBit |= reactiveBitmap;
|
depMask |= reactiveBitmap;
|
||||||
|
deps.add(propertyKey);
|
||||||
|
|
||||||
if (!depNodes[propertyKey]) depNodes[propertyKey] = [];
|
if (!depNodes[propertyKey]) depNodes[propertyKey] = [];
|
||||||
depNodes[propertyKey].push(this.t.cloneNode(innerPath.node));
|
depNodes[propertyKey].push(this.t.cloneNode(innerPath.node));
|
||||||
}
|
}
|
||||||
|
@ -586,7 +595,7 @@ export class ReactivityParser {
|
||||||
// e.g. { console.log(count); count = 1 }
|
// e.g. { console.log(count); count = 1 }
|
||||||
// this will cause infinite loop
|
// this will cause infinite loop
|
||||||
// so we eliminate "count" from deps
|
// so we eliminate "count" from deps
|
||||||
if (assignDepBit & depsBit) {
|
if (assignDepBit & depMask) {
|
||||||
// TODO: I think we should throw an error here to indicate the user that there is a loop
|
// TODO: I think we should throw an error here to indicate the user that there is a loop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -597,37 +606,8 @@ export class ReactivityParser {
|
||||||
return idx === i;
|
return idx === i;
|
||||||
});
|
});
|
||||||
|
|
||||||
// deps.forEach(this.usedProperties.add.bind(this.usedProperties));
|
deps.forEach(this.usedProperties.add.bind(this.usedProperties));
|
||||||
return [depsBit, dependencyNodes];
|
return [depMask, dependencyNodes];
|
||||||
}
|
|
||||||
|
|
||||||
private calDependencyIndexArr = (directDepKey: string) => {
|
|
||||||
// iterate the availableProperties reversely to find the index of the property
|
|
||||||
// cause the availableProperties is in the order of the code
|
|
||||||
const chainedDepKeys = this.findDependency(directDepKey);
|
|
||||||
const depKeyQueue = chainedDepKeys ? [directDepKey, ...chainedDepKeys] : [directDepKey];
|
|
||||||
depKeyQueue.forEach(this.usedProperties.add.bind(this.usedProperties));
|
|
||||||
|
|
||||||
let dep = depKeyQueue.shift();
|
|
||||||
const result: number[] = [];
|
|
||||||
for (let i = this.availableProperties.length - 1; i >= 0; i--) {
|
|
||||||
if (this.availableProperties[i] === dep) {
|
|
||||||
result.push(i);
|
|
||||||
dep = depKeyQueue.shift();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
private findDependency(propertyKey: string) {
|
|
||||||
let currentMap: ReactiveBitMap | undefined = this.reactiveBitMap;
|
|
||||||
do {
|
|
||||||
if (currentMap[propertyKey] !== undefined) {
|
|
||||||
return currentMap[propertyKey];
|
|
||||||
}
|
|
||||||
currentMap = currentMap[PrevMap];
|
|
||||||
} while (currentMap);
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -4,14 +4,14 @@ import type Babel from '@babel/core';
|
||||||
export interface DependencyValue<T> {
|
export interface DependencyValue<T> {
|
||||||
value: T;
|
value: T;
|
||||||
dynamic: boolean; // to removed
|
dynamic: boolean; // to removed
|
||||||
depsBit: number; // -> bit
|
depMask: number; // -> bit
|
||||||
dependenciesNode: t.ArrayExpression;
|
dependenciesNode: t.ArrayExpression;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DependencyProp {
|
export interface DependencyProp {
|
||||||
value: t.Expression;
|
value: t.Expression;
|
||||||
viewPropMap: Record<string, ViewParticle[]>;
|
viewPropMap: Record<string, ViewParticle[]>;
|
||||||
depsBit: number;
|
depMask: number;
|
||||||
dependenciesNode: t.ArrayExpression;
|
dependenciesNode: t.ArrayExpression;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ export interface TemplateProp {
|
||||||
path: number[];
|
path: number[];
|
||||||
value: t.Expression;
|
value: t.Expression;
|
||||||
dynamic: boolean;
|
dynamic: boolean;
|
||||||
depsBit: number;
|
depMask: number;
|
||||||
dependenciesNode: t.ArrayExpression;
|
dependenciesNode: t.ArrayExpression;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue