refactor(parse): use bitmap instead of dependency map

This commit is contained in:
Hoikan 2024-04-29 17:58:19 +08:00
parent f32da0e9c7
commit 0dcad572f3
10 changed files with 121 additions and 215 deletions

View File

@ -16,7 +16,7 @@
import { NodePath, type types as t } from '@babel/core';
import { ComponentNode, FunctionalExpression, LifeCycle, ReactiveVariable } from './types';
import { PropType } from '../constants';
import { ViewParticle, PrevMap } from '@openinula/reactivity-parser';
import { ViewParticle } from '@openinula/reactivity-parser';
export function createComponentNode(
name: string,
@ -29,7 +29,7 @@ export function createComponentNode(
name,
children: undefined,
variables: [],
dependencyMap: parent ? { [PrevMap]: parent.dependencyMap } : {},
_reactiveBitMap: parent ? new Map<string, number>(parent._reactiveBitMap) : new Map<string, number>(),
lifecycle: {},
parent,
fnNode,
@ -46,11 +46,14 @@ export function createComponentNode(
return comp;
}
export function addProperty(comp: ComponentNode, name: string, value: t.Expression | null, deps: string[] | null) {
comp.variables.push({ name, value, isComputed: !!deps?.length, type: 'reactive', deps });
if (comp.dependencyMap[name] === undefined) {
comp.dependencyMap[name] = null;
}
export function addProperty(comp: ComponentNode, name: string, value: t.Expression | null, depBits: number) {
// The index of the variable in the availableVariables
const idx = comp.availableVariables.length;
const bit = 1 << idx;
const bitmap = depBits ? depBits | bit : bit;
comp._reactiveBitMap.set(name, bitmap);
comp.variables.push({ name, value, isComputed: !!depBits, type: 'reactive', bitmap, level: comp.level });
}
export function addMethod(comp: ComponentNode, name: string, value: FunctionalExpression) {
@ -58,7 +61,7 @@ export function addMethod(comp: ComponentNode, name: string, value: FunctionalEx
}
export function addSubComponent(comp: ComponentNode, subComp: ComponentNode) {
comp.variables.push({ name: subComp.name, value: subComp, type: 'subComp' });
comp.variables.push({ ...subComp, type: 'subComp' });
}
export function addProp(

View File

@ -14,10 +14,9 @@
*/
import type { NodePath } from '@babel/core';
import { AnalyzeContext, DependencyMap } from '../types';
import { AnalyzeContext } from '../types';
import { types as t } from '@openinula/babel-api';
import { reactivityFuncNames } from '../../const';
import { PrevMap } from '@openinula/reactivity-parser';
/**
* @brief Get all valid dependencies of a babel path
@ -32,20 +31,23 @@ export function getDependenciesFromNode(
{ current }: AnalyzeContext
) {
// ---- Deps: console.log(count)
const deps = new Set<string>();
let depsBit = 0;
// ---- Assign deps: count = 1 or count++
const assignDeps = new Set<string>();
let assignDepBit = 0;
const depNodes: Record<string, t.Expression[]> = {};
const visitor = (innerPath: NodePath<t.Identifier>) => {
const propertyKey = innerPath.node.name;
if (isAssignmentExpressionLeft(innerPath) || isAssignmentFunction(innerPath)) {
assignDeps.add(propertyKey);
} else if (current.availableVariables.includes(propertyKey)) {
deps.add(propertyKey);
findDependency(current.dependencyMap, propertyKey)?.forEach(deps.add.bind(deps));
if (!depNodes[propertyKey]) depNodes[propertyKey] = [];
depNodes[propertyKey].push(t.cloneNode(innerPath.node));
const reactiveBitmap = current._reactiveBitMap.get(propertyKey);
if (reactiveBitmap !== undefined) {
if (isAssignmentExpressionLeft(innerPath) || isAssignmentFunction(innerPath)) {
assignDepBit |= reactiveBitmap;
} else {
depsBit |= reactiveBitmap;
if (!depNodes[propertyKey]) depNodes[propertyKey] = [];
depNodes[propertyKey].push(t.cloneNode(innerPath.node));
}
}
};
if (path.isIdentifier()) {
@ -59,16 +61,11 @@ export function getDependenciesFromNode(
// e.g. { console.log(count); count = 1 }
// this will cause infinite loop
// so we eliminate "count" from deps
assignDeps.forEach(dep => {
deps.delete(dep);
});
const depArr = [...deps];
if (deps.size > 0) {
current.dependencyMap[propertyKey] = depArr;
if (assignDepBit & depsBit) {
// TODO: I think we should throw an error here to indicate the user that there is a loop
}
return depArr;
return depsBit;
}
/**
@ -110,15 +107,3 @@ function isAssignmentFunction(innerPath: NodePath): boolean {
reactivityFuncNames.includes((parentPath.get('callee').node as t.Identifier).name)
);
}
function findDependency(dependencyMap: DependencyMap, propertyKey: string) {
let currentMap: DependencyMap | undefined = dependencyMap;
do {
if (currentMap[propertyKey] !== undefined) {
return currentMap[propertyKey];
}
// trace back to the previous map
currentMap = currentMap[PrevMap];
} while (currentMap);
return null;
}

View File

@ -30,7 +30,14 @@ interface BaseVariable<V> {
export interface ReactiveVariable extends BaseVariable<t.Expression | null> {
type: 'reactive';
level: number;
bitmap?: Bitmap;
/**
* indicate the dependency of the variable | the index of the reactive variable
* i.e.
* let name = 'John'; // name's bitmap is 0x0001
* let age = 18; // age's bitmap is 0x0010
* let greeting = `Hello, ${name}`; // greeting's bitmap is 0x0101
*/
bitmap: Bitmap;
// need a flag for computed to gen a getter
// watch is a static computed
isComputed: boolean;
@ -40,9 +47,7 @@ export interface MethodVariable extends BaseVariable<FunctionalExpression> {
type: 'method';
}
export interface SubCompVariable extends BaseVariable<ComponentNode> {
type: 'subComp';
}
export type SubCompVariable = ComponentNode<'subComp'>;
export type Variable = ReactiveVariable | MethodVariable | SubCompVariable;
@ -55,8 +60,8 @@ export interface Prop {
nestedRelationship: t.ObjectPattern | t.ArrayPattern | null;
}
export interface ComponentNode {
type: 'comp';
export interface ComponentNode<Type = 'comp'> {
type: Type;
name: string;
level: number;
// The variables defined in the component
@ -66,9 +71,9 @@ export interface ComponentNode {
*/
usedPropertySet?: Set<string>;
/**
* The available props for the component, including the nested props
* The map to find the reactive bitmap by name
*/
availableProps: string[];
_reactiveBitMap: Map<string, Bitmap>;
/**
* The available variables and props owned by the component
*/

View File

@ -43,7 +43,7 @@ export function variablesAnalyze(): Visitor {
} else if (id.isIdentifier()) {
// --- properties: the state / computed / plain properties / methods ---
const init = declaration.get('init');
let deps: string[] | null = null;
let depBits = 0;
if (isValidPath(init)) {
// handle the method
if (init.isArrowFunctionExpression() || init.isFunctionExpression()) {
@ -68,9 +68,9 @@ export function variablesAnalyze(): Visitor {
return;
}
deps = getDependenciesFromNode(id.node.name, init, ctx);
depBits = getDependenciesFromNode(id.node.name, init, ctx);
}
addProperty(ctx.current, id.node.name, init.node || null, deps);
addProperty(ctx.current, id.node.name, init.node || null, depBits);
}
});
},

View File

@ -34,10 +34,11 @@ export function viewAnalyze(): Visitor {
htmlTags,
parseTemplate: false,
});
// @ts-expect-error TODO: FIX TYPE
const [viewParticles, usedPropertySet] = parseReactivity(viewUnits, {
babelApi: getBabelApi(),
availableProperties: current.availableVariables,
dependencyMap: current.dependencyMap,
reactiveBitMap: current._reactiveBitMap,
reactivityFuncNames,
});

View File

@ -16,10 +16,9 @@
import { describe, expect, it } from 'vitest';
import { genCode, mockAnalyze } from '../mock';
import { variablesAnalyze } from '../../src/analyzer/variablesAnalyze';
import { propsAnalyze } from '../../src/analyzer/propsAnalyze';
import { ComponentNode, ReactiveVariable } from '../../src/analyzer/types';
import { ReactiveVariable, SubCompVariable } from '../../src/analyzer/types';
const analyze = (code: string) => mockAnalyze(code, [propsAnalyze, variablesAnalyze]);
const analyze = (code: string) => mockAnalyze(code, [variablesAnalyze]);
describe('analyze properties', () => {
it('should work', () => {
@ -48,7 +47,7 @@ describe('analyze properties', () => {
const barVar = root.variables[1] as ReactiveVariable;
expect(barVar.isComputed).toBe(true);
expect(genCode(barVar.value)).toBe('foo');
expect(root.dependencyMap).toEqual({ bar: ['foo'], foo: null });
expect(barVar.bitmap).toEqual(0b11);
});
it('should analyze dependency from state in different shape', () => {
@ -69,10 +68,11 @@ describe('analyze properties', () => {
foo: foo ? a : b
}"
`);
expect(root.dependencyMap).toEqual({ bar: ['foo', 'a', 'b'], foo: null, a: null, b: null });
expect(barVar.bitmap).toEqual(0b1111);
});
it('should analyze dependency from props', () => {
// TODO:MOVE TO PROPS PLUGIN TEST
it.skip('should analyze dependency from props', () => {
const root = analyze(`
Component(({ foo }) => {
let bar = foo;
@ -82,10 +82,10 @@ describe('analyze properties', () => {
const barVar = root.variables[0] as ReactiveVariable;
expect(barVar.isComputed).toBe(true);
expect(root.dependencyMap).toEqual({ bar: ['foo'] });
});
it('should analyze dependency from nested props', () => {
// TODO:MOVE TO PROPS PLUGIN TEST
it.skip('should analyze dependency from nested props', () => {
const root = analyze(`
Component(({ foo: foo1, name: [first, last] }) => {
let bar = [foo1, first, last];
@ -94,6 +94,7 @@ describe('analyze properties', () => {
expect(root.variables.length).toBe(1);
const barVar = root.variables[0] as ReactiveVariable;
expect(barVar.isComputed).toBe(true);
// @ts-expect-error ignore ts here
expect(root.dependencyMap).toEqual({ bar: ['foo1', 'first', 'last'] });
});
@ -107,7 +108,7 @@ describe('analyze properties', () => {
expect(root.variables.length).toBe(1);
const barVar = root.variables[0] as ReactiveVariable;
expect(barVar.isComputed).toBe(false);
expect(root.dependencyMap).toEqual({ bar: null });
expect(barVar.bitmap).toEqual(0b1);
});
});
@ -122,22 +123,14 @@ describe('analyze properties', () => {
})
`);
expect(root.variables.length).toBe(2);
expect(root.dependencyMap).toEqual({ foo: null });
expect((root.variables[1].value as ComponentNode).dependencyMap).toMatchInlineSnapshot(`
{
"bar": [
"foo",
],
Symbol(prevMap): {
"foo": null,
},
}
`);
expect(root.availableVariables[0].bitmap).toEqual(0b1);
expect((root.variables[1] as SubCompVariable).ownAvailableVariables[0].bitmap).toBe(0b11);
});
it('should analyze dependency in parent', () => {
const root = analyze(`
Component(({lastName}) => {
Component(() => {
let lastName;
let parentFirstName = 'sheldon';
const parentName = parentFirstName + lastName;
const Son = Component(() => {
@ -149,63 +142,15 @@ describe('analyze properties', () => {
});
})
`);
const sonNode = root.variables[2].value as ComponentNode;
expect(sonNode.dependencyMap).toMatchInlineSnapshot(`
{
"middleName": [
"parentName",
"parentFirstName",
"lastName",
],
"name": [
"middleName",
"parentName",
"parentFirstName",
"lastName",
],
Symbol(prevMap): {
"parentFirstName": null,
"parentName": [
"parentFirstName",
"lastName",
],
},
}
`);
const grandSonNode = sonNode.variables[2].value as ComponentNode;
expect(grandSonNode.dependencyMap).toMatchInlineSnapshot(`
{
"grandSonName": [
"lastName",
],
Symbol(prevMap): {
"middleName": [
"parentName",
"parentFirstName",
"lastName",
],
"name": [
"middleName",
"parentName",
"parentFirstName",
"lastName",
],
Symbol(prevMap): {
"parentFirstName": null,
"parentName": [
"parentFirstName",
"lastName",
],
},
},
}
`);
const sonNode = root.variables[3] as SubCompVariable;
// Son > middleName
expect(sonNode.ownAvailableVariables[0].bitmap).toBe(0b1111);
// Son > name
expect(sonNode.ownAvailableVariables[1].bitmap).toBe(0b11111);
const grandSonNode = sonNode.variables[2] as SubCompVariable;
// GrandSon > grandSonName
expect(grandSonNode.ownAvailableVariables[0].bitmap).toBe(0b100001);
});
// SubscriptionTree
// const SubscriptionTree = {
// lastName: ['parentName','son:middleName','son:name','son,grandSon:grandSonName'],
//
// }
});
it('should collect method', () => {
@ -222,10 +167,5 @@ describe('analyze properties', () => {
expect(root.variables.map(p => p.name)).toEqual(['foo', 'onClick', 'onHover', 'onInput']);
expect(root.variables[1].type).toBe('method');
expect(root.variables[2].type).toBe('method');
expect(root.dependencyMap).toMatchInlineSnapshot(`
{
"foo": null,
}
`);
});
});

View File

@ -5,27 +5,18 @@ import { viewAnalyze } from '../../src/analyzer/viewAnalyze';
import { genCode, mockAnalyze } from '../mock';
import { describe, expect, it } from 'vitest';
const analyze = (code: string) => mockAnalyze(code, [propsAnalyze, variablesAnalyze, viewAnalyze]);
const analyze = (code: string) => mockAnalyze(code, [variablesAnalyze, viewAnalyze]);
describe('viewAnalyze', () => {
it('should analyze view', () => {
const root = analyze(/*js*/ `
Component(({name ,className}) => {
Component(({}) => {
let name;
let className;
let count = name; // 1
let doubleCount = count* 2; // 2
let doubleCount2 = doubleCount* 2; // 4
const Input = Component(() => {
let count = 1;
watch(() => {
if (doubleCount2 > 10) {
count++;
}
console.log(doubleCount2);
});
const update = changed => {
if (changed & 0x1011) {
node1.update(_$this0.count, _$this0.doubleCount);
}
};
return <input>{count}{doubleCount}</input>;
});
return <div className={className + count}>{doubleCount2}</div>;

View File

@ -12,7 +12,7 @@ import {
type ForParticle,
type IfParticle,
type EnvParticle,
DependencyMap,
ReactiveBitMap,
} from './types';
import { type NodePath, type types as t, type traverse } from '@babel/core';
import {
@ -36,7 +36,7 @@ export class ReactivityParser {
private readonly traverse: typeof traverse;
private readonly availableProperties: string[];
private readonly availableIdentifiers?: string[];
private readonly dependencyMap: DependencyMap;
private readonly reactiveBitMap: ReactiveBitMap;
private readonly identifierDepMap: Record<string, string[]>;
private readonly dependencyParseType;
private readonly reactivityFuncNames;
@ -70,7 +70,7 @@ export class ReactivityParser {
this.traverse = config.babelApi.traverse;
this.availableProperties = config.availableProperties;
this.availableIdentifiers = config.availableIdentifiers;
this.dependencyMap = config.dependencyMap;
this.reactiveBitMap = config.reactiveBitMap;
this.identifierDepMap = config.identifierDepMap ?? {};
this.dependencyParseType = config.dependencyParseType ?? 'property';
this.reactivityFuncNames = config.reactivityFuncNames ?? [];
@ -123,8 +123,8 @@ export class ReactivityParser {
* @brief Generate a template
* There'll be a situation where the tag is dynamic, e.g. tag(this.htmlTag),
* which we can't generate a template string for it, so we'll wrap it in an ExpParticle in parseHTML() section
* @param htmlUnit
* @returns template string
* @param unit
*/
private generateTemplate(unit: HTMLUnit): HTMLParticle {
const staticProps = this.filterTemplateProps(
@ -240,7 +240,7 @@ export class ReactivityParser {
key: 'value',
path: [...path, idx],
value: child.content,
dependencyIndexArr: [],
depsBit: [],
dependenciesNode: this.t.arrayExpression([]),
dynamic: false,
});
@ -280,7 +280,7 @@ export class ReactivityParser {
* @returns ExpParticle | HTMLParticle
*/
private parseHTML(htmlUnit: HTMLUnit): ExpParticle | HTMLParticle {
const { dependencyIndexArr, dependenciesNode, dynamic } = this.getDependencies(htmlUnit.tag);
const { depsBit, dependenciesNode } = this.getDependencies(htmlUnit.tag);
const innerHTMLParticle: HTMLParticle = {
type: 'html',
@ -295,9 +295,6 @@ 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 {
@ -307,9 +304,8 @@ export class ReactivityParser {
viewPropMap: {
[id]: [innerHTMLParticle],
},
dependencyIndexArr,
depsBit,
dependenciesNode,
dynamic,
},
props: {},
};
@ -324,7 +320,7 @@ export class ReactivityParser {
* @returns CompParticle | ExpParticle
*/
private parseComp(compUnit: CompUnit): CompParticle | ExpParticle {
const { dependencyIndexArr, dependenciesNode, dynamic } = this.getDependencies(compUnit.tag);
const { depsBit, dependenciesNode } = this.getDependencies(compUnit.tag);
const compParticle: CompParticle = {
type: 'comp',
@ -348,7 +344,7 @@ export class ReactivityParser {
viewPropMap: {
[id]: [compParticle],
},
dependencyIndexArr,
depsBit,
dependenciesNode,
dynamic,
},
@ -364,7 +360,7 @@ export class ReactivityParser {
* @returns ForParticle
*/
private parseFor(forUnit: ForUnit): ForParticle {
const { dependencyIndexArr, dependenciesNode, dynamic } = this.getDependencies(forUnit.array);
const { depsBit, dependenciesNode } = this.getDependencies(forUnit.array);
const prevIdentifierDepMap = this.config.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,
@ -375,7 +371,7 @@ export class ReactivityParser {
this.config.identifierDepMap = Object.fromEntries(
this.getIdentifiers(this.t.assignmentExpression('=', forUnit.item, this.t.objectExpression([])))
.filter(id => !keyDep || id !== keyDep)
.map(id => [id, dependencyIndexArr.map(n => this.availableProperties[n])])
.map(id => [id, depsBit.map(n => this.availableProperties[n])])
);
const forParticle: ForParticle = {
@ -384,7 +380,7 @@ export class ReactivityParser {
array: {
value: forUnit.array,
dynamic,
dependencyIndexArr,
depsBit,
dependenciesNode,
},
children: forUnit.children.map(this.parseViewParticle.bind(this)),
@ -474,30 +470,24 @@ export class ReactivityParser {
* @returns dependency index array
*/
private getDependencies(node: t.Expression | t.Statement): {
dynamic: boolean;
dependencyIndexArr: number[];
depsBit: number;
dependenciesNode: t.ArrayExpression;
} {
if (this.t.isFunctionExpression(node) || this.t.isArrowFunctionExpression(node)) {
return {
dynamic: false,
dependencyIndexArr: [],
depsBit: 0,
dependenciesNode: this.t.arrayExpression([]),
};
}
// ---- Both id and prop deps need to be calculated because
// id is for snippet update, prop is normal update
// in a snippet, the depsNode should be both id and prop
const [directPropertyDeps, propertyDepNodes] = this.getPropertyDependencies(node);
const directDependencies = directPropertyDeps;
const identifierMapDependencies = this.getIdentifierMapDependencies(node);
const deps = [...new Set([...directDependencies, ...identifierMapDependencies])];
const [deps, propertyDepNodes] = this.getPropertyDependencies(node);
const depNodes = [...propertyDepNodes] as t.Expression[];
return {
dynamic: depNodes.length > 0 || deps.length > 0,
dependencyIndexArr: deps,
depsBit: deps,
dependenciesNode: this.t.arrayExpression(depNodes),
};
}
@ -534,7 +524,7 @@ export class ReactivityParser {
!this.isMemberInManualFunction(innerPath)
) {
deps.add(idName);
this.dependencyMap[idName]?.forEach(deps.add.bind(deps));
this.reactiveBitMap[idName]?.forEach(deps.add.bind(deps));
if (!depNodes[idName]) depNodes[idName] = [];
depNodes[idName].push(this.geneDependencyNode(innerPath));
}
@ -567,34 +557,39 @@ export class ReactivityParser {
* @param node
* @returns dependency index array
*/
private getPropertyDependencies(node: t.Expression | t.Statement): [number[], t.Node[]] {
const deps = new Set<string>();
const assignDeps = new Set<string>();
private getPropertyDependencies(node: t.Expression | t.Statement): [number, t.Node[]] {
// ---- Deps: console.log(count)
let depsBit = 0;
// ---- Assign deps: count = 1 or count++
let assignDepBit = 0;
const depNodes: Record<string, t.Node[]> = {};
const wrappedNode = this.valueWrapper(node);
this.traverse(wrappedNode, {
Identifier: innerPath => {
const propertyKey = innerPath.node.name;
const reactiveBitmap = this.reactiveBitMap.get(propertyKey);
if (this.isAssignmentExpressionLeft(innerPath) || this.isAssignmentFunction(innerPath)) {
assignDeps.add(propertyKey);
} else if (
this.availableProperties.includes(propertyKey) &&
!this.isMemberInEscapeFunction(innerPath) &&
!this.isMemberInManualFunction(innerPath)
) {
deps.add(propertyKey);
if (!depNodes[propertyKey]) depNodes[propertyKey] = [];
depNodes[propertyKey].push(this.geneDependencyNode(innerPath));
if (reactiveBitmap !== undefined) {
if (this.isAssignmentExpressionLeft(innerPath) || this.isAssignmentFunction(innerPath)) {
assignDepBit |= reactiveBitmap;
} else {
depsBit |= reactiveBitmap;
if (!depNodes[propertyKey]) depNodes[propertyKey] = [];
depNodes[propertyKey].push(this.t.cloneNode(innerPath.node));
}
}
},
});
const dependencyIdxArr = deduplicate([...deps].map(this.calDependencyIndexArr).flat());
assignDeps.forEach(dep => {
deps.delete(dep);
delete depNodes[dep];
});
// ---- Eliminate deps that are assigned in the same method
// e.g. { console.log(count); count = 1 }
// this will cause infinite loop
// so we eliminate "count" from deps
if (assignDepBit & depsBit) {
// TODO: I think we should throw an error here to indicate the user that there is a loop
}
let dependencyNodes = Object.values(depNodes).flat();
// ---- deduplicate the dependency nodes
dependencyNodes = dependencyNodes.filter((n, i) => {
@ -602,8 +597,8 @@ export class ReactivityParser {
return idx === i;
});
deps.forEach(this.usedProperties.add.bind(this.usedProperties));
return [dependencyIdxArr, dependencyNodes];
// deps.forEach(this.usedProperties.add.bind(this.usedProperties));
return [depsBit, dependencyNodes];
}
private calDependencyIndexArr = (directDepKey: string) => {
@ -625,7 +620,7 @@ export class ReactivityParser {
};
private findDependency(propertyKey: string) {
let currentMap: DependencyMap | undefined = this.dependencyMap;
let currentMap: ReactiveBitMap | undefined = this.reactiveBitMap;
do {
if (currentMap[propertyKey] !== undefined) {
return currentMap[propertyKey];

View File

@ -1,19 +1,17 @@
import { type types as t } from '@babel/core';
import type Babel from '@babel/core';
import { PrevMap } from '.';
export interface DependencyValue<T> {
value: T;
dynamic: boolean; // to removed
dependencyIndexArr: number[]; // -> bit
depsBit: number; // -> bit
dependenciesNode: t.ArrayExpression;
}
export interface DependencyProp {
value: t.Expression;
viewPropMap: Record<string, ViewParticle[]>;
dynamic: boolean;
dependencyIndexArr: number[];
depsBit: number;
dependenciesNode: t.ArrayExpression;
}
@ -23,7 +21,7 @@ export interface TemplateProp {
path: number[];
value: t.Expression;
dynamic: boolean;
dependencyIndexArr: number[];
depsBit: number;
dependenciesNode: t.ArrayExpression;
}
@ -128,25 +126,13 @@ export interface ReactivityParserConfig {
babelApi: typeof Babel;
availableProperties: string[];
availableIdentifiers?: string[];
dependencyMap: Record<string, string[] | null>;
reactiveBitMap: ReactiveBitMap;
identifierDepMap?: Record<string, string[]>;
dependencyParseType?: 'property' | 'identifier';
parseTemplate?: boolean;
reactivityFuncNames?: string[];
}
export interface DependencyMap {
/**
* key is the variable name, value is the dependencies
* i.e. {
* count: ['flag'],
* state1: ['count', 'flag'],
* state2: ['count', 'flag', 'state1'],
* state3: ['count', 'flag', 'state1', 'state2'],
* state4: ['count', 'flag', 'state1', 'state2', 'state3'],
* }
*/
[key: string]: string[] | null;
[PrevMap]?: DependencyMap;
}
// TODO: unify with the types in babel-inula-next-core
type Bitmap = number;
export type ReactiveBitMap = Map<string, Bitmap>;