lupine.web 1.1.4 → 1.1.5

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.
Files changed (41) hide show
  1. package/README.md +66 -3
  2. package/jsx-runtime/index.js +14 -14
  3. package/jsx-runtime/package.json +16 -16
  4. package/jsx-runtime/src/index.d.ts +2 -2
  5. package/package.json +53 -52
  6. package/src/core/bind-attributes.ts +61 -61
  7. package/src/core/bind-lang.ts +52 -52
  8. package/src/core/bind-links.ts +26 -16
  9. package/src/core/bind-meta.tsx +52 -52
  10. package/src/core/bind-ref.ts +51 -51
  11. package/src/core/bind-styles.ts +239 -239
  12. package/src/core/bind-theme.ts +53 -53
  13. package/src/core/camel-to-hyphens.ts +3 -3
  14. package/src/core/export-lupine.ts +80 -80
  15. package/src/core/index.ts +17 -17
  16. package/src/core/initialize.ts +116 -116
  17. package/src/core/mount-component.ts +72 -68
  18. package/src/core/page-loaded-events.ts +16 -16
  19. package/src/core/page-router.ts +180 -180
  20. package/src/core/render-component.ts +230 -233
  21. package/src/core/replace-innerhtml.ts +23 -23
  22. package/src/core/server-cookie.ts +24 -24
  23. package/src/global.d.ts +66 -66
  24. package/src/index.ts +14 -14
  25. package/src/jsx.ts +1044 -1043
  26. package/src/lib/cookie.ts +44 -44
  27. package/src/lib/debug-watch.ts +32 -32
  28. package/src/lib/index.ts +7 -7
  29. package/src/lib/is-frontend.ts +3 -3
  30. package/src/lib/logger.ts +55 -55
  31. package/src/lib/unique-id.ts +40 -40
  32. package/src/lib/web-config.ts +79 -79
  33. package/src/lib/web-env.ts +99 -99
  34. package/src/models/index.ts +4 -4
  35. package/src/models/json-props.ts +8 -8
  36. package/src/models/simple-storage-props.ts +9 -9
  37. package/src/models/theme-props.ts +7 -7
  38. package/src/models/to-client-delivery-props.ts +8 -8
  39. package/src/styles/css-styles.ts +814 -814
  40. package/src/styles/index.ts +4 -4
  41. package/tsconfig.json +113 -113
@@ -1,68 +1,72 @@
1
- import { bindAttributes } from './bind-attributes';
2
- import { bindLinks } from './bind-links';
3
- import { VNode } from '../jsx';
4
- // import { Logger } from '../lib/logger';
5
- // import { bindPageResetEvent } from './page-reset-events';
6
- import { callUnload, replaceInnerhtml } from './replace-innerhtml';
7
- import { renderComponent } from './render-component';
8
-
9
- // const logger = new Logger('mount-components');
10
- export const mountInnerComponent = async (selector: string | null | Element, jsxNodes: VNode<any>) => {
11
- renderComponent(jsxNodes.type, jsxNodes.props);
12
- const el = selector && (typeof selector === 'string' ? document.querySelector(selector) : selector);
13
- if (el) {
14
- // call unload before releace innerHTML
15
- await replaceInnerhtml(el, jsxNodes.props._html.join(''));
16
-
17
- bindAttributes(el, jsxNodes.type, jsxNodes.props);
18
- bindLinks(el);
19
- }
20
- };
21
-
22
- // suggest to use HtmlVar.
23
- export const mountOuterComponent = async (selector: string | Element, jsxNodes: VNode<any>) => {
24
- renderComponent(jsxNodes.type, jsxNodes.props);
25
- let el = selector && (typeof selector === 'string' ? document.querySelector(selector) : selector);
26
- if (el) {
27
- // Can't do outerHTML directly because it will lose attributes
28
- const template = document.createElement('template');
29
- // template.innerHTML = jsxNodes.props._html.join("");
30
- // call unload before releace innerHTML
31
- await replaceInnerhtml(template, jsxNodes.props._html.join(''));
32
- // renderComponent should only have one element
33
- template.content.children.length > 1 &&
34
- console.error('renderComponent should only have one element: ', template.content.children.length);
35
- const newEl = template.content.firstChild as Element;
36
- // el.replaceWith(newEl);
37
- await callUnload(el);
38
- el.parentNode?.replaceChild(newEl, el);
39
- bindAttributes(newEl, jsxNodes.type, jsxNodes.props);
40
- bindLinks(newEl);
41
- }
42
- };
43
-
44
- // suggest to use HtmlVar.
45
- export const mountSiblingComponent = async (selector: string | Element, jsxNodes: VNode<any>, position: 'before' | 'after' = 'after') => {
46
- renderComponent(jsxNodes.type, jsxNodes.props);
47
- let el = selector && (typeof selector === 'string' ? document.querySelector(selector) : selector);
48
- if (el) {
49
- // Can't do outerHTML directly because it will lose attributes
50
- const template = document.createElement('template');
51
- // template.innerHTML = jsxNodes.props._html.join("");
52
- // call unload before releace innerHTML
53
- await replaceInnerhtml(template, jsxNodes.props._html.join(''));
54
- // renderComponent should only have one element
55
- template.content.children.length > 1 &&
56
- console.error('renderComponent should only have one element: ', template.content.children.length);
57
- const newEl = template.content.firstChild as Element;
58
- // el.replaceWith(newEl);
59
- await callUnload(el);
60
- if (el.nextSibling || position === 'before') {
61
- el.parentNode?.insertBefore(newEl, position === 'after' ? el.nextSibling : el);
62
- } else {
63
- el.parentNode?.appendChild(newEl);
64
- }
65
- bindAttributes(newEl, jsxNodes.type, jsxNodes.props);
66
- bindLinks(newEl);
67
- }
68
- };
1
+ import { bindAttributes } from './bind-attributes';
2
+ import { bindLinks } from './bind-links';
3
+ import { VNode } from '../jsx';
4
+ // import { Logger } from '../lib/logger';
5
+ // import { bindPageResetEvent } from './page-reset-events';
6
+ import { callUnload, replaceInnerhtml } from './replace-innerhtml';
7
+ import { renderComponent } from './render-component';
8
+
9
+ // const logger = new Logger('mount-components');
10
+ export const mountInnerComponent = async (selector: string | null | Element, jsxNodes: VNode<any>) => {
11
+ renderComponent(jsxNodes.type, jsxNodes.props);
12
+ const el = selector && (typeof selector === 'string' ? document.querySelector(selector) : selector);
13
+ if (el) {
14
+ // call unload before releace innerHTML
15
+ await replaceInnerhtml(el, jsxNodes.props._html.join(''));
16
+
17
+ bindAttributes(el, jsxNodes.type, jsxNodes.props);
18
+ bindLinks(el);
19
+ }
20
+ };
21
+
22
+ // suggest to use HtmlVar.
23
+ export const mountOuterComponent = async (selector: string | Element, jsxNodes: VNode<any>) => {
24
+ renderComponent(jsxNodes.type, jsxNodes.props);
25
+ let el = selector && (typeof selector === 'string' ? document.querySelector(selector) : selector);
26
+ if (el) {
27
+ // Can't do outerHTML directly because it will lose attributes
28
+ const template = document.createElement('template');
29
+ // template.innerHTML = jsxNodes.props._html.join("");
30
+ // call unload before releace innerHTML
31
+ await replaceInnerhtml(template, jsxNodes.props._html.join(''));
32
+ // renderComponent should only have one element
33
+ template.content.children.length > 1 &&
34
+ console.error('renderComponent should only have one element: ', template.content.children.length);
35
+ const newEl = template.content.firstChild as Element;
36
+ // el.replaceWith(newEl);
37
+ await callUnload(el);
38
+ el.parentNode?.replaceChild(newEl, el);
39
+ bindAttributes(newEl, jsxNodes.type, jsxNodes.props);
40
+ bindLinks(newEl);
41
+ }
42
+ };
43
+
44
+ // suggest to use HtmlVar.
45
+ export const mountSiblingComponent = async (
46
+ selector: string | Element,
47
+ jsxNodes: VNode<any>,
48
+ position: 'before' | 'after' = 'after'
49
+ ) => {
50
+ renderComponent(jsxNodes.type, jsxNodes.props);
51
+ let el = selector && (typeof selector === 'string' ? document.querySelector(selector) : selector);
52
+ if (el) {
53
+ // Can't do outerHTML directly because it will lose attributes
54
+ const template = document.createElement('template');
55
+ // template.innerHTML = jsxNodes.props._html.join("");
56
+ // call unload before releace innerHTML
57
+ await replaceInnerhtml(template, jsxNodes.props._html.join(''));
58
+ // renderComponent should only have one element
59
+ template.content.children.length > 1 &&
60
+ console.error('renderComponent should only have one element: ', template.content.children.length);
61
+ const newEl = template.content.firstChild as Element;
62
+ // el.replaceWith(newEl);
63
+ await callUnload(el);
64
+ if (el.nextSibling || position === 'before') {
65
+ el.parentNode?.insertBefore(newEl, position === 'after' ? el.nextSibling : el);
66
+ } else {
67
+ el.parentNode?.appendChild(newEl);
68
+ }
69
+ bindAttributes(newEl, jsxNodes.type, jsxNodes.props);
70
+ bindLinks(newEl);
71
+ }
72
+ };
@@ -1,16 +1,16 @@
1
- // some events need to be done before the page is rendered
2
- const _pageLoadedEvents: Function[] = [];
3
- export const callPageLoadedEvent = () => {
4
- _pageLoadedEvents.forEach((i) => {
5
- try {
6
- i();
7
- } catch (e) {
8
- console.error(e);
9
- }
10
- });
11
- };
12
-
13
- // The fn is called when the page is first time loaded
14
- export const bindPageLoadedEvent = (fn: Function) => {
15
- _pageLoadedEvents.push(fn);
16
- };
1
+ // some events need to be done before the page is rendered
2
+ const _pageLoadedEvents: Function[] = [];
3
+ export const callPageLoadedEvent = () => {
4
+ _pageLoadedEvents.forEach((i) => {
5
+ try {
6
+ i();
7
+ } catch (e) {
8
+ console.error(e);
9
+ }
10
+ });
11
+ };
12
+
13
+ // The fn is called when the page is first time loaded
14
+ export const bindPageLoadedEvent = (fn: Function) => {
15
+ _pageLoadedEvents.push(fn);
16
+ };
@@ -1,180 +1,180 @@
1
- import { VNode } from '../jsx';
2
- import { isFrontEnd } from '../lib/is-frontend';
3
- import { PageProps } from './export-lupine';
4
- import { mountInnerComponent } from './mount-component';
5
- import { Logger } from '../lib/logger';
6
-
7
- export type PageRouterCallback = (props: PageProps) => Promise<VNode<any> | null>;
8
-
9
- export type PageRouterData = {
10
- path: string;
11
- handler: (PageRouterCallback | PageRouter)[];
12
- parameterVariables: string[];
13
- parameterLength: number;
14
- };
15
-
16
- export type FramePageProps = {
17
- component: (placeholderClassname: string, vnode: VNode<any>) => Promise<VNode<any>>;
18
- placeholderClassname: string;
19
- };
20
-
21
- export class PageRouter {
22
- logger = new Logger('page-router');
23
- private routerData: PageRouterData[] = [];
24
- private filter: PageRouterCallback | undefined;
25
- private framePage: FramePageProps | undefined;
26
- private subDir: string = '';
27
-
28
- // if the filter returns null (passed filter), the router will continue.
29
- // it works in the same way as in use method
30
- setFilter(filter: PageRouterCallback) {
31
- this.filter = filter;
32
- }
33
-
34
- // if the script is under a sub-dir (without last /), then findRoute needs to remove it from the url
35
- setSubDir(subDir: string) {
36
- this.subDir = subDir;
37
- }
38
-
39
- setFramePage(framePage: FramePageProps) {
40
- this.framePage = framePage;
41
- }
42
-
43
- // the path should start with / and end without /, and it can be
44
- // /aaa/:bbb/ccc/:ddd (ccc is a fixed section)
45
- // /aaa/:bbb/ccc/?ddd/?eee (from ddd, all sections are optional)
46
- // /aaa/:?bbb/ccc/ (from bbb, all sections are optional)
47
- private storeRouter(path: string, handler: (PageRouterCallback | PageRouter)[]) {
48
- let fixedPath;
49
- if (path === '*' || path === '' || path === '/*') {
50
- // removed path === '/' ||
51
- fixedPath = '*';
52
- } else {
53
- fixedPath = path;
54
- if (!fixedPath.startsWith('/')) {
55
- fixedPath = '/' + fixedPath;
56
- }
57
- if (fixedPath.endsWith('/') && fixedPath.length > 1) {
58
- fixedPath = fixedPath.substring(0, fixedPath.length - 1);
59
- }
60
- }
61
-
62
- let parameterLength = 0;
63
- let parameterVariables: string[] = [];
64
- const ind = fixedPath.indexOf('/:');
65
- if (ind >= 0) {
66
- parameterVariables = fixedPath.substring(ind + 1).split('/');
67
- fixedPath = fixedPath.substring(0, ind);
68
- // from optionInd, all will be optional
69
- const optionInd = parameterVariables.findIndex((item) => item.startsWith('?'));
70
- parameterLength = optionInd >= 0 ? optionInd : parameterVariables.length;
71
- }
72
-
73
- this.routerData.push({
74
- path: fixedPath,
75
- handler,
76
- parameterVariables,
77
- parameterLength,
78
- });
79
- }
80
-
81
- use(path: string, ...handler: (PageRouterCallback | PageRouter)[]) {
82
- this.storeRouter(path, handler);
83
- }
84
-
85
- private async callHandle(handle: PageRouterCallback, path: string, props: PageProps): Promise<VNode<any> | null> {
86
- try {
87
- const vNode = await handle(props);
88
- // logger.debug(`Processed path: ${path}`);
89
- return vNode;
90
- } catch (e: any) {
91
- this.logger.error(`Processed path: ${path}, error: ${e.message}`);
92
- // res.write(JSON.stringify({ status: 'error', message: `Processed path: ${path}, error: ${e.message}` }));
93
- }
94
- return null;
95
- }
96
-
97
- async findRoute(url: string, props: PageProps, renderPartPage: boolean): Promise<VNode<any> | null> {
98
- for (let i = 0, routerList; (routerList = this.routerData[i]); i++) {
99
- if (routerList.path === '*' || url === routerList.path || url.startsWith(routerList.path + '/')) {
100
- const parameters: { [key: string]: string } = {};
101
- let meet = true;
102
- if (routerList.parameterVariables.length > 0) {
103
- meet = false;
104
- let newUrl = url.substring(routerList.path.length + 1);
105
- if (newUrl.endsWith('/')) {
106
- newUrl = newUrl.substring(0, newUrl.length - 1);
107
- }
108
- const restPath = newUrl.split('/');
109
- // the path must have mandatory parameters but some parameters can be optional
110
- if (
111
- restPath.length >= routerList.parameterLength &&
112
- restPath.length <= routerList.parameterVariables.length
113
- ) {
114
- meet = true;
115
- for (const [index, item] of routerList.parameterVariables.entries()) {
116
- if (!item.startsWith(':') && !item.startsWith('?') && item !== restPath[index]) {
117
- meet = false;
118
- break;
119
- } else if ((item.startsWith(':') || item.startsWith('?')) && index < restPath.length) {
120
- parameters[item.replace(/[:?]/g, '')] = restPath[index];
121
- }
122
- }
123
- props.urlParameters = parameters;
124
- }
125
- }
126
-
127
- if (meet) {
128
- for (let j = 0, router; (router = routerList.handler[j]); j++) {
129
- if (router instanceof PageRouter) {
130
- // it's a sub-level router
131
- const nextPath =
132
- routerList.path === '*' || (url === '/' && routerList.path === '/')
133
- ? url
134
- : url.substring(routerList.path.length);
135
- // TODO: sub-level?
136
- const vNode = await router.handleRoute(nextPath, props, renderPartPage);
137
- if (vNode) {
138
- return vNode;
139
- }
140
- } else {
141
- // it should be a function
142
- // the query's url should match the api's path
143
- const dom = await this.callHandle(router, url, props);
144
- if (dom) {
145
- return dom;
146
- }
147
- }
148
- }
149
- // stop process for this path if no page is found
150
- return null;
151
- }
152
- }
153
- }
154
- return null;
155
- }
156
-
157
- async handleRoute(url: string, props: PageProps, renderPartPage: boolean): Promise<VNode<any> | null> {
158
- if (url.startsWith(this.subDir)) {
159
- url = url.substring(this.subDir.length);
160
- }
161
- let vNode = null;
162
- if (this.filter) {
163
- vNode = await this.callHandle(this.filter, url, props);
164
- }
165
- if (!vNode) {
166
- vNode = await this.findRoute(url, props, renderPartPage);
167
- }
168
-
169
- if (vNode && this.framePage) {
170
- const selector = '.' + this.framePage.placeholderClassname;
171
- if (renderPartPage && isFrontEnd() && document.querySelector(selector)) {
172
- await mountInnerComponent(selector, vNode);
173
- return null;
174
- } else {
175
- return this.framePage.component(this.framePage.placeholderClassname, vNode);
176
- }
177
- }
178
- return vNode;
179
- }
180
- }
1
+ import { VNode } from '../jsx';
2
+ import { isFrontEnd } from '../lib/is-frontend';
3
+ import { PageProps } from './export-lupine';
4
+ import { mountInnerComponent } from './mount-component';
5
+ import { Logger } from '../lib/logger';
6
+
7
+ export type PageRouterCallback = (props: PageProps) => Promise<VNode<any> | null>;
8
+
9
+ export type PageRouterData = {
10
+ path: string;
11
+ handler: (PageRouterCallback | PageRouter)[];
12
+ parameterVariables: string[];
13
+ parameterLength: number;
14
+ };
15
+
16
+ export type FramePageProps = {
17
+ component: (placeholderClassname: string, vnode: VNode<any>) => Promise<VNode<any>>;
18
+ placeholderClassname: string;
19
+ };
20
+
21
+ export class PageRouter {
22
+ logger = new Logger('page-router');
23
+ private routerData: PageRouterData[] = [];
24
+ private filter: PageRouterCallback | undefined;
25
+ private framePage: FramePageProps | undefined;
26
+ private subDir: string = '';
27
+
28
+ // if the filter returns null (passed filter), the router will continue.
29
+ // it works in the same way as in use method
30
+ setFilter(filter: PageRouterCallback) {
31
+ this.filter = filter;
32
+ }
33
+
34
+ // if the script is under a sub-dir (without last /), then findRoute needs to remove it from the url
35
+ setSubDir(subDir: string) {
36
+ this.subDir = subDir;
37
+ }
38
+
39
+ setFramePage(framePage: FramePageProps) {
40
+ this.framePage = framePage;
41
+ }
42
+
43
+ // the path should start with / and end without /, and it can be
44
+ // /aaa/:bbb/ccc/:ddd (ccc is a fixed section)
45
+ // /aaa/:bbb/ccc/?ddd/?eee (from ddd, all sections are optional)
46
+ // /aaa/:?bbb/ccc/ (from bbb, all sections are optional)
47
+ private storeRouter(path: string, handler: (PageRouterCallback | PageRouter)[]) {
48
+ let fixedPath;
49
+ if (path === '*' || path === '' || path === '/*') {
50
+ // removed path === '/' ||
51
+ fixedPath = '*';
52
+ } else {
53
+ fixedPath = path;
54
+ if (!fixedPath.startsWith('/')) {
55
+ fixedPath = '/' + fixedPath;
56
+ }
57
+ if (fixedPath.endsWith('/') && fixedPath.length > 1) {
58
+ fixedPath = fixedPath.substring(0, fixedPath.length - 1);
59
+ }
60
+ }
61
+
62
+ let parameterLength = 0;
63
+ let parameterVariables: string[] = [];
64
+ const ind = fixedPath.indexOf('/:');
65
+ if (ind >= 0) {
66
+ parameterVariables = fixedPath.substring(ind + 1).split('/');
67
+ fixedPath = fixedPath.substring(0, ind);
68
+ // from optionInd, all will be optional
69
+ const optionInd = parameterVariables.findIndex((item) => item.startsWith('?'));
70
+ parameterLength = optionInd >= 0 ? optionInd : parameterVariables.length;
71
+ }
72
+
73
+ this.routerData.push({
74
+ path: fixedPath,
75
+ handler,
76
+ parameterVariables,
77
+ parameterLength,
78
+ });
79
+ }
80
+
81
+ use(path: string, ...handler: (PageRouterCallback | PageRouter)[]) {
82
+ this.storeRouter(path, handler);
83
+ }
84
+
85
+ private async callHandle(handle: PageRouterCallback, path: string, props: PageProps): Promise<VNode<any> | null> {
86
+ try {
87
+ const vNode = await handle(props);
88
+ // logger.debug(`Processed path: ${path}`);
89
+ return vNode;
90
+ } catch (e: any) {
91
+ this.logger.error(`Processed path: ${path}, error: ${e.message}`);
92
+ // res.write(JSON.stringify({ status: 'error', message: `Processed path: ${path}, error: ${e.message}` }));
93
+ }
94
+ return null;
95
+ }
96
+
97
+ async findRoute(url: string, props: PageProps, renderPartPage: boolean): Promise<VNode<any> | null> {
98
+ for (let i = 0, routerList; (routerList = this.routerData[i]); i++) {
99
+ if (routerList.path === '*' || url === routerList.path || url.startsWith(routerList.path + '/')) {
100
+ const parameters: { [key: string]: string } = {};
101
+ let meet = true;
102
+ if (routerList.parameterVariables.length > 0) {
103
+ meet = false;
104
+ let newUrl = url.substring(routerList.path.length + 1);
105
+ if (newUrl.endsWith('/')) {
106
+ newUrl = newUrl.substring(0, newUrl.length - 1);
107
+ }
108
+ const restPath = newUrl.split('/');
109
+ // the path must have mandatory parameters but some parameters can be optional
110
+ if (
111
+ restPath.length >= routerList.parameterLength &&
112
+ restPath.length <= routerList.parameterVariables.length
113
+ ) {
114
+ meet = true;
115
+ for (const [index, item] of routerList.parameterVariables.entries()) {
116
+ if (!item.startsWith(':') && !item.startsWith('?') && item !== restPath[index]) {
117
+ meet = false;
118
+ break;
119
+ } else if ((item.startsWith(':') || item.startsWith('?')) && index < restPath.length) {
120
+ parameters[item.replace(/[:?]/g, '')] = restPath[index];
121
+ }
122
+ }
123
+ props.urlParameters = parameters;
124
+ }
125
+ }
126
+
127
+ if (meet) {
128
+ for (let j = 0, router; (router = routerList.handler[j]); j++) {
129
+ if (router instanceof PageRouter) {
130
+ // it's a sub-level router
131
+ const nextPath =
132
+ routerList.path === '*' || (url === '/' && routerList.path === '/')
133
+ ? url
134
+ : url.substring(routerList.path.length);
135
+ // TODO: sub-level?
136
+ const vNode = await router.handleRoute(nextPath, props, renderPartPage);
137
+ if (vNode) {
138
+ return vNode;
139
+ }
140
+ } else {
141
+ // it should be a function
142
+ // the query's url should match the api's path
143
+ const dom = await this.callHandle(router, url, props);
144
+ if (dom) {
145
+ return dom;
146
+ }
147
+ }
148
+ }
149
+ // stop process for this path if no page is found
150
+ return null;
151
+ }
152
+ }
153
+ }
154
+ return null;
155
+ }
156
+
157
+ async handleRoute(url: string, props: PageProps, renderPartPage: boolean): Promise<VNode<any> | null> {
158
+ if (url.startsWith(this.subDir)) {
159
+ url = url.substring(this.subDir.length);
160
+ }
161
+ let vNode = null;
162
+ if (this.filter) {
163
+ vNode = await this.callHandle(this.filter, url, props);
164
+ }
165
+ if (!vNode) {
166
+ vNode = await this.findRoute(url, props, renderPartPage);
167
+ }
168
+
169
+ if (vNode && this.framePage) {
170
+ const selector = '.' + this.framePage.placeholderClassname;
171
+ if (renderPartPage && isFrontEnd() && document.querySelector(selector)) {
172
+ await mountInnerComponent(selector, vNode);
173
+ return null;
174
+ } else {
175
+ return this.framePage.component(this.framePage.placeholderClassname, vNode);
176
+ }
177
+ }
178
+ return vNode;
179
+ }
180
+ }