Skip to content

Commit

Permalink
feat(@angular-devkit/schematics): allow path template to have propert…
Browse files Browse the repository at this point in the history
…y accessors

Path templates now allow dots (".") to be used to access properties.

Fixes angular#12446
  • Loading branch information
hansl committed Oct 2, 2018
1 parent ab1dfd0 commit 734935f
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 1 deletion.
29 changes: 28 additions & 1 deletion packages/angular_devkit/schematics/src/rules/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ import { isBinary } from './utils/is-binary';
export const TEMPLATE_FILENAME_RE = /\.template$/;


export class InvalidPropertyAccessException extends BaseException {
constructor(_name: string, accessor: string) {
super(`Invalid property access in path: ${accessor} is invalid.`);
}
}


export class OptionIsNotDefinedException extends BaseException {
constructor(name: string) { super(`Option "${name}" is not defined.`); }
}
Expand Down Expand Up @@ -46,6 +53,9 @@ export interface PathTemplateOptions {

// Separator for pipes. Do not specify to remove pipe support.
pipeSeparator?: string;

// Separator for property access.
propertySeparator?: string;
}


Expand Down Expand Up @@ -75,6 +85,7 @@ export function applyPathTemplate<T extends PathTemplateData>(
interpolationStart: '__',
interpolationEnd: '__',
pipeSeparator: '@',
propertySeparator: '.',
},
): FileOperator {
const is = options.interpolationStart;
Expand Down Expand Up @@ -105,7 +116,23 @@ export function applyPathTemplate<T extends PathTemplateData>(
}
} else {
const [name, ...pipes] = match.split(options.pipeSeparator);
replacement = data[name];

// Additionally, we check if we can do evaluation of a simple JS path here.
// This only supports `.`. For access in arrays, use `.0` for example for the first
// element. This is to avoid invalid character on some platforms (`[]`).
// The ultimate value is coerced to string below, though.
if (!options.propertySeparator) {
replacement = data[name];
} else {
replacement = name.split(options.propertySeparator)
.reduce((previous, curr) => {
if (typeof previous !== 'object' || previous[curr] === null) {
throw new InvalidPropertyAccessException(curr, name);
}

return previous[curr];
}, data);
}

if (typeof replacement == 'function') {
replacement = replacement.call(data, original);
Expand Down
9 changes: 9 additions & 0 deletions packages/angular_devkit/schematics/src/rules/template_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { FileEntry, MergeStrategy } from '../tree/interface';
import { callRule } from './call';
import {
InvalidPipeException,
InvalidPropertyAccessException,
OptionIsNotDefinedException,
UnknownPipeException,
applyContentTemplate,
Expand Down Expand Up @@ -54,6 +55,9 @@ describe('applyPathTemplate', () => {
expect(_applyPathTemplate('/a/b/__c__/d', { c: 'hello/world' })).toBe('/a/b/hello/world/d');
expect(_applyPathTemplate('/a__c__b', { c: 'hello/world' })).toBe('/ahello/worldb');
expect(_applyPathTemplate('/a__c__b__d__c', { c: '1', d: '2' })).toBe('/a1b2c');
expect(_applyPathTemplate('/a__c.d__b__d__c', { c: { d: '1' }, d: '2' })).toBe('/a1b2c');
expect(_applyPathTemplate('/a__c.d.e.f__b__d__c', { c: { d: { e: { f: '1' } } }, d: '2' }))
.toBe('/a1b2c');
});

it('works with single _', () => {
Expand Down Expand Up @@ -112,6 +116,11 @@ describe('applyPathTemplate', () => {
expect(() => _applyPathTemplate('/a__b@d__c', { b: 1, d: 1 }))
.toThrow(new InvalidPipeException('d'));
});

it('errors invalid accessors', () => {
expect(() => _applyPathTemplate('/a__a.b__c', { a: { b: null } }))
.toThrow(new InvalidPropertyAccessException('a', 'a.b'));
});
});


Expand Down

0 comments on commit 734935f

Please sign in to comment.