octane 0.2.8 → 0.2.10

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.
@@ -120,6 +120,7 @@ __export(index_exports, {
120
120
  injectStyle: () => import_runtime.injectStyle,
121
121
  invokeManualHook: () => import_runtime.invokeManualHook,
122
122
  isChildrenBlock: () => import_runtime.isChildrenBlock,
123
+ isHydratingStyle: () => import_runtime.isHydratingStyle,
123
124
  isInActScope: () => import_runtime.isInActScope,
124
125
  isValidElement: () => import_runtime.isValidElement,
125
126
  keyedForBlock: () => import_runtime.keyedForBlock,
@@ -188,6 +189,7 @@ __export(index_exports, {
188
189
  setSpread: () => import_runtime.setSpread,
189
190
  setStringData: () => import_runtime.setStringData,
190
191
  setStyle: () => import_runtime.setStyle,
192
+ setStyleProperties: () => import_runtime.setStyleProperties,
191
193
  setStyleProperty: () => import_runtime.setStyleProperty,
192
194
  setText: () => import_runtime.setText,
193
195
  setTransitionFallbackTimeout: () => import_runtime.setTransitionFallbackTimeout,
@@ -348,6 +350,7 @@ var import_method_dep = require("./method-dep.cjs");
348
350
  injectStyle,
349
351
  invokeManualHook,
350
352
  isChildrenBlock,
353
+ isHydratingStyle,
351
354
  isInActScope,
352
355
  isValidElement,
353
356
  keyedForBlock,
@@ -416,6 +419,7 @@ var import_method_dep = require("./method-dep.cjs");
416
419
  setSpread,
417
420
  setStringData,
418
421
  setStyle,
422
+ setStyleProperties,
419
423
  setStyleProperty,
420
424
  setText,
421
425
  setTransitionFallbackTimeout,
@@ -131,6 +131,7 @@ __export(runtime_exports, {
131
131
  injectStyle: () => injectStyle,
132
132
  invokeManualHook: () => invokeManualHook,
133
133
  isChildrenBlock: () => isChildrenBlock,
134
+ isHydratingStyle: () => isHydratingStyle,
134
135
  isInActScope: () => isInActScope,
135
136
  isValidElement: () => isValidElement,
136
137
  keyedForBlock: () => keyedForBlock,
@@ -229,6 +230,7 @@ __export(runtime_exports, {
229
230
  setStringData: () => setStringData,
230
231
  setStringDataIfChanged: () => setStringDataIfChanged,
231
232
  setStyle: () => setStyle,
233
+ setStyleProperties: () => setStyleProperties,
232
234
  setStyleProperty: () => setStyleProperty,
233
235
  setText: () => setText,
234
236
  setTransitionFallbackTimeout: () => setTransitionFallbackTimeout,
@@ -8731,7 +8733,7 @@ class HydrationCapability {
8731
8733
  this.classWrites.clear();
8732
8734
  }
8733
8735
  }
8734
- applyStyle(el, value, _prev, staticCss) {
8736
+ applyStyle(el, value, _prev, staticCss, entries) {
8735
8737
  const mode = hydrationMismatchMode(el);
8736
8738
  if (mode === 1) return true;
8737
8739
  const style = el.style;
@@ -8739,7 +8741,14 @@ class HydrationCapability {
8739
8741
  const before = style.cssText;
8740
8742
  const expectedStyle = document.createElement("div").style;
8741
8743
  if (staticCss !== void 0) expectedStyle.cssText = staticCss;
8742
- applyStyleValue(el, expectedStyle, value, void 0);
8744
+ if (entries === void 0) applyStyleValue(el, expectedStyle, value, void 0);
8745
+ else {
8746
+ for (let i = 0; i < entries.length; i += 2) {
8747
+ const entry = entries[i + 1];
8748
+ if (entry != null && typeof entry !== "boolean")
8749
+ applyStyleProperty(el, expectedStyle, entries[i], entry);
8750
+ }
8751
+ }
8743
8752
  const expected = expectedStyle.cssText;
8744
8753
  const expectsStyleAttribute = expected !== "";
8745
8754
  if (before === expected && hadStyleAttribute === expectsStyleAttribute) return true;
@@ -10198,6 +10207,9 @@ function setDeoptClass(el, value) {
10198
10207
  }
10199
10208
  }
10200
10209
  const IMPORTANT_SUFFIX = "!important";
10210
+ function isHydratingStyle() {
10211
+ return activeHydration() !== null;
10212
+ }
10201
10213
  function setStyle(el, value, prev) {
10202
10214
  const style = el.style;
10203
10215
  const hydration = activeHydration();
@@ -10220,6 +10232,26 @@ function setStyleProperty(el, name, value, staticCss, previous) {
10220
10232
  if (remove) style.removeProperty((0, import_css.styleName)(name));
10221
10233
  else applyStyleProperty(el, style, name, value);
10222
10234
  }
10235
+ function setStyleProperties(el, entries, staticCss) {
10236
+ const hydration = activeHydration();
10237
+ if (hydration !== null) {
10238
+ hydration.applyStyle(el, null, void 0, staticCss, entries);
10239
+ return;
10240
+ }
10241
+ const style = el.style;
10242
+ let journaled = false;
10243
+ for (let i = 0; i < entries.length; i += 2) {
10244
+ const value = entries[i + 1];
10245
+ if (value == null || typeof value === "boolean") continue;
10246
+ if (!journaled && TRANSITION_JOURNAL !== null) {
10247
+ journalAttr(el, "style");
10248
+ journaled = true;
10249
+ }
10250
+ const name = entries[i];
10251
+ if (hiddenStyleWriter !== null && hiddenStyleWriter(el, value, void 0, name)) continue;
10252
+ applyStyleProperty(el, style, name, value);
10253
+ }
10254
+ }
10223
10255
  function applyStyleValue(el, style, value, prev) {
10224
10256
  if (value == null || value === false || value === "") {
10225
10257
  if (prev && typeof prev === "object") {
@@ -22726,6 +22758,7 @@ function scriptResource(attrs) {
22726
22758
  injectStyle,
22727
22759
  invokeManualHook,
22728
22760
  isChildrenBlock,
22761
+ isHydratingStyle,
22729
22762
  isInActScope,
22730
22763
  isValidElement,
22731
22764
  keyedForBlock,
@@ -22824,6 +22857,7 @@ function scriptResource(attrs) {
22824
22857
  setStringData,
22825
22858
  setStringDataIfChanged,
22826
22859
  setStyle,
22860
+ setStyleProperties,
22827
22861
  setStyleProperty,
22828
22862
  setText,
22829
22863
  setTransitionFallbackTimeout,
@@ -21,7 +21,7 @@ __export(version_exports, {
21
21
  version: () => version
22
22
  });
23
23
  module.exports = __toCommonJS(version_exports);
24
- const version = "0.2.8";
24
+ const version = "0.2.10";
25
25
  // Annotate the CommonJS export names for ESM import in node:
26
26
  0 && (module.exports = {
27
27
  version
@@ -37,6 +37,7 @@ import {
37
37
  createStyleClassMapFromStylesheet,
38
38
  clone_ast_node as cloneAstNode,
39
39
  strongHash,
40
+ withDeferredImports,
40
41
  } from '@tsrx/core';
41
42
  import { parseModule } from '#octane/compiler-parser';
42
43
  import { createStyleScopePass } from './style-scopes.js';
@@ -436,6 +437,8 @@ function attrBindingHelper(bind) {
436
437
  return 'setStyle';
437
438
  case 'styleProperty':
438
439
  return 'setStyleProperty';
440
+ case 'styleProperties':
441
+ return 'setStyleProperties';
439
442
  // SVG/MathML `className` is read-only — setClassAttr sets the attribute and
440
443
  // clsx-composes (setClassName composes on HTML).
441
444
  case 'class':
@@ -2152,24 +2155,30 @@ function staticStyleObjectNeedsDevValidation(obj) {
2152
2155
  // dynamic styles are byte-identical in innerHTML.
2153
2156
  function staticObjectToCssString(obj) {
2154
2157
  const parts = [];
2158
+ const properties = new Map();
2155
2159
  for (const p of obj.properties || []) {
2156
2160
  const name = p.key.type === 'Identifier' ? p.key.name : p.key.value;
2157
- const value = p.value.value;
2158
- if (value == null || value === false || value === '') continue;
2159
- const cssValue = value === true ? '' : cssStyleValue(name, value);
2161
+ properties.set(name, p.value.value);
2162
+ }
2163
+ for (const [name, value] of properties) {
2164
+ if (value == null || typeof value === 'boolean' || value === '') continue;
2165
+ const cssValue = cssStyleValue(name, value);
2160
2166
  parts.push(`${hyphenateStyleName(name)}: ${cssValue};`);
2161
2167
  }
2162
2168
  return parts.join(' ');
2163
2169
  }
2164
2170
 
2165
- // Preserve both CSS declaration order and JavaScript object evaluation order:
2166
- // only a nonempty literal prefix followed by exactly one dynamic own property
2167
- // can move into the template plus an independently guarded scalar binding.
2171
+ // A literal prefix can live in the template when all following properties have
2172
+ // fixed, unique names. The values still evaluate together at the authored
2173
+ // style attribute, before any DOM writes. Fixed literal values
2174
+ // after the first dynamic property stay in the ordered suffix. Spreads,
2175
+ // computed names, and accessors retain ordinary object evaluation and diffing.
2168
2176
  function mixedStaticStyle(obj) {
2169
- if (obj?.type !== 'ObjectExpression' || obj.properties.length < 2) return null;
2170
- const seen = new Set();
2177
+ if (obj?.type !== 'ObjectExpression' || obj.properties.length === 0) return null;
2178
+ const seen = new Map();
2179
+ let duplicates = false;
2171
2180
  const prefix = [];
2172
- let dynamic = null;
2181
+ const dynamics = [];
2173
2182
  for (const property of obj.properties) {
2174
2183
  if (
2175
2184
  (property.type !== 'Property' && property.type !== 'ObjectProperty') ||
@@ -2185,29 +2194,56 @@ function mixedStaticStyle(obj) {
2185
2194
  }
2186
2195
  const name = key.type === 'Identifier' ? key.name : key.value;
2187
2196
  const normalized = hyphenateStyleName(name);
2188
- if (name === '__proto__' || name === 'dangerouslySetInnerHTML' || seen.has(normalized)) {
2197
+ if (
2198
+ name === '__proto__' ||
2199
+ name === 'dangerouslySetInnerHTML' ||
2200
+ (seen.has(normalized) && seen.get(normalized) !== name)
2201
+ ) {
2189
2202
  return null;
2190
2203
  }
2191
- seen.add(normalized);
2204
+ if (seen.has(normalized)) duplicates = true;
2205
+ seen.set(normalized, name);
2192
2206
 
2193
2207
  const value = property.value;
2194
- if (value?.type === 'Literal') {
2195
- if (
2196
- dynamic !== null ||
2197
- (typeof value.value !== 'string' && typeof value.value !== 'number') ||
2198
- value.value === ''
2199
- ) {
2200
- return null;
2201
- }
2208
+ const unwrappedValue = unwrapTsExpr(value);
2209
+ // Extracting an anonymous function/class into a temporary changes its
2210
+ // inferred name. A discarded class can observe that name in a static
2211
+ // initializer, so preserve the original object evaluation in this case.
2212
+ if (
2213
+ unwrappedValue?.type === 'ArrowFunctionExpression' ||
2214
+ ((unwrappedValue?.type === 'FunctionExpression' ||
2215
+ unwrappedValue?.type === 'ClassExpression') &&
2216
+ !unwrappedValue.id)
2217
+ ) {
2218
+ return null;
2219
+ }
2220
+ if (
2221
+ value?.type === 'Literal' &&
2222
+ dynamics.length === 0 &&
2223
+ (typeof value.value === 'string' || typeof value.value === 'number') &&
2224
+ value.value !== ''
2225
+ ) {
2202
2226
  prefix.push(property);
2203
2227
  } else {
2204
- if (dynamic !== null) return null;
2205
- dynamic = { name, key, value };
2228
+ dynamics.push({ name, key, value });
2206
2229
  }
2207
2230
  }
2208
- if (prefix.length === 0 || dynamic === null) return null;
2231
+ // A duplicate retains its first insertion position but uses its last value.
2232
+ // Keep every authored evaluation, including overwritten values, and avoid
2233
+ // baking declarations that a later duplicate could replace or remove.
2234
+ if (duplicates) {
2235
+ return {
2236
+ css: '',
2237
+ dynamics: obj.properties.map((property) => ({
2238
+ name: property.key.type === 'Identifier' ? property.key.name : property.key.value,
2239
+ key: property.key,
2240
+ value: property.value,
2241
+ })),
2242
+ };
2243
+ }
2244
+ if (dynamics.length === 0) return null;
2209
2245
  const css = staticObjectToCssString({ properties: prefix });
2210
- return css === '' ? null : { css, ...dynamic };
2246
+ return prefix.length !== 0 && css === '' ? null : { css, dynamics };
2211
2247
  }
2212
2248
 
2213
2249
  // ===========================================================================
@@ -9922,7 +9958,11 @@ function compileInternal(
9922
9958
  }).nodes,
9923
9959
  );
9924
9960
  if (hmrEnabled) hmrComponents.push({ name: node.declaration.id.name, exportKind: 'default' });
9925
- } else if (node.type === 'ImportDeclaration' && node.source.value === 'octane') {
9961
+ } else if (
9962
+ node.type === 'ImportDeclaration' &&
9963
+ node.source.value === 'octane' &&
9964
+ node.phase !== 'defer'
9965
+ ) {
9926
9966
  // Preserve ALL user-imported names from octane (Portal, createContext,
9927
9967
  // use, custom helpers, etc.) — merged into the single prelude import.
9928
9968
  addUserImportSpecifiers(ctx, node);
@@ -10572,8 +10612,16 @@ function compileServer(
10572
10612
  : compileServerComponent({ ...node.declaration, default: true }, ctx)),
10573
10613
  );
10574
10614
  } else if (node.type === 'ImportDeclaration' && node.source.value === 'octane') {
10575
- // User imports from 'octane' resolve to the server runtime instead.
10576
- addUserImportSpecifiers(ctx, node);
10615
+ // Preserve the authored deferred namespace while routing it to the server runtime.
10616
+ // Other user imports are merged into the eager server runtime prelude.
10617
+ if (node.phase === 'defer') {
10618
+ bodyNodes.push({
10619
+ ...node,
10620
+ source: { ...node.source, value: 'octane/server', raw: '"octane/server"' },
10621
+ });
10622
+ } else {
10623
+ addUserImportSpecifiers(ctx, node);
10624
+ }
10577
10625
  } else if (
10578
10626
  (node.type === 'ImportDeclaration' ||
10579
10627
  node.type === 'ExportNamedDeclaration' ||
@@ -22511,7 +22559,12 @@ function planJsx(
22511
22559
  }
22512
22560
  let mixedStylePaths = '';
22513
22561
  for (const binding of elementBindings) {
22514
- if (binding.kind === 'styleProperty') mixedStylePaths += `|${binding.path.join('.')}`;
22562
+ if (
22563
+ (binding.kind === 'styleProperty' || binding.kind === 'styleProperties') &&
22564
+ binding.staticCss !== ''
22565
+ ) {
22566
+ mixedStylePaths += `|${binding.path.join('.')}`;
22567
+ }
22515
22568
  }
22516
22569
  const cloneCall =
22517
22570
  mixedStylePaths !== ''
@@ -22790,6 +22843,10 @@ function planJsx(
22790
22843
  ctx.runtimeNeeded.add(`evt${arity}`);
22791
22844
  if (!b.mountOnly) ctx.runtimeNeeded.add(`evt${arity}u`);
22792
22845
  }
22846
+ if (b.kind === 'styleProperties') {
22847
+ ctx.runtimeNeeded.add('setStyleProperty');
22848
+ ctx.runtimeNeeded.add('isHydratingStyle');
22849
+ }
22793
22850
  if (b.kind === 'spread') {
22794
22851
  ctx.runtimeNeeded.add('setSpread');
22795
22852
  ctx.runtimeNeeded.add('queueOwnRefDetach'); // unmount-detach of a spread-supplied ref
@@ -23945,6 +24002,7 @@ const DEFERRABLE_MOUNT_KINDS = new Set([
23945
24002
  'class',
23946
24003
  'style',
23947
24004
  'styleProperty',
24005
+ 'styleProperties',
23948
24006
  'formAction',
23949
24007
  'htmlOnlyChild',
23950
24008
  ]);
@@ -24153,11 +24211,19 @@ function commitSourceRows(sources, valueOf) {
24153
24211
  // `setClassName(el, undefined)` no-op on a freshly-cloned element (so the output
24154
24212
  // is byte-identical to the old unconditional mount write).
24155
24213
  function emitDeferredMount(bind, elVar, bag) {
24156
- // Whole-object `style` diffs on `_sty`; scalar styles and other values on `_prev`.
24214
+ // Whole-object styles diff on `_sty`; grouped styles use it only as a
24215
+ // first-render marker. Each grouped property keeps a scalar previous value.
24157
24216
  bag.constField(
24158
- bind.kind === 'style' ? `_sty$${bind.id}` : `_prev$${bind.id}`,
24159
- bind.kind === 'styleProperty' ? 'style-unset' : 'undefined',
24217
+ bind.kind === 'style' || bind.kind === 'styleProperties'
24218
+ ? `_sty$${bind.id}`
24219
+ : `_prev$${bind.id}`,
24220
+ bind.kind === 'styleProperty' || bind.kind === 'styleProperties' ? 'style-unset' : 'undefined',
24160
24221
  );
24222
+ if (bind.kind === 'styleProperties') {
24223
+ for (let i = 0; i < bind.properties.length; i++) {
24224
+ bag.constField(`_prev$${bind.id}_${i}`, 'undefined');
24225
+ }
24226
+ }
24161
24227
  const key = `_el$${bind.id}`;
24162
24228
  if (!bag.host(key, elVar)) return null;
24163
24229
  return inheritOriginLoc(
@@ -24386,6 +24452,34 @@ function emitBindingMount(bind, elVar, bag) {
24386
24452
  ]),
24387
24453
  );
24388
24454
  }
24455
+ case 'styleProperties': {
24456
+ for (let i = 0; i < bind.properties.length; i++) bag.local(`_prev$${bind.id}_${i}`);
24457
+ const entries =
24458
+ bind.properties.length === bind.evaluations.length
24459
+ ? V()
24460
+ : b.array(
24461
+ bind.properties.flatMap((property) => [
24462
+ inheritOriginLoc(b.literal(property.name), property.key),
24463
+ b.member(V(), b.literal(property.evaluationIndex * 2 + 1), true),
24464
+ ]),
24465
+ );
24466
+ return st(
24467
+ b.block([
24468
+ b.const('_v', bind.expr),
24469
+ b.stmt(b.call(callee(), el(), entries, b.literal(bind.staticCss))),
24470
+ ...mountHost(),
24471
+ ...bind.properties.map((property, i) =>
24472
+ b.stmt(
24473
+ b.assignment(
24474
+ '=',
24475
+ local(`_prev$${bind.id}_${i}`),
24476
+ b.member(V(), b.literal(property.evaluationIndex * 2 + 1), true),
24477
+ ),
24478
+ ),
24479
+ ),
24480
+ ]),
24481
+ );
24482
+ }
24389
24483
  case 'spread': {
24390
24484
  // Detach a spread-supplied `ref` on unmount. setSpread attaches/updates
24391
24485
  // the ref during mount/update, but only a scope cleanup can detach it
@@ -24733,6 +24827,55 @@ function emitBindingUpdate(bind, bag, inlineBindingGuards = false) {
24733
24827
  ]),
24734
24828
  );
24735
24829
  }
24830
+ case 'styleProperties': {
24831
+ // Evaluate every authored value before the first CSS write, including
24832
+ // fixed literal suffix values. Mount and hydration compare one complete
24833
+ // style; ordinary updates only call the scalar setter for changed cells.
24834
+ const values = bind.evaluations.map((value, i) => b.const(`_v${i}`, value));
24835
+ const valueOf = (property) => b.id(`_v${property.evaluationIndex}`);
24836
+ const entries = b.array(
24837
+ bind.properties.flatMap((property) => [
24838
+ inheritOriginLoc(b.literal(property.name), property.key),
24839
+ valueOf(property),
24840
+ ]),
24841
+ );
24842
+ const publish = bind.properties.map((property, i) =>
24843
+ b.stmt(b.assignment('=', bagFieldNode(bag, `_prev$${bind.id}_${i}`), valueOf(property))),
24844
+ );
24845
+ const grouped = [
24846
+ b.stmt(b.call(callee(), F('_el'), entries, b.literal(bind.staticCss))),
24847
+ ...(bind.deferred ? [b.stmt(b.assignment('=', F('_sty'), b.literal(null)))] : []),
24848
+ ...publish,
24849
+ ];
24850
+ const propertyCallee = () =>
24851
+ attrLoweringToken(b.id(`_$${attrBindingHelper({ kind: 'styleProperty' })}`), bind);
24852
+ const scalar = bind.properties.map((property, i) => {
24853
+ const previous = bagFieldNode(bag, `_prev$${bind.id}_${i}`);
24854
+ const value = valueOf(property);
24855
+ return b.if(
24856
+ b.binary('!==', previous, value),
24857
+ b.block([
24858
+ b.stmt(
24859
+ b.call(
24860
+ propertyCallee(),
24861
+ F('_el'),
24862
+ inheritOriginLoc(b.literal(property.name), property.key),
24863
+ value,
24864
+ b.literal(''),
24865
+ previous,
24866
+ ),
24867
+ ),
24868
+ b.stmt(b.assignment('=', previous, value)),
24869
+ ]),
24870
+ null,
24871
+ );
24872
+ });
24873
+ const hydrate = b.call('_$isHydratingStyle');
24874
+ const needsGroup = bind.deferred
24875
+ ? b.logical('||', b.binary('===', F('_sty'), b.id('__s')), hydrate)
24876
+ : hydrate;
24877
+ return st(b.block([...values, b.if(needsGroup, b.block(grouped), b.block(scalar))]));
24878
+ }
24736
24879
  case 'spread': {
24737
24880
  // setSpread does its own per-key diffing internally and handles cleanup
24738
24881
  // of keys that vanished — always call it, but skip if the reference is
@@ -25913,8 +26056,8 @@ function emitElementHtml(
25913
26056
  inner = resolveStyleExpr(inner, cssHash);
25914
26057
 
25915
26058
  // `style={...}` — static literal object/string serialises into the HTML
25916
- // template (unless we're after a spread, which would clobber it); dynamic
25917
- // values become a setStyle binding.
26059
+ // template (unless we're after a spread, which would clobber it); a fixed
26060
+ // literal prefix plus dynamic suffix can avoid a whole-object diff.
25918
26061
  if (attrName === 'style') {
25919
26062
  if (!isAfterSpread && inner.type === 'Literal' && typeof inner.value === 'string') {
25920
26063
  const chunk = ` style="${escapeAttr(inner.value)}"`;
@@ -25942,27 +26085,59 @@ function emitElementHtml(
25942
26085
  ) {
25943
26086
  const mixed = mixedStaticStyle(inner);
25944
26087
  if (mixed !== null) {
25945
- appendBakedAttribute(
25946
- attrTemplate,
25947
- ` style="${escapeAttr(mixed.css)}"`,
25948
- attrName,
25949
- attr.name,
25950
- inner,
25951
- ctx.inspect,
25952
- );
25953
- registerAttrLoweringOrigin(ctx, mixed.key, null, mixed.name);
25954
- bindings.push({
25955
- id: bindings.length,
25956
- kind: 'styleProperty',
25957
- name: mixed.name,
25958
- expr: tsrxExprNode(mixed.value, ctx, componentName, inlinedSubs),
25959
- path,
25960
- ns: hostNs,
25961
- nameOrigin: attr.name,
25962
- propertyOrigin: mixed.key,
25963
- staticOrigin: inner,
25964
- staticCss: mixed.css,
25965
- });
26088
+ if (mixed.css !== '') {
26089
+ appendBakedAttribute(
26090
+ attrTemplate,
26091
+ ` style="${escapeAttr(mixed.css)}"`,
26092
+ attrName,
26093
+ attr.name,
26094
+ inner,
26095
+ ctx.inspect,
26096
+ );
26097
+ }
26098
+ for (const entry of mixed.dynamics) {
26099
+ registerAttrLoweringOrigin(ctx, entry.key, null, entry.name);
26100
+ }
26101
+ if (mixed.dynamics.length === 1) {
26102
+ const entry = mixed.dynamics[0];
26103
+ bindings.push({
26104
+ id: bindings.length,
26105
+ kind: 'styleProperty',
26106
+ name: entry.name,
26107
+ expr: tsrxExprNode(entry.value, ctx, componentName, inlinedSubs),
26108
+ path,
26109
+ ns: hostNs,
26110
+ nameOrigin: attr.name,
26111
+ propertyOrigin: entry.key,
26112
+ staticOrigin: inner,
26113
+ staticCss: mixed.css,
26114
+ });
26115
+ } else {
26116
+ const entries = [];
26117
+ const propertyBindings = new Map();
26118
+ const evaluations = [];
26119
+ for (const entry of mixed.dynamics) {
26120
+ const value = tsrxExprNode(entry.value, ctx, componentName, inlinedSubs);
26121
+ entries.push(inheritOriginLoc(b.literal(entry.name), entry.key), value);
26122
+ propertyBindings.set(entry.name, {
26123
+ name: entry.name,
26124
+ key: entry.key,
26125
+ evaluationIndex: evaluations.length,
26126
+ });
26127
+ evaluations.push(value);
26128
+ }
26129
+ bindings.push({
26130
+ id: bindings.length,
26131
+ kind: 'styleProperties',
26132
+ expr: inheritOriginLoc(b.array(entries), inner),
26133
+ properties: [...propertyBindings.values()],
26134
+ evaluations,
26135
+ path,
26136
+ ns: hostNs,
26137
+ nameOrigin: attr.name,
26138
+ staticCss: mixed.css,
26139
+ });
26140
+ }
25966
26141
  continue;
25967
26142
  }
25968
26143
  }
@@ -29464,7 +29639,7 @@ const esrapCommentOptions = {
29464
29639
  function printNodeWithMap(node, ctx) {
29465
29640
  const printable = stripTsOnlyWrappers(escapeMultilineStringLiterals(node));
29466
29641
  if (assertPrintedLocs()) assertNodeLocs(printable);
29467
- const { code, map } = esrapPrint(printable, esrapTsx(esrapCommentOptions), {
29642
+ const { code, map } = esrapPrint(printable, withDeferredImports(esrapTsx(esrapCommentOptions)), {
29468
29643
  sourceMapSource: ctx.mapSourceName,
29469
29644
  sourceMapContent: ctx.mapSource,
29470
29645
  sourceMapEncodeMappings: false,
package/dist/index.d.ts CHANGED
@@ -5,7 +5,7 @@ export { initializeHydrationEventCapture } from './hydration/event-capture.js';
5
5
  export { attachBehaviorRoot } from './behavior-root.js';
6
6
  export type * from './behavior-root.js';
7
7
  export { createSubSlot, subSlot, type SubSlot, type SlotlessSubSlot, type SubSlotOptions, } from './sub-slot.js';
8
- export { createRoot, hydrateRoot, flushSync, act, isInActScope, type Root, type RootContainer, type RootOptions, useState, useLinkedState, type LinkedStatePrevious, type LinkedStateOptions, useReducer, useEffect, useLayoutEffect, useInsertionEffect, useMemo, useCallback, useRef, useId, useImperativeHandle, useEffectEvent, useSyncExternalStore, useDeferredValue, useTransition, useActionState, useActionState as useFormState, useFormStatus, useOptimistic, useDebugValue, type FormStatus, startTransition, requestFormReset, memo, lazy, preload, preinit, preloadModule, preinitModule, preconnect, prefetchDNS, createContext, use, useContext, type Context, type ForeignHostContext, Suspense, ErrorBoundary, Hydrate, Activity, Activity as unstable_Activity, ViewTransition, addTransitionType, ViewTransition as unstable_ViewTransition, addTransitionType as unstable_addTransitionType, ViewTransitionPseudoElement, type ViewTransitionProps, type ViewTransitionInstance, Fragment, createPortal, type PortalDescriptor, createElement, cloneElement, isValidElement, isChildrenBlock, descriptorChildren, Children, type ElementDescriptor, type ComponentBody, type OctaneNode, TsrxErrorBoundary, __useStateWithGetter, __useLinkedStateWithGetter, __useReducerWithGetter, __createVoidRoot, bindRendererRegionOwner, EXTERNAL_HYDRATION_PROMISE, HYDRATION_RANGE_BOUNDARY, createHostContextRequest, __vtSeen, template, clone, drainFrag, bag0, bag1, bag2, bag3, bag4, bag5, bag6, bag7, bag8, bag9, bag10, bag11, bag12, bag13, bag14, bag15, bag16, bagOf, evt0, evt0u, evt1, evt1u, evt2, evt2u, evtN, evtNu, setEventHandler, devEventListener, devHtmlNesting, htext, htextSwap, child, sibling, setText, setScriptText, setHTML, setDangerouslySetInnerHTML, setDangerouslySetInnerHTMLSources, markDangerouslySetInnerHTMLChildren, setAttribute, setStringData, setBooleanAttribute, setAriaAttribute, setClassName, setClassAttr, normalizeClass, setStyle, setStyleProperty, setSpread, snapshotSpread, setHostPropSources, queueNativeChangeDiagnostic, queueFormAuthoringDiagnostic, markNativeChangeDiagnosticStatic, setFormAction, setValue, setFormControlSources, setChecked, setCheckedCheckable, setSelectValue, setDefaultValue, setDefaultValueUncontrolled, setDefaultChecked, setAutoFocus, attachRef, queueRefAttach, queueRefDetach, replaceRef, queueOwnRefDetach, injectStyle, headBlock, stylesheetResource, styleResource, scriptResource, namespaceHead, namespaceHeadElement, delegateEvents, delegateCaptureEvents, fastForBlock, fastKeyedForBlock, fastMapSlot, forBlock, keyedForBlock, mapSlot, ifBlock, errorBlock, tryBlock, switchBlock, activityBlock, componentSlot, componentSlotVoid, componentSlotLite, compilerCacheArray, compilerCacheImmutableArrayFilter, compilerCacheMappedArray, compilerCacheContext, compilerOwnsContextProvider, markSingleRoot, markWarm, markSingleRoot as __s, markChildrenBlock, createScopedValue, createScopedElement, childSlot, positionalChildren, textSlot, textHole, childTextHole, hostComponent, renderBlock, portal, hookSlots, withSlot, manualHook, invokeManualHook, useBatch, registerWarmPlan, seedOrCreate, warmMemo, warmChild, puMiss, puTake0, puTake1, puTake2, puTake3, puTake4, puPub, provideContext, mountFragmentRef, FragmentInstance, hmr, HMR, hasPendingWork, type Scope, type Block, drainPassiveEffects, setIsOctaneActEnvironment, resetFloatResourceState, setTransitionFallbackTimeout, getTransitionFallbackTimeout, } from './runtime.js';
8
+ export { createRoot, hydrateRoot, flushSync, act, isInActScope, type Root, type RootContainer, type RootOptions, useState, useLinkedState, type LinkedStatePrevious, type LinkedStateOptions, useReducer, useEffect, useLayoutEffect, useInsertionEffect, useMemo, useCallback, useRef, useId, useImperativeHandle, useEffectEvent, useSyncExternalStore, useDeferredValue, useTransition, useActionState, useActionState as useFormState, useFormStatus, useOptimistic, useDebugValue, type FormStatus, startTransition, requestFormReset, memo, lazy, preload, preinit, preloadModule, preinitModule, preconnect, prefetchDNS, createContext, use, useContext, type Context, type ForeignHostContext, Suspense, ErrorBoundary, Hydrate, Activity, Activity as unstable_Activity, ViewTransition, addTransitionType, ViewTransition as unstable_ViewTransition, addTransitionType as unstable_addTransitionType, ViewTransitionPseudoElement, type ViewTransitionProps, type ViewTransitionInstance, Fragment, createPortal, type PortalDescriptor, createElement, cloneElement, isValidElement, isChildrenBlock, descriptorChildren, Children, type ElementDescriptor, type ComponentBody, type OctaneNode, TsrxErrorBoundary, __useStateWithGetter, __useLinkedStateWithGetter, __useReducerWithGetter, __createVoidRoot, bindRendererRegionOwner, EXTERNAL_HYDRATION_PROMISE, HYDRATION_RANGE_BOUNDARY, createHostContextRequest, __vtSeen, template, clone, drainFrag, bag0, bag1, bag2, bag3, bag4, bag5, bag6, bag7, bag8, bag9, bag10, bag11, bag12, bag13, bag14, bag15, bag16, bagOf, evt0, evt0u, evt1, evt1u, evt2, evt2u, evtN, evtNu, setEventHandler, devEventListener, devHtmlNesting, htext, htextSwap, child, sibling, setText, setScriptText, setHTML, setDangerouslySetInnerHTML, setDangerouslySetInnerHTMLSources, markDangerouslySetInnerHTMLChildren, setAttribute, setStringData, setBooleanAttribute, setAriaAttribute, setClassName, setClassAttr, normalizeClass, isHydratingStyle, setStyle, setStyleProperty, setStyleProperties, setSpread, snapshotSpread, setHostPropSources, queueNativeChangeDiagnostic, queueFormAuthoringDiagnostic, markNativeChangeDiagnosticStatic, setFormAction, setValue, setFormControlSources, setChecked, setCheckedCheckable, setSelectValue, setDefaultValue, setDefaultValueUncontrolled, setDefaultChecked, setAutoFocus, attachRef, queueRefAttach, queueRefDetach, replaceRef, queueOwnRefDetach, injectStyle, headBlock, stylesheetResource, styleResource, scriptResource, namespaceHead, namespaceHeadElement, delegateEvents, delegateCaptureEvents, fastForBlock, fastKeyedForBlock, fastMapSlot, forBlock, keyedForBlock, mapSlot, ifBlock, errorBlock, tryBlock, switchBlock, activityBlock, componentSlot, componentSlotVoid, componentSlotLite, compilerCacheArray, compilerCacheImmutableArrayFilter, compilerCacheMappedArray, compilerCacheContext, compilerOwnsContextProvider, markSingleRoot, markWarm, markSingleRoot as __s, markChildrenBlock, createScopedValue, createScopedElement, childSlot, positionalChildren, textSlot, textHole, childTextHole, hostComponent, renderBlock, portal, hookSlots, withSlot, manualHook, invokeManualHook, useBatch, registerWarmPlan, seedOrCreate, warmMemo, warmChild, puMiss, puTake0, puTake1, puTake2, puTake3, puTake4, puPub, provideContext, mountFragmentRef, FragmentInstance, hmr, HMR, hasPendingWork, type Scope, type Block, drainPassiveEffects, setIsOctaneActEnvironment, resetFloatResourceState, setTransitionFallbackTimeout, getTransitionFallbackTimeout, } from './runtime.js';
9
9
  export type { HydrateOptions, HydrateProps, HydrateWhen, HydrationInteractionEvent, HydrationInteractionEvents, HydrationPrefetchContext, HydrationPrefetchFunction, HydrationPrefetchStrategy, HydrationPrefetchWaitReason, HydrationStrategy, HydrationWhen, } from './hydration/types.js';
10
10
  export { __serverRpc } from './server-rpc-client.js';
11
11
  export { __methodDep } from './method-dep.js';
package/dist/index.js CHANGED
@@ -122,8 +122,10 @@ import {
122
122
  setClassName,
123
123
  setClassAttr,
124
124
  normalizeClass,
125
+ isHydratingStyle,
125
126
  setStyle,
126
127
  setStyleProperty,
128
+ setStyleProperties,
127
129
  setSpread,
128
130
  snapshotSpread,
129
131
  setHostPropSources,
@@ -320,6 +322,7 @@ export {
320
322
  injectStyle,
321
323
  invokeManualHook,
322
324
  isChildrenBlock,
325
+ isHydratingStyle,
323
326
  isInActScope,
324
327
  isValidElement,
325
328
  keyedForBlock,
@@ -388,6 +391,7 @@ export {
388
391
  setSpread,
389
392
  setStringData,
390
393
  setStyle,
394
+ setStyleProperties,
391
395
  setStyleProperty,
392
396
  setText,
393
397
  setTransitionFallbackTimeout,
package/dist/runtime.d.ts CHANGED
@@ -1435,6 +1435,8 @@ import { normalizeClass } from './css.js';
1435
1435
  export { normalizeClass };
1436
1436
  export declare function setClassName(el: Element, value: unknown): void;
1437
1437
  export declare function setClassAttr(el: Element, value: unknown): void;
1438
+ /** Whether a grouped style must compare its complete value during hydration. @internal */
1439
+ export declare function isHydratingStyle(): boolean;
1438
1440
  export declare function setStyle(el: HTMLElement | SVGElement, value: any, prev: any): void;
1439
1441
  /**
1440
1442
  * Update one dynamic declaration after a compiler-baked static style prefix.
@@ -1443,6 +1445,12 @@ export declare function setStyle(el: HTMLElement | SVGElement, value: any, prev:
1443
1445
  * @internal
1444
1446
  */
1445
1447
  export declare function setStyleProperty(el: HTMLElement | SVGElement, name: string, value: any, staticCss: string, previous: any): void;
1448
+ /**
1449
+ * Apply ordered fixed-key declarations on mount. The compiler writes changed
1450
+ * values with scalar setters on updates; hydration compares the whole value.
1451
+ * @internal
1452
+ */
1453
+ export declare function setStyleProperties(el: HTMLElement | SVGElement, entries: readonly unknown[], staticCss: string): void;
1446
1454
  /** Snapshot a JSX spread with own-enumerable Object.assign semantics. */
1447
1455
  export declare function snapshotSpread(value: unknown): Record<string, unknown> | null;
1448
1456
  type HostPropSource = readonly [
package/dist/runtime.js CHANGED
@@ -8559,7 +8559,7 @@ class HydrationCapability {
8559
8559
  this.classWrites.clear();
8560
8560
  }
8561
8561
  }
8562
- applyStyle(el, value, _prev, staticCss) {
8562
+ applyStyle(el, value, _prev, staticCss, entries) {
8563
8563
  const mode = hydrationMismatchMode(el);
8564
8564
  if (mode === 1) return true;
8565
8565
  const style = el.style;
@@ -8567,7 +8567,14 @@ class HydrationCapability {
8567
8567
  const before = style.cssText;
8568
8568
  const expectedStyle = document.createElement("div").style;
8569
8569
  if (staticCss !== void 0) expectedStyle.cssText = staticCss;
8570
- applyStyleValue(el, expectedStyle, value, void 0);
8570
+ if (entries === void 0) applyStyleValue(el, expectedStyle, value, void 0);
8571
+ else {
8572
+ for (let i = 0; i < entries.length; i += 2) {
8573
+ const entry = entries[i + 1];
8574
+ if (entry != null && typeof entry !== "boolean")
8575
+ applyStyleProperty(el, expectedStyle, entries[i], entry);
8576
+ }
8577
+ }
8571
8578
  const expected = expectedStyle.cssText;
8572
8579
  const expectsStyleAttribute = expected !== "";
8573
8580
  if (before === expected && hadStyleAttribute === expectsStyleAttribute) return true;
@@ -10033,6 +10040,9 @@ function setDeoptClass(el, value) {
10033
10040
  }
10034
10041
  }
10035
10042
  const IMPORTANT_SUFFIX = "!important";
10043
+ function isHydratingStyle() {
10044
+ return activeHydration() !== null;
10045
+ }
10036
10046
  function setStyle(el, value, prev) {
10037
10047
  const style = el.style;
10038
10048
  const hydration = activeHydration();
@@ -10055,6 +10065,26 @@ function setStyleProperty(el, name, value, staticCss, previous) {
10055
10065
  if (remove) style.removeProperty(styleName(name));
10056
10066
  else applyStyleProperty(el, style, name, value);
10057
10067
  }
10068
+ function setStyleProperties(el, entries, staticCss) {
10069
+ const hydration = activeHydration();
10070
+ if (hydration !== null) {
10071
+ hydration.applyStyle(el, null, void 0, staticCss, entries);
10072
+ return;
10073
+ }
10074
+ const style = el.style;
10075
+ let journaled = false;
10076
+ for (let i = 0; i < entries.length; i += 2) {
10077
+ const value = entries[i + 1];
10078
+ if (value == null || typeof value === "boolean") continue;
10079
+ if (!journaled && TRANSITION_JOURNAL !== null) {
10080
+ journalAttr(el, "style");
10081
+ journaled = true;
10082
+ }
10083
+ const name = entries[i];
10084
+ if (hiddenStyleWriter !== null && hiddenStyleWriter(el, value, void 0, name)) continue;
10085
+ applyStyleProperty(el, style, name, value);
10086
+ }
10087
+ }
10058
10088
  function applyStyleValue(el, style, value, prev) {
10059
10089
  if (value == null || value === false || value === "") {
10060
10090
  if (prev && typeof prev === "object") {
@@ -22560,6 +22590,7 @@ export {
22560
22590
  injectStyle,
22561
22591
  invokeManualHook,
22562
22592
  isChildrenBlock,
22593
+ isHydratingStyle,
22563
22594
  isInActScope,
22564
22595
  isValidElement,
22565
22596
  keyedForBlock,
@@ -22658,6 +22689,7 @@ export {
22658
22689
  setStringData,
22659
22690
  setStringDataIfChanged,
22660
22691
  setStyle,
22692
+ setStyleProperties,
22661
22693
  setStyleProperty,
22662
22694
  setText,
22663
22695
  setTransitionFallbackTimeout,
package/dist/version.js CHANGED
@@ -1,4 +1,4 @@
1
- const version = "0.2.8";
1
+ const version = "0.2.10";
2
2
  export {
3
3
  version
4
4
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "octane",
3
- "version": "0.2.8",
3
+ "version": "0.2.10",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "sideEffects": [