leanweb 3.0.7 → 3.0.9
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/commands/init.js +1 -1
- package/leanweb.js +1 -1
- package/lib/lw-html-parser.js +2 -2
- package/package.json +7 -7
- package/templates/lib/lw-element.js +38 -13
- package/templates/lib/lw-expr-parser.js +45 -15
package/commands/init.js
CHANGED
package/leanweb.js
CHANGED
|
@@ -42,7 +42,7 @@ import * as utils from './commands/utils.js';
|
|
|
42
42
|
return;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
if (leanwebJSONExisted && target === 'version' || target === 'serve' || target === 'dist') {
|
|
45
|
+
if (leanwebJSONExisted && (target === 'version' || target === 'serve' || target === 'dist')) {
|
|
46
46
|
const leanwebPackageJSON = require(`${__dirname}/package.json`);
|
|
47
47
|
const projectLeanwebJSON = require(`${process.cwd()}/${utils.dirs.src}/leanweb.json`);
|
|
48
48
|
const upgradeAvailable = semver.gt(leanwebPackageJSON.version, projectLeanwebJSON.version);
|
package/lib/lw-html-parser.js
CHANGED
|
@@ -6,7 +6,7 @@ let astKey = 0;
|
|
|
6
6
|
const removeASTLocation = ast => {
|
|
7
7
|
if (Array.isArray(ast)) {
|
|
8
8
|
ast.forEach(a => removeASTLocation(a));
|
|
9
|
-
} else if (typeof ast === 'object') {
|
|
9
|
+
} else if (ast !== null && typeof ast === 'object') {
|
|
10
10
|
delete ast['loc'];
|
|
11
11
|
delete ast['start'];
|
|
12
12
|
delete ast['end'];
|
|
@@ -77,7 +77,7 @@ const walkNode = (node, interpolation) => {
|
|
|
77
77
|
|
|
78
78
|
if (attr.name === 'lw-bind:class') {
|
|
79
79
|
const classAttr = node.attrs.find(a => a.name === 'class');
|
|
80
|
-
node.attrs.push({ name: 'lw-init-class', value: classAttr
|
|
80
|
+
node.attrs.push({ name: 'lw-init-class', value: classAttr?.value ?? '' });
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
const ast = getAST(attr.value);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "leanweb",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.9",
|
|
4
4
|
"description": "Builds framework agnostic web components.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"leanweb": "leanweb.js",
|
|
@@ -20,15 +20,15 @@
|
|
|
20
20
|
"author": "Qian Chen",
|
|
21
21
|
"license": "MIT",
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@babel/parser": "^7.
|
|
24
|
-
"esbuild": "^0.
|
|
25
|
-
"fs-extra": "^11.3.
|
|
26
|
-
"globby": "^
|
|
23
|
+
"@babel/parser": "^7.29.0",
|
|
24
|
+
"esbuild": "^0.27.3",
|
|
25
|
+
"fs-extra": "^11.3.4",
|
|
26
|
+
"globby": "^16.1.1",
|
|
27
27
|
"html-minifier": "^4.0.0",
|
|
28
|
-
"isomorphic-git": "^1.
|
|
28
|
+
"isomorphic-git": "^1.37.2",
|
|
29
29
|
"live-server": "^1.2.2",
|
|
30
30
|
"node-watch": "^0.7.4",
|
|
31
31
|
"parse5": "^8.0.0",
|
|
32
|
-
"semver": "^7.7.
|
|
32
|
+
"semver": "^7.7.4"
|
|
33
33
|
}
|
|
34
34
|
}
|
|
@@ -111,10 +111,14 @@ export default class LWElement extends HTMLElement {
|
|
|
111
111
|
|
|
112
112
|
this._bindMethods();
|
|
113
113
|
setTimeout(() => {
|
|
114
|
-
this.
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
114
|
+
const result = this.domReady?.call(this);
|
|
115
|
+
if (result && typeof result.then === 'function') {
|
|
116
|
+
// domReady is async
|
|
117
|
+
result.then(() => this.update());
|
|
118
|
+
} else {
|
|
119
|
+
// domReady is sync
|
|
120
|
+
this.update();
|
|
121
|
+
}
|
|
118
122
|
});
|
|
119
123
|
|
|
120
124
|
if (this.urlHashChanged && typeof this.urlHashChanged === 'function') {
|
|
@@ -286,7 +290,9 @@ export default class LWElement extends HTMLElement {
|
|
|
286
290
|
} else if (modelNode.type === 'checkbox') {
|
|
287
291
|
if (Array.isArray(object[propertyExpr])) {
|
|
288
292
|
if (modelNode.checked) {
|
|
289
|
-
object[propertyExpr].
|
|
293
|
+
if (!object[propertyExpr].includes(modelNode.value)) {
|
|
294
|
+
object[propertyExpr].push(modelNode.value);
|
|
295
|
+
}
|
|
290
296
|
} else {
|
|
291
297
|
const index = object[propertyExpr].indexOf(modelNode.value);
|
|
292
298
|
if (index > -1) {
|
|
@@ -316,7 +322,7 @@ export default class LWElement extends HTMLElement {
|
|
|
316
322
|
}
|
|
317
323
|
|
|
318
324
|
updateModel(modelNode) {
|
|
319
|
-
if (modelNode.do_not_update && modelNode.type === 'number') {
|
|
325
|
+
if (modelNode.do_not_update && (modelNode.type === 'number' || modelNode.type === 'range')) {
|
|
320
326
|
return;
|
|
321
327
|
}
|
|
322
328
|
const key = modelNode.getAttribute('lw-model');
|
|
@@ -335,9 +341,14 @@ export default class LWElement extends HTMLElement {
|
|
|
335
341
|
} else if (modelNode.type === 'radio') {
|
|
336
342
|
modelNode.checked = parsed[0] === modelNode.value;
|
|
337
343
|
} else if (modelNode.type === 'select-multiple') {
|
|
344
|
+
// First, clear all selections
|
|
338
345
|
for (let i = 0; i < modelNode.options.length; ++i) {
|
|
339
|
-
|
|
340
|
-
|
|
346
|
+
modelNode.options[i].selected = false;
|
|
347
|
+
}
|
|
348
|
+
// Then, set selected options
|
|
349
|
+
if (parsed[0]) {
|
|
350
|
+
for (let i = 0; i < modelNode.options.length; ++i) {
|
|
351
|
+
const option = modelNode.options[i];
|
|
341
352
|
option.selected = parsed[0].includes(option.value);
|
|
342
353
|
}
|
|
343
354
|
}
|
|
@@ -377,7 +388,7 @@ export default class LWElement extends HTMLElement {
|
|
|
377
388
|
const parsed = parser.evaluate(interpolation.ast, context, interpolation.loc);
|
|
378
389
|
|
|
379
390
|
const hasLwFalse = ifNode.hasAttribute('lw-false');
|
|
380
|
-
if (parsed[0]
|
|
391
|
+
if (parsed[0]) {
|
|
381
392
|
hasLwFalse && ifNode.removeAttribute('lw-false');
|
|
382
393
|
setTimeout(() => {
|
|
383
394
|
ifNode.turnedOn?.call(ifNode);
|
|
@@ -419,11 +430,25 @@ export default class LWElement extends HTMLElement {
|
|
|
419
430
|
const parsed = parser.evaluate(interpolation.ast, context, interpolation.loc);
|
|
420
431
|
|
|
421
432
|
if (interpolation.lwValue === 'class') {
|
|
422
|
-
const
|
|
423
|
-
|
|
424
|
-
|
|
433
|
+
const dynamicClass = parsed[0];
|
|
434
|
+
const initClass = bindNode.getAttribute('lw-init-class') || '';
|
|
435
|
+
// Ensure initial classes are present
|
|
436
|
+
if (initClass) {
|
|
437
|
+
initClass.split(' ').forEach(cls => {
|
|
438
|
+
if (cls) bindNode.classList.add(cls);
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
// Remove previously applied dynamic class
|
|
442
|
+
const prevClass = bindNode['lw-prev-class-' + attrValue];
|
|
443
|
+
if (prevClass && prevClass !== dynamicClass) {
|
|
444
|
+
bindNode.classList.remove(prevClass);
|
|
445
|
+
}
|
|
446
|
+
// Add or record the dynamic class
|
|
447
|
+
if (dynamicClass) {
|
|
448
|
+
bindNode.classList.add(dynamicClass);
|
|
449
|
+
bindNode['lw-prev-class-' + attrValue] = dynamicClass;
|
|
425
450
|
} else {
|
|
426
|
-
bindNode
|
|
451
|
+
bindNode['lw-prev-class-' + attrValue] = null;
|
|
427
452
|
}
|
|
428
453
|
} else {
|
|
429
454
|
if (parsed[0] !== false && parsed[0] !== undefined && parsed[0] !== null) {
|
|
@@ -43,11 +43,6 @@ const assignmentOperations = {
|
|
|
43
43
|
'^=': (c, a, b) => { c[a] ^= b; },
|
|
44
44
|
};
|
|
45
45
|
|
|
46
|
-
const logicalOperators = {
|
|
47
|
-
'||': (a, b) => a || b,
|
|
48
|
-
'&&': (a, b) => a && b,
|
|
49
|
-
'??': (a, b) => a ?? b,
|
|
50
|
-
};
|
|
51
46
|
|
|
52
47
|
const unaryOperators = {
|
|
53
48
|
'-': a => -a,
|
|
@@ -96,20 +91,44 @@ const nodeHandlers = {
|
|
|
96
91
|
'ExpressionStatement': (node, context) => evalNode(node.expression, context),
|
|
97
92
|
'BinaryExpression': (node, context) => binaryOperations[node.operator](evalNode(node.left, context), evalNode(node.right, context)),
|
|
98
93
|
'AssignmentExpression': (node, context) => {
|
|
99
|
-
|
|
100
|
-
|
|
94
|
+
// Support complex left-hand sides (e.g., obj.prop = 1, obj[expr] = 1)
|
|
95
|
+
let obj, prop;
|
|
96
|
+
if (node.left.type === 'MemberExpression' || node.left.type === 'OptionalMemberExpression') {
|
|
97
|
+
obj = evalNode(node.left.object, context);
|
|
98
|
+
prop = node.left.computed ? evalNode(node.left.property, context) : node.left.property.name;
|
|
99
|
+
} else if (node.left.type === 'Identifier') {
|
|
100
|
+
// Simple variable assignment
|
|
101
|
+
obj = immediateContext(node.left, context);
|
|
102
|
+
prop = node.left.name;
|
|
103
|
+
} else {
|
|
104
|
+
throw new Error('Unsupported assignment left-hand side');
|
|
105
|
+
}
|
|
106
|
+
assignmentOperations[node.operator](obj, prop, evalNode(node.right, context));
|
|
107
|
+
},
|
|
108
|
+
'LogicalExpression': (node, context) => {
|
|
109
|
+
const left = evalNode(node.left, context);
|
|
110
|
+
if (node.operator === '&&') return left ? evalNode(node.right, context) : left;
|
|
111
|
+
if (node.operator === '||') return left ? left : evalNode(node.right, context);
|
|
112
|
+
if (node.operator === '??') return left ?? evalNode(node.right, context);
|
|
101
113
|
},
|
|
102
|
-
'LogicalExpression': (node, context) => logicalOperators[node.operator](evalNode(node.left, context), evalNode(node.right, context)),
|
|
103
114
|
'UnaryExpression': (node, context) => unaryOperators[node.operator](evalNode(node.argument, context)),
|
|
104
115
|
'UpdateExpression': (node, context) => {
|
|
105
|
-
|
|
106
|
-
|
|
116
|
+
// Support complex left-hand sides (e.g., ++obj.prop, ++obj[expr])
|
|
117
|
+
let obj, prop;
|
|
118
|
+
if (node.argument.type === 'MemberExpression' || node.argument.type === 'OptionalMemberExpression') {
|
|
119
|
+
obj = evalNode(node.argument.object, context);
|
|
120
|
+
prop = node.argument.computed ? evalNode(node.argument.property, context) : node.argument.property.name;
|
|
121
|
+
} else if (node.argument.type === 'Identifier') {
|
|
122
|
+
obj = immediateContext(node.argument, context);
|
|
123
|
+
prop = node.argument.name;
|
|
124
|
+
} else {
|
|
125
|
+
throw new Error('Unsupported update left-hand side');
|
|
126
|
+
}
|
|
127
|
+
return updateOperators(node.operator, node.prefix)(obj, prop);
|
|
107
128
|
},
|
|
108
129
|
'ConditionalExpression': (node, context) => {
|
|
109
130
|
const test = evalNode(node.test, context);
|
|
110
|
-
|
|
111
|
-
const alternate = evalNode(node.alternate, context);
|
|
112
|
-
return test ? consequent : alternate;
|
|
131
|
+
return test ? evalNode(node.consequent, context) : evalNode(node.alternate, context);
|
|
113
132
|
},
|
|
114
133
|
'MemberExpression': (node, context) => {
|
|
115
134
|
const object = evalNode(node.object, context);
|
|
@@ -143,7 +162,7 @@ const nodeHandlers = {
|
|
|
143
162
|
return arr;
|
|
144
163
|
},
|
|
145
164
|
'ObjectExpression': (node, context) => node.properties.reduce((acc, prop) => ({ ...acc, ...evalNode(prop, context) }), {}),
|
|
146
|
-
'ObjectProperty': (node, context) => ({ [evalNode(node.key, context)]: evalNode(node.value, context) }),
|
|
165
|
+
'ObjectProperty': (node, context) => ({ [node.computed ? evalNode(node.key, context) : (node.key.name ?? node.key.value)]: evalNode(node.value, context) }),
|
|
147
166
|
'SpreadElement': (node, context) => evalNode(node.argument, context),
|
|
148
167
|
|
|
149
168
|
'Identifier': (node, context) => {
|
|
@@ -160,7 +179,18 @@ const nodeHandlers = {
|
|
|
160
179
|
|
|
161
180
|
'CallExpression': (node, context) => callFunction(node, context),
|
|
162
181
|
'OptionalCallExpression': (node, context) => callFunction(node, context),
|
|
163
|
-
'NewExpression': (node, context) =>
|
|
182
|
+
'NewExpression': (node, context) => {
|
|
183
|
+
const callee = evalNode(node.callee, context);
|
|
184
|
+
const args = [];
|
|
185
|
+
node.arguments.map(argument => {
|
|
186
|
+
if (argument.type === 'SpreadElement') {
|
|
187
|
+
args.push(...evalNode(argument, context));
|
|
188
|
+
} else {
|
|
189
|
+
args.push(evalNode(argument, context));
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
return new callee(...args);
|
|
193
|
+
},
|
|
164
194
|
|
|
165
195
|
'Directive': (node, context) => evalNode(node.value, context),
|
|
166
196
|
'DirectiveLiteral': (node, context) => node.value,
|