@rettangoli/fe 0.0.7-rc8 → 0.0.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rettangoli/fe",
3
- "version": "0.0.7-rc8",
3
+ "version": "0.0.8",
4
4
  "description": "Frontend framework for building reactive web components",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -29,7 +29,7 @@
29
29
  "dependencies": {
30
30
  "esbuild": "^0.25.5",
31
31
  "immer": "^10.1.1",
32
- "jempl": "0.1.0-rc1",
32
+ "jempl": "0.3.2-rc2",
33
33
  "js-yaml": "^4.1.0",
34
34
  "rxjs": "^7.8.2",
35
35
  "snabbdom": "^3.6.2",
@@ -1,8 +1,8 @@
1
1
 
2
- export const handleOnMount = (deps) => {
2
+ export const handleOnMount = (deps, event) => {
3
3
  //
4
4
  }
5
5
 
6
- export const handlerSomeEvent = (e, deps) => {
6
+ export const handlerSomeEvent = (deps, event) => {
7
7
  //
8
8
  }
@@ -1,8 +1,8 @@
1
- export const INITIAL_STATE = Object.freeze({
2
- //
1
+ export const createInitialState = () => ({
2
+ //
3
3
  });
4
4
 
5
- export const toViewData = ({ state, props }) => {
5
+ export const selectViewData = ({ state, props }) => {
6
6
  return state;
7
7
  }
8
8
 
package/src/cli/build.js CHANGED
@@ -18,7 +18,7 @@ function capitalize(word) {
18
18
  // Function to process view files - loads YAML and creates temporary JS file
19
19
  export const writeViewFile = (view, category, component) => {
20
20
  // const { category, component } = extractCategoryAndComponent(filePath);
21
-
21
+
22
22
  const dir = `./.temp/${category}`;
23
23
  if (!existsSync(dir)) {
24
24
  mkdirSync(dir, { recursive: true });
@@ -30,12 +30,12 @@ export const writeViewFile = (view, category, component) => {
30
30
  };
31
31
 
32
32
  export const bundleFile = async (options) => {
33
- const { outfile = "./vt/static/main.js" } = options;
33
+ const { outfile = "./vt/static/main.js", development = false } = options;
34
34
  await esbuild.build({
35
35
  entryPoints: ["./.temp/dynamicImport.js"],
36
36
  bundle: true,
37
- minify: false,
38
- sourcemap: true,
37
+ minify: !development,
38
+ sourcemap: !!development,
39
39
  outfile: outfile,
40
40
  format: "esm",
41
41
  loader: {
@@ -47,7 +47,7 @@ export const bundleFile = async (options) => {
47
47
  const buildRettangoliFrontend = async (options) => {
48
48
  console.log("running build with options", options);
49
49
 
50
- const { dirs = ["./example"], outfile = "./vt/static/main.js", setup = "setup.js" } = options;
50
+ const { dirs = ["./example"], outfile = "./vt/static/main.js", setup = "setup.js", development = false } = options;
51
51
 
52
52
  const allFiles = getAllFiles(dirs).filter((filePath) => {
53
53
  return (
@@ -93,7 +93,12 @@ const buildRettangoliFrontend = async (options) => {
93
93
  count++;
94
94
  } else if (["view"].includes(fileType)) {
95
95
  const view = loadYaml(readFileSync(filePath, "utf8"));
96
- view.template = parse(view.template);
96
+ try {
97
+ view.template = parse(view.template);
98
+ } catch (error) {
99
+ console.error(`Error parsing template in file: ${filePath}`);
100
+ throw error;
101
+ }
97
102
  writeViewFile(view, category, component);
98
103
  output += `import ${component}${capitalize(
99
104
  fileType,
@@ -125,7 +130,7 @@ Object.keys(imports).forEach(category => {
125
130
 
126
131
  writeFileSync("./.temp/dynamicImport.js", output);
127
132
 
128
- await bundleFile({ outfile });
133
+ await bundleFile({ outfile, development });
129
134
 
130
135
  console.log(`Build complete. Output file: ${outfile}`);
131
136
  };
package/src/cli/watch.js CHANGED
@@ -64,13 +64,19 @@ async function startViteServer(options) {
64
64
  const startWatching = async (options) => {
65
65
  const { dirs = ['src'], port = 3001 } = options;
66
66
 
67
+ // Set development mode for all builds in watch mode
68
+ const watchOptions = {
69
+ development: true,
70
+ ...options
71
+ };
72
+
67
73
  // Do initial build with all directories
68
74
  console.log('Starting initial build...');
69
- await buildRettangoliFrontend(options);
75
+ await buildRettangoliFrontend(watchOptions);
70
76
  console.log('Initial build complete');
71
77
 
72
78
  dirs.forEach(dir => {
73
- setupWatcher(dir, options);
79
+ setupWatcher(dir, watchOptions);
74
80
  });
75
81
 
76
82
  startViteServer({ port, outfile: options.outfile });
@@ -1,5 +1,6 @@
1
1
  import { produce } from "immer";
2
2
  import { parseView } from "./parser.js";
3
+ import { parseAndRender } from "jempl";
3
4
 
4
5
  /**
5
6
  * covert this format of json into raw css strings
@@ -261,8 +262,10 @@ class BaseComponent extends HTMLElement {
261
262
  }
262
263
 
263
264
  get viewData() {
264
- // TODO decide whether to pass globalStore state
265
- const data = this.store.toViewData();
265
+ let data = {};
266
+ if (this.store.selectViewData) {
267
+ data = this.store.selectViewData();
268
+ }
266
269
  return data;
267
270
  }
268
271
 
@@ -295,7 +298,6 @@ class BaseComponent extends HTMLElement {
295
298
  this.renderTarget = document.createElement("div");
296
299
  this.renderTarget.style.cssText = "display: contents;";
297
300
  this.shadow.appendChild(this.renderTarget);
298
- this.transformedHandlers = {};
299
301
  if (!this.renderTarget.parentNode) {
300
302
  this.appendChild(this.renderTarget);
301
303
  }
@@ -307,10 +309,24 @@ class BaseComponent extends HTMLElement {
307
309
  dispatchEvent: this.dispatchEvent.bind(this),
308
310
  };
309
311
 
312
+
313
+ this.transformedHandlers = {
314
+ handleCallStoreAction: (event, payload) => {
315
+ const { render, store } = deps;
316
+ const context = parseAndRender(payload, {
317
+ event: {
318
+ target: event.target,
319
+ detail: event.detail
320
+ }
321
+ })
322
+ store[payload.action](context);
323
+ render();
324
+ }
325
+ };
310
326
  // TODO don't include onmount, subscriptions, etc in transformedHandlers
311
327
  Object.keys(this.handlers || {}).forEach((key) => {
312
- this.transformedHandlers[key] = (payload) => {
313
- const result = this.handlers[key](payload, deps);
328
+ this.transformedHandlers[key] = (event, payload) => {
329
+ const result = this.handlers[key](deps, event, payload);
314
330
  return result;
315
331
  };
316
332
  });
@@ -348,7 +364,10 @@ class BaseComponent extends HTMLElement {
348
364
  if (oldValue !== newValue && this.render) {
349
365
  // Call handleOnUpdate if it exists
350
366
  if (this.handlers?.handleOnUpdate) {
351
- const changes = { [name]: { oldValue, newValue } };
367
+ const changes = {
368
+ oldAttrs: { [name]: oldValue },
369
+ newAttrs: { [name]: newValue }
370
+ };
352
371
  const deps = {
353
372
  ...this.deps,
354
373
  refIds: this.refIds,
@@ -421,17 +440,19 @@ class BaseComponent extends HTMLElement {
421
440
  /**
422
441
  * Binds store functions with actual framework data flow
423
442
  * Makes state changes immutable with immer
424
- * Passes props to selectors and toViewData
443
+ * Passes props to selectors
425
444
  * @param {*} store
426
445
  * @param {*} props
427
446
  * @returns
428
447
  */
429
448
  const bindStore = (store, props, attrs) => {
430
- const { INITIAL_STATE, toViewData, ...selectorsAndActions } = store;
449
+ const { createInitialState, ...selectorsAndActions } = store;
431
450
  const selectors = {};
432
451
  const actions = {};
433
- let currentState = structuredClone(INITIAL_STATE);
434
-
452
+ let currentState = {};
453
+ if (createInitialState) {
454
+ currentState = createInitialState();
455
+ }
435
456
  Object.entries(selectorsAndActions).forEach(([key, fn]) => {
436
457
  if (key.startsWith("select")) {
437
458
  selectors[key] = (...args) => {
@@ -448,7 +469,6 @@ const bindStore = (store, props, attrs) => {
448
469
  });
449
470
 
450
471
  return {
451
- toViewData: () => toViewData({ state: currentState, props, attrs }),
452
472
  getState: () => currentState,
453
473
  ...actions,
454
474
  ...selectors,
package/src/parser.js CHANGED
@@ -164,7 +164,7 @@ export const createVirtualDom = ({
164
164
  const attrRegex = /(\S+?)=(?:\"([^\"]*)\"|\'([^\']*)\'|([^\s]+))/g;
165
165
  let match;
166
166
  const processedAttrs = new Set();
167
-
167
+
168
168
  while ((match = attrRegex.exec(attrsString)) !== null) {
169
169
  processedAttrs.add(match[1]);
170
170
  if (match[1].startsWith(".")) {
@@ -175,7 +175,7 @@ export const createVirtualDom = ({
175
175
  // Handle conditional boolean attributes
176
176
  const attrName = match[1].substring(1);
177
177
  const attrValue = match[2] || match[3] || match[4];
178
-
178
+
179
179
  // Convert string values to boolean
180
180
  let evalValue;
181
181
  if (attrValue === "true") {
@@ -186,7 +186,7 @@ export const createVirtualDom = ({
186
186
  // Try to get from viewData if it's not a literal boolean
187
187
  evalValue = lodashGet(viewData, attrValue);
188
188
  }
189
-
189
+
190
190
  // Only add attribute if value is truthy
191
191
  if (evalValue) {
192
192
  attrs[attrName] = "";
@@ -195,7 +195,7 @@ export const createVirtualDom = ({
195
195
  attrs[match[1]] = match[2] || match[3] || match[4];
196
196
  }
197
197
  }
198
-
198
+
199
199
  // Then, handle boolean attributes without values
200
200
  // Remove all processed attribute-value pairs from the string first
201
201
  let remainingAttrsString = attrsString;
@@ -209,7 +209,7 @@ export const createVirtualDom = ({
209
209
  processedMatches.forEach(match => {
210
210
  remainingAttrsString = remainingAttrsString.replace(match, ' ');
211
211
  });
212
-
212
+
213
213
  const booleanAttrRegex = /\b(\S+?)(?=\s|$)/g;
214
214
  let boolMatch;
215
215
  while ((boolMatch = booleanAttrRegex.exec(remainingAttrsString)) !== null) {
@@ -327,9 +327,18 @@ export const createVirtualDom = ({
327
327
  const eventListeners = refs[bestMatchRefKey].eventListeners;
328
328
  Object.entries(eventListeners).forEach(
329
329
  ([eventType, eventConfig]) => {
330
+ if (eventConfig.handler && eventConfig.action) {
331
+ throw new Error('Each listener can have hanlder or action but not both')
332
+ }
333
+
334
+ if (eventConfig.action) {
335
+ handlers.handleCallStoreAction(event, eventConfig.payload)
336
+ return;
337
+ }
338
+
330
339
  if (eventConfig.handler && handlers[eventConfig.handler]) {
331
340
  eventHandlers[eventType] = (event) => {
332
- handlers[eventConfig.handler](event);
341
+ handlers[eventConfig.handler](event, eventConfig.payload);
333
342
  };
334
343
  } else if (eventConfig.handler) {
335
344
  // Keep this warning for missing handlers