@lightningjs/blits 0.6.4 → 0.6.6
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/CHANGELOG.md +15 -0
- package/boilerplate/index.html +1 -1
- package/boilerplate/package.json +1 -1
- package/boilerplate/src/index.js +17 -3
- package/boilerplate/vite.config.js +1 -0
- package/package.json +1 -1
- package/src/application.js +1 -3
- package/src/component.js +3 -3
- package/src/element.js +15 -7
- package/src/lib/codegenerator/generator.js +13 -3
- package/src/lib/codegenerator/generator.test_.js +23 -23
- package/src/lib/symbols.js +1 -2
- package/src/lib/templateparser/parser.js +112 -43
- package/src/lib/templateparser/parser.test.js +84 -49
- package/vite/preCompiler.js +8 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
# v0.6.6
|
|
4
|
+
|
|
5
|
+
_24 jan 2024_
|
|
6
|
+
|
|
7
|
+
- Added temporary fix for renderer issue [#123](https://github.com/lightning-js/renderer/issues/123)
|
|
8
|
+
- Fixed issue with setting mount, pivot and scale values to zero (0) in object literal with x and y key
|
|
9
|
+
- Added automatic mapping of ref string to each item in a for-loop
|
|
10
|
+
|
|
11
|
+
# v0.6.5
|
|
12
|
+
|
|
13
|
+
_16 jan 2024_
|
|
14
|
+
|
|
15
|
+
- Added `path` to boilerplate vite.config.js for deployments in a sub folder
|
|
16
|
+
- Improved error handling in the template parser, with more contextual error messages
|
|
17
|
+
|
|
3
18
|
# v0.6.4
|
|
4
19
|
|
|
5
20
|
_15 jan 2024_
|
package/boilerplate/index.html
CHANGED
package/boilerplate/package.json
CHANGED
package/boilerplate/src/index.js
CHANGED
|
@@ -11,8 +11,22 @@ Blits.Launch(App, 'app', {
|
|
|
11
11
|
debugLevel: 1,
|
|
12
12
|
fontLoader: fontLoader,
|
|
13
13
|
fonts: [
|
|
14
|
-
{
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
{
|
|
15
|
+
family: 'lato',
|
|
16
|
+
type: 'msdf',
|
|
17
|
+
png: 'fonts/Lato-Regular.msdf.png',
|
|
18
|
+
json: 'fonts/Lato-Regular.msdf.json',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
family: 'raleway',
|
|
22
|
+
type: 'msdf',
|
|
23
|
+
png: 'fonts/Raleway-ExtraBold.msdf.png',
|
|
24
|
+
json: 'fonts/Raleway-ExtraBold.msdf.json',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
family: 'opensans',
|
|
28
|
+
type: 'web',
|
|
29
|
+
file: 'fonts/OpenSans-Medium.ttf',
|
|
30
|
+
},
|
|
17
31
|
],
|
|
18
32
|
})
|
|
@@ -3,6 +3,7 @@ import blitsVitePlugins from '@lightningjs/blits/vite'
|
|
|
3
3
|
|
|
4
4
|
export default defineConfig(({ command, mode, ssrBuild }) => {
|
|
5
5
|
return {
|
|
6
|
+
base: '/', // Set to your base path if you are deploying to a subdirectory (example: /myApp/)
|
|
6
7
|
plugins: [...blitsVitePlugins],
|
|
7
8
|
resolve: {
|
|
8
9
|
mainFields: ['browser', 'module', 'jsnext:main', 'jsnext'],
|
package/package.json
CHANGED
package/src/application.js
CHANGED
package/src/component.js
CHANGED
|
@@ -47,11 +47,11 @@ const required = (name) => {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
const Component = (name = required('name'), config = required('config')) => {
|
|
50
|
-
const setupComponent = (lifecycle) => {
|
|
50
|
+
const setupComponent = (lifecycle, parentComponent) => {
|
|
51
51
|
// code generation
|
|
52
52
|
if (!config.code) {
|
|
53
53
|
Log.debug(`Generating code for ${name} component`)
|
|
54
|
-
config.code = codegenerator.call(config, parser(config.template))
|
|
54
|
+
config.code = codegenerator.call(config, parser(config.template, name, parentComponent))
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
setupBase(component)
|
|
@@ -115,7 +115,7 @@ const Component = (name = required('name'), config = required('config')) => {
|
|
|
115
115
|
this.lifecycle = createLifecycle(this)
|
|
116
116
|
|
|
117
117
|
if (!component.setup) {
|
|
118
|
-
setupComponent(this.lifecycle)
|
|
118
|
+
setupComponent(this.lifecycle, parentComponent)
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
this.parent = parentComponent
|
package/src/element.js
CHANGED
|
@@ -51,6 +51,8 @@ const unpackTransition = (obj) => {
|
|
|
51
51
|
return obj
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
let firstNode = false
|
|
55
|
+
|
|
54
56
|
const Props = {
|
|
55
57
|
set parent(v) {
|
|
56
58
|
this._props.parent = v === 'root' ? renderer.root : v.node
|
|
@@ -126,11 +128,11 @@ const Props = {
|
|
|
126
128
|
},
|
|
127
129
|
set mount(v) {
|
|
128
130
|
if (typeof v === 'object' || (isObjectString(v) && (v = parseToObject(v)))) {
|
|
129
|
-
if (v
|
|
131
|
+
if ('x' in v) {
|
|
130
132
|
this._props.mountX = v.x
|
|
131
133
|
this._set.add('mountX')
|
|
132
134
|
}
|
|
133
|
-
if (v
|
|
135
|
+
if ('y' in v) {
|
|
134
136
|
this._props.mountY = v.y
|
|
135
137
|
this._set.add('mountY')
|
|
136
138
|
}
|
|
@@ -142,11 +144,11 @@ const Props = {
|
|
|
142
144
|
},
|
|
143
145
|
set pivot(v) {
|
|
144
146
|
if (typeof v === 'object' || (isObjectString(v) && (v = parseToObject(v)))) {
|
|
145
|
-
if (v
|
|
147
|
+
if ('x' in v) {
|
|
146
148
|
this._set.add('pivotX')
|
|
147
149
|
this._props.pivotX = v.x
|
|
148
150
|
}
|
|
149
|
-
if (v
|
|
151
|
+
if ('y' in v) {
|
|
150
152
|
this._set.add('pivotY')
|
|
151
153
|
this._props.pivotY = v.y
|
|
152
154
|
}
|
|
@@ -158,8 +160,8 @@ const Props = {
|
|
|
158
160
|
},
|
|
159
161
|
set scale(v) {
|
|
160
162
|
if (typeof v === 'object' || (isObjectString(v) && (v = parseToObject(v)))) {
|
|
161
|
-
if (v
|
|
162
|
-
if (v
|
|
163
|
+
if ('x' in v) this._props.scaleX = v.x
|
|
164
|
+
if ('y' in v) this._props.scaleY = v.y
|
|
163
165
|
} else {
|
|
164
166
|
this._props.scale = v
|
|
165
167
|
}
|
|
@@ -239,6 +241,13 @@ const Element = {
|
|
|
239
241
|
this.props._set.has('src') || this.props._set.has('texture') ? 0xffffffff : 0
|
|
240
242
|
}
|
|
241
243
|
|
|
244
|
+
// temporary workaround for renderer issue https://github.com/lightning-js/renderer/issues/123
|
|
245
|
+
if (!firstNode) {
|
|
246
|
+
this.props._props.src =
|
|
247
|
+
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+ip1sAAAAASUVORK5CYII='
|
|
248
|
+
firstNode = true
|
|
249
|
+
}
|
|
250
|
+
|
|
242
251
|
this.node = props.__textnode
|
|
243
252
|
? renderer.createTextNode(this.props._props)
|
|
244
253
|
: renderer.createNode(this.props._props)
|
|
@@ -266,7 +275,6 @@ const Element = {
|
|
|
266
275
|
return this.animate(p, v, value.transition)
|
|
267
276
|
} else {
|
|
268
277
|
this.node[p] = v
|
|
269
|
-
console.log('set it', this.node[p], v)
|
|
270
278
|
}
|
|
271
279
|
}
|
|
272
280
|
},
|
|
@@ -56,7 +56,7 @@ const generateElementCode = function (
|
|
|
56
56
|
|
|
57
57
|
renderCode.push(`
|
|
58
58
|
if(!${elm}) {
|
|
59
|
-
${elm} = this.element({
|
|
59
|
+
${elm} = this.element({parent: parent || 'root'}, component)
|
|
60
60
|
}
|
|
61
61
|
const elementConfig${counter} = {}
|
|
62
62
|
`)
|
|
@@ -221,9 +221,19 @@ const generateForLoopCode = function (templateObject, parent) {
|
|
|
221
221
|
const scope = Object.assign(component, {
|
|
222
222
|
key: Math.random(),
|
|
223
223
|
${index}: __index,
|
|
224
|
-
${item}: collection[__index]
|
|
225
|
-
|
|
224
|
+
${item}: collection[__index],
|
|
225
|
+
`)
|
|
226
|
+
|
|
227
|
+
if ('ref' in templateObject && templateObject.ref.indexOf('$') === -1) {
|
|
228
|
+
// automatically map the ref for each item in the loop based on the given ref key
|
|
229
|
+
ctx.renderCode.push(`
|
|
230
|
+
__ref: '${templateObject.ref}' + __index
|
|
226
231
|
`)
|
|
232
|
+
templateObject.ref = '$__ref'
|
|
233
|
+
}
|
|
234
|
+
ctx.renderCode.push(`
|
|
235
|
+
})
|
|
236
|
+
`)
|
|
227
237
|
|
|
228
238
|
if ('key' in templateObject) {
|
|
229
239
|
ctx.renderCode.push(`
|
|
@@ -107,7 +107,7 @@ test('Generate render and effect code for a template with a single simple elemen
|
|
|
107
107
|
function anonymous(parent,component,context) {
|
|
108
108
|
const elms = []
|
|
109
109
|
|
|
110
|
-
elms[1] = this.element(
|
|
110
|
+
elms[1] = this.element(parentId: parent && parent.nodeId || 'root'})
|
|
111
111
|
const elementConfig1 = {}
|
|
112
112
|
|
|
113
113
|
elementConfig1['type'] = "Component"
|
|
@@ -149,7 +149,7 @@ test('Generate code for a template with a simple element and a simple nested ele
|
|
|
149
149
|
function anonymous(parent,component,context) {
|
|
150
150
|
const elms = []
|
|
151
151
|
|
|
152
|
-
elms[1] = this.element(
|
|
152
|
+
elms[1] = this.element(parentId: parent && parent.nodeId || 'root'})
|
|
153
153
|
const elementConfig1 = {}
|
|
154
154
|
|
|
155
155
|
elementConfig1['type'] = "Component"
|
|
@@ -158,7 +158,7 @@ test('Generate code for a template with a simple element and a simple nested ele
|
|
|
158
158
|
|
|
159
159
|
parent = elms[1]
|
|
160
160
|
|
|
161
|
-
elms[2] = this.element(
|
|
161
|
+
elms[2] = this.element(parentId: parent && parent.nodeId || 'root'})
|
|
162
162
|
const elementConfig2 = {}
|
|
163
163
|
|
|
164
164
|
elementConfig2['type'] = "Element"
|
|
@@ -198,7 +198,7 @@ test('Generate code for a template with a single element with attributes', (asse
|
|
|
198
198
|
function anonymous(parent,component,context) {
|
|
199
199
|
const elms = []
|
|
200
200
|
|
|
201
|
-
elms[1] = this.element(
|
|
201
|
+
elms[1] = this.element(parentId: parent && parent.nodeId || 'root'})
|
|
202
202
|
const elementConfig1 = {}
|
|
203
203
|
|
|
204
204
|
elementConfig1['type'] = "Component"
|
|
@@ -246,7 +246,7 @@ test('Generate code for a template with attributes and a nested element with att
|
|
|
246
246
|
function anonymous(parent,component,context) {
|
|
247
247
|
const elms = []
|
|
248
248
|
|
|
249
|
-
elms[1] = this.element(
|
|
249
|
+
elms[1] = this.element(parentId: parent && parent.nodeId || 'root'})
|
|
250
250
|
const elementConfig1 = {}
|
|
251
251
|
|
|
252
252
|
elementConfig1['type'] = "Component"
|
|
@@ -257,7 +257,7 @@ test('Generate code for a template with attributes and a nested element with att
|
|
|
257
257
|
|
|
258
258
|
parent = elms[1]
|
|
259
259
|
|
|
260
|
-
elms[2] = this.element(
|
|
260
|
+
elms[2] = this.element(parentId: parent && parent.nodeId || 'root'})
|
|
261
261
|
const elementConfig2 = {}
|
|
262
262
|
|
|
263
263
|
elementConfig2['type'] = "Element"
|
|
@@ -313,7 +313,7 @@ test('Generate code for a template with attributes and 2 nested elements with at
|
|
|
313
313
|
function anonymous(parent,component,context) {
|
|
314
314
|
const elms = []
|
|
315
315
|
|
|
316
|
-
elms[1] = this.element(
|
|
316
|
+
elms[1] = this.element(parentId: parent && parent.nodeId || 'root'})
|
|
317
317
|
const elementConfig1 = {}
|
|
318
318
|
|
|
319
319
|
elementConfig1['type'] = "Component"
|
|
@@ -324,7 +324,7 @@ test('Generate code for a template with attributes and 2 nested elements with at
|
|
|
324
324
|
|
|
325
325
|
parent = elms[1]
|
|
326
326
|
|
|
327
|
-
elms[2] = this.element(
|
|
327
|
+
elms[2] = this.element(parentId: parent && parent.nodeId || 'root'})
|
|
328
328
|
const elementConfig2 = {}
|
|
329
329
|
|
|
330
330
|
elementConfig2['type'] = "Element"
|
|
@@ -336,7 +336,7 @@ test('Generate code for a template with attributes and 2 nested elements with at
|
|
|
336
336
|
|
|
337
337
|
parent = elms[1]
|
|
338
338
|
|
|
339
|
-
elms[3] = this.element(
|
|
339
|
+
elms[3] = this.element(parentId: parent && parent.nodeId || 'root'})
|
|
340
340
|
const elementConfig3 = {}
|
|
341
341
|
|
|
342
342
|
elementConfig3['type'] = "Element"
|
|
@@ -403,7 +403,7 @@ test('Generate code for a template with attributes and deep nested elements with
|
|
|
403
403
|
function anonymous(parent,component,context) {
|
|
404
404
|
const elms = []
|
|
405
405
|
|
|
406
|
-
elms[1] = this.element(
|
|
406
|
+
elms[1] = this.element(parentId: parent && parent.nodeId || 'root'})
|
|
407
407
|
const elementConfig1 = {}
|
|
408
408
|
|
|
409
409
|
elementConfig1['type'] = "Component"
|
|
@@ -414,7 +414,7 @@ test('Generate code for a template with attributes and deep nested elements with
|
|
|
414
414
|
|
|
415
415
|
parent = elms[1]
|
|
416
416
|
|
|
417
|
-
elms[2] = this.element(
|
|
417
|
+
elms[2] = this.element(parentId: parent && parent.nodeId || 'root'})
|
|
418
418
|
const elementConfig2 = {}
|
|
419
419
|
|
|
420
420
|
elementConfig2['type'] = "Element"
|
|
@@ -426,7 +426,7 @@ test('Generate code for a template with attributes and deep nested elements with
|
|
|
426
426
|
|
|
427
427
|
parent = elms[1]
|
|
428
428
|
|
|
429
|
-
elms[3] = this.element(
|
|
429
|
+
elms[3] = this.element(parentId: parent && parent.nodeId || 'root'})
|
|
430
430
|
const elementConfig3 = {}
|
|
431
431
|
|
|
432
432
|
elementConfig3['type'] = "Element"
|
|
@@ -438,7 +438,7 @@ test('Generate code for a template with attributes and deep nested elements with
|
|
|
438
438
|
|
|
439
439
|
parent = elms[3]
|
|
440
440
|
|
|
441
|
-
elms[4] = this.element(
|
|
441
|
+
elms[4] = this.element(parentId: parent && parent.nodeId || 'root'})
|
|
442
442
|
const elementConfig4 = {}
|
|
443
443
|
|
|
444
444
|
elementConfig4['type'] = "Element"
|
|
@@ -448,7 +448,7 @@ test('Generate code for a template with attributes and deep nested elements with
|
|
|
448
448
|
|
|
449
449
|
parent = elms[3]
|
|
450
450
|
|
|
451
|
-
elms[5] = this.element(
|
|
451
|
+
elms[5] = this.element(parentId: parent && parent.nodeId || 'root'})
|
|
452
452
|
const elementConfig5 = {}
|
|
453
453
|
|
|
454
454
|
elementConfig5['type'] = "Element"
|
|
@@ -492,7 +492,7 @@ test('Generate code for a template with simple dynamic attributes', (assert) =>
|
|
|
492
492
|
function anonymous(parent,component,context) {
|
|
493
493
|
const elms = []
|
|
494
494
|
|
|
495
|
-
elms[1] = this.element(
|
|
495
|
+
elms[1] = this.element(parentId: parent && parent.nodeId || 'root'})
|
|
496
496
|
const elementConfig1 = {}
|
|
497
497
|
|
|
498
498
|
elementConfig1['type'] = "Component"
|
|
@@ -559,7 +559,7 @@ test('Generate code for a template with an attribute with a dash', (assert) => {
|
|
|
559
559
|
function anonymous(parent,component,context) {
|
|
560
560
|
const elms = []
|
|
561
561
|
|
|
562
|
-
elms[1] = this.element(
|
|
562
|
+
elms[1] = this.element(parentId: parent && parent.nodeId || 'root'})
|
|
563
563
|
const elementConfig1 = {}
|
|
564
564
|
|
|
565
565
|
elementConfig1['type'] = "Component"
|
|
@@ -604,7 +604,7 @@ test('Generate code for a template with dynamic attributes with code to be evalu
|
|
|
604
604
|
function anonymous(parent,component,context) {
|
|
605
605
|
const elms = []
|
|
606
606
|
|
|
607
|
-
elms[1] = this.element(
|
|
607
|
+
elms[1] = this.element(parentId: parent && parent.nodeId || 'root'})
|
|
608
608
|
const elementConfig1 = {}
|
|
609
609
|
|
|
610
610
|
elementConfig1['type'] = "Component"
|
|
@@ -690,7 +690,7 @@ test('Generate code for a template with custom components', (assert) => {
|
|
|
690
690
|
function anonymous(parent,component,context) {
|
|
691
691
|
const elms = []
|
|
692
692
|
|
|
693
|
-
elms[1] = this.element(
|
|
693
|
+
elms[1] = this.element(parentId: parent && parent.nodeId || 'root'})
|
|
694
694
|
const elementConfig1 = {}
|
|
695
695
|
|
|
696
696
|
elementConfig1['type'] = "Component"
|
|
@@ -746,7 +746,7 @@ test('Generate code for a template with an unregistered custom component', (asse
|
|
|
746
746
|
function anonymous(parent,component,context) {
|
|
747
747
|
const elms = []
|
|
748
748
|
|
|
749
|
-
elms[1] = this.element(
|
|
749
|
+
elms[1] = this.element(parentId: parent && parent.nodeId || 'root'})
|
|
750
750
|
const elementConfig1 = {}
|
|
751
751
|
|
|
752
752
|
elementConfig1['type'] = "Component"
|
|
@@ -757,7 +757,7 @@ test('Generate code for a template with an unregistered custom component', (asse
|
|
|
757
757
|
|
|
758
758
|
parent = elms[1]
|
|
759
759
|
|
|
760
|
-
elms[3] = this.element(
|
|
760
|
+
elms[3] = this.element(parentId: parent && parent.nodeId || 'root'})
|
|
761
761
|
const elementConfig3 = {}
|
|
762
762
|
|
|
763
763
|
elementConfig3['type'] = "Poster2"
|
|
@@ -813,7 +813,7 @@ test('Generate code for a template with custom components with arguments', (asse
|
|
|
813
813
|
function anonymous(parent,component,context) {
|
|
814
814
|
const elms = []
|
|
815
815
|
|
|
816
|
-
elms[1] = this.element(
|
|
816
|
+
elms[1] = this.element(parentId: parent && parent.nodeId || 'root'})
|
|
817
817
|
const elementConfig1 = {}
|
|
818
818
|
|
|
819
819
|
elementConfig1['type'] = "Component"
|
|
@@ -876,7 +876,7 @@ test('Generate code for a template with custom components with reactive props',
|
|
|
876
876
|
function anonymous(parent,component,context) {
|
|
877
877
|
const elms = []
|
|
878
878
|
|
|
879
|
-
elms[1] = this.element(
|
|
879
|
+
elms[1] = this.element(parentId: parent && parent.nodeId || 'root'})
|
|
880
880
|
const elementConfig1 = {}
|
|
881
881
|
|
|
882
882
|
elementConfig1['type'] = "Component"
|
|
@@ -946,7 +946,7 @@ test('Generate code for a template with a transition attributes', (assert) => {
|
|
|
946
946
|
function anonymous(parent,component,context) {
|
|
947
947
|
const elms = []
|
|
948
948
|
|
|
949
|
-
elms[1] = this.element(
|
|
949
|
+
elms[1] = this.element(parentId: parent && parent.nodeId || 'root'})
|
|
950
950
|
const elementConfig1 = {}
|
|
951
951
|
|
|
952
952
|
elementConfig1['type'] = "Component"
|
package/src/lib/symbols.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
currentView: Symbol('currentView'),
|
|
3
|
+
cursorTagStart: Symbol('cursorTagStart'),
|
|
3
4
|
computedKeys: Symbol('computedKeys'),
|
|
4
5
|
destroy: Symbol('destroy'),
|
|
5
6
|
index: Symbol('index'),
|
|
@@ -31,8 +32,6 @@ export default {
|
|
|
31
32
|
children: Symbol.for('children'),
|
|
32
33
|
// Symbol 'components' utilized within generated code
|
|
33
34
|
components: Symbol.for('components'),
|
|
34
|
-
// Symbol 'id' utilized within generated code
|
|
35
|
-
id: Symbol.for('id'),
|
|
36
35
|
// Symbol 'isSlot' utilized within generated code
|
|
37
36
|
isSlot: Symbol.for('isSlot'),
|
|
38
37
|
// Symbol 'props' utilized within generated code
|
|
@@ -17,16 +17,9 @@
|
|
|
17
17
|
|
|
18
18
|
import symbols from '../symbols.js'
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
constructor(message, name, context) {
|
|
22
|
-
super(`TemplateParseError: ${message}`)
|
|
23
|
-
this.name = name
|
|
24
|
-
this.context = context
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export default (template = '') => {
|
|
20
|
+
export default (template = '', componentName, parentComponent, filePath = null) => {
|
|
29
21
|
let cursor = 0
|
|
22
|
+
let prevCursor = 0
|
|
30
23
|
let tags = []
|
|
31
24
|
let currentTag = null
|
|
32
25
|
let currentLevel = 0
|
|
@@ -45,13 +38,10 @@ export default (template = '') => {
|
|
|
45
38
|
parseLoop(parseEmptyTagStart)
|
|
46
39
|
return format(tags)
|
|
47
40
|
} catch (error) {
|
|
48
|
-
if (error
|
|
49
|
-
|
|
50
|
-
} else {
|
|
51
|
-
console.error(error)
|
|
41
|
+
if (error.name == 'TemplateParseError' || error.name == 'TemplateStructureError') {
|
|
42
|
+
error.message = `${error.message}\n${error.context}`
|
|
52
43
|
}
|
|
53
|
-
|
|
54
|
-
return null
|
|
44
|
+
throw error
|
|
55
45
|
}
|
|
56
46
|
}
|
|
57
47
|
|
|
@@ -63,9 +53,9 @@ export default (template = '') => {
|
|
|
63
53
|
}
|
|
64
54
|
|
|
65
55
|
// utils
|
|
66
|
-
const clean = (
|
|
56
|
+
const clean = (templateText) => {
|
|
67
57
|
// remove all unnecessary new lines and comments
|
|
68
|
-
return
|
|
58
|
+
return templateText
|
|
69
59
|
.replace(/<!--.*?-->/gms, '') // remove comments
|
|
70
60
|
.replace(/\r?\n\s*\r\n/gm, ' ') // remove empty lines
|
|
71
61
|
.replace(/\r?\n\s*(\S)/gm, ' $1') // remove line endings & spacing
|
|
@@ -75,7 +65,10 @@ export default (template = '') => {
|
|
|
75
65
|
|
|
76
66
|
const moveCursorOnMatch = (regex) => {
|
|
77
67
|
const match = template.slice(cursor).match(regex)
|
|
78
|
-
if (match)
|
|
68
|
+
if (match) {
|
|
69
|
+
prevCursor = cursor
|
|
70
|
+
cursor += match[0].length
|
|
71
|
+
}
|
|
79
72
|
return match
|
|
80
73
|
}
|
|
81
74
|
|
|
@@ -83,7 +76,12 @@ export default (template = '') => {
|
|
|
83
76
|
const parseEmptyTagStart = () => {
|
|
84
77
|
const match = moveCursorOnMatch(emptyTagStartRegex)
|
|
85
78
|
if (match) {
|
|
86
|
-
tags.push({
|
|
79
|
+
tags.push({
|
|
80
|
+
type: null,
|
|
81
|
+
[symbols.type]: 'opening',
|
|
82
|
+
[symbols.level]: currentLevel,
|
|
83
|
+
[symbols.cursorTagStart]: prevCursor,
|
|
84
|
+
})
|
|
87
85
|
currentLevel++
|
|
88
86
|
parseLoop(parseEmptyTagStart)
|
|
89
87
|
} else {
|
|
@@ -95,7 +93,11 @@ export default (template = '') => {
|
|
|
95
93
|
const match = moveCursorOnMatch(emptyTagEndRegex)
|
|
96
94
|
if (match) {
|
|
97
95
|
currentLevel--
|
|
98
|
-
tags.push({
|
|
96
|
+
tags.push({
|
|
97
|
+
type: null,
|
|
98
|
+
[symbols.type]: 'closing',
|
|
99
|
+
[symbols.level]: currentLevel,
|
|
100
|
+
})
|
|
99
101
|
parseLoop(parseEmptyTagStart)
|
|
100
102
|
} else {
|
|
101
103
|
parseLoop(parseTag)
|
|
@@ -105,16 +107,22 @@ export default (template = '') => {
|
|
|
105
107
|
const parseTag = () => {
|
|
106
108
|
const match = moveCursorOnMatch(tagStartRegex)
|
|
107
109
|
if (match) {
|
|
110
|
+
currentTag = {
|
|
111
|
+
type: match[1],
|
|
112
|
+
[symbols.level]: currentLevel,
|
|
113
|
+
[symbols.cursorTagStart]: prevCursor,
|
|
114
|
+
}
|
|
108
115
|
if (match[0].startsWith('</')) {
|
|
109
116
|
currentLevel--
|
|
110
|
-
currentTag
|
|
117
|
+
currentTag[symbols.type] = 'closing'
|
|
118
|
+
currentTag[symbols.level] = currentLevel
|
|
111
119
|
} else {
|
|
112
|
-
currentTag
|
|
120
|
+
currentTag[symbols.type] = 'opening'
|
|
113
121
|
currentLevel++
|
|
114
122
|
}
|
|
115
123
|
parseLoop(parseTagEnd)
|
|
116
124
|
} else {
|
|
117
|
-
throw
|
|
125
|
+
throw TemplateParseError('InvalidTag')
|
|
118
126
|
}
|
|
119
127
|
}
|
|
120
128
|
|
|
@@ -123,8 +131,7 @@ export default (template = '') => {
|
|
|
123
131
|
if (match) {
|
|
124
132
|
if (match[1] === '/>') {
|
|
125
133
|
if (currentTag[symbols.type] === 'closing') {
|
|
126
|
-
|
|
127
|
-
throw new TemplateParseError('InvalidClosingTag', template.slice(cursor - 10))
|
|
134
|
+
throw TemplateParseError('InvalidClosingTag')
|
|
128
135
|
}
|
|
129
136
|
currentTag[symbols.type] = 'self-closing'
|
|
130
137
|
currentLevel-- // because it was parsed as opening tag before
|
|
@@ -147,9 +154,14 @@ export default (template = '') => {
|
|
|
147
154
|
}
|
|
148
155
|
}
|
|
149
156
|
|
|
157
|
+
//fixme: closing tags cannot have attributes
|
|
150
158
|
const parseAttributes = () => {
|
|
151
159
|
const attrNameMatch = moveCursorOnMatch(attrNameRegex)
|
|
152
160
|
if (attrNameMatch) {
|
|
161
|
+
if (currentTag[symbols.type] === 'closing') {
|
|
162
|
+
throw TemplateParseError('AttributesInClosingTag')
|
|
163
|
+
}
|
|
164
|
+
|
|
153
165
|
const delimiter = attrNameMatch[2]
|
|
154
166
|
const attrValueRegex = new RegExp(`^(.*?)${delimiter}\\s*`)
|
|
155
167
|
const attrValueMatch = moveCursorOnMatch(attrValueRegex)
|
|
@@ -160,10 +172,10 @@ export default (template = '') => {
|
|
|
160
172
|
currentTag[attr.name] = attr.value
|
|
161
173
|
parseLoop(parseTagEnd)
|
|
162
174
|
} else {
|
|
163
|
-
throw
|
|
175
|
+
throw TemplateParseError('MissingOrInvalidAttributeValue')
|
|
164
176
|
}
|
|
165
177
|
} else {
|
|
166
|
-
throw
|
|
178
|
+
throw TemplateParseError('InvalidAttribute')
|
|
167
179
|
}
|
|
168
180
|
}
|
|
169
181
|
|
|
@@ -177,10 +189,8 @@ export default (template = '') => {
|
|
|
177
189
|
return { name, value }
|
|
178
190
|
}
|
|
179
191
|
|
|
180
|
-
// formating and validation
|
|
181
|
-
|
|
182
192
|
/*
|
|
183
|
-
validation rules:
|
|
193
|
+
Formatting & validation rules:
|
|
184
194
|
#1: Every opening tag must have a corresponding closing tag at the same level. If a closing tag is encountered without
|
|
185
195
|
a preceding opening tag at the same level, or if an opening tag is not followed by a corresponding closing tag at
|
|
186
196
|
the same level, an error should be thrown.
|
|
@@ -200,7 +210,7 @@ export default (template = '') => {
|
|
|
200
210
|
// Rule #1
|
|
201
211
|
if (element[symbols.level] === 0 && element[symbols.type] !== 'closing') {
|
|
202
212
|
if (rootElementDefined) {
|
|
203
|
-
throw
|
|
213
|
+
throw TemplateStructureError('MultipleTopLevelTags', element)
|
|
204
214
|
}
|
|
205
215
|
rootElementDefined = true
|
|
206
216
|
}
|
|
@@ -210,6 +220,7 @@ export default (template = '') => {
|
|
|
210
220
|
stack.push({
|
|
211
221
|
[symbols.level]: element[symbols.level],
|
|
212
222
|
[symbols.type]: element[symbols.type],
|
|
223
|
+
[symbols.cursorTagStart]: element[symbols.cursorTagStart],
|
|
213
224
|
type: element.type,
|
|
214
225
|
parent: currentParent, // helps getting the previous parent when closing tag is encountered
|
|
215
226
|
})
|
|
@@ -223,7 +234,7 @@ export default (template = '') => {
|
|
|
223
234
|
}
|
|
224
235
|
|
|
225
236
|
if (isStackEmpty || isLevelMismatch || isTagMismatch) {
|
|
226
|
-
throw
|
|
237
|
+
throw TemplateStructureError('MismatchedClosingTag', element)
|
|
227
238
|
}
|
|
228
239
|
|
|
229
240
|
// when we remove the closing element from the stack, we should set
|
|
@@ -235,6 +246,7 @@ export default (template = '') => {
|
|
|
235
246
|
const newItem = { ...element }
|
|
236
247
|
delete newItem[symbols.type]
|
|
237
248
|
delete newItem[symbols.level]
|
|
249
|
+
delete newItem[symbols.cursorTagStart]
|
|
238
250
|
|
|
239
251
|
// if it is an opening tag, add children[] to it and update current parent
|
|
240
252
|
if (element[symbols.type] === 'opening') {
|
|
@@ -251,21 +263,78 @@ export default (template = '') => {
|
|
|
251
263
|
|
|
252
264
|
// Check if all tags are closed (so stack should be empty)[Rule #1]
|
|
253
265
|
if (stack.length > 0) {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
266
|
+
throw TemplateStructureError('UnclosedTags', stack)
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return output
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// error reporting
|
|
273
|
+
const contextPaddingBefore = 10 // number of characters to show before the error location
|
|
274
|
+
const contextPaddingAfter = 50 // number of characters to show after the error location
|
|
275
|
+
|
|
276
|
+
const TemplateParseError = (message, context) => {
|
|
277
|
+
const location = getErrorLocation()
|
|
278
|
+
message = `${message} in ${location}`
|
|
279
|
+
|
|
280
|
+
const error = new Error(message)
|
|
281
|
+
error.name = 'TemplateParseError'
|
|
282
|
+
|
|
283
|
+
// generate context if the error is related to parsing
|
|
284
|
+
if (!context) {
|
|
285
|
+
const start = Math.max(0, prevCursor - contextPaddingBefore)
|
|
286
|
+
const end = Math.min(template.length, cursor + contextPaddingAfter)
|
|
287
|
+
const contextText = template.slice(start, end)
|
|
288
|
+
|
|
289
|
+
// add ^ caret to show where the error is
|
|
290
|
+
const caretPosition = cursor - start
|
|
291
|
+
error.context = insertContextCaret(caretPosition, contextText)
|
|
292
|
+
} else {
|
|
293
|
+
error.context = context
|
|
260
294
|
}
|
|
295
|
+
return error
|
|
296
|
+
}
|
|
261
297
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
298
|
+
const TemplateStructureError = (message, context) => {
|
|
299
|
+
const location = getErrorLocation()
|
|
300
|
+
message = `${message} in ${location}`
|
|
301
|
+
|
|
302
|
+
const error = new Error(message)
|
|
303
|
+
error.name = 'TemplateStructureError'
|
|
304
|
+
|
|
305
|
+
// check if context is an array
|
|
306
|
+
if (Array.isArray(context)) {
|
|
307
|
+
error.context = context.map((tag) => generateContext(tag)).join('\n')
|
|
308
|
+
} else {
|
|
309
|
+
error.context = generateContext(context)
|
|
266
310
|
}
|
|
267
311
|
|
|
268
|
-
|
|
312
|
+
function generateContext(element) {
|
|
313
|
+
const start = Math.max(0, element[symbols.cursorTagStart] - contextPaddingBefore)
|
|
314
|
+
const contextText = template.slice(start, start + contextPaddingAfter)
|
|
315
|
+
// add ^ caret to show where the error is
|
|
316
|
+
return insertContextCaret(contextPaddingBefore, contextText)
|
|
317
|
+
}
|
|
318
|
+
return error
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const insertContextCaret = (position, contextText) => {
|
|
322
|
+
const caret = ' '.repeat(position) + '^'
|
|
323
|
+
return `\n${contextText}\n${caret}\n`
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const getErrorLocation = () => {
|
|
327
|
+
if (parentComponent) {
|
|
328
|
+
let hierarchy = componentName || ''
|
|
329
|
+
let currentParent = parentComponent
|
|
330
|
+
|
|
331
|
+
while (currentParent) {
|
|
332
|
+
hierarchy = `${currentParent.type}/${hierarchy}`
|
|
333
|
+
currentParent = currentParent.parent
|
|
334
|
+
}
|
|
335
|
+
return hierarchy
|
|
336
|
+
}
|
|
337
|
+
return filePath ? filePath : 'Blits.Application'
|
|
269
338
|
}
|
|
270
339
|
|
|
271
340
|
return parse()
|
|
@@ -594,43 +594,6 @@ test('Parse template with a nameless tag', (assert) => {
|
|
|
594
594
|
assert.end()
|
|
595
595
|
})
|
|
596
596
|
|
|
597
|
-
// test('Parse template with a nameless tag but with arguments', (assert) => {
|
|
598
|
-
// const template = `
|
|
599
|
-
// <x="100" y="200">
|
|
600
|
-
// <x="50" y="20">
|
|
601
|
-
// <Component w="100" h="20" />
|
|
602
|
-
// </>
|
|
603
|
-
// </>`
|
|
604
|
-
|
|
605
|
-
// const expected = {
|
|
606
|
-
// children: [
|
|
607
|
-
// {
|
|
608
|
-
// type: null,
|
|
609
|
-
// x: '100',
|
|
610
|
-
// y: '200',
|
|
611
|
-
// children: [
|
|
612
|
-
// {
|
|
613
|
-
// type: null,
|
|
614
|
-
// x: '50',
|
|
615
|
-
// y: '20',
|
|
616
|
-
// children: [
|
|
617
|
-
// {
|
|
618
|
-
// type: 'Component',
|
|
619
|
-
// w: '100',
|
|
620
|
-
// h: '20',
|
|
621
|
-
// },
|
|
622
|
-
// ],
|
|
623
|
-
// },
|
|
624
|
-
// ],
|
|
625
|
-
// },
|
|
626
|
-
// ],
|
|
627
|
-
// }
|
|
628
|
-
// const actual = parser(template)
|
|
629
|
-
|
|
630
|
-
// assert.deepEqual(actual, expected, 'Parser should return object representation of template')
|
|
631
|
-
// assert.end()
|
|
632
|
-
// })
|
|
633
|
-
|
|
634
597
|
test('Parse template with a transition argument (single value)', (assert) => {
|
|
635
598
|
const template = `
|
|
636
599
|
<Element x.transition="$offset" y="200">
|
|
@@ -974,8 +937,16 @@ test('Parse template with multiple top level elements and parsing should fail',
|
|
|
974
937
|
/>
|
|
975
938
|
</Component>`
|
|
976
939
|
|
|
977
|
-
|
|
978
|
-
|
|
940
|
+
try {
|
|
941
|
+
parser(template)
|
|
942
|
+
assert.fail('Parser should throw TemplateStructureError:MultipleTopLevelTags')
|
|
943
|
+
} catch (error) {
|
|
944
|
+
assert.equal(error.name, 'TemplateStructureError', 'Parser should throw TemplateStructureError')
|
|
945
|
+
assert.ok(
|
|
946
|
+
error.message.startsWith('MultipleTopLevelTags'),
|
|
947
|
+
'Parser should throw TemplateStructureError:MultipleTopLevelTags'
|
|
948
|
+
)
|
|
949
|
+
}
|
|
979
950
|
|
|
980
951
|
assert.end()
|
|
981
952
|
})
|
|
@@ -987,8 +958,16 @@ test('Parse template with unclosed tag and parsing should fail', (assert) => {
|
|
|
987
958
|
</Component>
|
|
988
959
|
`
|
|
989
960
|
|
|
990
|
-
|
|
991
|
-
|
|
961
|
+
try {
|
|
962
|
+
parser(template)
|
|
963
|
+
assert.fail('Parser should throw TemplateStructureError:MismatchedClosingTag')
|
|
964
|
+
} catch (error) {
|
|
965
|
+
assert.equal(error.name, 'TemplateStructureError', 'Parser should throw TemplateStructureError')
|
|
966
|
+
assert.ok(
|
|
967
|
+
error.message.startsWith('MismatchedClosingTag'),
|
|
968
|
+
'Parser should throw TemplateStructureError:MismatchedClosingTag'
|
|
969
|
+
)
|
|
970
|
+
}
|
|
992
971
|
assert.end()
|
|
993
972
|
})
|
|
994
973
|
|
|
@@ -1004,8 +983,16 @@ test('Parse template with multiple unclosed tags and parsing should fail', (asse
|
|
|
1004
983
|
</Component>
|
|
1005
984
|
`
|
|
1006
985
|
|
|
1007
|
-
|
|
1008
|
-
|
|
986
|
+
try {
|
|
987
|
+
parser(template)
|
|
988
|
+
assert.fail('Parser should throw TemplateStructureError:MismatchedClosingTag')
|
|
989
|
+
} catch (error) {
|
|
990
|
+
assert.equal(error.name, 'TemplateStructureError', 'Parser should throw TemplateStructureError')
|
|
991
|
+
assert.ok(
|
|
992
|
+
error.message.startsWith('MismatchedClosingTag'),
|
|
993
|
+
'Parser should throw TemplateStructureError:MismatchedClosingTag'
|
|
994
|
+
)
|
|
995
|
+
}
|
|
1009
996
|
assert.end()
|
|
1010
997
|
})
|
|
1011
998
|
|
|
@@ -1017,8 +1004,16 @@ test('Parse template with an invalid closing tag and parsing should fail', (asse
|
|
|
1017
1004
|
</Component>
|
|
1018
1005
|
`
|
|
1019
1006
|
|
|
1020
|
-
|
|
1021
|
-
|
|
1007
|
+
try {
|
|
1008
|
+
parser(template)
|
|
1009
|
+
assert.fail('Parser should throw TemplateParseError:InvalidClosingTag')
|
|
1010
|
+
} catch (error) {
|
|
1011
|
+
assert.equal(error.name, 'TemplateParseError', 'Parser should throw TemplateParseError')
|
|
1012
|
+
assert.ok(
|
|
1013
|
+
error.message.startsWith('InvalidClosingTag'),
|
|
1014
|
+
'Parser should throw TemplateParseError:InvalidClosingTag'
|
|
1015
|
+
)
|
|
1016
|
+
}
|
|
1022
1017
|
|
|
1023
1018
|
assert.end()
|
|
1024
1019
|
})
|
|
@@ -1029,8 +1024,17 @@ test('Parse template with multiple self-closing tags at the top level and parsin
|
|
|
1029
1024
|
<Element/>
|
|
1030
1025
|
`
|
|
1031
1026
|
|
|
1032
|
-
|
|
1033
|
-
|
|
1027
|
+
try {
|
|
1028
|
+
parser(template)
|
|
1029
|
+
assert.fail('Parser should throw TemplateStructureError:MultipleTopLevelTags')
|
|
1030
|
+
} catch (error) {
|
|
1031
|
+
assert.equal(error.name, 'TemplateStructureError', 'Parser should throw TemplateStructureError')
|
|
1032
|
+
assert.ok(
|
|
1033
|
+
error.message.startsWith('MultipleTopLevelTags'),
|
|
1034
|
+
'Parser should throw TemplateStructureError:MultipleTopLevelTags'
|
|
1035
|
+
)
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1034
1038
|
assert.end()
|
|
1035
1039
|
})
|
|
1036
1040
|
|
|
@@ -1042,7 +1046,38 @@ test('Parse template with a closing tag at the beginning and parsing should fail
|
|
|
1042
1046
|
</Component>
|
|
1043
1047
|
`
|
|
1044
1048
|
|
|
1045
|
-
|
|
1046
|
-
|
|
1049
|
+
try {
|
|
1050
|
+
parser(template)
|
|
1051
|
+
assert.fail('Parser should throw TemplateStructureError:MismatchedClosingTag')
|
|
1052
|
+
} catch (error) {
|
|
1053
|
+
assert.equal(error.name, 'TemplateStructureError', 'Parser should throw TemplateStructureError')
|
|
1054
|
+
assert.ok(
|
|
1055
|
+
error.message.startsWith('MismatchedClosingTag'),
|
|
1056
|
+
'Parser should throw TemplateStructureError:MismatchedClosingTag'
|
|
1057
|
+
)
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
assert.end()
|
|
1061
|
+
})
|
|
1062
|
+
|
|
1063
|
+
test('Parse template with a closing tag that has attributes and parsing should fail', (assert) => {
|
|
1064
|
+
const template = `
|
|
1065
|
+
<Component>
|
|
1066
|
+
<Text>Lorem ipsum dolor sit amet</Text>
|
|
1067
|
+
<Element></Element x="200">
|
|
1068
|
+
</Component>
|
|
1069
|
+
`
|
|
1070
|
+
|
|
1071
|
+
try {
|
|
1072
|
+
parser(template)
|
|
1073
|
+
assert.fail('Parser should throw TemplateParseError:AttributesInClosingTag')
|
|
1074
|
+
} catch (error) {
|
|
1075
|
+
assert.equal(error.name, 'TemplateParseError', 'Parser should throw TemplateParseError')
|
|
1076
|
+
assert.ok(
|
|
1077
|
+
error.message.startsWith('AttributesInClosingTag'),
|
|
1078
|
+
'Parser should throw TemplateParseError:AttributesInClosingTag'
|
|
1079
|
+
)
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1047
1082
|
assert.end()
|
|
1048
1083
|
})
|
package/vite/preCompiler.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import parser from '../src/lib/templateparser/parser.js'
|
|
2
2
|
import generator from '../src/lib/codegenerator/generator.js'
|
|
3
|
+
import path from 'path'
|
|
3
4
|
|
|
4
5
|
export default function () {
|
|
5
6
|
let config
|
|
@@ -8,7 +9,7 @@ export default function () {
|
|
|
8
9
|
configResolved(resolvedConfig) {
|
|
9
10
|
config = resolvedConfig
|
|
10
11
|
},
|
|
11
|
-
transform(source) {
|
|
12
|
+
transform(source, filePath) {
|
|
12
13
|
if (config.blits && config.blits.precompile === false) return source
|
|
13
14
|
if (source.indexOf('Blits.Component(') > -1 || source.indexOf('Blits.Application(') > -1) {
|
|
14
15
|
// get the start of the template key in de component configuration object
|
|
@@ -27,7 +28,12 @@ export default function () {
|
|
|
27
28
|
templateStartIndex + templateContentResult.index + templateContentResult[0].length
|
|
28
29
|
|
|
29
30
|
// Parse the template
|
|
30
|
-
|
|
31
|
+
let resourceName = 'Blits.Application'
|
|
32
|
+
if (source.indexOf('Blits.Component(') > -1) {
|
|
33
|
+
resourceName = source.match(/Blits\.Component\(['"](.*)['"]\s*,/)[1]
|
|
34
|
+
}
|
|
35
|
+
const componentPath = path.relative(process.cwd(), filePath)
|
|
36
|
+
const parsed = parser(templateContentResult[1], resourceName, null, componentPath)
|
|
31
37
|
|
|
32
38
|
// Generate the code
|
|
33
39
|
const code = generator.call({ components: {} }, parsed)
|