Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions deps.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
export * as path from "https://deno.land/std@0.75.0/path/mod.ts";
export { v4 } from "https://deno.land/std@0.67.0/uuid/mod.ts";
export * as colors from "https://deno.land/std@0.61.0/fmt/colors.ts";
export {
assertEquals,
} from "https://deno.land/std@0.76.0/testing/asserts.ts";
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
export const name = "AppHello";
export default function() {
export const name = "component-a";
export default function(this: VMC) {
return (<>
<template>
{() => this.message}
<div> {() => this.message} </div>
<div> {() => this.array[1]} </div>
<for let={(number) => <div>{ number }</div>}/>
</template>
</>
)
}
export class ViewModel {
export class VMC {
message = "Hello World";
array = [1, 3];
static props(props) {
static props(this: VMC, props: { message: string }) {
this.message = props.message;
return props
}
}
31 changes: 19 additions & 12 deletions examples/hello-app/HelloApp.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,30 @@
import * as HelloApp2 from './HelloApp2.jsx';
import * as HelloApp3 from './HelloApp2.jsx';
import * as HelloApp4 from './HelloApp2.jsx';

export const name = "AppHello";
export default function(this: ViewModel): JSX.Element {
export const name = "hello-app";
export default function(this: VMC) {
// what to do about all the things referenced here
// maybe it's a helper zone for SSR
// but what about SPA
// keep in mind, this function is just to do the dom tree
return (<>
<template>
<div class="container">{() => this.message}</div>
<template test={() => this.message}>
<style>
{`.container { color: red; }`}
</style>
<component-a>
</component-a>
<div class="container" onclick={this.switch}>
{() => this.message}
</div>
</template>
<style>
{`.container { color: red; }`}
</style>
</>
)
}
export class ViewModel {
export class VMC {
public message = "Hello World";
public switch() {
this.message = 'test';
}
static props(this: VMC, props: { message: string }): boolean {
this.message = props.message;
return true;
}
}
20 changes: 0 additions & 20 deletions examples/hello-app/ListForeach.tsx

This file was deleted.

51 changes: 47 additions & 4 deletions mod.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,49 @@
import { ModuleGetter } from './src/classes/ModuleGetter.ts';
import ModuleResolver from './src/classes/ModuleResolver.ts';
import './src/functions/jsxFactory.ts';
import { ModuleGetterOptions } from './src/classes/ModuleGetter.ts';
import EonComponent from './src/classes/EonComponent.ts';
import DOMElementRegistry from './src/classes/DOMElementRegistry.ts';

const component = await ModuleGetter.buildModule({
entrypoint: './examples/hello-app/HelloApp.tsx',
});
console.warn(component);
export class Eon {
static async define(opts: ModuleGetterOptions): Promise<EonComponent> {
const module = await ModuleGetter.buildModule(opts);
const component = await ModuleResolver.resolve(module, opts);
return component
}
static async mount(component: EonComponent): Promise<boolean> {
const isSaved: boolean = await ModuleResolver.setComponentTemplate(component);
return isSaved;
}
/**
* start development of the application
* @param root {string} path to the root component
* @param registry {string} all the components used in the application
*/
static async dev(root: string, registry: string[]): Promise<EonComponent[]> {
async function render(path: string) {
const component = await Eon.define({
entrypoint: path,
});
// assign the root position to the root component
component.isRootComponent = root === path;
return component;
}
const rootComponent = await render(root);
const components = [rootComponent];
for await (const componentPath of registry) {
const component = await render(componentPath);
components.push(component);
}
// Eon.mount will set the template of the component
for await (const component of components) {
await Eon.mount(component);
}
ModuleGetter.typeCheckComponents();
return components;
}
}

const components = await Eon.dev('./examples/hello-app/HelloApp.tsx', [
'./examples/hello-app/AnotherComponent.tsx'
])
107 changes: 107 additions & 0 deletions src/classes/DOMElement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import EonComponent from './EonComponent.ts';
import { increment } from '../functions/increment.ts';
import DOMElementRegistry from './DOMElementRegistry.ts';
/**
* class that participate to the DOM Tree description
*/
export type DOMTreeElement = DOMElement;
interface DOMElementInterface {
/** the parent element of the element, undefined if the element is on top */
parent?: DOMTreeElement;
/** the children of the element */
children: DOMTreeElement[];
/** the name of the element */
name?: string;
/** the value of the element, defined if it's a textnode */
value?: unknown;
/** the type of the element
* 1 for all elements including the fragments
* 2 for attributes
* 3 for textnodes
* 11 for fragments
*/
nodeType?: 1 | 2 | 3 | 11;
/**
* the element is a template and on top of the dom
* or direct child of the top fragment
*/
isTemplate?: boolean;
/**
* the element is a style element and on top of the dom
* or direct child of the top fragment
*/
isStyle?: boolean;
/** the element is on top and it's a fragment element */
isFragment?: boolean;
/** the attributes of the element */
attributes?: { [k: string]: unknown };
/** related component */
component?: EonComponent;
}
export default class DOMElement implements DOMElementInterface {
parent: DOMElementInterface['parent'];
children: DOMElementInterface['children'];
name: DOMElementInterface['name'];
nodeType: DOMElementInterface['nodeType'];
value: DOMElementInterface['value'];
attributes: DOMElementInterface['attributes'];
component: DOMElementInterface['component'];
id?: number;
constructor(opts: DOMElementInterface) {
const {
nodeType,
parent,
name,
children,
value,
attributes,
component,
} = opts;
this.nodeType = nodeType;
this.parent = parent;
this.name = name;
this.children = children;
this.value = value;
this.attributes = attributes;
this.component = component;
this.id = increment();
DOMElementRegistry.subscribe(this.uuid, this);
}
get uuid(): string {
const idType = this.isBoundTextnode ? 't'
: this.isTemplate ? 'tmp'
: this.isComponent ? 'c'
: 'n';
return `${idType}${this.id}`;
}
get isBoundTextnode(): boolean {
return this.nodeType === 3 && typeof this.value === 'function';
}
get isTemplate(): boolean {
return this.nodeType === 1 && this.name === 'template' && (!this.parent || this.parent.isFragment);
}
get isStyle(): boolean {
return this.nodeType === 1 && this.name === 'style' && (!this.parent || this.parent.isFragment);
}
get isFragment(): boolean {
return this.nodeType === 11 && this.name === undefined && !this.parent;
}
get isComponent(): boolean {
return this.nodeType === 1 && !!this.component;
}
/** returns the component that is using this element */
get parentComponent(): EonComponent | undefined {
let parent = this.parent, domelement: DOMElement | undefined;
while (parent) {
domelement = this.parent;
parent = this.parent?.parent;
}
return (domelement || this).component;
}
setParent(parent: DOMTreeElement) {
this.parent = parent;
}
setChild(child: DOMTreeElement) {
this.children.push(child);
}
}
21 changes: 21 additions & 0 deletions src/classes/DOMElementRegistry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import DOMElement from './DOMElement.ts';
/**
* class to save all the element used
*/
export default class DOMElementRegistry {
private static registry: Map<string, DOMElement> = new Map();
public static subscribe(uuid: string, domelement: DOMElement) {
if (!this.registry.has(uuid)) {
this.registry.set(uuid, domelement);
}
}
static get collection(): DOMElement[] {
return Array.from(this.registry.entries()).map(([key, domelement]) => domelement);
}
static getElementsByNodeType(nodeType: number) {
return this.collection.filter((domelement) => domelement && domelement.nodeType === nodeType);
}
static getElementsOfComponent(uuid: string): DOMElement[] {
return this.collection.filter((domelement) => domelement && domelement.parentComponent && domelement.parentComponent.uuid === uuid);
}
}
48 changes: 48 additions & 0 deletions src/classes/EonComponent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import DOMElement from './DOMElement.ts';
import type { EonModule } from './ModuleGetter.ts';
import EonComponentRegistry from './EonComponentRegistry.ts';

export interface EonComponentInterface {
/** uuid */
uuid?: string;
/** name */
name?: string;
/** path of the component */
file?: string;
/** the DOM tree of the component */
template?: DOMElement;
/** component's VMC */
VMC?: EonModule['VMC'];
/** returns the DOM tree of the component */
templateFactory: EonModule['default'];
/** if the component is the first component */
isRootComponent?: boolean;
}
export default class EonComponent implements EonComponentInterface {
uuid: EonComponentInterface['uuid'];
name: EonComponentInterface['name'];
file: EonComponentInterface['file'];
template: EonComponentInterface['template'];
VMC: EonComponentInterface['VMC'];
isRootComponent: EonComponentInterface['isRootComponent'] = false;
templateFactory: EonComponentInterface['templateFactory'];
constructor(opts: EonComponentInterface) {
const {
file,
uuid,
template,
VMC,
templateFactory,
name
} = opts;
this.file = file;
this.uuid = uuid;
this.template = template;
this.VMC = VMC;
this.name = name;
this.templateFactory = templateFactory;
if (this.uuid) {
EonComponentRegistry.subscribe(this.uuid, this);
}
}
}
23 changes: 23 additions & 0 deletions src/classes/EonComponentRegistry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import EonComponent from './EonComponent.ts';

export default abstract class EonComponentRegistry {
private static readonly registry: Map<string, EonComponent> = new Map();
static subscribe(uuid: string, component: EonComponent): boolean {
this.registry.set(uuid, component);
return true;
}
static getComponent(uuid: string): EonComponent | undefined {
return this.registry.get(uuid);
}
static getItemByName(name: string): EonComponent | undefined {
const entries = Array.from(this.registry.entries());
const found = entries.find(([key, component]) => component.name === name);
if (found) {
const [, component] = found;
return component;
}
}
static get collection() {
return Array.from(this.registry.entries())
}
}
Loading