fix(compiler): emit quoted object literal keys if the source is quoted
feat(tsc-wrapped): recored when to quote a object literal key Collecting quoted literals is off by default as it introduces a breaking change in the .metadata.json file. A follow-up commit will address this. Fixes #13249 Closes #13356
This commit is contained in:

committed by
Victor Berchet

parent
f238c8ac7a
commit
dd0519abad
@ -13,11 +13,22 @@ import {ClassMetadata, ConstructorMetadata, FunctionMetadata, MemberMetadata, Me
|
||||
import {Symbols} from './symbols';
|
||||
|
||||
|
||||
/**
|
||||
* A set of collector options to use when collecting metadata.
|
||||
*/
|
||||
export class CollectorOptions {
|
||||
/**
|
||||
* Collect a hidden field "$quoted$" in objects literals that record when the key was quoted in
|
||||
* the source.
|
||||
*/
|
||||
quotedNames?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect decorator metadata from a TypeScript module.
|
||||
*/
|
||||
export class MetadataCollector {
|
||||
constructor() {}
|
||||
constructor(private options: CollectorOptions = {}) {}
|
||||
|
||||
/**
|
||||
* Returns a JSON.stringify friendly form describing the decorators of the exported classes from
|
||||
@ -26,7 +37,7 @@ export class MetadataCollector {
|
||||
public getMetadata(sourceFile: ts.SourceFile, strict: boolean = false): ModuleMetadata {
|
||||
const locals = new Symbols(sourceFile);
|
||||
const nodeMap = new Map<MetadataValue|ClassMetadata|FunctionMetadata, ts.Node>();
|
||||
const evaluator = new Evaluator(locals, nodeMap);
|
||||
const evaluator = new Evaluator(locals, nodeMap, this.options);
|
||||
let metadata: {[name: string]: MetadataValue | ClassMetadata | FunctionMetadata}|undefined;
|
||||
let exports: ModuleExportMetadata[];
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {CollectorOptions} from './collector';
|
||||
import {MetadataEntry, MetadataError, MetadataGlobalReferenceExpression, MetadataImportedSymbolReferenceExpression, MetadataSymbolicCallExpression, MetadataSymbolicReferenceExpression, MetadataValue, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataImportedSymbolReferenceExpression, isMetadataModuleReferenceExpression, isMetadataSymbolicReferenceExpression, isMetadataSymbolicSpreadExpression} from './schema';
|
||||
import {Symbols} from './symbols';
|
||||
|
||||
@ -97,7 +98,9 @@ export function errorSymbol(
|
||||
* possible.
|
||||
*/
|
||||
export class Evaluator {
|
||||
constructor(private symbols: Symbols, private nodeMap: Map<MetadataEntry, ts.Node>) {}
|
||||
constructor(
|
||||
private symbols: Symbols, private nodeMap: Map<MetadataEntry, ts.Node>,
|
||||
private options: CollectorOptions = {}) {}
|
||||
|
||||
nameOf(node: ts.Node): string|MetadataError {
|
||||
if (node.kind == ts.SyntaxKind.Identifier) {
|
||||
@ -223,11 +226,16 @@ export class Evaluator {
|
||||
switch (node.kind) {
|
||||
case ts.SyntaxKind.ObjectLiteralExpression:
|
||||
let obj: {[name: string]: any} = {};
|
||||
let quoted: string[] = [];
|
||||
ts.forEachChild(node, child => {
|
||||
switch (child.kind) {
|
||||
case ts.SyntaxKind.ShorthandPropertyAssignment:
|
||||
case ts.SyntaxKind.PropertyAssignment:
|
||||
const assignment = <ts.PropertyAssignment|ts.ShorthandPropertyAssignment>child;
|
||||
if (assignment.name.kind == ts.SyntaxKind.StringLiteral) {
|
||||
const name = (assignment.name as ts.StringLiteral).text;
|
||||
quoted.push(name);
|
||||
}
|
||||
const propertyName = this.nameOf(assignment.name);
|
||||
if (isMetadataError(propertyName)) {
|
||||
error = propertyName;
|
||||
@ -245,6 +253,9 @@ export class Evaluator {
|
||||
}
|
||||
});
|
||||
if (error) return error;
|
||||
if (this.options.quotedNames && quoted.length) {
|
||||
obj['$quoted$'] = quoted;
|
||||
}
|
||||
return obj;
|
||||
case ts.SyntaxKind.ArrayLiteralExpression:
|
||||
let arr: MetadataValue[] = [];
|
||||
|
@ -50,7 +50,7 @@ describe('Collector', () => {
|
||||
]);
|
||||
service = ts.createLanguageService(host, documentRegistry);
|
||||
program = service.getProgram();
|
||||
collector = new MetadataCollector();
|
||||
collector = new MetadataCollector({quotedNames: true});
|
||||
});
|
||||
|
||||
it('should not have errors in test data', () => { expectValidSources(service, program); });
|
||||
@ -164,11 +164,16 @@ describe('Collector', () => {
|
||||
version: 2,
|
||||
metadata: {
|
||||
HEROES: [
|
||||
{'id': 11, 'name': 'Mr. Nice'}, {'id': 12, 'name': 'Narco'},
|
||||
{'id': 13, 'name': 'Bombasto'}, {'id': 14, 'name': 'Celeritas'},
|
||||
{'id': 15, 'name': 'Magneta'}, {'id': 16, 'name': 'RubberMan'},
|
||||
{'id': 17, 'name': 'Dynama'}, {'id': 18, 'name': 'Dr IQ'}, {'id': 19, 'name': 'Magma'},
|
||||
{'id': 20, 'name': 'Tornado'}
|
||||
{'id': 11, 'name': 'Mr. Nice', '$quoted$': ['id', 'name']},
|
||||
{'id': 12, 'name': 'Narco', '$quoted$': ['id', 'name']},
|
||||
{'id': 13, 'name': 'Bombasto', '$quoted$': ['id', 'name']},
|
||||
{'id': 14, 'name': 'Celeritas', '$quoted$': ['id', 'name']},
|
||||
{'id': 15, 'name': 'Magneta', '$quoted$': ['id', 'name']},
|
||||
{'id': 16, 'name': 'RubberMan', '$quoted$': ['id', 'name']},
|
||||
{'id': 17, 'name': 'Dynama', '$quoted$': ['id', 'name']},
|
||||
{'id': 18, 'name': 'Dr IQ', '$quoted$': ['id', 'name']},
|
||||
{'id': 19, 'name': 'Magma', '$quoted$': ['id', 'name']},
|
||||
{'id': 20, 'name': 'Tornado', '$quoted$': ['id', 'name']}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
Reference in New Issue
Block a user