vaderjs 1.3.1 → 1.3.3-2124566be812
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/README.md +247 -0
- package/client/index.js +705 -0
- package/package.json +26 -20
- package/runtime/index.html +20 -0
- package/runtime/router.js +1 -0
- package/runtime/vader.js +1 -0
- package/vader.js +1314 -1178
- package/.vscode/settings.json +0 -5
- package/.vscode/vaderjs.autosense.json +0 -5
- package/index.js +0 -12
- package/jsconfig.json +0 -17
- package/readme.md +0 -262
- package/tsconfig.json +0 -18
- package/vader-min.js +0 -1
- package/vaderRouter.js +0 -231
- package/worker-min.js +0 -1
- package/worker.js +0 -223
package/vader.js
CHANGED
|
@@ -1,1272 +1,1408 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
let level = header.split('#').length;
|
|
19
|
-
content = content.replace(header, `<h${level} class="markdown_heading">${header.replace(/#/g, '')}</h${level}>`);
|
|
20
|
-
});
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import { glob, globSync, globStream, globStreamSync, Glob, } from 'glob'
|
|
4
|
+
import puppeteer from 'puppeteer';
|
|
5
|
+
import http from 'http'
|
|
6
|
+
import { WebSocketServer } from 'ws'
|
|
7
|
+
import { watch } from "fs";
|
|
8
|
+
import path from 'path'
|
|
9
|
+
let config = await import('file://' + process.cwd() + '/vader.config.js').then((e) => e.default || e)
|
|
10
|
+
let writer = async (file, data) => {
|
|
11
|
+
globalThis.isWriting = file
|
|
12
|
+
switch (true) {
|
|
13
|
+
case !fs.existsSync(file):
|
|
14
|
+
fs.mkdirSync(file.split('/').slice(0, -1).join('/'), { recursive: true })
|
|
15
|
+
break;
|
|
21
16
|
}
|
|
17
|
+
if (globalThis.isWriting !== file) {
|
|
18
|
+
return
|
|
19
|
+
}
|
|
20
|
+
await fs.writeFileSync(file, data);
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
content = content.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (match, text, url) => {
|
|
33
|
-
return `<a class="markdown_link" href="${url}">${text}</a>`;
|
|
34
|
-
});
|
|
35
|
-
content = content.replace(/!\[([^\]]+)\]\(([^)]+)\)/g, (match, alt, src) => {
|
|
36
|
-
return `<img class="markdown_image" src="${src}" alt="${alt}" />`;
|
|
37
|
-
});
|
|
38
|
-
content = content.split('\n').map((line, index, arr) => {
|
|
39
|
-
if (line.match(/^\s*-\s+(.*?)$/gm)) {
|
|
40
|
-
if (index === 0 || !arr[index - 1].match(/^\s*-\s+(.*?)$/gm)) {
|
|
41
|
-
return `<ul class="markdown_unordered" style="list-style-type:disc;list-style:inside"><li>${line.replace(/^\s*-\s+(.*?)$/gm, '$1')}</li>`;
|
|
42
|
-
} else if (index === arr.length - 1 || !arr[index + 1].match(/^\s*-\s+(.*?)$/gm)) {
|
|
43
|
-
return `<li>${line.replace(/^\s*-\s+(.*?)$/gm, '$1')}</li></ul>`;
|
|
44
|
-
} else {
|
|
45
|
-
return `<li>${line.replace(/^\s*-\s+(.*?)$/gm, '$1')}</li>`;
|
|
46
|
-
}
|
|
47
|
-
} else {
|
|
48
|
-
return line;
|
|
49
|
-
}
|
|
50
|
-
}).join('\n');
|
|
51
|
-
|
|
52
|
-
content = content.split('\n').map((line, index, arr) => {
|
|
53
|
-
if (line.match(/^\s*\d+\.\s+(.*?)$/gm)) {
|
|
54
|
-
if (index === 0 || !arr[index - 1].match(/^\s*\d+\.\s+(.*?)$/gm)) {
|
|
55
|
-
return `<ol class="markdown_ordered" style="list-style-type:decimal;"><li>${line.replace(/^\s*\d+\.\s+(.*?)$/gm, '$1')}</li>`;
|
|
56
|
-
} else if (index === arr.length - 1 || !arr[index + 1].match(/^\s*\d+\.\s+(.*?)$/gm)) {
|
|
57
|
-
return `<li>${line.replace(/^\s*\d+\.\s+(.*?)$/gm, '$1')}</li></ol>`;
|
|
58
|
-
} else {
|
|
59
|
-
return `<li>${line.replace(/^\s*\d+\.\s+(.*?)$/gm, '$1')}</li>`;
|
|
60
|
-
}
|
|
61
|
-
} else {
|
|
62
|
-
return line;
|
|
63
|
-
}
|
|
64
|
-
}).join('\n');
|
|
22
|
+
globalThis.isWriting = null
|
|
23
|
+
return { _written: true };
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
let bundleSize = 0;
|
|
27
|
+
|
|
28
|
+
if (!fs.existsSync(process.cwd() + '/dist')) {
|
|
29
|
+
fs.mkdirSync(process.cwd() + '/dist')
|
|
30
|
+
}
|
|
65
31
|
|
|
66
32
|
|
|
67
|
-
|
|
68
|
-
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
if (typeof process.env.isCloudflare !== "undefined" || !fs.existsSync(process.cwd() + '/dist/index.html')) {
|
|
36
|
+
let htmlFile = fs.readFileSync(process.cwd() + "/node_modules/vaderjs/runtime/index.html", 'utf8')
|
|
37
|
+
fs.writeFileSync(process.cwd() + "/dist/index.html", htmlFile)
|
|
69
38
|
}
|
|
70
39
|
|
|
71
|
-
|
|
72
40
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
41
|
+
|
|
42
|
+
function Compiler(func, file) {
|
|
43
|
+
let string = func;
|
|
44
|
+
let returns = []
|
|
45
|
+
let comments = string.match(/\{\s*\/\*.*\*\/\s*}/gs)?.map((comment) => comment.trim());
|
|
46
|
+
|
|
79
47
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
let childs = [];
|
|
53
|
+
|
|
54
|
+
const spreadAttributeRegex = /\s*([a-zA-Z0-9_-]+)(\s*\$\s*=\s*{{((?:[^{}]|(?:\{(?:[^{}]|(?:\{[^{}]*\})*)*\})*)*)}})/gs;
|
|
55
|
+
let spreadMatch;
|
|
56
|
+
while ((spreadMatch = spreadAttributeRegex.exec(string)) !== null) {
|
|
57
|
+
let [, element, spread] = spreadMatch;
|
|
58
|
+
let isJSXComponent = element.match(/[A-Z]/) ? true : false;
|
|
59
|
+
if (isJSXComponent) {
|
|
60
|
+
continue
|
|
61
|
+
|
|
95
62
|
}
|
|
96
|
-
|
|
63
|
+
let old = spread;
|
|
97
64
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
65
|
+
// turn spread into attributes
|
|
66
|
+
spread = spread.replace(/\s*$\s*=\s*/, "");
|
|
67
|
+
spread = spread.replace(/{{/, "");
|
|
68
|
+
spread = spread.replace(/}}/, "");
|
|
69
|
+
spread = spread.replace(/\$\s*=\s*/, "");
|
|
70
|
+
|
|
71
|
+
// turn : into =
|
|
72
|
+
|
|
73
|
+
// do not split inner Objects ex: {color: 'red', background: {color: 'blue'}} -> {color: 'red', background: {color: 'blue'}}
|
|
74
|
+
let splitByCommas = spread.split(/,(?![^{]*})/g);
|
|
75
|
+
splitByCommas = splitByCommas.map((e) => e.trim())
|
|
76
|
+
splitByCommas = splitByCommas.map((e) => {
|
|
77
|
+
switch (true) {
|
|
78
|
+
case e.includes('function') || e.includes('=>'):
|
|
79
|
+
e = e.replace(/:(.*)/gs, '={$1}')
|
|
80
|
+
break;
|
|
81
|
+
case e.includes('style'):
|
|
82
|
+
e = e.replace(/:(.*)/gs, '="${this.parseStyle($1)}"')
|
|
83
|
+
break;
|
|
84
|
+
case e.includes('[') && e.includes(']'):
|
|
85
|
+
e = e.replace(/:(.*)/gs, '={$1.join(" ")}')
|
|
86
|
+
break;
|
|
87
|
+
default:
|
|
88
|
+
e = e.replace(/:(.*)/gs, '=$1')
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return e.trim()
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
let newSpread = `\t` + splitByCommas.join(' ') + `\t`
|
|
96
|
+
|
|
97
|
+
string = string.replace(old, newSpread);
|
|
98
|
+
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
function extractAttributes(code) {
|
|
104
|
+
// Match elements with opening tags
|
|
105
|
+
const elementRegex = /<([a-zA-Z0-9_-]+)([^>]*)>/gs;
|
|
106
|
+
|
|
107
|
+
// Match attributes in an opening tag, including those with ={}
|
|
108
|
+
// Match attributes in an opening tag, including those with ={...}
|
|
109
|
+
const attributeRegex =
|
|
110
|
+
/\s*([a-zA-Z0-9_-]+)(\s*=\s*("([^"\\]*(\\.[^"\\]*)*)"|'([^'\\]*(\\.[^'\\]*)*)'|\{(?:[^{}]|(?:\{(?:[^{}]|(?:\{[^{}]*\})*)*\})*)*\}|(?:\([^)]*\)|\{[^}]*\}|()=>\s*(?:\{[^}]*\})?)|\[[^\]]*\]))?/gs;
|
|
103
111
|
|
|
104
|
-
let components = [];
|
|
105
|
-
/**
|
|
106
|
-
* @class Component
|
|
107
|
-
* @description Allows you to create a component
|
|
108
|
-
* @returns {void}
|
|
109
|
-
* @example
|
|
110
|
-
* import { Vader } from "../../dist/vader/index.js";
|
|
111
|
-
* export class Home extends Vader.Component {
|
|
112
|
-
* constructor() {
|
|
113
|
-
* super();
|
|
114
|
-
* }
|
|
115
|
-
* async render() {
|
|
116
|
-
* return this.html(`
|
|
117
|
-
* <div className="hero p-5">
|
|
118
|
-
* <h1>Home</h1>
|
|
119
|
-
* </div>
|
|
120
|
-
* `);
|
|
121
|
-
* }
|
|
122
|
-
* }
|
|
123
|
-
*/
|
|
124
|
-
export class Component {
|
|
125
|
-
constructor() {
|
|
126
|
-
this.states = {};
|
|
127
|
-
//@ts-ignore
|
|
128
|
-
this.name = this.constructor.name;
|
|
129
|
-
this.executedEffects = {};
|
|
130
|
-
this.storedProps = {};
|
|
131
|
-
this.componentMounted = false;
|
|
132
|
-
this.hasMounted = false;
|
|
133
|
-
this.$_signal_subscribers = [];
|
|
134
|
-
/**
|
|
135
|
-
* @property {Array} $_signal_subscribers_ran
|
|
136
|
-
* @description Allows you to keep track of signal subscribers
|
|
137
|
-
* @private
|
|
138
|
-
*/
|
|
139
|
-
this.$_signal_subscribers_ran = [];
|
|
140
|
-
this.effects = {};
|
|
141
|
-
this.$_useStore_subscribers = [];
|
|
142
|
-
this.init();
|
|
143
|
-
this.Componentcontent = null;
|
|
144
|
-
this.$_signal_dispatch_event = new CustomEvent("signalDispatch", {
|
|
145
|
-
detail: {
|
|
146
|
-
hasUpdated: false,
|
|
147
|
-
state: null,
|
|
148
|
-
},
|
|
149
|
-
});
|
|
150
|
-
this.snapshots = [];
|
|
151
|
-
/**
|
|
152
|
-
* @property {Object} dom
|
|
153
|
-
* @description Allows you to get reference to DOM element
|
|
154
|
-
* @returns {void | HTMLElement}
|
|
155
|
-
*
|
|
156
|
-
*/
|
|
157
|
-
this.dom = []
|
|
158
112
|
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
const functionAttributeRegex = /\s*([a-zA-Z0-9_-]+)(\s*=\s*{((?:[^{}]|(?:\{(?:[^{}]|(?:\{[^{}]*\})*)*\})*)*)})/gs;
|
|
116
|
+
|
|
117
|
+
let attributesList = [];
|
|
118
|
+
|
|
119
|
+
let spreadAttributes = [];
|
|
120
|
+
let spreadMatch;
|
|
159
121
|
/**
|
|
160
|
-
* @
|
|
161
|
-
* @
|
|
162
|
-
*
|
|
122
|
+
* @search - handle spread for html elements
|
|
123
|
+
* @keywords - spread, spread attributes, spread props, spread html attributes
|
|
163
124
|
*/
|
|
164
|
-
|
|
125
|
+
|
|
126
|
+
|
|
165
127
|
/**
|
|
166
|
-
* @
|
|
167
|
-
* @
|
|
168
|
-
|
|
128
|
+
* @search - handle function parsing for html elements
|
|
129
|
+
* @keywords - function, function attributes, function props, function html attributes
|
|
130
|
+
*
|
|
169
131
|
*/
|
|
170
|
-
|
|
171
|
-
|
|
132
|
+
let functionAttributes = [];
|
|
133
|
+
let functionMatch;
|
|
134
|
+
while ((functionMatch = functionAttributeRegex.exec(code)) !== null) {
|
|
172
135
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
* @description Allows you to create an adapter - this is used to create custom logic
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
*/
|
|
179
|
-
adapter() {
|
|
180
|
-
return
|
|
181
|
-
}
|
|
182
|
-
init() {
|
|
183
|
-
this.registerComponent();
|
|
184
|
-
|
|
185
|
-
}
|
|
136
|
+
let [, attributeName, attributeValue] = functionMatch;
|
|
137
|
+
let attribute = {};
|
|
186
138
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
139
|
+
if (attributeValue && attributeValue.includes("=>") || attributeValue && attributeValue.includes("function")
|
|
140
|
+
&& !spreadFunctions.includes(attributeValue)
|
|
141
|
+
) {
|
|
142
|
+
|
|
143
|
+
let ref = Math.random().toString(36).substring(2).split('').filter((e) => !Number(e)).join('')
|
|
144
|
+
let old = `${attributeName}${attributeValue}`
|
|
145
|
+
|
|
146
|
+
let elementMatch = string.match(/<([a-zA-Z0-9_-]+)([^>]*)>/gs);
|
|
147
|
+
let isJSXComponent = false;
|
|
148
|
+
elementMatch.forEach((element) => {
|
|
149
|
+
element = element.trim().replace(/\s+/g, " ");
|
|
150
|
+
if (element.includes(attributeName)) {
|
|
151
|
+
let elementTag = element
|
|
152
|
+
.split("<")[1]
|
|
153
|
+
.split(">")[0]
|
|
154
|
+
.split(" ")[0];
|
|
155
|
+
isJSXComponent = elementTag.match(/^[A-Z]/) ? true : false;
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
// add ; after newlines
|
|
190
159
|
|
|
191
|
-
/**
|
|
192
|
-
* @method setState
|
|
193
|
-
* @description Allows you to set state
|
|
194
|
-
* @param {String} key
|
|
195
|
-
* @param {*} value
|
|
196
|
-
* @returns {void}
|
|
197
|
-
* @example
|
|
198
|
-
* this.setState('count', 1)
|
|
199
|
-
* */
|
|
200
|
-
setState(key, value) {
|
|
201
|
-
this.states[key] = value;
|
|
202
|
-
this.updateComponent();
|
|
203
|
-
}
|
|
204
|
-
/**
|
|
205
|
-
* @method componentUnmount
|
|
206
|
-
* @description Allows you to run code after component has unmounted
|
|
207
|
-
* @type {VoidFunction}
|
|
208
|
-
* @returns {void}
|
|
209
|
-
*/
|
|
210
|
-
unmount() {
|
|
211
|
-
this.componentMounted = false;
|
|
212
|
-
this.componentWillUnmount();
|
|
213
|
-
// @ts-ignore
|
|
214
|
-
document.querySelector(`[data-component="${this.name}"]`).remove();
|
|
215
|
-
if (!document.querySelector(`[data-component="${this.name}"]`)) {
|
|
216
|
-
components = components.filter(
|
|
217
|
-
(component) => component.name !== this.name
|
|
218
|
-
);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
160
|
|
|
222
|
-
|
|
223
|
-
* @method componentUpdate
|
|
224
|
-
* @description Allows you to run code after component has updated
|
|
225
|
-
* @param {Object} prev_state
|
|
226
|
-
* @param {Object} prev_props
|
|
227
|
-
* @param {Object} snapshot
|
|
228
|
-
* @returns {void}
|
|
229
|
-
* @example
|
|
230
|
-
* componentUpdate(prev_state, prev_props, snapshot) {
|
|
231
|
-
* console.log(prev_state, prev_props, snapshot)
|
|
232
|
-
* }
|
|
233
|
-
* */
|
|
234
|
-
componentUpdate(prev_state, prev_props, snapshot) {}
|
|
235
|
-
/**
|
|
236
|
-
* @method componentDidMount
|
|
237
|
-
* @description Allows you to run code after component has mounted
|
|
238
|
-
*/
|
|
239
|
-
componentDidMount() {}
|
|
161
|
+
let newvalue = attributeValue.includes('=>') ? attributeValue.split("=>").slice(1).join("=>").trim() : attributeValue.split("function").slice(1).join("function").trim()
|
|
240
162
|
|
|
241
|
-
/**
|
|
242
|
-
* @method componentWillUnmount
|
|
243
|
-
* @description Allows you to run code before component unmounts
|
|
244
|
-
* @type {VoidFunction}
|
|
245
|
-
* @returns {void}
|
|
246
|
-
*/
|
|
247
|
-
componentWillUnmount() {}
|
|
248
163
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
164
|
+
|
|
165
|
+
newvalue = newvalue.trim();
|
|
166
|
+
|
|
167
|
+
//remove starting {
|
|
168
|
+
newvalue = newvalue.replace("{", "")
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
let params = attributeValue
|
|
173
|
+
.split("=>")[0]
|
|
174
|
+
.split("(")[1]
|
|
175
|
+
.split(")")[0]
|
|
176
|
+
.trim();
|
|
177
|
+
|
|
178
|
+
// remove comments
|
|
179
|
+
params = params.match(/\/\*.*\*\//gs)
|
|
180
|
+
? params.replace(params.match(/\/\*.*\*\//gs)[0], "")
|
|
181
|
+
: params;
|
|
182
|
+
// split first {}
|
|
183
|
+
newvalue = newvalue.trim();
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
newvalue = newvalue.replace(/}\s*$/, '');
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
newvalue = newvalue.trim();
|
|
192
|
+
|
|
193
|
+
// remmove trailing }
|
|
194
|
+
|
|
195
|
+
newvalue = newvalue.trim();
|
|
196
|
+
newvalue = newvalue.replace(/}\s*$/, '');
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
newvalue = newvalue.replaceAll(',,', ',')
|
|
202
|
+
let paramnames = params ? params.split(',').map((e) => e.trim()) : null
|
|
203
|
+
paramnames = paramnames ? paramnames.filter((e) => e.length > 0) : null
|
|
204
|
+
// remove comments
|
|
205
|
+
paramnames = paramnames ? paramnames.map((e) => e.match(/\/\*.*\*\//gs) ? e.replace(e.match(/\/\*.*\*\//gs)[0], "") : e) : null
|
|
206
|
+
|
|
207
|
+
// add ; after newlines
|
|
208
|
+
newvalue = newvalue.replaceAll(/\n/g, ";\n")
|
|
209
|
+
|
|
210
|
+
let bind = isJSXComponent ? `${attributeName}='function(${params}){${newvalue}}'` : `${attributeName}="\$\{this.bind(function(){${newvalue}}.bind(this), ${isJSXComponent}, "${ref}", "${paramnames ? paramnames.map((e, index) => {
|
|
211
|
+
if (e.length < 1) return ''
|
|
212
|
+
if (e.length > 0) {
|
|
213
|
+
index == 0 ? e : ',' + e
|
|
287
214
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
window.dispatchEvent(this.$_signal_dispatch_event);
|
|
215
|
+
return e
|
|
216
|
+
}) : ''}" ${params ? params.split(',').map((e) => e.trim()).filter(Boolean).map((e) => `,${e}`).join('') : ''})}"`
|
|
217
|
+
|
|
218
|
+
string = string.replace(old, bind);
|
|
293
219
|
}
|
|
294
|
-
}
|
|
220
|
+
}
|
|
221
|
+
|
|
295
222
|
/**
|
|
296
|
-
* @
|
|
297
|
-
* @
|
|
298
|
-
* @param {*} fn
|
|
299
|
-
* @param {*} runonce
|
|
300
|
-
* @returns {void}
|
|
301
|
-
*
|
|
223
|
+
* @search - handle attributes for html elements
|
|
224
|
+
* @keywords - attributes, props, html attributes
|
|
302
225
|
*/
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
this.$_signal_dispatch = () => {
|
|
315
|
-
for (var i = 0; i < this.$_signal_subscribers.length; i++) {
|
|
316
|
-
if (
|
|
317
|
-
this.$_signal_subscribers[i].runonce &&
|
|
318
|
-
// @ts-ignore
|
|
319
|
-
this.$_signal_subscribers_ran.includes(this.$_signal_subscribers[i])
|
|
320
|
-
) {
|
|
321
|
-
break;
|
|
322
|
-
} else {
|
|
323
|
-
this.$_signal_subscribers[i].function(state);
|
|
324
|
-
this.$_signal_subscribers_ran.push(this.$_signal_subscribers[i]);
|
|
325
|
-
}
|
|
226
|
+
let match;
|
|
227
|
+
while ((match = elementRegex.exec(code)) !== null) {
|
|
228
|
+
let [, element, attributes] = match;
|
|
229
|
+
|
|
230
|
+
let attributesMatch;
|
|
231
|
+
let elementAttributes = {};
|
|
232
|
+
|
|
233
|
+
while ((attributesMatch = attributeRegex.exec(attributes)) !== null) {
|
|
234
|
+
let [, attributeName, attributeValue] = attributesMatch;
|
|
235
|
+
|
|
236
|
+
elementAttributes[attributeName] = attributeValue || null;
|
|
326
237
|
}
|
|
327
|
-
};
|
|
328
|
-
this.$_signal_get = () => {
|
|
329
|
-
return state;
|
|
330
|
-
};
|
|
331
|
-
this.$_signal_call = () => {
|
|
332
|
-
hasCaller = true;
|
|
333
|
-
// @ts-ignore
|
|
334
|
-
this.$_signal_dispatch();
|
|
335
|
-
};
|
|
336
|
-
/**
|
|
337
|
-
* @function $_signal_set
|
|
338
|
-
* @description Allows you to set the value of a signal
|
|
339
|
-
* @param {*} detail
|
|
340
|
-
*/
|
|
341
|
-
this.$_signal_set = (detail) => {
|
|
342
|
-
setState(detail);
|
|
343
|
-
};
|
|
344
238
|
|
|
345
|
-
|
|
346
|
-
/**
|
|
347
|
-
* @function subscribe
|
|
348
|
-
* @description Allows you to subscribe to a signal
|
|
349
|
-
* @param {*} fn
|
|
350
|
-
* @param {*} runonce
|
|
351
|
-
*/
|
|
352
|
-
subscribe: this.$_signal_subscribe,
|
|
353
|
-
/**
|
|
354
|
-
* @function cleanup
|
|
355
|
-
* @description Allows you to cleanup a signal
|
|
356
|
-
* @param {*} fn
|
|
357
|
-
* @returns {null}
|
|
358
|
-
*/
|
|
359
|
-
cleanup: this.$_signal_cleanup,
|
|
360
|
-
/**
|
|
361
|
-
* @function dispatch
|
|
362
|
-
* @description Allows you to dispatch a signal
|
|
363
|
-
* @returns {null}
|
|
364
|
-
*/
|
|
365
|
-
dispatch: this.$_signal_dispatch,
|
|
366
|
-
/**
|
|
367
|
-
* @function call
|
|
368
|
-
* @description Allows you to call a signal
|
|
369
|
-
* @returns {null}
|
|
370
|
-
*/
|
|
371
|
-
call: this.$_signal_call,
|
|
372
|
-
/**
|
|
373
|
-
* @function set
|
|
374
|
-
* @description Allows you to set the value of a signal
|
|
375
|
-
* @param {*} detail
|
|
376
|
-
* @returns {null}
|
|
377
|
-
*/
|
|
378
|
-
set: this.$_signal_set,
|
|
379
|
-
/**
|
|
380
|
-
* @function get
|
|
381
|
-
* @readonly
|
|
382
|
-
* @description Allows you to get the value of a signal
|
|
383
|
-
* @returns {any}
|
|
384
|
-
*/
|
|
385
|
-
get: this.$_signal_get,
|
|
386
|
-
};
|
|
387
|
-
};
|
|
388
|
-
/**
|
|
389
|
-
* @method useAuth
|
|
390
|
-
* @description Allows you to create an auth object
|
|
391
|
-
* @param {Object} options
|
|
392
|
-
* @param {Array} options.rulesets
|
|
393
|
-
* @param {Object} options.user
|
|
394
|
-
* @returns {Object} {can, hasRole, canWithRole, assignRule, revokeRule, canAnyOf, canAllOf, canGroup}
|
|
395
|
-
* @example
|
|
396
|
-
* let auth = this.useAuth({
|
|
397
|
-
* rulesets: [
|
|
398
|
-
* {
|
|
399
|
-
* action: 'create',
|
|
400
|
-
* condition: (user) => {
|
|
401
|
-
* return user.role === 'admin'
|
|
402
|
-
* }
|
|
403
|
-
* }
|
|
404
|
-
* ],
|
|
405
|
-
* user: {
|
|
406
|
-
* role: 'admin'
|
|
407
|
-
* }
|
|
408
|
-
* })
|
|
409
|
-
* auth.can('create') // true
|
|
410
|
-
*/
|
|
411
|
-
useAuth(options) {
|
|
412
|
-
/**@type {Array}**/
|
|
413
|
-
let rules = options.rulesets;
|
|
414
|
-
if (!rules) {
|
|
415
|
-
throw new Error("No rulesets provided");
|
|
239
|
+
attributesList.push({ element, attributes: elementAttributes });
|
|
416
240
|
}
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
let auth = {
|
|
420
|
-
can: (action) => {
|
|
421
|
-
let can = false;
|
|
422
|
-
rules.forEach((rule) => {
|
|
423
|
-
if (rule.action === action) {
|
|
424
|
-
if (rule.condition(user)) {
|
|
425
|
-
can = true;
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
});
|
|
429
|
-
return can;
|
|
430
|
-
},
|
|
431
|
-
/**
|
|
432
|
-
* @function hasRole
|
|
433
|
-
* @description Allows you to check if user has a role
|
|
434
|
-
* @param {String} role
|
|
435
|
-
* @returns {Boolean}
|
|
436
|
-
*/
|
|
437
|
-
hasRole: (role) => {
|
|
438
|
-
return user.role && user.role.includes(role);
|
|
439
|
-
},
|
|
440
|
-
/**
|
|
441
|
-
* @function canWithRole
|
|
442
|
-
* @param {String} action
|
|
443
|
-
* @param {String} role
|
|
444
|
-
* @returns
|
|
445
|
-
*/
|
|
446
|
-
canWithRole: (action, role) => {
|
|
447
|
-
return auth.can(action) && auth.hasRole(role);
|
|
448
|
-
},
|
|
449
|
-
assignRule: (rule) => {
|
|
450
|
-
if (
|
|
451
|
-
!rules.some((existingRule) => existingRule.action === rule.action)
|
|
452
|
-
) {
|
|
453
|
-
rules.push(rule);
|
|
454
|
-
}
|
|
455
|
-
},
|
|
456
|
-
revokeRule: (action) => {
|
|
457
|
-
rules = rules.filter((rule) => rule.action !== action);
|
|
458
|
-
},
|
|
459
|
-
canAnyOf: (actions) => {
|
|
460
|
-
return actions.some((action) => auth.can(action));
|
|
461
|
-
},
|
|
462
|
-
canAllOf: (actions) => {
|
|
463
|
-
return actions.every((action) => auth.can(action));
|
|
464
|
-
},
|
|
465
|
-
canGroup: (actions, logicalOperator = "any") => {
|
|
466
|
-
return logicalOperator === "any"
|
|
467
|
-
? auth.canAnyOf(actions)
|
|
468
|
-
: auth.canAllOf(actions);
|
|
469
|
-
},
|
|
470
|
-
};
|
|
471
|
-
return auth;
|
|
241
|
+
|
|
242
|
+
return attributesList;
|
|
472
243
|
}
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
* throw new Error()
|
|
490
|
-
* }
|
|
491
|
-
* }, 0)
|
|
492
|
-
* setCount({type: 'increment'})
|
|
493
|
-
*/
|
|
494
|
-
useReducer(key, reducer, initialState) {
|
|
495
|
-
if (!this.states[key]) {
|
|
496
|
-
this.states[key] = initialState;
|
|
244
|
+
|
|
245
|
+
function extractOuterReturn(code) {
|
|
246
|
+
// match return [...]
|
|
247
|
+
let returns = code.match(/return\s*\<>.*\<\/>|return\s*\(.*\)/gs);
|
|
248
|
+
|
|
249
|
+
return returns || [];
|
|
250
|
+
}
|
|
251
|
+
if (string.match(/return\s*\<>|return\s*\(.*\)/gs) && !string.match(/return\s*\<>.*\<\/>|return\s*\(.*\)/gs)
|
|
252
|
+
|| string.match(/return\s*\<[a-zA-Z0-9_-]+.*>/gs)
|
|
253
|
+
) {
|
|
254
|
+
|
|
255
|
+
try {
|
|
256
|
+
throw new SyntaxError("You forgot to enclose jsx in a fragment return <> jsx html </> or return (<> jsx html </>) at line " + string.split(/return\s*\<[a-zA-Z0-9_-]+.*>/gs)[0].split('\n').length + ' in file ' + file)
|
|
257
|
+
} catch (error) {
|
|
258
|
+
console.error(error)
|
|
259
|
+
process.exit(1)
|
|
497
260
|
}
|
|
498
|
-
return [
|
|
499
|
-
this.states[key],
|
|
500
|
-
/**
|
|
501
|
-
* @function dispatch
|
|
502
|
-
* @description Allows you to dispatch a reducer
|
|
503
|
-
* @param {*} action
|
|
504
|
-
* @returns {void}
|
|
505
|
-
*/
|
|
506
|
-
(action) => {
|
|
507
|
-
this.states[key] = reducer(this.states[key], action);
|
|
508
|
-
this.updateComponent();
|
|
509
|
-
},
|
|
510
|
-
];
|
|
511
261
|
}
|
|
512
262
|
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
this.effects[component].forEach((effect) => {
|
|
517
|
-
if (!this.executedEffects[effect]) {
|
|
518
|
-
effect();
|
|
519
|
-
this.executedEffects[effect] = true;
|
|
520
|
-
}
|
|
521
|
-
});
|
|
522
|
-
});
|
|
523
|
-
}
|
|
524
|
-
|
|
263
|
+
let outerReturn = extractOuterReturn(string);
|
|
264
|
+
let contents = "";
|
|
265
|
+
let updatedContents = "";
|
|
525
266
|
/**
|
|
526
|
-
* @
|
|
527
|
-
* @
|
|
528
|
-
* @param {String} storeName
|
|
529
|
-
* @param {*} initialState
|
|
530
|
-
* @returns {Object} {getField, setField, subscribe, clear}
|
|
531
|
-
* @example
|
|
532
|
-
* let store = this.useSyncStore('store', {count: 0})
|
|
533
|
-
* store.setField('count', 1)
|
|
534
|
-
* store.getField('count') // 1
|
|
535
|
-
*
|
|
267
|
+
* @search - handle return [...]
|
|
268
|
+
* @keywords - return, return jsx, return html, return [...]
|
|
536
269
|
*/
|
|
537
|
-
|
|
538
|
-
let [storedState, setStoredState] = this.useState(
|
|
539
|
-
storeName,
|
|
540
|
-
initialState ||
|
|
541
|
-
// @ts-ignore
|
|
542
|
-
localStorage.getItem(`$_vader_${storeName}`, (s) => {
|
|
543
|
-
localStorage.setItem(`$_vader_${storeName}`, JSON.stringify(s));
|
|
544
|
-
this.$_useStore_subscribers.forEach((subscriber) => {
|
|
545
|
-
subscriber(s);
|
|
546
|
-
});
|
|
547
|
-
}) ||
|
|
548
|
-
{},
|
|
270
|
+
outerReturn.forEach((returnStatement) => {
|
|
549
271
|
|
|
550
|
-
);
|
|
272
|
+
let lines = returnStatement.split("\n");
|
|
551
273
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
return this.$_useStore_subscribers.push(subscriber);
|
|
561
|
-
};
|
|
274
|
+
for (let i = 0; i < lines.length; i++) {
|
|
275
|
+
let line = lines[i];
|
|
276
|
+
if (line.match(/return\s*\<>|return\s*\(/gs)) {
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
contents += line + "\n";
|
|
280
|
+
}
|
|
281
|
+
let usesBraces = returnStatement.match(/return\s*\(/gs) ? true : false;
|
|
562
282
|
|
|
563
|
-
const clear = (fieldName) => {
|
|
564
|
-
let newState = storedState;
|
|
565
|
-
delete newState[fieldName];
|
|
566
|
-
setStoredState(newState);
|
|
567
|
-
};
|
|
568
|
-
return {
|
|
569
|
-
getField,
|
|
570
|
-
setField,
|
|
571
|
-
subscribe,
|
|
572
|
-
clear,
|
|
573
|
-
};
|
|
574
|
-
}
|
|
575
|
-
/**
|
|
576
|
-
* @method useState
|
|
577
|
-
* @description Allows you to create a state
|
|
578
|
-
* @param {String} key
|
|
579
|
-
* @param {*} initialValue
|
|
580
|
-
* @param {*} callback
|
|
581
|
-
* @url - useState works similarly to - https://react.dev/reference/react/useState
|
|
582
|
-
* @returns {Array} [state, setState]
|
|
583
|
-
* @example
|
|
584
|
-
* let [count, setCount] = this.useState('count', 0, () => {
|
|
585
|
-
* console.log('count has been updated')
|
|
586
|
-
* })
|
|
587
|
-
*
|
|
588
|
-
* setCount(count + 1)
|
|
589
|
-
*/
|
|
590
|
-
useState(key, initialValue, callback = null) {
|
|
591
|
-
|
|
592
|
-
if(!this.states[key]){
|
|
593
|
-
this.states[key] = initialValue;
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
return [
|
|
598
|
-
this.states[key],
|
|
599
|
-
/**
|
|
600
|
-
* @function setState
|
|
601
|
-
* @description Allows you to set state
|
|
602
|
-
* @param {*} value
|
|
603
|
-
* @returns {void}
|
|
604
|
-
*/
|
|
605
|
-
(value) => {
|
|
606
|
-
this.states[key] = value;
|
|
607
|
-
this.updateComponent();
|
|
608
|
-
// @ts-ignore
|
|
609
|
-
typeof callback === "function" ? callback() : null;
|
|
610
|
-
},
|
|
611
|
-
];
|
|
612
|
-
}
|
|
613
|
-
/**
|
|
614
|
-
* @method useRef
|
|
615
|
-
* @memberof Component
|
|
616
|
-
* @param {string} ref
|
|
617
|
-
* @description Allows you to get reference to DOM elements from the dom array
|
|
618
|
-
* @returns {Object} {current, update}
|
|
619
|
-
* @example
|
|
620
|
-
* let ref = this.useRef('ref')
|
|
621
|
-
* ref.current // returns the DOM element
|
|
622
283
|
|
|
623
|
-
|
|
284
|
+
contents = contents.trim().replace(/\]$/, "")
|
|
285
|
+
contents = contents.replace(/\)$/, "");
|
|
286
|
+
usesBraces ? !contents.includes('<>') ? contents = `<>${contents}</>` : null : null
|
|
287
|
+
updatedContents = contents;
|
|
288
|
+
let attributes = extractAttributes(contents);
|
|
624
289
|
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
const getElement = () => element;
|
|
631
|
-
|
|
632
|
-
const update = (data) => {
|
|
633
|
-
const newDom = new DOMParser().parseFromString(data, "text/html");
|
|
634
|
-
const newElement = newDom.body.firstChild;
|
|
635
|
-
|
|
636
|
-
if (element) {
|
|
637
|
-
// @ts-ignore
|
|
638
|
-
const isDifferent = !newElement.isEqualNode(element);
|
|
639
|
-
if (isDifferent) {
|
|
640
|
-
// @ts-ignore
|
|
641
|
-
element.parentNode.replaceChild(newElement, element);
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
};
|
|
290
|
+
let newAttributes = [];
|
|
291
|
+
let oldAttributes = [];
|
|
292
|
+
attributes.forEach((attribute) => {
|
|
293
|
+
const { element, attributes } = attribute;
|
|
294
|
+
if (Object.keys(attributes).length === 0) return;
|
|
645
295
|
|
|
646
|
-
return {
|
|
647
|
-
/**@type {HTMLElement} */
|
|
648
|
-
// @ts-ignore
|
|
649
|
-
current: getElement,
|
|
650
|
-
/**@type {Function} */
|
|
651
|
-
update,
|
|
652
|
-
};
|
|
653
|
-
}
|
|
654
296
|
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
* @function useEffect
|
|
658
|
-
* @param {*} effectFn
|
|
659
|
-
* @param {*} dependencies
|
|
660
|
-
* @description Allows you to run side effects
|
|
661
|
-
* @deprecated - this is no longer suggested please use vader signals instead
|
|
662
|
-
* @returns {Object} {cleanup}
|
|
663
|
-
*/
|
|
664
|
-
useEffect(effectFn, dependencies) {
|
|
665
|
-
if (!this.effects[this.name]) {
|
|
666
|
-
this.effects[this.name] = [];
|
|
667
|
-
}
|
|
668
|
-
this.effects[this.name].push(effectFn);
|
|
297
|
+
newAttributes.push(attribute);
|
|
298
|
+
for (let key in attributes) {
|
|
669
299
|
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
if (
|
|
673
|
-
|
|
674
|
-
"
|
|
675
|
-
|
|
676
|
-
}
|
|
677
|
-
});
|
|
678
|
-
} else if (!this.hasMounted) {
|
|
679
|
-
effectFn();
|
|
680
|
-
this.hasMounted = true;
|
|
681
|
-
}
|
|
300
|
+
let value = attributes[key];
|
|
301
|
+
let oldvalue = value;
|
|
302
|
+
if (value && !value.new) {
|
|
303
|
+
if (value && value.includes("={")) {
|
|
304
|
+
value = value.replace("=", "");
|
|
305
|
+
value == "undefined" ? (value = '"') : (value = value);
|
|
682
306
|
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
(effect) => effect !== effectFn
|
|
687
|
-
);
|
|
688
|
-
},
|
|
689
|
-
};
|
|
690
|
-
}
|
|
691
|
-
/**
|
|
692
|
-
* @method $Function
|
|
693
|
-
* @description Allows you to create a function in global scope
|
|
694
|
-
* @returns {Function}
|
|
695
|
-
* @example
|
|
696
|
-
* let func = this.$Function(function add(e, a){
|
|
697
|
-
* return e + a
|
|
698
|
-
* })
|
|
699
|
-
* @param {*} fn
|
|
700
|
-
*/
|
|
701
|
-
$Function(fn) {
|
|
702
|
-
// @ts-ignore
|
|
703
|
-
if (!typeof fn === "function") {
|
|
704
|
-
throw new Error("fn must be a function");
|
|
705
|
-
}
|
|
706
|
-
let name = fn.name;
|
|
707
|
-
if (!name) {
|
|
708
|
-
name = "anonymous" + Math.floor(Math.random() * 100000000000000000);
|
|
709
|
-
}
|
|
710
|
-
window[name] = fn;
|
|
711
|
-
// @ts-ignore
|
|
712
|
-
return `window.${name}()`;
|
|
713
|
-
}
|
|
307
|
+
key == 'style'
|
|
308
|
+
&& value.includes("{{")
|
|
309
|
+
? value = `{this.parseStyle({${value.split('{{')[1].split('}}')[0]}})}` : null
|
|
714
310
|
|
|
715
|
-
// Add other methods like render, useEffect, useReducer, useAuth, etc.
|
|
716
311
|
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
/**
|
|
728
|
-
* @property {Object} snapshot
|
|
729
|
-
* @description Allows you to keep track of component snapshots
|
|
730
|
-
* @private
|
|
731
|
-
* @returns {Object} {name, time, prev_state, prev_props, content}
|
|
732
|
-
*/
|
|
733
|
-
let snapshot = {
|
|
734
|
-
name: name,
|
|
735
|
-
time: time,
|
|
736
|
-
prev_state: this.states,
|
|
737
|
-
prev_props: this.storedProps,
|
|
738
|
-
// @ts-ignore
|
|
739
|
-
content: componentContainer.innerHTML,
|
|
740
|
-
};
|
|
741
|
-
|
|
742
|
-
if (!componentContainer) return;
|
|
743
|
-
const newHtml = await new Function(
|
|
744
|
-
"useState",
|
|
745
|
-
"useEffect",
|
|
746
|
-
"useAuth",
|
|
747
|
-
"useReducer",
|
|
748
|
-
"useSyncStore",
|
|
749
|
-
"signal",
|
|
750
|
-
"rf",
|
|
751
|
-
"props",
|
|
752
|
-
"render",
|
|
753
|
-
"return `" + (await this.render()) + "`;"
|
|
754
|
-
)(
|
|
755
|
-
this.useState,
|
|
756
|
-
this.useEffect,
|
|
757
|
-
this.useAuth,
|
|
758
|
-
this.useReducer,
|
|
759
|
-
this.useSyncStore,
|
|
760
|
-
this.signal,
|
|
761
|
-
this.render
|
|
762
|
-
);
|
|
763
|
-
|
|
764
|
-
if (newHtml !== componentContainer.innerHTML) {
|
|
765
|
-
if (this.snapshots.length > 0) {
|
|
766
|
-
let lastSnapshot = this.snapshots[this.snapshots.length - 1];
|
|
767
|
-
if (lastSnapshot !== snapshot) {
|
|
768
|
-
this.snapshots.push(snapshot);
|
|
312
|
+
|
|
313
|
+
value = `="\$${value}",`;
|
|
314
|
+
string = string.replace(oldvalue, value);
|
|
315
|
+
|
|
316
|
+
} else if (value && value.includes("={`")) {
|
|
317
|
+
value = value.replace("=", "");
|
|
318
|
+
|
|
319
|
+
value = `"\$${value}",`;
|
|
320
|
+
string = string.replace(oldvalue, value);
|
|
321
|
+
|
|
769
322
|
}
|
|
770
|
-
} else {
|
|
771
|
-
|
|
323
|
+
} else if (value && value.new) {
|
|
324
|
+
string = string.replace(oldvalue, value.new);
|
|
772
325
|
}
|
|
773
|
-
this.componentUpdate(
|
|
774
|
-
snapshot.prev_state,
|
|
775
|
-
snapshot.prev_props,
|
|
776
|
-
snapshot.content
|
|
777
|
-
);
|
|
778
|
-
// batch updates
|
|
779
|
-
fragment.appendChild(
|
|
780
|
-
document.createRange().createContextualFragment(newHtml)
|
|
781
|
-
);
|
|
782
|
-
componentContainer.innerHTML = "";
|
|
783
|
-
componentContainer.appendChild(fragment);
|
|
784
|
-
this.runEffects();
|
|
785
326
|
}
|
|
786
327
|
});
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
if (comments) {
|
|
333
|
+
comments.forEach((comment) => {
|
|
334
|
+
let before = comment.trim();
|
|
335
|
+
|
|
336
|
+
comment = comment.replaceAll(/\s+/g, " ");
|
|
337
|
+
comment = comment.trim();
|
|
338
|
+
string = string.replace(before, comment);
|
|
339
|
+
let to_remove = comment.split("{")[1].split("}")[0].trim();
|
|
340
|
+
let beforeComment = comment;
|
|
341
|
+
comment = comment.replaceAll(`{ ${to_remove} }`, "");
|
|
342
|
+
|
|
343
|
+
string = string.replace(beforeComment, comment);
|
|
344
|
+
});
|
|
787
345
|
}
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
* @param {String} className
|
|
791
|
-
* @private
|
|
792
|
-
* @returns {Boolean}
|
|
793
|
-
*/
|
|
794
|
-
validateClassName(className) {
|
|
795
|
-
// validate classNames ensure they are camelCase but also allow for - and _
|
|
796
|
-
return /^[a-zA-Z0-9-_]+$/.test(className);
|
|
797
|
-
}
|
|
346
|
+
let lines = string.split("\n");
|
|
347
|
+
lines.forEach((line) => {
|
|
798
348
|
|
|
349
|
+
if (
|
|
350
|
+
line.includes("let") ||
|
|
351
|
+
line.includes("const") ||
|
|
352
|
+
line.includes("var")
|
|
353
|
+
) {
|
|
354
|
+
switch (true) {
|
|
355
|
+
case line.includes("useState") && !line.includes("import"):
|
|
356
|
+
let varType = line.split("[")[0]
|
|
357
|
+
let key = line.split("=")[0].split(",")[0].trim().split('[')[1];
|
|
799
358
|
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
!element.hasAttribute("alt") &&
|
|
811
|
-
!document.documentElement.outerHTML
|
|
812
|
-
.trim()
|
|
813
|
-
.includes("<!-- #vader-disable_accessibility -->")
|
|
814
|
-
) {
|
|
815
|
-
throw new SyntaxError(
|
|
816
|
-
`Image: ${element.outerHTML} missing alt attribute`
|
|
817
|
-
);
|
|
818
|
-
} else if (
|
|
819
|
-
element.hasAttribute("alt") &&
|
|
820
|
-
// @ts-ignore
|
|
821
|
-
element.getAttribute("alt").length < 1 &&
|
|
822
|
-
!document.documentElement.outerHTML
|
|
823
|
-
.trim()
|
|
824
|
-
.includes("<!-- #vader-disable_accessibility -->")
|
|
825
|
-
) {
|
|
826
|
-
throw new SyntaxError(
|
|
827
|
-
`Image: ${element.outerHTML} alt attribute cannot be empty`
|
|
828
|
-
);
|
|
829
|
-
|
|
830
|
-
} else if (
|
|
831
|
-
element.hasAttribute("src") &&
|
|
832
|
-
!element.getAttribute("src")?.includes("http") || !element.getAttribute("src")?.includes("https") &&
|
|
833
|
-
!document.documentElement.outerHTML
|
|
834
|
-
.trim()
|
|
835
|
-
.includes("<!-- #vader-disable_accessibility -->")
|
|
836
|
-
) {
|
|
837
|
-
let prevurl = element.getAttribute("src");
|
|
838
|
-
element.setAttribute("aria-hidden", "true");
|
|
839
|
-
element.setAttribute("hidden", "true");
|
|
840
|
-
// if window.lcoation.pathname includes a html file remove it and only use the path
|
|
841
|
-
let url = window.location.origin + window.location.pathname.replace(/\/[^\/]*$/, '') + '/public/' + element.getAttribute("src");
|
|
842
|
-
let image = new Image();
|
|
843
|
-
image.src = url;
|
|
844
|
-
image.onerror = () => {
|
|
845
|
-
// @ts-ignore
|
|
846
|
-
element.setAttribute("src", prevurl);
|
|
847
|
-
throw new Error(`Image: ${element.outerHTML} not found`);
|
|
848
|
-
};
|
|
849
|
-
element.setAttribute("src", url);
|
|
850
|
-
|
|
851
|
-
image.onload = () => {
|
|
852
|
-
document.querySelectorAll(`img[src="${url}"]`).forEach((img) => {
|
|
853
|
-
img.setAttribute("src", url);
|
|
854
|
-
img.removeAttribute("aria-hidden");
|
|
855
|
-
img.removeAttribute("hidden");
|
|
856
|
-
});
|
|
857
|
-
};
|
|
858
|
-
}
|
|
359
|
+
let setKey = line.split("=")[0].trim().split(",")[1].trim().replace("]", "");
|
|
360
|
+
key = key.replace("[", "").replace(",", "");
|
|
361
|
+
let valuestate = line.split("=")[1].split("useState(")[1];
|
|
362
|
+
|
|
363
|
+
let regex = /useState\((.*)\)/gs;
|
|
364
|
+
valuestate = valuestate.match(regex) ? valuestate.match(regex)[0].split("useState(")[1].split(")")[0].trim() : valuestate
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
let newState = `${varType} [${key}, ${setKey}] = this.useState('${key}', ${valuestate}`;
|
|
368
|
+
string = string.replace(line, newState);
|
|
859
369
|
break;
|
|
370
|
+
case line.includes("useRef") && !line.includes("import"):
|
|
371
|
+
line = line.trim();
|
|
372
|
+
let typeref = line.split(" ")[0]
|
|
860
373
|
|
|
861
|
-
|
|
862
|
-
if (element.hasAttribute("ref")) {
|
|
863
|
-
// @ts-ignore
|
|
864
|
-
dom[element.getAttribute("ref")] = element;
|
|
865
|
-
}
|
|
866
|
-
if(element.nodeName === "MARKDOWN"){
|
|
867
|
-
element.innerHTML = markdown(element.innerHTML.replace(/\\n/g, '\n').trim())
|
|
868
|
-
}
|
|
374
|
+
let keyref = line.split(typeref)[1].split("=")[0].trim().replace("[", "").replace(",", "");
|
|
869
375
|
|
|
870
|
-
if (element.hasAttribute("class")) {
|
|
871
|
-
const allowClassComments =
|
|
872
|
-
document.documentElement.outerHTML.includes(
|
|
873
|
-
"<!-- #vader-allow_class -->"
|
|
874
|
-
);
|
|
875
|
-
if (!allowClassComments) {
|
|
876
|
-
console.warn(
|
|
877
|
-
"you can disable class errors using, <!-- #vader-allow_class -->"
|
|
878
|
-
);
|
|
879
|
-
throw new Error(
|
|
880
|
-
"class attribute is not allowed, please use className instead"
|
|
881
|
-
);
|
|
882
|
-
}
|
|
883
|
-
} else if (element.hasAttribute("className")) {
|
|
884
|
-
// @ts-ignore
|
|
885
|
-
element.setAttribute("class", element.getAttribute("className"));
|
|
886
|
-
element.removeAttribute("className");
|
|
887
|
-
}
|
|
888
376
|
|
|
889
|
-
|
|
890
|
-
element.hasAttribute("href") &&
|
|
891
|
-
// @ts-ignore
|
|
892
|
-
element.getAttribute("href").startsWith("/") &&
|
|
893
|
-
!document.documentElement.outerHTML
|
|
894
|
-
.trim()
|
|
895
|
-
.includes("<!-- #vader-disable_relative-paths -->")
|
|
896
|
-
) {
|
|
897
|
-
element.setAttribute(
|
|
898
|
-
"href",
|
|
899
|
-
// @ts-ignore
|
|
900
|
-
`#/${element.getAttribute("href").replace("/", "")}`
|
|
901
|
-
);
|
|
902
|
-
}
|
|
377
|
+
let valueref = line.split("=")[1].split("useRef(")[1];
|
|
903
378
|
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
// @ts-ignore
|
|
907
|
-
!element.getAttribute("src").includes("http") &&
|
|
908
|
-
// @ts-ignore
|
|
909
|
-
!element.getAttribute("src").includes("https") &&
|
|
910
|
-
!document.documentElement.outerHTML.includes(`<!-- #vader-disable_relative-paths -->`)
|
|
911
|
-
) {
|
|
912
|
-
element.setAttribute(
|
|
913
|
-
"src",
|
|
914
|
-
// @ts-ignore
|
|
915
|
-
`./public/${element.getAttribute("src")}`
|
|
916
|
-
);
|
|
917
|
-
}
|
|
379
|
+
let newStateref = `${typeref} ${keyref} = this.useRef('${keyref}', ${valueref}`;
|
|
380
|
+
string = string.replace(line, newStateref);
|
|
918
381
|
break;
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
382
|
+
case line.includes("useReducer") && !line.includes("import"):
|
|
383
|
+
line = line.trim();
|
|
384
|
+
line = line.replaceAll(/\s+/g, " ");
|
|
385
|
+
|
|
386
|
+
let varTypereducer = line.split(" ")[0];
|
|
387
|
+
let keyreducer = line
|
|
388
|
+
.split("=")[0]
|
|
389
|
+
.split(" ")[1]
|
|
390
|
+
.trim()
|
|
391
|
+
.replace("[", "")
|
|
392
|
+
.replace(",", "");
|
|
393
|
+
let setKeyreducer = line.split("=")[0].trim().split(",")[1].trim().replace("]", "");
|
|
922
394
|
|
|
923
|
-
|
|
395
|
+
let reducer = line.split("=")[1].split("useReducer(")[1];
|
|
924
396
|
|
|
925
|
-
|
|
397
|
+
let newStatereducer = `${varTypereducer} [${keyreducer}, ${setKeyreducer}] = this.useReducer('${keyreducer}', ${line.includes('=>') ? reducer + '=>{' : reducer}`;
|
|
398
|
+
|
|
399
|
+
string = string.replace(line, newStatereducer);
|
|
400
|
+
break;
|
|
401
|
+
}
|
|
926
402
|
|
|
927
|
-
if (!result.includes("<div data-component")) {
|
|
928
|
-
result = `<div data-component="${this.name}">${result}</div>`;
|
|
929
403
|
}
|
|
930
|
-
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
string = string.replaceAll(/\$\{\/\*.*\*\/\}/gs, "");
|
|
408
|
+
|
|
409
|
+
string = string.replaceAll('../src', './src')
|
|
410
|
+
|
|
411
|
+
function parseComponents(body, isChild) {
|
|
412
|
+
let componentRegex = /<([A-Z][A-Za-z0-9_-]+)\s*([^>]*)>\s*([\s\S]*?)\s*<\/\1>|<([A-Z][A-Za-z0-9_-]+)([^]*?)\/>/gs;
|
|
413
|
+
|
|
414
|
+
let componentMatch = body.match(componentRegex);
|
|
415
|
+
let topComponent = "";
|
|
416
|
+
componentMatch?.forEach(async (component) => {
|
|
417
|
+
|
|
418
|
+
let [, element, attributes] = component;
|
|
419
|
+
let before = component;
|
|
420
|
+
component = component.trim().replace(/\s+/g, " ");
|
|
421
|
+
|
|
422
|
+
!isChild ? (topComponent = component) : null;
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
let myChildrens = [];
|
|
426
|
+
|
|
427
|
+
let name = component.split("<")[1].split(">")[0].split(" ")[0].replace("/", "");
|
|
428
|
+
let componentAttributes = component.split("<")[1].split(">")[0].split(" ").join(" ").replace(name, "").trim();
|
|
429
|
+
const dynamicAttributesRegex = /(\w+)(?:="([^"]*?)"|='([^']*?)'|(?:=\{([^}]*?)\})?|(?:=\{(.*?)*\})?|(?:={([^}]*?)})?|(?:{([^}]*?)})?|(?:}))?|\$=\s*\{\s*\{\s*(.*?)\s*\}\s*\}/gs;
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
let props = component.match(dynamicAttributesRegex)
|
|
435
|
+
|
|
436
|
+
let filteredProps = [];
|
|
437
|
+
let isWithinComponent = false;
|
|
438
|
+
let componentName = name
|
|
439
|
+
let currentProps = []
|
|
440
|
+
|
|
441
|
+
let $_ternaryprops = []
|
|
442
|
+
|
|
443
|
+
for (let prop of props) {
|
|
444
|
+
|
|
445
|
+
if (prop === componentName) {
|
|
446
|
+
|
|
447
|
+
isWithinComponent = true;
|
|
448
|
+
filteredProps.push(prop);
|
|
449
|
+
} else if (isWithinComponent && prop.includes('=')) {
|
|
450
|
+
|
|
451
|
+
if (prop.startsWith('$=')) {
|
|
452
|
+
let old = prop
|
|
453
|
+
prop = prop.replace('$=', '$_ternary=')
|
|
454
|
+
|
|
455
|
+
// remove trailing }
|
|
456
|
+
prop = prop.replace(/}\s*$/, '')
|
|
457
|
+
component = component.replace(old, prop)
|
|
458
|
+
componentAttributes = componentAttributes.replace(old, prop)
|
|
459
|
+
|
|
460
|
+
$_ternaryprops.push(prop)
|
|
931
461
|
|
|
932
|
-
}
|
|
933
|
-
|
|
934
|
-
/**
|
|
935
|
-
* The `html` method generates and processes HTML content for a component, performing various validations and tasks.
|
|
936
|
-
*
|
|
937
|
-
* @param {String} strings - The HTML content to be processed.
|
|
938
|
-
* @param {...any} args - Dynamic values to be inserted into the template.
|
|
939
|
-
* @returns {string} - The processed HTML content as a string.
|
|
940
|
-
*
|
|
941
|
-
* @throws {SyntaxError} - Throws a `SyntaxError` if image-related attributes are missing or invalid.
|
|
942
|
-
* @throws {Error} - Throws an `Error` if there are issues with class names or relative paths.
|
|
943
|
-
*
|
|
944
|
-
* @example
|
|
945
|
-
* // Example usage within a component:
|
|
946
|
-
* const myComponent = new Component();
|
|
947
|
-
* const htmlContent = myComponent.html`
|
|
948
|
-
* <div>
|
|
949
|
-
* <img src="/images/example.jpg" alt="Example Image" />
|
|
950
|
-
* </div>
|
|
951
|
-
* `;
|
|
952
|
-
* document.body.innerHTML = htmlContent;
|
|
953
|
-
*
|
|
954
|
-
* @remarks
|
|
955
|
-
* The `html` method is a core function used in component rendering. It allows you to define and generate HTML content within your component while enforcing best practices and accessibility standards. The method performs several essential tasks:
|
|
956
|
-
*
|
|
957
|
-
* 1. **Image Validation**: It checks images for the presence of 'alt' attributes and their validity.
|
|
958
|
-
* - Throws a `SyntaxError` if an image is missing the 'alt' attribute.
|
|
959
|
-
* - Throws a `SyntaxError` if the 'alt' attribute is empty.
|
|
960
|
-
* - Checks for an 'aria-hidden' attribute for image elements.
|
|
961
|
-
*
|
|
962
|
-
* 2. **Class Attribute Handling**: It enforces class attribute usage and allows optional configuration via comments.
|
|
963
|
-
* - Throws an `Error` if 'class' attributes are used without permission.
|
|
964
|
-
* - Supports 'className' attributes for class definitions.
|
|
965
|
-
* - Allows or disallows class-related comments based on your configuration.
|
|
966
|
-
*
|
|
967
|
-
* 3. **Relative Path Handling**: It processes relative paths in 'href' and 'src' attributes, ensuring proper routing.
|
|
968
|
-
* - Converts relative 'href' attributes to anchor links with appropriate routing.
|
|
969
|
-
* - Converts relative 'src' attributes to absolute paths with 'public' directories.
|
|
970
|
-
*
|
|
971
|
-
* 4. **Custom Component Attributes**: It supports adding a 'data-component' attribute to the root element.
|
|
972
|
-
* - Ensures that the 'data-component' attribute is present for component identification.
|
|
973
|
-
*
|
|
974
|
-
* 5. **Lifecycle Method Invocation**: It invokes the `componentDidMount` method if called from a 'render' context.
|
|
975
|
-
* - Executes `componentDidMount` to handle component initialization once the DOM is ready.
|
|
976
|
-
*
|
|
977
|
-
* @see {@link Component}
|
|
978
|
-
* @see {@link Component#componentDidMount}
|
|
979
|
-
*/
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
html(strings, ...args) {
|
|
984
|
-
// @ts-ignore
|
|
985
|
-
let tiemr = setInterval(()=>{
|
|
986
|
-
if(document.querySelector(`[data-component="${this.name}"]`)){
|
|
987
|
-
clearInterval(tiemr)
|
|
988
|
-
this.componentMounted = true;
|
|
989
|
-
|
|
990
|
-
document.querySelector(`[data-component="${this.name}"]`)?.querySelectorAll("*").forEach((element)=>{
|
|
991
|
-
if(element.hasAttribute("ref")){
|
|
992
|
-
// @ts-ignore
|
|
993
|
-
this.dom[element.getAttribute("ref")] = element
|
|
994
462
|
}
|
|
995
|
-
|
|
996
|
-
|
|
463
|
+
else if (prop.includes('${')) {
|
|
464
|
+
|
|
465
|
+
prop = prop.replace('="', ':').replace('}"', '}')
|
|
466
|
+
if (prop.includes('${')) {
|
|
467
|
+
prop = prop.replace('="', ':')
|
|
468
|
+
prop = prop.replace('${', '')
|
|
469
|
+
prop = prop.replace('}', '')
|
|
470
|
+
|
|
471
|
+
}
|
|
472
|
+
if (prop.includes('="${{')) {
|
|
473
|
+
prop = prop.replace('${{', '{')
|
|
474
|
+
prop = prop.replace('}}', '}')
|
|
475
|
+
prop = prop.replace('="', ':')
|
|
476
|
+
prop = prop.replace('}"', '}')
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
}
|
|
480
|
+
else if (prop.startsWith('={')) {
|
|
481
|
+
prop = prop.replace('={', ':`${')
|
|
482
|
+
prop.replace('} ', '}`')
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
if (prop.includes('function')) {
|
|
486
|
+
// parse 'function' to function
|
|
487
|
+
prop = prop.replace("'", '')
|
|
488
|
+
|
|
489
|
+
if (prop.endsWith("}'")) {
|
|
490
|
+
prop = prop.replace("}'", '}')
|
|
491
|
+
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
prop = prop.replace('=function', ':function')
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
filteredProps.push(prop);
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
else {
|
|
505
|
+
isWithinComponent = false;
|
|
506
|
+
}
|
|
997
507
|
}
|
|
998
|
-
|
|
999
|
-
let script = document.createElement("script");
|
|
1000
|
-
script.setAttribute("type", "text/javascript");
|
|
1001
|
-
script.setAttribute(`data-component-script`, this.name);
|
|
1002
|
-
|
|
508
|
+
component = component.replaceAll(/\s+/g, " ");
|
|
1003
509
|
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
510
|
+
component = component.replace(componentAttributes, '')
|
|
511
|
+
$_ternaryprops.forEach((prop) => {
|
|
512
|
+
component = component.replace(prop, '')
|
|
513
|
+
})
|
|
514
|
+
|
|
515
|
+
let children = component.split(`<${name}`)[1].split(`</${name}>`)[0].trim().replace(/\s+/g, " ").trim().replace(/,$/, '').replace('>', '').replace(/\/$/, '').trim()
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
props = filteredProps.join(',').replace(/\s+/g, " ").trim().replace(/,$/, '')
|
|
519
|
+
|
|
520
|
+
let savedname = name;
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
name = name + Math.random().toString(36).substring(2);
|
|
525
|
+
if (children && children.match(componentRegex)) {
|
|
526
|
+
children = parseComponents(children, true);
|
|
527
|
+
childs.push({ parent: name, children: children });
|
|
528
|
+
} else {
|
|
529
|
+
|
|
530
|
+
children ? childs.push({ parent: name, children: children }) : null;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
childs.forEach((child) => {
|
|
534
|
+
if (child.parent == name) {
|
|
535
|
+
let html = child.children.match(
|
|
536
|
+
/<([A-Z][A-Za-z0-9_-]+)([^>]*)>(.*?)<\/\1>|<([A-Z][A-Za-z0-9_-]+)([^]*?)\/>/gs
|
|
537
|
+
);
|
|
538
|
+
if (html) {
|
|
539
|
+
html = html.map((h) => h.trim().replace(/\s+/g, " ")).join(" ");
|
|
540
|
+
child.children = child.children.replaceAll(html, `${html}`);
|
|
541
|
+
// remove duplicate quotes
|
|
1014
542
|
}
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
543
|
+
|
|
544
|
+
myChildrens.push(child.children);
|
|
545
|
+
childs = childs.filter((e) => e.parent !== name);
|
|
546
|
+
}
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
props = props.replaceAll(`,${savedname}`, '').replaceAll(savedname, '')
|
|
553
|
+
if (props.startsWith(',')) {
|
|
554
|
+
props = props.replace(',', '')
|
|
555
|
+
}
|
|
556
|
+
props = props.replaceAll("='", ":'")
|
|
557
|
+
.replaceAll('=`', ':`')
|
|
558
|
+
.replaceAll('="', ':"')
|
|
559
|
+
.replaceAll('={', ':')
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* @memoize - memoize a component to be remembered on each render and replace the old jsx
|
|
564
|
+
*/
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
let replace = "";
|
|
568
|
+
replace = `\${this.memoize(this.createComponent(${savedname}, {${props}}, [\`${myChildrens.join('')}\`]))}`;
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
body = body.replace(before, replace);
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
return body;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
string = string.replaceAll('vaderjs/client', '/vader.js')
|
|
578
|
+
|
|
579
|
+
const importRegex = /import\s*([^\s,]+|\{[^}]+\})\s*from\s*(['"])(.*?)\2/g;
|
|
580
|
+
const imports = string.match(importRegex);
|
|
581
|
+
let replaceMents = [];
|
|
582
|
+
|
|
583
|
+
|
|
584
|
+
for (let match of imports) {
|
|
585
|
+
let path = match.split('from')[1].trim().replace(/'/g, '').replace(/"/g, '').trim()
|
|
586
|
+
switch (true) {
|
|
587
|
+
case path && !path.includes('./') && !path.includes('/vader.js') && !path.includes('/vaderjs/client') && !path.startsWith('src') && !path.startsWith('public') && !path.includes('http') && !path.includes('https'):
|
|
588
|
+
let componentFolder = fs.existsSync(process.cwd() + '/node_modules/' + path) ? process.cwd() + '/node_modules/' + path : process.cwd() + '/node_modules/' + path.split('/')[0]
|
|
589
|
+
componentFolder = componentFolder.split(process.cwd())[1]
|
|
590
|
+
if (!fs.existsSync(process.cwd() + componentFolder)) {
|
|
591
|
+
throw new Error('Could not find ' + path + ' at ' + match + ' in file ' + file)
|
|
1049
592
|
}
|
|
1050
|
-
|
|
1051
|
-
|
|
593
|
+
|
|
594
|
+
if (!fs.existsSync(process.cwd() + '/dist/src/' + componentFolder.split('/').slice(2).join('/'))) {
|
|
595
|
+
fs.mkdirSync(process.cwd() + '/dist/src/' + componentFolder.split('/').slice(2).join('/'), { recursive: true })
|
|
1052
596
|
}
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
597
|
+
|
|
598
|
+
let baseFolder = componentFolder.split('node_modules')[1].split('/')[1]
|
|
599
|
+
let glp = globSync('**/**/**/**.{jsx,js}', {
|
|
600
|
+
cwd: process.cwd() + '/node_modules/' + baseFolder + '/',
|
|
601
|
+
absolute: true,
|
|
602
|
+
recursive: true
|
|
603
|
+
})
|
|
604
|
+
for (let file of glp) {
|
|
605
|
+
let text = fs.readFileSync(file, "utf8");
|
|
606
|
+
if (!file.endsWith('.js') && file.endsWith('.jsx')) {
|
|
607
|
+
text = Compiler(text, file);
|
|
608
|
+
|
|
609
|
+
}
|
|
610
|
+
let dest = file.split('node_modules')[1]
|
|
611
|
+
dest = dest.split(baseFolder)[1]
|
|
612
|
+
// write to dist
|
|
613
|
+
writer(process.cwd() + '/dist/src/' + baseFolder + dest, text)
|
|
614
|
+
let importname = match.split('import')[1].split('from')[0].trim()
|
|
615
|
+
let oldImportstring = match.split('from')[1].trim().replace(/'/g, '').replace(/"/g, '').trim()
|
|
616
|
+
let newImport = `/src/${baseFolder + dest}`
|
|
617
|
+
newImport = newImport.replaceAll('.jsx', '.js').replaceAll('\\', '/')
|
|
618
|
+
replaceMents.push({ match: oldImportstring, replace: newImport })
|
|
619
|
+
console.log(`📦 imported Node Package ${baseFolder} `)
|
|
1062
620
|
}
|
|
621
|
+
|
|
622
|
+
|
|
623
|
+
break;
|
|
624
|
+
default:
|
|
625
|
+
break;
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
for (let replace of replaceMents) {
|
|
630
|
+
string = string.replaceAll(replace.match, replace.replace)
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
string = string.replaceAll(/\$\{[^{]*\.{3}/gs, (match) => {
|
|
634
|
+
if (match.includes('...')) {
|
|
635
|
+
// Your logic for replacement goes here
|
|
636
|
+
// For example, you can replace '...' with some other string
|
|
637
|
+
return match.replace('...', '');
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
return match;
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
string = string.replaceAll(/\$\{\/\*.*\*\/\}/gs, "");
|
|
644
|
+
string = string.replaceAll("<>", "`").replaceAll("</>", "`");
|
|
645
|
+
string = string.replaceAll(".jsx", ".js");
|
|
646
|
+
string = parseComponents(string);
|
|
647
|
+
|
|
648
|
+
string = string
|
|
649
|
+
.replaceAll("className", "class")
|
|
650
|
+
.replaceAll("classname", "class");
|
|
651
|
+
|
|
652
|
+
string = string.replaceAll('../src', './src')
|
|
653
|
+
string += `\n\n //wascompiled`;
|
|
654
|
+
|
|
655
|
+
|
|
656
|
+
string = string.replaceAll("undefined", "");
|
|
657
|
+
const parse = (css) => {
|
|
658
|
+
let styles = {};
|
|
659
|
+
let currentSelector = '';
|
|
660
|
+
|
|
661
|
+
css.split('\n').forEach(line => {
|
|
662
|
+
line = line.trim();
|
|
663
|
+
|
|
664
|
+
if (line.endsWith('{')) {
|
|
665
|
+
// Start of a block, extract the selector
|
|
666
|
+
currentSelector = line.slice(0, -1).trim();
|
|
667
|
+
styles[currentSelector] = {};
|
|
668
|
+
} else if (line.endsWith('}')) {
|
|
669
|
+
// End of a block
|
|
670
|
+
currentSelector = '';
|
|
671
|
+
} else if (line.includes(':') && currentSelector) {
|
|
672
|
+
// Inside a block and contains key-value pair
|
|
673
|
+
let [key, value] = line.split(':').map(part => part.trim());
|
|
674
|
+
styles[currentSelector][key] = value;
|
|
1063
675
|
}
|
|
1064
|
-
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
return styles;
|
|
679
|
+
};
|
|
680
|
+
string.split('\n').forEach(line => {
|
|
681
|
+
if (line.includes('import')) {
|
|
682
|
+
// Regular expression for matching import() statements
|
|
683
|
+
let asyncimportMatch = line.match(/import\s*\((.*)\)/gs);
|
|
684
|
+
// handle import { Component } from 'vaderjs/runtime/vader.js'
|
|
685
|
+
let regularimportMatch = line.match(/import\s*([A-Za-z0-9_-]+)\s*from\s*([A-Za-z0-9_-]+)|import\s*([A-Za-z0-9_-]+)\s*from\s*(".*")|import\s*([A-Za-z0-9_-]+)\s*from\s*('.*')|import\s*([A-Za-z0-9_-]+)\s*from\s*(\{.*\})/gs);
|
|
686
|
+
|
|
687
|
+
if (asyncimportMatch) {
|
|
688
|
+
asyncimportMatch.forEach(async (match) => {
|
|
689
|
+
let beforeimport = match
|
|
690
|
+
let path = match.split('(')[1].split(')')[0].trim()
|
|
691
|
+
let newImport = ''
|
|
692
|
+
let name = match.split('import')[1].split('from')[0].trim()
|
|
693
|
+
switch (true) {
|
|
694
|
+
case path && path.includes('json'):
|
|
695
|
+
path = path.replace(';', '')
|
|
696
|
+
newImport = `let ${name} = await fetch('${path}').then(res => res.json())`
|
|
697
|
+
|
|
698
|
+
break;
|
|
699
|
+
case path && path.includes('module.css'):
|
|
700
|
+
let css = await fs.readFileSync(process.cwd() + path, 'utf8')
|
|
701
|
+
css = css.replaceAll('.', '')
|
|
702
|
+
|
|
703
|
+
if (!name) {
|
|
704
|
+
throw new Error('Could not find name for css module ' + path + ' at' + beforeimport + ' file' + file)
|
|
705
|
+
}
|
|
706
|
+
newImport = `let ${name} = ${JSON.stringify(parse(css.replaceAll('.', '').replace(/\s+/g, " ")))}`
|
|
707
|
+
|
|
708
|
+
break;
|
|
709
|
+
default:
|
|
710
|
+
let deep = path.split('/').length - 1
|
|
711
|
+
for (let i = 0; i < deep; i++) {
|
|
712
|
+
path = path.split('../').join('')
|
|
713
|
+
path = path.split('./').join('')
|
|
714
|
+
}
|
|
715
|
+
path = path.replace(/'/g, '').trim().replace(/"/g, '').trim()
|
|
716
|
+
// remove double / from path
|
|
717
|
+
path = path.split('//').join('/')
|
|
718
|
+
if (!path.startsWith('./') && !path.includes('/vader.js') && !path.startsWith('src')
|
|
719
|
+
&& !path.startsWith('/public')
|
|
720
|
+
) {
|
|
721
|
+
path = '/src/' + path
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
path = path.replaceAll('.jsx', '.js');
|
|
725
|
+
newImport = `await import(${path})`
|
|
726
|
+
}
|
|
727
|
+
if (newImport) {
|
|
728
|
+
string = string.replace(beforeimport, newImport)
|
|
729
|
+
}
|
|
730
|
+
})
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
if (regularimportMatch) {
|
|
734
|
+
for (let match of regularimportMatch) {
|
|
735
|
+
let beforeimport = match
|
|
736
|
+
let path = match.split('from')[1] ? match.split('from')[1].trim() : match.split('import')[1].trim()
|
|
737
|
+
|
|
738
|
+
let newImport = ''
|
|
739
|
+
let name = match.split('import')[1].split('from')[0].trim()
|
|
740
|
+
|
|
741
|
+
|
|
742
|
+
switch (true) {
|
|
743
|
+
case path && path.includes('json'):
|
|
744
|
+
path = path.replace(';', '')
|
|
745
|
+
newImport = `let ${name} = await fetch('${path}').then(res => res.json())`
|
|
746
|
+
|
|
747
|
+
break;
|
|
748
|
+
case path && path.includes('module.css'):
|
|
749
|
+
|
|
750
|
+
path = path.replace(';', '')
|
|
751
|
+
path = path.replace(/'/g, '').trim().replace(/"/g, '').trim()
|
|
752
|
+
path = path.replaceAll('.jsx', '.js');
|
|
753
|
+
path = path.replaceAll('../', '');
|
|
754
|
+
|
|
755
|
+
let css = fs.readFileSync(process.cwd() + '/' + path, 'utf8')
|
|
756
|
+
|
|
757
|
+
css = css.replaceAll('.', '')
|
|
758
|
+
newImport = `let ${name} = ${JSON.stringify(parse(css))}`
|
|
759
|
+
string = string.replace(beforeimport, newImport)
|
|
760
|
+
break;
|
|
761
|
+
case path && path.includes('.css'):
|
|
762
|
+
string = string.replace(beforeimport, '')
|
|
763
|
+
newImport = ``
|
|
764
|
+
break;
|
|
765
|
+
case path && !path.startsWith('./') && !path.includes('/vader.js') && !path.startsWith('src') && !path.startsWith('public') &&
|
|
766
|
+
path.match(/^[A-Za-z0-9_-]+$/gs) && !path.includes('http') && !path.includes('https'):
|
|
767
|
+
|
|
768
|
+
break;
|
|
769
|
+
default:
|
|
770
|
+
let beforePath = path
|
|
771
|
+
let deep = path.split('/').length - 1
|
|
772
|
+
for (let i = 0; i < deep; i++) {
|
|
773
|
+
path = path.split('../').join('')
|
|
774
|
+
path = path.split('./').join('')
|
|
775
|
+
}
|
|
776
|
+
path = path.replace(/'/g, '').trim().replace(/"/g, '').trim()
|
|
777
|
+
// remove double / from path
|
|
778
|
+
path = path.split('//').join('/')
|
|
779
|
+
if (!path.startsWith('./') && !path.includes('/vader.js') && !path.startsWith('src') && !path.startsWith('public')) {
|
|
780
|
+
path.includes('src') ? path.split('src')[1] : null
|
|
781
|
+
path = '/src/' + path
|
|
782
|
+
} else if (path.startsWith('src') || path.startsWith('public')) {
|
|
783
|
+
path = '/' + path
|
|
784
|
+
}
|
|
785
|
+
path = path.replaceAll('.jsx', '.js');
|
|
786
|
+
|
|
787
|
+
|
|
788
|
+
string = string.replace(beforePath, "'" + path + "'")
|
|
789
|
+
break;
|
|
790
|
+
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
|
|
794
|
+
if (newImport) {
|
|
795
|
+
string = string.replace(beforeimport, newImport)
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
}
|
|
799
|
+
|
|
1065
800
|
|
|
1066
|
-
if (!result.trim().startsWith("<body>")) {
|
|
1067
|
-
console.warn(
|
|
1068
|
-
"You should wrap your html in a body tag, vader may not grab all html!"
|
|
1069
|
-
);
|
|
1070
801
|
}
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
return this.parseHTML(result);
|
|
1075
802
|
}
|
|
1076
803
|
|
|
1077
|
-
|
|
1078
|
-
}
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
* @method render
|
|
1082
|
-
* @description Allows you to render html
|
|
1083
|
-
* @returns {Promise <any>}
|
|
1084
|
-
* @example
|
|
1085
|
-
* async render() {
|
|
1086
|
-
* return this.html(`
|
|
1087
|
-
* <div className="hero p-5">
|
|
1088
|
-
* <h1>Home</h1>
|
|
1089
|
-
* </div>
|
|
1090
|
-
* `);
|
|
1091
|
-
*/
|
|
1092
|
-
async render(props) {}
|
|
804
|
+
|
|
805
|
+
})
|
|
806
|
+
|
|
807
|
+
return string
|
|
1093
808
|
}
|
|
1094
809
|
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
* </div>
|
|
1111
|
-
* `);
|
|
1112
|
-
* }
|
|
1113
|
-
*/
|
|
1114
|
-
const Vader = {
|
|
1115
|
-
/**
|
|
1116
|
-
* @class Component
|
|
1117
|
-
* @description Allows you to create a component
|
|
1118
|
-
* @returns {void}
|
|
1119
|
-
* @memberof {Vader}
|
|
1120
|
-
* @example
|
|
1121
|
-
* import { Vader } from "../../dist/vader/index.js";
|
|
1122
|
-
* export class Home extends Vader.Component {
|
|
1123
|
-
* constructor() {
|
|
1124
|
-
* super();
|
|
1125
|
-
* }
|
|
1126
|
-
* async render() {
|
|
1127
|
-
* return this.html(`
|
|
1128
|
-
* <div className="hero p-5">
|
|
1129
|
-
* <h1>Home</h1>
|
|
1130
|
-
* </div>
|
|
1131
|
-
* `);
|
|
1132
|
-
* }
|
|
1133
|
-
* }
|
|
1134
|
-
*/
|
|
1135
|
-
Component: Component,
|
|
1136
|
-
useRef: useRef,
|
|
1137
|
-
};
|
|
1138
|
-
export const component = (name) => {
|
|
1139
|
-
return new Component();
|
|
1140
|
-
};
|
|
810
|
+
globalThis.isBuilding = false
|
|
811
|
+
globalThis.isWriting = null
|
|
812
|
+
const glb = await glob("**/**/**/**.{jsx,js}", {
|
|
813
|
+
ignore: ["node_modules/**/*", "dist/**/*"],
|
|
814
|
+
cwd: process.cwd() + '/pages/',
|
|
815
|
+
absolute: true,
|
|
816
|
+
recursive: true
|
|
817
|
+
});
|
|
818
|
+
async function Build() {
|
|
819
|
+
globalThis.isBuilding = true
|
|
820
|
+
console.log('Compiling......')
|
|
821
|
+
let reader = async (file) => {
|
|
822
|
+
let text = await fs.readFileSync(file, "utf8");
|
|
823
|
+
return text;
|
|
824
|
+
};
|
|
1141
825
|
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
let
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
if (elements[i].nodeName === "INCLUDE") {
|
|
1162
|
-
if(!elements[i].getAttribute("src") || elements[i].getAttribute("src") === ""){
|
|
1163
|
-
throw new Error("Include tag must have src attribute")
|
|
826
|
+
|
|
827
|
+
function ssg(routes = []) {
|
|
828
|
+
globalThis.isBuilding = true
|
|
829
|
+
console.log(`Generating html files for ${routes.length} routes`)
|
|
830
|
+
routes.forEach(async (route) => {
|
|
831
|
+
if (route.url.includes(':')) {
|
|
832
|
+
console.log('Route ' + route.url + ' is a dynamic route and will not be generated')
|
|
833
|
+
return
|
|
834
|
+
}
|
|
835
|
+
let equalparamroute = routes.map((e) => {
|
|
836
|
+
if (e.url.includes(':')) {
|
|
837
|
+
let url = e.url.split('/:')[0]
|
|
838
|
+
if (url && route.url === url) {
|
|
839
|
+
return e
|
|
840
|
+
} else {
|
|
841
|
+
return null
|
|
842
|
+
|
|
843
|
+
}
|
|
1164
844
|
}
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
dom.body.querySelectorAll('include').forEach((el)=>{
|
|
1206
|
-
el.remove()
|
|
1207
|
-
})
|
|
1208
|
-
// replace ` with \`\` to allow for template literals
|
|
1209
|
-
dom.body.outerHTML = dom.body.outerHTML.replace(/`/g, "\\`")
|
|
1210
|
-
dom.body.outerHTML = dom.body.outerHTML.replace(el.outerHTML, new Function(`return \`${newdom.body.outerHTML}\`;`)())
|
|
845
|
+
return null
|
|
846
|
+
}).filter(Boolean)
|
|
847
|
+
let document = `
|
|
848
|
+
<!DOCTYPE html>
|
|
849
|
+
<html lang="en">
|
|
850
|
+
<head>
|
|
851
|
+
<script>
|
|
852
|
+
window.routes = JSON.parse('${JSON.stringify(routes)}')
|
|
853
|
+
</script>
|
|
854
|
+
<script id="isServer">
|
|
855
|
+
window.isServer = true
|
|
856
|
+
</script>
|
|
857
|
+
<meta charset="UTF-8">
|
|
858
|
+
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
|
859
|
+
<script type="module" id="meta">
|
|
860
|
+
window.history.pushState({}, '', '${route.url}')
|
|
861
|
+
window.module = await import('/${route.fileName.replace('.jsx', '.js')}')
|
|
862
|
+
let metadata = await module.$metadata
|
|
863
|
+
if(metadata && metadata.title){
|
|
864
|
+
document.head.innerHTML += '<title>' + metadata.title + '</title>'
|
|
865
|
+
}
|
|
866
|
+
if(metadata && metadata.description){
|
|
867
|
+
document.head.innerHTML += '<meta name="description" content="' + metadata.description + '">'
|
|
868
|
+
}
|
|
869
|
+
if(metadata && metadata.keywords){
|
|
870
|
+
document.head.innerHTML += '<meta name="keywords" content="' + metadata.keywords + '">'
|
|
871
|
+
}
|
|
872
|
+
if(metadata && metadata.author){
|
|
873
|
+
document.head.innerHTML += '<meta name="author" content="' + metadata.author + '">'
|
|
874
|
+
}
|
|
875
|
+
if(metadata && metadata.image){
|
|
876
|
+
let image = metadata.image.file
|
|
877
|
+
let type = metadata.image.type
|
|
878
|
+
|
|
879
|
+
document.head.innerHTML += '<meta property="og:image" content="' + image + '">'
|
|
880
|
+
document.head.innerHTML += '<meta property="og:image:type" content="' + type + '">'
|
|
881
|
+
}
|
|
882
|
+
if(metadata && metadata.url){
|
|
883
|
+
document.head.innerHTML += '<meta property="og:url" content="' + metadata.url + '">'
|
|
884
|
+
}
|
|
1211
885
|
|
|
1212
|
-
|
|
1213
|
-
|
|
886
|
+
if(metadata && metadata.robot){
|
|
887
|
+
document.head.innerHTML += '<meta name="robots" content="' + metadata.robot + '">'
|
|
888
|
+
}
|
|
889
|
+
if(metadata && metadata.manifest){
|
|
890
|
+
document.head.innerHTML += '<link rel="manifest" href="' + metadata.manifest + '">'
|
|
891
|
+
}
|
|
892
|
+
if(metadata && metadata.tags){
|
|
893
|
+
metadata.tags.forEach(tag => {
|
|
894
|
+
document.head.innerHTML += tag
|
|
895
|
+
})
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
if(metadata && metadata.styles){
|
|
899
|
+
metadata.styles.forEach(style => {
|
|
900
|
+
style = style.replaceAll('./', '/')
|
|
901
|
+
style = style.replaceAll('../', '/')
|
|
902
|
+
style = style.replace("'", '')
|
|
903
|
+
document.head.innerHTML += '<link rel="stylesheet" href="' + style + '">'
|
|
904
|
+
})
|
|
905
|
+
}
|
|
906
|
+
if(metadata && metadata.icon){
|
|
907
|
+
document.head.innerHTML += '<link rel="icon" href="' + metadata.icon + '">'
|
|
908
|
+
}
|
|
909
|
+
</script>
|
|
910
|
+
<script type="module" id="router">
|
|
911
|
+
import VaderRouter from '/router.js'
|
|
912
|
+
const router = new VaderRouter('${route.url}', 3000)
|
|
913
|
+
router.get('${route.url}', async (req, res) => {
|
|
914
|
+
let module = await import('/${route.fileName.replace('.jsx', '.js')}')
|
|
915
|
+
if(Object.keys(module).includes('$prerender') && !module.$prerender){
|
|
916
|
+
document.head.setAttribute('prerender', 'false')
|
|
917
|
+
}
|
|
918
|
+
res.render(module, req, res, module.$metadata)
|
|
919
|
+
})
|
|
920
|
+
${equalparamroute.length > 0 ? equalparamroute.map((e) => {
|
|
921
|
+
|
|
922
|
+
|
|
923
|
+
|
|
924
|
+
return `router.get('${e.url}', async (req, res) => {
|
|
925
|
+
let module = await import('/${e.fileName.replace('.jsx', '.js')}')
|
|
926
|
+
res.render(module, req, res, module.$metadata)
|
|
927
|
+
})\n`
|
|
928
|
+
}) : ''}
|
|
929
|
+
router.listen(3000)
|
|
930
|
+
|
|
931
|
+
</script>
|
|
932
|
+
</head>
|
|
933
|
+
<body>
|
|
934
|
+
<div id="root"></div>
|
|
935
|
+
</body>
|
|
1214
936
|
|
|
1215
|
-
|
|
1216
|
-
|
|
937
|
+
|
|
938
|
+
</html>
|
|
939
|
+
`;
|
|
940
|
+
|
|
941
|
+
// generate random but common ports
|
|
942
|
+
let port = Math.floor(Math.random() * (65535 - 49152 + 1) + 49152)
|
|
943
|
+
|
|
944
|
+
const server = http.createServer((req, res) => {
|
|
945
|
+
|
|
946
|
+
if (req.url === '/') {
|
|
947
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
948
|
+
res.end(document);
|
|
949
|
+
} else {
|
|
950
|
+
// Serve static files (adjust the file paths based on your project structure)
|
|
951
|
+
const filePath = process.cwd() + '/dist/' + req.url
|
|
1217
952
|
|
|
953
|
+
fs.readFile(filePath, (err, data) => {
|
|
954
|
+
if (err) {
|
|
955
|
+
res.writeHead(404, { 'Content-Type': filePath.includes('js') ? 'text/javascript' : 'text/html' });
|
|
956
|
+
res.end('File not found');
|
|
957
|
+
} else {
|
|
958
|
+
res.writeHead(200, { 'Content-Type': filePath.includes('js') ? 'text/javascript' : 'text/html' });
|
|
959
|
+
res.end(data);
|
|
960
|
+
}
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
});
|
|
964
|
+
|
|
965
|
+
server.listen(port)
|
|
966
|
+
server.on('error', (err) => {
|
|
967
|
+
if (err.code === 'EADDRINUSE') {
|
|
968
|
+
console.log(`Port ${port} is in use, trying another port...`);
|
|
969
|
+
setTimeout(() => {
|
|
970
|
+
server.close();
|
|
971
|
+
server.listen(++port);
|
|
972
|
+
}, 1000);
|
|
973
|
+
}
|
|
974
|
+
})
|
|
975
|
+
|
|
976
|
+
globalThis.listen = true;
|
|
977
|
+
|
|
978
|
+
const browser = await puppeteer.launch({
|
|
979
|
+
headless: "new", args: ['--no-sandbox', '--disable-setuid-sandbox'],
|
|
980
|
+
warning: false,
|
|
981
|
+
})
|
|
982
|
+
|
|
983
|
+
const browserPID = browser.process().pid
|
|
984
|
+
try {
|
|
985
|
+
|
|
986
|
+
route.url = route.url.replaceAll(/\/:[a-zA-Z0-9_-]+/gs, '')
|
|
987
|
+
let page = await browser.newPage();
|
|
988
|
+
await page.goto(`http://localhost:${port}/`, { waitUntil: 'networkidle2' });
|
|
989
|
+
await page.on('console', msg => console.log('PAGE LOG:', msg.text()));
|
|
990
|
+
await page.on('error', err => console.log('PAGE LOG:', err));
|
|
991
|
+
await page.on('pageerror', err => console.log('PAGE LOG:', err));
|
|
992
|
+
await page.on('requestfailed', err => console.log(err));
|
|
993
|
+
await page.evaluate(() => {
|
|
994
|
+
window.onerror = function (msg, url, lineNo, columnNo, error) {
|
|
995
|
+
console.log(msg, url, lineNo, columnNo, error)
|
|
996
|
+
}
|
|
997
|
+
})
|
|
998
|
+
await page.waitForSelector('#root', { timeout: 10000 })
|
|
999
|
+
await page.evaluate(() => {
|
|
1000
|
+
document.getElementById('meta').remove()
|
|
1001
|
+
document.querySelector('#isServer').innerHTML = 'window.isServer = false'
|
|
1002
|
+
if (document.head.getAttribute('prerender') === 'false') {
|
|
1003
|
+
document.querySelector('#root').innerHTML = ''
|
|
1004
|
+
console.log(`Disabled prerendering for ${window.location.pathname}`)
|
|
1005
|
+
}
|
|
1006
|
+
})
|
|
1007
|
+
const html = await page.content();
|
|
1008
|
+
|
|
1009
|
+
await page.close();
|
|
1010
|
+
await writer(process.cwd() + '/dist/' + (route.url === '/' ? 'index.html' : `${route.url}/` + 'index.html'), html)
|
|
1011
|
+
await browser.close();
|
|
1012
|
+
server.close()
|
|
1013
|
+
|
|
1014
|
+
} catch (error) {
|
|
1015
|
+
server.close()
|
|
1016
|
+
await browser.close();
|
|
1218
1017
|
}
|
|
1018
|
+
finally {
|
|
1019
|
+
await browser.close();
|
|
1020
|
+
server.close()
|
|
1021
|
+
}
|
|
1022
|
+
try {
|
|
1023
|
+
process.kill(browserPID)
|
|
1024
|
+
} catch (error) {
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
|
|
1028
|
+
})
|
|
1029
|
+
|
|
1030
|
+
let timeout = setTimeout(() => {
|
|
1031
|
+
globalThis.isBuilding = false
|
|
1032
|
+
clearTimeout(timeout)
|
|
1033
|
+
}, 1000)
|
|
1034
|
+
console.log(`Generated ${routes.length} html files for ${routes.length} routes`)
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
globalThis.routes = []
|
|
1038
|
+
|
|
1039
|
+
for await (let file of glb) {
|
|
1040
|
+
// Normalize file paths
|
|
1041
|
+
let origin = file.replace(/\\/g, '/');
|
|
1042
|
+
let fileName = origin.split('/pages/')[1].split('.jsx')[0].replace('.jsx', '') + '.jsx';
|
|
1043
|
+
let isBasePath = fileName === 'index.jsx';
|
|
1044
|
+
let isParamRoute = fileName.includes('[') && fileName.includes(']') ? true : false
|
|
1045
|
+
|
|
1046
|
+
// Extract all dynamic parameters from the file path [param1]/[param2]/[param3
|
|
1047
|
+
let aburl = origin.split('/pages')[1].split('.jsx')[0].replace('.jsx', '').split('[').join(':').split(']').join('');
|
|
1048
|
+
|
|
1049
|
+
if (aburl.includes('...')) {
|
|
1050
|
+
// this is a catch all route
|
|
1051
|
+
// it should be /pages/[...]/index.jsx or /pages/[...].jsx
|
|
1052
|
+
aburl = aburl.split('...').join('*').split(':*').join('*')
|
|
1053
|
+
aburl = aburl.replaceAll('./index', '')
|
|
1054
|
+
|
|
1219
1055
|
}
|
|
1056
|
+
// Create an object with URL and pathname properties
|
|
1057
|
+
let obj = {
|
|
1058
|
+
url: isBasePath ? '/' : aburl.replaceAll('/index', ''),
|
|
1059
|
+
pathname: `/pages/${origin.split('pages/')[1].split('.jsx')[0].replace('.jsx', '')}.jsx`,
|
|
1060
|
+
fullpath: origin,
|
|
1061
|
+
};
|
|
1062
|
+
|
|
1063
|
+
|
|
1064
|
+
|
|
1065
|
+
let data = await fs.readFileSync(origin, "utf8");
|
|
1066
|
+
console.log(`Compiling ${fileName}...`)
|
|
1067
|
+
data = Compiler(data, origin);
|
|
1068
|
+
|
|
1069
|
+
|
|
1070
|
+
await writer(process.cwd() + "/dist/" + fileName.replace('.jsx', '.js'), data).then(async () => {
|
|
1071
|
+
|
|
1072
|
+
|
|
1073
|
+
|
|
1074
|
+
await writer(process.cwd() + "/dist/" + fileName.replace('.jsx', '.js'), data)
|
|
1075
|
+
|
|
1076
|
+
})
|
|
1077
|
+
|
|
1078
|
+
// configure routing for each page
|
|
1079
|
+
|
|
1080
|
+
obj.compiledPath = process.cwd() + "/dist/pages/" + fileName.replace('.jsx', '.js')
|
|
1081
|
+
let providerRedirects = { cloudflare: '_redirects', vercel: 'vercel.json', netlify: '_redirects' }
|
|
1082
|
+
switch (true) {
|
|
1083
|
+
case config && config.host && !config.host['_redirect']:
|
|
1084
|
+
let host = config.host.provider
|
|
1085
|
+
|
|
1086
|
+
let provider = providerRedirects[host]
|
|
1087
|
+
if (provider) {
|
|
1088
|
+
|
|
1089
|
+
let redirectFile = null
|
|
1090
|
+
switch (true) {
|
|
1091
|
+
case provider === '_redirects':
|
|
1092
|
+
redirectFile = fs.existsSync(process.cwd() + '/dist/' + provider) ? fs.readFileSync(process.cwd() + '/dist/' + provider, 'utf8') : ''
|
|
1093
|
+
break;
|
|
1094
|
+
case provider === 'vercel.json':
|
|
1095
|
+
redirectFile = fs.existsSync(process.cwd() + '/' + provider) ? fs.readFileSync(process.cwd() + '/' + provider, 'utf8') : ''
|
|
1096
|
+
break;
|
|
1097
|
+
default:
|
|
1098
|
+
break;
|
|
1099
|
+
}
|
|
1100
|
+
let type = provider === '_redirects' ? 'text/plain' : 'application/json'
|
|
1101
|
+
|
|
1102
|
+
let root = obj.url.includes(':') ? obj.url.split('/:')[0] : obj.url
|
|
1103
|
+
switch (true) {
|
|
1104
|
+
case root === '/':
|
|
1105
|
+
break;
|
|
1106
|
+
case type === 'text/plain' && !redirectFile.includes(obj.url) && obj.url.includes(':'):
|
|
1107
|
+
let page = obj.pathname.split('/pages/')[1].replace('.jsx', '.js')
|
|
1108
|
+
redirectFile += `\n/${page} /${page} 200\n${obj.url} ${root} 200\n`
|
|
1109
|
+
!redirectFile.includes('/404') ? redirectFile += `\n/404 /404 404` : null
|
|
1110
|
+
fs.writeFileSync(process.cwd() + '/dist/' + provider, redirectFile)
|
|
1111
|
+
console.log(`Added ${obj.url} ${obj.url} 200 to ${provider}`)
|
|
1112
|
+
break;
|
|
1113
|
+
case type === 'application/json' && !redirectFile?.includes(`${obj.url}`):
|
|
1114
|
+
let json = redirectFile ? JSON.parse(redirectFile) : {}
|
|
1115
|
+
let isVercel = provider === 'vercel.json' ? true : false
|
|
1116
|
+
if (isVercel) {
|
|
1117
|
+
json['rewrites'] = json['rewrites'] || []
|
|
1118
|
+
json['rewrites'].push({ "source": obj.url, "destination": `${root}/index.html` })
|
|
1119
|
+
fs.writeFileSync(process.cwd() + '/' + provider, JSON.stringify(json, null, 2))
|
|
1120
|
+
console.log(`Added ${obj.url} ${root}/index.html to ${provider}`)
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
break;
|
|
1126
|
+
case config && config.host && config.host['_redirect']:
|
|
1127
|
+
let file = config.host['_redirect']
|
|
1128
|
+
file = file.split('./').join('')
|
|
1129
|
+
let redirectFile = fs.existsSync(process.cwd() + '/' + file) ? fs.readFileSync(process.cwd() + '/' + file, 'utf8') : ''
|
|
1130
|
+
fs.writeFileSync(process.cwd() + '/dist/' + file, redirectFile)
|
|
1131
|
+
console.log(`Using ${file} for redirects`)
|
|
1132
|
+
default:
|
|
1133
|
+
break;
|
|
1134
|
+
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
|
|
1138
|
+
globalThis.routes.push({ fileName: fileName, url: obj.url, html: '/' + (isBasePath ? 'index.html' : `${obj.url}/` + 'index.html') })
|
|
1139
|
+
|
|
1140
|
+
|
|
1220
1141
|
|
|
1221
|
-
|
|
1222
1142
|
}
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1143
|
+
|
|
1144
|
+
ssg(globalThis.routes)
|
|
1145
|
+
|
|
1146
|
+
|
|
1147
|
+
const scannedSourceFiles = await glob("**/**.{jsx,js,json}", {
|
|
1148
|
+
ignore: ["node_modules/**/*", "dist/**/*"],
|
|
1149
|
+
cwd: process.cwd() + '/src/',
|
|
1150
|
+
absolute: true,
|
|
1151
|
+
});
|
|
1152
|
+
const scannedVaderFiles = await glob("**/**.{html,js,json}", {
|
|
1153
|
+
cwd: process.cwd() + '/node_modules/vaderjs/runtime',
|
|
1154
|
+
absolute: true,
|
|
1155
|
+
});
|
|
1156
|
+
|
|
1157
|
+
scannedVaderFiles.forEach(async (file) => {
|
|
1158
|
+
file = file.replace(/\\/g, '/');
|
|
1159
|
+
|
|
1160
|
+
|
|
1161
|
+
let name = file.split('/node_modules/vaderjs/runtime/')[1]
|
|
1162
|
+
if (file.includes('index.html') && fs.existsSync(process.cwd() + "/dist/" + name)) {
|
|
1163
|
+
return
|
|
1164
|
+
}
|
|
1165
|
+
let data = await reader(file)
|
|
1166
|
+
bundleSize += fs.statSync(file).size;
|
|
1167
|
+
await writer(process.cwd() + "/dist/" + name, data);
|
|
1168
|
+
})
|
|
1169
|
+
scannedSourceFiles.forEach(async (file) => {
|
|
1170
|
+
file = file.replace(/\\/g, '/');
|
|
1171
|
+
let name = file.split('/src/')[1]
|
|
1172
|
+
//parse jsx
|
|
1173
|
+
|
|
1174
|
+
let data = await reader(process.cwd() + "/src/" + name)
|
|
1175
|
+
if (name.includes('.jsx')) {
|
|
1176
|
+
data = Compiler(data, process.cwd() + "/src/" + name);
|
|
1177
|
+
|
|
1178
|
+
await writer(process.cwd() + "/dist/src/" + name.split('.jsx').join('.js'), data).then(async () => {
|
|
1179
|
+
await writer(process.cwd() + "/dist/src/" + name.replace('.jsx', '.js'), data)
|
|
1180
|
+
|
|
1181
|
+
})
|
|
1182
|
+
return
|
|
1183
|
+
}
|
|
1184
|
+
bundleSize += fs.statSync(process.cwd() + "/src/" + name).size;
|
|
1185
|
+
await writer(process.cwd() + "/dist/src/" + name, data);
|
|
1186
|
+
})
|
|
1187
|
+
|
|
1188
|
+
const scannedPublicFiles = await glob("**/**.{css,js,html,mjs,cjs}", {
|
|
1189
|
+
ignore: ["node_modules/**/*", "dist/**/*"],
|
|
1190
|
+
cwd: process.cwd() + '/public/',
|
|
1191
|
+
absolute: true,
|
|
1192
|
+
});
|
|
1193
|
+
scannedPublicFiles.forEach(async (file) => {
|
|
1194
|
+
file = file.replace(/\\/g, '/');
|
|
1195
|
+
file = file.split('/public/')[1]
|
|
1196
|
+
let data = await reader(process.cwd() + "/public/" + file)
|
|
1197
|
+
bundleSize += fs.statSync(process.cwd() + "/public/" + file).size;
|
|
1198
|
+
await writer(process.cwd() + "/dist/public/" + file, data);
|
|
1199
|
+
})
|
|
1200
|
+
const scannedFiles = await glob("**/**.{css,js,html}", {
|
|
1201
|
+
ignore: ["node_modules/**/*", "dist/**/*"],
|
|
1202
|
+
cwd: process.cwd() + "/runtime/",
|
|
1203
|
+
absolute: true,
|
|
1204
|
+
})
|
|
1205
|
+
|
|
1206
|
+
|
|
1207
|
+
if (!fs.existsSync(process.cwd() + "/dist/index.html")) {
|
|
1208
|
+
|
|
1209
|
+
scannedFiles.forEach(async (file) => {
|
|
1210
|
+
file = file.split(process.cwd() + '/runtime/')[1]
|
|
1211
|
+
|
|
1212
|
+
let objCase = {
|
|
1213
|
+
...file == "app.js" ? { exit: true } : null,
|
|
1214
|
+
...file.includes("index.html") && fs.existsSync(process.cwd() + "/dist/" + file) ? { exit: true } : null,
|
|
1215
|
+
|
|
1216
|
+
}
|
|
1217
|
+
if (objCase.exit) {
|
|
1218
|
+
console.log('exiting')
|
|
1219
|
+
return true
|
|
1220
|
+
}
|
|
1221
|
+
bundleSize += fs.statSync(process.cwd() + "/node_modules/vaderjs/runtime/" + file).size;
|
|
1222
|
+
let data = await reader(process.cwd() + "/node_modules/vaderjs/runtime/" + file)
|
|
1223
|
+
await writer(process.cwd() + "/dist/" + file, data);
|
|
1224
|
+
});
|
|
1225
|
+
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
globalThis.isBuilding = false
|
|
1229
|
+
console.log(`📦 Build completed: Build Size -> ${Math.round(bundleSize / 1000)}kb`)
|
|
1230
|
+
|
|
1231
|
+
bundleSize = 0;
|
|
1232
|
+
|
|
1233
|
+
return true
|
|
1229
1234
|
}
|
|
1230
|
-
|
|
1231
|
-
* @function include
|
|
1232
|
-
* @description Allows you to include html file
|
|
1233
|
-
* @returns {Promise} - modified string with html content
|
|
1234
|
-
* @param {string} path
|
|
1235
|
-
*/
|
|
1235
|
+
const s = () => {
|
|
1236
1236
|
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
.
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1237
|
+
const server = http.createServer((req, res) => {
|
|
1238
|
+
|
|
1239
|
+
const validExtensions = ['.js', '.css', '.mjs', '.cjs', '.html', '.json', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.mp4', '.webm', '.ogg'];
|
|
1240
|
+
|
|
1241
|
+
if (!validExtensions.some(ext => req.url.endsWith(ext))) {
|
|
1242
|
+
req.url = req.url !== '/' ? req.url.split('/')[1] : req.url;
|
|
1243
|
+
req.url = path.join(process.cwd(), 'dist', req.url, 'index.html');
|
|
1244
|
+
} else {
|
|
1245
|
+
req.url = path.join(process.cwd(), 'dist', req.url);
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
const filePath = req.url
|
|
1249
|
+
|
|
1250
|
+
fs.readFile(filePath, (err, data) => {
|
|
1251
|
+
if (err) {
|
|
1252
|
+
res.writeHead(404, { 'Content-Type': 'text/html' });
|
|
1253
|
+
res.end(fs.existsSync(process.cwd() + '/dist/404') ? fs.readFileSync(process.cwd() + '/dist/404/index.html') : '404');
|
|
1254
|
+
} else {
|
|
1255
|
+
const contentType = getContentType(filePath);
|
|
1256
|
+
switch (true) {
|
|
1257
|
+
case contentType === 'text/html' && globalThis.devMode:
|
|
1258
|
+
data = data.toString() + `<script type="module">
|
|
1259
|
+
let ws = new WebSocket('ws://localhost:${process.env.PORT || 3000}')
|
|
1260
|
+
ws.onmessage = (e) => {
|
|
1261
|
+
if(e.data === 'reload'){
|
|
1262
|
+
window.location.reload()
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
</script>
|
|
1266
|
+
`
|
|
1267
|
+
}
|
|
1268
|
+
res.writeHead(200, { 'Content-Type': contentType });
|
|
1269
|
+
res.end(data);
|
|
1270
|
+
}
|
|
1271
|
+
});
|
|
1272
|
+
});
|
|
1273
|
+
|
|
1274
|
+
|
|
1275
|
+
const ws = new WebSocketServer({ server });
|
|
1276
|
+
ws.on('connection', (socket) => {
|
|
1277
|
+
console.log('WebSocket Hydration Client connected');
|
|
1278
|
+
socket.on('close', () => console.log('WebSocket Hydration Client disconnected'));
|
|
1279
|
+
});
|
|
1280
|
+
|
|
1281
|
+
|
|
1282
|
+
function getContentType(filePath) {
|
|
1283
|
+
let ext = ['.js', '.css', '.mjs', '.cjs', '.html', '.json', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.mp4', '.webm', '.ogg'].includes(path.extname(filePath)) ? path.extname(filePath) : '.html'
|
|
1284
|
+
switch (ext) {
|
|
1285
|
+
case '.js':
|
|
1286
|
+
return 'text/javascript';
|
|
1287
|
+
case '.css':
|
|
1288
|
+
return 'text/css';
|
|
1289
|
+
case '.mjs':
|
|
1290
|
+
return 'text/javascript';
|
|
1291
|
+
case '.cjs':
|
|
1292
|
+
return 'text/javascript';
|
|
1293
|
+
case '.html':
|
|
1294
|
+
return 'text/html';
|
|
1295
|
+
case '.json':
|
|
1296
|
+
return 'application/json';
|
|
1297
|
+
case '.png':
|
|
1298
|
+
return 'image/png';
|
|
1299
|
+
case '.jpg':
|
|
1300
|
+
return 'image/jpg';
|
|
1301
|
+
case '.jpeg':
|
|
1302
|
+
return 'image/jpeg';
|
|
1303
|
+
case '.gif':
|
|
1304
|
+
return 'image/gif';
|
|
1305
|
+
case '.svg':
|
|
1306
|
+
return 'image/svg+xml';
|
|
1307
|
+
case '.mp4':
|
|
1308
|
+
return 'video/mp4';
|
|
1309
|
+
case '.webm':
|
|
1310
|
+
return 'video/webm';
|
|
1311
|
+
case '.ogg':
|
|
1312
|
+
return 'video/ogg';
|
|
1313
|
+
default:
|
|
1314
|
+
return 'application/octet-stream';
|
|
1315
|
+
}
|
|
1249
1316
|
}
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1317
|
+
|
|
1318
|
+
const PORT = process.env.PORT || 3000;
|
|
1319
|
+
server.listen(PORT, () => {
|
|
1320
|
+
console.log(`Server is running on port ${PORT}`);
|
|
1321
|
+
});
|
|
1322
|
+
let i =
|
|
1323
|
+
setInterval(() => {
|
|
1324
|
+
if (globalThis.isBuilding && globalThis.devMode) {
|
|
1325
|
+
|
|
1326
|
+
ws.clients.forEach((client) => {
|
|
1327
|
+
client.send('reload')
|
|
1328
|
+
})
|
|
1329
|
+
} else {
|
|
1330
|
+
clearInterval(i)
|
|
1258
1331
|
}
|
|
1259
|
-
|
|
1332
|
+
}, 120)
|
|
1333
|
+
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
|
|
1337
|
+
switch (true) {
|
|
1338
|
+
case process.argv.includes('--watch') && !process.argv.includes('--build') && !process.argv.includes('--serve'):
|
|
1339
|
+
|
|
1340
|
+
globalThis.devMode = true
|
|
1341
|
+
console.log(`
|
|
1342
|
+
Vader.js v1.3.3
|
|
1343
|
+
- Watching for changes in ./pages
|
|
1344
|
+
- Watching for changes in ./src
|
|
1345
|
+
- Watching for changes in ./public
|
|
1346
|
+
`)
|
|
1347
|
+
!globalThis.isBuilding ? Build() : null
|
|
1348
|
+
|
|
1349
|
+
|
|
1350
|
+
Array.from(Array(3).keys()).forEach((i) => {
|
|
1351
|
+
let p = `${process.cwd()}${i == 0 ? '/pages/' : i == 1 ? '/src/' : '/public/'}`
|
|
1352
|
+
watch(p
|
|
1353
|
+
, { recursive: true }, (event, filename) => {
|
|
1354
|
+
if (event == 'change'
|
|
1355
|
+
&& !globalThis.isBuilding
|
|
1356
|
+
) {
|
|
1357
|
+
|
|
1358
|
+
Build()
|
|
1359
|
+
}
|
|
1360
|
+
}).on('error', (err) => console.log(err))
|
|
1260
1361
|
})
|
|
1261
|
-
.
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1362
|
+
let p = process.argv[process.argv.indexOf('--watch') + 1] || process.env.PORT || 3000
|
|
1363
|
+
|
|
1364
|
+
process.env.PORT = p
|
|
1365
|
+
s()
|
|
1366
|
+
|
|
1367
|
+
globalThis.listen = true;
|
|
1368
|
+
|
|
1369
|
+
break;
|
|
1370
|
+
case process.argv.includes('--build') && !process.argv.includes('--watch') && !process.argv.includes('--serve'):
|
|
1371
|
+
globalThis.devMode = false
|
|
1372
|
+
console.log(`
|
|
1373
|
+
Vader.js v1.3.3
|
|
1374
|
+
Building to ./dist
|
|
1375
|
+
`)
|
|
1376
|
+
Build()
|
|
1377
|
+
|
|
1378
|
+
break;
|
|
1379
|
+
case process.argv.includes('--serve') && !process.argv.includes('--watch') && !process.argv.includes('--build'):
|
|
1380
|
+
let port = process.argv[process.argv.indexOf('--serve') + 1] || 3000
|
|
1381
|
+
process.env.PORT = port
|
|
1382
|
+
globalThis.devMode = false
|
|
1383
|
+
console.log(`
|
|
1384
|
+
Vader.js v1.3.3
|
|
1385
|
+
Serving ./dist on port ${port}
|
|
1386
|
+
url: http://localhost:${port}
|
|
1387
|
+
`)
|
|
1388
|
+
s()
|
|
1389
|
+
break;
|
|
1390
|
+
default:
|
|
1391
|
+
console.log(`
|
|
1392
|
+
Vader.js is a reactive framework for building interactive applications for the web built ontop of bun.js!
|
|
1265
1393
|
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1394
|
+
Usage: vader <command>
|
|
1395
|
+
|
|
1396
|
+
Commands:
|
|
1397
|
+
--watch (port) Watch the pages folder for changes with hot reloading
|
|
1398
|
+
|
|
1399
|
+
--build Build the project to ./dist
|
|
1400
|
+
|
|
1401
|
+
--serve (400) Serve the project on a port (default 3000 or process.env.PORT)
|
|
1402
|
+
|
|
1403
|
+
Learn more about vader: https://vader-js.pages.dev/
|
|
1404
|
+
|
|
1405
|
+
`)
|
|
1406
|
+
break;
|
|
1271
1407
|
|
|
1272
|
-
|
|
1408
|
+
}
|