@fluffjs/cli 0.3.9 → 0.4.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.
@@ -11,6 +11,36 @@ export type { IfMarkerConfig } from './interfaces/IfMarkerConfig.js';
11
11
  export type { SwitchMarkerConfig } from './interfaces/SwitchMarkerConfig.js';
12
12
  export type { TextMarkerConfig } from './interfaces/TextMarkerConfig.js';
13
13
  export type MarkerConfig = IfMarkerConfig | ForMarkerConfig | SwitchMarkerConfig | TextMarkerConfig | BreakMarkerConfig;
14
+ /**
15
+ * Compact Binding Format (Encoder)
16
+ *
17
+ * Bindings are serialized as tuples to minimize bundle size. All strings are
18
+ * interned into a global string table and referenced by index.
19
+ *
20
+ * Format: [nameIdx, bindType, deps, id, extras?]
21
+ *
22
+ * - nameIdx: Index into global string table for the binding name (e.g., "value", "click")
23
+ * - bindType: Numeric binding type (0=property, 1=event, 2=two-way, 3=class, 4=style, 5=ref)
24
+ * - deps: Array of interned dependency chains, or null. Each dep is either:
25
+ * - A single index (for simple property like "foo")
26
+ * - An array of indices (for nested property chain like ["device", "name"])
27
+ * - id: Expression ID (for property/two-way/class/style) or Handler ID (for event), or null
28
+ * - extras: Optional object with additional binding metadata:
29
+ * - t: Target property name for two-way bindings
30
+ * - s: Subscribe source name
31
+ * - p: Pipes array of [pipeNameIdx, argExprIds[]]
32
+ *
33
+ * The global string table is passed to FluffBase.__setExpressionTable() as the third argument.
34
+ * The runtime decodes these compact bindings back to BindingInfo objects.
35
+ */
36
+ export type CompactDep = number | number[];
37
+ export type CompactBinding = [
38
+ number,
39
+ number,
40
+ CompactDep[] | null,
41
+ number | null,
42
+ Record<string, unknown>?
43
+ ];
14
44
  export declare class CodeGenerator {
15
45
  private readonly componentSelectors;
16
46
  private readonly componentSelector;
@@ -18,6 +48,8 @@ export declare class CodeGenerator {
18
48
  private static globalExprs;
19
49
  private static readonly globalHandlerIdsByExpr;
20
50
  private static globalHandlers;
51
+ private static readonly globalStringTable;
52
+ private static readonly globalStringIndices;
21
53
  private markerId;
22
54
  private readonly markerConfigs;
23
55
  private readonly usedExprIds;
@@ -27,6 +59,8 @@ export declare class CodeGenerator {
27
59
  private readonly collectedTemplates;
28
60
  constructor(componentSelectors?: Set<string>, componentSelector?: string);
29
61
  static resetGlobalState(): void;
62
+ private static internString;
63
+ static getStringTable(): string[];
30
64
  generateRenderMethod(template: ParsedTemplate, styles?: string): string;
31
65
  generateHtml(template: ParsedTemplate): string;
32
66
  generateRenderMethodFromHtml(html: string, styles?: string, markerConfigExpr?: t.Expression): string;
@@ -36,7 +70,7 @@ export declare class CodeGenerator {
36
70
  private buildDepsExpression;
37
71
  private buildPropertyChainExpression;
38
72
  generateBindingsSetup(): string;
39
- getBindingsMap(): Record<string, Record<string, unknown>[]>;
73
+ getBindingsMap(): Record<string, CompactBinding[]>;
40
74
  generateExpressionAssignments(): string;
41
75
  static generateGlobalExprTable(): string;
42
76
  private static buildExpressionArrowFunction;
@@ -49,6 +83,7 @@ export declare class CodeGenerator {
49
83
  private renderTextToParent;
50
84
  private isComponentTag;
51
85
  private serializeBinding;
86
+ private internDep;
52
87
  private internExpression;
53
88
  private internHandler;
54
89
  private renderInterpolationToParent;
package/CodeGenerator.js CHANGED
@@ -5,6 +5,20 @@ import { generate, parseMethodBody } from './BabelHelpers.js';
5
5
  import { ExpressionTransformer } from './ExpressionTransformer.js';
6
6
  import { Parse5Helpers } from './Parse5Helpers.js';
7
7
  const RESTRICTED_ELEMENT_PREFIX = 'x-fluff-el-';
8
+ const BIND_PROPERTY = 0;
9
+ const BIND_EVENT = 1;
10
+ const BIND_TWO_WAY = 2;
11
+ const BIND_CLASS = 3;
12
+ const BIND_STYLE = 4;
13
+ const BIND_REF = 5;
14
+ const BINDING_TYPE_MAP = {
15
+ 'property': BIND_PROPERTY,
16
+ 'event': BIND_EVENT,
17
+ 'two-way': BIND_TWO_WAY,
18
+ 'class': BIND_CLASS,
19
+ 'style': BIND_STYLE,
20
+ 'ref': BIND_REF
21
+ };
8
22
  export class CodeGenerator {
9
23
  componentSelectors;
10
24
  componentSelector;
@@ -12,6 +26,8 @@ export class CodeGenerator {
12
26
  static globalExprs = [];
13
27
  static globalHandlerIdsByExpr = new Map();
14
28
  static globalHandlers = [];
29
+ static globalStringTable = [];
30
+ static globalStringIndices = new Map();
15
31
  markerId = 0;
16
32
  markerConfigs = new Map();
17
33
  usedExprIds = [];
@@ -28,6 +44,21 @@ export class CodeGenerator {
28
44
  CodeGenerator.globalExprs = [];
29
45
  CodeGenerator.globalHandlerIdsByExpr.clear();
30
46
  CodeGenerator.globalHandlers = [];
47
+ CodeGenerator.globalStringTable.length = 0;
48
+ CodeGenerator.globalStringIndices.clear();
49
+ }
50
+ static internString(str) {
51
+ const existing = CodeGenerator.globalStringIndices.get(str);
52
+ if (existing !== undefined) {
53
+ return existing;
54
+ }
55
+ const id = CodeGenerator.globalStringTable.length;
56
+ CodeGenerator.globalStringTable.push(str);
57
+ CodeGenerator.globalStringIndices.set(str, id);
58
+ return id;
59
+ }
60
+ static getStringTable() {
61
+ return CodeGenerator.globalStringTable;
31
62
  }
32
63
  generateRenderMethod(template, styles) {
33
64
  this.markerId = 0;
@@ -179,8 +210,13 @@ export class CodeGenerator {
179
210
  const normalizedHandler = CodeGenerator.normalizeCompiledExpr(h);
180
211
  return CodeGenerator.buildHandlerArrowFunction(['t', 'l', '__ev'], normalizedHandler);
181
212
  });
213
+ const stringElements = CodeGenerator.globalStringTable.map(s => t.stringLiteral(s));
182
214
  const fluffBaseImport = t.importDeclaration([t.importSpecifier(t.identifier('FluffBase'), t.identifier('FluffBase'))], t.stringLiteral('@fluffjs/fluff'));
183
- const setExprTableCall = t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('FluffBase'), t.identifier('__setExpressionTable')), [t.arrayExpression(exprElements), t.arrayExpression(handlerElements)]));
215
+ const setExprTableCall = t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('FluffBase'), t.identifier('__setExpressionTable')), [
216
+ t.arrayExpression(exprElements),
217
+ t.arrayExpression(handlerElements),
218
+ t.arrayExpression(stringElements)
219
+ ]));
184
220
  const program = t.program([fluffBaseImport, setExprTableCall]);
185
221
  return generate(program, { compact: false }).code;
186
222
  }
@@ -284,46 +320,52 @@ export class CodeGenerator {
284
320
  return this.componentSelectors.has(resolvedTagName);
285
321
  }
286
322
  serializeBinding(binding) {
287
- const result = {
288
- n: binding.name,
289
- b: binding.binding
290
- };
323
+ const nameIdx = CodeGenerator.internString(binding.name);
324
+ const bindType = BINDING_TYPE_MAP[binding.binding];
291
325
  if (binding.binding === 'ref') {
292
- return result;
293
- }
294
- if (binding.deps) {
295
- result.d = binding.deps;
296
- }
297
- if (binding.subscribe) {
298
- result.s = binding.subscribe;
326
+ return [nameIdx, bindType, null, null];
299
327
  }
328
+ const deps = binding.deps
329
+ ? binding.deps.map(dep => this.internDep(dep))
330
+ : null;
331
+ let id = null;
300
332
  if (binding.binding === 'event') {
301
333
  if (!binding.expression) {
302
334
  throw new Error(`Event binding for ${binding.name} is missing expression`);
303
335
  }
304
- result.h = this.internHandler(binding.expression);
305
- return result;
336
+ id = this.internHandler(binding.expression);
337
+ return [nameIdx, bindType, deps, id];
338
+ }
339
+ if (!binding.expression) {
340
+ throw new Error(`Binding for ${binding.name} is missing expression`);
306
341
  }
342
+ id = this.internExpression(binding.expression);
343
+ const extras = {};
307
344
  if (binding.binding === 'two-way') {
308
- if (!binding.expression) {
309
- throw new Error(`Two-way binding for ${binding.name} is missing expression`);
310
- }
311
345
  if (!binding.expression.startsWith('this.')) {
312
346
  throw new Error(`Two-way binding for ${binding.name} must target a component property`);
313
347
  }
314
- result.t = binding.expression.slice('this.'.length);
348
+ extras.t = binding.expression.slice('this.'.length);
315
349
  }
316
- if (!binding.expression) {
317
- throw new Error(`Binding for ${binding.name} is missing expression`);
350
+ if (binding.subscribe) {
351
+ extras.s = binding.subscribe;
318
352
  }
319
- result.e = this.internExpression(binding.expression);
320
353
  if (binding.pipes && binding.pipes.length > 0) {
321
- result.p = binding.pipes.map(pipe => ({
322
- n: pipe.name,
323
- a: pipe.args.map(arg => this.internExpression(arg))
324
- }));
354
+ extras.p = binding.pipes.map(pipe => ([
355
+ CodeGenerator.internString(pipe.name),
356
+ pipe.args.map(arg => this.internExpression(arg))
357
+ ]));
325
358
  }
326
- return result;
359
+ if (Object.keys(extras).length > 0) {
360
+ return [nameIdx, bindType, deps, id, extras];
361
+ }
362
+ return [nameIdx, bindType, deps, id];
363
+ }
364
+ internDep(dep) {
365
+ if (Array.isArray(dep)) {
366
+ return dep.map(s => CodeGenerator.internString(s));
367
+ }
368
+ return CodeGenerator.internString(dep);
327
369
  }
328
370
  internExpression(expr) {
329
371
  const existing = CodeGenerator.globalExprIdsByExpr.get(expr);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluffjs/cli",
3
- "version": "0.3.9",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "main": "./index.js",
6
6
  "module": "./index.js",