@opentf/web 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{CHANGELOG.md → dist/CHANGELOG.md} +6 -0
- package/dist/babel.config.cjs +6 -0
- package/{compiler → dist/compiler}/babel-plugin.cjs +10 -9
- package/{core → dist/core}/mount.js +12 -12
- package/{index.js → dist/index.js} +1 -1
- package/dist/node_modules/@babel/core/LICENSE +22 -0
- package/dist/node_modules/@babel/core/README.md +19 -0
- package/dist/node_modules/@babel/core/lib/config/cache-contexts.js +5 -0
- package/dist/node_modules/@babel/core/lib/config/cache-contexts.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/config/caching.js +261 -0
- package/dist/node_modules/@babel/core/lib/config/caching.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/config/config-chain.js +469 -0
- package/dist/node_modules/@babel/core/lib/config/config-chain.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/config/config-descriptors.js +190 -0
- package/dist/node_modules/@babel/core/lib/config/config-descriptors.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/config/files/configuration.js +290 -0
- package/dist/node_modules/@babel/core/lib/config/files/configuration.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/config/files/import.cjs +6 -0
- package/dist/node_modules/@babel/core/lib/config/files/import.cjs.map +1 -0
- package/dist/node_modules/@babel/core/lib/config/files/index-browser.js +58 -0
- package/dist/node_modules/@babel/core/lib/config/files/index-browser.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/config/files/index.js +78 -0
- package/dist/node_modules/@babel/core/lib/config/files/index.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/config/files/module-types.js +203 -0
- package/dist/node_modules/@babel/core/lib/config/files/module-types.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/config/files/package.js +61 -0
- package/dist/node_modules/@babel/core/lib/config/files/package.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/config/files/plugins.js +220 -0
- package/dist/node_modules/@babel/core/lib/config/files/plugins.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/config/files/types.js +5 -0
- package/dist/node_modules/@babel/core/lib/config/files/types.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/config/files/utils.js +36 -0
- package/dist/node_modules/@babel/core/lib/config/files/utils.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/config/full.js +312 -0
- package/dist/node_modules/@babel/core/lib/config/full.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/config/helpers/config-api.js +85 -0
- package/dist/node_modules/@babel/core/lib/config/helpers/config-api.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/config/helpers/deep-array.js +23 -0
- package/dist/node_modules/@babel/core/lib/config/helpers/deep-array.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/config/helpers/environment.js +12 -0
- package/dist/node_modules/@babel/core/lib/config/helpers/environment.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/config/index.js +87 -0
- package/dist/node_modules/@babel/core/lib/config/index.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/config/item.js +67 -0
- package/dist/node_modules/@babel/core/lib/config/item.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/config/partial.js +158 -0
- package/dist/node_modules/@babel/core/lib/config/partial.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/config/pattern-to-regex.js +38 -0
- package/dist/node_modules/@babel/core/lib/config/pattern-to-regex.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/config/plugin.js +33 -0
- package/dist/node_modules/@babel/core/lib/config/plugin.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/config/printer.js +113 -0
- package/dist/node_modules/@babel/core/lib/config/printer.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/config/resolve-targets-browser.js +41 -0
- package/dist/node_modules/@babel/core/lib/config/resolve-targets-browser.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/config/resolve-targets.js +61 -0
- package/dist/node_modules/@babel/core/lib/config/resolve-targets.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/config/util.js +31 -0
- package/dist/node_modules/@babel/core/lib/config/util.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/config/validation/option-assertions.js +277 -0
- package/dist/node_modules/@babel/core/lib/config/validation/option-assertions.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/config/validation/options.js +187 -0
- package/dist/node_modules/@babel/core/lib/config/validation/options.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/config/validation/plugins.js +67 -0
- package/dist/node_modules/@babel/core/lib/config/validation/plugins.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/config/validation/removed.js +68 -0
- package/dist/node_modules/@babel/core/lib/config/validation/removed.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/errors/config-error.js +18 -0
- package/dist/node_modules/@babel/core/lib/errors/config-error.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/errors/rewrite-stack-trace.js +98 -0
- package/dist/node_modules/@babel/core/lib/errors/rewrite-stack-trace.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/gensync-utils/async.js +90 -0
- package/dist/node_modules/@babel/core/lib/gensync-utils/async.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/gensync-utils/fs.js +31 -0
- package/dist/node_modules/@babel/core/lib/gensync-utils/fs.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/gensync-utils/functional.js +58 -0
- package/dist/node_modules/@babel/core/lib/gensync-utils/functional.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/index.js +230 -0
- package/dist/node_modules/@babel/core/lib/index.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/parse.js +45 -0
- package/dist/node_modules/@babel/core/lib/parse.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/parser/index.js +85 -0
- package/dist/node_modules/@babel/core/lib/parser/index.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/parser/util/missing-plugin-helper.js +337 -0
- package/dist/node_modules/@babel/core/lib/parser/util/missing-plugin-helper.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/tools/build-external-helpers.js +144 -0
- package/dist/node_modules/@babel/core/lib/tools/build-external-helpers.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/transform-ast.js +48 -0
- package/dist/node_modules/@babel/core/lib/transform-ast.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/transform-file-browser.js +23 -0
- package/dist/node_modules/@babel/core/lib/transform-file-browser.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/transform-file.js +40 -0
- package/dist/node_modules/@babel/core/lib/transform-file.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/transform.js +47 -0
- package/dist/node_modules/@babel/core/lib/transform.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/transformation/block-hoist-plugin.js +84 -0
- package/dist/node_modules/@babel/core/lib/transformation/block-hoist-plugin.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/transformation/file/babel-7-helpers.cjs +4 -0
- package/dist/node_modules/@babel/core/lib/transformation/file/babel-7-helpers.cjs.map +1 -0
- package/dist/node_modules/@babel/core/lib/transformation/file/file.js +204 -0
- package/dist/node_modules/@babel/core/lib/transformation/file/file.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/transformation/file/generate.js +84 -0
- package/dist/node_modules/@babel/core/lib/transformation/file/generate.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/transformation/file/merge-map.js +37 -0
- package/dist/node_modules/@babel/core/lib/transformation/file/merge-map.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/transformation/index.js +90 -0
- package/dist/node_modules/@babel/core/lib/transformation/index.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/transformation/normalize-file.js +127 -0
- package/dist/node_modules/@babel/core/lib/transformation/normalize-file.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/transformation/normalize-opts.js +59 -0
- package/dist/node_modules/@babel/core/lib/transformation/normalize-opts.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/transformation/plugin-pass.js +48 -0
- package/dist/node_modules/@babel/core/lib/transformation/plugin-pass.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/transformation/util/clone-deep.js +54 -0
- package/dist/node_modules/@babel/core/lib/transformation/util/clone-deep.js.map +1 -0
- package/dist/node_modules/@babel/core/lib/vendor/import-meta-resolve.js +1042 -0
- package/dist/node_modules/@babel/core/lib/vendor/import-meta-resolve.js.map +1 -0
- package/dist/node_modules/@babel/core/package.json +82 -0
- package/dist/node_modules/@babel/core/src/config/files/index-browser.ts +115 -0
- package/dist/node_modules/@babel/core/src/config/files/index.ts +30 -0
- package/dist/node_modules/@babel/core/src/config/resolve-targets-browser.ts +42 -0
- package/dist/node_modules/@babel/core/src/config/resolve-targets.ts +54 -0
- package/dist/node_modules/@babel/core/src/transform-file-browser.ts +33 -0
- package/dist/node_modules/@babel/core/src/transform-file.ts +56 -0
- package/dist/node_modules/@babel/helper-module-imports/LICENSE +22 -0
- package/dist/node_modules/@babel/helper-module-imports/README.md +19 -0
- package/dist/node_modules/@babel/helper-module-imports/lib/import-builder.js +122 -0
- package/dist/node_modules/@babel/helper-module-imports/lib/import-builder.js.map +1 -0
- package/dist/node_modules/@babel/helper-module-imports/lib/import-injector.js +304 -0
- package/dist/node_modules/@babel/helper-module-imports/lib/import-injector.js.map +1 -0
- package/dist/node_modules/@babel/helper-module-imports/lib/index.js +37 -0
- package/dist/node_modules/@babel/helper-module-imports/lib/index.js.map +1 -0
- package/dist/node_modules/@babel/helper-module-imports/lib/is-module.js +11 -0
- package/dist/node_modules/@babel/helper-module-imports/lib/is-module.js.map +1 -0
- package/dist/node_modules/@babel/helper-module-imports/package.json +28 -0
- package/dist/node_modules/@babel/plugin-syntax-jsx/LICENSE +22 -0
- package/dist/node_modules/@babel/plugin-syntax-jsx/README.md +19 -0
- package/dist/node_modules/@babel/plugin-syntax-jsx/lib/index.js +21 -0
- package/dist/node_modules/@babel/plugin-syntax-jsx/lib/index.js.map +1 -0
- package/dist/node_modules/@babel/plugin-syntax-jsx/package.json +33 -0
- package/dist/node_modules/@preact/signals-core/CHANGELOG.md +331 -0
- package/dist/node_modules/@preact/signals-core/LICENSE +21 -0
- package/dist/node_modules/@preact/signals-core/README.md +258 -0
- package/dist/node_modules/@preact/signals-core/dist/signals-core.d.ts +149 -0
- package/dist/node_modules/@preact/signals-core/dist/signals-core.js +1 -0
- package/dist/node_modules/@preact/signals-core/dist/signals-core.js.map +1 -0
- package/dist/node_modules/@preact/signals-core/dist/signals-core.min.js +1 -0
- package/dist/node_modules/@preact/signals-core/dist/signals-core.min.js.map +1 -0
- package/dist/node_modules/@preact/signals-core/dist/signals-core.mjs +1 -0
- package/dist/node_modules/@preact/signals-core/dist/signals-core.mjs.map +1 -0
- package/dist/node_modules/@preact/signals-core/dist/signals-core.module.js +1 -0
- package/dist/node_modules/@preact/signals-core/dist/signals-core.module.js.map +1 -0
- package/dist/node_modules/@preact/signals-core/package.json +48 -0
- package/dist/node_modules/@preact/signals-core/src/index.ts +1121 -0
- package/dist/package.json +30 -0
- package/dist/router/Link.js +76 -0
- package/{router → dist/router}/index.js +43 -61
- package/{runtime → dist/runtime}/For.js +1 -2
- package/{runtime → dist/runtime}/dom.js +7 -14
- package/{runtime → dist/runtime}/lifecycle.js +1 -4
- package/{runtime → dist/runtime}/props.js +4 -15
- package/package.json +14 -7
- package/router/Link.jsx +0 -27
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@opentf/web",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "The core engine of the Web App Framework, including the zero-VDOM compiler and signal-based runtime.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.js",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "babel . --out-dir dist --extensions .js,.jsx --copy-files --config-file ./babel.config.cjs --ignore \"node_modules,dist,package.json,bun.lock,*.md\"",
|
|
10
|
+
"prepublishOnly": "npm run build"
|
|
11
|
+
},
|
|
12
|
+
"exports": {
|
|
13
|
+
".": "./dist/index.js",
|
|
14
|
+
"./compiler": "./dist/compiler/babel-plugin.cjs",
|
|
15
|
+
"./runtime": "./dist/runtime/index.js",
|
|
16
|
+
"./router": "./dist/router/index.js"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist"
|
|
20
|
+
],
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@preact/signals-core": "^1.14.1",
|
|
23
|
+
"@babel/core": "^7.29.0",
|
|
24
|
+
"@babel/helper-module-imports": "^7.28.6",
|
|
25
|
+
"@babel/plugin-syntax-jsx": "^7.28.6"
|
|
26
|
+
},
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { createPropsProxy as _createPropsProxy, withInstance as _withInstance } from "../index.js";
|
|
2
|
+
import { signal as _signal } from "@preact/signals-core";
|
|
3
|
+
import { renderDynamic as _renderDynamic } from "../index.js";
|
|
4
|
+
import { effect as _effect } from "@preact/signals-core";
|
|
5
|
+
import { navigate } from "./index";
|
|
6
|
+
class LinkElement extends HTMLElement {
|
|
7
|
+
static observedAttributes = ["href", "className", "style", "children"];
|
|
8
|
+
set href(val) {
|
|
9
|
+
this._propsSignals["href"].value = val;
|
|
10
|
+
}
|
|
11
|
+
set className(val) {
|
|
12
|
+
this._propsSignals["className"].value = val;
|
|
13
|
+
this.setAttribute("class", val);
|
|
14
|
+
}
|
|
15
|
+
set style(val) {
|
|
16
|
+
this._propsSignals["style"].value = val;
|
|
17
|
+
if (val && typeof val === "object") Object.assign(this.style, val);
|
|
18
|
+
}
|
|
19
|
+
set children(val) {
|
|
20
|
+
this._propsSignals["children"].value = val;
|
|
21
|
+
}
|
|
22
|
+
get href() {
|
|
23
|
+
return this._propsSignals["href"].value;
|
|
24
|
+
}
|
|
25
|
+
get className() {
|
|
26
|
+
return this._propsSignals["className"].value;
|
|
27
|
+
}
|
|
28
|
+
get style() {
|
|
29
|
+
return this._propsSignals["style"].value;
|
|
30
|
+
}
|
|
31
|
+
get children() {
|
|
32
|
+
return this._propsSignals["children"].value;
|
|
33
|
+
}
|
|
34
|
+
constructor() {
|
|
35
|
+
super();
|
|
36
|
+
this._propsSignals = {
|
|
37
|
+
href: _signal(null),
|
|
38
|
+
className: _signal(null),
|
|
39
|
+
style: _signal(null),
|
|
40
|
+
children: _signal(null)
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
attributeChangedCallback(name, _, value) {
|
|
44
|
+
this._propsSignals[name].value = value;
|
|
45
|
+
}
|
|
46
|
+
connectedCallback() {
|
|
47
|
+
this._onMounts = [];
|
|
48
|
+
this._onCleanups = [];
|
|
49
|
+
const props = _createPropsProxy(this);
|
|
50
|
+
this._children = Array.from(this.childNodes);
|
|
51
|
+
while (this.firstChild) this.removeChild(this.firstChild);
|
|
52
|
+
_withInstance(this, () => {
|
|
53
|
+
const rootElement = (() => {
|
|
54
|
+
const el0 = document.createElement("a");
|
|
55
|
+
_effect(() => el0.href = props.href);
|
|
56
|
+
_effect(() => el0.className = props.className);
|
|
57
|
+
_effect(() => Object.assign(el0.style, props.style));
|
|
58
|
+
el0.onclick = e => {
|
|
59
|
+
if (e.defaultPrevented || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || e.button !== 0) return;
|
|
60
|
+
e.preventDefault();
|
|
61
|
+
navigate(props.href);
|
|
62
|
+
window.scrollTo(0, 0);
|
|
63
|
+
};
|
|
64
|
+
_renderDynamic(el0, () => props.children);
|
|
65
|
+
return el0;
|
|
66
|
+
})();
|
|
67
|
+
this.appendChild(rootElement);
|
|
68
|
+
});
|
|
69
|
+
this._onMounts.forEach(fn => fn());
|
|
70
|
+
}
|
|
71
|
+
disconnectedCallback() {
|
|
72
|
+
this._onCleanups.forEach(fn => fn());
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
customElements.define("web-link", LinkElement);
|
|
76
|
+
export default LinkElement;
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import { withInstance } from '../runtime/lifecycle.js'
|
|
2
|
-
import { signal } from '@preact/signals-core'
|
|
3
|
-
|
|
1
|
+
import { withInstance } from '../runtime/lifecycle.js';
|
|
2
|
+
import { signal } from '@preact/signals-core';
|
|
4
3
|
export const routes = {
|
|
5
4
|
pages: {},
|
|
6
5
|
layouts: {},
|
|
7
6
|
notFound: null
|
|
8
|
-
}
|
|
9
|
-
|
|
7
|
+
};
|
|
10
8
|
const routerSignals = {
|
|
11
9
|
pathname: signal(window.location.pathname),
|
|
12
10
|
searchParams: signal(new URLSearchParams(window.location.search)),
|
|
@@ -14,12 +12,10 @@ const routerSignals = {
|
|
|
14
12
|
isGuarding: signal(false),
|
|
15
13
|
guard: null
|
|
16
14
|
};
|
|
17
|
-
|
|
18
15
|
export const router = new Proxy(routerSignals, {
|
|
19
16
|
get: (target, key) => {
|
|
20
|
-
if (key === "push") return
|
|
21
|
-
if (key === "replace") return
|
|
22
|
-
|
|
17
|
+
if (key === "push") return path => navigate(path);
|
|
18
|
+
if (key === "replace") return path => navigate(path, undefined, true);
|
|
23
19
|
const s = target[key];
|
|
24
20
|
if (s && typeof s === 'object' && 'value' in s && typeof s.subscribe === 'function') {
|
|
25
21
|
return s.value;
|
|
@@ -36,20 +32,13 @@ export const router = new Proxy(routerSignals, {
|
|
|
36
32
|
return true;
|
|
37
33
|
}
|
|
38
34
|
});
|
|
39
|
-
|
|
40
35
|
let currentPageInstance = null;
|
|
41
|
-
|
|
42
36
|
export function registerRoutes(pages) {
|
|
43
37
|
for (const path in pages) {
|
|
44
38
|
const isLayout = path.endsWith('layout.jsx') || path.endsWith('layout.tsx');
|
|
45
39
|
const isNotFound = path.endsWith('404.jsx') || path.endsWith('404.tsx');
|
|
46
|
-
|
|
47
|
-
let route = path
|
|
48
|
-
.replace(/^.*\/app/, '')
|
|
49
|
-
.replace(/\/(page|layout|404)\.(jsx|tsx)$/, '');
|
|
50
|
-
|
|
40
|
+
let route = path.replace(/^.*\/app/, '').replace(/\/(page|layout|404)\.(jsx|tsx)$/, '');
|
|
51
41
|
if (route === '') route = '/';
|
|
52
|
-
|
|
53
42
|
if (isNotFound) {
|
|
54
43
|
routes.notFound = pages[path];
|
|
55
44
|
} else if (isLayout) {
|
|
@@ -59,26 +48,22 @@ export function registerRoutes(pages) {
|
|
|
59
48
|
}
|
|
60
49
|
}
|
|
61
50
|
}
|
|
62
|
-
|
|
63
51
|
function matchRoute(path) {
|
|
64
52
|
for (const route in routes.pages) {
|
|
65
|
-
const pattern = route
|
|
66
|
-
.replace(/\[\.\.\.([^\]]+)\]/g, '(?<$1>.+)')
|
|
67
|
-
.replace(/\[([^\]]+)\]/g, '(?<$1>[^/]+)');
|
|
68
|
-
|
|
53
|
+
const pattern = route.replace(/\[\.\.\.([^\]]+)\]/g, '(?<$1>.+)').replace(/\[([^\]]+)\]/g, '(?<$1>[^/]+)');
|
|
69
54
|
const regex = new RegExp(`^${pattern}$`);
|
|
70
55
|
const match = path.match(regex);
|
|
71
56
|
if (match) {
|
|
72
|
-
const params = {
|
|
73
|
-
|
|
57
|
+
const params = {
|
|
58
|
+
...(match.groups || {})
|
|
59
|
+
};
|
|
74
60
|
for (const key in params) {
|
|
75
61
|
if (route.includes(`[...${key}]`)) {
|
|
76
62
|
params[key] = params[key].split('/');
|
|
77
63
|
}
|
|
78
64
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
page: routes.pages[route],
|
|
65
|
+
return {
|
|
66
|
+
page: routes.pages[route],
|
|
82
67
|
params,
|
|
83
68
|
route
|
|
84
69
|
};
|
|
@@ -86,26 +71,24 @@ function matchRoute(path) {
|
|
|
86
71
|
}
|
|
87
72
|
return null;
|
|
88
73
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
74
|
function scrollToHash(hash) {
|
|
92
75
|
if (!hash) return;
|
|
93
76
|
const id = hash.replace('#', '');
|
|
94
77
|
const el = document.getElementById(id);
|
|
95
78
|
if (el) {
|
|
96
|
-
el.scrollIntoView({
|
|
79
|
+
el.scrollIntoView({
|
|
80
|
+
behavior: 'smooth'
|
|
81
|
+
});
|
|
97
82
|
}
|
|
98
83
|
}
|
|
99
|
-
|
|
100
84
|
export async function navigate(path, root = document.getElementById("app"), replace = false, isPopState = false) {
|
|
101
85
|
if (!path) return;
|
|
102
|
-
|
|
103
86
|
const url = new URL(path, window.location.origin);
|
|
104
87
|
const pathname = url.pathname;
|
|
105
88
|
const oldPathname = window.location.pathname;
|
|
106
89
|
const oldSearch = window.location.search;
|
|
107
90
|
const oldHash = window.location.hash;
|
|
108
|
-
|
|
91
|
+
|
|
109
92
|
// 1. Run Route Guard if registered
|
|
110
93
|
if (router.guard) {
|
|
111
94
|
const match = matchRoute(pathname);
|
|
@@ -115,23 +98,21 @@ export async function navigate(path, root = document.getElementById("app"), repl
|
|
|
115
98
|
params: match?.params || {},
|
|
116
99
|
query: Object.fromEntries(new URLSearchParams(url.search))
|
|
117
100
|
};
|
|
118
|
-
|
|
119
101
|
routerSignals.isGuarding.value = true;
|
|
120
|
-
|
|
121
102
|
try {
|
|
122
103
|
await new Promise((resolve, reject) => {
|
|
123
104
|
const tools = {
|
|
124
105
|
next: () => resolve(),
|
|
125
|
-
redirect:
|
|
106
|
+
redirect: p => {
|
|
126
107
|
navigate(p, root, false);
|
|
127
108
|
reject('redirected');
|
|
128
109
|
},
|
|
129
|
-
replace:
|
|
110
|
+
replace: p => {
|
|
130
111
|
navigate(p, root, true);
|
|
131
112
|
reject('redirected');
|
|
132
113
|
}
|
|
133
114
|
};
|
|
134
|
-
|
|
115
|
+
|
|
135
116
|
// Execute guard
|
|
136
117
|
Promise.resolve(routerSignals.guard(to, tools)).catch(reject);
|
|
137
118
|
});
|
|
@@ -141,10 +122,8 @@ export async function navigate(path, root = document.getElementById("app"), repl
|
|
|
141
122
|
console.error('Route Guard Error:', e);
|
|
142
123
|
return;
|
|
143
124
|
}
|
|
144
|
-
|
|
145
125
|
routerSignals.isGuarding.value = false;
|
|
146
126
|
}
|
|
147
|
-
|
|
148
127
|
if (!isPopState) {
|
|
149
128
|
routerSignals.pathname.value = pathname;
|
|
150
129
|
routerSignals.searchParams.value = new URLSearchParams(url.search);
|
|
@@ -154,25 +133,30 @@ export async function navigate(path, root = document.getElementById("app"), repl
|
|
|
154
133
|
// If only hash changed, don't re-render everything
|
|
155
134
|
if (pathname === oldPathname && url.search === oldSearch && url.hash !== oldHash) {
|
|
156
135
|
if (!isPopState) {
|
|
157
|
-
if (replace) window.history.replaceState({}, '', path);
|
|
158
|
-
else window.history.pushState({}, '', path);
|
|
136
|
+
if (replace) window.history.replaceState({}, '', path);else window.history.pushState({}, '', path);
|
|
159
137
|
}
|
|
160
138
|
scrollToHash(url.hash);
|
|
161
139
|
return;
|
|
162
140
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
141
|
+
const match = matchRoute(pathname) || (routes.notFound ? {
|
|
142
|
+
page: routes.notFound,
|
|
143
|
+
params: {},
|
|
144
|
+
route: null
|
|
145
|
+
} : null);
|
|
166
146
|
if (match) {
|
|
167
|
-
const {
|
|
168
|
-
|
|
147
|
+
const {
|
|
148
|
+
page,
|
|
149
|
+
params,
|
|
150
|
+
route
|
|
151
|
+
} = match;
|
|
169
152
|
if (currentPageInstance && currentPageInstance._onCleanups) {
|
|
170
153
|
currentPageInstance._onCleanups.forEach(fn => fn());
|
|
171
154
|
}
|
|
172
|
-
|
|
173
|
-
|
|
155
|
+
const instance = {
|
|
156
|
+
_onMounts: [],
|
|
157
|
+
_onCleanups: []
|
|
158
|
+
};
|
|
174
159
|
currentPageInstance = instance;
|
|
175
|
-
|
|
176
160
|
const layoutChain = [];
|
|
177
161
|
if (route) {
|
|
178
162
|
let currentPath = route;
|
|
@@ -184,32 +168,31 @@ export async function navigate(path, root = document.getElementById("app"), repl
|
|
|
184
168
|
currentPath = currentPath.substring(0, currentPath.lastIndexOf('/')) || '/';
|
|
185
169
|
}
|
|
186
170
|
}
|
|
187
|
-
|
|
188
171
|
let content = document.createDocumentFragment();
|
|
189
172
|
withInstance(instance, () => {
|
|
190
|
-
page.render(content, {
|
|
191
|
-
|
|
173
|
+
page.render(content, {
|
|
174
|
+
params
|
|
175
|
+
});
|
|
192
176
|
for (let i = layoutChain.length - 1; i >= 0; i--) {
|
|
193
177
|
const layout = layoutChain[i];
|
|
194
178
|
const nextContent = document.createDocumentFragment();
|
|
195
|
-
layout.render(nextContent, {
|
|
179
|
+
layout.render(nextContent, {
|
|
180
|
+
children: content,
|
|
181
|
+
params
|
|
182
|
+
});
|
|
196
183
|
content = nextContent;
|
|
197
184
|
}
|
|
198
185
|
});
|
|
199
|
-
|
|
200
186
|
if (!root) root = document.getElementById("app");
|
|
201
187
|
if (root) {
|
|
202
188
|
root.innerHTML = '';
|
|
203
189
|
root.appendChild(content);
|
|
204
190
|
}
|
|
205
|
-
|
|
206
191
|
if (instance._onMounts) {
|
|
207
192
|
instance._onMounts.forEach(fn => fn());
|
|
208
193
|
}
|
|
209
|
-
|
|
210
194
|
if (!isPopState) {
|
|
211
|
-
if (replace) window.history.replaceState({}, '', path);
|
|
212
|
-
else window.history.pushState({}, '', path);
|
|
195
|
+
if (replace) window.history.replaceState({}, '', path);else window.history.pushState({}, '', path);
|
|
213
196
|
}
|
|
214
197
|
|
|
215
198
|
// Scroll to hash after render
|
|
@@ -218,7 +201,6 @@ export async function navigate(path, root = document.getElementById("app"), repl
|
|
|
218
201
|
if (root) root.innerHTML = '<h1>404 Not Found</h1>';
|
|
219
202
|
}
|
|
220
203
|
}
|
|
221
|
-
|
|
222
204
|
if (typeof window !== 'undefined') {
|
|
223
205
|
window.addEventListener('popstate', () => {
|
|
224
206
|
const fullPath = window.location.pathname + window.location.search + window.location.hash;
|
|
@@ -231,4 +213,4 @@ if (typeof window !== 'undefined') {
|
|
|
231
213
|
routerSignals.hash.value = window.location.hash;
|
|
232
214
|
scrollToHash(window.location.hash);
|
|
233
215
|
});
|
|
234
|
-
}
|
|
216
|
+
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { mapped, renderDynamic } from "./dom.js";
|
|
2
2
|
import { withInstance } from "./lifecycle.js";
|
|
3
|
-
|
|
4
3
|
export function For(props) {
|
|
5
4
|
// We return a "marker" that renderDynamic will handle
|
|
6
5
|
// But wait, if we are called as a component, we need to render something.
|
|
@@ -14,4 +13,4 @@ class ForElement extends HTMLElement {
|
|
|
14
13
|
const renderFn = this.childNodes[0]; // Wait, this is tricky
|
|
15
14
|
// ...
|
|
16
15
|
}
|
|
17
|
-
}
|
|
16
|
+
}
|
|
@@ -1,41 +1,33 @@
|
|
|
1
1
|
import { effect } from "@preact/signals-core";
|
|
2
|
-
|
|
3
2
|
export function renderDynamic(parent, fn) {
|
|
4
3
|
const anchor = document.createComment("dynamic");
|
|
5
4
|
parent.appendChild(anchor);
|
|
6
|
-
|
|
7
5
|
let currentNodes = [];
|
|
8
|
-
|
|
9
6
|
effect(() => {
|
|
10
7
|
let value = fn();
|
|
11
8
|
// If the value is a function (e.g., from mapped), execute it to get the nodes
|
|
12
9
|
if (typeof value === "function") value = value();
|
|
13
|
-
|
|
14
10
|
if (value === null || value === undefined || value === false) value = [];
|
|
15
11
|
if (!Array.isArray(value)) value = [value];
|
|
16
|
-
|
|
17
12
|
const nextNodes = value.map(v => {
|
|
18
13
|
if (v instanceof Node) return v;
|
|
19
14
|
return document.createTextNode(String(v));
|
|
20
15
|
});
|
|
21
|
-
|
|
22
16
|
reconcile(anchor.parentNode, anchor, currentNodes, nextNodes);
|
|
23
17
|
currentNodes = nextNodes;
|
|
24
18
|
});
|
|
25
19
|
}
|
|
26
|
-
|
|
27
20
|
export function mapped(source, fn) {
|
|
28
21
|
let cache = new Map(); // key -> { node }
|
|
29
|
-
|
|
22
|
+
|
|
30
23
|
return () => {
|
|
31
24
|
const list = (typeof source === "function" ? source() : source.value) || [];
|
|
32
25
|
const nextNodes = [];
|
|
33
26
|
const nextCache = new Map();
|
|
34
|
-
|
|
35
27
|
list.forEach((item, index) => {
|
|
36
28
|
const key = item.key ?? item.id ?? index;
|
|
37
29
|
let cached = cache.get(key);
|
|
38
|
-
|
|
30
|
+
|
|
39
31
|
// If the item exists and the reference is identical, reuse the node
|
|
40
32
|
if (cached && cached.item === item) {
|
|
41
33
|
nextNodes.push(cached.node);
|
|
@@ -45,7 +37,10 @@ export function mapped(source, fn) {
|
|
|
45
37
|
const node = fn(item, index);
|
|
46
38
|
node._key = key;
|
|
47
39
|
nextNodes.push(node);
|
|
48
|
-
nextCache.set(key, {
|
|
40
|
+
nextCache.set(key, {
|
|
41
|
+
node,
|
|
42
|
+
item
|
|
43
|
+
});
|
|
49
44
|
}
|
|
50
45
|
});
|
|
51
46
|
|
|
@@ -55,12 +50,10 @@ export function mapped(source, fn) {
|
|
|
55
50
|
if (cached.node.remove) cached.node.remove();
|
|
56
51
|
}
|
|
57
52
|
});
|
|
58
|
-
|
|
59
53
|
cache = nextCache;
|
|
60
54
|
return nextNodes;
|
|
61
55
|
};
|
|
62
56
|
}
|
|
63
|
-
|
|
64
57
|
function reconcile(parent, anchor, oldNodes, nextNodes) {
|
|
65
58
|
// 1. Remove nodes that are no longer present
|
|
66
59
|
const nextSet = new Set(nextNodes);
|
|
@@ -77,4 +70,4 @@ function reconcile(parent, anchor, oldNodes, nextNodes) {
|
|
|
77
70
|
}
|
|
78
71
|
current = node;
|
|
79
72
|
}
|
|
80
|
-
}
|
|
73
|
+
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
let currentInstance = null;
|
|
2
|
-
|
|
3
2
|
export function withInstance(inst, fn) {
|
|
4
3
|
const prev = currentInstance;
|
|
5
4
|
currentInstance = inst;
|
|
@@ -9,7 +8,6 @@ export function withInstance(inst, fn) {
|
|
|
9
8
|
currentInstance = prev;
|
|
10
9
|
}
|
|
11
10
|
}
|
|
12
|
-
|
|
13
11
|
export function onMount(fn) {
|
|
14
12
|
if (currentInstance) {
|
|
15
13
|
if (!currentInstance._onMounts) currentInstance._onMounts = [];
|
|
@@ -19,10 +17,9 @@ export function onMount(fn) {
|
|
|
19
17
|
fn();
|
|
20
18
|
}
|
|
21
19
|
}
|
|
22
|
-
|
|
23
20
|
export function onCleanup(fn) {
|
|
24
21
|
if (currentInstance) {
|
|
25
22
|
if (!currentInstance._onCleanups) currentInstance._onCleanups = [];
|
|
26
23
|
currentInstance._onCleanups.push(fn);
|
|
27
24
|
}
|
|
28
|
-
}
|
|
25
|
+
}
|
|
@@ -1,19 +1,16 @@
|
|
|
1
1
|
import { effect, signal } from "@preact/signals-core";
|
|
2
|
-
|
|
3
2
|
function toKebabCase(str) {
|
|
4
3
|
return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
|
5
4
|
}
|
|
6
|
-
|
|
7
5
|
export function createPropsProxy(el) {
|
|
8
6
|
if (!el._propsSignals) el._propsSignals = {};
|
|
9
|
-
|
|
10
7
|
return new Proxy(el, {
|
|
11
8
|
get: (target, key) => {
|
|
12
9
|
if (key === "__signals") return target._propsSignals;
|
|
13
10
|
if (key === "children") return target._children;
|
|
14
11
|
if (typeof key === "symbol") return target[key];
|
|
15
12
|
if (key === "then") return undefined; // Promise check
|
|
16
|
-
|
|
13
|
+
|
|
17
14
|
if (!target._propsSignals[key]) {
|
|
18
15
|
const initialValue = target.getAttribute(toKebabCase(key)) || target[key];
|
|
19
16
|
target._propsSignals[key] = signal(initialValue);
|
|
@@ -22,11 +19,9 @@ export function createPropsProxy(el) {
|
|
|
22
19
|
},
|
|
23
20
|
set: (target, key, value) => {
|
|
24
21
|
const isSignal = value && typeof value === "object" && "value" in value && typeof value.subscribe === "function";
|
|
25
|
-
|
|
26
22
|
if (!target._propsSignals[key]) {
|
|
27
23
|
target._propsSignals[key] = signal(isSignal ? value.value : value);
|
|
28
24
|
}
|
|
29
|
-
|
|
30
25
|
if (isSignal) {
|
|
31
26
|
// Link the internal signal to the external one
|
|
32
27
|
if (target._propsSignals[key]._link) target._propsSignals[key]._link(); // Cleanup old link
|
|
@@ -36,11 +31,10 @@ export function createPropsProxy(el) {
|
|
|
36
31
|
} else {
|
|
37
32
|
target._propsSignals[key].value = value;
|
|
38
33
|
}
|
|
39
|
-
|
|
40
34
|
target[key] = value;
|
|
41
35
|
return true;
|
|
42
36
|
},
|
|
43
|
-
ownKeys:
|
|
37
|
+
ownKeys: target => {
|
|
44
38
|
const keys = new Set(Object.keys(target));
|
|
45
39
|
Object.keys(target._propsSignals).forEach(k => keys.add(k));
|
|
46
40
|
keys.add("children");
|
|
@@ -54,24 +48,19 @@ export function createPropsProxy(el) {
|
|
|
54
48
|
}
|
|
55
49
|
});
|
|
56
50
|
}
|
|
57
|
-
|
|
58
51
|
export function applySpread(el, props) {
|
|
59
52
|
if (!props) return;
|
|
60
|
-
|
|
61
53
|
const signals = props?.__signals;
|
|
62
|
-
|
|
63
54
|
Object.keys(props).forEach(key => {
|
|
64
55
|
let value = props[key];
|
|
65
56
|
if (signals && signals[key]) value = signals[key];
|
|
66
|
-
|
|
67
57
|
const name = key.toLowerCase();
|
|
68
|
-
const attrProp =
|
|
58
|
+
const attrProp = name === "class" || name === "classname" ? "className" : key;
|
|
69
59
|
const isProperty = ["className", "style", "value", "checked", "id", "title", "href", "src", "key"].includes(attrProp);
|
|
70
60
|
const isEvent = name.startsWith("on");
|
|
71
61
|
|
|
72
62
|
// Simple heuristic for signal detection: object with .value and .subscribe
|
|
73
63
|
const isSignal = value && typeof value === "object" && "value" in value && typeof value.subscribe === "function";
|
|
74
|
-
|
|
75
64
|
if (isSignal) {
|
|
76
65
|
effect(() => {
|
|
77
66
|
const val = value.value;
|
|
@@ -111,4 +100,4 @@ export function applySpread(el, props) {
|
|
|
111
100
|
}
|
|
112
101
|
}
|
|
113
102
|
});
|
|
114
|
-
}
|
|
103
|
+
}
|
package/package.json
CHANGED
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opentf/web",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "The core engine of the Web App Framework, including the zero-VDOM compiler and signal-based runtime.",
|
|
5
|
-
"main": "index.js",
|
|
6
|
-
"module": "index.js",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.js",
|
|
7
7
|
"type": "module",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "babel . --out-dir dist --extensions .js,.jsx --copy-files --config-file ./babel.config.cjs --ignore \"node_modules,dist,package.json,bun.lock,*.md\"",
|
|
10
|
+
"prepublishOnly": "npm run build"
|
|
11
|
+
},
|
|
8
12
|
"exports": {
|
|
9
|
-
".": "./index.js",
|
|
10
|
-
"./compiler": "./compiler/babel-plugin.cjs",
|
|
11
|
-
"./runtime": "./runtime/index.js",
|
|
12
|
-
"./router": "./router/index.js"
|
|
13
|
+
".": "./dist/index.js",
|
|
14
|
+
"./compiler": "./dist/compiler/babel-plugin.cjs",
|
|
15
|
+
"./runtime": "./dist/runtime/index.js",
|
|
16
|
+
"./router": "./dist/router/index.js"
|
|
13
17
|
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist"
|
|
20
|
+
],
|
|
14
21
|
"dependencies": {
|
|
15
22
|
"@preact/signals-core": "^1.14.1",
|
|
16
23
|
"@babel/core": "^7.29.0",
|
package/router/Link.jsx
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { navigate } from "./index"
|
|
2
|
-
|
|
3
|
-
export default function Link(props) {
|
|
4
|
-
return (
|
|
5
|
-
<a
|
|
6
|
-
href={props.href}
|
|
7
|
-
className={props.className}
|
|
8
|
-
style={props.style}
|
|
9
|
-
onclick={(e) => {
|
|
10
|
-
if (
|
|
11
|
-
e.defaultPrevented ||
|
|
12
|
-
e.metaKey ||
|
|
13
|
-
e.ctrlKey ||
|
|
14
|
-
e.shiftKey ||
|
|
15
|
-
e.altKey ||
|
|
16
|
-
e.button !== 0
|
|
17
|
-
) return
|
|
18
|
-
|
|
19
|
-
e.preventDefault()
|
|
20
|
-
navigate(props.href)
|
|
21
|
-
window.scrollTo(0, 0)
|
|
22
|
-
}}
|
|
23
|
-
>
|
|
24
|
-
{props.children}
|
|
25
|
-
</a>
|
|
26
|
-
)
|
|
27
|
-
}
|