Skip to content

Commit dd77233

Browse files
crisbetothePunderWoman
authored andcommitted
refactor(compiler): integrate regular expression literals into pipeline (#63857)
Adds support for regular expression literals in the template pipeline and adds some logic to optimize non-global ones. PR Close #63857
1 parent f2ef838 commit dd77233

File tree

12 files changed

+178
-1
lines changed

12 files changed

+178
-1
lines changed

packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/value_composition/GOLDEN_PARTIAL.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -841,3 +841,61 @@ export declare class MyApp {
841841
static ɵcmp: i0.ɵɵComponentDeclaration<MyApp, "my-app", never, {}, {}, never, never, true, never>;
842842
}
843843

844+
/****************************************************************************************************
845+
* PARTIAL FILE: regular_expression_without_global_flag.js
846+
****************************************************************************************************/
847+
import { Component } from '@angular/core';
848+
import * as i0 from "@angular/core";
849+
export class TestComp {
850+
constructor() {
851+
this.value = '123';
852+
}
853+
}
854+
TestComp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: TestComp, deps: [], target: i0.ɵɵFactoryTarget.Component });
855+
TestComp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: TestComp, isStandalone: true, selector: "ng-component", ngImport: i0, template: `{{/^hello/i.test(value)}}`, isInline: true });
856+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: TestComp, decorators: [{
857+
type: Component,
858+
args: [{
859+
template: `{{/^hello/i.test(value)}}`,
860+
}]
861+
}] });
862+
863+
/****************************************************************************************************
864+
* PARTIAL FILE: regular_expression_without_global_flag.d.ts
865+
****************************************************************************************************/
866+
import * as i0 from "@angular/core";
867+
export declare class TestComp {
868+
value: string;
869+
static ɵfac: i0.ɵɵFactoryDeclaration<TestComp, never>;
870+
static ɵcmp: i0.ɵɵComponentDeclaration<TestComp, "ng-component", never, {}, {}, never, never, true, never>;
871+
}
872+
873+
/****************************************************************************************************
874+
* PARTIAL FILE: regular_expression_with_global_flag.js
875+
****************************************************************************************************/
876+
import { Component } from '@angular/core';
877+
import * as i0 from "@angular/core";
878+
export class TestComp {
879+
constructor() {
880+
this.value = '123';
881+
}
882+
}
883+
TestComp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: TestComp, deps: [], target: i0.ɵɵFactoryTarget.Component });
884+
TestComp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: TestComp, isStandalone: true, selector: "ng-component", ngImport: i0, template: `{{/^hello/g.test(value)}}`, isInline: true });
885+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: TestComp, decorators: [{
886+
type: Component,
887+
args: [{
888+
template: `{{/^hello/g.test(value)}}`,
889+
}]
890+
}] });
891+
892+
/****************************************************************************************************
893+
* PARTIAL FILE: regular_expression_with_global_flag.d.ts
894+
****************************************************************************************************/
895+
import * as i0 from "@angular/core";
896+
export declare class TestComp {
897+
value: string;
898+
static ɵfac: i0.ɵɵFactoryDeclaration<TestComp, never>;
899+
static ɵcmp: i0.ɵɵComponentDeclaration<TestComp, "ng-component", never, {}, {}, never, never, true, never>;
900+
}
901+

packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/value_composition/TEST_CASES.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,26 @@
277277
"files": ["tagged_template_literals.js"]
278278
}
279279
]
280+
},
281+
{
282+
"description": "should optimize regular expressions without flags",
283+
"inputFiles": ["regular_expression_without_global_flag.ts"],
284+
"expectations": [
285+
{
286+
"failureMessage": "Invalid template",
287+
"files": ["regular_expression_without_global_flag.js"]
288+
}
289+
]
290+
},
291+
{
292+
"description": "should not optimize global regular expressions",
293+
"inputFiles": ["regular_expression_with_global_flag.ts"],
294+
"expectations": [
295+
{
296+
"failureMessage": "Invalid template",
297+
"files": ["regular_expression_with_global_flag.js"]
298+
}
299+
]
280300
}
281301
]
282302
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
function TestComp_Template(rf, ctx) {
2+
if (rf & 1) {
3+
$r3$.ɵɵtext(0);
4+
}
5+
if (rf & 2) {
6+
$r3$.ɵɵtextInterpolate(/^hello/g.test(ctx.value));
7+
}
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import {Component} from '@angular/core';
2+
3+
@Component({
4+
template: `{{/^hello/g.test(value)}}`,
5+
})
6+
export class TestComp {
7+
value = '123';
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
const $_c0$ = /^hello/i;
2+
3+
function TestComp_Template(rf, ctx) {
4+
if (rf & 1) {
5+
$r3$.ɵɵtext(0);
6+
}
7+
if (rf & 2) {
8+
$r3$.ɵɵtextInterpolate($_c0$.test(ctx.value));
9+
}
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import {Component} from '@angular/core';
2+
3+
@Component({
4+
template: `{{/^hello/i.test(value)}}`,
5+
})
6+
export class TestComp {
7+
value = '123';
8+
}

packages/compiler/src/constant_pool.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,8 @@ export class GenericKeyFn implements ExpressionKeyFn {
311311
return `"${expr.value}"`;
312312
} else if (expr instanceof o.LiteralExpr) {
313313
return String(expr.value);
314+
} else if (expr instanceof o.RegularExpressionLiteral) {
315+
return `/${expr.body}/${expr.flags ?? ''}`;
314316
} else if (expr instanceof o.LiteralArrayExpr) {
315317
const entries: string[] = [];
316318
for (const entry of expr.entries) {

packages/compiler/src/template/pipeline/ir/src/expression.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1320,7 +1320,8 @@ export function transformExpressionsInExpression(
13201320
} else if (
13211321
expr instanceof o.ReadVarExpr ||
13221322
expr instanceof o.ExternalExpr ||
1323-
expr instanceof o.LiteralExpr
1323+
expr instanceof o.LiteralExpr ||
1324+
expr instanceof o.RegularExpressionLiteral
13241325
) {
13251326
// No action for these types.
13261327
} else {

packages/compiler/src/template/pipeline/src/emit.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ import {createVariadicPipes} from './phases/pipe_variadic';
6060
import {propagateI18nBlocks} from './phases/propagate_i18n_blocks';
6161
import {extractPureFunctions} from './phases/pure_function_extraction';
6262
import {generatePureLiteralStructures} from './phases/pure_literal_structures';
63+
import {optimizeRegularExpressions} from './phases/regular_expression_optimization';
6364
import {reify} from './phases/reify';
6465
import {removeEmptyBindings} from './phases/remove_empty_bindings';
6566
import {removeI18nContexts} from './phases/remove_i18n_contexts';
@@ -101,6 +102,7 @@ type Phase =
101102

102103
const phases: Phase[] = [
103104
{kind: Kind.Tmpl, fn: removeContentSelectors},
105+
{kind: Kind.Both, fn: optimizeRegularExpressions},
104106
{kind: Kind.Host, fn: parseHostStyleProperties},
105107
{kind: Kind.Tmpl, fn: emitNamespaceChanges},
106108
{kind: Kind.Tmpl, fn: propagateI18nBlocks},

packages/compiler/src/template/pipeline/src/ingest.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1198,6 +1198,8 @@ function convertAst(
11981198
undefined,
11991199
convertSourceSpan(ast.span, baseSourceSpan),
12001200
);
1201+
} else if (ast instanceof e.RegularExpressionLiteral) {
1202+
return new o.RegularExpressionLiteral(ast.body, ast.flags, baseSourceSpan);
12011203
} else {
12021204
throw new Error(
12031205
`Unhandled expression type "${ast.constructor.name}" in file "${baseSourceSpan?.start.file.url}"`,

0 commit comments

Comments
 (0)