/
TaggedTemplateExpression.ts
109 lines (100 loc) · 3.65 KB
/
TaggedTemplateExpression.ts
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
import type MagicString from 'magic-string';
import { errorCannotCallNamespace } from '../../utils/error';
import { type RenderOptions } from '../../utils/renderHelpers';
import type { HasEffectsContext, InclusionContext } from '../ExecutionContext';
import type { NodeInteractionWithThisArgument } from '../NodeInteractions';
import { INTERACTION_CALLED } from '../NodeInteractions';
import type { PathTracker } from '../utils/PathTracker';
import { EMPTY_PATH, SHARED_RECURSION_TRACKER, UNKNOWN_PATH } from '../utils/PathTracker';
import type Identifier from './Identifier';
import MemberExpression from './MemberExpression';
import * as NodeType from './NodeType';
import type TemplateLiteral from './TemplateLiteral';
import CallExpressionBase from './shared/CallExpressionBase';
import type { ExpressionEntity } from './shared/Expression';
import { UNKNOWN_EXPRESSION } from './shared/Expression';
import type { ExpressionNode, IncludeChildren } from './shared/Node';
export default class TaggedTemplateExpression extends CallExpressionBase {
declare quasi: TemplateLiteral;
declare tag: ExpressionNode;
declare type: NodeType.tTaggedTemplateExpression;
bind(): void {
super.bind();
if (this.tag.type === NodeType.Identifier) {
const name = (this.tag as Identifier).name;
const variable = this.scope.findVariable(name);
if (variable.isNamespace) {
this.context.warn(errorCannotCallNamespace(name), this.start);
}
}
}
hasEffects(context: HasEffectsContext): boolean {
try {
for (const argument of this.quasi.expressions) {
if (argument.hasEffects(context)) return true;
}
return (
this.tag.hasEffects(context) ||
this.tag.hasEffectsOnInteractionAtPath(EMPTY_PATH, this.interaction, context)
);
} finally {
if (!this.deoptimized) this.applyDeoptimizations();
}
}
include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void {
if (!this.deoptimized) this.applyDeoptimizations();
if (includeChildrenRecursively) {
super.include(context, includeChildrenRecursively);
} else {
this.included = true;
this.tag.include(context, includeChildrenRecursively);
this.quasi.include(context, includeChildrenRecursively);
}
this.tag.includeCallArguments(context, this.interaction.args);
const returnExpression = this.getReturnExpression();
if (!returnExpression.included) {
returnExpression.include(context, false);
}
}
initialise(): void {
this.interaction = {
args: [UNKNOWN_EXPRESSION, ...this.quasi.expressions],
thisArg: this.tag instanceof MemberExpression && !this.tag.variable ? this.tag.object : null,
type: INTERACTION_CALLED,
withNew: false
};
}
render(code: MagicString, options: RenderOptions): void {
this.tag.render(code, options, { isCalleeOfRenderedParent: true });
this.quasi.render(code, options);
}
protected applyDeoptimizations(): void {
this.deoptimized = true;
if (this.interaction.thisArg) {
this.tag.deoptimizeThisOnInteractionAtPath(
this.interaction as NodeInteractionWithThisArgument,
EMPTY_PATH,
SHARED_RECURSION_TRACKER
);
}
for (const argument of this.quasi.expressions) {
// This will make sure all properties of parameters behave as "unknown"
argument.deoptimizePath(UNKNOWN_PATH);
}
this.context.requestTreeshakingPass();
}
protected getReturnExpression(
recursionTracker: PathTracker = SHARED_RECURSION_TRACKER
): ExpressionEntity {
if (this.returnExpression === null) {
this.returnExpression = UNKNOWN_EXPRESSION;
return (this.returnExpression = this.tag.getReturnExpressionWhenCalledAtPath(
EMPTY_PATH,
this.interaction,
recursionTracker,
this
));
}
return this.returnExpression;
}
}