/*
* File: /src/elements/BaseElement.ts
* Project: react-ast
* File Created: 28-11-2023 15:04:04
* Author: dharmendra
* -----
* BitSpur (c) Copyright 2019 - 2023
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import PropTypes from "prop-types";
import _get from "lodash.get";
import _set from "lodash.set";
import type { ParserOptions } from "@babel/parser";
import type {
BaseNode,
Path,
Node,
TextInstance,
Instance,
Props,
} from "../types";
import { flattenPath } from "../util";
export interface IElement
{
propTypes: Record;
defaultProps: Props;
new (props?: P, parserOptions?: ParserOptions): BaseElement;
}
export interface Meta {
bodyPath: Path;
parentBodyPath: Path | null;
}
export default class BaseElement implements Instance {
static defaultProps: Props = {};
static propTypes: Record = {};
node: Node;
props: Props;
children: (Instance | TextInstance)[] = [];
meta: Meta = {
bodyPath: "body",
parentBodyPath: null,
};
constructor(
baseNode: BaseNode | BaseNode[],
props: Props = {},
meta?: Partial,
) {
if (Array.isArray(baseNode)) throw new Error("cannot be array");
if (meta) {
this.meta = {
...this.meta,
...meta,
};
}
this.node = baseNode;
this.props = this.getProps(props);
}
getBodyPath(path?: Path | null): string {
return flattenPath(path || this.meta.bodyPath);
}
getBody(
body: BaseNode | BaseNode[],
path?: Path | null,
): BaseNode | BaseNode[] {
const bodyPath = this.getBodyPath(path);
if (!bodyPath.length) return body;
return _get(body, bodyPath);
}
setBody(
body: BaseNode | BaseNode[],
value: BaseNode | BaseNode[],
path?: Path | null,
): BaseNode | BaseNode[] {
const bodyPath = this.getBodyPath(path);
if (!bodyPath.length) return body;
return _set(body, bodyPath, value);
}
appendChild(child: Instance | TextInstance) {
const body = this.getBody(this.node, child.meta.parentBodyPath);
this.children.push(child);
if (Array.isArray(body)) {
body.push(child.node);
} else {
this.setBody(this.node, child.node, child.meta.parentBodyPath);
}
}
removeChild(child: Instance | TextInstance) {
const body = this.getBody(this.node, child.meta.parentBodyPath);
if (!body || !Array.isArray(body)) return;
this.children.splice(this.children.indexOf(child), 1);
body.splice(body.indexOf(child.node), 1);
}
commitMount() {
return undefined;
}
commitUpdate(newProps: Props) {
this.props = {
...this.props,
...newProps,
};
}
getProps(props: Props): Props {
props = { ...props };
const { defaultProps, propTypes } = this.constructor as IElement;
Object.keys(defaultProps).forEach((key) => {
const defaultProp = defaultProps[key];
if (typeof props[key] === "undefined" || props[key] === null) {
props[key] = defaultProp;
}
});
PropTypes.checkPropTypes(propTypes, props, "prop", this.constructor.name);
return props;
}
}