init
This commit is contained in:
75
src/main.ts
75
src/main.ts
@@ -1,33 +1,50 @@
|
|||||||
import { Component, type ComponentBlueprint } from "./module/Component";
|
import { Component, type Blueprint } from "./module/Component";
|
||||||
import { HTMLComponent } from "./module/HTMLComponent";
|
|
||||||
import { State } from "./module/State";
|
import { State } from "./module/State";
|
||||||
import { TextComponent } from "./module/TextComponent";
|
import { html } from "./module/html";
|
||||||
|
|
||||||
class Counter extends Component<{ initValue: number, children: State<ComponentBlueprint<any>[]> }> {
|
|
||||||
state: { count: State<number> } = {
|
|
||||||
count: new State(0)
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(props: { initValue: number }) {
|
|
||||||
super({
|
|
||||||
...props,
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
component: HTMLComponent,
|
|
||||||
props: {
|
|
||||||
children: new State([{
|
|
||||||
component: TextComponent,
|
|
||||||
props: {
|
|
||||||
content: this.state.count
|
|
||||||
}
|
|
||||||
}])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
type Props = { initValue?: number }
|
||||||
|
class Counter extends Component<Props, { count: State<number>, double: State<number> }> {
|
||||||
init() {
|
init() {
|
||||||
this.state.count.set(this.props.initValue);
|
this.states.count = new State(this.propStates.initValue?.value ?? 0);
|
||||||
|
this.states.double = this.derived((value) => value * 2, [this.states.count]);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
blueprint(): Blueprint[] {
|
||||||
|
const s = html`
|
||||||
|
<button onclick="${() => this.states.count.update((val) => val + 1)}">
|
||||||
|
${this.states.count}
|
||||||
|
</button>
|
||||||
|
`;
|
||||||
|
return [
|
||||||
|
...s,
|
||||||
|
{
|
||||||
|
type: 'component',
|
||||||
|
component: RedText,
|
||||||
|
props: {
|
||||||
|
content: this.derived((double) => `doulbed: ${double}`, [this.states.double])
|
||||||
|
},
|
||||||
|
children: []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RedText extends Component<{ content: string }, {}> {
|
||||||
|
init() { }
|
||||||
|
blueprint(): Blueprint[] {
|
||||||
|
return html`
|
||||||
|
<span style="color: red;">
|
||||||
|
${this.propStates.content}
|
||||||
|
</span>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const counter = new Counter({ initValue: 1 });
|
||||||
|
counter.mount(document.body);
|
||||||
|
|
||||||
|
|
||||||
|
const btn = document.createElement('button');
|
||||||
|
btn.innerText = "destroy";
|
||||||
|
btn.onclick = () => {counter.destroy()};
|
||||||
|
document.body.appendChild(btn);
|
||||||
@@ -1,44 +1,27 @@
|
|||||||
|
import { ComponentBase } from "./ComponentBase";
|
||||||
|
import { ComponentFactory } from "./ComponentFactory";
|
||||||
import { State } from "./State";
|
import { State } from "./State";
|
||||||
import { TextComponent } from "./TextComponent";
|
|
||||||
|
|
||||||
export abstract class Component<Props extends PropsBase> {
|
export abstract class Component<Props extends PropsBase, States extends StatesBase> extends ComponentBase<Props, States> {
|
||||||
static propsToPropState<Props extends PropsBase>(props: Props) {
|
declare node: DocumentFragment;
|
||||||
const propsState: Record<string, State<any>> = {};
|
|
||||||
Object.entries(props).forEach(([key, value]) => {
|
constructor(props: Props, children: Blueprint[] = []) {
|
||||||
if (value instanceof State) {
|
super(props);
|
||||||
propsState[key] = value;
|
this.node = this.createNode();
|
||||||
}
|
|
||||||
else {
|
const blueprint = this.blueprint(children);
|
||||||
propsState[key] = new State(value);
|
this.children = blueprint.map((bp) => ComponentFactory.blueprintToComponentInstance(bp));
|
||||||
}
|
this.children.forEach((child) => {
|
||||||
|
child.mount(this.node);
|
||||||
});
|
});
|
||||||
return propsState as PropState<Props>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static blueprintsToComponentInstances(blueprints: Blueprint[]): Component<any>[] {
|
|
||||||
return blueprints.map((blueprint) => this.blueprintToComponentInstance(blueprint));
|
|
||||||
}
|
|
||||||
|
|
||||||
static blueprintToComponentInstance(blueprint: Blueprint) {
|
|
||||||
if (blueprint.type === "text") {
|
|
||||||
return new TextComponent(blueprint.props);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
propState: PropState<Props>;
|
|
||||||
children: Component<any>[] = [];
|
|
||||||
mounted: boolean = false;
|
|
||||||
subscriberSymbols: Symbol[] = [];
|
|
||||||
abstract node: Node;
|
|
||||||
|
|
||||||
constructor(props: Props) {
|
|
||||||
this.propState = Component.propsToPropState(props);
|
|
||||||
this.init();
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract init(): void;
|
|
||||||
abstract blueprint(children?: Blueprint[]): Blueprint[];
|
abstract blueprint(children?: Blueprint[]): Blueprint[];
|
||||||
mount(target: HTMLElement) {
|
createNode(): DocumentFragment {
|
||||||
|
return document.createDocumentFragment();
|
||||||
|
}
|
||||||
|
|
||||||
|
mount(target: Node) {
|
||||||
if (this.mounted) {
|
if (this.mounted) {
|
||||||
throw new Error("This component is already mounted");
|
throw new Error("This component is already mounted");
|
||||||
}
|
}
|
||||||
@@ -46,18 +29,22 @@ export abstract class Component<Props extends PropsBase> {
|
|||||||
this.mounted = true;
|
this.mounted = true;
|
||||||
}
|
}
|
||||||
destroy() {
|
destroy() {
|
||||||
|
this.children.forEach((child) => child.destroy());
|
||||||
|
this.subscriberSymbols.forEach(({ state, symbol }) => {
|
||||||
|
state.unsubscribe(symbol);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type StatesBase = Record<string, State<any>>;
|
||||||
export type PropsBase = Record<string, any>;
|
export type PropsBase = Record<string, any>;
|
||||||
export type PropState<Props extends PropsBase> = {
|
export type PropState<Props extends PropsBase> = {
|
||||||
[K in keyof Props]: State<Props[K]>
|
[K in keyof Props]: State<Props[K]>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComponentBlueprint<Props extends PropsBase> = {
|
export type ComponentBlueprint<Props extends PropsBase, States extends StatesBase> = {
|
||||||
type: 'component',
|
type: 'component',
|
||||||
component: new (props: Props) => Component<Props>,
|
component: typeof Component<Props, States>,
|
||||||
props: Props,
|
props: Props,
|
||||||
children: Blueprint[]
|
children: Blueprint[]
|
||||||
}
|
}
|
||||||
@@ -71,7 +58,4 @@ export type TextBlueprint = {
|
|||||||
type: 'text',
|
type: 'text',
|
||||||
props: { content: State<any> | any }
|
props: { content: State<any> | any }
|
||||||
}
|
}
|
||||||
export type Blueprint = ComponentBlueprint<any> | HTMLBlueprint | TextBlueprint;
|
export type Blueprint = ComponentBlueprint<any, any> | HTMLBlueprint | TextBlueprint;
|
||||||
|
|
||||||
type K = PropState<{ asd: number }>
|
|
||||||
type X = K['asd']
|
|
||||||
104
src/module/ComponentBase.ts
Normal file
104
src/module/ComponentBase.ts
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
import { State, type ValuesOfStateArray } from "./State";
|
||||||
|
|
||||||
|
export abstract class ComponentBase<Props extends PropsBase, States extends StatesBase> {
|
||||||
|
static propsToPropStates<Props extends PropsBase>(props: Props | PropStates<Props>) {
|
||||||
|
const propsState: Record<string, State<any>> = {};
|
||||||
|
Object.entries(props).forEach(([key, value]) => {
|
||||||
|
if (value instanceof State) {
|
||||||
|
propsState[key] = value;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
propsState[key] = new State(value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return propsState as PropStates<Props>;
|
||||||
|
}
|
||||||
|
|
||||||
|
states: States = {} as States;
|
||||||
|
propStates: PropStates<Props>;
|
||||||
|
children: ComponentBase<any, any>[] = [];
|
||||||
|
mounted: boolean = false;
|
||||||
|
subscriberSymbols: { state: State<any>, symbol: Symbol }[] = [];
|
||||||
|
abstract node: Node;
|
||||||
|
|
||||||
|
constructor(props: PropStates<Props> | Props) {
|
||||||
|
this.propStates = ComponentBase.propsToPropStates(props);
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
init(): void { }
|
||||||
|
protected abstract createNode(): Node;
|
||||||
|
|
||||||
|
mount(target: Node) {
|
||||||
|
if (this.mounted) {
|
||||||
|
throw new Error("This component is already mounted");
|
||||||
|
}
|
||||||
|
target.appendChild(this.node);
|
||||||
|
this.mounted = true;
|
||||||
|
this.onMount?.();
|
||||||
|
}
|
||||||
|
destroy() {
|
||||||
|
this.children.forEach((child) => child.destroy());
|
||||||
|
this.subscriberSymbols.forEach(({ state, symbol }) => {
|
||||||
|
state.unsubscribe(symbol);
|
||||||
|
});
|
||||||
|
this.onDestoy?.();
|
||||||
|
};
|
||||||
|
onMount(): void { };
|
||||||
|
onDestoy(): void { };
|
||||||
|
|
||||||
|
effect<const T extends State<any>[]>(callback: (...values: ValuesOfStateArray<T>) => void, states: T) {
|
||||||
|
states.forEach((state) => {
|
||||||
|
const symbol = state.subscribe(() => callback(...states.map(({ value }) => value) as ValuesOfStateArray<T>));
|
||||||
|
this.subscriberSymbols.push({ state, symbol });
|
||||||
|
})
|
||||||
|
}
|
||||||
|
derived<const T extends State<any>[], U>(callback: (...values: ValuesOfStateArray<T>) => U, states: T): State<U> {
|
||||||
|
const derivedState = new State<U>(undefined as U);
|
||||||
|
states.forEach((state) => {
|
||||||
|
const symbol = state.subscribe(() => {
|
||||||
|
const derivedValue = callback(...states.map(({ value }) => value) as ValuesOfStateArray<T>);
|
||||||
|
derivedState.set(derivedValue);
|
||||||
|
});
|
||||||
|
this.subscriberSymbols.push({ state, symbol });
|
||||||
|
});
|
||||||
|
return derivedState;
|
||||||
|
}
|
||||||
|
update<T extends (keyof Props | (string & {})), U>(
|
||||||
|
key: T,
|
||||||
|
value: T extends keyof Props ?
|
||||||
|
Props[T] extends State<U> ? U : Props[T] :
|
||||||
|
U
|
||||||
|
) {
|
||||||
|
let propState: State<any> = this.propStates[key];
|
||||||
|
if (!propState) {
|
||||||
|
propState = new State(value);
|
||||||
|
this.propStates[key] = propState;
|
||||||
|
}
|
||||||
|
propState.set(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type StatesBase = Record<string, State<any>>;
|
||||||
|
export type PropsBase = Record<string, any>;
|
||||||
|
export type PropStates<Props extends PropsBase> = {
|
||||||
|
[K in keyof Props]: State<Props[K]>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ComponentBlueprint<Props extends PropsBase, States extends StatesBase> = {
|
||||||
|
type: 'component',
|
||||||
|
component: new (props: Props, children?: Blueprint[]) => ComponentBase<Props, States>,
|
||||||
|
props: Props,
|
||||||
|
children: Blueprint[]
|
||||||
|
}
|
||||||
|
export type HTMLBlueprint = {
|
||||||
|
type: 'html',
|
||||||
|
tag: string;
|
||||||
|
props: Record<string, any>;
|
||||||
|
children: Blueprint[]
|
||||||
|
}
|
||||||
|
export type TextBlueprint = {
|
||||||
|
type: 'text',
|
||||||
|
props: { content: State<any> | any }
|
||||||
|
}
|
||||||
|
export type Blueprint = ComponentBlueprint<any, any> | HTMLBlueprint | TextBlueprint;
|
||||||
18
src/module/ComponentFactory.ts
Normal file
18
src/module/ComponentFactory.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import type { Blueprint } from "./Component";
|
||||||
|
import type { ComponentBase } from "./ComponentBase";
|
||||||
|
import { HTMLComponent } from "./HTMLComponent";
|
||||||
|
import { TextComponent } from "./TextComponent";
|
||||||
|
|
||||||
|
export namespace ComponentFactory {
|
||||||
|
export function blueprintToComponentInstance(blueprint: Blueprint): ComponentBase<any, any> {
|
||||||
|
if (blueprint.type === "text") {
|
||||||
|
return new TextComponent(blueprint.props);
|
||||||
|
}
|
||||||
|
else if (blueprint.type === "html") {
|
||||||
|
return new HTMLComponent(blueprint.tag, blueprint.props, blueprint.children);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return new blueprint.component(blueprint.props, blueprint.children);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,31 +1,49 @@
|
|||||||
import { Component, type Blueprint, type PropsBase } from "./Component";
|
import { type Blueprint, type PropsBase } from "./Component";
|
||||||
|
import { ComponentBase } from "./ComponentBase";
|
||||||
|
import { ComponentFactory } from "./ComponentFactory";
|
||||||
import { State } from "./State";
|
import { State } from "./State";
|
||||||
|
|
||||||
export class HTMLComponent<Props extends PropsBase> extends Component<Props> {
|
export class HTMLComponent<Props extends PropsBase> extends ComponentBase<Props, {}> {
|
||||||
state: Record<string, State<any>> = {};
|
state: Record<string, State<any>> = {};
|
||||||
tag: string;
|
tag: string;
|
||||||
node: HTMLElement;
|
declare node: HTMLElement;
|
||||||
subscriberSymbols: { state: State<any>, symbol: Symbol }[] = [];
|
|
||||||
|
|
||||||
constructor(tag: string, props: Props) {
|
constructor(tag: string, props: Props, children: Blueprint[] = []) {
|
||||||
super(props);
|
super(props);
|
||||||
this.tag = tag;
|
this.tag = tag;
|
||||||
this.node = document.createElement(tag);
|
this.node = this.createNode();
|
||||||
|
|
||||||
const blueprint = this.blueprint();
|
const blueprint = this.blueprint(children);
|
||||||
this.children = Component.blueprintsToComponentInstances(blueprint);
|
this.children = blueprint.map((bp) => ComponentFactory.blueprintToComponentInstance(bp));
|
||||||
this.children.forEach((child) => {
|
this.children.forEach((child) => {
|
||||||
this.node.appendChild(child.node);
|
child.mount(this.node);
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.entries(this.propStates).forEach(([key, prop]) => {
|
||||||
|
if (prop instanceof State) {
|
||||||
|
prop.subscribe((value) => {
|
||||||
|
this.setNodePropertyOrAttribute(key, value);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.setNodePropertyOrAttribute(key, prop);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
init() { }
|
private setNodePropertyOrAttribute(key: string, value: any) {
|
||||||
|
if (key in this.node) {
|
||||||
|
(this.node as any)[key] = value;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.node.setAttribute(key, value + "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createNode() {
|
||||||
|
return document.createElement(this.tag);
|
||||||
|
}
|
||||||
blueprint(children?: Blueprint[]): Blueprint[] {
|
blueprint(children?: Blueprint[]): Blueprint[] {
|
||||||
return children ?? [];
|
return children ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy(): void {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -29,4 +29,5 @@ export class State<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type SubscribeCallback<T> = (value: T) => any
|
type SubscribeCallback<T> = (value: T) => any
|
||||||
|
export type ValuesOfStateArray<T extends State<any>[]> = { [K in keyof T]: T[K] extends State<infer V> ? V : never };
|
||||||
@@ -1,23 +1,22 @@
|
|||||||
import { Component, type TextBlueprint } from "./Component";
|
import { ComponentBase } from "./ComponentBase";
|
||||||
|
|
||||||
export class TextComponent extends Component<{ content: any }> {
|
export class TextComponent extends ComponentBase<{ content: any }, {}> {
|
||||||
init() { };
|
init() { };
|
||||||
node: Text;
|
node: Text;
|
||||||
subscribeSymbol: Symbol;
|
|
||||||
|
|
||||||
constructor(props: { content: any }) {
|
constructor(props: { content: any }) {
|
||||||
super(props);
|
super(props);
|
||||||
this.node = document.createTextNode(this.propState.content.value + "");
|
this.node = this.createNode();
|
||||||
this.subscribeSymbol = this.propState.content.subscribe((value) => {
|
this.effect((value) => {
|
||||||
this.node.textContent = value = "";
|
this.node.textContent = value + "";
|
||||||
});
|
}, [this.propStates.content])
|
||||||
|
}
|
||||||
|
|
||||||
|
protected createNode(): Text {
|
||||||
|
return document.createTextNode(this.propStates.content.value + "");
|
||||||
}
|
}
|
||||||
|
|
||||||
blueprint() {
|
blueprint() {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy(): void {
|
|
||||||
this.propState.content.unsubscribe(this.subscribeSymbol);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
113
src/module/html.ts
Normal file
113
src/module/html.ts
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
import { type Blueprint } from "./Component";
|
||||||
|
import { State } from "./State";
|
||||||
|
|
||||||
|
const MARKER_PREFIX = "__GEMINI_MARKER_";
|
||||||
|
// DOMParser는 태그 이름을 소문자로 변환하므로 소문자 버전도 준비합니다.
|
||||||
|
const LOWER_MARKER_PREFIX = MARKER_PREFIX.toLowerCase();
|
||||||
|
const MARKER_REGEX = new RegExp(`${MARKER_PREFIX}(\\d+)__`, 'g');
|
||||||
|
const TAG_MARKER_REGEX = new RegExp(`${LOWER_MARKER_PREFIX}(\\d+)__`);
|
||||||
|
|
||||||
|
export function html(strings: TemplateStringsArray, ...values: any[]): Blueprint[] {
|
||||||
|
const htmlString = strings.reduce((acc, str, i) => {
|
||||||
|
return acc + str + (i < values.length ? `${MARKER_PREFIX}${i}__` : "");
|
||||||
|
}, "");
|
||||||
|
|
||||||
|
const parser = new DOMParser();
|
||||||
|
const doc = parser.parseFromString(htmlString, "text/html");
|
||||||
|
|
||||||
|
const blueprints: Blueprint[] = [];
|
||||||
|
Array.from(doc.body.childNodes).forEach(node => {
|
||||||
|
const result = nodeToBlueprint(node, values);
|
||||||
|
if (Array.isArray(result)) {
|
||||||
|
blueprints.push(...result);
|
||||||
|
} else if (result) {
|
||||||
|
blueprints.push(result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return blueprints;
|
||||||
|
}
|
||||||
|
|
||||||
|
function nodeToBlueprint(node: Node, values: any[]): Blueprint | Blueprint[] | null {
|
||||||
|
if (node.nodeType === Node.TEXT_NODE) {
|
||||||
|
const text = node.textContent || "";
|
||||||
|
const matches = [...text.matchAll(MARKER_REGEX)];
|
||||||
|
|
||||||
|
if (matches.length === 0) {
|
||||||
|
if (text.trim() === "") return null;
|
||||||
|
return { type: 'text', props: { content: text } };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matches.length === 1 && text.trim() === matches[0][0]) {
|
||||||
|
const index = parseInt(matches[0][1]);
|
||||||
|
const val = values[index];
|
||||||
|
if (Array.isArray(val)) return val;
|
||||||
|
if (val && typeof val === 'object' && 'type' in val) return val;
|
||||||
|
return { type: 'text', props: { content: val } };
|
||||||
|
}
|
||||||
|
|
||||||
|
let content: any = text;
|
||||||
|
matches.forEach(match => {
|
||||||
|
const index = parseInt(match[1]);
|
||||||
|
const val = values[index];
|
||||||
|
|
||||||
|
// 컴포넌트 클래스(함수)가 텍스트 노드에 포함된 경우 치환하지 않고 건너뜁니다.
|
||||||
|
if (typeof val === 'function' && !(val instanceof State)) {
|
||||||
|
content = content.replace(match[0], "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const replacement = val instanceof State ? val.value : val;
|
||||||
|
content = content.replace(match[0], replacement);
|
||||||
|
});
|
||||||
|
return { type: 'text', props: { content } };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.nodeType === Node.ELEMENT_NODE) {
|
||||||
|
const el = node as HTMLElement;
|
||||||
|
const tagName = el.tagName.toLowerCase();
|
||||||
|
|
||||||
|
// 태그 자체가 마커인지 확인 (컴포넌트 케이스)
|
||||||
|
const tagMatch = tagName.match(TAG_MARKER_REGEX);
|
||||||
|
|
||||||
|
const props: Record<string, any> = {};
|
||||||
|
Array.from(el.attributes).forEach(attr => {
|
||||||
|
const match = attr.value.match(new RegExp(`${MARKER_PREFIX}(\\d+)__`));
|
||||||
|
if (match) {
|
||||||
|
const index = parseInt(match[1]);
|
||||||
|
props[attr.name] = values[index];
|
||||||
|
} else {
|
||||||
|
props[attr.name] = attr.value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const children: Blueprint[] = [];
|
||||||
|
Array.from(el.childNodes).forEach(child => {
|
||||||
|
const result = nodeToBlueprint(child, values);
|
||||||
|
if (Array.isArray(result)) {
|
||||||
|
children.push(...result);
|
||||||
|
} else if (result) {
|
||||||
|
children.push(result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (tagMatch) {
|
||||||
|
const index = parseInt(tagMatch[1]);
|
||||||
|
const componentClass = values[index];
|
||||||
|
return {
|
||||||
|
type: 'component',
|
||||||
|
component: componentClass,
|
||||||
|
props,
|
||||||
|
children
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'html',
|
||||||
|
tag: tagName,
|
||||||
|
props,
|
||||||
|
children
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
"strict": true,
|
"strict": true,
|
||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
"noUnusedParameters": true,
|
"noUnusedParameters": true,
|
||||||
"erasableSyntaxOnly": true,
|
"erasableSyntaxOnly": false,
|
||||||
"noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
"noUncheckedSideEffectImports": true
|
"noUncheckedSideEffectImports": true
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user