Skip to content

Commit

Permalink
Tree-shakes side effect free string methods on template literals (rol…
Browse files Browse the repository at this point in the history
  • Loading branch information
lukastaegert authored and pos777 committed Jun 18, 2022
1 parent 1722594 commit 7b4c552
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 6 deletions.
4 changes: 0 additions & 4 deletions src/ast/nodes/Literal.ts
Expand Up @@ -57,10 +57,6 @@ export default class Literal<T extends LiteralValue = LiteralValue> extends Node
return path.length > 1;
}

hasEffectsWhenAssignedAtPath(path: ObjectPath): boolean {
return path.length > 0;
}

hasEffectsWhenCalledAtPath(
path: ObjectPath,
callOptions: CallOptions,
Expand Down
38 changes: 37 additions & 1 deletion src/ast/nodes/TemplateLiteral.ts
@@ -1,23 +1,59 @@
import type MagicString from 'magic-string';
import type { RenderOptions } from '../../utils/renderHelpers';
import { CallOptions } from '../CallOptions';
import { HasEffectsContext } from '../ExecutionContext';
import type { ObjectPath } from '../utils/PathTracker';
import {
getMemberReturnExpressionWhenCalled,
hasMemberEffectWhenCalled,
literalStringMembers
} from '../values';
import type * as NodeType from './NodeType';
import type TemplateElement from './TemplateElement';
import { type LiteralValueOrUnknown, UnknownValue } from './shared/Expression';
import {
ExpressionEntity,
type LiteralValueOrUnknown,
UNKNOWN_EXPRESSION,
UnknownValue
} from './shared/Expression';
import { type ExpressionNode, NodeBase } from './shared/Node';

export default class TemplateLiteral extends NodeBase {
declare expressions: ExpressionNode[];
declare quasis: TemplateElement[];
declare type: NodeType.tTemplateLiteral;

deoptimizeThisOnEventAtPath(): void {}

getLiteralValueAtPath(path: ObjectPath): LiteralValueOrUnknown {
if (path.length > 0 || this.quasis.length !== 1) {
return UnknownValue;
}
return this.quasis[0].value.cooked;
}

getReturnExpressionWhenCalledAtPath(path: ObjectPath): ExpressionEntity {
if (path.length !== 1) {
return UNKNOWN_EXPRESSION;
}
return getMemberReturnExpressionWhenCalled(literalStringMembers, path[0]);
}

hasEffectsWhenAccessedAtPath(path: ObjectPath): boolean {
return path.length > 1;
}

hasEffectsWhenCalledAtPath(
path: ObjectPath,
callOptions: CallOptions,
context: HasEffectsContext
): boolean {
if (path.length === 1) {
return hasMemberEffectWhenCalled(literalStringMembers, path[0], callOptions, context);
}
return true;
}

render(code: MagicString, options: RenderOptions): void {
(code.indentExclusionRanges as [number, number][]).push([this.start, this.end]);
super.render(code, options);
Expand Down
2 changes: 1 addition & 1 deletion src/ast/values.ts
Expand Up @@ -190,7 +190,7 @@ const literalNumberMembers: MemberDescriptions = assembleMemberDescriptions(
objectMembers
);

const literalStringMembers: MemberDescriptions = assembleMemberDescriptions(
export const literalStringMembers: MemberDescriptions = assembleMemberDescriptions(
{
anchor: returnsString,

Expand Down
@@ -0,0 +1,3 @@
module.exports = {
description: 'Tree-shake known string template literal prototype functions'
};
@@ -0,0 +1,8 @@
// deep property access is forbidden
`ab`.x.y;

// due to strict mode, extension is forbidden
`ab`.x = 1;

// throws when called
`ab`();
15 changes: 15 additions & 0 deletions test/form/samples/builtin-prototypes/template-literal/main.js
@@ -0,0 +1,15 @@
`ab`.trim();
`ab`.trim().trim();
`ab`.toString().trim();

// property access is allowed
const accessString = `ab`.x;

// deep property access is forbidden
const deepString = `ab`.x.y;

// due to strict mode, extension is forbidden
`ab`.x = 1;

// throws when called
`ab`();

0 comments on commit 7b4c552

Please sign in to comment.