feat(change_detection): make INTERPOLATE_REGEXP customizable (#7417)

BREAKING CHANGES:

`Parser` constructor required new parameter `config: CompilerConfig` as second argument.
This commit is contained in:
Suguru Inatomi 2016-05-27 05:08:39 +09:00 committed by Miško Hevery
parent 9036f78b74
commit c3fafa0651
10 changed files with 43 additions and 18 deletions

View File

@ -41,6 +41,7 @@ function _createCompilerConfig() {
*/
export const COMPILER_PROVIDERS: Array<any | Type | {[k: string]: any} | any[]> =
/*@ts2dart_const*/[
/*@ts2dart_Provider*/ {provide: CompilerConfig, useFactory: _createCompilerConfig, deps: []},
Lexer,
Parser,
HtmlParser,
@ -50,7 +51,6 @@ export const COMPILER_PROVIDERS: Array<any | Type | {[k: string]: any} | any[]>
DEFAULT_PACKAGE_URL_PROVIDER,
StyleCompiler,
ViewCompiler,
/*@ts2dart_Provider*/ {provide: CompilerConfig, useFactory: _createCompilerConfig, deps: []},
RuntimeCompiler,
/*@ts2dart_Provider*/ {provide: ComponentResolver, useExisting: RuntimeCompiler},
DomElementSchemaRegistry,

View File

@ -5,12 +5,17 @@ import {CompileIdentifierMetadata} from './compile_metadata';
export class CompilerConfig {
public renderTypes: RenderTypes;
public interpolateRegexp: RegExp;
constructor(public genDebugInfo: boolean, public logBindingUpdate: boolean,
public useJit: boolean, renderTypes: RenderTypes = null) {
public useJit: boolean, renderTypes: RenderTypes = null, interpolateRegexp: RegExp = null) {
if (isBlank(renderTypes)) {
renderTypes = new DefaultRenderTypes();
}
this.renderTypes = renderTypes;
if (isBlank(interpolateRegexp)) {
interpolateRegexp = DEFAULT_INTERPOLATE_REGEXP;
}
this.interpolateRegexp = interpolateRegexp;
}
}
@ -36,3 +41,8 @@ export class DefaultRenderTypes implements RenderTypes {
renderNode = null;
renderEvent = null;
}
/**
* A regexp pattern used to interpolate in default.
*/
export var DEFAULT_INTERPOLATE_REGEXP = /\{\{([\s\S]*?)\}\}/g;

View File

@ -1,4 +1,4 @@
import {Injectable} from '@angular/core';
import {Injectable, Inject} from '@angular/core';
import {isBlank, isPresent, StringWrapper} from '../../src/facade/lang';
import {BaseException} from '../../src/facade/exceptions';
import {ListWrapper} from '../../src/facade/collection';
@ -46,11 +46,10 @@ import {
AstVisitor,
Quote
} from './ast';
import {CompilerConfig} from '../config';
var _implicitReceiver = new ImplicitReceiver();
// TODO(tbosch): Cannot make this const/final right now because of the transpiler...
var INTERPOLATION_REGEXP = /\{\{([\s\S]*?)\}\}/g;
class ParseException extends BaseException {
constructor(message: string, input: string, errLocation: string, ctxLocation?: any) {
@ -69,7 +68,9 @@ export class TemplateBindingParseResult {
@Injectable()
export class Parser {
constructor(/** @internal */
public _lexer: Lexer) {}
public _lexer: Lexer,
/** @internal */
public _config: CompilerConfig) {}
parseAction(input: string, location: any): ASTWithSource {
this._checkNoInterpolation(input, location);
@ -137,7 +138,7 @@ export class Parser {
}
splitInterpolation(input: string, location: string): SplitInterpolation {
var parts = StringWrapper.split(input, INTERPOLATION_REGEXP);
var parts = StringWrapper.split(input, this._config.interpolateRegexp);
if (parts.length <= 1) {
return null;
}
@ -187,7 +188,7 @@ export class Parser {
}
private _checkNoInterpolation(input: string, location: any): void {
var parts = StringWrapper.split(input, INTERPOLATION_REGEXP);
var parts = StringWrapper.split(input, this._config.interpolateRegexp);
if (parts.length > 1) {
throw new ParseException('Got interpolation ({{}}) where expression was expected', input,
`at column ${this._findInterpolationErrorColumn(parts, 1)} in`,

View File

@ -2,11 +2,12 @@ import {ddescribe, describe, it, xit, iit, expect, beforeEach} from '@angular/co
import {isBlank, isPresent} from '../../src/facade/lang';
import {Parser} from '@angular/compiler/src/expression_parser/parser';
import {Unparser} from './unparser';
import {CompilerConfig} from '@angular/compiler';
import {Lexer} from '@angular/compiler/src/expression_parser/lexer';
import {BindingPipe, LiteralPrimitive, AST} from '@angular/compiler/src/expression_parser/ast';
export function main() {
function createParser() { return new Parser(new Lexer()); }
function createParser() { return new Parser(new Lexer(), new CompilerConfig(true, true, true)); }
function parseAction(text, location = null): any {
return createParser().parseAction(text, location);
@ -453,6 +454,14 @@ export function main() {
checkInterpolation(`{{ 'foo' +\n 'bar' +\r 'baz' }}`, `{{ "foo" + "bar" + "baz" }}`);
});
it('should support custom interpolation regexp', () => {
var customParser = new Parser(new Lexer(), new CompilerConfig(true, true, true, null, /<<([\s\S]*?)>>/g));
var ast = (customParser.parseInterpolation('<< a >>', null) as any).ast;
expect(ast.strings).toEqual(['', '']);
expect(ast.expressions.length).toEqual(1);
expect(ast.expressions[0].name).toEqual('a');
});
describe("comments", () => {
it('should ignore comments in interpolation expressions',
() => { checkInterpolation('{{a //comment}}', '{{ a }}'); });

View File

@ -1,6 +1,7 @@
import {describe, expect, it, iit, ddescribe} from "@angular/core/testing/testing_internal";
import {I18nHtmlParser} from "@angular/compiler/src/i18n/i18n_html_parser";
import {Message, id} from "@angular/compiler/src/i18n/message";
import {CompilerConfig} from "@angular/compiler/src/config";
import {Parser} from "@angular/compiler/src/expression_parser/parser";
import {Lexer} from "@angular/compiler/src/expression_parser/lexer";
import {StringMapWrapper} from "../../src/facade/collection";
@ -14,7 +15,7 @@ export function main() {
describe('I18nHtmlParser', () => {
function parse(template: string, messages: {[key: string]: string}, implicitTags: string[] = [],
implicitAttrs: {[k: string]: string[]} = {}): HtmlParseTreeResult {
var parser = new Parser(new Lexer());
var parser = new Parser(new Lexer(), new CompilerConfig(true, true, true));
let htmlParser = new HtmlParser();
let msgs = '';

View File

@ -11,6 +11,7 @@ import {
} from '@angular/core/testing/testing_internal';
import {HtmlParser} from '@angular/compiler/src/html_parser';
import {CompilerConfig} from '@angular/compiler/src/config';
import {MessageExtractor, removeDuplicates} from '@angular/compiler/src/i18n/message_extractor';
import {Message} from '@angular/compiler/src/i18n/message';
import {Parser} from '@angular/compiler/src/expression_parser/parser';
@ -22,7 +23,7 @@ export function main() {
beforeEach(() => {
let htmlParser = new HtmlParser();
var parser = new Parser(new Lexer());
var parser = new Parser(new Lexer(), new CompilerConfig(true, true, true));
extractor = new MessageExtractor(htmlParser, parser, ['i18n-tag'], {'i18n-el': ['trans']});
});

View File

@ -50,10 +50,11 @@ function _createOfflineCompiler(xhr: MockXHR, emitter: OutputEmitter): OfflineCo
xhr.when(`${THIS_MODULE_PATH}/offline_compiler_compa.html`, 'Hello World {{user}}!');
var htmlParser = new HtmlParser();
var normalizer = new DirectiveNormalizer(xhr, urlResolver, htmlParser);
var config = new CompilerConfig(true, true, true);
return new OfflineCompiler(
normalizer, new TemplateParser(new Parser(new Lexer()), new MockSchemaRegistry({}, {}),
normalizer, new TemplateParser(new Parser(new Lexer(), config), new MockSchemaRegistry({}, {}),
htmlParser, new Console(), []),
new StyleCompiler(urlResolver), new ViewCompiler(new CompilerConfig(true, true, true)),
new StyleCompiler(urlResolver), new ViewCompiler(config),
emitter, xhr);
}
@ -79,4 +80,4 @@ export class SimpleJsImportGenerator implements ImportGenerator {
return importedUrlStr;
}
}
}
}

View File

@ -157,12 +157,13 @@ export class CodeGenerator {
StaticAndDynamicReflectionCapabilities.install(staticReflector);
const htmlParser = new HtmlParser();
const normalizer = new DirectiveNormalizer(xhr, urlResolver, htmlParser);
const parser = new Parser(new Lexer());
const config = new compiler.CompilerConfig(true, true, true);
const parser = new Parser(new Lexer(), config);
const tmplParser = new TemplateParser(parser, new DomElementSchemaRegistry(), htmlParser,
/*console*/ null, []);
const offlineCompiler = new compiler.OfflineCompiler(
normalizer, tmplParser, new StyleCompiler(urlResolver),
new ViewCompiler(new compiler.CompilerConfig(true, true, true)),
new ViewCompiler(config),
new TypeScriptEmitter(reflectorHost), xhr);
const resolver = new CompileMetadataResolver(
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),

View File

@ -24,7 +24,7 @@ OfflineCompiler createTemplateCompiler(AssetReader reader,
var _urlResolver = createOfflineCompileUrlResolver();
// TODO(yjbanov): add router AST transformer when ready
var parser = new ng.Parser(new ng.Lexer());
var parser = new ng.Parser(new ng.Lexer(), compilerConfig);
var _htmlParser = _createHtmlParser(translations, parser);
var templateParser = new TemplateParser(

View File

@ -1046,8 +1046,9 @@ const COMPILER = [
'CompileQueryMetadata.selectors:Array<CompileTokenMetadata>',
'CompileQueryMetadata.toJson():{[key:string]:any}',
'CompilerConfig',
'CompilerConfig.constructor(genDebugInfo:boolean, logBindingUpdate:boolean, useJit:boolean, renderTypes:RenderTypes)',
'CompilerConfig.constructor(genDebugInfo:boolean, logBindingUpdate:boolean, useJit:boolean, renderTypes:RenderTypes, interpolateRegexp:RegExp)',
'CompilerConfig.renderTypes:RenderTypes',
'CompilerConfig.interpolateRegexp:RegExp',
'CompileTemplateMetadata',
'CompileTemplateMetadata.animations:CompileAnimationEntryMetadata[]',
'CompileTemplateMetadata.constructor({encapsulation,template,templateUrl,styles,styleUrls,animations,ngContentSelectors}:{encapsulation?:ViewEncapsulation, template?:string, templateUrl?:string, styles?:string[], styleUrls?:string[], ngContentSelectors?:string[], animations?:CompileAnimationEntryMetadata[]})',