@lwc/ssr-compiler 8.5.0 → 8.7.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/dist/compile-js/types.d.ts +2 -0
- package/dist/estree/validators.d.ts +7 -1
- package/dist/index.cjs.js +184 -74
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.js +184 -74
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
|
@@ -14,9 +14,15 @@ type RenderCall = CallExpression & {
|
|
|
14
14
|
callee: RenderMemberExpression;
|
|
15
15
|
};
|
|
16
16
|
/** Returns `true` if the node is an identifier or `<something>.render()`. */
|
|
17
|
-
export declare const isIdentOrRenderCall:
|
|
17
|
+
export declare const isIdentOrRenderCall: {
|
|
18
|
+
(node: Node | null | undefined): node is Identifier | RenderCall;
|
|
19
|
+
__debugName: string;
|
|
20
|
+
};
|
|
18
21
|
/** A validator that returns `true` if the node is `null`. */
|
|
19
22
|
type NullableChecker<T extends Node> = (node: Node | null | undefined) => node is T | null;
|
|
20
23
|
/** Extends a validator to return `true` if the node is `null`. */
|
|
21
24
|
export declare function isNullableOf<T extends Node>(validator: Checker<T>): NullableChecker<T>;
|
|
25
|
+
export declare namespace isNullableOf {
|
|
26
|
+
var __debugName: string;
|
|
27
|
+
}
|
|
22
28
|
export {};
|
package/dist/index.cjs.js
CHANGED
|
@@ -11,9 +11,9 @@ var meriyah = require('meriyah');
|
|
|
11
11
|
var immer = require('immer');
|
|
12
12
|
var node_path = require('node:path');
|
|
13
13
|
var acorn = require('acorn');
|
|
14
|
+
var shared = require('@lwc/shared');
|
|
14
15
|
var templateCompiler = require('@lwc/template-compiler');
|
|
15
16
|
var builders = require('estree-toolkit/dist/builders');
|
|
16
|
-
var shared = require('@lwc/shared');
|
|
17
17
|
var util = require('util');
|
|
18
18
|
|
|
19
19
|
/*
|
|
@@ -297,10 +297,13 @@ const getReplacementNode = (state, placeholderId) => {
|
|
|
297
297
|
!(Array.isArray(replacementNode)
|
|
298
298
|
? replacementNode.every(validateReplacement)
|
|
299
299
|
: validateReplacement(replacementNode))) {
|
|
300
|
-
const
|
|
300
|
+
const expectedType = validateReplacement.__debugName ||
|
|
301
|
+
validateReplacement.name ||
|
|
302
|
+
'(could not determine)';
|
|
303
|
+
const actualType = Array.isArray(replacementNode)
|
|
301
304
|
? `[${replacementNode.map((n) => n.type)}.join(', ')]`
|
|
302
305
|
: replacementNode?.type;
|
|
303
|
-
throw new Error(`Validation failed for templated node
|
|
306
|
+
throw new Error(`Validation failed for templated node. Expected type ${expectedType}, but received ${actualType}.`);
|
|
304
307
|
}
|
|
305
308
|
return replacementNode;
|
|
306
309
|
};
|
|
@@ -431,6 +434,24 @@ const isIdentOrRenderCall = (node) => {
|
|
|
431
434
|
estreeToolkit.is.identifier(node.callee.property) &&
|
|
432
435
|
node.callee.property.name === 'render'));
|
|
433
436
|
};
|
|
437
|
+
isIdentOrRenderCall.__debugName = 'identifier or .render() call';
|
|
438
|
+
/** Extends a validator to return `true` if the node is `null`. */
|
|
439
|
+
function isNullableOf(validator) {
|
|
440
|
+
const nullableValidator = (node) => {
|
|
441
|
+
return node === null || validator(node);
|
|
442
|
+
};
|
|
443
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
444
|
+
nullableValidator.__debugName = `nullable(${validator.__debugName || validator.name || 'unknown validator'})`;
|
|
445
|
+
}
|
|
446
|
+
return nullableValidator;
|
|
447
|
+
}
|
|
448
|
+
isNullableOf.__debugName = 'isNullableOf';
|
|
449
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
450
|
+
// Modifying another package's exports is a code smell!
|
|
451
|
+
for (const [key, val] of shared.entries(estreeToolkit.is)) {
|
|
452
|
+
val.__debugName = key;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
434
455
|
|
|
435
456
|
/*
|
|
436
457
|
* Copyright (c) 2024, salesforce.com, inc.
|
|
@@ -440,7 +461,13 @@ const isIdentOrRenderCall = (node) => {
|
|
|
440
461
|
*/
|
|
441
462
|
const bGenerateMarkup = (esTemplate `
|
|
442
463
|
export async function* generateMarkup(tagName, props, attrs, slotted) {
|
|
443
|
-
attrs = attrs ??
|
|
464
|
+
attrs = attrs ?? Object.create(null);
|
|
465
|
+
props = props ?? Object.create(null);
|
|
466
|
+
props = __filterProperties(
|
|
467
|
+
props,
|
|
468
|
+
${ /*public fields*/estreeToolkit.is.arrayExpression},
|
|
469
|
+
${ /*private fields*/estreeToolkit.is.arrayExpression},
|
|
470
|
+
);
|
|
444
471
|
const instance = new ${ /* Component class */estreeToolkit.is.identifier}({
|
|
445
472
|
tagName: tagName.toUpperCase(),
|
|
446
473
|
});
|
|
@@ -453,13 +480,15 @@ const bGenerateMarkup = (esTemplate `
|
|
|
453
480
|
}
|
|
454
481
|
const tmplFn = ${isIdentOrRenderCall} ?? __fallbackTmpl;
|
|
455
482
|
yield \`<\${tagName}\`;
|
|
456
|
-
const shouldRenderScopeToken =
|
|
483
|
+
const shouldRenderScopeToken =
|
|
484
|
+
tmplFn.hasScopedStylesheets ||
|
|
485
|
+
hasScopedStaticStylesheets(${ /*component class*/2});
|
|
457
486
|
if (shouldRenderScopeToken) {
|
|
458
487
|
yield \` class="\${tmplFn.stylesheetScopeToken}-host"\`;
|
|
459
488
|
}
|
|
460
489
|
yield* __renderAttrs(instance, attrs);
|
|
461
490
|
yield '>';
|
|
462
|
-
yield* tmplFn(props, attrs, slotted, ${
|
|
491
|
+
yield* tmplFn(props, attrs, slotted, ${ /*component class*/2}, instance);
|
|
463
492
|
yield \`</\${tagName}>\`;
|
|
464
493
|
}
|
|
465
494
|
`);
|
|
@@ -481,7 +510,7 @@ const bAssignGenerateMarkupToComponentClass = (esTemplate `
|
|
|
481
510
|
* - deferring to the template function for yielding child content
|
|
482
511
|
*/
|
|
483
512
|
function addGenerateMarkupExport(program, state, filename) {
|
|
484
|
-
const { hasRenderMethod, tmplExplicitImports } = state;
|
|
513
|
+
const { hasRenderMethod, privateFields, publicFields, tmplExplicitImports } = state;
|
|
485
514
|
const classIdentifier = estreeToolkit.builders.identifier(state.lwcClassName);
|
|
486
515
|
const renderCall = hasRenderMethod
|
|
487
516
|
? estreeToolkit.builders.callExpression(estreeToolkit.builders.memberExpression(estreeToolkit.builders.identifier('instance'), estreeToolkit.builders.identifier('render')), [])
|
|
@@ -493,13 +522,14 @@ function addGenerateMarkupExport(program, state, filename) {
|
|
|
493
522
|
program.body.unshift(bImportDeclaration([
|
|
494
523
|
{
|
|
495
524
|
fallbackTmpl: '__fallbackTmpl',
|
|
525
|
+
filterProperties: '__filterProperties',
|
|
496
526
|
mutationTracker: '__mutationTracker',
|
|
497
527
|
renderAttrs: '__renderAttrs',
|
|
498
528
|
SYMBOL__SET_INTERNALS: '__SYMBOL__SET_INTERNALS',
|
|
499
529
|
},
|
|
500
530
|
]));
|
|
501
531
|
program.body.unshift(bImportDeclaration(['hasScopedStaticStylesheets']));
|
|
502
|
-
program.body.push(bGenerateMarkup(classIdentifier, renderCall));
|
|
532
|
+
program.body.push(bGenerateMarkup(estreeToolkit.builders.arrayExpression(publicFields.map(estreeToolkit.builders.literal)), estreeToolkit.builders.arrayExpression(privateFields.map(estreeToolkit.builders.literal)), classIdentifier, renderCall));
|
|
503
533
|
}
|
|
504
534
|
/**
|
|
505
535
|
* Attach the `generateMarkup` function to the Component class so that it can be found later
|
|
@@ -549,13 +579,23 @@ const visitors = {
|
|
|
549
579
|
}
|
|
550
580
|
},
|
|
551
581
|
PropertyDefinition(path, state) {
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
582
|
+
const node = path.node;
|
|
583
|
+
if (!estreeToolkit.is.identifier(node?.key)) {
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
// TODO [#4773]: Why do we get conflicting PropertyDefinition types when removing "vitest/globals"?
|
|
587
|
+
const decorators = node.decorators;
|
|
588
|
+
if (estreeToolkit.is.identifier(decorators[0]?.expression) && decorators[0].expression.name === 'api') {
|
|
589
|
+
state.publicFields.push(node.key.name);
|
|
590
|
+
}
|
|
591
|
+
else {
|
|
592
|
+
state.privateFields.push(node.key.name);
|
|
593
|
+
}
|
|
594
|
+
if (node.static &&
|
|
595
|
+
node.key.name === 'stylesheets' &&
|
|
596
|
+
estreeToolkit.is.arrayExpression(node.value) &&
|
|
597
|
+
node.value.elements.every((el) => estreeToolkit.is.identifier(el))) {
|
|
598
|
+
catalogStaticStylesheets(node.value.elements.map((el) => el.name), state);
|
|
559
599
|
}
|
|
560
600
|
},
|
|
561
601
|
MethodDefinition(path, state) {
|
|
@@ -615,6 +655,8 @@ function compileJS(src, filename, compilationMode) {
|
|
|
615
655
|
tmplExplicitImports: null,
|
|
616
656
|
cssExplicitImports: null,
|
|
617
657
|
staticStylesheetIds: null,
|
|
658
|
+
publicFields: [],
|
|
659
|
+
privateFields: [],
|
|
618
660
|
};
|
|
619
661
|
estreeToolkit.traverse(ast, visitors, state);
|
|
620
662
|
if (!state.isLWC) {
|
|
@@ -1787,6 +1829,12 @@ var DiagnosticLevel;
|
|
|
1787
1829
|
level: DiagnosticLevel.Error,
|
|
1788
1830
|
url: '',
|
|
1789
1831
|
},
|
|
1832
|
+
IGNORED_SLOT_ATTRIBUTE_IN_CHILD: {
|
|
1833
|
+
code: 1201,
|
|
1834
|
+
message: 'The slot attribute in {0} will be ignored due to its ancestor {1}. It must be a direct child of the containing component.',
|
|
1835
|
+
level: DiagnosticLevel.Warning,
|
|
1836
|
+
url: '',
|
|
1837
|
+
},
|
|
1790
1838
|
});
|
|
1791
1839
|
|
|
1792
1840
|
/**
|
|
@@ -1799,7 +1847,7 @@ var CompilerMetrics;
|
|
|
1799
1847
|
CompilerMetrics["LWCSpreadDirective"] = "lwc-spread-directive";
|
|
1800
1848
|
CompilerMetrics["DynamicImportTransform"] = "dynamic-import-transform";
|
|
1801
1849
|
})(CompilerMetrics || (CompilerMetrics = {}));
|
|
1802
|
-
/** version: 8.
|
|
1850
|
+
/** version: 8.7.0 */
|
|
1803
1851
|
|
|
1804
1852
|
/*
|
|
1805
1853
|
* Copyright (c) 2024, Salesforce, Inc.
|
|
@@ -1960,8 +2008,8 @@ function expressionIrToEs(node, cxt) {
|
|
|
1960
2008
|
*/
|
|
1961
2009
|
const bYieldFromChildGenerator = (esTemplateWithYield `
|
|
1962
2010
|
{
|
|
1963
|
-
const childProps = ${estreeToolkit.is.objectExpression};
|
|
1964
|
-
const childAttrs = ${estreeToolkit.is.objectExpression};
|
|
2011
|
+
const childProps = __cloneAndDeepFreeze(${ /* child props */estreeToolkit.is.objectExpression});
|
|
2012
|
+
const childAttrs = ${ /* child attrs */estreeToolkit.is.objectExpression};
|
|
1965
2013
|
const slottedContent = {
|
|
1966
2014
|
light: Object.create(null),
|
|
1967
2015
|
shadow: async function* () {
|
|
@@ -1976,13 +2024,16 @@ const bYieldFromChildGenerator = (esTemplateWithYield `
|
|
|
1976
2024
|
slottedContent.light[name] = [fn]
|
|
1977
2025
|
}
|
|
1978
2026
|
}
|
|
1979
|
-
${ /* addContent statements */estreeToolkit.is.callExpression}
|
|
2027
|
+
${ /* light DOM addContent statements */estreeToolkit.is.callExpression}
|
|
2028
|
+
${ /* scoped slot addContent statements */estreeToolkit.is.callExpression}
|
|
1980
2029
|
yield* ${estreeToolkit.is.identifier}(${estreeToolkit.is.literal}, childProps, childAttrs, slottedContent);
|
|
1981
2030
|
}
|
|
1982
2031
|
`);
|
|
1983
2032
|
const bAddContent = (esTemplate `
|
|
1984
|
-
addContent(${ /* slot name */estreeToolkit.is.expression} ?? "", async function* (
|
|
1985
|
-
|
|
2033
|
+
addContent(${ /* slot name */estreeToolkit.is.expression} ?? "", async function* (${
|
|
2034
|
+
/* scoped slot data variable */ isNullableOf(estreeToolkit.is.identifier)}) {
|
|
2035
|
+
// FIXME: make validation work again
|
|
2036
|
+
${ /* slot content */false}
|
|
1986
2037
|
});
|
|
1987
2038
|
`);
|
|
1988
2039
|
const bImportGenerateMarkup = (localName, importPath) => estreeToolkit.builders.importDeclaration([estreeToolkit.builders.importSpecifier(estreeToolkit.builders.identifier('generateMarkup'), estreeToolkit.builders.identifier(localName))], estreeToolkit.builders.literal(importPath));
|
|
@@ -2006,46 +2057,43 @@ function getChildAttrsOrProps(attrs, cxt) {
|
|
|
2006
2057
|
});
|
|
2007
2058
|
return estreeToolkit.builders.objectExpression(objectAttrsOrProps);
|
|
2008
2059
|
}
|
|
2009
|
-
function reflectAriaPropsAsAttrs(props) {
|
|
2010
|
-
return props
|
|
2011
|
-
.map((prop) => {
|
|
2012
|
-
if (prop.attributeName.startsWith('aria-') || prop.attributeName === 'role') {
|
|
2013
|
-
return {
|
|
2014
|
-
type: 'Attribute',
|
|
2015
|
-
name: prop.attributeName,
|
|
2016
|
-
value: prop.value,
|
|
2017
|
-
};
|
|
2018
|
-
}
|
|
2019
|
-
return null;
|
|
2020
|
-
})
|
|
2021
|
-
.filter((el) => el !== null);
|
|
2022
|
-
}
|
|
2023
2060
|
const Component = function Component(node, cxt) {
|
|
2024
2061
|
// Import the custom component's generateMarkup export.
|
|
2025
2062
|
const childGeneratorLocalName = `generateMarkup_${templateCompiler.toPropertyName(node.name)}`;
|
|
2026
2063
|
const importPath = templateCompiler.kebabcaseToCamelcase(node.name);
|
|
2027
2064
|
const componentImport = bImportGenerateMarkup(childGeneratorLocalName, importPath);
|
|
2028
2065
|
cxt.hoist(componentImport, childGeneratorLocalName);
|
|
2066
|
+
cxt.hoist(bImportDeclaration([{ cloneAndDeepFreeze: '__cloneAndDeepFreeze' }]), 'import:cloneAndDeepFreeze');
|
|
2029
2067
|
const childTagName = node.name;
|
|
2030
|
-
|
|
2031
|
-
const
|
|
2032
|
-
const
|
|
2068
|
+
// Anything inside the slotted content is a normal slotted content except for `<template lwc:slot-data>` which is a scoped slot.
|
|
2069
|
+
const slottableChildren = node.children.filter((child) => child.type !== 'ScopedSlotFragment');
|
|
2070
|
+
const scopedSlottableChildren = node.children.filter((child) => child.type === 'ScopedSlotFragment');
|
|
2071
|
+
const shadowSlotContent = optimizeAdjacentYieldStmts(irChildrenToEs(slottableChildren, cxt));
|
|
2072
|
+
const lightSlotContent = slottableChildren.map((child) => {
|
|
2033
2073
|
if ('attributes' in child) {
|
|
2034
2074
|
const slotName = bAttributeValue(child, 'slot');
|
|
2035
|
-
// FIXME: We don't know what happens for slot attributes inside an lwc:if block
|
|
2036
2075
|
// Light DOM slots do not actually render the `slot` attribute.
|
|
2037
2076
|
const clone = immer.produce(child, (draft) => {
|
|
2038
2077
|
draft.attributes = draft.attributes.filter((attr) => attr.name !== 'slot');
|
|
2039
2078
|
});
|
|
2040
2079
|
const slotContent = irToEs(clone, cxt);
|
|
2041
|
-
return bAddContent(slotName, slotContent);
|
|
2080
|
+
return bAddContent(slotName, null, slotContent);
|
|
2042
2081
|
}
|
|
2043
2082
|
else {
|
|
2044
|
-
return bAddContent(estreeToolkit.builders.literal(''), irToEs(child, cxt));
|
|
2083
|
+
return bAddContent(estreeToolkit.builders.literal(''), null, irToEs(child, cxt));
|
|
2045
2084
|
}
|
|
2046
2085
|
});
|
|
2086
|
+
const scopedSlotContent = scopedSlottableChildren.map((child) => {
|
|
2087
|
+
const boundVariableName = child.slotData.value.name;
|
|
2088
|
+
const boundVariable = estreeToolkit.builders.identifier(boundVariableName);
|
|
2089
|
+
cxt.pushLocalVars([boundVariableName]);
|
|
2090
|
+
// TODO [#4768]: what if the bound variable is `generateMarkup` or some framework-specific identifier?
|
|
2091
|
+
const addContentExpr = bAddContent(child.slotName, boundVariable, irChildrenToEs(child.children, cxt));
|
|
2092
|
+
cxt.popLocalVars();
|
|
2093
|
+
return addContentExpr;
|
|
2094
|
+
});
|
|
2047
2095
|
return [
|
|
2048
|
-
bYieldFromChildGenerator(getChildAttrsOrProps(node.properties, cxt), getChildAttrsOrProps(attributes, cxt), shadowSlotContent, lightSlotContent, estreeToolkit.builders.identifier(childGeneratorLocalName), estreeToolkit.builders.literal(childTagName)),
|
|
2096
|
+
bYieldFromChildGenerator(getChildAttrsOrProps(node.properties, cxt), getChildAttrsOrProps(node.attributes, cxt), shadowSlotContent, lightSlotContent, scopedSlotContent, estreeToolkit.builders.identifier(childGeneratorLocalName), estreeToolkit.builders.literal(childTagName)),
|
|
2049
2097
|
];
|
|
2050
2098
|
};
|
|
2051
2099
|
|
|
@@ -2059,17 +2107,32 @@ const bYield$1 = (expr) => estreeToolkit.builders.expressionStatement(estreeTool
|
|
|
2059
2107
|
// TODO [#4714]: scope token renders as a suffix for literals, but prefix for expressions
|
|
2060
2108
|
const bConditionalLiveYield = (esTemplateWithYield `
|
|
2061
2109
|
{
|
|
2062
|
-
const
|
|
2110
|
+
const attrName = ${ /* attribute name */estreeToolkit.is.literal};
|
|
2111
|
+
let attrValue = ${ /* attribute value expression */estreeToolkit.is.expression};
|
|
2112
|
+
const isHtmlBooleanAttr = ${ /* isHtmlBooleanAttr */estreeToolkit.is.literal};
|
|
2113
|
+
|
|
2114
|
+
const shouldRenderScopeToken = attrName === 'class' &&
|
|
2063
2115
|
(hasScopedStylesheets || hasScopedStaticStylesheets(Cmp));
|
|
2064
2116
|
const prefix = shouldRenderScopeToken ? stylesheetScopeToken + ' ' : '';
|
|
2065
2117
|
|
|
2066
|
-
|
|
2067
|
-
|
|
2118
|
+
// Global HTML boolean attributes are specially coerced into booleans
|
|
2119
|
+
// https://github.com/salesforce/lwc/blob/f34a347/packages/%40lwc/template-compiler/src/codegen/index.ts#L450-L454
|
|
2120
|
+
if (isHtmlBooleanAttr) {
|
|
2121
|
+
attrValue = attrValue ? '' : undefined;
|
|
2122
|
+
}
|
|
2068
2123
|
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2124
|
+
// Global HTML "tabindex" attribute is specially massaged into a stringified number
|
|
2125
|
+
// This follows the historical behavior in api.ts:
|
|
2126
|
+
// https://github.com/salesforce/lwc/blob/f34a347/packages/%40lwc/engine-core/src/framework/api.ts#L193-L211
|
|
2127
|
+
if (attrName === 'tabindex') {
|
|
2128
|
+
const shouldNormalize = attrValue > 0 && typeof attrValue !== 'boolean';
|
|
2129
|
+
attrValue = shouldNormalize ? 0 : attrValue;
|
|
2130
|
+
}
|
|
2131
|
+
|
|
2132
|
+
if (attrValue !== undefined && attrValue !== null) {
|
|
2133
|
+
yield ' ' + attrName;
|
|
2134
|
+
if (attrValue !== '') {
|
|
2135
|
+
yield \`="\${prefix}\${htmlEscape(String(attrValue), true)}"\`;
|
|
2073
2136
|
}
|
|
2074
2137
|
}
|
|
2075
2138
|
}
|
|
@@ -2077,10 +2140,18 @@ const bConditionalLiveYield = (esTemplateWithYield `
|
|
|
2077
2140
|
// TODO [#4714]: scope token renders as a suffix for literals, but prefix for expressions
|
|
2078
2141
|
const bStringLiteralYield = (esTemplateWithYield `
|
|
2079
2142
|
{
|
|
2080
|
-
const
|
|
2143
|
+
const attrName = ${ /* attribute name */estreeToolkit.is.literal}
|
|
2144
|
+
const attrValue = ${ /* attribute value */estreeToolkit.is.literal};
|
|
2145
|
+
|
|
2146
|
+
const shouldRenderScopeToken = attrName === 'class' &&
|
|
2081
2147
|
(hasScopedStylesheets || hasScopedStaticStylesheets(Cmp));
|
|
2082
2148
|
const suffix = shouldRenderScopeToken ? ' ' + stylesheetScopeToken : '';
|
|
2083
|
-
|
|
2149
|
+
|
|
2150
|
+
yield ' ' + attrName;
|
|
2151
|
+
if (attrValue !== '' || shouldRenderScopeToken) {
|
|
2152
|
+
yield '="' + attrValue + suffix + '"';
|
|
2153
|
+
}
|
|
2154
|
+
|
|
2084
2155
|
}
|
|
2085
2156
|
`);
|
|
2086
2157
|
const bConditionallyYieldScopeTokenClass = (esTemplateWithYield `
|
|
@@ -2091,7 +2162,10 @@ const bConditionallyYieldScopeTokenClass = (esTemplateWithYield `
|
|
|
2091
2162
|
}
|
|
2092
2163
|
}
|
|
2093
2164
|
`);
|
|
2094
|
-
|
|
2165
|
+
const bYieldSanitizedHtml = esTemplateWithYield `
|
|
2166
|
+
yield sanitizeHtmlContent(${ /* lwc:inner-html content */estreeToolkit.is.expression})
|
|
2167
|
+
`;
|
|
2168
|
+
function yieldAttrOrPropLiteralValue(name, valueNode) {
|
|
2095
2169
|
const { value, type } = valueNode;
|
|
2096
2170
|
if (typeof value === 'string') {
|
|
2097
2171
|
let yieldedValue;
|
|
@@ -2102,19 +2176,25 @@ function yieldAttrOrPropLiteralValue(name, valueNode, isClass) {
|
|
|
2102
2176
|
// @ts-expect-error weird indirection results in wrong overload being picked up
|
|
2103
2177
|
yieldedValue = shared.StringReplace.call(shared.StringTrim.call(value), /\s+/g, ' ');
|
|
2104
2178
|
}
|
|
2179
|
+
else if (name === 'spellcheck') {
|
|
2180
|
+
// `spellcheck` string values are specially handled to massage them into booleans.
|
|
2181
|
+
// https://github.com/salesforce/lwc/blob/fe4e95f/packages/%40lwc/template-compiler/src/codegen/index.ts#L445-L448
|
|
2182
|
+
yieldedValue = String(value.toLowerCase() !== 'false');
|
|
2183
|
+
}
|
|
2105
2184
|
else {
|
|
2106
2185
|
yieldedValue = value;
|
|
2107
2186
|
}
|
|
2108
|
-
return [bStringLiteralYield(estreeToolkit.builders.literal(
|
|
2187
|
+
return [bStringLiteralYield(estreeToolkit.builders.literal(name), estreeToolkit.builders.literal(yieldedValue))];
|
|
2109
2188
|
}
|
|
2110
2189
|
else if (typeof value === 'boolean') {
|
|
2111
2190
|
return [bYield$1(estreeToolkit.builders.literal(` ${name}`))];
|
|
2112
2191
|
}
|
|
2113
2192
|
throw new Error(`Unknown attr/prop literal: ${type}`);
|
|
2114
2193
|
}
|
|
2115
|
-
function yieldAttrOrPropLiveValue(name, value,
|
|
2194
|
+
function yieldAttrOrPropLiveValue(elementName, name, value, cxt) {
|
|
2195
|
+
const isHtmlBooleanAttr = shared.isBooleanAttribute(name, elementName);
|
|
2116
2196
|
const scopedExpression = getScopedExpression(value, cxt);
|
|
2117
|
-
return [bConditionalLiveYield(estreeToolkit.builders.literal(
|
|
2197
|
+
return [bConditionalLiveYield(estreeToolkit.builders.literal(name), scopedExpression, estreeToolkit.builders.literal(isHtmlBooleanAttr))];
|
|
2118
2198
|
}
|
|
2119
2199
|
function reorderAttributes(attrs, props) {
|
|
2120
2200
|
let classAttr = null;
|
|
@@ -2141,19 +2221,28 @@ const Element = function Element(node, cxt) {
|
|
|
2141
2221
|
const innerHtmlDirective = node.type === 'Element' && node.directives.find((dir) => dir.name === 'InnerHTML');
|
|
2142
2222
|
const attrsAndProps = reorderAttributes(node.attributes, node.properties);
|
|
2143
2223
|
let hasClassAttribute = false;
|
|
2144
|
-
const yieldAttrsAndProps = attrsAndProps
|
|
2224
|
+
const yieldAttrsAndProps = attrsAndProps
|
|
2225
|
+
.filter(({ name }) => {
|
|
2226
|
+
// `<input checked>`/`<input value>` is treated as a property, not an attribute,
|
|
2227
|
+
// so should never be SSR'd. See https://github.com/salesforce/lwc/issues/4763
|
|
2228
|
+
return !(node.name === 'input' && (name === 'value' || name === 'checked'));
|
|
2229
|
+
})
|
|
2230
|
+
.flatMap((attr) => {
|
|
2145
2231
|
const { name, value, type } = attr;
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2232
|
+
if (type === 'Attribute') {
|
|
2233
|
+
if (name === 'inner-h-t-m-l' || name === 'outer-h-t-m-l') {
|
|
2234
|
+
throw new Error(`Cannot set attribute "${name}" on <${node.name}>.`);
|
|
2235
|
+
}
|
|
2236
|
+
else if (name === 'class') {
|
|
2237
|
+
hasClassAttribute = true;
|
|
2238
|
+
}
|
|
2150
2239
|
}
|
|
2151
2240
|
cxt.hoist(bImportHtmlEscape(), importHtmlEscapeKey);
|
|
2152
2241
|
if (value.type === 'Literal') {
|
|
2153
|
-
return yieldAttrOrPropLiteralValue(name, value
|
|
2242
|
+
return yieldAttrOrPropLiteralValue(name, value);
|
|
2154
2243
|
}
|
|
2155
2244
|
else {
|
|
2156
|
-
return yieldAttrOrPropLiveValue(name,
|
|
2245
|
+
return yieldAttrOrPropLiveValue(node.name, name, value, cxt);
|
|
2157
2246
|
}
|
|
2158
2247
|
});
|
|
2159
2248
|
if (shared.isVoidElement(node.name, shared.HTML_NAMESPACE)) {
|
|
@@ -2168,7 +2257,8 @@ const Element = function Element(node, cxt) {
|
|
|
2168
2257
|
else if (innerHtmlDirective) {
|
|
2169
2258
|
const value = innerHtmlDirective.value;
|
|
2170
2259
|
const unsanitizedHtmlExpression = value.type === 'Literal' ? estreeToolkit.builders.literal(value.value) : expressionIrToEs(value, cxt);
|
|
2171
|
-
childContent = [
|
|
2260
|
+
childContent = [bYieldSanitizedHtml(unsanitizedHtmlExpression)];
|
|
2261
|
+
cxt.hoist(bImportDeclaration(['sanitizeHtmlContent']), 'import:sanitizeHtmlContent');
|
|
2172
2262
|
}
|
|
2173
2263
|
else {
|
|
2174
2264
|
childContent = [];
|
|
@@ -2294,13 +2384,19 @@ const IfBlock = function IfBlock(node, cxt) {
|
|
|
2294
2384
|
*/
|
|
2295
2385
|
const bConditionalSlot = (esTemplateWithYield `
|
|
2296
2386
|
if (isLightDom) {
|
|
2297
|
-
|
|
2387
|
+
const isScopedSlot = ${ /* isScopedSlot */estreeToolkit.is.literal};
|
|
2388
|
+
// start bookend HTML comment for light DOM slot vfragment
|
|
2298
2389
|
yield '<!---->';
|
|
2299
2390
|
|
|
2391
|
+
// scoped slot factory has its own vfragment hence its own bookend
|
|
2392
|
+
if (isScopedSlot) {
|
|
2393
|
+
yield '<!---->';
|
|
2394
|
+
}
|
|
2395
|
+
|
|
2300
2396
|
const generators = slottedContent?.light[${ /* slotName */estreeToolkit.is.expression} ?? ""];
|
|
2301
2397
|
if (generators) {
|
|
2302
2398
|
for (const generator of generators) {
|
|
2303
|
-
yield* generator();
|
|
2399
|
+
yield* generator(${ /* scoped slot data */isNullableOf(estreeToolkit.is.expression)});
|
|
2304
2400
|
}
|
|
2305
2401
|
} else {
|
|
2306
2402
|
// If we're in this else block, then the generator _must_ have yielded
|
|
@@ -2310,19 +2406,29 @@ const bConditionalSlot = (esTemplateWithYield `
|
|
|
2310
2406
|
// TODO: default/fallback slot content
|
|
2311
2407
|
${ /* slot fallback content */estreeToolkit.is.statement}
|
|
2312
2408
|
}
|
|
2409
|
+
|
|
2410
|
+
// scoped slot factory has its own vfragment hence its own bookend
|
|
2411
|
+
if (isScopedSlot) {
|
|
2412
|
+
yield '<!---->';
|
|
2413
|
+
}
|
|
2313
2414
|
|
|
2314
|
-
// end bookend HTML comment
|
|
2415
|
+
// end bookend HTML comment for light DOM slot vfragment
|
|
2315
2416
|
yield '<!---->';
|
|
2316
2417
|
} else {
|
|
2317
2418
|
${ /* slot element AST */estreeToolkit.is.statement}
|
|
2318
2419
|
}
|
|
2319
2420
|
`);
|
|
2320
2421
|
const Slot = function Slot(node, ctx) {
|
|
2422
|
+
const slotBindDirective = node.directives.find((dir) => dir.name === 'SlotBind');
|
|
2423
|
+
const slotBound = slotBindDirective?.value
|
|
2424
|
+
? getScopedExpression(slotBindDirective.value, ctx)
|
|
2425
|
+
: null;
|
|
2321
2426
|
const slotName = bAttributeValue(node, 'name');
|
|
2322
2427
|
// FIXME: avoid serializing the slot's children twice
|
|
2323
2428
|
const slotAst = Element(node, ctx);
|
|
2324
2429
|
const slotChildren = irChildrenToEs(node.children, ctx);
|
|
2325
|
-
|
|
2430
|
+
const isScopedSlot = estreeToolkit.builders.literal(Boolean(slotBound));
|
|
2431
|
+
return [bConditionalSlot(isScopedSlot, slotName, slotBound, slotChildren, slotAst)];
|
|
2326
2432
|
};
|
|
2327
2433
|
|
|
2328
2434
|
/*
|
|
@@ -2334,12 +2440,16 @@ const Slot = function Slot(node, ctx) {
|
|
|
2334
2440
|
const bYield = (expr) => estreeToolkit.builders.expressionStatement(estreeToolkit.builders.yieldExpression(expr));
|
|
2335
2441
|
const bYieldEscapedString = (esTemplateWithYield `
|
|
2336
2442
|
const ${estreeToolkit.is.identifier} = ${estreeToolkit.is.expression};
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2443
|
+
switch (typeof ${0}) {
|
|
2444
|
+
case 'string':
|
|
2445
|
+
yield (${estreeToolkit.is.literal} && ${0} === '') ? '\\u200D' : htmlEscape(${0});
|
|
2446
|
+
break;
|
|
2447
|
+
case 'number':
|
|
2448
|
+
case 'boolean':
|
|
2449
|
+
yield String(${0});
|
|
2450
|
+
break;
|
|
2451
|
+
default:
|
|
2452
|
+
yield ${0} ? htmlEscape(${0}.toString()) : '\\u200D';
|
|
2343
2453
|
}
|
|
2344
2454
|
`);
|
|
2345
2455
|
function isLiteral(node) {
|
|
@@ -2572,5 +2682,5 @@ function compileTemplateForSSR(src, filename, options, mode = 'asyncYield') {
|
|
|
2572
2682
|
|
|
2573
2683
|
exports.compileComponentForSSR = compileComponentForSSR;
|
|
2574
2684
|
exports.compileTemplateForSSR = compileTemplateForSSR;
|
|
2575
|
-
/** version: 8.
|
|
2685
|
+
/** version: 8.7.0 */
|
|
2576
2686
|
//# sourceMappingURL=index.cjs.js.map
|