octane 0.1.30 → 0.1.32
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/dist/compiler/compile-universal.js +290 -22
- package/dist/compiler/compile.js +350 -48
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -0
- package/dist/runtime.d.ts +7 -0
- package/dist/runtime.js +251 -34
- package/dist/runtime.server.d.ts +1 -1
- package/dist/runtime.server.js +11 -1
- package/dist/universal-core.d.ts +40 -1
- package/dist/universal-core.js +313 -89
- package/dist/version.js +1 -2
- package/package.json +1 -1
|
@@ -1811,6 +1811,62 @@ function collectComponentNames(ast) {
|
|
|
1811
1811
|
return names;
|
|
1812
1812
|
}
|
|
1813
1813
|
|
|
1814
|
+
function collectOwnerFreeThreeHostComponents(ast, state, development) {
|
|
1815
|
+
if (
|
|
1816
|
+
development ||
|
|
1817
|
+
state.hmr ||
|
|
1818
|
+
state.profile ||
|
|
1819
|
+
state.renderer.id !== 'three' ||
|
|
1820
|
+
state.renderer.module !== '@octanejs/three/renderer'
|
|
1821
|
+
) {
|
|
1822
|
+
return null;
|
|
1823
|
+
}
|
|
1824
|
+
|
|
1825
|
+
const constructors = new Set();
|
|
1826
|
+
for (const statement of ast.body ?? []) {
|
|
1827
|
+
if (
|
|
1828
|
+
statement.type !== 'ImportDeclaration' ||
|
|
1829
|
+
statement.importKind === 'type' ||
|
|
1830
|
+
statement.source?.value !== '@octanejs/three'
|
|
1831
|
+
) {
|
|
1832
|
+
continue;
|
|
1833
|
+
}
|
|
1834
|
+
for (const specifier of statement.specifiers ?? []) {
|
|
1835
|
+
if (
|
|
1836
|
+
specifier.type === 'ImportSpecifier' &&
|
|
1837
|
+
specifier.importKind !== 'type' &&
|
|
1838
|
+
(specifier.imported?.name ?? specifier.imported?.value) === 'extend' &&
|
|
1839
|
+
typeof specifier.local?.name === 'string'
|
|
1840
|
+
) {
|
|
1841
|
+
constructors.add(specifier.local.name);
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
if (constructors.size === 0) return null;
|
|
1846
|
+
|
|
1847
|
+
const names = new Set();
|
|
1848
|
+
for (const statement of ast.body ?? []) {
|
|
1849
|
+
const declaration =
|
|
1850
|
+
statement.type === 'ExportNamedDeclaration' ? statement.declaration : statement;
|
|
1851
|
+
if (declaration?.type !== 'VariableDeclaration' || declaration.kind !== 'const') continue;
|
|
1852
|
+
for (const binding of declaration.declarations ?? []) {
|
|
1853
|
+
const call = binding.init;
|
|
1854
|
+
if (
|
|
1855
|
+
binding.id?.type === 'Identifier' &&
|
|
1856
|
+
call?.type === 'CallExpression' &&
|
|
1857
|
+
call.optional !== true &&
|
|
1858
|
+
call.callee?.type === 'Identifier' &&
|
|
1859
|
+
constructors.has(call.callee.name) &&
|
|
1860
|
+
call.arguments?.length === 1 &&
|
|
1861
|
+
call.arguments[0]?.type !== 'SpreadElement'
|
|
1862
|
+
) {
|
|
1863
|
+
names.add(binding.id.name);
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
return names.size === 0 ? null : { names, lexical: createLexicalAnalysis(ast) };
|
|
1868
|
+
}
|
|
1869
|
+
|
|
1814
1870
|
function addPatternNames(pattern, names) {
|
|
1815
1871
|
if (!pattern) return;
|
|
1816
1872
|
if (pattern.type === 'Identifier') {
|
|
@@ -1966,32 +2022,52 @@ function isOwnerFreeForAttribute(attribute) {
|
|
|
1966
2022
|
);
|
|
1967
2023
|
}
|
|
1968
2024
|
|
|
1969
|
-
function
|
|
2025
|
+
function ownerFreeForLeaf(node) {
|
|
1970
2026
|
if (node.empty != null) return null;
|
|
1971
2027
|
if (!isOwnerFreeForExpression(node.right) || !isOwnerFreeForExpression(node.key)) return null;
|
|
1972
2028
|
const body = (node.body?.body ?? []).filter(
|
|
1973
2029
|
(statement) => statement.type !== 'JSXText' || normalizeJsxText(statement.value ?? '') !== '',
|
|
1974
2030
|
);
|
|
1975
2031
|
if (body.length !== 1) return null;
|
|
1976
|
-
const
|
|
2032
|
+
const leaf = body[0];
|
|
2033
|
+
if (leaf.type !== 'JSXElement' && leaf.type !== 'Element') return null;
|
|
1977
2034
|
if (
|
|
1978
|
-
(
|
|
1979
|
-
isComponentElement(host) ||
|
|
1980
|
-
jsxName(host) === 'Activity'
|
|
1981
|
-
) {
|
|
1982
|
-
return null;
|
|
1983
|
-
}
|
|
1984
|
-
const type = jsxName(host);
|
|
1985
|
-
if (type === null || !/^[a-z]/.test(type)) return null;
|
|
1986
|
-
if (
|
|
1987
|
-
(host.children ?? []).some(
|
|
2035
|
+
(leaf.children ?? []).some(
|
|
1988
2036
|
(child) => child.type !== 'JSXText' || normalizeJsxText(child.value ?? '') !== '',
|
|
1989
2037
|
)
|
|
1990
2038
|
) {
|
|
1991
2039
|
return null;
|
|
1992
2040
|
}
|
|
1993
|
-
const attributes =
|
|
1994
|
-
return attributes.every(isOwnerFreeForAttribute) ?
|
|
2041
|
+
const attributes = leaf.openingElement?.attributes ?? leaf.attributes ?? [];
|
|
2042
|
+
return attributes.every(isOwnerFreeForAttribute) ? leaf : null;
|
|
2043
|
+
}
|
|
2044
|
+
|
|
2045
|
+
function ownerFreeForHost(node) {
|
|
2046
|
+
const host = ownerFreeForLeaf(node);
|
|
2047
|
+
if (host === null || isComponentElement(host) || jsxName(host) === 'Activity') return null;
|
|
2048
|
+
const type = jsxName(host);
|
|
2049
|
+
return type !== null && /^[a-z]/.test(type) ? host : null;
|
|
2050
|
+
}
|
|
2051
|
+
|
|
2052
|
+
function ownerFreeForThreeHostComponent(node, state) {
|
|
2053
|
+
const trusted = state.ownerFreeThreeHostComponents;
|
|
2054
|
+
if (trusted == null) return null;
|
|
2055
|
+
const component = ownerFreeForLeaf(node);
|
|
2056
|
+
if (component === null || !isComponentElement(component)) return null;
|
|
2057
|
+
const attributes = component.openingElement?.attributes ?? component.attributes ?? [];
|
|
2058
|
+
const attributesSeen = new Set();
|
|
2059
|
+
for (const attribute of attributes) {
|
|
2060
|
+
const attributeKey = attributeName(attribute);
|
|
2061
|
+
if (attributeKey === '__proto__' || attributesSeen.has(attributeKey)) return null;
|
|
2062
|
+
attributesSeen.add(attributeKey);
|
|
2063
|
+
}
|
|
2064
|
+
const name = component.openingElement?.name ?? component.name;
|
|
2065
|
+
if (name?.type !== 'JSXIdentifier' || !trusted.names.has(name.name)) return null;
|
|
2066
|
+
const binding = trusted.lexical.resolveBinding(
|
|
2067
|
+
trusted.lexical.nodeScopes.get(name) ?? trusted.lexical.rootScope,
|
|
2068
|
+
name.name,
|
|
2069
|
+
);
|
|
2070
|
+
return binding?.scope === trusted.lexical.rootScope ? component : null;
|
|
1995
2071
|
}
|
|
1996
2072
|
|
|
1997
2073
|
function allocPlan(state, root, origin = null) {
|
|
@@ -2407,6 +2483,85 @@ function compilePlainPropsObjectAst(attributes, state, origin) {
|
|
|
2407
2483
|
return inheritGeneratedOrigin(b.object(entries), origin);
|
|
2408
2484
|
}
|
|
2409
2485
|
|
|
2486
|
+
/**
|
|
2487
|
+
* Lower a `class={[…]}` array literal to its clsx-composed string when every
|
|
2488
|
+
* element is statically a string or falsy.
|
|
2489
|
+
*
|
|
2490
|
+
* The runtime composes class arrays clsx-style, so `['row', on && 'danger']`
|
|
2491
|
+
* is only ever observed as `'row'` or `'row danger'` — but as a slot value the
|
|
2492
|
+
* array is rebuilt on every render, and on a transported root each rebuild is
|
|
2493
|
+
* re-encoded, making the hottest per-row prop a fresh allocation per render.
|
|
2494
|
+
* Emitting the string-building expression instead makes the slot value a
|
|
2495
|
+
* primitive: identity-comparable, allocation-free, and byte-identical in the
|
|
2496
|
+
* background and main-thread programs (so first-screen adoption still sees
|
|
2497
|
+
* the same tree). An all-literal array folds further, into a static plan prop
|
|
2498
|
+
* with no slot at all. Anything not statically string-or-falsy (spreads,
|
|
2499
|
+
* nested arrays, objects, expressions with non-literal truthy arms) keeps the
|
|
2500
|
+
* authored array slot and the runtime's general composition.
|
|
2501
|
+
*
|
|
2502
|
+
* Returns `{ staticValue }` for a fully static class, `{ expression }` for a
|
|
2503
|
+
* string-building lowering, or `null` to keep the authored value.
|
|
2504
|
+
*/
|
|
2505
|
+
function loweredHostClassAst(expression, state) {
|
|
2506
|
+
if (expression.type !== 'ArrayExpression') return null;
|
|
2507
|
+
const elements = expression.elements ?? [];
|
|
2508
|
+
if (elements.length === 0) return { staticValue: '' };
|
|
2509
|
+
/** @type {({ static: string } | { test: any, value: string })[]} */
|
|
2510
|
+
const parts = [];
|
|
2511
|
+
for (const element of elements) {
|
|
2512
|
+
if (element == null || element.type === 'SpreadElement') return null;
|
|
2513
|
+
if (element.type === 'Literal' && typeof element.value === 'string') {
|
|
2514
|
+
if (element.value !== '') parts.push({ static: element.value });
|
|
2515
|
+
continue;
|
|
2516
|
+
}
|
|
2517
|
+
if (
|
|
2518
|
+
element.type === 'LogicalExpression' &&
|
|
2519
|
+
element.operator === '&&' &&
|
|
2520
|
+
element.right.type === 'Literal' &&
|
|
2521
|
+
typeof element.right.value === 'string' &&
|
|
2522
|
+
element.right.value !== ''
|
|
2523
|
+
) {
|
|
2524
|
+
parts.push({ test: element.left, value: element.right.value });
|
|
2525
|
+
continue;
|
|
2526
|
+
}
|
|
2527
|
+
return null;
|
|
2528
|
+
}
|
|
2529
|
+
// The first part must be a literal so every later piece can join with an
|
|
2530
|
+
// unconditional leading space.
|
|
2531
|
+
if (parts.length !== 0 && parts[0].static === undefined) return null;
|
|
2532
|
+
let composed = null;
|
|
2533
|
+
let pendingStatic = '';
|
|
2534
|
+
const flushStatic = () => {
|
|
2535
|
+
if (pendingStatic === '') return;
|
|
2536
|
+
const literal = b.literal(pendingStatic, JSON.stringify(pendingStatic));
|
|
2537
|
+
composed = composed === null ? literal : b.binary('+', composed, literal);
|
|
2538
|
+
pendingStatic = '';
|
|
2539
|
+
};
|
|
2540
|
+
for (const part of parts) {
|
|
2541
|
+
if (part.static !== undefined) {
|
|
2542
|
+
pendingStatic += pendingStatic === '' && composed === null ? part.static : ` ${part.static}`;
|
|
2543
|
+
continue;
|
|
2544
|
+
}
|
|
2545
|
+
flushStatic();
|
|
2546
|
+
const spaced = ` ${part.value}`;
|
|
2547
|
+
composed = b.binary(
|
|
2548
|
+
'+',
|
|
2549
|
+
composed,
|
|
2550
|
+
inheritGeneratedOrigin(
|
|
2551
|
+
b.conditional(
|
|
2552
|
+
dynamicExpressionAst(part.test, state),
|
|
2553
|
+
b.literal(spaced, JSON.stringify(spaced)),
|
|
2554
|
+
b.literal('', '""'),
|
|
2555
|
+
),
|
|
2556
|
+
part.test,
|
|
2557
|
+
),
|
|
2558
|
+
);
|
|
2559
|
+
}
|
|
2560
|
+
if (composed === null) return { staticValue: pendingStatic };
|
|
2561
|
+
flushStatic();
|
|
2562
|
+
return { expression: inheritGeneratedOrigin(composed, expression) };
|
|
2563
|
+
}
|
|
2564
|
+
|
|
2410
2565
|
function compileAttributeAst(attribute, context, state, canonicalizeHostClass) {
|
|
2411
2566
|
if (attribute.type === 'JSXSpreadAttribute' || attribute.type === 'SpreadAttribute') {
|
|
2412
2567
|
throw universalError(
|
|
@@ -2428,6 +2583,18 @@ function compileAttributeAst(attribute, context, state, canonicalizeHostClass) {
|
|
|
2428
2583
|
if (value.type === 'Literal') return { name, staticValue: value.value };
|
|
2429
2584
|
if (value.type === 'JSXExpressionContainer') {
|
|
2430
2585
|
if (!value.expression || value.expression.type === 'JSXEmptyExpression') return null;
|
|
2586
|
+
if (name === 'class') {
|
|
2587
|
+
const lowered = loweredHostClassAst(value.expression, state);
|
|
2588
|
+
if (lowered !== null) {
|
|
2589
|
+
if (lowered.expression === undefined) return { name, staticValue: lowered.staticValue };
|
|
2590
|
+
const slot = context.values.length;
|
|
2591
|
+
context.values.push(lowered.expression);
|
|
2592
|
+
return { name, slot };
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
if (value.expression.type === 'Literal' && isStaticPropLiteral(value.expression.value)) {
|
|
2596
|
+
return { name, staticValue: value.expression.value };
|
|
2597
|
+
}
|
|
2431
2598
|
const slot = context.values.length;
|
|
2432
2599
|
context.values.push(mainThreadHostValueAst(name, value.expression, state));
|
|
2433
2600
|
return { name, slot };
|
|
@@ -2435,6 +2602,15 @@ function compileAttributeAst(attribute, context, state, canonicalizeHostClass) {
|
|
|
2435
2602
|
throw universalError(state.filename, attribute, `unsupported value for host attribute ${name}.`);
|
|
2436
2603
|
}
|
|
2437
2604
|
|
|
2605
|
+
/**
|
|
2606
|
+
* Literal values that can ride the frozen plan instead of a per-render slot.
|
|
2607
|
+
* `null` stays a slot: it is a legal "no handler" value for event props, whose
|
|
2608
|
+
* erasure in main-thread first-screen programs happens at the slot site.
|
|
2609
|
+
*/
|
|
2610
|
+
function isStaticPropLiteral(value) {
|
|
2611
|
+
return typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean';
|
|
2612
|
+
}
|
|
2613
|
+
|
|
2438
2614
|
function addDynamicAst(context, expression) {
|
|
2439
2615
|
const slot = context.values.length;
|
|
2440
2616
|
context.values.push(expression);
|
|
@@ -2710,6 +2886,55 @@ function rewriteSetupStatementAst(statement, state) {
|
|
|
2710
2886
|
return rewritten === null ? [] : [rewritten];
|
|
2711
2887
|
}
|
|
2712
2888
|
|
|
2889
|
+
function compileOwnerFreeForHostAst(host, state, itemBinding, indexBinding) {
|
|
2890
|
+
const context = { values: [] };
|
|
2891
|
+
const plan = allocPlan(state, compileHostElementAst(host, context, state), host);
|
|
2892
|
+
return {
|
|
2893
|
+
plan: generatedIdentifier(plan, host),
|
|
2894
|
+
render: generatedArrow(
|
|
2895
|
+
[itemBinding, indexBinding],
|
|
2896
|
+
inheritGeneratedOrigin(b.array(context.values), host),
|
|
2897
|
+
host,
|
|
2898
|
+
),
|
|
2899
|
+
};
|
|
2900
|
+
}
|
|
2901
|
+
|
|
2902
|
+
function compileOwnerFreeThreeHostComponentAst(component, state, itemBinding, indexBinding) {
|
|
2903
|
+
const attributes = component.openingElement?.attributes ?? component.attributes ?? [];
|
|
2904
|
+
const names = [];
|
|
2905
|
+
const values = [];
|
|
2906
|
+
for (const attribute of attributes) {
|
|
2907
|
+
const name = attributeName(attribute);
|
|
2908
|
+
const value = attribute.value;
|
|
2909
|
+
const expression =
|
|
2910
|
+
value === null
|
|
2911
|
+
? inheritGeneratedOrigin(b.literal(true), attribute)
|
|
2912
|
+
: value.type === 'Literal'
|
|
2913
|
+
? inheritGeneratedOrigin(b.literal(value.value), value)
|
|
2914
|
+
: mainThreadHostValueAst(name, value.expression, state);
|
|
2915
|
+
names.push(name);
|
|
2916
|
+
values.push(expression);
|
|
2917
|
+
}
|
|
2918
|
+
const helper = (state.helpers.hostComponentLeafPlan ??= allocName(
|
|
2919
|
+
state,
|
|
2920
|
+
'__octaneUniversalHostComponentLeafPlan',
|
|
2921
|
+
));
|
|
2922
|
+
const signature = JSON.stringify(names);
|
|
2923
|
+
return {
|
|
2924
|
+
plan: generatedCall(
|
|
2925
|
+
helper,
|
|
2926
|
+
[b.literal(state.renderer.id), jsxNameExpressionAst(component, state), b.literal(signature)],
|
|
2927
|
+
component,
|
|
2928
|
+
),
|
|
2929
|
+
render: generatedArrow(
|
|
2930
|
+
[itemBinding, indexBinding],
|
|
2931
|
+
inheritGeneratedOrigin(b.array(values), component),
|
|
2932
|
+
component,
|
|
2933
|
+
),
|
|
2934
|
+
signature: inheritGeneratedOrigin(b.literal(signature), component),
|
|
2935
|
+
};
|
|
2936
|
+
}
|
|
2937
|
+
|
|
2713
2938
|
function compileForAst(node, context, state) {
|
|
2714
2939
|
if (node.await) {
|
|
2715
2940
|
throw universalError(
|
|
@@ -2730,18 +2955,43 @@ function compileForAst(node, context, state) {
|
|
|
2730
2955
|
node.index ?? generatedIdentifier(allocName(state, '__octaneUniversalIndex'), node);
|
|
2731
2956
|
assertNoResidualTemplate(node.right, state, '@for source');
|
|
2732
2957
|
assertNoResidualTemplate(node.key, state, '@for key');
|
|
2958
|
+
const host = !state.hmr ? ownerFreeForHost(node) : null;
|
|
2959
|
+
const component = host === null ? ownerFreeForThreeHostComponent(node, state) : null;
|
|
2960
|
+
const compactHost =
|
|
2961
|
+
host === null ? null : compileOwnerFreeForHostAst(host, state, itemBinding, indexBinding);
|
|
2962
|
+
const compactComponent =
|
|
2963
|
+
component === null
|
|
2964
|
+
? null
|
|
2965
|
+
: compileOwnerFreeThreeHostComponentAst(component, state, itemBinding, indexBinding);
|
|
2733
2966
|
const args = [
|
|
2734
2967
|
rewriteSourceAst(node.right, state),
|
|
2735
2968
|
generatedArrow([itemBinding, indexBinding], rewriteSourceAst(node.key, state), node.key),
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2969
|
+
compactHost?.render ??
|
|
2970
|
+
compactComponent?.render ??
|
|
2971
|
+
compileBlockValueAst(
|
|
2972
|
+
node.body?.body ?? [],
|
|
2973
|
+
state,
|
|
2974
|
+
[itemBinding, indexBinding],
|
|
2975
|
+
node.body ?? node,
|
|
2976
|
+
),
|
|
2742
2977
|
];
|
|
2743
|
-
if (
|
|
2744
|
-
args.push(
|
|
2978
|
+
if (host !== null) {
|
|
2979
|
+
args.push(
|
|
2980
|
+
b.literal(null, 'null'),
|
|
2981
|
+
b.literal(true),
|
|
2982
|
+
b.literal(true),
|
|
2983
|
+
inheritGeneratedOrigin(b.unary('void', b.literal(0)), host),
|
|
2984
|
+
compactHost.plan,
|
|
2985
|
+
);
|
|
2986
|
+
} else if (component !== null) {
|
|
2987
|
+
args.push(
|
|
2988
|
+
b.literal(null, 'null'),
|
|
2989
|
+
b.literal(true),
|
|
2990
|
+
b.literal(true),
|
|
2991
|
+
jsxNameExpressionAst(component, state),
|
|
2992
|
+
compactComponent.plan,
|
|
2993
|
+
compactComponent.signature,
|
|
2994
|
+
);
|
|
2745
2995
|
} else if (node.empty) {
|
|
2746
2996
|
args.push(compileBlockValueAst(node.empty?.body ?? [], state, [], node.empty));
|
|
2747
2997
|
}
|
|
@@ -2828,6 +3078,16 @@ function compileChildAst(node, context, state) {
|
|
|
2828
3078
|
}
|
|
2829
3079
|
if (node.type === 'JSXExpressionContainer') {
|
|
2830
3080
|
if (!node.expression || node.expression.type === 'JSXEmptyExpression') return [];
|
|
3081
|
+
// A string-literal child is authored text with braces around it: fold it
|
|
3082
|
+
// into the plan like JSXText, so the constant stops riding every render's
|
|
3083
|
+
// slot array. Renderers without host text keep the renderable-hole slot.
|
|
3084
|
+
if (
|
|
3085
|
+
node.expression.type === 'Literal' &&
|
|
3086
|
+
typeof node.expression.value === 'string' &&
|
|
3087
|
+
state.renderer.text === 'host'
|
|
3088
|
+
) {
|
|
3089
|
+
return [withPlanOrigin({ kind: 'text', value: node.expression.value }, node)];
|
|
3090
|
+
}
|
|
2831
3091
|
return [addDynamicAst(context, dynamicExpressionAst(node.expression, state))];
|
|
2832
3092
|
}
|
|
2833
3093
|
if (node.type === 'JSXElement' || node.type === 'Element') {
|
|
@@ -3048,6 +3308,9 @@ function universalHelperImportAst(state, extraPairs = [], origin = null) {
|
|
|
3048
3308
|
['universalPlan', state.helpers.plan],
|
|
3049
3309
|
['universalValue', state.helpers.value],
|
|
3050
3310
|
['universalComponent', state.helpers.nestedComponent],
|
|
3311
|
+
...(state.helpers.hostComponentLeafPlan === undefined
|
|
3312
|
+
? []
|
|
3313
|
+
: [['universalHostComponentLeafPlan', state.helpers.hostComponentLeafPlan]]),
|
|
3051
3314
|
['universalProps', state.helpers.props],
|
|
3052
3315
|
['universalIf', state.helpers.if],
|
|
3053
3316
|
['universalSwitch', state.helpers.switch],
|
|
@@ -3696,6 +3959,11 @@ export function compileUniversal(
|
|
|
3696
3959
|
componentNames: collectComponentNames(ast),
|
|
3697
3960
|
runtimeImports: new Map(),
|
|
3698
3961
|
};
|
|
3962
|
+
state.ownerFreeThreeHostComponents = collectOwnerFreeThreeHostComponents(
|
|
3963
|
+
ast,
|
|
3964
|
+
state,
|
|
3965
|
+
options.dev === true,
|
|
3966
|
+
);
|
|
3699
3967
|
state.helpers.component = allocName(state, '__octaneDefineUniversalComponent');
|
|
3700
3968
|
state.helpers.plan = allocName(state, '__octaneUniversalPlan');
|
|
3701
3969
|
state.helpers.value = allocName(state, '__octaneUniversalValue');
|