GithubHelp home page GithubHelp logo

yeejone / ts-poet Goto Github PK

View Code? Open in Web Editor NEW

This project forked from stephenh/ts-poet

1.0 0.0 0.0 618 KB

A code generator DSL for typescript

License: Apache License 2.0

JavaScript 1.25% TypeScript 98.75%

ts-poet's Introduction

npm CircleCI

Overview

ts-poet is a TypeScript code generator that is a fancy wrapper around template literals.

Here's some example HelloWorld output generated by ts-poet:

import { Observable } from "rxjs/Observable";

export class Greeter {
  private name: string;

  constructor(private name: string) {}

  greet(): Observable<string> {
    return Observable.from(`Hello $name`);
  }
}

And this is the code to generate it with ts-poet:

import { code, imp } from "ts-poet";

// Use `imp` to declare an import that will conditionally auto-imported
const Observable = imp("@rxjs/Observable");

// Optionally create helper consts/methods to incrementally create output 
const greet = code`
  greet(): ${Observable}<string> {
    return ${Observable}.from(\`Hello $name\`);
  }
`;

// Combine all of the output (note no imports are at the top, they'll be auto-added)
const greeter = code`
export class Greeter {

  private name: string;

  constructor(private name: string) {
  }

  ${greet}
}
`;

// Generate the full output, with imports
const output = greeter.toStringWithImports("Greeter");

I.e. the primary value provided by ts-poet is:

  1. "Auto import" only actually-used symbols

I.e. if you use imp to define the modules/imports you need in your generated code, ts-poet will create the import stanza at the top of the file.

This can seem fairly minor, but it facilitates decomposition of your code generation code, so that you can have multiple levels of helper methods/etc. that can return code template literals that embed both the code itself as well as the import types.

And when the final file is generated, ts-poet will collect and emit the necessary imports.

  1. Includes any other conditional output (see later), as/if needed.

  2. Formats the output with prettier (using your local .prettierrc if it exists)

Import Specs

Given the primary goal of ts-poet is to manage imports for you, there are several ways of specifying imports to the imp function:

  • imp("Observable@rxjs") --> import { Observable } from "rxjs"
  • imp("Observable:CustomizedObservable@rxjs") --> import { Observable as CustomizedObservable } from "rxjs"
  • imp("t:Observable@rxjs") --> import type { Observable } from "rxjs"
  • imp("t:Observable:CustomizedObservable@rxjs") --> import type { Observable as CustomizedObservable } from "rxjs"
  • imp("Observable@./Api") --> import { Observable } from "./Api"
  • imp("Observable*./Api") --> import * as Observable from "./Api"
  • imp("Observable=./Api") --> import Observable from "./Api"
  • imp("@rxjs/Observable") --> import { Observable } from "rxjs/Observable"
  • imp("*rxjs/Observable") --> import * as Observable from "rxjs/Observable"
  • imp("@Api") --> import { Api } from "Api"
  • imp("describe+mocha") --> import "mocha"

Avoiding Import Conflicts

Sometimes code generation output may declare a symbol that conflicts with an imported type (usually for generic names like Error).

ts-poet will automatically detect and avoid conflicts if you tell it which symbols you're declaring, i.e.:

const bar = imp('Bar@./bar');
const output = code`
  class ${def("Bar")} extends ${bar} {
     ...
  }
`;

Will result in the imported Bar symbol being remapped to Bar1 in the output:

import { Bar as Bar1 } from "./bar";
class Bar extends Bar1 {}

This is an admittedly contrived example for documentation purposes, but can be really useful when generating code against arbitrary / user-defined input (i.e. a schema that happens to uses a really common term).

Conditional Output

Sometimes when generating larger, intricate output, you want to conditionally include helper methods. I.e. have a convertTimestamps function declared at the top of your module, but only actually include that function if some other part of the output actually uses timestamps (which might depend on the specific input/schema you're generating code against).

ts-poet supports this with a conditionalOutput method:

const convertTimestamps = conditionalOutput(
  // The string to output at the usage site
  "convertTimestamps",
  // The code to conditionally output if convertTimestamps is used
  code`function convertTimestamps() { ...impl... }`,
);

const output = code`
  ${someSchema.map(f => {
    if (f.type === "timestamp") {
      // Using the convertTimestamps const marks it as used in our output
      return code`${convertTimestamps}(f)`;
    }
  })}
  // The .ifUsed result will be empty unless `convertTimestamps` has been marked has used
  ${convertTimestamps.ifUsed}
`;

And your output will have the convertTimestamps declaration only if one of the schema fields had a timestamp type.

This helps cut down on unnecessary output in the code, and compiler/IDE warnings like unused functions.

Literals

If you want to add a literal value, you can use literalOf and arrayOf:

code output
let a = ${literalOf('foo')} let a = 'foo';
let a = ${arrayOf(1, 2, 3)} let a = [1, 2, 3];
let a = ${{foo: 'bar'}} let a = { foo: 'bar' };
let a = ${{foo: code`bar`}} let a = { foo: bar };

History

ts-poet was originally inspired by Square's JavaPoet code generation DSL, which has a very "Java-esque" builder API of addFunction/addProperty/etc. that ts-poet copied in it's original v1/v2 releases.

JavaPoet's approach worked very well for the Java ecosystem, as it was providing three features:

  1. nice formatting (historically code generation output has looked terrible; bad formatting, bad indentation, etc.)
  2. nice multi-line string support, via appendLine(...).appendLine(...) style methods.
  3. "auto organize imports", of collecting imported symbols across the entire compilation unit of output, and organizing/formatting them at the top of the output file.

However, in the JavaScript/TypeScript world we have prettier for formatting, and nice multi-line string support via template literals, so really the only value add that ts-poet needs to provide is the "auto organize imports", which is what the post-v2/3.0 API has been rewritten (and dramatically simplified as a result) to provide.

ts-poet's People

Contributors

dependabot[bot] avatar kdubb avatar ntkoopman avatar stephenh avatar stezu avatar v-gjy avatar yeejone avatar

Stargazers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.