|
|
|
@ -1,4 +1,5 @@
|
|
|
|
/** @import { AST } from '#compiler' */
|
|
|
|
/** @import { AST } from '#compiler' */
|
|
|
|
|
|
|
|
/** @import { Expression } from 'estree' */
|
|
|
|
/** @import { AnalysisState, Context } from '../../types' */
|
|
|
|
/** @import { AnalysisState, Context } from '../../types' */
|
|
|
|
import * as e from '../../../../errors.js';
|
|
|
|
import * as e from '../../../../errors.js';
|
|
|
|
import { get_attribute_expression, is_expression_attribute } from '../../../../utils/ast.js';
|
|
|
|
import { get_attribute_expression, is_expression_attribute } from '../../../../utils/ast.js';
|
|
|
|
@ -15,10 +16,67 @@ import { mark_subtree_dynamic } from './fragment.js';
|
|
|
|
* @param {Context} context
|
|
|
|
* @param {Context} context
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
export function visit_component(node, context) {
|
|
|
|
export function visit_component(node, context) {
|
|
|
|
node.metadata.snippets = [];
|
|
|
|
// link this node to all the snippets that it could render, so that we can prune CSS correctly
|
|
|
|
|
|
|
|
node.metadata.snippets = new Set();
|
|
|
|
|
|
|
|
|
|
|
|
// TODO populate node.metadata.snippets with all the locally-defined snippets that this
|
|
|
|
let resolved = true;
|
|
|
|
// could refer to, and create bidirectional links
|
|
|
|
|
|
|
|
|
|
|
|
for (const attribute of node.attributes) {
|
|
|
|
|
|
|
|
/** @type {Expression | undefined} */
|
|
|
|
|
|
|
|
let expression;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (attribute.type === 'SpreadAttribute' || attribute.type === 'BindDirective') {
|
|
|
|
|
|
|
|
resolved = false;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (attribute.type !== 'Attribute' || attribute.value === true) {
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (Array.isArray(attribute.value)) {
|
|
|
|
|
|
|
|
if (attribute.value.length === 1 && attribute.value[0].type === 'ExpressionTag') {
|
|
|
|
|
|
|
|
expression = attribute.value[0].expression;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
expression = attribute.value.expression;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!expression) continue;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (expression.type === 'Identifier') {
|
|
|
|
|
|
|
|
const binding = context.state.scope.get(expression.name);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
|
|
|
binding &&
|
|
|
|
|
|
|
|
binding.declaration_kind !== 'import' &&
|
|
|
|
|
|
|
|
binding.kind !== 'prop' &&
|
|
|
|
|
|
|
|
binding.kind !== 'rest_prop' &&
|
|
|
|
|
|
|
|
binding.kind !== 'bindable_prop' &&
|
|
|
|
|
|
|
|
binding.initial?.type !== 'SnippetBlock'
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
resolved = false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (binding?.initial?.type === 'SnippetBlock') {
|
|
|
|
|
|
|
|
node.metadata.snippets.add(binding.initial);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
// we can't safely know which snippets this component could render,
|
|
|
|
|
|
|
|
// so we deopt. this _could_ result in unused CSS not being discarded
|
|
|
|
|
|
|
|
resolved = false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (resolved) {
|
|
|
|
|
|
|
|
for (const child of node.fragment.nodes) {
|
|
|
|
|
|
|
|
if (child.type === 'SnippetBlock') {
|
|
|
|
|
|
|
|
node.metadata.snippets.add(child);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
context.state.analysis.snippet_renderers.set(node, resolved);
|
|
|
|
|
|
|
|
|
|
|
|
mark_subtree_dynamic(context.path);
|
|
|
|
mark_subtree_dynamic(context.path);
|
|
|
|
|
|
|
|
|
|
|
|
|