lilact 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Icon/r +0 -0
- package/LICENSE.TXT +26 -0
- package/README.md +116 -0
- package/bin/copy-demo.js +26 -0
- package/bin/transpile-dir.js +112 -0
- package/bin/transpile.js +137 -0
- package/dist/lilact.development.js +9344 -0
- package/dist/lilact.development.js.map +1 -0
- package/dist/lilact.production.min.js +63 -0
- package/dist/lilact.production.min.js.map +1 -0
- package/docs/.nojekyll +1 -0
- package/docs/assets/hierarchy.js +1 -0
- package/docs/assets/highlight.css +71 -0
- package/docs/assets/icons.js +18 -0
- package/docs/assets/icons.svg +1 -0
- package/docs/assets/main.js +60 -0
- package/docs/assets/navigation.js +1 -0
- package/docs/assets/search.js +1 -0
- package/docs/assets/style.css +1633 -0
- package/docs/classes/accessories.ErrorBoundary.html +37 -0
- package/docs/classes/accessories.Suspense.html +41 -0
- package/docs/classes/components.Component.html +45 -0
- package/docs/classes/components.HTMLComponent.html +32 -0
- package/docs/classes/components.RootComponent.html +32 -0
- package/docs/classes/transition.Transition.html +31 -0
- package/docs/functions/accessories.Spinner.html +9 -0
- package/docs/functions/components.createComponent.html +8 -0
- package/docs/functions/components.createRoot.html +5 -0
- package/docs/functions/components.render.html +5 -0
- package/docs/functions/hooks.createContext.html +3 -0
- package/docs/functions/hooks.useActionState.html +3 -0
- package/docs/functions/hooks.useCallback.html +5 -0
- package/docs/functions/hooks.useContext.html +3 -0
- package/docs/functions/hooks.useDeferredValue.html +4 -0
- package/docs/functions/hooks.useEffect.html +3 -0
- package/docs/functions/hooks.useHook.html +5 -0
- package/docs/functions/hooks.useId.html +4 -0
- package/docs/functions/hooks.useLayoutEffect.html +3 -0
- package/docs/functions/hooks.useLocalStorage.html +4 -0
- package/docs/functions/hooks.useMemo.html +4 -0
- package/docs/functions/hooks.useReducer.html +6 -0
- package/docs/functions/hooks.useRef.html +4 -0
- package/docs/functions/hooks.useState.html +3 -0
- package/docs/functions/hooks.useTransition.html +3 -0
- package/docs/functions/jsx.transpileJSX.html +6 -0
- package/docs/functions/misc.Fragment.html +3 -0
- package/docs/functions/misc.classNames.html +2 -0
- package/docs/functions/misc.deepEqual.html +3 -0
- package/docs/functions/misc.findDOMNode.html +3 -0
- package/docs/functions/misc.getComponentByPointer.html +3 -0
- package/docs/functions/misc.isAsync.html +3 -0
- package/docs/functions/misc.isClass.html +3 -0
- package/docs/functions/misc.isEmpty.html +3 -0
- package/docs/functions/misc.isError.html +3 -0
- package/docs/functions/misc.isThenable.html +3 -0
- package/docs/functions/misc.isValidElement.html +3 -0
- package/docs/functions/misc.shallowEqual.html +3 -0
- package/docs/functions/redux.Provider.html +5 -0
- package/docs/functions/redux.connect.html +5 -0
- package/docs/functions/redux.useDispatch.html +3 -0
- package/docs/functions/redux.useSelector.html +5 -0
- package/docs/functions/redux.useStore.html +3 -0
- package/docs/functions/router.HashRouter.html +5 -0
- package/docs/functions/router.Link.html +12 -0
- package/docs/functions/router.NavLink.html +10 -0
- package/docs/functions/router.Route.html +6 -0
- package/docs/functions/router.Routes.html +4 -0
- package/docs/functions/router.useLocation.html +3 -0
- package/docs/functions/router.useNavigate.html +3 -0
- package/docs/functions/run.require.html +14 -0
- package/docs/functions/run.run.html +13 -0
- package/docs/functions/run.runScripts.html +7 -0
- package/docs/functions/run.traceError.html +6 -0
- package/docs/functions/timers.animationFramePromise.html +3 -0
- package/docs/functions/timers.clearInterval.html +2 -0
- package/docs/functions/timers.clearTimeout.html +2 -0
- package/docs/functions/timers.grabTimers.html +3 -0
- package/docs/functions/timers.pauseTimers.html +2 -0
- package/docs/functions/timers.releaseTimers.html +2 -0
- package/docs/functions/timers.resetTimers.html +2 -0
- package/docs/functions/timers.resumeTimers.html +2 -0
- package/docs/functions/timers.setInterval.html +5 -0
- package/docs/functions/timers.setTimeout.html +4 -0
- package/docs/functions/timers.timeoutPromise.html +4 -0
- package/docs/functions/transition.CSSTransition.html +18 -0
- package/docs/functions/transition.TransitionGroup.html +5 -0
- package/docs/hierarchy.html +1 -0
- package/docs/index.html +88 -0
- package/docs/media/icon.png +0 -0
- package/docs/modules/accessories.html +1 -0
- package/docs/modules/components.html +1 -0
- package/docs/modules/hooks.html +1 -0
- package/docs/modules/jsx.html +1 -0
- package/docs/modules/misc.html +1 -0
- package/docs/modules/redux.html +1 -0
- package/docs/modules/router.html +1 -0
- package/docs/modules/run.html +1 -0
- package/docs/modules/timers.html +1 -0
- package/docs/modules/transition.html +1 -0
- package/docs/modules.html +1 -0
- package/docs/static/demos/actionstate.jsx +41 -0
- package/docs/static/demos/boundary.jsx +52 -0
- package/docs/static/demos/context.jsx +68 -0
- package/docs/static/demos/css-transition.jsx +65 -0
- package/docs/static/demos/modal.jsx +41 -0
- package/docs/static/demos/proptypes.jsx +37 -0
- package/docs/static/demos/reducer.jsx +71 -0
- package/docs/static/demos/redux.jsx +58 -0
- package/docs/static/demos/router.jsx +153 -0
- package/docs/static/demos/stopwatch.jsx +42 -0
- package/docs/static/demos/suspense.jsx +48 -0
- package/docs/static/demos/transition.jsx +40 -0
- package/docs/static/demos/use-differed.jsx +30 -0
- package/docs/static/demos/usetransition.jsx +37 -0
- package/docs/static/index.html +212 -0
- package/docs/static/lilact.development.js +9344 -0
- package/docs/static/lilact.production.min.js +63 -0
- package/docs/static/prismjs/LICENSE.md +21 -0
- package/docs/static/prismjs/prism-jsx.min.js +1 -0
- package/docs/static/prismjs/prism.min.css +1 -0
- package/docs/static/prismjs/prism.min.js +16 -0
- package/docs/variables/jsx.transpilerConfig.html +1 -0
- package/docs/variables/misc.Children.html +10 -0
- package/icon.png +0 -0
- package/package.json +39 -0
- package/root/demos/actionstate.jsx +41 -0
- package/root/demos/boundary.jsx +52 -0
- package/root/demos/context.jsx +68 -0
- package/root/demos/css-transition.jsx +65 -0
- package/root/demos/modal.jsx +41 -0
- package/root/demos/proptypes.jsx +37 -0
- package/root/demos/reducer.jsx +71 -0
- package/root/demos/redux.jsx +58 -0
- package/root/demos/router.jsx +153 -0
- package/root/demos/stopwatch.jsx +42 -0
- package/root/demos/suspense.jsx +48 -0
- package/root/demos/transition.jsx +40 -0
- package/root/demos/use-differed.jsx +30 -0
- package/root/demos/usetransition.jsx +37 -0
- package/root/index.html +212 -0
- package/root/lilact.development.js +9344 -0
- package/root/lilact.production.min.js +63 -0
- package/root/prismjs/LICENSE.md +21 -0
- package/root/prismjs/prism-jsx.min.js +1 -0
- package/root/prismjs/prism.min.css +1 -0
- package/root/prismjs/prism.min.js +16 -0
- package/src/accessories.jsx +287 -0
- package/src/components.jsx +1063 -0
- package/src/events.jsx +175 -0
- package/src/hooks.jsx +461 -0
- package/src/jsx.addons.js +70 -0
- package/src/jsx.js +1168 -0
- package/src/lilact.jsx +115 -0
- package/src/loader.cjs +59 -0
- package/src/misc.jsx +419 -0
- package/src/redux.jsx +198 -0
- package/src/router.jsx +282 -0
- package/src/run.jsx +275 -0
- package/src/timers.jsx +313 -0
- package/src/transition.jsx +244 -0
- package/src/vlq.js +94 -0
- package/tsconfig.json +15 -0
- package/typedoc.json +23 -0
- package/webpack.config.js +196 -0
package/src/jsx.js
ADDED
|
@@ -0,0 +1,1168 @@
|
|
|
1
|
+
/*
|
|
2
|
+
|
|
3
|
+
Lilact
|
|
4
|
+
Copyright (C) 2024-2026 Arash Kazemi <contact.arash.kazemi@gmail.com>
|
|
5
|
+
All rights reserved.
|
|
6
|
+
|
|
7
|
+
BSD-2-Clause
|
|
8
|
+
|
|
9
|
+
Redistribution and use in source and binary forms, with or without
|
|
10
|
+
modification, are permitted provided that the following conditions are met:
|
|
11
|
+
|
|
12
|
+
* Redistributions of source code must retain the above copyright
|
|
13
|
+
notice, this list of conditions and the following disclaimer.
|
|
14
|
+
* Redistributions in binary form must reproduce the above copyright
|
|
15
|
+
notice, this list of conditions and the following disclaimer in the
|
|
16
|
+
documentation and/or other materials provided with the distribution.
|
|
17
|
+
|
|
18
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
19
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
20
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
21
|
+
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
|
22
|
+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
23
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
24
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
25
|
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
26
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
27
|
+
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
28
|
+
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
// This is a JSX transpiler that covers all the standard with some
|
|
32
|
+
// additional features like a nice preprocessor. It doesn't rely on any other
|
|
33
|
+
// library, even Lilact itself. It also generates sourcemap internally!
|
|
34
|
+
// It is not very fast, the xml look-ahead can be quite expensive, but it is not
|
|
35
|
+
// slow at all.
|
|
36
|
+
|
|
37
|
+
// Check the webpack config and also the transpiler.js helper script for
|
|
38
|
+
// usage outside Lilact. Also make sure to check the default configuration.
|
|
39
|
+
|
|
40
|
+
// There are some situations that are solved with tricks, as some aspects of
|
|
41
|
+
// javascripts import a complete parser and I didn't want to get into that!
|
|
42
|
+
// Works almosr perfectly so far (all observed bugs except 1 are fixed), works
|
|
43
|
+
// even on nasty things like the compressed version of tensorflow.js! Feel free
|
|
44
|
+
// to report any bugs, and please include a testable example. Thanks! :)
|
|
45
|
+
//
|
|
46
|
+
// note: until an efficient fix is found, make sure the slashes inside
|
|
47
|
+
// a regexp are escaped, even if they work without escaping.
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
// todo: injected string try catchs should be generated by a function
|
|
51
|
+
|
|
52
|
+
import * as jsxAddons from './jsx.addons.js';
|
|
53
|
+
|
|
54
|
+
export const transpilerConfig = {
|
|
55
|
+
addons: jsxAddons,
|
|
56
|
+
setFunctionLabels: true,
|
|
57
|
+
enableLabelStack: false,
|
|
58
|
+
injectLabels: true,
|
|
59
|
+
// todo: alt+shift+7 on apple abc extended layout is better i think,
|
|
60
|
+
// but my current editor doesn't support syntax highlighting with it.
|
|
61
|
+
preprocessorDelimiter: 'ʔ', // alt+shift+. on apple abc extended layout
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
required: {},
|
|
65
|
+
func_labels: {},
|
|
66
|
+
func_num: Math.floor(Math.random()*10000)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
const TRANSPILER_OUTPUT = Symbol.for("LILACT:TRANSPILER_OUTPUT");
|
|
71
|
+
|
|
72
|
+
import * as vlq from './vlq.js';
|
|
73
|
+
|
|
74
|
+
let raiseError;
|
|
75
|
+
let tab=0;
|
|
76
|
+
|
|
77
|
+
function lookAhead(f, code, index, ...args)
|
|
78
|
+
{
|
|
79
|
+
const b = f(code, index, ...args);
|
|
80
|
+
if(b) {
|
|
81
|
+
return [b.end, b];
|
|
82
|
+
}
|
|
83
|
+
return [index];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function parseRegex(code, index, container)
|
|
87
|
+
{
|
|
88
|
+
// previous token can only be(end with) ;=,([{}!^~&|:?*-+\\/<> or directive or
|
|
89
|
+
// the slash can only be the first character
|
|
90
|
+
// the backward scan automatically passes for i=0
|
|
91
|
+
|
|
92
|
+
// todo: i have doubts about including preprocessorDelimiter
|
|
93
|
+
|
|
94
|
+
let i = index;
|
|
95
|
+
const dels = `;=,([{}!^~&|:?*-+\\/<>${transpilerConfig.preprocessorDelimiter}`;
|
|
96
|
+
while(--i>0) {
|
|
97
|
+
if( ' \t\n\r'.indexOf(code[i])!==-1 ) continue;
|
|
98
|
+
if( dels.indexOf(code[i])!==-1 ) break;
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const b = {
|
|
103
|
+
type: "regex",
|
|
104
|
+
begin: index++
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
while(index<code.length) {
|
|
108
|
+
const ch = code[index];
|
|
109
|
+
switch(ch) {
|
|
110
|
+
case '\n':
|
|
111
|
+
return;
|
|
112
|
+
case '/':
|
|
113
|
+
b.end = index+1;
|
|
114
|
+
if(container) container.children.push(b);
|
|
115
|
+
return b;
|
|
116
|
+
case '\\':
|
|
117
|
+
index++;
|
|
118
|
+
default:
|
|
119
|
+
index++;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function parseComment(code, index, container)
|
|
125
|
+
{
|
|
126
|
+
const b = {
|
|
127
|
+
type: "comment",
|
|
128
|
+
begin: index++
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
if(code[index]!=='*' && code[index]!=='/') {
|
|
132
|
+
return parseRegex(code, index-1, container);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const is_multiline = code[index]==='*';
|
|
136
|
+
|
|
137
|
+
index++;
|
|
138
|
+
|
|
139
|
+
while(index<code.length) {
|
|
140
|
+
|
|
141
|
+
const ch = code[index];
|
|
142
|
+
switch(ch) {
|
|
143
|
+
|
|
144
|
+
case '*':
|
|
145
|
+
if(is_multiline && code[index+1]==='/') {
|
|
146
|
+
b.end=index+2;
|
|
147
|
+
if(container) container.children.push(b);
|
|
148
|
+
return b;
|
|
149
|
+
}
|
|
150
|
+
break;
|
|
151
|
+
|
|
152
|
+
case '\n':
|
|
153
|
+
if(!is_multiline) {
|
|
154
|
+
b.end = index+1;
|
|
155
|
+
if(container) container.children.push(b);
|
|
156
|
+
return b;
|
|
157
|
+
}
|
|
158
|
+
break;
|
|
159
|
+
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
index++;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
raiseError(`unterminated comment`, index);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
function parseDirective(code, index, container)
|
|
170
|
+
{
|
|
171
|
+
let i=index+1;
|
|
172
|
+
while( code[i] &&
|
|
173
|
+
(code[i]!=='\n' && code[i]!==transpilerConfig.preprocessorDelimiter) ||
|
|
174
|
+
code[i-1]==='\\')
|
|
175
|
+
{
|
|
176
|
+
i++;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const is_exp = code[i]===transpilerConfig.preprocessorDelimiter;
|
|
180
|
+
|
|
181
|
+
if(is_exp) {
|
|
182
|
+
const b = {
|
|
183
|
+
type: "directive",
|
|
184
|
+
begin: index,
|
|
185
|
+
cbegin: index,
|
|
186
|
+
end: i+1,
|
|
187
|
+
|
|
188
|
+
expression: code.slice(index+1,i).replace(/\\(.)/gs, '$1'),
|
|
189
|
+
value: ''
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
if(container) container.children.push(b);
|
|
193
|
+
return b;
|
|
194
|
+
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
let j=index;
|
|
198
|
+
|
|
199
|
+
while(j-->=0 && (code[j]===' ' || code[j]==='\t'));
|
|
200
|
+
const is_first = j===-1 || code[j]==='\n';
|
|
201
|
+
|
|
202
|
+
if(is_first) {
|
|
203
|
+
const b = {
|
|
204
|
+
type: "directive",
|
|
205
|
+
begin: index,
|
|
206
|
+
cbegin: index,
|
|
207
|
+
end: i+1,
|
|
208
|
+
|
|
209
|
+
pragma: code.slice(index+1,i+1).replace(/\\(.)/gs, '$1'),
|
|
210
|
+
value: ''
|
|
211
|
+
};
|
|
212
|
+
if(container) container.children.push(b);
|
|
213
|
+
return b;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
raiseError('error in preprocessor statement.')
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
// note: pragma blocks should begin and end in same block main trunk (not its children).
|
|
222
|
+
// note: preprocessor code must be JS, not JSX.
|
|
223
|
+
|
|
224
|
+
function preprocessPragmas(node, context)
|
|
225
|
+
{
|
|
226
|
+
const all_nodes = [];
|
|
227
|
+
|
|
228
|
+
var segments = ["const {" + Object.keys(jsxAddons).join(', ') + "} = transpilerConfig.addons;"];
|
|
229
|
+
|
|
230
|
+
var scope_stack = [];
|
|
231
|
+
var last_block = null;
|
|
232
|
+
|
|
233
|
+
const clone_block = (i, val)=>{
|
|
234
|
+
last_block = typeof(all_nodes[i])==='object'?
|
|
235
|
+
structuredClone(
|
|
236
|
+
{ ...all_nodes[i], children: undefined, attributes: {} }
|
|
237
|
+
)
|
|
238
|
+
:all_nodes[i];
|
|
239
|
+
|
|
240
|
+
if(last_block.type==='directive') {
|
|
241
|
+
last_block.value = val;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
scope_stack[0].out ??= [];
|
|
245
|
+
scope_stack[0].out.push(last_block);
|
|
246
|
+
|
|
247
|
+
return last_block;
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const clone_attr = (i, name)=>{
|
|
251
|
+
last_block = typeof(all_nodes[i])==='object'?
|
|
252
|
+
structuredClone(
|
|
253
|
+
{ ...all_nodes[i], children: undefined, attributes: {} }
|
|
254
|
+
)
|
|
255
|
+
:all_nodes[i];
|
|
256
|
+
|
|
257
|
+
scope_stack[0].attributes[name] = last_block;
|
|
258
|
+
|
|
259
|
+
return last_block;
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
const push_scope = ()=>{ scope_stack = [last_block, scope_stack]; };
|
|
263
|
+
const pop_scope = ()=>{ scope_stack = scope_stack[1]; };
|
|
264
|
+
|
|
265
|
+
const vine_traverse = (nd)=> {
|
|
266
|
+
|
|
267
|
+
if(typeof(nd)!=='object') return segments;
|
|
268
|
+
|
|
269
|
+
segments.push(`push_scope();`);
|
|
270
|
+
|
|
271
|
+
if(nd.attributes) {
|
|
272
|
+
for(const attr in nd.attributes) {
|
|
273
|
+
const ch = nd.attributes[attr];
|
|
274
|
+
|
|
275
|
+
all_nodes.push(ch);
|
|
276
|
+
|
|
277
|
+
segments.push(`clone_attr(${all_nodes.length-1}, "${attr}");`);
|
|
278
|
+
vine_traverse(ch);
|
|
279
|
+
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
if(nd.children) {
|
|
283
|
+
for(const ch of nd.children) {
|
|
284
|
+
all_nodes.push(ch);
|
|
285
|
+
|
|
286
|
+
if(ch.type==='directive' && ch.expression===undefined) {
|
|
287
|
+
segments.push(ch.pragma);
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
if(ch.expression)
|
|
291
|
+
segments.push(`clone_block(${all_nodes.length-1}, ${ch.expression});`);
|
|
292
|
+
else
|
|
293
|
+
segments.push(`clone_block(${all_nodes.length-1});`);
|
|
294
|
+
vine_traverse(ch);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
segments.push(`pop_scope();`);
|
|
300
|
+
|
|
301
|
+
return segments;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
node.out = [];
|
|
306
|
+
last_block = node;
|
|
307
|
+
push_scope();
|
|
308
|
+
vine_traverse(node);
|
|
309
|
+
|
|
310
|
+
const cmd = segments.join('\n');
|
|
311
|
+
eval(cmd);
|
|
312
|
+
|
|
313
|
+
return segments;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
function parseString(code, index, q, container)
|
|
318
|
+
{
|
|
319
|
+
const b = {
|
|
320
|
+
type: "string",
|
|
321
|
+
begin: index++,
|
|
322
|
+
cbegin: index
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
while(index<code.length) {
|
|
326
|
+
|
|
327
|
+
const ch = code[index];
|
|
328
|
+
|
|
329
|
+
switch(ch) {
|
|
330
|
+
|
|
331
|
+
case q:
|
|
332
|
+
b.cend=index;
|
|
333
|
+
b.end=index+1;
|
|
334
|
+
if(container) container.children.push(b);
|
|
335
|
+
return b;
|
|
336
|
+
|
|
337
|
+
case '\n':
|
|
338
|
+
if(q!=='\`') raiseError(`unterminated string`, index);
|
|
339
|
+
break;
|
|
340
|
+
|
|
341
|
+
case '$':
|
|
342
|
+
if(q==='`') {
|
|
343
|
+
index++;
|
|
344
|
+
if(code[index]==='{') {
|
|
345
|
+
[index] = lookAhead( parseJS, code, index, true, container );
|
|
346
|
+
index--;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
break;
|
|
350
|
+
|
|
351
|
+
case '\\':
|
|
352
|
+
index++;
|
|
353
|
+
break;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
index++;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
raiseError(`unterminated string`, index);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
function parseXMLContent(code, index, container, eols)
|
|
364
|
+
{
|
|
365
|
+
let i = index;
|
|
366
|
+
let cur = index;
|
|
367
|
+
|
|
368
|
+
while(i<code.length) {
|
|
369
|
+
switch(code[i]) {
|
|
370
|
+
|
|
371
|
+
case '<':
|
|
372
|
+
if(code[i+1]==='/') {
|
|
373
|
+
|
|
374
|
+
i+=2;
|
|
375
|
+
|
|
376
|
+
let j=i;
|
|
377
|
+
|
|
378
|
+
while(j<code.length && code[j]!=='>') {
|
|
379
|
+
j++;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if(j===code.length) {
|
|
383
|
+
return null; //throw `ill-formed xml (slash) ${j}`;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
const tag = code.substring(i,j).trim();
|
|
387
|
+
|
|
388
|
+
if(container.tag!==tag) {
|
|
389
|
+
throw `ill-formed xml (close tag) [${container.tag},${tag}] ${j}`;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
container.end = j+1;
|
|
393
|
+
|
|
394
|
+
container.cbegin = index+1; // content begin index
|
|
395
|
+
container.cend = i-2; // content end index
|
|
396
|
+
|
|
397
|
+
return j;
|
|
398
|
+
}
|
|
399
|
+
else {
|
|
400
|
+
[i] = lookAhead( parseXML, code, i, container );
|
|
401
|
+
|
|
402
|
+
if(i>cur) {
|
|
403
|
+
cur = i;
|
|
404
|
+
i--;
|
|
405
|
+
}
|
|
406
|
+
else cur++;
|
|
407
|
+
|
|
408
|
+
}
|
|
409
|
+
break;
|
|
410
|
+
|
|
411
|
+
case '{':
|
|
412
|
+
const j=i;
|
|
413
|
+
[i] = lookAhead( parseJS, code, i, true, container );
|
|
414
|
+
i--;
|
|
415
|
+
break;
|
|
416
|
+
|
|
417
|
+
}
|
|
418
|
+
i++;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
return cur;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
function parseXML(code, index, container)
|
|
426
|
+
{
|
|
427
|
+
// delimiters
|
|
428
|
+
const delims = [ ' ','\t','\n', '/','&','^','%','|','!','~','+','*','?',
|
|
429
|
+
'<','>',';',',','=','{','}','(',')','[',']','\'','\"',
|
|
430
|
+
'\`', '\\', '', undefined ];
|
|
431
|
+
|
|
432
|
+
const skip_spaces =
|
|
433
|
+
()=> { while( code[index]===' ' || code[index]==='\t' || code[index]==='\n' ) index++; };
|
|
434
|
+
|
|
435
|
+
const b = { // block
|
|
436
|
+
type: "xml",
|
|
437
|
+
begin: index++,
|
|
438
|
+
children: [],
|
|
439
|
+
attributes: {},
|
|
440
|
+
self_closing: false,
|
|
441
|
+
js_attributes: []
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
skip_spaces();
|
|
445
|
+
|
|
446
|
+
let i = index;
|
|
447
|
+
while(i<code.length && delims.indexOf(code[i])===-1) i++;
|
|
448
|
+
if(i===code.length) return;
|
|
449
|
+
|
|
450
|
+
b.tag = code.substring(index,i);
|
|
451
|
+
|
|
452
|
+
index = i;
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
let last_attr = undefined;
|
|
456
|
+
|
|
457
|
+
while(index<code.length) {
|
|
458
|
+
i = index;
|
|
459
|
+
|
|
460
|
+
while( i<code.length && delims.indexOf(code[i])===-1 ) {
|
|
461
|
+
i++;
|
|
462
|
+
}
|
|
463
|
+
if(i===code.length) return;
|
|
464
|
+
|
|
465
|
+
const tok = code.substring(index,i);
|
|
466
|
+
if(tok.length) {
|
|
467
|
+
last_attr = tok;
|
|
468
|
+
b.attributes[tok] = true;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
index = i;
|
|
472
|
+
|
|
473
|
+
switch(code[index]) {
|
|
474
|
+
|
|
475
|
+
case '=':
|
|
476
|
+
|
|
477
|
+
if(last_attr) {
|
|
478
|
+
index++;
|
|
479
|
+
skip_spaces();
|
|
480
|
+
|
|
481
|
+
if(code[index]==='/') { // todo: comment after =, should be tested
|
|
482
|
+
[i] = lookAhead( parseComment, code, index );
|
|
483
|
+
if(i>index) index = i;
|
|
484
|
+
else index++;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
let av; // attribute value
|
|
488
|
+
|
|
489
|
+
switch(code[index]) {
|
|
490
|
+
case '\'':
|
|
491
|
+
case '\"':
|
|
492
|
+
//case '\`':
|
|
493
|
+
[index, av] = lookAhead( parseString, code, index, code[index] );
|
|
494
|
+
b.attributes[last_attr] = av;
|
|
495
|
+
last_attr = undefined;
|
|
496
|
+
break;
|
|
497
|
+
|
|
498
|
+
case '{':
|
|
499
|
+
[index, av] = lookAhead( parseJS, code, index, true );
|
|
500
|
+
b.attributes[last_attr] = av;
|
|
501
|
+
last_attr = undefined;
|
|
502
|
+
break;
|
|
503
|
+
|
|
504
|
+
default:
|
|
505
|
+
{
|
|
506
|
+
i = index;
|
|
507
|
+
while( i<code.length && delims.indexOf(code[i])===-1 ) {
|
|
508
|
+
i++;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
if(i===code.length) {
|
|
512
|
+
return; // not xml
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
const tok = code.substring(index,i);
|
|
516
|
+
index = i;
|
|
517
|
+
|
|
518
|
+
b.attributes[last_attr] = tok;
|
|
519
|
+
last_attr = undefined;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
else {
|
|
524
|
+
//console.log(`ill-formed xml (attribute)`, index);
|
|
525
|
+
return; // not xml
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
break;
|
|
529
|
+
|
|
530
|
+
case '{':
|
|
531
|
+
let jsc;
|
|
532
|
+
[index, jsc] = lookAhead( parseJS, code, index, true );
|
|
533
|
+
jsc.is_xml_js = true;
|
|
534
|
+
b.js_attributes.push(jsc);
|
|
535
|
+
break;
|
|
536
|
+
|
|
537
|
+
case '/':
|
|
538
|
+
if(code[index+1]==='>') {
|
|
539
|
+
b.end = index+2;
|
|
540
|
+
b.cbegin = b.begin;
|
|
541
|
+
b.cend = b.end;
|
|
542
|
+
|
|
543
|
+
//b.children = undefined;
|
|
544
|
+
b.self_closing = true;
|
|
545
|
+
|
|
546
|
+
if(container) container.children.push(b);
|
|
547
|
+
return b;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
[i] = lookAhead( parseComment, code, index );
|
|
551
|
+
if(i>index) index = i;
|
|
552
|
+
else index++;
|
|
553
|
+
|
|
554
|
+
break;
|
|
555
|
+
|
|
556
|
+
case '>':
|
|
557
|
+
const i = parseXMLContent(code, index, b);
|
|
558
|
+
|
|
559
|
+
// not xml
|
|
560
|
+
// todo: side effects must be checked
|
|
561
|
+
if(i===null) return;
|
|
562
|
+
|
|
563
|
+
if(i>index) index = i;
|
|
564
|
+
else index++;
|
|
565
|
+
|
|
566
|
+
if(b.end) {
|
|
567
|
+
if(container) container.children.push(b);
|
|
568
|
+
return b;
|
|
569
|
+
}
|
|
570
|
+
else {
|
|
571
|
+
index++;
|
|
572
|
+
}
|
|
573
|
+
break;
|
|
574
|
+
|
|
575
|
+
case ' ':
|
|
576
|
+
case '\t':
|
|
577
|
+
case '\n':
|
|
578
|
+
skip_spaces();
|
|
579
|
+
break;
|
|
580
|
+
|
|
581
|
+
default:
|
|
582
|
+
return;
|
|
583
|
+
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
|
|
589
|
+
function parseParanthesis(code, index, container)
|
|
590
|
+
{
|
|
591
|
+
const b = { // block
|
|
592
|
+
type: "paranthesis",
|
|
593
|
+
begin: index,
|
|
594
|
+
cbegin: ++index,
|
|
595
|
+
children: [],
|
|
596
|
+
self_closing: false
|
|
597
|
+
};
|
|
598
|
+
|
|
599
|
+
while(index<code.length) {
|
|
600
|
+
const ch = code[index];
|
|
601
|
+
|
|
602
|
+
switch(ch) {
|
|
603
|
+
|
|
604
|
+
case '<':{
|
|
605
|
+
let [i] = lookAhead( parseXML, code, index, b );
|
|
606
|
+
if(i>index) index = i;
|
|
607
|
+
else index++;
|
|
608
|
+
break;
|
|
609
|
+
}
|
|
610
|
+
case '\"':
|
|
611
|
+
case '\'':
|
|
612
|
+
case '\`':
|
|
613
|
+
[index] = lookAhead( parseString, code, index, ch );
|
|
614
|
+
break;
|
|
615
|
+
|
|
616
|
+
case '{':
|
|
617
|
+
[index] = lookAhead( parseJS, code, index, true, b );
|
|
618
|
+
break;
|
|
619
|
+
|
|
620
|
+
case '(':
|
|
621
|
+
[index] = lookAhead( parseParanthesis, code, index, b );
|
|
622
|
+
break;
|
|
623
|
+
|
|
624
|
+
case '}':
|
|
625
|
+
raiseError(`unmatched curly bracket`, index);
|
|
626
|
+
break;
|
|
627
|
+
|
|
628
|
+
case ')':
|
|
629
|
+
b.end = index+1;
|
|
630
|
+
b.cend = index;
|
|
631
|
+
if(container) container.children.push(b);
|
|
632
|
+
return b;
|
|
633
|
+
|
|
634
|
+
case '/':
|
|
635
|
+
const [i] = lookAhead( parseComment, code, index, b );
|
|
636
|
+
if(i>index) index = i;
|
|
637
|
+
else index++;
|
|
638
|
+
break;
|
|
639
|
+
|
|
640
|
+
case '\\':
|
|
641
|
+
index++;
|
|
642
|
+
default:
|
|
643
|
+
index++;
|
|
644
|
+
break;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
raiseError(`unterminated paranthesis block (started at ${b.begin})`, index);
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
|
|
652
|
+
function parseJS(code, index=0, is_block=false, container)
|
|
653
|
+
{
|
|
654
|
+
const b = { // block
|
|
655
|
+
type: "js",
|
|
656
|
+
begin: index,
|
|
657
|
+
cbegin: index+=(is_block?1:0),
|
|
658
|
+
children: []
|
|
659
|
+
};
|
|
660
|
+
|
|
661
|
+
while(index<code.length) {
|
|
662
|
+
const ch = code[index];
|
|
663
|
+
|
|
664
|
+
switch(ch) {
|
|
665
|
+
|
|
666
|
+
case '<': {
|
|
667
|
+
let [i] = lookAhead( parseXML, code, index, b );
|
|
668
|
+
|
|
669
|
+
if(i>index) index = i;
|
|
670
|
+
else index++;
|
|
671
|
+
break;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
case '\"':
|
|
675
|
+
case '\'':
|
|
676
|
+
case '\`':
|
|
677
|
+
[index] = lookAhead( parseString, code, index, ch );
|
|
678
|
+
break;
|
|
679
|
+
|
|
680
|
+
case '{': {
|
|
681
|
+
const i = index;
|
|
682
|
+
[index] = lookAhead( parseJS, code, index, true, b );
|
|
683
|
+
break;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
case '(': // this is only for function detection
|
|
687
|
+
[index] = lookAhead( parseParanthesis, code, index, b );
|
|
688
|
+
break;
|
|
689
|
+
|
|
690
|
+
case ')': // this is only for function detection
|
|
691
|
+
raiseError(`unmatched paranthesis ${index} [from ${b.begin}]`, index);
|
|
692
|
+
break;
|
|
693
|
+
|
|
694
|
+
case '}':
|
|
695
|
+
if(is_block) {
|
|
696
|
+
b.end = index+1;
|
|
697
|
+
b.cend = index;
|
|
698
|
+
if(container) container.children.push(b);
|
|
699
|
+
return b;
|
|
700
|
+
}
|
|
701
|
+
raiseError(`unmatched curly bracket`, index);
|
|
702
|
+
break;
|
|
703
|
+
|
|
704
|
+
case '/':
|
|
705
|
+
{
|
|
706
|
+
let [i] = lookAhead( parseComment, code, index, b );
|
|
707
|
+
if(i>index) index = i;
|
|
708
|
+
else index++;
|
|
709
|
+
break;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
case transpilerConfig.preprocessorDelimiter:
|
|
713
|
+
{
|
|
714
|
+
let [i] = lookAhead( parseDirective, code, index, b );
|
|
715
|
+
if(i>index) index = i;
|
|
716
|
+
else index++;
|
|
717
|
+
break;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
case '\\':
|
|
721
|
+
index++;
|
|
722
|
+
|
|
723
|
+
default:
|
|
724
|
+
index++;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
if(is_block) raiseError(`unterminated js block at ${index} (started at ${b.begin})`);
|
|
729
|
+
|
|
730
|
+
b.end = index;
|
|
731
|
+
b.cend = index;
|
|
732
|
+
if(container) container.children.push(b);
|
|
733
|
+
return b;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
|
|
737
|
+
// this is for lilact internal use, it is a workaround for approximating error location
|
|
738
|
+
// in eval when transpiling and running jsx directly in browser.
|
|
739
|
+
|
|
740
|
+
function labelFunctions(node, eols)
|
|
741
|
+
{
|
|
742
|
+
|
|
743
|
+
node.already_labeled = true;
|
|
744
|
+
|
|
745
|
+
function getNext(i, step=1)
|
|
746
|
+
{
|
|
747
|
+
while( (i+=step) < node.children.length && i>=0) {
|
|
748
|
+
const ch = node.children[i];
|
|
749
|
+
|
|
750
|
+
if(typeof(ch)==='string') {
|
|
751
|
+
const t = ch.trim();
|
|
752
|
+
if(t.length) return [i, t];
|
|
753
|
+
}
|
|
754
|
+
if(typeof(ch)==='object' && ch.type!=='comment') {
|
|
755
|
+
return [i,ch];
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
return [null,null];
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
let i, nxt, prev;
|
|
762
|
+
for(let chi=0; chi<node.children.length; chi++) {
|
|
763
|
+
|
|
764
|
+
if(typeof(node.children[chi])==='object' && node.children[chi].type==='paranthesis') {
|
|
765
|
+
|
|
766
|
+
[i, prev] = getNext(chi, -1);
|
|
767
|
+
if(prev===null) continue;
|
|
768
|
+
|
|
769
|
+
if(typeof(prev)!=='string' || ['switch', 'catch', 'try', 'class'].find((x)=>prev.trim().endsWith(x))!==undefined) continue;
|
|
770
|
+
let label = prev;
|
|
771
|
+
|
|
772
|
+
[i, prev] = getNext(i, -1);
|
|
773
|
+
if(prev===null) continue;
|
|
774
|
+
if(typeof(prev)==='string' && ['extern', 'class'].indexOf(prev)!==-1) continue;
|
|
775
|
+
|
|
776
|
+
|
|
777
|
+
[i, nxt] = getNext(chi);
|
|
778
|
+
|
|
779
|
+
if(nxt===null) continue;
|
|
780
|
+
|
|
781
|
+
if(transpilerConfig.injectTraceLabels && typeof(nxt)==='object' && nxt.type==='js') {
|
|
782
|
+
const begin = getRowCol(eols, chi); // begin is always the location of function args paranthesis
|
|
783
|
+
nxt.children.splice(1,0, `/*LILACTBLOCK${++transpilerConfig.func_num}:${begin}:${label}*/try{`);
|
|
784
|
+
if(transpilerConfig.enableLabelStack) {
|
|
785
|
+
nxt.children.splice(nxt.children.length-1, 0, `} catch(e){ if(typeof(e)!=='object') e=new Error(e);e.lilact_trace=[${transpilerConfig.func_num},e.lilact_trace];throw e}`);
|
|
786
|
+
}
|
|
787
|
+
else {
|
|
788
|
+
nxt.children.splice(nxt.children.length-1, 0, `} catch(e){ if(typeof(e)!=='object') e=new Error(e);e.lilact_trace=${transpilerConfig.func_num};throw e}`);
|
|
789
|
+
}
|
|
790
|
+
chi += 2;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
else if(node.children[chi]==='=>') {
|
|
795
|
+
[i, prev] = getNext(chi, -1);
|
|
796
|
+
|
|
797
|
+
if(typeof(prev)==='object' && prev.type==='paranthesis') {
|
|
798
|
+
|
|
799
|
+
const begin = getRowCol(eols, prev.begin); // begin is always the location of function args paranthesis
|
|
800
|
+
|
|
801
|
+
[i, nxt] = getNext(chi);
|
|
802
|
+
|
|
803
|
+
if(typeof(nxt)==='object' && nxt.type==='js') {
|
|
804
|
+
|
|
805
|
+
if(transpilerConfig.injectTraceLabels) {
|
|
806
|
+
|
|
807
|
+
nxt.children.splice(1,0,
|
|
808
|
+
`/*LILACTBLOCK${++transpilerConfig.func_num}:${begin}:<ARROW>*/try {`);
|
|
809
|
+
|
|
810
|
+
if(transpilerConfig.enableLabelStack) {
|
|
811
|
+
nxt.children.splice(nxt.children.length-1, 0,
|
|
812
|
+
`} catch(e){if(typeof(e)!=='object') e=new Error(e);e.lilact_trace=[${transpilerConfig.func_num},e.lilact_trace];throw e}`);
|
|
813
|
+
}
|
|
814
|
+
else {
|
|
815
|
+
nxt.children.splice(nxt.children.length-1, 0,
|
|
816
|
+
`} catch(e){if(typeof(e)!=='object') e=new Error(e);e.lilact_trace=${transpilerConfig.func_num};throw e}`);
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
}
|
|
820
|
+
chi += 2;
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
function scanEOLs(code)
|
|
830
|
+
{
|
|
831
|
+
const endlines = [0];
|
|
832
|
+
|
|
833
|
+
for(let i in code) {
|
|
834
|
+
if(code[i]==='\n') {
|
|
835
|
+
endlines.push(parseInt(i));
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
endlines.push(code.length);
|
|
839
|
+
|
|
840
|
+
return endlines;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
function getRowCol(eols, i)
|
|
844
|
+
{
|
|
845
|
+
if(!eols.last) eols.last=1;
|
|
846
|
+
|
|
847
|
+
while(i>eols[eols.last]) {
|
|
848
|
+
eols.last++;
|
|
849
|
+
if(eols.last===eols.length) {
|
|
850
|
+
eols.last=eols.length-1;
|
|
851
|
+
break;
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
while(i<eols[eols.last-1] && eols.last>1) {
|
|
855
|
+
eols.last--;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
return [eols.last-1, i-eols[eols.last-1]];
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
|
|
862
|
+
function generateSourceMap(json, path, jsx_eols, out_eols, mappings=[])
|
|
863
|
+
{
|
|
864
|
+
let mpps = [];
|
|
865
|
+
|
|
866
|
+
const sourcemap = {
|
|
867
|
+
"version":3,
|
|
868
|
+
"file":path,
|
|
869
|
+
"sourceRoot":"",
|
|
870
|
+
"sources":[path],
|
|
871
|
+
"names":[],
|
|
872
|
+
"mappings":""
|
|
873
|
+
};
|
|
874
|
+
|
|
875
|
+
|
|
876
|
+
const scan_leaves = (node)=>{
|
|
877
|
+
if(!!node.children) {
|
|
878
|
+
|
|
879
|
+
for(const ch of node.children) {
|
|
880
|
+
scan_leaves(ch);
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
if(node.begin!==undefined && node.out_index!==undefined) {
|
|
884
|
+
mpps.push( [ ...getRowCol(out_eols,node.out_index), ...getRowCol(jsx_eols,node.begin), node ] );
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
scan_leaves(json);
|
|
889
|
+
|
|
890
|
+
mpps = mpps.sort( (a,b)=>{
|
|
891
|
+
if (a[0] == b[0]) {
|
|
892
|
+
return a[1] - b[1];
|
|
893
|
+
}
|
|
894
|
+
return a[0] - b[0];
|
|
895
|
+
}
|
|
896
|
+
);
|
|
897
|
+
|
|
898
|
+
mappings.push(...mpps);
|
|
899
|
+
|
|
900
|
+
let mstr = "";
|
|
901
|
+
let r=0, oc=0;
|
|
902
|
+
|
|
903
|
+
let lr = 0, lc=0;
|
|
904
|
+
|
|
905
|
+
for(let i=0; i<mpps.length;i++) {
|
|
906
|
+
const m = mpps[i];
|
|
907
|
+
|
|
908
|
+
while(r<m[0]) {
|
|
909
|
+
oc=0;
|
|
910
|
+
mstr+=(";");
|
|
911
|
+
r++;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
mstr+=',';
|
|
915
|
+
|
|
916
|
+
mstr+=( vlq.encode(m[1]-oc,0,m[2]-lr,m[3]-lc) );
|
|
917
|
+
|
|
918
|
+
oc = m[1];
|
|
919
|
+
lr = m[2];
|
|
920
|
+
lc = m[3];
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
sourcemap.mappings = mstr.substring(1).replace(/;,/g, ";");
|
|
924
|
+
|
|
925
|
+
return "\n\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,"+btoa(JSON.stringify(sourcemap));
|
|
926
|
+
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
|
|
930
|
+
/**
|
|
931
|
+
* This is the function that transpiles jsx code to js. It is not recommended to use directly.
|
|
932
|
+
*
|
|
933
|
+
* Use `Lilact.require` instead, or `Lilact.run` available in `run`, or add a `<scirpt type="text/jsx" .../>` to your html.
|
|
934
|
+
*
|
|
935
|
+
* Lilact jsx parser doesn't rely on the Lilact itself, and can be used by other engines. The easiest way
|
|
936
|
+
* is using the `bin/transpile.js` script. It has its own help.
|
|
937
|
+
*
|
|
938
|
+
* @returns The transpiled javascript code.
|
|
939
|
+
*/
|
|
940
|
+
|
|
941
|
+
export function transpileJSX( jsx, {
|
|
942
|
+
factory = "Lilact.createComponent",
|
|
943
|
+
path = "anonymous",
|
|
944
|
+
appendSourcemap = true,
|
|
945
|
+
mappings = [],
|
|
946
|
+
injectTraceLabels = false,
|
|
947
|
+
discardComments = false
|
|
948
|
+
} = {} )
|
|
949
|
+
{
|
|
950
|
+
|
|
951
|
+
// func_num is shared so consecutive transpilations create new ids each time.
|
|
952
|
+
transpilerConfig.func_num ??= 0;
|
|
953
|
+
transpilerConfig.preprocessorDelimiter ??= 'ʔ'; // alt+shift+. on apple abc extended layout
|
|
954
|
+
|
|
955
|
+
transpilerConfig.injectTraceLabels ??= injectTraceLabels; // alt+shift+. on apple abc extended layout
|
|
956
|
+
|
|
957
|
+
const eols = scanEOLs(jsx);
|
|
958
|
+
|
|
959
|
+
raiseError = ((eols, msg, index)=>{
|
|
960
|
+
const er = new Error(msg);
|
|
961
|
+
er.name = 'JSXParseError';
|
|
962
|
+
[er.lineNumber, er.columnNumber] = getRowCol(eols, index);
|
|
963
|
+
er.lilact_trace = 'parse';
|
|
964
|
+
throw er;
|
|
965
|
+
}).bind(null, eols);
|
|
966
|
+
|
|
967
|
+
const tokenize_re = /([\{\}\(\),;\[\]\n]|[\s^\n]+)/g;
|
|
968
|
+
|
|
969
|
+
const json = parseJS(jsx);
|
|
970
|
+
json.data = jsx;
|
|
971
|
+
|
|
972
|
+
const prepare = (node)=>{
|
|
973
|
+
|
|
974
|
+
if(node.attributes!==undefined) {
|
|
975
|
+
for(const attr in node.attributes) {
|
|
976
|
+
if(typeof(node.attributes[attr])==='object') {
|
|
977
|
+
prepare(node.attributes[attr]);
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
if(node.children!==undefined && !node.self_closing) {
|
|
983
|
+
|
|
984
|
+
let i = node.type==='js'? node.begin : (node.cbegin || node.begin);
|
|
985
|
+
for(let chi=0; chi<node.children.length; chi++) {
|
|
986
|
+
const ch = node.children[chi];
|
|
987
|
+
|
|
988
|
+
if(ch.begin>i) {
|
|
989
|
+
let s = jsx.substring(i, ch.begin);
|
|
990
|
+
|
|
991
|
+
if(node.type==='xml') s = s.trim();
|
|
992
|
+
if(s.length) {
|
|
993
|
+
if(node.type==='xml') {
|
|
994
|
+
node.children.splice(chi, 0, '"'+s.replaceAll("\n", "\\\n")+'"');
|
|
995
|
+
}
|
|
996
|
+
else {
|
|
997
|
+
node.children.splice(chi, 0, ...s.split(tokenize_re));
|
|
998
|
+
}
|
|
999
|
+
chi++;
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
i = ch.end;
|
|
1003
|
+
|
|
1004
|
+
prepare(ch);
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
const e = node.type==='js'? node.end : (node.cend || node.end);
|
|
1008
|
+
|
|
1009
|
+
if(i<e && !node.self_closing) {
|
|
1010
|
+
let s = jsx.substring(i, e);
|
|
1011
|
+
if(node.type==='xml') s = s.trim();
|
|
1012
|
+
if(s.length) {
|
|
1013
|
+
if(node.type==='xml') {
|
|
1014
|
+
node.children.push( '"'+s.replaceAll("\n", "\\\n")+'"' );
|
|
1015
|
+
}
|
|
1016
|
+
else {
|
|
1017
|
+
node.children.push( ...s.split(tokenize_re) );
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
node.children = node.children.filter( (x)=>x!=="" );
|
|
1023
|
+
|
|
1024
|
+
if(transpilerConfig.setFunctionLabels && !node.already_labeled) {
|
|
1025
|
+
labelFunctions(node,eols);
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
prepare(json);
|
|
1031
|
+
|
|
1032
|
+
preprocessPragmas(json);
|
|
1033
|
+
|
|
1034
|
+
const codify = (outlen, node, is_attr=false, is_xml=false)=> {
|
|
1035
|
+
|
|
1036
|
+
if(typeof(node)!=='object') return node;
|
|
1037
|
+
|
|
1038
|
+
node.out_index = outlen;
|
|
1039
|
+
|
|
1040
|
+
if(node.type==='string') return jsx.substring(node.begin, node.end);
|
|
1041
|
+
if(node.type==='regex') return jsx.substring(node.begin, node.end);
|
|
1042
|
+
if(node.type==='comment') return (discardComments || is_attr)?'':jsx.substring(node.begin, node.end);
|
|
1043
|
+
if(node.type==='directive') return node.value;
|
|
1044
|
+
|
|
1045
|
+
|
|
1046
|
+
if(node.type==='paranthesis') {
|
|
1047
|
+
let out = "(";
|
|
1048
|
+
|
|
1049
|
+
if(node.out) {
|
|
1050
|
+
for(const ch of node.out) {
|
|
1051
|
+
out+=codify(outlen+out.length-1, ch);
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
return out + ")";
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
if(node.type==='js') {
|
|
1058
|
+
let out = "";
|
|
1059
|
+
|
|
1060
|
+
if(node.out) {
|
|
1061
|
+
for(const ch of node.out) {
|
|
1062
|
+
if(is_xml && ch.type==='comment') continue;
|
|
1063
|
+
out+=codify( outlen + out.length - (is_attr?1:0), ch);
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
if(is_attr) {
|
|
1068
|
+
return out.substring(1, out.length-1);
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
// todo: this is very simple, it should parse recursively
|
|
1072
|
+
if(node?.is_xml_js) {
|
|
1073
|
+
return jsx.substring(node.begin+1, node.end-1);
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
return out;
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
if(node.type==='xml') {
|
|
1080
|
+
|
|
1081
|
+
if(node.tag.length===0) {
|
|
1082
|
+
node.tag = "null";
|
|
1083
|
+
}
|
|
1084
|
+
else if(node.tag[0]!==node.tag[0].toUpperCase()) {
|
|
1085
|
+
node.tag = `"${node.tag}"`;
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
let out = '';
|
|
1089
|
+
|
|
1090
|
+
|
|
1091
|
+
let is_first=true;
|
|
1092
|
+
|
|
1093
|
+
for(const a of node.js_attributes) {
|
|
1094
|
+
let oo = codify(outlen + out.length, a, false);
|
|
1095
|
+
|
|
1096
|
+
out+=`${is_first?'':', '}${oo}`
|
|
1097
|
+
is_first=false;
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
|
|
1101
|
+
if(node.attributes) {
|
|
1102
|
+
for(const a in node.attributes) {
|
|
1103
|
+
out+=`${is_first?'':', '}"${a}": ${codify(outlen + out.length, node.attributes[a], true)}`
|
|
1104
|
+
is_first=false;
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
out+=" }";
|
|
1109
|
+
|
|
1110
|
+
if(node.out) {
|
|
1111
|
+
for(const ch of node.out) {
|
|
1112
|
+
const o = codify(outlen + out.length, ch, true, true);
|
|
1113
|
+
if(o.length>0) {
|
|
1114
|
+
out+=`, ${o}`
|
|
1115
|
+
is_first=false;
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
const loc = getRowCol(eols,node.begin);
|
|
1121
|
+
out = `${factory}( ${node.tag}, { ${out} )`; //lilact_jsx_loc:["${path}", ${loc}]
|
|
1122
|
+
|
|
1123
|
+
return out;
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
let out = '';
|
|
1128
|
+
if(injectTraceLabels) {
|
|
1129
|
+
out=`/*LILACTBLOCK${++transpilerConfig.func_num}:0,0:<EXEC>*/try{`
|
|
1130
|
+
|
|
1131
|
+
transpilerConfig.func_labels[transpilerConfig.func_num] = {
|
|
1132
|
+
path,
|
|
1133
|
+
row: 1,
|
|
1134
|
+
col: 1,
|
|
1135
|
+
label: transpilerConfig.func_num,
|
|
1136
|
+
required: transpilerConfig.required[path]
|
|
1137
|
+
};
|
|
1138
|
+
|
|
1139
|
+
}
|
|
1140
|
+
out+=codify(0, json);
|
|
1141
|
+
|
|
1142
|
+
if(injectTraceLabels) {
|
|
1143
|
+
if(transpilerConfig.enableLabelStack) {
|
|
1144
|
+
out += `}catch(e){ if(typeof(e)!=='object') e=new Error(e);e.lilact_trace=[${transpilerConfig.func_num},e.lilact_trace];throw e}`;
|
|
1145
|
+
}
|
|
1146
|
+
else {
|
|
1147
|
+
out += `}catch(e){ if(typeof(e)!=='object') e=new Error(e);e.lilact_trace=${transpilerConfig.func_num};throw e}`;
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
const inline_sm = generateSourceMap(json, path, eols, scanEOLs(out), mappings);
|
|
1152
|
+
if(appendSourcemap) {
|
|
1153
|
+
out += inline_sm;
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
if(globalThis[TRANSPILER_OUTPUT]) {
|
|
1157
|
+
try {
|
|
1158
|
+
const fs = import('fs');
|
|
1159
|
+
fs.writeFileSync(globalThis[TRANSPILER_OUTPUT], out);
|
|
1160
|
+
}
|
|
1161
|
+
catch(e) {}
|
|
1162
|
+
globalThis[TRANSPILER_OUTPUT] = undefined;
|
|
1163
|
+
}
|
|
1164
|
+
//console.log(out);
|
|
1165
|
+
|
|
1166
|
+
return out;
|
|
1167
|
+
}
|
|
1168
|
+
|