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