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