leanweb 3.0.8 → 3.10.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/commands/build.js CHANGED
@@ -44,6 +44,13 @@ const buildModule = (projectPath) => {
44
44
  utils.writeIfChanged(`${utils.dirs.build}/${project.name}.js`, jsString);
45
45
  };
46
46
 
47
+ const buildGlobalStyles = () => {
48
+ const globalCssPath = `${utils.dirs.build}/global-styles.css`;
49
+ const globalCss = fs.existsSync(globalCssPath) ? fs.readFileSync(globalCssPath, 'utf8') : '';
50
+ const jsModule = `const sheet = new CSSStyleSheet();\nsheet.replaceSync(${JSON.stringify(globalCss)});\nexport default sheet;\n`;
51
+ utils.writeIfChanged(`${utils.dirs.build}/global-styles.js`, jsModule);
52
+ };
53
+
47
54
  const buildHTML = () => {
48
55
  try {
49
56
  project.components.forEach(cmp => {
@@ -53,7 +60,7 @@ const buildModule = (projectPath) => {
53
60
  if (htmlFileExists) {
54
61
  const cssFilename = `${utils.dirs.build}/components/${cmp}/${cmpName}.css`;
55
62
  const cssFileExists = fs.existsSync(cssFilename);
56
- let cssString = `@import "global-styles.css";\n`;
63
+ let cssString = '';
57
64
  if (cssFileExists) {
58
65
  cssString += fs.readFileSync(cssFilename, 'utf8');
59
66
  }
@@ -82,6 +89,7 @@ const buildModule = (projectPath) => {
82
89
  copySrc();
83
90
  copyEnv();
84
91
  buildJS();
92
+ buildGlobalStyles();
85
93
  buildHTML();
86
94
 
87
95
  return project.name;
package/commands/dist.js CHANGED
@@ -60,7 +60,6 @@ const appCSS = fs.readFileSync(`./${utils.dirs.build}/${project.name}.css`, 'utf
60
60
  fs.writeFileSync(`./${utils.dirs.dist}/${project.name}.css`, appCSS);
61
61
 
62
62
  fse.copySync(`./${utils.dirs.build}/favicon.svg`, `./${utils.dirs.dist}/favicon.svg`);
63
- fse.copySync(`./${utils.dirs.build}/global-styles.css`, `./${utils.dirs.dist}/global-styles.css`);
64
63
  project.resources?.forEach(resource => {
65
64
  const source = `./${utils.dirs.build}/${resource}`;
66
65
  if (fs.existsSync(source)) {
package/commands/init.js CHANGED
@@ -23,7 +23,7 @@ import * as utils from './utils.js';
23
23
  return;
24
24
  }
25
25
 
26
- const projectName = path.basename(path.resolve());
26
+ let projectName = path.basename(path.resolve());
27
27
 
28
28
  if (args.length >= 3) {
29
29
  projectName = args[2];
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);
@@ -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.value });
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.8",
3
+ "version": "3.10.0",
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.28.4",
24
- "esbuild": "^0.25.9",
25
- "fs-extra": "^11.3.1",
26
- "globby": "^14.1.0",
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.33.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.2"
32
+ "semver": "^7.7.4"
33
33
  }
34
34
  }
@@ -1,5 +1,6 @@
1
1
  import * as parser from './lw-expr-parser.js';
2
2
  import LWEventBus from './lw-event-bus.js';
3
+ import globalStyleSheet from '../global-styles.js';
3
4
 
4
5
  globalThis.leanweb = globalThis.leanweb ?? {
5
6
  componentsListeningOnUrlChanges: [],
@@ -106,8 +107,11 @@ export default class LWElement extends HTMLElement {
106
107
  leanweb.builderVersion = ast.builderVersion;
107
108
 
108
109
  const node = document.createElement('template');
109
- node.innerHTML = `<style>${ast.css}</style>${ast.html}`;
110
+ const componentSheet = new CSSStyleSheet();
111
+ componentSheet.replaceSync(ast.css);
112
+ node.innerHTML = ast.html;
110
113
  this.attachShadow({ mode: 'open' }).appendChild(node.content);
114
+ this.shadowRoot.adoptedStyleSheets = [globalStyleSheet, componentSheet];
111
115
 
112
116
  this._bindMethods();
113
117
  setTimeout(() => {
@@ -322,7 +326,7 @@ export default class LWElement extends HTMLElement {
322
326
  }
323
327
 
324
328
  updateModel(modelNode) {
325
- if (modelNode.do_not_update && modelNode.type === 'number') {
329
+ if (modelNode.do_not_update && (modelNode.type === 'number' || modelNode.type === 'range')) {
326
330
  return;
327
331
  }
328
332
  const key = modelNode.getAttribute('lw-model');
@@ -388,7 +392,7 @@ export default class LWElement extends HTMLElement {
388
392
  const parsed = parser.evaluate(interpolation.ast, context, interpolation.loc);
389
393
 
390
394
  const hasLwFalse = ifNode.hasAttribute('lw-false');
391
- if (parsed[0] !== false && parsed[0] !== undefined && parsed[0] !== null) {
395
+ if (parsed[0]) {
392
396
  hasLwFalse && ifNode.removeAttribute('lw-false');
393
397
  setTimeout(() => {
394
398
  ifNode.turnedOn?.call(ifNode);
@@ -438,12 +442,17 @@ export default class LWElement extends HTMLElement {
438
442
  if (cls) bindNode.classList.add(cls);
439
443
  });
440
444
  }
441
- // Add or remove the dynamic class
445
+ // Remove previously applied dynamic class
446
+ const prevClass = bindNode['lw-prev-class-' + attrValue];
447
+ if (prevClass && prevClass !== dynamicClass) {
448
+ bindNode.classList.remove(prevClass);
449
+ }
450
+ // Add or record the dynamic class
442
451
  if (dynamicClass) {
443
452
  bindNode.classList.add(dynamicClass);
444
- } else if (dynamicClass !== undefined) {
445
- // Only remove if not undefined (so we don't remove all classes accidentally)
446
- bindNode.classList.remove(dynamicClass);
453
+ bindNode['lw-prev-class-' + attrValue] = dynamicClass;
454
+ } else {
455
+ bindNode['lw-prev-class-' + attrValue] = null;
447
456
  }
448
457
  } else {
449
458
  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,
@@ -110,7 +105,12 @@ const nodeHandlers = {
110
105
  }
111
106
  assignmentOperations[node.operator](obj, prop, evalNode(node.right, context));
112
107
  },
113
- 'LogicalExpression': (node, context) => logicalOperators[node.operator](evalNode(node.left, context), evalNode(node.right, context)),
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);
113
+ },
114
114
  'UnaryExpression': (node, context) => unaryOperators[node.operator](evalNode(node.argument, context)),
115
115
  'UpdateExpression': (node, context) => {
116
116
  // Support complex left-hand sides (e.g., ++obj.prop, ++obj[expr])
@@ -128,9 +128,7 @@ const nodeHandlers = {
128
128
  },
129
129
  'ConditionalExpression': (node, context) => {
130
130
  const test = evalNode(node.test, context);
131
- const consequent = evalNode(node.consequent, context);
132
- const alternate = evalNode(node.alternate, context);
133
- return test ? consequent : alternate;
131
+ return test ? evalNode(node.consequent, context) : evalNode(node.alternate, context);
134
132
  },
135
133
  'MemberExpression': (node, context) => {
136
134
  const object = evalNode(node.object, context);
@@ -164,7 +162,7 @@ const nodeHandlers = {
164
162
  return arr;
165
163
  },
166
164
  'ObjectExpression': (node, context) => node.properties.reduce((acc, prop) => ({ ...acc, ...evalNode(prop, context) }), {}),
167
- '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) }),
168
166
  'SpreadElement': (node, context) => evalNode(node.argument, context),
169
167
 
170
168
  'Identifier': (node, context) => {
@@ -181,7 +179,18 @@ const nodeHandlers = {
181
179
 
182
180
  'CallExpression': (node, context) => callFunction(node, context),
183
181
  'OptionalCallExpression': (node, context) => callFunction(node, context),
184
- 'NewExpression': (node, context) => callFunction(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
+ },
185
194
 
186
195
  'Directive': (node, context) => evalNode(node.value, context),
187
196
  'DirectiveLiteral': (node, context) => node.value,