Skip to content

Instantly share code, notes, and snippets.

View phenomnomnominal's full-sized avatar
🥑
Hangry

Craig Spence phenomnomnominal

🥑
Hangry
View GitHub Profile
@phenomnomnominal
phenomnomnominal / transform-value.ts
Created May 30, 2019 08:25
Final version of the @value decorator transform, which converts the decorator to static code.
// Dependencies:
import { tsquery } from '@phenomnomnominal/tsquery';
import { tstemplate } from '@phenomnomnominal/tstemplate';
import { createIdentifier, createLiteral, createToken, SyntaxKind, visitEachChild, visitNode } from 'typescript';
import { Node, PropertyDeclaration, SourceFile, TransformationContext, Transformer, TransformerFactory } from 'typescript';
// Constants:
const CAST_PROPERTIES_QUERY = `PropertyDeclaration:has(Decorator:has(Identifier[name="Value"]))`;
const RESULT_QUERY = 'PropertyDeclaration, GetAccessor, SetAccessor';
const OTHER_DECORATORS_QUERY = `Decorator:has(Decorator > CallExpression[expression.name!="Value"])`;
import { Component, Input } from '@angular/core';
import { Value, isBool, isNotNull } from '@trademe/ensure';
@Component({
selector: 'hello'
// ...
})
export class HelloComponent {
private _primary: boolean;
@Input()
@phenomnomnominal
phenomnomnominal / transform-value.v5.ts
Created May 30, 2019 08:16
Fifth version of our transform, which moves the casting calls from the decorator to the getter and setter
// Constants:
// ...
const CASTERS_QUERY = 'CallExpression[expression.name!="Value"], CallExpression[expression.name="Value"] > Identifier[name!="Value"], CallExpression[expression.name="Value"] > ArrayLiteralExpression > Identifier[name!="Value"]';
const CAST_RESULT_EXPRESSION_QUERY = 'ExpressionStatement';
const GETTER_CAST_QUERY = 'Identifier[name="isNotNull"]';
const CAST_CALL_TEMPLATE = tstemplate.compile(`
val = <%= name %>(val, <%= propertyName %>);
`);
@phenomnomnominal
phenomnomnominal / transform-value.v4.ts
Last active May 30, 2019 08:14
Fourth version of our transform, which keeps all other decorators
// Constants:
// ...
const OTHER_DECORATORS_QUERY = `Decorator:has(Decorator > CallExpression[expression.name!="Value"])`;
export function transformer (source: SourceFile): TransformerFactory<Node> {
// ...
}
export function valueDecoratorToGetterAndSetterFactory (nodes: Array<Node>): TransformerFactory<Node> {
return function (context: TransformationContext): Transformer<Node> {
import { Component, Input } from '@angular/core';
import { Value, isBool, isNotNull } from '@trademe/ensure';
@Component({
selector: 'hello'
// ...
})
export class HelloComponent {
private _primary: boolean;
public get primary (): boolean {
@phenomnomnominal
phenomnomnominal / transform-value.v3.ts
Last active May 30, 2019 07:54
Third version of our transform, which creates the new AST nodes for the getter and setter
// Dependencies:
// ...
import { tstemplate } from '@phenomnomnominal/tstemplate';
// Constants:
// ...
const RESULT_QUERY = 'PropertyDeclaration, GetAccessor, SetAccessor';
const GETTER_SETTER_TEMPLATE = tstemplate.compile(`
class Template {
@phenomnomnominal
phenomnomnominal / transform-value.v2.ts
Last active May 30, 2019 07:16
Second version of our transform, which adds a filter for only Value decorator nodes!
// Dependencies:
import { tsquery } from '@phenomnomnominal/tsquery';
import { Node, visitNode, SourceFile, TransformationContext, Transformer, TransformerFactory } from 'typescript';
// Constants:
const CAST_PROPERTIES_QUERY = `PropertyDeclaration:has(Decorator:has(Identifier[name="Value"]))`;
export function transformer (source: SourceFile): TransformerFactory<Node> {
const castProperties = tsquery(source, CAST_PROPERTIES_QUERY);
return valueDecoratorToGetterAndSetterFactory(castProperties);
@phenomnomnominal
phenomnomnominal / transform-value.v1.ts
Created May 30, 2019 06:56
First version of our transform, which looks at each node and does nothing!
// Dependencies:
import { Node, visitNode, SourceFile, TransformationContext, Transformer, TransformerFactory } from 'typescript';
export function transformer (source: SourceFile): TransformerFactory<Node> {
return function (context: TransformationContext): Transformer<Node> {
return function (node: Node): Node {
return visitNode(node, visit);
};
function visit (node: Node): Node | Array<Node> {
import { createPrinter, transform, SourceFile} from 'typescript';
import { transformer } from './transformer';
function transformFile (file: SourceFile): string {
const result = transform(file, [transformer(file)]);
const [transformed] = result.transformed;
const printer = createPrinter();
return printer.printFile(transformed as SourceFile);
}
import * as tslib_1 from "tslib";
import { Component, Input } from "@angular/core";
import { Value, isBool, isNotNull } from "@trademe/ensure";
var HelloComponent = (function () {
function HelloComponent() { }
HelloComponent.decorators = [{
type: Component,
args: [{
selector: "hello"