forked from salesforce/lwc
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.ts
More file actions
155 lines (139 loc) · 6.24 KB
/
index.ts
File metadata and controls
155 lines (139 loc) · 6.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/*
* Copyright (c) 2024, salesforce.com, inc.
* All rights reserved.
* SPDX-License-Identifier: MIT
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
*/
import { generate } from 'astring';
import { is, builders as b } from 'estree-toolkit';
import { parse, type Config as TemplateCompilerConfig } from '@lwc/template-compiler';
import { LWC_VERSION_COMMENT, type CompilationMode } from '@lwc/shared';
import { produce } from 'immer';
import { esTemplate } from '../estemplate';
import { getStylesheetImports } from '../compile-js/stylesheets';
import { addScopeTokenDeclarations } from '../compile-js/stylesheet-scope-token';
import { transmogrify } from '../transmogrify';
import { optimizeAdjacentYieldStmts } from './shared';
import { templateIrToEsTree } from './ir-to-es';
import type {
ExportDefaultDeclaration as EsExportDefaultDeclaration,
FunctionDeclaration,
} from 'estree';
// TODO [#4663]: Render mode mismatch between template and compiler should throw.
const bExportTemplate = esTemplate`
export default async function* __lwcTmpl(
// This is where $$emit comes from
shadowSlottedContent,
lightSlottedContent,
scopedSlottedContent,
Cmp,
instance
) {
// Deliberately using let so we can mutate as many times as we want in the same scope.
// These should be scoped to the "tmpl" function however, to avoid conflicts with other templates.
let textContentBuffer = '';
let didBufferTextContent = false;
// This will get overridden but requires initialization.
const slotAttributeValue = null;
// Establishes a contextual relationship between two components for ContextProviders.
// This variable will typically get overridden (shadowed) within slotted content.
const contextfulParent = instance;
const isLightDom = Cmp.renderMode === 'light';
if (!isLightDom) {
yield \`<template shadowrootmode="open"\${Cmp.delegatesFocus ? ' shadowrootdelegatesfocus' : ''}>\`
}
const { stylesheets: staticStylesheets } = Cmp;
if (defaultStylesheets || defaultScopedStylesheets || staticStylesheets) {
yield renderStylesheets(
$$emit,
defaultStylesheets,
defaultScopedStylesheets,
staticStylesheets,
stylesheetScopeToken,
Cmp,
hasScopedStylesheets,
);
}
${is.statement};
if (!isLightDom) {
yield '</template>';
if (shadowSlottedContent) {
// instance must be passed in; this is used to establish the contextful relationship
// between context provider (aka parent component) and context consumer (aka slotted content)
yield* shadowSlottedContent(contextfulParent);
}
}
}
`<EsExportDefaultDeclaration & { declaration: FunctionDeclaration }>;
export default function compileTemplate(
src: string,
filename: string,
options: TemplateCompilerConfig,
compilationMode: CompilationMode
) {
const { root, warnings } = parse(src, {
// `options` is from @lwc/compiler, and may have flags that @lwc/template-compiler doesn't
// know about, so we must explicitly extract the relevant props.
name: options.name,
namespace: options.namespace,
customRendererConfig: options.customRendererConfig,
experimentalComputedMemberExpression: options.experimentalComputedMemberExpression,
experimentalComplexExpressions: options.experimentalComplexExpressions,
enableDynamicComponents: options.enableDynamicComponents,
enableLwcOn: options.enableLwcOn,
preserveHtmlComments: options.preserveHtmlComments,
enableStaticContentOptimization: options.enableStaticContentOptimization,
instrumentation: options.instrumentation,
apiVersion: options.apiVersion,
disableSyntheticShadowSupport: options.disableSyntheticShadowSupport,
// TODO [#3331]: remove usage of lwc:dynamic in 246
experimentalDynamicDirective: options.experimentalDynamicDirective,
});
if (!root || warnings.length) {
for (const warning of warnings) {
// eslint-disable-next-line no-console
console.error('Cannot compile:', warning.message);
}
// The legacy SSR implementation would not bail from compilation even if a
// DiagnosticLevel.Fatal error was encountered. It would only fail if the
// template parser failed to return a root node. That behavior is duplicated
// here.
if (!root) {
throw new Error('Template compilation failure; see warnings in the console.');
}
}
const preserveComments = !!root.directives.find(
(directive) => directive.name === 'PreserveComments'
)?.value?.value;
const experimentalComplexExpressions = Boolean(options.experimentalComplexExpressions);
const { addImport, getImports, statements } = templateIrToEsTree(root, {
preserveComments,
experimentalComplexExpressions,
});
addImport(['renderStylesheets', 'hasScopedStaticStylesheets']);
for (const [imports, source] of getStylesheetImports(filename)) {
addImport(imports, source);
}
let tmplDecl = bExportTemplate(optimizeAdjacentYieldStmts(statements));
// Ideally, we'd just do ${LWC_VERSION_COMMENT} in the code template,
// but placeholders have a special meaning for `esTemplate`.
tmplDecl = produce(tmplDecl, (draft) => {
draft.declaration.body.trailingComments = [
{
type: 'Block',
value: LWC_VERSION_COMMENT,
},
];
});
let program = b.program([...getImports(), tmplDecl], 'module');
addScopeTokenDeclarations(program, filename, options.namespace, options.name);
if (compilationMode === 'async' || compilationMode === 'sync') {
program = transmogrify(program, compilationMode);
}
return {
code: generate(program, {
// The generated AST doesn't have comments; this just preserves the LWC version comment
comments: true,
}),
};
}