From 3a231cd37c0920a205c257c383331529dbd093b3 Mon Sep 17 00:00:00 2001 From: benStre Date: Tue, 27 Feb 2024 17:03:21 +0100 Subject: [PATCH 01/56] migrate to now decorators --- src/base/decorators.ts | 245 +++++++++++++---------------------- src/base/uix-datex-module.ts | 10 +- src/components/Component.ts | 24 ++-- src/html/template.ts | 55 ++++++-- 4 files changed, 146 insertions(+), 188 deletions(-) diff --git a/src/base/decorators.ts b/src/base/decorators.ts index 4882d03b4..58802b3a0 100644 --- a/src/base/decorators.ts +++ b/src/base/decorators.ts @@ -1,53 +1,50 @@ -import { Datex } from "datex-core-legacy"; -import { context_kind, context_meta_getter, context_meta_setter, context_name, handleDecoratorArgs, METADATA } from "datex-core-legacy/datex_all.ts"; -import { logger } from "../utils/global-values.ts"; +import { Datex } from "datex-core-legacy/mod.ts"; +import { handleClassDecoratorWithArgs, handleClassFieldDecoratorWithOptionalArgs } from "datex-core-legacy/js_adapter/decorators.ts"; import { getCloneKeys, Component } from "../components/Component.ts"; import { getCallerFile } from "datex-core-legacy/utils/caller_metadata.ts"; import { domContext, domUtils } from "../app/dom-context.ts"; import { getTransformWrapper } from "../uix-dom/datex-bindings/transform-wrapper.ts"; import { client_type } from "datex-core-legacy/utils/constants.ts"; - - /** * @defaultOptions to define component default options */ -export function defaultOptions (default_options:Partial>):any -export function defaultOptions():any -export function defaultOptions(target: Function & { prototype: C }):any -export function defaultOptions(...args:any[]):any { +export function defaultOptions>(defaultOptions:Partial>): (value: typeof Component, context: ClassDecoratorContext)=>void { const url = getCallerFile(); // TODO: called even if _init_module set - return handleDecoratorArgs(args, (...args)=>_defaultOptions(url, ...args)); + return handleClassDecoratorWithArgs([defaultOptions], ([defaultOptions], value, context) => { + console.log("@defaultOptions",defaultOptions, value,context) + return _defaultOptions(url, value as unknown as ComponentClass, defaultOptions) + }) } +type ComponentClass = + typeof Component & + (new (...args: unknown[]) => unknown) & // fix abstract class + {_init_module: string, _module: string, _use_resources: boolean} // access protected properties + const transformWrapper = getTransformWrapper(domUtils, domContext) -function _defaultOptions(url:string, component_class:typeof HTMLElement, name:context_name, kind:context_kind, is_static:boolean, is_private:boolean, setMetadata:context_meta_setter, getMetadata:context_meta_getter, params:[any] = []) { - url = Object.hasOwn(component_class, "_init_module") ? - component_class._init_module : +function _defaultOptions>(url:string, componentClass: ComponentClass, defaultOptions?:Partial>) { + url = Object.hasOwn(componentClass, "_init_module") ? + componentClass._init_module : url; if (!url) { console.log(new Error().stack) - throw new Error("Could not get the location of the UIX component '"+component_class.name+"'. This should not happen"); + throw new Error("Could not get the location of the UIX component '"+componentClass.name+"'. This should not happen"); } - // deprecated message - // if (component_class.prototype instanceof Components.Base) { - // logger.warn("UIX.Components.Base is deprecated - please use Component") - // } - - if (component_class.prototype instanceof Component) { + if (componentClass.prototype instanceof Component) { // set auto module (url from stack trace), not if module === null => resources was disabled with @NoResources - if (url && component_class._module !== null) component_class._module = url; + if (url && componentClass._module !== null) componentClass._module = url; // default value of _use_resources is true (independent of parent class), if it was not overriden for this Component with @NoResources - if (!Object.hasOwn(component_class, '_use_resources')) component_class._use_resources = true; + if (!Object.hasOwn(componentClass, '_use_resources')) componentClass._use_resources = true; // preload css files - component_class.preloadStylesheets?.(); + componentClass.preloadStylesheets?.(); - const name = String(component_class.name).replace(/1$/,'').split(/([A-Z][a-z]+)/).filter(t=>!!t).map(t=>t.toLowerCase()).join("-"); // convert from CamelCase to snake-case + const name = String(componentClass.name).replace(/1$/,'').split(/([A-Z][a-z]+)/).filter(t=>!!t).map(t=>t.toLowerCase()).join("-"); // convert from CamelCase to snake-case const datex_type = Datex.Type.get("std", "uix", name); const options_datex_type = Datex.Type.get("uixopt", name); @@ -58,9 +55,9 @@ function _defaultOptions(url:string, component_class:typeof HTMLElement, name:co } // create template class for component - const new_class = Datex.createTemplateClass(component_class, datex_type, true); + const new_class = Datex.createTemplateClass(componentClass, datex_type, true) as ComponentClass; - component_class[Datex.DX_TYPE] = datex_type; + componentClass[Datex.DX_TYPE] = datex_type; const html_interface = Datex.Type.get('html').interface_config!; datex_type.interface_config.cast_no_tuple = html_interface.cast_no_tuple; // handle casts from object @@ -70,7 +67,7 @@ function _defaultOptions(url:string, component_class:typeof HTMLElement, name:co datex_type.interface_config.serialize = (value) => { // serialize html part (style, attr, content) - const html_serialized = > html_interface.serialize!(value); + const html_serialized = > html_interface.serialize!(value); // add additional properties (same as in Datex.Runtime.serializeValue) const pointer = Datex.Pointer.getByValue(value) @@ -85,22 +82,15 @@ function _defaultOptions(url:string, component_class:typeof HTMLElement, name:co // component default options new_class.DEFAULT_OPTIONS = Object.create(Object.getPrototypeOf(new_class).DEFAULT_OPTIONS ?? {}); - if (!params[0]) params[0] = {}; + if (!defaultOptions) defaultOptions = {}; // set default options + title // ! title is overriden, even if a parent class has specified another default title // if (!(params[0]).title)(params[0]).title = component_class.name; - Object.assign(new_class.DEFAULT_OPTIONS, params[0]) + Object.assign(new_class.DEFAULT_OPTIONS, defaultOptions) // find non-primitive values in default options (must be copied) new_class.CLONE_OPTION_KEYS = getCloneKeys(new_class.DEFAULT_OPTIONS); - // initial constraints - if (Object.getPrototypeOf(new_class).INITIAL_CONSTRAINTS) new_class.INITIAL_CONSTRAINTS = {...Object.getPrototypeOf(new_class).INITIAL_CONSTRAINTS}; - if (params[1]) { - if (!new_class.INITIAL_CONSTRAINTS) new_class.INITIAL_CONSTRAINTS = {} - Object.assign(new_class.INITIAL_CONSTRAINTS, params[1]); - } - // create DATEX type for options (with prototype) options_datex_type.setJSInterface({ prototype: new_class.DEFAULT_OPTIONS, @@ -109,8 +99,8 @@ function _defaultOptions(url:string, component_class:typeof HTMLElement, name:co is_normal_object: true, }) // define custom DOM element after everything is initialized - domContext.customElements.define("uix-" + name, component_class) - return new_class //element_class + domContext.customElements.define("uix-" + name, componentClass as typeof HTMLElement) + return new_class } else throw new Error("Invalid @defaultOptions - class must extend or Component") } @@ -118,21 +108,15 @@ function _defaultOptions(url:string, component_class:typeof HTMLElement, name:co /** * @NoResources: disable external resource loading (.css, .dx), must be located below the @defaultOptions decorator */ -export function NoResources(target: Function & { prototype: C }):any -export function NoResources(...args:any[]):any { - return handleDecoratorArgs(args, _NoResources); -} - -function _NoResources(component_class:typeof HTMLElement, name:context_name, kind:context_kind, is_static:boolean, is_private:boolean, setMetadata:context_meta_setter, getMetadata:context_meta_getter) { +export function NoResources(value: typeof Component, context: ClassDecoratorContext) { // was called after @Component - if (Object.hasOwn(component_class, '_module')) { - throw new Error("Please put the @NoResources decorator for the component '"+name+"' below the @defaultOptions decorator"); + if (Object.hasOwn(value, '_module')) { + throw new Error("Please put the @NoResources decorator for the component '"+context.name+"' below the @defaultOptions decorator"); } - component_class._use_resources = false; + (value as ComponentClass)._use_resources = false; } - export const ID_PROPS: unique symbol = Symbol("ID_PROPS"); export const CONTENT_PROPS: unique symbol = Symbol("CONTENT_PROPS"); export const CHILD_PROPS: unique symbol = Symbol("CHILD_PROPS"); @@ -141,125 +125,76 @@ export const IMPORT_PROPS: unique symbol = Symbol("IMPORT_PROPS"); export const STANDALONE_PROPS: unique symbol = Symbol("STANDALONE_PROPS"); export const ORIGIN_PROPS: unique symbol = Symbol("ORIGIN_PROPS"); -/** @id to automatically assign a element id to a component property */ -export function id(id?:string):any -export function id(target: any, name?: string, method?:any):any -export function id(...args:any[]) { - return handleDecoratorArgs(args, _id); +/** \@id to automatically assign a element id to a component property */ +export function id(id?:string): (value: undefined, context: ClassFieldDecoratorContext) => void +export function id(value: undefined, context: ClassFieldDecoratorContext): void +export function id(id:string|undefined, context?: ClassFieldDecoratorContext) { + return handleClassFieldDecoratorWithOptionalArgs([id], context, ([id], context) => { + console.log("set @id", id??context.name) + context.metadata[ID_PROPS] = id ?? context.name; + }) } -function _id(element_class:HTMLElement, name:context_name, kind:context_kind, is_static:boolean, is_private:boolean, setMetadata:context_meta_setter, getMetadata:context_meta_getter, params:[string?] = []) { - if (kind != "field") { - logger.error("@UIX.id has to be used on a field"); - return; - } - - setMetadata(ID_PROPS, params[0]??name); -} -/** @content to automatically assign a element id to a component property and add element to component content (#content) */ -export function content(id?:string):any -export function content(target: any, name?: string, method?:any):any -export function content(...args:any[]) { - return handleDecoratorArgs(args, _content); +/** \@content to automatically assign a element id to a component property and add element to component content (#content) */ +export function content(id?:string): (value: undefined, context: ClassFieldDecoratorContext) => void +export function content(value: undefined, context: ClassFieldDecoratorContext): void +export function content(id:string|undefined, context?: ClassFieldDecoratorContext) { + return handleClassFieldDecoratorWithOptionalArgs([id], context, ([id], context) => { + console.log("set @content", id??context.name) + context.metadata[CONTENT_PROPS] = id ?? context.name; + }) } -function _content(element_class:typeof HTMLElement, name:context_name, kind:context_kind, is_static:boolean, is_private:boolean, setMetadata:context_meta_setter, getMetadata:context_meta_getter, params:[string?] = []) { - if (kind != "field") { - logger.error("@UIX.content has to be used on a field"); - return; - } - - setMetadata(CONTENT_PROPS, params[0]??name); -} /** @layout to automatically assign a element id to a component property and add element to component content container layout (#layout) */ -export function layout(id?:string):any -export function layout(target: any, name?: string, method?:any):any -export function layout(...args:any[]) { - return handleDecoratorArgs(args, _layout); -} - -function _layout(element_class:typeof HTMLElement, name:context_name, kind:context_kind, is_static:boolean, is_private:boolean, setMetadata:context_meta_setter, getMetadata:context_meta_getter, params:[string?] = []) { - if (kind != "field") { - logger.error("@UIX.layout has to be used on a field"); - return; - } - - setMetadata(LAYOUT_PROPS, params[0]??name); -} - -/** @child to automatically assign a element id to a component property and add element as a component child */ -export function child(id?:string):any -export function child(target: any, name?: string, method?:any):any -export function child(...args:any[]) { - return handleDecoratorArgs(args, _child); -} - -function _child(element_class:typeof HTMLElement, name:context_name, kind:context_kind, is_static:boolean, is_private:boolean, setMetadata:context_meta_setter, getMetadata:context_meta_getter, params:[string?] = []) { - if (kind != "field") { - logger.error("@UIX.child has to be used on a field"); - return; - } - - setMetadata(CHILD_PROPS, params[0]??name); -} - - -/** @UIX.use to bind static properties */ -export function include(resource?:string, export_name?:string):any -export function include(target: any, name?: string, method?:any):any -export function include(...args:any[]) { - return handleDecoratorArgs(args, _include); -} - -/** - * @depreacted use @include - */ -export const use = include; - -function _include(element_class:typeof HTMLElement, name:context_name, kind:context_kind, is_static:boolean, is_private:boolean, setMetadata:context_meta_setter, getMetadata:context_meta_getter, params:[string?, string?] = []) { - - if (kind != "field" && kind != "method") { - logger.error("@include has to be used on a field or method"); - return; - } - - setMetadata(IMPORT_PROPS, [params[0], params[1]??name]); +export function layout(id?:string): (value: undefined, context: ClassFieldDecoratorContext) => void +export function layout(value: undefined, context: ClassFieldDecoratorContext): void +export function layout(id:string|undefined, context?: ClassFieldDecoratorContext) { + return handleClassFieldDecoratorWithOptionalArgs([id], context, ([id], context) => { + console.log("set @layout", id??context.name) + context.metadata[LAYOUT_PROPS] = id ?? context.name; + }) +} + + +/** \@child to automatically assign a element id to a component property and add element as a component child */ +export function child(id?:string): (value: undefined, context: ClassFieldDecoratorContext) => void +export function child(value: undefined, context: ClassFieldDecoratorContext): void +export function child(id:string|undefined, context?: ClassFieldDecoratorContext) { + return handleClassFieldDecoratorWithOptionalArgs([id], context, ([id], context) => { + console.log("set @child", id??context.name) + context.metadata[CHILD_PROPS] = id ?? context.name; + }) +} + +/** \@include to bind static properties */ +export function include(resource?:string, export_name?:string): (value: undefined, context: ClassFieldDecoratorContext) => void +export function include(value: undefined, context: ClassFieldDecoratorContext): void +export function include(value: undefined|string, context?: ClassFieldDecoratorContext|string) { + return handleClassFieldDecoratorWithOptionalArgs([value, context as string], context as ClassFieldDecoratorContext, + ([resource, export_name], context) => { + console.log("set @include", resource, export_name, context.name) + context.metadata[IMPORT_PROPS] = [resource, export_name??context.name]; + } + ) } /** \@frontend decorator to declare methods that always run on the frontend */ -export function frontend(target: any, name?: string, method?:any):any -export function frontend(...args:any[]) { - return handleDecoratorArgs(args, _frontend); -} - - - -function _frontend(element_class:typeof HTMLElement, name:context_name, kind:context_kind, is_static:boolean, is_private:boolean, setMetadata:context_meta_setter, getMetadata:context_meta_getter) { - if (is_static) { - logger.error("@frontend cannot be used on static class fields"); - return; - } - - setMetadata(STANDALONE_PROPS, name); +export function frontend(_value: undefined, context: ClassFieldDecoratorContext|ClassMethodDecoratorContext) { + context.metadata[STANDALONE_PROPS] = context.name; } /** @bindOrigin to declare methods that work in a standlone context, but are executed in the original context */ -export function bindOrigin(options:{datex:boolean}):any -export function bindOrigin(target: any, propertyKey: string, descriptor: PropertyDescriptor):any -export function bindOrigin(_invalid_param_0_: HTMLElement, _invalid_param_1_?: string, _invalid_param_2_?: PropertyDescriptor):any - -export function bindOrigin(...args:any[]) { - return handleDecoratorArgs(args, _bindOrigin); -} - -function _bindOrigin(val:(...args:any)=>any, name:context_name, kind:context_kind, is_static:boolean, is_private:boolean, setMetadata:context_meta_setter, getMetadata:context_meta_getter, params:[{datex:boolean}?] = []) { - if (is_static) { - logger.error("@UIX.bindOrigin cannot be used on static class fields"); - return; - } - setMetadata(STANDALONE_PROPS, name); - setMetadata(ORIGIN_PROPS, params[0]??{datex:false}); +export function bindOrigin(options:{datex:boolean}): (value: undefined, context: ClassFieldDecoratorContext|ClassMethodDecoratorContext) => void +export function bindOrigin(value: undefined, context: ClassFieldDecoratorContext|ClassMethodDecoratorContext): void +export function bindOrigin(options:{datex:boolean}|undefined, context?: ClassFieldDecoratorContext|ClassMethodDecoratorContext) { + return handleClassFieldDecoratorWithOptionalArgs([options], context as ClassFieldDecoratorContext, + ([options], context:ClassFieldDecoratorContext|ClassMethodDecoratorContext) => { + console.log("set @bindOrigin", options, context.name) + context.metadata[STANDALONE_PROPS] = context.name; + context.metadata[ORIGIN_PROPS] = options ?? {datex:false}; + } + ) } \ No newline at end of file diff --git a/src/base/uix-datex-module.ts b/src/base/uix-datex-module.ts index dd1016754..108abb534 100644 --- a/src/base/uix-datex-module.ts +++ b/src/base/uix-datex-module.ts @@ -1,5 +1,5 @@ -import { scope, expose } from "datex-core-legacy"; import { client_type } from "datex-core-legacy/utils/constants.ts"; +import { datex } from "datex-core-legacy/mod.ts"; const stage = client_type == "deno" ? (await import("../app/args.ts#lazy")).stage : "TODO!"; @@ -11,8 +11,8 @@ const stageTransformFunction = await datex` ); ` -@scope("uix") class UIXDatexModule { - @expose static LANG = "en"; - @expose static stage = stageTransformFunction - @expose static currentStage = stage +@endpoint class uix { + @property static LANG = "en"; + @property static stage = stageTransformFunction + @property static currentStage = stage } \ No newline at end of file diff --git a/src/components/Component.ts b/src/components/Component.ts index 9d9cc38fa..8c84e97ae 100644 --- a/src/components/Component.ts +++ b/src/components/Component.ts @@ -1,5 +1,5 @@ // deno-lint-ignore-file no-async-promise-executor -import { constructor, Datex, property, replicator, template, get} from "datex-core-legacy" +import { Datex, property, get} from "datex-core-legacy" import { logger } from "../utils/global-values.ts" import { Class, Logger, METADATA, ValueError } from "datex-core-legacy/datex_all.ts" import { CHILD_PROPS, CONTENT_PROPS, ID_PROPS, IMPORT_PROPS, LAYOUT_PROPS, ORIGIN_PROPS, STANDALONE_PROPS } from "../base/decorators.ts"; @@ -29,15 +29,9 @@ export type standaloneContentPropertyData = {type:'id'|'content'|'layout'|'child export type standalonePropertyData = {type:'prop'} export type standaloneProperties = Record; -// deno-lint-ignore no-namespace -export namespace Component { - export interface Options { - title?: string - } -} // @template("uix:component") -export abstract class Component extends domContext.HTMLElement implements RouteManager { +export abstract class Component = Record, ChildElement = JSX.singleOrMultipleChildren> extends domContext.HTMLElement implements RouteManager { /************************************ STATIC ***************************************/ @@ -49,7 +43,7 @@ export abstract class Component // list of all default option keys that need to be cloned when options are initialized (non-primitive options) // guessing module stylesheets, get added to normal stylesheets array after successful fetch @@ -680,7 +674,7 @@ export abstract class Component): Promise { + async construct(options?:Datex.DatexObjectInit): Promise { // options already handled in constructor // handle default component options (class, ...) @@ -706,7 +700,7 @@ export abstract class Componentthis.constructor).init(); @@ -775,7 +769,7 @@ export abstract class Componentname] = > this.attributes[i].value; + options[name] = > this.attributes[i].value; } } } @@ -1083,7 +1077,7 @@ export abstract class Component>delegate).onRoute?.(route.route[0]??"", initial_route); + const child = await (>delegate).onRoute?.(route.route[0]??"", initial_route); if (child == false) return []; // route not valid else if (typeof (child)?.focus == "function") { @@ -1135,10 +1129,10 @@ export abstract class Component void) { + public observeOption(key:keyof O, handler: (value: unknown, key?: unknown, type?: Datex.Ref.UPDATE_TYPE) => void) { Datex.Ref.observeAndInit(this.options.$$[key as keyof typeof this.options.$$], handler, this); } - public observeOptions(keys:(keyof O & Component.Options)[], handler: (value: unknown, key?: unknown, type?: Datex.Ref.UPDATE_TYPE) => void) { + public observeOptions(keys:(keyof O)[], handler: (value: unknown, key?: unknown, type?: Datex.Ref.UPDATE_TYPE) => void) { for (const key of keys) this.observeOption(key, handler); } diff --git a/src/html/template.ts b/src/html/template.ts index 5b7382dde..53c90dfd2 100644 --- a/src/html/template.ts +++ b/src/html/template.ts @@ -6,6 +6,7 @@ import { DOMUtils } from "../uix-dom/datex-bindings/dom-utils.ts"; import { domContext, domUtils } from "../app/dom-context.ts"; import type { Element, Node, HTMLElement, HTMLTemplateElement } from "../uix-dom/dom/mod.ts"; import { defaultOptions } from "../base/decorators.ts"; +import type { Class } from "datex-core-legacy/utils/global_types.ts"; /** * cloneNode(true), but also clones shadow roots. @@ -72,15 +73,29 @@ type Props, Children, handleAllProps = tr ) : unknown ) + type ObjectWithCollapsedValues> = { [K in keyof O]: O[K] extends Datex.RefOrValue ? T : O[K] } -export type jsxInputGenerator, Children, handleAllProps = true, optionalChildren = true, Context = unknown> = +export type jsxInputGenerator< + Return, + Options extends Record, + Children, + handleAllProps = true, + optionalChildren = true, + Context extends HTMLElement = HTMLElement +> = ( this: Context, - props: Props, - propsValues: ObjectWithCollapsedValues> + props: Props< + // inferred options: + Context extends {options:unknown} ? Omit<(Context)['options'], '$'|'$$'> : Options + , Children, handleAllProps, optionalChildren>, + propsValues: ObjectWithCollapsedValues : Options + , Children, handleAllProps, optionalChildren>> ) => Return; @@ -90,7 +105,7 @@ export type jsxInputGenerator, Ch * Custom Attributes can be handled in the generator * @example * ```tsx - * const CustomComponent = UIX.template<{color:string}>(({color}) =>
) + * const CustomComponent = template<{color:string}>(({color}) =>
) * // create: * const comp = * ``` @@ -99,7 +114,7 @@ export type jsxInputGenerator, Ch * Children are appended to the element inside the root: * @example * ```tsx - * const CustomComponent2 = UIX.template<{color:string}>(({color}) => + * const CustomComponent2 = template<{color:string}>(({color}) => *
* Custom content before children * @@ -115,19 +130,27 @@ export type jsxInputGenerator, Ch * ``` * @param elementGenerator */ -export function template = {}, Children = JSX.childrenOrChildrenPromise|JSX.childrenOrChildrenPromise[], Context = unknown>(elementGenerator:jsxInputGenerator, Options, never, false, false, Context>):jsxInputGenerator, Options, Children>&((cl:typeof HTMLElement)=>any) +export function template< + Options extends Record = Record, + Children = JSX.childrenOrChildrenPromise|JSX.childrenOrChildrenPromise[], + Context extends typeof HTMLElement = typeof HTMLElement +> ( + elementGenerator: jsxInputGenerator, Options, never, false, false, InstanceType> +): + jsxInputGenerator, Options, Children>&((cl: Context, context: ClassDecoratorContext)=>any) + /** * Define an HTML template that can be used as an anonymous JSX component. * Default HTML Attributes defined in JSX are also set for the root element. * @example * ```tsx - * const CustomComponent = UIX.template(
) + * const CustomComponent = template(
) * // create: * const comp = * ``` * @param elementGenerator */ -export function template = {}, Children = JSX.childrenOrChildrenPromise|JSX.childrenOrChildrenPromise[]>(element:JSX.Element):jsxInputGenerator&((cl:typeof HTMLElement)=>any) +export function template = {}, Children = JSX.childrenOrChildrenPromise|JSX.childrenOrChildrenPromise[]>(element:JSX.Element):jsxInputGenerator&((cl: Class, context: ClassDecoratorContext)=>any) /** * Empty template for component @@ -138,7 +161,7 @@ export function template = {}, Children = JS * ``` * @param elementGenerator */ -export function template():jsxInputGenerator, never>&((cl:typeof HTMLElement)=>any) +export function template():jsxInputGenerator, never>&((cl: Class, context: ClassDecoratorContext)=>any) export function template(templateOrGenerator?:JSX.Element|jsxInputGenerator, any, any, any>) { @@ -204,15 +227,15 @@ export function template(templateOrGenerator?:JSX.Element|jsxInputGenerator(({color, style, id, children}) =>

Header

{...children}
) + * const CustomComponent = blankTemplate<{color:string}>(({color, style, id, children}) =>

Header

{...children}
) * // create: * const comp = ( * @@ -223,7 +246,13 @@ export function template(templateOrGenerator?:JSX.Element|jsxInputGenerator, Children = JSX.childrenOrChildrenPromise|JSX.childrenOrChildrenPromise[]>(elementGenerator:jsxInputGenerator, Options, Children extends any[] ? Children : Children[], true, false>):jsxInputGenerator, Options, Children>&((cl:typeof HTMLElement)=>any) { +export function blankTemplate< + Options extends Record, + Children = JSX.childrenOrChildrenPromise|JSX.childrenOrChildrenPromise[], + Context extends typeof HTMLElement = typeof HTMLElement +> ( + elementGenerator: jsxInputGenerator, Options, Children extends any[] ? Children : Children[], true, false, InstanceType> +): jsxInputGenerator, Options, Children>&((cl: Context, context: ClassDecoratorContext)=>any) { const module = getCallerFile(); function generator(propsOrClass:any) { From 968ec31310af93a7345ac4591242f2edd4a65470 Mon Sep 17 00:00:00 2001 From: benStre Date: Tue, 27 Feb 2024 21:23:53 +0100 Subject: [PATCH 02/56] fix component decorators --- src/base/decorators.ts | 29 ++++++++++++----------------- src/base/uix-datex-module.ts | 24 ++++++++++++------------ src/html/style.ts | 10 ++++++++-- src/html/template.ts | 21 ++++++++++++++------- src/server/network-interface.ts | 6 +++--- src/server/transpiler.ts | 7 +++---- 6 files changed, 52 insertions(+), 45 deletions(-) diff --git a/src/base/decorators.ts b/src/base/decorators.ts index 58802b3a0..91e843ab6 100644 --- a/src/base/decorators.ts +++ b/src/base/decorators.ts @@ -5,6 +5,7 @@ import { getCallerFile } from "datex-core-legacy/utils/caller_metadata.ts"; import { domContext, domUtils } from "../app/dom-context.ts"; import { getTransformWrapper } from "../uix-dom/datex-bindings/transform-wrapper.ts"; import { client_type } from "datex-core-legacy/utils/constants.ts"; +import { Decorators } from "datex-core-legacy/datex_all.ts"; /** * @defaultOptions to define component default options @@ -13,7 +14,7 @@ export function defaultOptions>(defaultOptions const url = getCallerFile(); // TODO: called even if _init_module set return handleClassDecoratorWithArgs([defaultOptions], ([defaultOptions], value, context) => { console.log("@defaultOptions",defaultOptions, value,context) - return _defaultOptions(url, value as unknown as ComponentClass, defaultOptions) + return initDefaultOptions(url, value as unknown as ComponentClass, defaultOptions) }) } @@ -24,7 +25,7 @@ type ComponentClass = const transformWrapper = getTransformWrapper(domUtils, domContext) -function _defaultOptions>(url:string, componentClass: ComponentClass, defaultOptions?:Partial>) { +export function initDefaultOptions>(url:string, componentClass: ComponentClass, defaultOptions?:Partial>) { url = Object.hasOwn(componentClass, "_init_module") ? componentClass._init_module : url; @@ -102,7 +103,7 @@ function _defaultOptions>(url:string, componen domContext.customElements.define("uix-" + name, componentClass as typeof HTMLElement) return new_class } - else throw new Error("Invalid @defaultOptions - class must extend or Component") + else throw new Error("Invalid @defaultOptions - class must extend Component") } /** @@ -130,8 +131,7 @@ export function id(id?:string): (value: undefined, context: ClassFieldDecoratorC export function id(value: undefined, context: ClassFieldDecoratorContext): void export function id(id:string|undefined, context?: ClassFieldDecoratorContext) { return handleClassFieldDecoratorWithOptionalArgs([id], context, ([id], context) => { - console.log("set @id", id??context.name) - context.metadata[ID_PROPS] = id ?? context.name; + Decorators.setMetadata(context, ID_PROPS, id ?? context.name); }) } @@ -141,8 +141,7 @@ export function content(id?:string): (value: undefined, context: ClassFieldDecor export function content(value: undefined, context: ClassFieldDecoratorContext): void export function content(id:string|undefined, context?: ClassFieldDecoratorContext) { return handleClassFieldDecoratorWithOptionalArgs([id], context, ([id], context) => { - console.log("set @content", id??context.name) - context.metadata[CONTENT_PROPS] = id ?? context.name; + Decorators.setMetadata(context, CONTENT_PROPS, id ?? context.name); }) } @@ -152,8 +151,7 @@ export function layout(id?:string): (value: undefined, context: ClassFieldDecora export function layout(value: undefined, context: ClassFieldDecoratorContext): void export function layout(id:string|undefined, context?: ClassFieldDecoratorContext) { return handleClassFieldDecoratorWithOptionalArgs([id], context, ([id], context) => { - console.log("set @layout", id??context.name) - context.metadata[LAYOUT_PROPS] = id ?? context.name; + Decorators.setMetadata(context, LAYOUT_PROPS, id ?? context.name); }) } @@ -163,8 +161,7 @@ export function child(id?:string): (value: undefined, context: ClassFieldDecorat export function child(value: undefined, context: ClassFieldDecoratorContext): void export function child(id:string|undefined, context?: ClassFieldDecoratorContext) { return handleClassFieldDecoratorWithOptionalArgs([id], context, ([id], context) => { - console.log("set @child", id??context.name) - context.metadata[CHILD_PROPS] = id ?? context.name; + Decorators.setMetadata(context, CHILD_PROPS, id ?? context.name); }) } @@ -174,15 +171,14 @@ export function include(value: undefined, context: ClassFieldDecoratorContext): export function include(value: undefined|string, context?: ClassFieldDecoratorContext|string) { return handleClassFieldDecoratorWithOptionalArgs([value, context as string], context as ClassFieldDecoratorContext, ([resource, export_name], context) => { - console.log("set @include", resource, export_name, context.name) - context.metadata[IMPORT_PROPS] = [resource, export_name??context.name]; + Decorators.setMetadata(context, IMPORT_PROPS, [resource, export_name??context.name]); } ) } /** \@frontend decorator to declare methods that always run on the frontend */ export function frontend(_value: undefined, context: ClassFieldDecoratorContext|ClassMethodDecoratorContext) { - context.metadata[STANDALONE_PROPS] = context.name; + Decorators.setMetadata(context, STANDALONE_PROPS, context.name); } @@ -192,9 +188,8 @@ export function bindOrigin(value: undefined, context: ClassFieldDecoratorContext export function bindOrigin(options:{datex:boolean}|undefined, context?: ClassFieldDecoratorContext|ClassMethodDecoratorContext) { return handleClassFieldDecoratorWithOptionalArgs([options], context as ClassFieldDecoratorContext, ([options], context:ClassFieldDecoratorContext|ClassMethodDecoratorContext) => { - console.log("set @bindOrigin", options, context.name) - context.metadata[STANDALONE_PROPS] = context.name; - context.metadata[ORIGIN_PROPS] = options ?? {datex:false}; + Decorators.setMetadata(context, STANDALONE_PROPS, context.name); + Decorators.setMetadata(context, ORIGIN_PROPS, options ?? {datex:false}); } ) } \ No newline at end of file diff --git a/src/base/uix-datex-module.ts b/src/base/uix-datex-module.ts index 108abb534..8ca0add74 100644 --- a/src/base/uix-datex-module.ts +++ b/src/base/uix-datex-module.ts @@ -3,16 +3,16 @@ import { datex } from "datex-core-legacy/mod.ts"; const stage = client_type == "deno" ? (await import("../app/args.ts#lazy")).stage : "TODO!"; -// uix.stage -const stageTransformFunction = await datex` - function (options) ( - use currentStage from #public.uix; - always options.(currentStage) default @@local - ); -` +// // uix.stage +// const stageTransformFunction = await datex` +// function (options) ( +// use currentStage from #public.uix; +// always options.(currentStage) default @@local +// ); +// ` -@endpoint class uix { - @property static LANG = "en"; - @property static stage = stageTransformFunction - @property static currentStage = stage -} \ No newline at end of file +// @endpoint class uix { +// @property static LANG = "en"; +// @property static stage = stageTransformFunction +// @property static currentStage = stage +// } \ No newline at end of file diff --git a/src/html/style.ts b/src/html/style.ts index 5ab238fd6..e973e8feb 100644 --- a/src/html/style.ts +++ b/src/html/style.ts @@ -20,7 +20,13 @@ import type { HTMLElement } from "../uix-dom/dom/mod.ts"; * ``` * @param styleGenerator */ -export function style = {}, Children = JSX.childrenOrChildrenPromise|JSX.childrenOrChildrenPromise[], Context = unknown>(styleGenerator:jsxInputGenerator):jsxInputGenerator&((cl:typeof HTMLElement)=>any) +export function style< + Options extends Record = {}, + Children = JSX.childrenOrChildrenPromise|JSX.childrenOrChildrenPromise[], + Context extends typeof HTMLElement = typeof HTMLElement +>( + styleGenerator:jsxInputGenerator> +): jsxInputGenerator&((cl: Context, context: ClassDecoratorContext)=>any) /** * \@style decorator @@ -37,7 +43,7 @@ export function style = {}, Children = JSX.c * ``` * @param styleGenerator */ -export function style(style:CSSStyleSheet):((cl:typeof HTMLElement)=>any) +export function style(style:CSSStyleSheet):((cl:typeof HTMLElement, context: ClassDecoratorContext)=>any) /** * \@style decorator diff --git a/src/html/template.ts b/src/html/template.ts index 53c90dfd2..a9ad75f0c 100644 --- a/src/html/template.ts +++ b/src/html/template.ts @@ -5,8 +5,9 @@ import { Component } from "../components/Component.ts"; import { DOMUtils } from "../uix-dom/datex-bindings/dom-utils.ts"; import { domContext, domUtils } from "../app/dom-context.ts"; import type { Element, Node, HTMLElement, HTMLTemplateElement } from "../uix-dom/dom/mod.ts"; -import { defaultOptions } from "../base/decorators.ts"; +import { defaultOptions, initDefaultOptions } from "../base/decorators.ts"; import type { Class } from "datex-core-legacy/utils/global_types.ts"; +import { METADATA } from "datex-core-legacy/js_adapter/js_class_adapter.ts"; /** * cloneNode(true), but also clones shadow roots. @@ -171,7 +172,9 @@ export function template(templateOrGenerator?:JSX.Element|jsxInputGenerator Date: Wed, 28 Feb 2024 23:59:09 +0100 Subject: [PATCH 03/56] fix frontend decorator for methods --- src/base/decorators.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/base/decorators.ts b/src/base/decorators.ts index 91e843ab6..b21a356d5 100644 --- a/src/base/decorators.ts +++ b/src/base/decorators.ts @@ -177,7 +177,7 @@ export function include(value: undefined|string, context?: ClassFieldDecoratorCo } /** \@frontend decorator to declare methods that always run on the frontend */ -export function frontend(_value: undefined, context: ClassFieldDecoratorContext|ClassMethodDecoratorContext) { +export function frontend(_value: undefined|((...args:any[])=>any), context: ClassFieldDecoratorContext|ClassMethodDecoratorContext) { Decorators.setMetadata(context, STANDALONE_PROPS, context.name); } From 43550b4eaa5007edd34d96cd6e35e0f79b9f1f2f Mon Sep 17 00:00:00 2001 From: benStre Date: Wed, 28 Feb 2024 23:59:31 +0100 Subject: [PATCH 04/56] fix decorator metadata --- src/components/Component.ts | 56 ++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/src/components/Component.ts b/src/components/Component.ts index 8c84e97ae..1a71b317d 100644 --- a/src/components/Component.ts +++ b/src/components/Component.ts @@ -17,7 +17,7 @@ import { BOUND_TO_ORIGIN, bindToOrigin, getValueInitializer } from "../app/datex import type { DynamicCSSStyleSheet } from "../utils/css-template-strings.ts"; import { addCSSScopeSelector } from "../utils/css-scoping.ts" import { jsxInputGenerator } from "../html/template.ts"; -import { bindObserver, domContext, domUtils } from "../app/dom-context.ts"; +import { domContext, domUtils } from "../app/dom-context.ts"; import { UIX } from "../../uix.ts"; import { convertToWebPath } from "../app/convert-to-web-path.ts"; import { client_type } from "datex-core-legacy/utils/constants.ts"; @@ -29,9 +29,11 @@ export type standaloneContentPropertyData = {type:'id'|'content'|'layout'|'child export type standalonePropertyData = {type:'prop'} export type standaloneProperties = Record; +// deno-lint-ignore no-empty-interface +interface Options {} // @template("uix:component") -export abstract class Component = Record, ChildElement = JSX.singleOrMultipleChildren> extends domContext.HTMLElement implements RouteManager { +export abstract class Component extends domContext.HTMLElement implements RouteManager { /************************************ STATIC ***************************************/ @@ -191,17 +193,15 @@ export abstract class Component = Record(); private static loadStandaloneProps() { - const scope = this.prototype; - if (this.standalone_loaded.has(this)) return; this.standalone_loaded.add(this); this.standaloneMethods = {}; this.standaloneProperties = {}; - const parentProps = (Object.getPrototypeOf(this).prototype)?.[METADATA]?.[STANDALONE_PROPS]?.public; - const props:Record = scope[METADATA]?.[STANDALONE_PROPS]?.public; - const originProps:Record = scope[METADATA]?.[ORIGIN_PROPS]?.public; + const parentProps = (Object.getPrototypeOf(this))?.[METADATA]?.[STANDALONE_PROPS]?.public; + const props:Record = this[METADATA]?.[STANDALONE_PROPS]?.public; + const originProps:Record = this[METADATA]?.[ORIGIN_PROPS]?.public; if (!props) return; // workaround: [STANDALONE_PROPS] from parent isn't overriden, just ignore @@ -209,14 +209,13 @@ export abstract class Component = Record>>", originProps?.[name], typeof originProps?.[name], name, "<--") - if (scope[name]) { + if ((this.prototype as any)[name]) { // also bound to origin if (originProps?.[name]) { this.addStandaloneProperty(name, originProps?.[name]); - } else this.addStandaloneMethod(name, scope[name]); + } else this.addStandaloneMethod(name, (this.prototype as any)[name]); } - // otherwise, instace property + // otherwise, instance property else this.addStandaloneProperty(name, originProps?.[name]); } } @@ -240,8 +239,8 @@ export abstract class Component = Record = Record = Record = Object.getPrototypeOf(this)[METADATA]?.[ID_PROPS]?.public; - const content_props:Record = Object.getPrototypeOf(this)[METADATA]?.[CONTENT_PROPS]?.public; - const layout_props:Record = Object.getPrototypeOf(this)[METADATA]?.[LAYOUT_PROPS]?.public; + const id_props:Record = (this.constructor as any)[METADATA]?.[ID_PROPS]?.public; + const content_props:Record = (this.constructor as any)[METADATA]?.[CONTENT_PROPS]?.public; + const layout_props:Record = (this.constructor as any)[METADATA]?.[LAYOUT_PROPS]?.public; // only add children when constructing component, otherwise they are added twice - const child_props:Record = constructed ? Object.getPrototypeOf(this)[METADATA]?.[CHILD_PROPS]?.public : undefined; - bindContentProperties(this, id_props, content_props, layout_props, child_props); + const child_props:Record = constructed ? (this.constructor as any)[METADATA]?.[CHILD_PROPS]?.public : undefined; + bindContentProperties(this, id_props, content_props, layout_props, child_props); } @@ -860,20 +859,19 @@ export abstract class Component = Record = scope[METADATA]?.[ORIGIN_PROPS]?.public; + const scope = this.constructor as any; + const originProps:Record|undefined = scope[METADATA]?.[ORIGIN_PROPS]?.public; // init props with current values for (const [name, data] of Object.entries((this.constructor as typeof Component).standaloneProperties)) { // check if prop is method if (typeof this[name] === "function") { - if (originProps[name] && !(this[name] as any)[BOUND_TO_ORIGIN]) { + if (originProps?.[name] && !(this[name] as any)[BOUND_TO_ORIGIN]) { // @ts-ignore $ - this[name] = bindToOrigin(this[name], this, null, originProps[name].datex); + this[name] = bindToOrigin(this[name], this, null, originProps[name].datex); } js_code += `self["${name}"] = ${Component.getStandaloneMethodContentWithMappedImports(this[name] as Function)};\n`; } From dffcd20c3828e4faae9cdc3401622cd40fc1a3b7 Mon Sep 17 00:00:00 2001 From: benStre Date: Thu, 29 Feb 2024 00:39:21 +0100 Subject: [PATCH 05/56] update uix-dom --- src/uix-dom | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uix-dom b/src/uix-dom index c3809434c..61ba8d254 160000 --- a/src/uix-dom +++ b/src/uix-dom @@ -1 +1 @@ -Subproject commit c3809434cf401743033e15f40d972363bbeab33f +Subproject commit 61ba8d254c129baeabffaba4d8f29b4e323a09f0 From 180366823bc9a91d54e612db429edc8fe34f62c7 Mon Sep 17 00:00:00 2001 From: benStre Date: Thu, 29 Feb 2024 00:40:44 +0100 Subject: [PATCH 06/56] update uix-dom --- src/uix-dom | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uix-dom b/src/uix-dom index 61ba8d254..c5c0b0155 160000 --- a/src/uix-dom +++ b/src/uix-dom @@ -1 +1 @@ -Subproject commit 61ba8d254c129baeabffaba4d8f29b4e323a09f0 +Subproject commit c5c0b015535b1dbd62ec379efd1f9fbbff28c4ab From 371859c12ac7029d714e85822cbe65dca334583a Mon Sep 17 00:00:00 2001 From: benStre Date: Thu, 29 Feb 2024 19:00:48 +0100 Subject: [PATCH 07/56] fix metadata --- src/html/template.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/html/template.ts b/src/html/template.ts index a9ad75f0c..79f824f21 100644 --- a/src/html/template.ts +++ b/src/html/template.ts @@ -173,7 +173,7 @@ export function template(templateOrGenerator?:JSX.Element|jsxInputGenerator Date: Thu, 29 Feb 2024 19:01:10 +0100 Subject: [PATCH 08/56] update file server cdn (unrelated) --- src/server/standalone-file-server.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server/standalone-file-server.ts b/src/server/standalone-file-server.ts index 36407be59..657cc07c8 100644 --- a/src/server/standalone-file-server.ts +++ b/src/server/standalone-file-server.ts @@ -4,8 +4,8 @@ await Deno.run({ "run", "-Aq", "--import-map", - "https://dev.cdn.unyt.org/importmap.json", - "https://dev.cdn.unyt.org/uix1/src/server/file-server-runner.ts", + "https://cdn.unyt.org/uix/importmap.json", + "https://cdn.unyt.org/uix/src/server/file-server-runner.ts", ...Deno.args ] }).status() \ No newline at end of file From 9c23097f49f0ae6eaca1434e02cb2f2d020f157f Mon Sep 17 00:00:00 2001 From: benStre Date: Thu, 29 Feb 2024 19:02:09 +0100 Subject: [PATCH 09/56] update uix-dom --- src/uix-dom | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uix-dom b/src/uix-dom index c5c0b0155..18886ac50 160000 --- a/src/uix-dom +++ b/src/uix-dom @@ -1 +1 @@ -Subproject commit c5c0b015535b1dbd62ec379efd1f9fbbff28c4ab +Subproject commit 18886ac508fd35f0ffae8454213d111cd3380cee From 75335ae95c916e4340e1d78f6c080b989f7544cd Mon Sep 17 00:00:00 2001 From: benStre Date: Thu, 29 Feb 2024 22:45:09 +0100 Subject: [PATCH 10/56] update uix-dom --- src/html/render.ts | 6 +++--- src/hydration/partial.ts | 2 +- src/uix-dom | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/html/render.ts b/src/html/render.ts index fc192c698..0222ee8f5 100644 --- a/src/html/render.ts +++ b/src/html/render.ts @@ -254,7 +254,7 @@ function _getOuterHTML(el:Node, opts?:_renderOptions, collectedStylesheets?:stri else attrs.push("uix-static"); // inject event listeners - if (dataPtr && opts?._injectedJsData && ((el)[DOMUtils.EVENT_LISTENERS] || (el)[DOMUtils.PSEUDO_ATTR_BINDINGS])) { + if (dataPtr && opts?._injectedJsData && ((el)[DOMUtils.EVENT_LISTENERS] || (el)[DOMUtils.ATTR_BINDINGS])) { let context: HTMLElement|undefined; let parent: Element|null = el; let hasScriptContent = false; // indicates whether the generated script actually contains relevant content, not just skeleton code @@ -315,7 +315,7 @@ function _getOuterHTML(el:Node, opts?:_renderOptions, collectedStylesheets?:stri throw new Error(`Invalid datex-update="onsubmit", no form found`) } - for (const [attr, ptr] of (el)[DOMUtils.PSEUDO_ATTR_BINDINGS] ?? []) { + for (const [attr, ptr] of (el)[DOMUtils.ATTR_BINDINGS] ?? []) { opts?.requiredPointers?.add(ptr); @@ -484,7 +484,7 @@ const isNormalFunction = (fnSrc:string) => { function isLiveNode(node: Node) { // hybrid components are always live (TODO: not for backend-only components) if (node instanceof Component) return true; - if (node[DOMUtils.PSEUDO_ATTR_BINDINGS]?.size) return true; + if (node[DOMUtils.ATTR_BINDINGS]?.size) return true; if (node[DOMUtils.EVENT_LISTENERS]?.size) return true; // uix-placeholder are always live if (node instanceof domContext.Element && node.tagName?.toLowerCase() == "uix-placeholder") return true; diff --git a/src/hydration/partial.ts b/src/hydration/partial.ts index 3312d1ddb..837185a97 100644 --- a/src/hydration/partial.ts +++ b/src/hydration/partial.ts @@ -29,7 +29,7 @@ export function getLiveNodes(treeRoot: Element, includeEventListeners = true, _l } if (!isLive) { - if (treeRoot[DOMUtils.PSEUDO_ATTR_BINDINGS]?.size) isLive = true + if (treeRoot[DOMUtils.ATTR_BINDINGS]?.size) isLive = true } if (isLive) _list.push(treeRoot); diff --git a/src/uix-dom b/src/uix-dom index 18886ac50..94fba652f 160000 --- a/src/uix-dom +++ b/src/uix-dom @@ -1 +1 @@ -Subproject commit 18886ac508fd35f0ffae8454213d111cd3380cee +Subproject commit 94fba652fea3062a49db5fc504e618a60295d3f7 From b07e53d72549561e5e05196fd8578ffda918b582 Mon Sep 17 00:00:00 2001 From: benStre Date: Fri, 1 Mar 2024 17:04:08 +0100 Subject: [PATCH 11/56] update version warning in docs --- docs/manual/01 Getting Started.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/manual/01 Getting Started.md b/docs/manual/01 Getting Started.md index a16c49e36..00bdec139 100644 --- a/docs/manual/01 Getting Started.md +++ b/docs/manual/01 Getting Started.md @@ -36,8 +36,7 @@ This is why UIX ships with integrated features such as: To install uix, you need to install [Deno](https://docs.deno.com/runtime/manual/getting_started/installation) first. > [!WARNING] -> UIX is currently only supported for Deno versions <= 1.39.4. We are actively working on a -> release that is compatible with newer Deno versions. +> UIX is only supported for Deno versions > 1.40.0 #### Linux / MacOS From f52a0126d89f2f9b576ff5bfdbf9b0a118db279a Mon Sep 17 00:00:00 2001 From: benStre Date: Fri, 1 Mar 2024 17:04:41 +0100 Subject: [PATCH 12/56] remove pinned deno version --- src/plugins/git-deploy.ts | 10 ++-------- src/runners/run-local-docker.ts | 2 +- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/plugins/git-deploy.ts b/src/plugins/git-deploy.ts index 2648baacb..34c20c70b 100644 --- a/src/plugins/git-deploy.ts +++ b/src/plugins/git-deploy.ts @@ -90,10 +90,7 @@ export default class GitDeployPlugin implements AppPlugin { }, { name: 'Setup Deno', - uses: 'denoland/setup-deno@v1', - with: { - 'deno-version': '1.39.4' - } + uses: 'denoland/setup-deno@v1' }, { name: 'Run Tests', @@ -120,10 +117,7 @@ export default class GitDeployPlugin implements AppPlugin { }, { name: 'Setup Deno', - uses: 'denoland/setup-deno@v1', - with: { - 'deno-version': '1.39.4' - } + uses: 'denoland/setup-deno@v1' }, { name: 'Deploy UIX App', diff --git a/src/runners/run-local-docker.ts b/src/runners/run-local-docker.ts index d89e4045d..26477c4cb 100644 --- a/src/runners/run-local-docker.ts +++ b/src/runners/run-local-docker.ts @@ -113,7 +113,7 @@ export default class LocalDockerRunner implements UIXRunner { services: { "uix-app": { container_name: `${name}`, - image: "denoland/deno:1.37.2", // max 1.39.4, currently 1.37.2 because of websocket issues + image: "denoland/deno", expose: ["80"], ports, From cdf31d2e3d9faa71245a2268bb495737e3a1eeb2 Mon Sep 17 00:00:00 2001 From: benStre Date: Fri, 1 Mar 2024 17:04:59 +0100 Subject: [PATCH 13/56] fix public.uix namespace --- run.ts | 18 ++---------------- src/app/start-app.ts | 5 +++++ src/base/uix-datex-module.ts | 24 +++++++++++------------- 3 files changed, 18 insertions(+), 29 deletions(-) diff --git a/run.ts b/run.ts index 5c956d904..b151b005f 100644 --- a/run.ts +++ b/run.ts @@ -4,7 +4,6 @@ import { Datex, datex } from "datex-core-legacy/no_init.ts"; // required by getAppConfig import type { Datex as _Datex } from "datex-core-legacy"; // required by getAppConfig - import { getAppOptions } from "./src/app/config-files.ts"; import { getExistingFile } from "./src/utils/file-utils.ts"; import { clear, command_line_options, enableTLS, login, init, rootPath, stage, watch, watch_backend, live } from "./src/app/args.ts"; @@ -22,6 +21,8 @@ import { getDXConfigData } from "./src/app/dx-config-parser.ts"; import { Path } from "./src/utils/path.ts"; import { handleAutoUpdate, updateCache } from "./auto-update.ts"; +import "./src/base/uix-datex-module.ts" + import { enableErrorReporting } from "datex-core-legacy/utils/error-reporting.ts"; import { getErrorReportingPreference, saveErrorReportingPreference, shouldAskForErrorReportingPreference } from "./src/utils/error-reporting-preference.ts"; import { isCIRunner } from "./src/utils/check-ci.ts"; @@ -144,20 +145,7 @@ async function loadPlugins() { return plugins; } -/** - * Mock #public.uix - */ -async function mockUIX() { - await datex` - #public.uix = { - stage: function (options) ( - options.${stage} default @@local - ) - } - ` -} -await mockUIX(); // find importmap (from app.dx or deno.json) to start the actual deno process with valid imports const plugins = await loadPlugins(); const runners = [new LocalDockerRunner()]; @@ -166,8 +154,6 @@ if (!options.import_map) throw new Error("Could not find importmap"); options.import_map = await createProxyImports(options, new_base_url, params.deno_config_path!); -// make sure UIX mock is not overridden -await mockUIX(); await runBackends(options); diff --git a/src/app/start-app.ts b/src/app/start-app.ts index bbcb6ac60..c8e84a510 100644 --- a/src/app/start-app.ts +++ b/src/app/start-app.ts @@ -11,6 +11,8 @@ import { getDirType } from "./utils.ts"; import { WebSocketServerInterface } from "datex-core-legacy/network/communication-interfaces/websocket-server-interface.ts" import { HTTPServerInterface } from "datex-core-legacy/network/communication-interfaces/http-server-interface.ts" import { communicationHub } from "datex-core-legacy/network/communication-hub.ts"; +import { resolveDependencies } from "../html/dependency-resolver.ts"; +import { resolve } from "https://deno.land/std@0.172.0/path/win32.ts"; const logger = new Datex.Logger("UIX App"); @@ -166,6 +168,9 @@ export async function startApp(app: {domains:string[], hostDomains: string[], op const {HTTP} = await import("./http-over-datex.ts") HTTP.setServer(server); } + + // preload dependencies + resolveDependencies(import.meta.resolve("datex-core-legacy")) return { defaultServer: server, diff --git a/src/base/uix-datex-module.ts b/src/base/uix-datex-module.ts index 8ca0add74..7d79f1b47 100644 --- a/src/base/uix-datex-module.ts +++ b/src/base/uix-datex-module.ts @@ -3,16 +3,14 @@ import { datex } from "datex-core-legacy/mod.ts"; const stage = client_type == "deno" ? (await import("../app/args.ts#lazy")).stage : "TODO!"; -// // uix.stage -// const stageTransformFunction = await datex` -// function (options) ( -// use currentStage from #public.uix; -// always options.(currentStage) default @@local -// ); -// ` - -// @endpoint class uix { -// @property static LANG = "en"; -// @property static stage = stageTransformFunction -// @property static currentStage = stage -// } \ No newline at end of file +// Can't use @endpoint class here, because endpoint is only initialized after #public.uix is required in .dx config +await datex` + #public.uix = { + LANG: "en", + currentStage: ${stage}, + stage: function (options) ( + use currentStage from #public.uix; + always options.(currentStage) default @@local + ) + }; +` \ No newline at end of file From 69aab832bd6b693fd1f1648db6620b30c6c96bca Mon Sep 17 00:00:00 2001 From: benStre Date: Fri, 1 Mar 2024 17:12:42 +0100 Subject: [PATCH 14/56] fix containermanager --- src/runners/run-remote.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/runners/run-remote.ts b/src/runners/run-remote.ts index 2c1ed4c50..627099aa7 100644 --- a/src/runners/run-remote.ts +++ b/src/runners/run-remote.ts @@ -108,8 +108,7 @@ export async function runRemote(params: runParams, root_path: URL, options: norm env.push(`UIX_VERSION=0.1`) const container = await datex ` - use ContainerManager from ${requiredLocation}; - ContainerManager.createUIXAppContainer( + ${requiredLocation}.ContainerManager.createUIXAppContainer( ${repo.origin}, ${repo.branch}, ${stageEndpoint}, From 06e64579f0416b946b7a54707f6f980acb971f6c Mon Sep 17 00:00:00 2001 From: benStre Date: Fri, 1 Mar 2024 18:36:10 +0100 Subject: [PATCH 15/56] support custom importmap/run paths for deployment --- run.ts | 6 +++-- src/app/app-plugin.ts | 5 +++- src/app/config-files.ts | 35 +++++++++++++++----------- src/app/options.ts | 53 ++++++++++++++++++++++++++------------- src/plugins/git-deploy.ts | 20 +++++++++------ src/runners/run-remote.ts | 13 ++++++---- src/runners/runner.ts | 2 +- src/utils/importmap.ts | 9 ++++--- 8 files changed, 91 insertions(+), 52 deletions(-) diff --git a/run.ts b/run.ts index b151b005f..3d426d531 100644 --- a/run.ts +++ b/run.ts @@ -27,6 +27,7 @@ import { enableErrorReporting } from "datex-core-legacy/utils/error-reporting.ts import { getErrorReportingPreference, saveErrorReportingPreference, shouldAskForErrorReportingPreference } from "./src/utils/error-reporting-preference.ts"; import { isCIRunner } from "./src/utils/check-ci.ts"; import { logger, runParams } from "./src/runners/runner.ts"; +import { applyPlugins } from "./src/app/config-files.ts"; // login flow if (login) await triggerLogin(); @@ -149,11 +150,12 @@ async function loadPlugins() { // find importmap (from app.dx or deno.json) to start the actual deno process with valid imports const plugins = await loadPlugins(); const runners = [new LocalDockerRunner()]; -const [options, new_base_url] = await normalizeAppOptions(await getAppOptions(rootPath, plugins), rootPath); +const [options, new_base_url] = await normalizeAppOptions(await getAppOptions(rootPath), rootPath); if (!options.import_map) throw new Error("Could not find importmap"); - options.import_map = await createProxyImports(options, new_base_url, params.deno_config_path!); +await applyPlugins(plugins, rootPath, options) + await runBackends(options); diff --git a/src/app/app-plugin.ts b/src/app/app-plugin.ts index 677a0d832..b53150c38 100644 --- a/src/app/app-plugin.ts +++ b/src/app/app-plugin.ts @@ -1,4 +1,7 @@ +import { Path } from "datex-core-legacy/utils/path.ts"; +import type { normalizedAppOptions } from "./options.ts"; + export interface AppPlugin { name: string - apply(data:Data): Promise|void + apply(data:Data, rootPath:Path.File, appOptions:normalizedAppOptions): Promise|void } diff --git a/src/app/config-files.ts b/src/app/config-files.ts index e750c704b..123e76afb 100644 --- a/src/app/config-files.ts +++ b/src/app/config-files.ts @@ -1,10 +1,11 @@ // no explicit imports, should also work without import maps... import {getExistingFile} from "../utils/file-utils.ts"; import { command_line_options } from "../app/args.ts"; -import { Path } from "../utils/path.ts"; +import { Path } from "datex-core-legacy/utils/path.ts"; import { Datex } from "datex-core-legacy/mod.ts"; import type { AppPlugin } from "./app-plugin.ts"; import { logger } from "../utils/global-values.ts"; +import { normalizedAppOptions } from "./options.ts"; const default_importmap = "https://dev.cdn.unyt.org/importmap.json"; const arg_import_map_string = command_line_options.option("import-map", {type:"string", description: "Import map path"}); @@ -15,10 +16,27 @@ const arg_import_map = (arg_import_map_string?.startsWith("http://")||arg_import undefined ) +export async function applyPlugins(plugins: AppPlugin[], rootPath:Path, appOptions: normalizedAppOptions) { + const config_path = getExistingFile(rootPath, './app.dx', './app.json'); + + if (!config_path) throw "Could not find an app.dx or app.json config file in " + new Path(rootPath).normal_pathname + + // handle plugins (only if in dev environment, not on host, TODO: better solution) + if (plugins?.length && !Deno.env.has("UIX_HOST_ENDPOINT")) { + const pluginData = await datex.get>(config_path, undefined, undefined, plugins.map(p=>p.name)); + for (const plugin of plugins) { + if (pluginData[plugin.name]) { + logger.debug(`using plugin "${plugin.name}"`); + await plugin.apply(pluginData[plugin.name], rootPath, appOptions) + } + } + } +} + /** * get combined config of app.dx and deno.json and command line args */ -export async function getAppOptions(root_path:URL, plugins?: AppPlugin[]) { +export async function getAppOptions(root_path:URL) { const config_path = getExistingFile(root_path, './app.dx', './app.json'); let config:Record = {} @@ -29,18 +47,6 @@ export async function getAppOptions(root_path:URL, plugins?: AppPlugin[]) { throw "Invalid config file" } config = Object.fromEntries(Datex.DatexObject.entries(>raw_config)); - - // handle plugins (only if in dev environment, not on host, TODO: better solution) - if (plugins?.length && !Deno.env.has("UIX_HOST_ENDPOINT")) { - const pluginData = await datex.get>(config_path, undefined, undefined, plugins.map(p=>p.name)); - for (const plugin of plugins) { - if (pluginData[plugin.name]) { - logger.debug(`using plugin "${plugin.name}"`); - await plugin.apply(pluginData[plugin.name]) - } - } - } - } else throw "Could not find an app.dx or app.json config file in " + new Path(root_path).normal_pathname @@ -70,7 +76,6 @@ export async function getAppOptions(root_path:URL, plugins?: AppPlugin[]) { } catch {} } - if (!config.import_map && !config.import_map_path) config.import_map_path = default_importmap; // if (config.import_map) throw "embeded import maps are not yet supported for uix apps"; diff --git a/src/app/options.ts b/src/app/options.ts index 3cec12cb3..5d1011046 100644 --- a/src/app/options.ts +++ b/src/app/options.ts @@ -1,6 +1,6 @@ import type { Tuple } from "datex-core-legacy/types/tuple.ts"; import { ImportMap } from "../utils/importmap.ts"; -import { Path } from "../utils/path.ts"; +import { Path } from "datex-core-legacy/utils/path.ts"; declare const Datex: any; // cannot import Datex here, circular dependency problems @@ -43,14 +43,14 @@ export interface normalizedAppOptions extends appOptions { experimentalFeatures: string[] } -export async function normalizeAppOptions(options:appOptions = {}, base_url?:string|URL): Promise<[normalizedAppOptions, URL]> { +export async function normalizeAppOptions(options:appOptions = {}, baseURL?:string|URL): Promise<[normalizedAppOptions, Path.File]> { const n_options = {}; // determine base url - if (typeof base_url == "string" && !base_url.startsWith("file://")) base_url = 'file://' + base_url; - base_url ??= new Error().stack?.trim()?.match(/((?:https?|file)\:\/\/.*?)(?::\d+)*(?:$|\nevaluate@)/)?.[1]; - if (!base_url) throw new Error("Could not determine the app base url (this should not happen)"); - base_url = new URL(base_url.toString()); + if (typeof baseURL == "string" && !baseURL.startsWith("file://")) baseURL = 'file://' + baseURL; + baseURL ??= new Error().stack?.trim()?.match(/((?:https?|file)\:\/\/.*?)(?::\d+)*(?:$|\nevaluate@)/)?.[1]; + if (!baseURL) throw new Error("Could not determine the app base url (this should not happen)"); + const basePath = Path.File(baseURL.toString()); n_options.name = options.name; n_options.description = options.description; @@ -76,9 +76,9 @@ export async function normalizeAppOptions(options:appOptions = {}, base_url?:str else throw new Error("No importmap found or set in the app configuration") // should not happen // default frontend, backend, common - if (options.frontend==undefined && new Path('./frontend/', base_url).fs_exists) options.frontend = [new Path('./frontend/', base_url)] - if (options.backend==undefined && new Path('./backend/', base_url).fs_exists) options.backend = [new Path('./backend/', base_url)] - if (options.common==undefined && new Path('./common/', base_url).fs_exists) options.common = [new Path('./common/', base_url)] + if (options.frontend==undefined && new Path('./frontend/', basePath).fs_exists) options.frontend = [new Path('./frontend/', basePath)] + if (options.backend==undefined && new Path('./backend/', basePath).fs_exists) options.backend = [new Path('./backend/', basePath)] + if (options.common==undefined && new Path('./common/', basePath).fs_exists) options.common = [new Path('./common/', basePath)] // convert to arrays const frontends = options.frontend instanceof Array ? options.frontend : options.frontend instanceof Datex.Tuple ? (options.frontend as unknown as Tuple).toArray() : [options.frontend] @@ -86,14 +86,14 @@ export async function normalizeAppOptions(options:appOptions = {}, base_url?:str const commons = options.common instanceof Array ? options.common : options.common instanceof Datex.Tuple ? (options.common as unknown as Tuple).toArray() : [options.common] // convert to absolute paths - n_options.frontend = frontends.filter(p=>!!p).map(p=>new Path(p,base_url).asDir().fsCreateIfNotExists()); - n_options.backend = backends.filter(p=>!!p).map(p=>new Path(p,base_url).asDir().fsCreateIfNotExists()); - n_options.common = commons.filter(p=>!!p).map(p=>new Path(p,base_url).asDir().fsCreateIfNotExists()); + n_options.frontend = frontends.filter(p=>!!p).map(p=>new Path(p,basePath).asDir().fsCreateIfNotExists()); + n_options.backend = backends.filter(p=>!!p).map(p=>new Path(p,basePath).asDir().fsCreateIfNotExists()); + n_options.common = commons.filter(p=>!!p).map(p=>new Path(p,basePath).asDir().fsCreateIfNotExists()); // pages dir or default pages dir - if (options.pages) n_options.pages = new Path(options.pages,base_url).asDir() + if (options.pages) n_options.pages = new Path(options.pages,basePath).asDir() else { - const defaultPagesDir = new Path('./pages/', base_url); + const defaultPagesDir = new Path('./pages/', basePath); if (defaultPagesDir.fs_exists) n_options.pages = defaultPagesDir; } @@ -104,7 +104,7 @@ export async function normalizeAppOptions(options:appOptions = {}, base_url?:str if (!n_options.frontend.length) { // try to find the frontend dir - const frontend_dir = new Path("./frontend/",base_url); + const frontend_dir = new Path("./frontend/",basePath); try { if (!Deno.statSync(frontend_dir).isFile) n_options.frontend.push(frontend_dir) } @@ -113,7 +113,7 @@ export async function normalizeAppOptions(options:appOptions = {}, base_url?:str if (!n_options.backend.length) { // try to find the backend dir - const backend_dir = new Path("./backend/",base_url); + const backend_dir = new Path("./backend/",basePath); try { if (!Deno.statSync(backend_dir).isFile) n_options.backend.push(backend_dir) } @@ -122,12 +122,29 @@ export async function normalizeAppOptions(options:appOptions = {}, base_url?:str if (!n_options.common.length) { // try to find the common dir - const common_dir = new Path("./common/",base_url); + const common_dir = new Path("./common/",basePath); try { if (!Deno.statSync(common_dir).isFile) n_options.common.push(common_dir) } catch {} } - return [n_options, base_url] + return [n_options, basePath] +} + + +export function getInferredRunPaths(importMap: ImportMap, rootPath: Path.File): {importMapPath: string|null, uixRunPath: string|null} { + const importMapPath = importMap.originalPath ? importMap.originalPath.getAsRelativeFrom(rootPath) : null; + const inferredAbsoluteRunPath = importMap.imports["uix"]?.replace(/\/uix\.ts$/, '/run.ts') ?? null as string|null; + const uixRunPath = inferredAbsoluteRunPath ? + ( + Path.pathIsURL(inferredAbsoluteRunPath) ? + inferredAbsoluteRunPath : + new Path(inferredAbsoluteRunPath, importMap.path).getAsRelativeFrom(rootPath)) : + null; + + return { + importMapPath, + uixRunPath + } } \ No newline at end of file diff --git a/src/plugins/git-deploy.ts b/src/plugins/git-deploy.ts index 34c20c70b..2a6a13ec8 100644 --- a/src/plugins/git-deploy.ts +++ b/src/plugins/git-deploy.ts @@ -4,13 +4,17 @@ import { GitRepo } from "../utils/git.ts"; import { json2yaml } from "https://deno.land/x/json2yaml@v1.0.1/mod.ts"; import { Datex } from "datex-core-legacy/mod.ts"; import { isCIRunner } from "../utils/check-ci.ts"; +import { normalizedAppOptions } from "../app/options.ts"; +import { app } from "../app/app.ts"; +import { Path } from "datex-core-legacy/utils/path.ts"; +import { getInferredRunPaths } from "../app/options.ts"; const logger = new Logger("Git Deploy Plugin"); export default class GitDeployPlugin implements AppPlugin { name = "git_deploy" - async apply(data: Record) { + async apply(data: Record, rootPath: Path.File, appOptions: normalizedAppOptions) { // don't update CI workflows from inside a CI runner if (isCIRunner()) { @@ -28,7 +32,7 @@ export default class GitDeployPlugin implements AppPlugin { const workflowDir = await gitRepo.initWorkflowDirectory(); // TODO: also support gitlab - const workflows = this.generateGithubWorkflows(data); + const workflows = this.generateGithubWorkflows(data, rootPath, appOptions); // first delete all old uix-deploy.yml files for await (const entry of Deno.readDir(workflowDir.normal_pathname)) { @@ -43,9 +47,11 @@ export default class GitDeployPlugin implements AppPlugin { } - generateGithubWorkflows(data: Record) { + generateGithubWorkflows(data: Record, rootPath: Path.File, appOptions: normalizedAppOptions) { const workflows: Record = {} + const {importMapPath, uixRunPath} = getInferredRunPaths(appOptions.import_map, rootPath) + for (let [stage, config] of Object.entries(data)) { config = Object.fromEntries(Datex.DatexObject.entries(config)); @@ -55,10 +61,10 @@ export default class GitDeployPlugin implements AppPlugin { const tests = config.tests ?? true; const useDevCDN = config.useDevCDN; - const importmapPath = useDevCDN ? "https://dev.cdn.unyt.org/importmap.json" : "https://cdn.unyt.org/importmap.json" - const importmapPathUIX = useDevCDN ? "https://dev.cdn.unyt.org/uix1/importmap.dev.json" : "https://cdn.unyt.org/uix/importmap.json" + const importmapPath = useDevCDN ? "https://dev.cdn.unyt.org/importmap.json" : (importMapPath??"https://cdn.unyt.org/importmap.json") + const importmapPathUIX = useDevCDN ? "https://dev.cdn.unyt.org/uix1/importmap.dev.json" : (importMapPath??"https://cdn.unyt.org/importmap.json") const testRunPath = useDevCDN ? "https://dev.cdn.unyt.org/unyt_tests/run.ts" : "https://cdn.unyt.org/unyt-tests/run.ts" - const uixRunPath = useDevCDN ? "https://dev.cdn.unyt.org/uix1/run.ts" : "https://cdn.unyt.org/uix/run.ts" + const uixRunnerPath = useDevCDN ? "https://dev.cdn.unyt.org/uix1/run.ts" : (uixRunPath??"https://cdn.unyt.org/uix/run.ts") if (branch && branch !== "*") { on = { @@ -121,7 +127,7 @@ export default class GitDeployPlugin implements AppPlugin { }, { name: 'Deploy UIX App', - run: `deno run --importmap ${importmapPathUIX} -Aqr ${uixRunPath} --stage ${stage} --detach` + (args ? ' ' + args.join(" ") : '') + (env_strings ? ' ' + env_strings.join(" ") : '') + run: `deno run --importmap ${importmapPathUIX} -Aqr ${uixRunnerPath} --stage ${stage} --detach` + (args ? ' ' + args.join(" ") : '') + (env_strings ? ' ' + env_strings.join(" ") : '') } ] } diff --git a/src/runners/run-remote.ts b/src/runners/run-remote.ts index 627099aa7..f7f5639ab 100644 --- a/src/runners/run-remote.ts +++ b/src/runners/run-remote.ts @@ -1,8 +1,8 @@ -import { normalizedAppOptions } from "../app/options.ts"; +import { getInferredRunPaths, normalizedAppOptions } from "../app/options.ts"; import { stage, env, watch, clear } from "../app/args.ts"; import { ESCAPE_SEQUENCES, verboseArg } from "datex-core-legacy/utils/logger.ts"; import { GitRepo } from "../utils/git.ts"; -import { Path } from "../utils/path.ts"; +import { Path } from "datex-core-legacy/utils/path.ts"; import { runParams } from "./runner.ts"; import { logger } from "../utils/global-values.ts"; import { gitToken } from "../app/args.ts"; @@ -30,7 +30,7 @@ function onlyDenoFileChanges(fileOutput: string) { * Run UIX app on a remote host * Currently using git for file sync with remote */ -export async function runRemote(params: runParams, root_path: URL, options: normalizedAppOptions, backend: URL, requiredLocation: Datex.Endpoint, stageEndpoint: Datex.Endpoint, customDomains: Record = {}, volumes:URL[] = []) { +export async function runRemote(params: runParams, root_path: Path.File, options: normalizedAppOptions, backend: URL, requiredLocation: Datex.Endpoint, stageEndpoint: Datex.Endpoint, customDomains: Record = {}, volumes:URL[] = []) { const logger = new Datex.Logger(); const repo = await GitRepo.get(); @@ -103,7 +103,9 @@ export async function runRemote(params: runParams, root_path: URL, options: norm const relativeVolumePath = new Path(volume).getAsRelativeFrom(repoRoot) normalizedVolumes.push(relativeVolumePath) } - + + const {importMapPath, uixRunPath} = getInferredRunPaths(options.import_map, root_path) + // tell docker host to use uix v.0.1 env.push(`UIX_VERSION=0.1`) @@ -117,7 +119,8 @@ export async function runRemote(params: runParams, root_path: URL, options: norm ${env}, ${args}, ${normalizedVolumes}, - ${gitToken ?? Deno.env.get("GITHUB_TOKEN")} + ${gitToken ?? Deno.env.get("GITHUB_TOKEN")}, + ${{importMapPath, uixRunPath}} ) ` // console.log(""); diff --git a/src/runners/runner.ts b/src/runners/runner.ts index 53ba07939..def464b8f 100644 --- a/src/runners/runner.ts +++ b/src/runners/runner.ts @@ -13,7 +13,7 @@ export type runParams = { inspect: string | undefined; unstable: boolean | undefined; detach: boolean | undefined; - deno_config_path: string | URL | null; + deno_config_path: URL | null; } export type runOptions = { diff --git a/src/utils/importmap.ts b/src/utils/importmap.ts index 568d7fd8a..141657cf2 100644 --- a/src/utils/importmap.ts +++ b/src/utils/importmap.ts @@ -1,4 +1,4 @@ -import { Path } from "../utils/path.ts"; +import { Path } from "datex-core-legacy/utils/path.ts"; import { Logger } from "datex-core-legacy/utils/logger.ts"; const logger = new Logger("UIX Import Map"); @@ -46,21 +46,24 @@ export class ImportMap { #readonly = true; #json: {imports:Record, scopes?:Record>}; #path?: Path; + #originalPath?: Path #temporary_imports = new Set; get readonly() {return this.#readonly} get path() {return this.#path} + get originalPath() {return this.#originalPath??this.#path} static async fromPath(path:string|URL) { const map = JSON.parse(await new Path(path).getTextContent()); return new ImportMap(map, path); } - constructor(map:{imports:Record}, path?:string|URL, resetOnUnload = false) { + constructor(map:{imports:Record}, path?:string|URL, resetOnUnload = false, originalPath?:Path) { this.#json = map; this.#path = path ? new Path(path) : undefined; + this.#originalPath = originalPath; this.#readonly = !this.#path || this.#path.is_web; this.clearEntriesForExtension(".dx.d.ts"); // remove temporarily created dx.d.ts entries from previous sessions @@ -208,7 +211,7 @@ export class ImportMap { } mappedImports[key] = value; } - return new ImportMap({imports:mappedImports}, newImportMapLocation, resetOnUnload); + return new ImportMap({imports:mappedImports}, newImportMapLocation, resetOnUnload, this.originalPath); } } From 517f75a1c6185516a758db2c7a3ebe2d5c37db80 Mon Sep 17 00:00:00 2001 From: benStre Date: Fri, 1 Mar 2024 20:56:16 +0100 Subject: [PATCH 16/56] fix include datex properties --- src/components/Component.ts | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/components/Component.ts b/src/components/Component.ts index 1a71b317d..eb913df4c 100644 --- a/src/components/Component.ts +++ b/src/components/Component.ts @@ -108,9 +108,10 @@ export abstract class Component = {} + private static async loadDatexImports(target:Component|typeof Component, valid_dx_files:string[], dx_file_values:Map]>){ const allowed_imports:Record = target[METADATA]?.[IMPORT_PROPS]?.public - + // try to resolve imports for (const [prop, [location, exprt]] of Object.entries(allowed_imports??{})) { - // try to get from module dx files if (location == undefined) { let found = false; @@ -145,13 +148,13 @@ export abstract class Componenttarget)[prop] = file_val; + this.virtualDatexPrototype[prop] = file_val; found = true; file_data[1].add(exprt); // remember that export was used logger.debug(`using DATEX export '${exprt}' ${exprt!=prop?`as '${prop}' `:''}in '${this.name}'`); } else if (Datex.DatexObject.has(file_val, exprt)) { - (target)[prop] = Datex.DatexObject.get(file_val, exprt); + this.virtualDatexPrototype[prop] = Datex.DatexObject.get(file_val, exprt); found = true; file_data[1].add(exprt); // remember that export was used logger.debug(`using DATEX export '${exprt}' ${exprt!=prop?`as '${prop}' `:''}in '${this.name}'`); @@ -388,6 +391,9 @@ export abstract class Componentthis.constructor).init(); + this.inheritDatexProperties(); + if (!this.reconstructed_from_dom) await this.loadTemplate(); else this.logger.debug("Reconstructed from DOM, not creating new template content") this.loadDefaultStyle() @@ -703,7 +712,8 @@ export abstract class Componentthis.constructor).init(); - // this.loadTemplate(); + this.inheritDatexProperties(); + this.loadDefaultStyle() await this.init(); this.#datex_lifecycle_ready_resolve?.(); // onCreate can be called (required because of async) @@ -777,6 +787,13 @@ export abstract class Componentthis.constructor).virtualDatexPrototype); + } + // init for base element (and every element) protected async init(constructed = false) { @@ -799,7 +816,6 @@ export abstract class Component Date: Fri, 1 Mar 2024 23:44:54 +0100 Subject: [PATCH 17/56] minor fixes --- src/app/start-app.ts | 4 +++- src/plugins/git-deploy.ts | 5 ++++- src/routing/frontend-routing.ts | 4 ++-- src/runners/run-remote.ts | 1 + uix-short.ts | 1 + 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/app/start-app.ts b/src/app/start-app.ts index c8e84a510..f61573c58 100644 --- a/src/app/start-app.ts +++ b/src/app/start-app.ts @@ -69,7 +69,9 @@ export async function startApp(app: {domains:string[], hostDomains: string[], op // also override endpoint default if (backend_with_default_export) { Datex.Runtime.endpoint_entrypoint = backend_with_default_export.entrypointProxy; - backend_with_default_export.content_provider[Datex.DX_SOURCE] = Datex.Runtime.endpoint.toString(); // use @@local::#entrypoint as dx source + const content_provider = backend_with_default_export.content_provider; + if ((content_provider && typeof content_provider == "object") || typeof content_provider == "function") + (content_provider as any)[Datex.DX_SOURCE] = Datex.Runtime.endpoint.toString(); // use @@local::#entrypoint as dx source } let server:Server|undefined diff --git a/src/plugins/git-deploy.ts b/src/plugins/git-deploy.ts index 2a6a13ec8..2566d4137 100644 --- a/src/plugins/git-deploy.ts +++ b/src/plugins/git-deploy.ts @@ -119,7 +119,10 @@ export default class GitDeployPlugin implements AppPlugin { steps: [ { name: 'Checkout Repo', - uses: 'actions/checkout@v3' + uses: 'actions/checkout@v3', + with: { + submodules: 'recursive' + } }, { name: 'Setup Deno', diff --git a/src/routing/frontend-routing.ts b/src/routing/frontend-routing.ts index 158103ac5..21490aff9 100644 --- a/src/routing/frontend-routing.ts +++ b/src/routing/frontend-routing.ts @@ -88,11 +88,11 @@ export namespace Routing { if (el instanceof HTMLElement && el.hasAttribute("slot")) { const name = el.getAttribute("slot")! slot = querySelector(`frontend-slot[name="${domUtils.escapeHtml(name)}"]`) as HTMLElement; - if (!slot) logger.error(`Could not find a matching for frontend entrypoint route`); + if (!slot) logger.error(`Could not find a matching for content provided from frontend entrypoint. Make sure your backend and frontend routes are not unintentionally colliding.`); } else { slot = querySelector("frontend-slot") as HTMLElement; - if (!slot) logger.error("Could not find a matching for frontend entrypoint route"); + if (!slot) logger.error("Could not find a matching for content provided from frontend entrypoint. Make sure your backend and frontend routes are not unintentionally colliding."); } if (slot) { diff --git a/src/runners/run-remote.ts b/src/runners/run-remote.ts index f7f5639ab..776628aef 100644 --- a/src/runners/run-remote.ts +++ b/src/runners/run-remote.ts @@ -157,6 +157,7 @@ export async function runRemote(params: runParams, root_path: Path.File, options } catch (e) { + console.error(e) logFailure(stageEndpoint, requiredLocation, customDomains, e.message) } diff --git a/uix-short.ts b/uix-short.ts index b6d6a6b4f..19eff7e08 100644 --- a/uix-short.ts +++ b/uix-short.ts @@ -21,6 +21,7 @@ declare global { const ref: typeof _content; const id: typeof _id; const layout: typeof _layout; + const content: typeof _content; const value: typeof _child; const NoResources: typeof _NoResources; const frontend: typeof _frontend; From 3af7b787e7c4e620bb15f2e043021b05a35fbffb Mon Sep 17 00:00:00 2001 From: benStre Date: Fri, 1 Mar 2024 23:56:44 +0100 Subject: [PATCH 18/56] debug logs --- src/runners/run-remote.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/runners/run-remote.ts b/src/runners/run-remote.ts index 776628aef..924938ded 100644 --- a/src/runners/run-remote.ts +++ b/src/runners/run-remote.ts @@ -90,6 +90,8 @@ export async function runRemote(params: runParams, root_path: Path.File, options try { let loaded = false; + await Datex.MessageLogger.enable() + setTimeout(()=>{ if (!loaded) { logger.error('❌ Timeout ' + stageEndpoint + (Object.keys(customDomains).length ? ` (${Object.keys(customDomains).map(domain=>`https://${domain}`).join(", ")})` : '') +" on " + requiredLocation); @@ -124,7 +126,7 @@ export async function runRemote(params: runParams, root_path: Path.File, options ) ` // console.log(""); - // logger.error(container) + logger.error(container) // observe container status and exit From 5f17751e587fbd880ceaebdd8ebd9cc2eb19e63b Mon Sep 17 00:00:00 2001 From: benStre Date: Sat, 2 Mar 2024 00:40:33 +0100 Subject: [PATCH 19/56] remove debug --- src/runners/run-remote.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/runners/run-remote.ts b/src/runners/run-remote.ts index 924938ded..9bf63aa40 100644 --- a/src/runners/run-remote.ts +++ b/src/runners/run-remote.ts @@ -90,8 +90,6 @@ export async function runRemote(params: runParams, root_path: Path.File, options try { let loaded = false; - await Datex.MessageLogger.enable() - setTimeout(()=>{ if (!loaded) { logger.error('❌ Timeout ' + stageEndpoint + (Object.keys(customDomains).length ? ` (${Object.keys(customDomains).map(domain=>`https://${domain}`).join(", ")})` : '') +" on " + requiredLocation); @@ -126,8 +124,6 @@ export async function runRemote(params: runParams, root_path: Path.File, options ) ` // console.log(""); - logger.error(container) - // observe container status and exit const handler = async (status: ContainerStatus) => { From 32a0cf19d4fceeac680d57cc5c2056f2408780f5 Mon Sep 17 00:00:00 2001 From: benStre Date: Sat, 2 Mar 2024 01:02:48 +0100 Subject: [PATCH 20/56] fix env variable checking --- src/app/config-files.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/config-files.ts b/src/app/config-files.ts index 123e76afb..e3ac124bf 100644 --- a/src/app/config-files.ts +++ b/src/app/config-files.ts @@ -22,7 +22,7 @@ export async function applyPlugins(plugins: AppPlugin[], rootPath:Path, appOptio if (!config_path) throw "Could not find an app.dx or app.json config file in " + new Path(rootPath).normal_pathname // handle plugins (only if in dev environment, not on host, TODO: better solution) - if (plugins?.length && !Deno.env.has("UIX_HOST_ENDPOINT")) { + if (plugins?.length && !Deno.env.get("UIX_HOST_ENDPOINT")) { const pluginData = await datex.get>(config_path, undefined, undefined, plugins.map(p=>p.name)); for (const plugin of plugins) { if (pluginData[plugin.name]) { From 16cb5e240163f366042dfabe2b27d0fc7b915853 Mon Sep 17 00:00:00 2001 From: benStre Date: Sat, 2 Mar 2024 01:34:02 +0100 Subject: [PATCH 21/56] fix swc segfault --- src/server/transpiler.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/server/transpiler.ts b/src/server/transpiler.ts index 49bb6ecde..cabdb4479 100644 --- a/src/server/transpiler.ts +++ b/src/server/transpiler.ts @@ -545,9 +545,8 @@ export class Transpiler { if (!valid) throw new Error("the typescript file cannot be transpiled - not a valid file extension"); - return app.options?.experimentalFeatures.includes('embedded-reactivity') ? - this.transpileToJSSWC(ts_dist_path, true): - this.transpileToJSSWC(ts_dist_path, false) + // return this.transpileToJSDenoEmit(ts_dist_path) + return this.transpileToJSSWC(ts_dist_path, app.options?.experimentalFeatures.includes('embedded-reactivity')); } private async transpileToJSDenoEmit(ts_dist_path:Path.File) { @@ -565,7 +564,11 @@ export class Transpiler { inlineSources: !!this.#options.sourceMap, ...jsxOptions }); - if (transpiled != undefined) await Deno.writeTextFile(js_dist_path.normal_pathname, transpiled); + if (transpiled != undefined) await Deno.writeTextFile(js_dist_path.normal_pathname, + this.#options.minifyJS ? + await this.minifyJS(transpiled) : + transpiled + ); else throw "unknown error" } catch (e) { @@ -576,7 +579,8 @@ export class Transpiler { } private async transpileToJSSWC(ts_dist_path: Path.File, useJusix = false) { - const {transformSync} = await import("npm:@swc/core@^1.4.2"); + // TODO: investigate/bug report: later versions lead to Segfault in docker containers with deno 1.41 + const {transformSync} = await import("npm:@swc/core@1.3.100"); const experimentalPlugins = useJusix ? { plugins: [ From 073722186052149ba377ca2d05e21155564d18ab Mon Sep 17 00:00:00 2001 From: benStre Date: Sat, 2 Mar 2024 13:26:53 +0100 Subject: [PATCH 22/56] minor fixes --- src/html/html-provider.ts | 2 +- src/hydration/partial-hydration.ts | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/html/html-provider.ts b/src/html/html-provider.ts index 7b1d98b59..da9e408a3 100644 --- a/src/html/html-provider.ts +++ b/src/html/html-provider.ts @@ -41,7 +41,7 @@ export class HTMLProvider { resolveForBackend(path:string|URL):string { path = path.toString(); if (path.startsWith("uix://")) path = new Path("." + path.replace("uix:///@uix/src",""), this.base_path).toString(); - return import.meta.resolve(path) + return Path.pathIsURL(path) ? path.toString() : import.meta.resolve(path) } getRelativeImportMap() { diff --git a/src/hydration/partial-hydration.ts b/src/hydration/partial-hydration.ts index 2e573c30b..65c897c06 100644 --- a/src/hydration/partial-hydration.ts +++ b/src/hydration/partial-hydration.ts @@ -1,11 +1,9 @@ -import { constructor } from "datex-core-legacy/js_adapter/legacy_decorators.ts"; import type { Element, Node } from "../uix-dom/dom/mod.ts"; @sync("uix:PartialHydration") export class PartialHydration { @property nodes!: Node[] - constructor(root:Element){} - @constructor construct(root: Element) { + construct(root: Element) { // this.nodes = getLiveNodes(root); } From c78ce26eeea8f8687e7dc5b3e24f2d947a3f26bc Mon Sep 17 00:00:00 2001 From: benStre Date: Sat, 2 Mar 2024 13:30:01 +0100 Subject: [PATCH 23/56] refactoring --- src/components/Component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Component.ts b/src/components/Component.ts index eb913df4c..66e8a1666 100644 --- a/src/components/Component.ts +++ b/src/components/Component.ts @@ -569,10 +569,11 @@ export abstract class ComponentclassType).construct(this, [], true, true); } From 0e42599fe561a1b6aaee12596168ceafefe488f9 Mon Sep 17 00:00:00 2001 From: benStre Date: Sat, 2 Mar 2024 13:48:03 +0100 Subject: [PATCH 24/56] add swc fixes --- src/server/transpiler.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/server/transpiler.ts b/src/server/transpiler.ts index cabdb4479..1cdfc03c3 100644 --- a/src/server/transpiler.ts +++ b/src/server/transpiler.ts @@ -616,8 +616,8 @@ export class Transpiler { if (transpiled != undefined) { await Deno.writeTextFile(js_dist_path.normal_pathname, this.#options.minifyJS ? - await this.minifyJS(transpiled) : - transpiled + await this.minifyJS(this.applySWCFixes(transpiled)) : + this.applySWCFixes(transpiled) ); } else throw "unknown error" @@ -630,6 +630,12 @@ export class Transpiler { return js_dist_path; } + private applySWCFixes(source: string) { + // fix computedKey + return source + .replace(/^_computedKey = /gm, 'let _computedKey = ') + } + private async minifyJS(source: string) { const {minify} = await import("npm:terser"); const minifiedSource = await minify(source, { From f89c7209881341f2b6f535f9beb9cb2548dc08e8 Mon Sep 17 00:00:00 2001 From: benStre Date: Sat, 2 Mar 2024 13:51:19 +0100 Subject: [PATCH 25/56] add swc fixes --- src/server/transpiler.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/server/transpiler.ts b/src/server/transpiler.ts index 1cdfc03c3..dc402e74f 100644 --- a/src/server/transpiler.ts +++ b/src/server/transpiler.ts @@ -632,8 +632,7 @@ export class Transpiler { private applySWCFixes(source: string) { // fix computedKey - return source - .replace(/^_computedKey = /gm, 'let _computedKey = ') + if (!source.includes("var _computedKey")) source = source.replace(/^_computedKey = /gm, 'var _computedKey = ') } private async minifyJS(source: string) { From 37a92d2c587a52e41de3ccd7c1d01e8c49423da5 Mon Sep 17 00:00:00 2001 From: benStre Date: Sat, 2 Mar 2024 13:52:53 +0100 Subject: [PATCH 26/56] add swc fixes --- src/server/transpiler.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/server/transpiler.ts b/src/server/transpiler.ts index dc402e74f..ec73041f7 100644 --- a/src/server/transpiler.ts +++ b/src/server/transpiler.ts @@ -633,6 +633,7 @@ export class Transpiler { private applySWCFixes(source: string) { // fix computedKey if (!source.includes("var _computedKey")) source = source.replace(/^_computedKey = /gm, 'var _computedKey = ') + return source; } private async minifyJS(source: string) { From 0531df0cd0e3c9235f9765909d280b260b345251 Mon Sep 17 00:00:00 2001 From: benStre Date: Sat, 2 Mar 2024 13:56:35 +0100 Subject: [PATCH 27/56] add swc fixes --- src/server/transpiler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/transpiler.ts b/src/server/transpiler.ts index ec73041f7..7c51dc6d9 100644 --- a/src/server/transpiler.ts +++ b/src/server/transpiler.ts @@ -632,7 +632,7 @@ export class Transpiler { private applySWCFixes(source: string) { // fix computedKey - if (!source.includes("var _computedKey")) source = source.replace(/^_computedKey = /gm, 'var _computedKey = ') + if (!source.match(/^var _computedKey\d*/gm)) source = source.replace(/^(_computedKey\d*) = /gm, 'var $1 = ') return source; } From 3889dd6287356ec17c2bb0c95a12310791c46d6c Mon Sep 17 00:00:00 2001 From: benStre Date: Sat, 2 Mar 2024 14:05:01 +0100 Subject: [PATCH 28/56] add swc fixes --- src/server/transpiler.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server/transpiler.ts b/src/server/transpiler.ts index 7c51dc6d9..9899af565 100644 --- a/src/server/transpiler.ts +++ b/src/server/transpiler.ts @@ -580,7 +580,7 @@ export class Transpiler { private async transpileToJSSWC(ts_dist_path: Path.File, useJusix = false) { // TODO: investigate/bug report: later versions lead to Segfault in docker containers with deno 1.41 - const {transformSync} = await import("npm:@swc/core@1.3.100"); + const {transformSync} = await import("npm:@swc/core@1.3.107"); const experimentalPlugins = useJusix ? { plugins: [ @@ -632,7 +632,7 @@ export class Transpiler { private applySWCFixes(source: string) { // fix computedKey - if (!source.match(/^var _computedKey\d*/gm)) source = source.replace(/^(_computedKey\d*) = /gm, 'var $1 = ') + // if (!source.match(/^var _computedKey\d*/gm)) source = source.replace(/^(_computedKey\d*) = /gm, 'var $1 = ') return source; } From 604dcc1bff3699028677d667d3a14e96d74c14ba Mon Sep 17 00:00:00 2001 From: benStre Date: Sat, 2 Mar 2024 14:12:56 +0100 Subject: [PATCH 29/56] test swc version --- src/server/transpiler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/transpiler.ts b/src/server/transpiler.ts index 9899af565..5da7488bb 100644 --- a/src/server/transpiler.ts +++ b/src/server/transpiler.ts @@ -580,7 +580,7 @@ export class Transpiler { private async transpileToJSSWC(ts_dist_path: Path.File, useJusix = false) { // TODO: investigate/bug report: later versions lead to Segfault in docker containers with deno 1.41 - const {transformSync} = await import("npm:@swc/core@1.3.107"); + const {transformSync} = await import("npm:@swc/core@1.3.106"); const experimentalPlugins = useJusix ? { plugins: [ From c0f2ac290228ad88bb4998696b64de755cf73255 Mon Sep 17 00:00:00 2001 From: benStre Date: Sat, 2 Mar 2024 14:14:47 +0100 Subject: [PATCH 30/56] test swc version --- src/server/transpiler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/transpiler.ts b/src/server/transpiler.ts index 5da7488bb..b868b8c2b 100644 --- a/src/server/transpiler.ts +++ b/src/server/transpiler.ts @@ -580,7 +580,7 @@ export class Transpiler { private async transpileToJSSWC(ts_dist_path: Path.File, useJusix = false) { // TODO: investigate/bug report: later versions lead to Segfault in docker containers with deno 1.41 - const {transformSync} = await import("npm:@swc/core@1.3.106"); + const {transformSync} = await import("npm:@swc/core@1.3.101"); const experimentalPlugins = useJusix ? { plugins: [ From 6556a683b5acdfd73828c9f08d5348d71f5d73e7 Mon Sep 17 00:00:00 2001 From: benStre Date: Sat, 2 Mar 2024 14:17:08 +0100 Subject: [PATCH 31/56] test swc version --- src/server/transpiler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/transpiler.ts b/src/server/transpiler.ts index b868b8c2b..61ea2474c 100644 --- a/src/server/transpiler.ts +++ b/src/server/transpiler.ts @@ -580,7 +580,7 @@ export class Transpiler { private async transpileToJSSWC(ts_dist_path: Path.File, useJusix = false) { // TODO: investigate/bug report: later versions lead to Segfault in docker containers with deno 1.41 - const {transformSync} = await import("npm:@swc/core@1.3.101"); + const {transformSync} = await import("npm:@swc/core@1.3.100"); const experimentalPlugins = useJusix ? { plugins: [ From d23064e36f5850a6d1b12023ade484bbc4b3f0e1 Mon Sep 17 00:00:00 2001 From: benStre Date: Sat, 2 Mar 2024 14:21:40 +0100 Subject: [PATCH 32/56] test swc version --- src/server/transpiler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/transpiler.ts b/src/server/transpiler.ts index 61ea2474c..b868b8c2b 100644 --- a/src/server/transpiler.ts +++ b/src/server/transpiler.ts @@ -580,7 +580,7 @@ export class Transpiler { private async transpileToJSSWC(ts_dist_path: Path.File, useJusix = false) { // TODO: investigate/bug report: later versions lead to Segfault in docker containers with deno 1.41 - const {transformSync} = await import("npm:@swc/core@1.3.100"); + const {transformSync} = await import("npm:@swc/core@1.3.101"); const experimentalPlugins = useJusix ? { plugins: [ From 38e2613833005637ab3b06f2fe22883b6bae25e6 Mon Sep 17 00:00:00 2001 From: benStre Date: Sat, 2 Mar 2024 14:23:04 +0100 Subject: [PATCH 33/56] test swc version --- src/server/transpiler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/transpiler.ts b/src/server/transpiler.ts index b868b8c2b..61ea2474c 100644 --- a/src/server/transpiler.ts +++ b/src/server/transpiler.ts @@ -580,7 +580,7 @@ export class Transpiler { private async transpileToJSSWC(ts_dist_path: Path.File, useJusix = false) { // TODO: investigate/bug report: later versions lead to Segfault in docker containers with deno 1.41 - const {transformSync} = await import("npm:@swc/core@1.3.101"); + const {transformSync} = await import("npm:@swc/core@1.3.100"); const experimentalPlugins = useJusix ? { plugins: [ From f2efec50fd044deb8bfc6a365ac2dd1b4fd409d8 Mon Sep 17 00:00:00 2001 From: benStre Date: Sat, 2 Mar 2024 14:29:21 +0100 Subject: [PATCH 34/56] test swc version --- src/server/transpiler.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/server/transpiler.ts b/src/server/transpiler.ts index 61ea2474c..5f9bf59b5 100644 --- a/src/server/transpiler.ts +++ b/src/server/transpiler.ts @@ -10,7 +10,8 @@ const copy = client_type === "deno" ? (await import("https://deno.land/std@0.160 const walk = client_type === "deno" ? (await import("https://deno.land/std@0.177.0/fs/mod.ts")).walk : null; const sass = client_type === "deno" ? (await import("https://deno.land/x/denosass@1.0.6/mod.ts")).default : null; - +// TODO: investigate/bug report: later versions lead to Segfault in docker containers with deno 1.41 +const {transformSync} = client_type === "deno" ? await import("npm:@swc/core@1.3.101") : {transformSync:undefined}; const logger = new Datex.Logger("transpiler"); @@ -579,8 +580,6 @@ export class Transpiler { } private async transpileToJSSWC(ts_dist_path: Path.File, useJusix = false) { - // TODO: investigate/bug report: later versions lead to Segfault in docker containers with deno 1.41 - const {transformSync} = await import("npm:@swc/core@1.3.100"); const experimentalPlugins = useJusix ? { plugins: [ From 8d34090d98c716eb90224137512862304764f642 Mon Sep 17 00:00:00 2001 From: benStre Date: Sat, 2 Mar 2024 14:32:14 +0100 Subject: [PATCH 35/56] test swc version --- src/server/transpiler.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/server/transpiler.ts b/src/server/transpiler.ts index 5f9bf59b5..43e18f755 100644 --- a/src/server/transpiler.ts +++ b/src/server/transpiler.ts @@ -10,8 +10,11 @@ const copy = client_type === "deno" ? (await import("https://deno.land/std@0.160 const walk = client_type === "deno" ? (await import("https://deno.land/std@0.177.0/fs/mod.ts")).walk : null; const sass = client_type === "deno" ? (await import("https://deno.land/x/denosass@1.0.6/mod.ts")).default : null; +import { transformSync } from "npm:@swc/core@1.3.101"; + +console.log("imported everything in transpiler") // TODO: investigate/bug report: later versions lead to Segfault in docker containers with deno 1.41 -const {transformSync} = client_type === "deno" ? await import("npm:@swc/core@1.3.101") : {transformSync:undefined}; +// const {transformSync} = client_type === "deno" ? await import("npm:@swc/core@1.3.101") : {transformSync:undefined}; const logger = new Datex.Logger("transpiler"); From 8c728b37877d4403ee216a60d4ab1e5bd16ee1d0 Mon Sep 17 00:00:00 2001 From: benStre Date: Sat, 2 Mar 2024 14:46:22 +0100 Subject: [PATCH 36/56] test swc version --- src/server/transpiler.ts | 14 ++++++++------ src/server/ts-import-resolver.ts | 5 +++-- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/server/transpiler.ts b/src/server/transpiler.ts index 43e18f755..7ca6f174f 100644 --- a/src/server/transpiler.ts +++ b/src/server/transpiler.ts @@ -10,11 +10,7 @@ const copy = client_type === "deno" ? (await import("https://deno.land/std@0.160 const walk = client_type === "deno" ? (await import("https://deno.land/std@0.177.0/fs/mod.ts")).walk : null; const sass = client_type === "deno" ? (await import("https://deno.land/x/denosass@1.0.6/mod.ts")).default : null; -import { transformSync } from "npm:@swc/core@1.3.101"; -console.log("imported everything in transpiler") -// TODO: investigate/bug report: later versions lead to Segfault in docker containers with deno 1.41 -// const {transformSync} = client_type === "deno" ? await import("npm:@swc/core@1.3.101") : {transformSync:undefined}; const logger = new Datex.Logger("transpiler"); @@ -583,7 +579,8 @@ export class Transpiler { } private async transpileToJSSWC(ts_dist_path: Path.File, useJusix = false) { - + // TODO: investigate/bug report: later versions lead to Segfault in docker containers with deno 1.41 + const {transformSync} = await import("npm:@swc/core@^1.4.2"); const experimentalPlugins = useJusix ? { plugins: [ ["jusix", {}] @@ -592,7 +589,10 @@ export class Transpiler { const js_dist_path = this.getFileWithMappedExtension(ts_dist_path); try { - const transpiled = transformSync!(await Deno.readTextFile(ts_dist_path.normal_pathname), { + console.log("fileread") + const file = await Deno.readTextFile(ts_dist_path.normal_pathname) + console.log("transformstart") + const transpiled = transformSync!(file, { jsc: { parser: { tsx: !!ts_dist_path.hasFileExtension("tsx"), @@ -615,6 +615,8 @@ export class Transpiler { experimental: experimentalPlugins } }).code + console.log("tansformend") + if (transpiled != undefined) { await Deno.writeTextFile(js_dist_path.normal_pathname, this.#options.minifyJS ? diff --git a/src/server/ts-import-resolver.ts b/src/server/ts-import-resolver.ts index 367498dcf..2ea3f6161 100644 --- a/src/server/ts-import-resolver.ts +++ b/src/server/ts-import-resolver.ts @@ -139,8 +139,9 @@ export class TypescriptImportResolver { const rel_import_path = is_prefix ? null : this.resolveImportSpecifier(specifier) const abs_import_path = is_prefix ? null : new Path(rel_import_path!, reference_path); - // workaround: ignore 'node:x' paths - if (abs_import_path?.toString().startsWith("node:")) return match; + // workaround: ignore 'node:x'/'npm:x' paths + const pathString = abs_import_path?.toString() + if (pathString?.startsWith("node:") || pathString?.startsWith("npm:")) return match; // already resolved web path if (abs_import_path?.is_web) return match; From bb0c5a89060a2b8f012cf36927deb3e26e7c1e75 Mon Sep 17 00:00:00 2001 From: benStre Date: Sat, 2 Mar 2024 14:49:12 +0100 Subject: [PATCH 37/56] test swc version --- src/server/transpiler.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/server/transpiler.ts b/src/server/transpiler.ts index 7ca6f174f..ed99fe959 100644 --- a/src/server/transpiler.ts +++ b/src/server/transpiler.ts @@ -589,9 +589,9 @@ export class Transpiler { const js_dist_path = this.getFileWithMappedExtension(ts_dist_path); try { - console.log("fileread") + console.log("fileread " + ts_dist_path.normal_pathname) const file = await Deno.readTextFile(ts_dist_path.normal_pathname) - console.log("transformstart") + console.log("transformstart " + ts_dist_path.normal_pathname) const transpiled = transformSync!(file, { jsc: { parser: { @@ -615,7 +615,7 @@ export class Transpiler { experimental: experimentalPlugins } }).code - console.log("tansformend") + console.log("tansformend " + ts_dist_path.normal_pathname) if (transpiled != undefined) { await Deno.writeTextFile(js_dist_path.normal_pathname, From 7f19161e93ae79bae72bdf642b516448766046fe Mon Sep 17 00:00:00 2001 From: benStre Date: Sat, 2 Mar 2024 14:56:40 +0100 Subject: [PATCH 38/56] test swc version --- src/server/transpiler.ts | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/server/transpiler.ts b/src/server/transpiler.ts index ed99fe959..b78e0824c 100644 --- a/src/server/transpiler.ts +++ b/src/server/transpiler.ts @@ -580,7 +580,7 @@ export class Transpiler { private async transpileToJSSWC(ts_dist_path: Path.File, useJusix = false) { // TODO: investigate/bug report: later versions lead to Segfault in docker containers with deno 1.41 - const {transformSync} = await import("npm:@swc/core@^1.4.2"); + const {transform} = await import("npm:@swc/core@^1.4.2"); const experimentalPlugins = useJusix ? { plugins: [ ["jusix", {}] @@ -589,10 +589,8 @@ export class Transpiler { const js_dist_path = this.getFileWithMappedExtension(ts_dist_path); try { - console.log("fileread " + ts_dist_path.normal_pathname) const file = await Deno.readTextFile(ts_dist_path.normal_pathname) - console.log("transformstart " + ts_dist_path.normal_pathname) - const transpiled = transformSync!(file, { + const transpiled = (await transform(file, { jsc: { parser: { tsx: !!ts_dist_path.hasFileExtension("tsx"), @@ -614,8 +612,7 @@ export class Transpiler { externalHelpers: false, experimental: experimentalPlugins } - }).code - console.log("tansformend " + ts_dist_path.normal_pathname) + })).code if (transpiled != undefined) { await Deno.writeTextFile(js_dist_path.normal_pathname, @@ -634,6 +631,23 @@ export class Transpiler { return js_dist_path; } + /** only transforms one at a time */ + // private transformQueue = new Mapvoid>() + // private transformQueued(source: string) { + // return new Promise((resolve)=>{ + // this.transformQueue.set(source, resolve); + // // transform next + // if (this.transformQueue.size == 1) { + // // iterate over copy of map, because transformQueue might be modified during iteration + // for (const [source, resolve] of [...this.transformQueue]) { + // this.transformQueue.delete(source); + // resolve(this.transform(source)); + // break; + // } + // } + // }) + // } + private applySWCFixes(source: string) { // fix computedKey // if (!source.match(/^var _computedKey\d*/gm)) source = source.replace(/^(_computedKey\d*) = /gm, 'var $1 = ') From 65c727f9066a9085b66ff2ddfc3d7832a711e325 Mon Sep 17 00:00:00 2001 From: benStre Date: Sat, 2 Mar 2024 15:01:28 +0100 Subject: [PATCH 39/56] test swc version --- src/server/transpiler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/transpiler.ts b/src/server/transpiler.ts index b78e0824c..3b616dc2d 100644 --- a/src/server/transpiler.ts +++ b/src/server/transpiler.ts @@ -170,7 +170,7 @@ export class Transpiler { */ public async transpileDir(dir: Path.File) { const promises = [] - const maxParallel = 100; + const maxParallel = 1; for await (const e of walk!(dir, {includeDirs: false, exts: this.#transpile_exts.map(x=>'.'+x)})) { promises.push(this.updateFile(new Path(e.path), true, true)); if (promises.length >= maxParallel) { From 361af119590712a4a1312c60048d41773f17721c Mon Sep 17 00:00:00 2001 From: benStre Date: Sat, 2 Mar 2024 15:05:53 +0100 Subject: [PATCH 40/56] test swc version --- src/server/transpiler.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/server/transpiler.ts b/src/server/transpiler.ts index 3b616dc2d..7578e7a0f 100644 --- a/src/server/transpiler.ts +++ b/src/server/transpiler.ts @@ -589,7 +589,9 @@ export class Transpiler { const js_dist_path = this.getFileWithMappedExtension(ts_dist_path); try { + console.log("reading file: " + ts_dist_path.normal_pathname) const file = await Deno.readTextFile(ts_dist_path.normal_pathname) + console.log("transpiling: " + ts_dist_path.normal_pathname) const transpiled = (await transform(file, { jsc: { parser: { @@ -613,6 +615,7 @@ export class Transpiler { experimental: experimentalPlugins } })).code + console.log("transpiled: " + ts_dist_path.normal_pathname) if (transpiled != undefined) { await Deno.writeTextFile(js_dist_path.normal_pathname, From a2ea7ff518029be7fe12a8b15eb36b5461b98050 Mon Sep 17 00:00:00 2001 From: benStre Date: Sat, 2 Mar 2024 15:17:33 +0100 Subject: [PATCH 41/56] debug --- src/server/transpiler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/transpiler.ts b/src/server/transpiler.ts index 7578e7a0f..f59d610b0 100644 --- a/src/server/transpiler.ts +++ b/src/server/transpiler.ts @@ -589,7 +589,7 @@ export class Transpiler { const js_dist_path = this.getFileWithMappedExtension(ts_dist_path); try { - console.log("reading file: " + ts_dist_path.normal_pathname) + console.log("reading file: " + ts_dist_path.normal_pathname, {scope:this.#src_dir.toString(), tmp:this.tmp_dir.normal_pathname, dist:this.dist_dir.normal_pathname}) const file = await Deno.readTextFile(ts_dist_path.normal_pathname) console.log("transpiling: " + ts_dist_path.normal_pathname) const transpiled = (await transform(file, { From eaf569f0301d4968011ff0f916334611df070b09 Mon Sep 17 00:00:00 2001 From: benStre Date: Sat, 2 Mar 2024 15:19:39 +0100 Subject: [PATCH 42/56] debug --- src/server/transpiler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/transpiler.ts b/src/server/transpiler.ts index f59d610b0..ee350133f 100644 --- a/src/server/transpiler.ts +++ b/src/server/transpiler.ts @@ -589,7 +589,7 @@ export class Transpiler { const js_dist_path = this.getFileWithMappedExtension(ts_dist_path); try { - console.log("reading file: " + ts_dist_path.normal_pathname, {scope:this.#src_dir.toString(), tmp:this.tmp_dir.normal_pathname, dist:this.dist_dir.normal_pathname}) + console.log("reading file: " + this.#src_dir.toString() + " - " + ts_dist_path.normal_pathname) const file = await Deno.readTextFile(ts_dist_path.normal_pathname) console.log("transpiling: " + ts_dist_path.normal_pathname) const transpiled = (await transform(file, { From f3027995f062c3e0357685ad940c1e77ed133a1f Mon Sep 17 00:00:00 2001 From: benStre Date: Sat, 2 Mar 2024 15:31:20 +0100 Subject: [PATCH 43/56] debug --- src/server/transpiler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/transpiler.ts b/src/server/transpiler.ts index ee350133f..0823ed609 100644 --- a/src/server/transpiler.ts +++ b/src/server/transpiler.ts @@ -580,7 +580,7 @@ export class Transpiler { private async transpileToJSSWC(ts_dist_path: Path.File, useJusix = false) { // TODO: investigate/bug report: later versions lead to Segfault in docker containers with deno 1.41 - const {transform} = await import("npm:@swc/core@^1.4.2"); + const {transformSync} = await import("npm:@swc/core@1.3.100"); const experimentalPlugins = useJusix ? { plugins: [ ["jusix", {}] From 539ada275dca06c0cb4f2b0324d176665f4b7d63 Mon Sep 17 00:00:00 2001 From: benStre Date: Sat, 2 Mar 2024 15:34:26 +0100 Subject: [PATCH 44/56] debug --- src/server/transpiler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/transpiler.ts b/src/server/transpiler.ts index 0823ed609..2710d9643 100644 --- a/src/server/transpiler.ts +++ b/src/server/transpiler.ts @@ -580,7 +580,7 @@ export class Transpiler { private async transpileToJSSWC(ts_dist_path: Path.File, useJusix = false) { // TODO: investigate/bug report: later versions lead to Segfault in docker containers with deno 1.41 - const {transformSync} = await import("npm:@swc/core@1.3.100"); + const {transformSync} = await import("npm:@swc/core@^1.3.101"); const experimentalPlugins = useJusix ? { plugins: [ ["jusix", {}] From 1c002a48be6f27d827fcb870d651eea920220037 Mon Sep 17 00:00:00 2001 From: benStre Date: Sat, 2 Mar 2024 15:37:21 +0100 Subject: [PATCH 45/56] debug --- src/server/transpiler.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server/transpiler.ts b/src/server/transpiler.ts index 2710d9643..1f5325240 100644 --- a/src/server/transpiler.ts +++ b/src/server/transpiler.ts @@ -592,7 +592,7 @@ export class Transpiler { console.log("reading file: " + this.#src_dir.toString() + " - " + ts_dist_path.normal_pathname) const file = await Deno.readTextFile(ts_dist_path.normal_pathname) console.log("transpiling: " + ts_dist_path.normal_pathname) - const transpiled = (await transform(file, { + const transpiled = transformSync(file, { jsc: { parser: { tsx: !!ts_dist_path.hasFileExtension("tsx"), @@ -614,7 +614,7 @@ export class Transpiler { externalHelpers: false, experimental: experimentalPlugins } - })).code + }).code console.log("transpiled: " + ts_dist_path.normal_pathname) if (transpiled != undefined) { From 68eafe9cb7323ec6f95bb592b0ca27824ae21529 Mon Sep 17 00:00:00 2001 From: benStre Date: Sat, 2 Mar 2024 15:37:28 +0100 Subject: [PATCH 46/56] debug --- src/server/transpiler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/transpiler.ts b/src/server/transpiler.ts index 1f5325240..6a978acfc 100644 --- a/src/server/transpiler.ts +++ b/src/server/transpiler.ts @@ -580,7 +580,7 @@ export class Transpiler { private async transpileToJSSWC(ts_dist_path: Path.File, useJusix = false) { // TODO: investigate/bug report: later versions lead to Segfault in docker containers with deno 1.41 - const {transformSync} = await import("npm:@swc/core@^1.3.101"); + const {transformSync} = await import("npm:@swc/core@1.3.101"); const experimentalPlugins = useJusix ? { plugins: [ ["jusix", {}] From b98766c935824c36a16e2aa8a293f8333bd1622a Mon Sep 17 00:00:00 2001 From: benStre Date: Sat, 2 Mar 2024 16:33:50 +0100 Subject: [PATCH 47/56] debug --- src/server/transpiler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/transpiler.ts b/src/server/transpiler.ts index 6a978acfc..6a147c354 100644 --- a/src/server/transpiler.ts +++ b/src/server/transpiler.ts @@ -580,7 +580,7 @@ export class Transpiler { private async transpileToJSSWC(ts_dist_path: Path.File, useJusix = false) { // TODO: investigate/bug report: later versions lead to Segfault in docker containers with deno 1.41 - const {transformSync} = await import("npm:@swc/core@1.3.101"); + const {transformSync} = await import("npm:@swc/core@1.3.100"); const experimentalPlugins = useJusix ? { plugins: [ ["jusix", {}] From 33a2c4c764dd8f48be18515ab757c570dcfb2779 Mon Sep 17 00:00:00 2001 From: benStre Date: Sat, 2 Mar 2024 16:40:01 +0100 Subject: [PATCH 48/56] debug --- src/server/transpiler.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/server/transpiler.ts b/src/server/transpiler.ts index 6a147c354..d54af554c 100644 --- a/src/server/transpiler.ts +++ b/src/server/transpiler.ts @@ -580,6 +580,8 @@ export class Transpiler { private async transpileToJSSWC(ts_dist_path: Path.File, useJusix = false) { // TODO: investigate/bug report: later versions lead to Segfault in docker containers with deno 1.41 + + await sleep(1000000); const {transformSync} = await import("npm:@swc/core@1.3.100"); const experimentalPlugins = useJusix ? { plugins: [ From 79b891db4ccb8bfa760abdf2078d77d2211fe54a Mon Sep 17 00:00:00 2001 From: benStre Date: Sat, 2 Mar 2024 17:12:10 +0100 Subject: [PATCH 49/56] revert debug --- src/server/transpiler.ts | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/src/server/transpiler.ts b/src/server/transpiler.ts index d54af554c..36858f772 100644 --- a/src/server/transpiler.ts +++ b/src/server/transpiler.ts @@ -170,7 +170,7 @@ export class Transpiler { */ public async transpileDir(dir: Path.File) { const promises = [] - const maxParallel = 1; + const maxParallel = 100; for await (const e of walk!(dir, {includeDirs: false, exts: this.#transpile_exts.map(x=>'.'+x)})) { promises.push(this.updateFile(new Path(e.path), true, true)); if (promises.length >= maxParallel) { @@ -580,9 +580,8 @@ export class Transpiler { private async transpileToJSSWC(ts_dist_path: Path.File, useJusix = false) { // TODO: investigate/bug report: later versions lead to Segfault in docker containers with deno 1.41 - - await sleep(1000000); - const {transformSync} = await import("npm:@swc/core@1.3.100"); + const {transform} = await import("npm:@swc/core@^1.4.2"); + const experimentalPlugins = useJusix ? { plugins: [ ["jusix", {}] @@ -594,7 +593,7 @@ export class Transpiler { console.log("reading file: " + this.#src_dir.toString() + " - " + ts_dist_path.normal_pathname) const file = await Deno.readTextFile(ts_dist_path.normal_pathname) console.log("transpiling: " + ts_dist_path.normal_pathname) - const transpiled = transformSync(file, { + const transpiled = (await transform(file, { jsc: { parser: { tsx: !!ts_dist_path.hasFileExtension("tsx"), @@ -616,7 +615,7 @@ export class Transpiler { externalHelpers: false, experimental: experimentalPlugins } - }).code + })).code console.log("transpiled: " + ts_dist_path.normal_pathname) if (transpiled != undefined) { @@ -636,23 +635,6 @@ export class Transpiler { return js_dist_path; } - /** only transforms one at a time */ - // private transformQueue = new Mapvoid>() - // private transformQueued(source: string) { - // return new Promise((resolve)=>{ - // this.transformQueue.set(source, resolve); - // // transform next - // if (this.transformQueue.size == 1) { - // // iterate over copy of map, because transformQueue might be modified during iteration - // for (const [source, resolve] of [...this.transformQueue]) { - // this.transformQueue.delete(source); - // resolve(this.transform(source)); - // break; - // } - // } - // }) - // } - private applySWCFixes(source: string) { // fix computedKey // if (!source.match(/^var _computedKey\d*/gm)) source = source.replace(/^(_computedKey\d*) = /gm, 'var $1 = ') From e63a54dab4891b0e02d60bfd43d0c023d68b922e Mon Sep 17 00:00:00 2001 From: benStre Date: Sat, 2 Mar 2024 17:12:52 +0100 Subject: [PATCH 50/56] remove debug logs --- src/server/transpiler.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/server/transpiler.ts b/src/server/transpiler.ts index 36858f772..5b6a0175a 100644 --- a/src/server/transpiler.ts +++ b/src/server/transpiler.ts @@ -590,9 +590,7 @@ export class Transpiler { const js_dist_path = this.getFileWithMappedExtension(ts_dist_path); try { - console.log("reading file: " + this.#src_dir.toString() + " - " + ts_dist_path.normal_pathname) const file = await Deno.readTextFile(ts_dist_path.normal_pathname) - console.log("transpiling: " + ts_dist_path.normal_pathname) const transpiled = (await transform(file, { jsc: { parser: { @@ -616,7 +614,6 @@ export class Transpiler { experimental: experimentalPlugins } })).code - console.log("transpiled: " + ts_dist_path.normal_pathname) if (transpiled != undefined) { await Deno.writeTextFile(js_dist_path.normal_pathname, From c566b167c924bea51bb8ec21e1570175559acec2 Mon Sep 17 00:00:00 2001 From: benStre Date: Sat, 2 Mar 2024 17:25:52 +0100 Subject: [PATCH 51/56] minify using swc --- src/server/transpiler.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/server/transpiler.ts b/src/server/transpiler.ts index 5b6a0175a..a9af6fa92 100644 --- a/src/server/transpiler.ts +++ b/src/server/transpiler.ts @@ -611,15 +611,20 @@ export class Transpiler { target: "esnext", keepClassNames: true, externalHelpers: false, - experimental: experimentalPlugins + experimental: experimentalPlugins, + minify: this.#options.minifyJS ? + { + module: true, + keep_classnames: true + }: + undefined } })).code if (transpiled != undefined) { - await Deno.writeTextFile(js_dist_path.normal_pathname, - this.#options.minifyJS ? - await this.minifyJS(this.applySWCFixes(transpiled)) : - this.applySWCFixes(transpiled) + await Deno.writeTextFile( + js_dist_path.normal_pathname, + this.applySWCFixes(transpiled) ); } else throw "unknown error" From 5920dec1052d5ab7b7710145066114d082010fa8 Mon Sep 17 00:00:00 2001 From: benStre Date: Sat, 2 Mar 2024 21:29:41 +0100 Subject: [PATCH 52/56] fix hydration --- src/app/frontend-manager.ts | 12 +++-- src/base/decorators.ts | 51 +++++++++++++++++++--- src/components/Component.ts | 24 ++++++---- src/standalone/get_prototype_properties.ts | 14 ++++++ 4 files changed, 83 insertions(+), 18 deletions(-) diff --git a/src/app/frontend-manager.ts b/src/app/frontend-manager.ts index 6b015f953..69e6f5541 100644 --- a/src/app/frontend-manager.ts +++ b/src/app/frontend-manager.ts @@ -827,7 +827,13 @@ if (!window.location.origin.endsWith(".unyt.app")) { if (content instanceof Blob || content instanceof Response) return [content, RenderMethod.RAW_CONTENT, status_code, openGraphData, headers]; // Markdown - if (content instanceof Datex.Markdown) return [getOuterHTML( content.getHTML(false), {includeShadowRoots:true, injectStandaloneJS:render_method!=RenderMethod.STATIC&&render_method!=RenderMethod.HYBRID, injectStandaloneComponents:render_method!=RenderMethod.STATIC&&render_method!=RenderMethod.HYBRID/*TODO: should also work with HYBRID, but cannot create standalone component class and new class later*/, allowIgnoreDatexFunctions:(render_method==RenderMethod.HYBRID||render_method==RenderMethod.PREVIEW), lang}), render_method, status_code, openGraphData, headers]; + if (content instanceof Datex.Markdown) return [getOuterHTML( content.getHTML(false), { + includeShadowRoots:true, + injectStandaloneJS:render_method!=RenderMethod.STATIC, + injectStandaloneComponents:render_method!=RenderMethod.STATIC, + allowIgnoreDatexFunctions:(render_method==RenderMethod.HYBRID||render_method==RenderMethod.PREVIEW), + lang + }), render_method, status_code, openGraphData, headers]; // convert content to valid HTML string if (content instanceof Element || content instanceof DocumentFragment) { @@ -836,8 +842,8 @@ if (!window.location.origin.endsWith(".unyt.app")) { content as Element, { includeShadowRoots:true, - injectStandaloneJS:render_method!=RenderMethod.STATIC&&render_method!=RenderMethod.HYBRID, - injectStandaloneComponents:render_method!=RenderMethod.STATIC&&render_method!=RenderMethod.HYBRID, + injectStandaloneJS:render_method!=RenderMethod.STATIC, + injectStandaloneComponents:render_method!=RenderMethod.STATIC, allowIgnoreDatexFunctions:(render_method==RenderMethod.HYBRID||render_method==RenderMethod.PREVIEW), lang, requiredPointers diff --git a/src/base/decorators.ts b/src/base/decorators.ts index b21a356d5..f33bda886 100644 --- a/src/base/decorators.ts +++ b/src/base/decorators.ts @@ -1,11 +1,11 @@ -import { Datex } from "datex-core-legacy/mod.ts"; +import { Datex, handleClassDecoratorWithOptionalArgs } from "datex-core-legacy/mod.ts"; import { handleClassDecoratorWithArgs, handleClassFieldDecoratorWithOptionalArgs } from "datex-core-legacy/js_adapter/decorators.ts"; import { getCloneKeys, Component } from "../components/Component.ts"; import { getCallerFile } from "datex-core-legacy/utils/caller_metadata.ts"; import { domContext, domUtils } from "../app/dom-context.ts"; import { getTransformWrapper } from "../uix-dom/datex-bindings/transform-wrapper.ts"; import { client_type } from "datex-core-legacy/utils/constants.ts"; -import { Decorators } from "datex-core-legacy/datex_all.ts"; +import { Class, Decorators } from "datex-core-legacy/datex_all.ts"; /** * @defaultOptions to define component default options @@ -58,6 +58,8 @@ export function initDefaultOptions>(url:string // create template class for component const new_class = Datex.createTemplateClass(componentClass, datex_type, true) as ComponentClass; + // Object.defineProperty(componentClass, Datex.DX_TYPE, {set:(v)=>{console.log("set",v,new Error().stack)}, get:()=>datex_type}); + componentClass[Datex.DX_TYPE] = datex_type; const html_interface = Datex.Type.get('html').interface_config!; @@ -74,7 +76,8 @@ export function initDefaultOptions>(url:string const pointer = Datex.Pointer.getByValue(value) for (const key of datex_type.visible_children){ if (!html_serialized.p) html_serialized.p = {}; - html_serialized.p[key] = pointer?.shadow_object ? pointer.shadow_object[key]/*keep references*/ : value[key]; + + html_serialized.p[key] = pointer?.shadow_object ? pointer.shadow_object[key]/*keep references*/ : value[key]; } return html_serialized; @@ -176,9 +179,45 @@ export function include(value: undefined|string, context?: ClassFieldDecoratorCo ) } -/** \@frontend decorator to declare methods that always run on the frontend */ -export function frontend(_value: undefined|((...args:any[])=>any), context: ClassFieldDecoratorContext|ClassMethodDecoratorContext) { - Decorators.setMetadata(context, STANDALONE_PROPS, context.name); +type frontendClassDecoratorOptions = { + inheritedFields?: string[] +} + +/** + * \@frontend + * Exposes all methods of the class to the frontend context. + */ +export function frontend(value: Class, context: ClassDecoratorContext): void +/** + * \@frontend + * Exposes all methods of the class to the frontend context. + * Optionally inherited properties and methods that should be exposed to the frontend can be specified. + */ +export function frontend(options: frontendClassDecoratorOptions): (value: Class, context: ClassDecoratorContext) => void +/** + * \@frontend + * Exposes the property or method to the frontend context. + */ +export function frontend(_value: undefined|((...args:any[])=>any), context: ClassFieldDecoratorContext|ClassMethodDecoratorContext): void +export function frontend(_value: undefined|Class|((...args:any[])=>any)|frontendClassDecoratorOptions, context?: ClassDecoratorContext|ClassFieldDecoratorContext|ClassMethodDecoratorContext): any { + // class decorator + if (!context || context.kind == "class") { + return handleClassDecoratorWithOptionalArgs( + [_value as frontendClassDecoratorOptions], + _value as Class, + context as ClassDecoratorContext, + ([options], value, context) => { + for (const prop of [...Object.getOwnPropertyNames(value.prototype), ...options.inheritedFields??[]]) { + if (prop == "constructor") continue; + Decorators.setMetadata({...(context as unknown as ClassFieldDecoratorContext), kind:"field",name:prop}, STANDALONE_PROPS, prop); + } + } + ) + } + // field/method decorator + else { + Decorators.setMetadata(context!, STANDALONE_PROPS, context!.name); + } } diff --git a/src/components/Component.ts b/src/components/Component.ts index 66e8a1666..5528418c6 100644 --- a/src/components/Component.ts +++ b/src/components/Component.ts @@ -23,6 +23,7 @@ import { convertToWebPath } from "../app/convert-to-web-path.ts"; import { client_type } from "datex-core-legacy/utils/constants.ts"; import { app } from "../app/app.ts"; import { fileExists } from "../utils/files.ts"; +import { DISPOSE_BOUND_PROTOTYPE } from "../standalone/get_prototype_properties.ts"; export type propInit = {datex?:boolean}; export type standaloneContentPropertyData = {type:'id'|'content'|'layout'|'child',id:string}; @@ -804,26 +805,31 @@ export abstract class Componentthis.constructor).stylesheets??[]) loaders.push(this.addStyleSheet(url)); + let standaloneOnDisplayWasTriggered = false + if ((this as any)[DISPOSE_BOUND_PROTOTYPE]) { + standaloneOnDisplayWasTriggered = (this as any)[DISPOSE_BOUND_PROTOTYPE](); + } + + this.onCreateLayout?.(); // custom layout extensions + + // @id, @content, @layout + this.handleIdProps(constructed); + + // @standlone props only relevant for backend + if (UIX.context == "backend") this.loadStandaloneProps(); + Datex.Pointer.onPointerForValueCreated(this, () => { const pointer = Datex.Pointer.getByValue(this)! if (!this.hasAttribute("uix-ptr")) this.setAttribute("uix-ptr", pointer.id); if (this.is_skeleton && UIX.context == "frontend") { this.logger.debug("hybrid initialization") - this.onDisplay?.(); + if (!standaloneOnDisplayWasTriggered) this.onDisplay?.(); } // TODO: required? should probably not be called per default // bindObserver(this) }) - this.onCreateLayout?.(); // custom layout extensions - - // @id, @content, @layout - this.handleIdProps(constructed); - - // @standlone props only relevant for backend - if (UIX.context == "backend") this.loadStandaloneProps(); - if (constructed) await this.onConstruct?.(); // this.bindOriginMethods(); diff --git a/src/standalone/get_prototype_properties.ts b/src/standalone/get_prototype_properties.ts index c3be43496..af46b2bf6 100644 --- a/src/standalone/get_prototype_properties.ts +++ b/src/standalone/get_prototype_properties.ts @@ -21,13 +21,27 @@ export function* getPrototypeProperties(clss: Class) { return; } + +export const DISPOSE_BOUND_PROTOTYPE = Symbol("DISPOSE_BOUND_PROTOTYPE"); + /** * Bind class prototoype to a value to act if the value was extending the class * @param value object * @param clss class with prototype */ export function bindPrototype(value:Record, clss:Class) { + const keys = new Set() + value[DISPOSE_BOUND_PROTOTYPE] = () => { + const hasOnDisplay = !!keys.has("onDisplay"); + for (const k of keys) delete value[k]; + delete value[DISPOSE_BOUND_PROTOTYPE]; + return hasOnDisplay; + } + for (const [n,v] of getPrototypeProperties(clss)) { + if (n == "constructor") continue; value[n] = v.bind(value); + keys.add(n) } + } \ No newline at end of file From ab0b63cf5621e6499d27a9fc42857f226c055190 Mon Sep 17 00:00:00 2001 From: benStre Date: Sat, 2 Mar 2024 21:31:52 +0100 Subject: [PATCH 53/56] update uix-dom --- src/uix-dom | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uix-dom b/src/uix-dom index 94fba652f..18d50ba77 160000 --- a/src/uix-dom +++ b/src/uix-dom @@ -1 +1 @@ -Subproject commit 94fba652fea3062a49db5fc504e618a60295d3f7 +Subproject commit 18d50ba776d80c649d17f47c9eaeb80dfd0671f2 From d02f24ac4d5f4da9bd67a6b6fb3644ab44af5ab5 Mon Sep 17 00:00:00 2001 From: benStre Date: Sat, 2 Mar 2024 22:38:23 +0100 Subject: [PATCH 54/56] decorator version selection (temp workaround for cdn) --- src/html/style.ts | 2 +- src/server/transpiler.ts | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/html/style.ts b/src/html/style.ts index e973e8feb..6dccd030a 100644 --- a/src/html/style.ts +++ b/src/html/style.ts @@ -56,7 +56,7 @@ export function style(style:CSSStyleSheet):((cl:typeof HTMLElement, context: Cla * ``` * @param styleGenerator */ -export function style(file:string|URL):((cl:typeof HTMLElement)=>any) +export function style(file:string|URL):((cl:typeof HTMLElement, context: ClassDecoratorContext)=>any) export function style(templateOrGenerator:string|URL|CSSStyleSheet|jsxInputGenerator) { diff --git a/src/server/transpiler.ts b/src/server/transpiler.ts index a9af6fa92..7f4c0df60 100644 --- a/src/server/transpiler.ts +++ b/src/server/transpiler.ts @@ -579,7 +579,6 @@ export class Transpiler { } private async transpileToJSSWC(ts_dist_path: Path.File, useJusix = false) { - // TODO: investigate/bug report: later versions lead to Segfault in docker containers with deno 1.41 const {transform} = await import("npm:@swc/core@^1.4.2"); const experimentalPlugins = useJusix ? { @@ -590,6 +589,12 @@ export class Transpiler { const js_dist_path = this.getFileWithMappedExtension(ts_dist_path); try { + + // workaround: select decorators based on uix/datex version + let decoratorVersion = "2022-03"; + const pathname = ts_dist_path.normal_pathname; + if (pathname.match(/\/uix-0\.(0|1)\.\d+\//)||pathname.match(/\/datex-core-js-legacy-0\.0\.\d+\//)) decoratorVersion = "2021-12"; + const file = await Deno.readTextFile(ts_dist_path.normal_pathname) const transpiled = (await transform(file, { jsc: { @@ -601,7 +606,7 @@ export class Transpiler { }, transform: { - decoratorVersion: "2022-03", + decoratorVersion, react: { runtime: "automatic", importSource: "uix", From 147c7a634c54d58f5fe882a9607220f11316f5ed Mon Sep 17 00:00:00 2001 From: benStre Date: Sun, 3 Mar 2024 00:16:28 +0100 Subject: [PATCH 55/56] remove unnecessary method --- src/server/transpiler.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/server/transpiler.ts b/src/server/transpiler.ts index 7f4c0df60..60b3f4a9e 100644 --- a/src/server/transpiler.ts +++ b/src/server/transpiler.ts @@ -629,7 +629,7 @@ export class Transpiler { if (transpiled != undefined) { await Deno.writeTextFile( js_dist_path.normal_pathname, - this.applySWCFixes(transpiled) + transpiled ); } else throw "unknown error" @@ -642,12 +642,6 @@ export class Transpiler { return js_dist_path; } - private applySWCFixes(source: string) { - // fix computedKey - // if (!source.match(/^var _computedKey\d*/gm)) source = source.replace(/^(_computedKey\d*) = /gm, 'var $1 = ') - return source; - } - private async minifyJS(source: string) { const {minify} = await import("npm:terser"); const minifiedSource = await minify(source, { From d65b22f2d604c81954e1f4516a74f0e40f2b1f38 Mon Sep 17 00:00:00 2001 From: benStre Date: Sun, 3 Mar 2024 01:23:17 +0100 Subject: [PATCH 56/56] add source map support --- src/app/frontend-manager.ts | 14 +++++++------ src/app/options.ts | 2 ++ src/server/transpiler.ts | 40 +++++++++++++++++++++++++------------ 3 files changed, 37 insertions(+), 19 deletions(-) diff --git a/src/app/frontend-manager.ts b/src/app/frontend-manager.ts index 69e6f5541..2598951af 100644 --- a/src/app/frontend-manager.ts +++ b/src/app/frontend-manager.ts @@ -4,7 +4,7 @@ import { TypescriptImportResolver } from "../server/ts-import-resolver.ts"; import { $$, Datex } from "datex-core-legacy"; import { Server, requestMetadata } from "../server/server.ts"; import { ALLOWED_ENTRYPOINT_FILE_NAMES, app } from "./app.ts"; -import { Path } from "../utils/path.ts"; +import { Path } from "datex-core-legacy/utils/path.ts"; import { BackendManager } from "./backend-manager.ts"; import { getExistingFile, getExistingFileExclusive } from "../utils/file-utils.ts"; import { logger } from "../utils/global-values.ts"; @@ -87,11 +87,12 @@ export class FrontendManager extends HTMLProvider { initFrontendDir(){ this.transpiler = new Transpiler(this.scope, { - sourceMap: app.stage == "dev", + sourceMaps: this.app_options.source_maps ?? app.stage == "dev", watch: this.#watch, minifyJS: this.app_options.minify_js, import_resolver: this.import_resolver, - on_file_update: this.#watch ? ()=>this.handleFrontendReload() : undefined + on_file_update: this.#watch ? ()=>this.handleFrontendReload() : undefined, + basePath: this.#base_path }); } @@ -104,7 +105,7 @@ export class FrontendManager extends HTMLProvider { intCommonDirs() { for (const common_dir of this.app_options.common) { const transpiler = new Transpiler(new Path(common_dir), { - sourceMap: app.stage == "dev", + sourceMaps: this.app_options.source_maps ?? app.stage == "dev", dist_parent_dir: this.transpiler.tmp_dir, watch: this.#watch, minifyJS: this.app_options.minify_js, @@ -118,7 +119,8 @@ export class FrontendManager extends HTMLProvider { this.#backend?.handleUpdate("common"); } this.handleFrontendReload(); - } : undefined + } : undefined, + basePath: this.#base_path }) this.#common_transpilers.set(common_dir.toString(), [transpiler, this.srcPrefix + new Path(common_dir).name + '/']) @@ -190,7 +192,7 @@ export class FrontendManager extends HTMLProvider { #backend?: BackendManager - #base_path!:Path + #base_path!:Path.File #web_path!: Path #entrypoint?: Path diff --git a/src/app/options.ts b/src/app/options.ts index 5d1011046..38679a377 100644 --- a/src/app/options.ts +++ b/src/app/options.ts @@ -28,6 +28,7 @@ export type appOptions = { debug_mode?: boolean // enable debug interfaces available on /@debug/... minify_js?: boolean // minify transpiled javascript modules, default: true preload_dependencies?: boolean // automatically preload all ts module dependencies, default: true + source_maps?: boolean // generate source maps for transpiled javascript modules, default: false, true for dev stage } export interface normalizedAppOptions extends appOptions { @@ -67,6 +68,7 @@ export async function normalizeAppOptions(options:appOptions = {}, baseURL?:stri n_options.debug_mode = options.debug_mode ?? false; n_options.minify_js = options.minify_js ?? true; n_options.preload_dependencies = options.preload_dependencies ?? true; + n_options.source_maps = options.source_maps; // import map or import map path if (options.import_map) n_options.import_map = new ImportMap(options.import_map); diff --git a/src/server/transpiler.ts b/src/server/transpiler.ts index 60b3f4a9e..7585608dc 100644 --- a/src/server/transpiler.ts +++ b/src/server/transpiler.ts @@ -1,4 +1,4 @@ -import { Path } from "../utils/path.ts"; +import { Path } from "datex-core-legacy/utils/path.ts"; import { Datex } from "datex-core-legacy"; import { TypescriptImportResolver } from "./ts-import-resolver.ts"; import { getCallerDir } from "datex-core-legacy/utils/caller_metadata.ts"; @@ -33,8 +33,9 @@ export type transpiler_options = { import_resolver?: TypescriptImportResolver, dist_parent_dir?: Path.File, // parent dir for dist dirs dist_dir?: Path.File, // use different path for dist (default: generated tmp dir) - sourceMap?: boolean // generate inline source maps when transpiling ts, + sourceMaps?: boolean // generate inline source maps when transpiling ts, minifyJS?: boolean // minify js files after transpiling + basePath?: Path.File } type transpiler_options_all = Required; @@ -88,7 +89,7 @@ export class Transpiler { // returns true if the file has to be transpiled isTranspiledFile(path:Path) { - return path.hasFileExtension(...this.#transpile_exts) && !path.hasFileExtension('d.ts') + return (path.hasFileExtension(...this.#transpile_exts, 'map')) && !path.hasFileExtension('d.ts') } // returns true if the tranpiled file has the same name as the src file (e.g. x.css -> x.css) @@ -351,7 +352,7 @@ export class Transpiler { } protected async transpileTS(dist_path:Path.File, src_path:Path.File) { - const js_dist_path = await this.transpileToJS(dist_path) + const js_dist_path = await this.transpileToJS(dist_path, src_path) if (this.import_resolver) { await this.import_resolver.resolveImports(dist_path, src_path, true); // resolve imports in ts, no side effects (don't update referenced module files) await this.import_resolver.resolveImports(js_dist_path, src_path) @@ -525,7 +526,7 @@ export class Transpiler { if (dist_path_js && await dist_path_js.fsExists()) await Deno.remove(dist_path_js) } - private transpileToJS(ts_dist_path: Path.File) { + private transpileToJS(ts_dist_path: Path.File, src_path: Path.File) { // check if corresponding ts file exists @@ -546,7 +547,7 @@ export class Transpiler { if (!valid) throw new Error("the typescript file cannot be transpiled - not a valid file extension"); // return this.transpileToJSDenoEmit(ts_dist_path) - return this.transpileToJSSWC(ts_dist_path, app.options?.experimentalFeatures.includes('embedded-reactivity')); + return this.transpileToJSSWC(ts_dist_path, src_path, app.options?.experimentalFeatures.includes('embedded-reactivity')); } private async transpileToJSDenoEmit(ts_dist_path:Path.File) { @@ -560,8 +561,8 @@ export class Transpiler { } as const : null; // TODO: remove jsxAutomatic:true, currently only because of caching problems const transpiled = await transpile(await Deno.readTextFile(ts_dist_path.normal_pathname), { - inlineSourceMap: !!this.#options.sourceMap, - inlineSources: !!this.#options.sourceMap, + inlineSourceMap: !!this.#options.sourceMaps, + inlineSources: !!this.#options.sourceMaps, ...jsxOptions }); if (transpiled != undefined) await Deno.writeTextFile(js_dist_path.normal_pathname, @@ -578,7 +579,7 @@ export class Transpiler { return js_dist_path; } - private async transpileToJSSWC(ts_dist_path: Path.File, useJusix = false) { + private async transpileToJSSWC(ts_dist_path: Path.File, src_path: Path.File, useJusix = false) { const {transform} = await import("npm:@swc/core@^1.4.2"); const experimentalPlugins = useJusix ? { @@ -596,7 +597,8 @@ export class Transpiler { if (pathname.match(/\/uix-0\.(0|1)\.\d+\//)||pathname.match(/\/datex-core-js-legacy-0\.0\.\d+\//)) decoratorVersion = "2021-12"; const file = await Deno.readTextFile(ts_dist_path.normal_pathname) - const transpiled = (await transform(file, { + let {code: transpiled, map} = await transform(file, { + sourceMaps: !!this.#options.sourceMaps, jsc: { parser: { tsx: !!ts_dist_path.hasFileExtension("tsx"), @@ -617,19 +619,31 @@ export class Transpiler { keepClassNames: true, externalHelpers: false, experimental: experimentalPlugins, - minify: this.#options.minifyJS ? + minify: (this.#options.minifyJS && this.#options.sourceMaps) ? { module: true, keep_classnames: true }: undefined } - })).code + }); + if (map) { + transpiled = transpiled + `\n//# sourceMappingURL=./${ts_dist_path.filename}.map`; + const jsonMap = JSON.parse(map); + if (this.#options.basePath) jsonMap.sourceRoot = src_path.parent_dir.getAsRelativeFrom(this.#options.basePath).replace(/^\.\//, '/').replace(/\/[^/]+\/@uix\/src/,''); + jsonMap.file = src_path.filename; + jsonMap.sources[0] = src_path.filename; + await Deno.writeTextFile( + ts_dist_path.normal_pathname + ".map", + JSON.stringify(jsonMap) + ); + } if (transpiled != undefined) { + const minifyOptimized = this.#options.minifyJS && !this.#options.sourceMaps await Deno.writeTextFile( js_dist_path.normal_pathname, - transpiled + minifyOptimized ? await this.minifyJS(transpiled) : transpiled ); } else throw "unknown error"