{"_id":"@000alen/ts-poet","name":"@000alen/ts-poet","dist-tags":{"latest":"6.9.0"},"versions":{"6.9.0":{"name":"@000alen/ts-poet","version":"6.9.0","description":"code generation DSL for TypeScript","main":"build/index.js","types":"build/index.d.ts","scripts":{"prepare":"yarn build","test":"yarn jest","build":"yarn tsc","format":"prettier --write './src/**/(*.ts|*.tsx|*.html|*.css)'"},"repository":{"type":"git","url":"git+https://github.com/stephenh/ts-poet.git"},"keywords":[],"author":{"name":"Stephen Haberman"},"license":"Apache-2.0","devDependencies":{"@types/jest":"^29.5.11","@types/mock-fs":"^4.13.1","@types/node":"^20.10.6","jest":"^29.7.0","mock-fs":"^5.2.0","prettier":"3.1.1","ts-jest":"^29.1.1","typescript":"^5.3.3"},"packageManager":"yarn@4.0.2","_id":"@000alen/ts-poet@6.9.0","gitHead":"aa180900959f8019126feeab4eda6c1745923d34","bugs":{"url":"https://github.com/stephenh/ts-poet/issues"},"homepage":"https://github.com/stephenh/ts-poet#readme","_nodeVersion":"20.12.2","_npmVersion":"10.9.0","dist":{"integrity":"sha512-FEAZ10Bj1hAdRDpQBkQfEMLWs1Y6Oi8GLOMGBej/4g6DOLWEQUSwNH6xuZYSKRtyC/tDwMHExOZ7zpLKP0wB1Q==","shasum":"c4d7dbe4b3ca4234868604c555dc2aeb235d9af4","tarball":"https://registry.npmjs.org/@000alen/ts-poet/-/ts-poet-6.9.0.tgz","fileCount":27,"unpackedSize":70798,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICMsepBYfvwz4lWNMQrh2u4PLZIlFrkBZ2O50rDamCAzAiBfFap1ThGT5U9VAdrX/GODK6HbRoV4nkLfq4ghAgOTiQ=="}]},"_npmUser":{"name":"000alen","email":"lclc.alen@gmail.com"},"directories":{},"maintainers":[{"name":"000alen","email":"lclc.alen@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ts-poet_6.9.0_1730836241693_0.30059994140392354"},"_hasShrinkwrap":false}},"time":{"created":"2024-11-05T19:50:41.582Z","6.9.0":"2024-11-05T19:50:41.891Z","modified":"2024-11-05T19:50:42.178Z"},"maintainers":[{"name":"000alen","email":"lclc.alen@gmail.com"}],"description":"code generation DSL for TypeScript","homepage":"https://github.com/stephenh/ts-poet#readme","keywords":[],"repository":{"type":"git","url":"git+https://github.com/stephenh/ts-poet.git"},"author":{"name":"Stephen Haberman"},"bugs":{"url":"https://github.com/stephenh/ts-poet/issues"},"license":"Apache-2.0","readme":"![npm](https://img.shields.io/npm/v/ts-poet)\n[![CircleCI](https://circleci.com/gh/stephenh/ts-poet.svg?style=svg)](https://circleci.com/gh/stephenh/ts-poet)\n\nOverview\n========\n\nts-poet is a TypeScript code generator that is a small wrapper around [template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals).\n\nIt lets you generate code as \"just strings\" (no need to tediously create a low-level AST), but then also:\n\n1. \"Auto imports\" only the types/symbols that are actually used in the output\n\n   I.e. you use `const Foo = imp(\"Foo@foo\")` to define the imports you need in your generated code, use them like `function printFoo(foo: ${Foo}): void` in your `code` template strings, and then ts-poet creates the entire import stanza of `import { Foo } from foo` at the top of your generated output.\n\n   This freedom, to not worry about what symbols you do/do not need make \"import ...\" lines for, lets you break up/decompose your code generation logic, so that you can have multiple levels of helper methods/etc. that can return `code` template literals that embed both the code itself and the necessary type imports.\n\n   And when the final file is generated, ts-poet will collect and emit the necessary imports.\n\n2. Automatically avoids import symbol collisions.\n\n   If you're generating types from an external schema, you'll occassionally run into naming conflicts where the external schema has a name that conflicts with your library's own internal name.\n\n   Like the external schema has a `Message` table/resource, so we want to generate a `class Message { ... }` type, but `Message` is also the name of a symbol used by our library itself, i.e. `import { Message } from runtime-library` for doing like `Message.encode` or `Message.decode` calls as part of our implementation.\n\n   With ts-poet, if you declare the symbols in your generated output as `class ${def(\"Message\")}`, ts-poet will give namespace preference to those definitions, and automatically rewrite the `Message` import to `import { Message as Message1 } from runtime-library`, both at the declaration site, as well as the usage sites, i.e. `${Message}.encode(...)` in the generated output will automatically become `Message1.encode(...)` to reflect the rewritten symbol name.\n\n3. Includes any other conditional output (see later), as/if needed.\n\nExample\n=======\n\nHere's some example `HelloWorld` output generated by ts-poet:\n\n```typescript\nimport { Observable } from \"rxjs/Observable\";\n\nexport class Greeter {\n  private name: string;\n\n  constructor(private name: string) {}\n\n  greet(): Observable<string> {\n    return Observable.from(`Hello $name`);\n  }\n}\n```\n\nAnd this is the code to generate it with ts-poet:\n\n```typescript\nimport { code, imp } from \"ts-poet\";\n\n// Use `imp` to declare an import that will conditionally auto-imported\nconst Observable = imp(\"Observable@rxjs\");\n\n// Optionally create helper consts/methods to better organize\n// the code generator logic\nconst greetMethod = code`\n  greet(): ${Observable}<string> {\n    return ${Observable}.from(\\`Hello \\${this.name}\\`);\n  }\n`;\n\n// Combine all of the output (note no imports are at the top, they'll be auto-added)\nconst greeter = code`\n  export class Greeter {\n    private name: string;\n    constructor(name: string) {\n      this.name = name;\n    }\n    ${greetMethod}\n  }\n`;\n\n// Generate the full output, with imports\nconst output = greeter.toString();\n```\n\nImport Specs\n============\n\nGiven the primary goal of ts-poet is managing imports, there are several ways of specifying imports via the `imp` function:\n\n* `imp(\"Observable@rxjs\")` --> `import { Observable } from \"rxjs\"` (named import)\n* `imp(\"Observable@./Api\")` --> `import { Observable } from \"./Api\"`\n* `imp(\"Observable:Obs@rxjs\")` --> `import { Observable as Obs } from \"rxjs\"` (renamed import)\n* `imp(\"t:Observable@rxjs\")` --> `import type { Observable } from \"rxjs\"` (type import)\n* `imp(\"t:Observable:Obs@rxjs\")` --> `import type { Observable as Obs } from \"rxjs\"`\n* `imp(\"api*./Api\")` --> `import * as api from \"./Api\"` (namespace import)\n* `imp(\"api=./Api\")` --> `import api from \"./Api\"` (default import)\n* `imp(\"describe+mocha\")` --> `import \"mocha\"` (implicit import)\n\n### Avoiding Import Conflicts\n\nSometimes code generation output may declare a symbol that conflicts with an imported type (usually for generic names like `Error`).\n\nts-poet will automatically detect and avoid conflicts if you tell it which symbols you're declaring, i.e.:\n\n```typescript\nconst bar = imp('Bar@./bar');\nconst output = code`\n  class ${def(\"Bar\")} extends ${bar} {\n     ...\n  }\n`;\n```\n\nWill result in the imported `Bar` symbol being remapped to `Bar1` in the output:\n\n```typescript\nimport { Bar as Bar1 } from \"./bar\";\nclass Bar extends Bar1 {}\n```\n\nThis 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).\n\n# saveFiles\n\nIf you're generating multiple files, ts-poet provides a `saveFiles` command that can help with conditional output, i.e.:\n\n```typescript\nconst author = {\n  name: \"Author.ts\",\n  contents: \"class Author extends AuthorCodegen {}\",\n  overwrite: false,\n};\nconst authorCodegen = {\n   name: \"AuthorCodegen.ts\",\n   contents: \"class AuthorCodegen {}\",\n   overwrite: true,\n};\nawait saveFiles({\n directory: \"./src/entities\",\n files: [author, authorCodegen]\n});\n\n```\n# Conditional Output\n\nSometimes 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).\n\nts-poet supports this with a `conditionalOutput` method:\n\n```typescript\nconst convertTimestamps = conditionalOutput(\n  // The string to output at the usage site\n  \"convertTimestamps\",\n  // The code to conditionally output if convertTimestamps is used\n  code`function convertTimestamps() { ...impl... }`,\n);\n\nconst output = code`\n  ${someSchema.map(f => {\n    if (f.type === \"timestamp\") {\n      // Using the convertTimestamps const marks it as used in our output\n      return code`${convertTimestamps}(f)`;\n    }\n  })}\n  // The .ifUsed result will be empty unless `convertTimestamps` has been marked has used\n  ${convertTimestamps.ifUsed}\n`;\n```\n\nAnd your output will have the `convertTimestamps` declaration only if one of the schema fields had a `timestamp` type.\n\nThis helps cut down on unnecessary output in the code, and compiler/IDE warnings like unused functions.\n\n# Literals\n\nIf you want to add a literal value, you can use `literalOf` and `arrayOf`:\n\n| code                              | output                    |\n| --------------------------------- | ------------------------- |\n| `let a = ${literalOf('foo')}`     | `let a = 'foo';`          |\n| `let a = ${arrayOf(1, 2, 3)}`     | `let a = [1, 2, 3];`      |\n| `let a = ${{foo: 'bar'}}`         | `let a = { foo: 'bar' };` |\n| `` let a = ${{foo: code`bar`}} `` | `let a = { foo: bar };`   |\n\n# esModuleInterop Support\n\nUnfortunately some dependencies need different imports based on your project's `esModuleInterop` setting.\n\nFor example, with protobufjs, the `Reader` symbol is imported differently:\n\n```typescript\n// With esModuleInterop: true, need to use default import\n// import m1 from \"protobufjs\"\n// let r1: m1.Reader = ...\nconst r1 = imp(\"Reader@protobufjs\")\n\n// With esModuleInterop: false, need to use module star import\n// import * as m1 from \"protobufjs\"\n// let r1: m1.Reader = ...\nconst r2 = imp(\"Reader@protobufjs\")\n```\n\nFor these scenarios, you can use `forceDefaultImport` or `forceModuleImport`:\n\n```ts\nconst Reader = imp(\"Reader@protobufjs\")\nconst c = code`let r1: ${Reader} = ...`\nconst esModuleInterop = fromYourConfig();\nconsole.log(c.toString(\n  esModuleInterop\n    ? { forceDefaultImport: [\"protobufjs\"] }\n    : { forceModuleImport: [\"protobufjs\"] }\n));\n```\n\nThis is most useful for frameworks that generate code, and have to support downstream projects that might have either `esModuleInterop` setting.\n\nSimilarly, you can force the [import require](https://www.typescriptlang.org/docs/handbook/modules.html#export--and-import--require) syntax, to support projects that use the pre-default exports `export =` typing syntax, to export a single symbol:\n\n```ts\nconst Long = imp(\"Long=long\")\nconsole.log(c.toString({ forceRequireImport: [\"long\"] }));\n// outputs import Long = require(\"long\")\n```\n\nHistory\n=======\n\nts-poet was originally inspired by Square's [JavaPoet](https://github.com/square/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.\n\nJavaPoet's approach worked very well for the Java ecosystem, as it was providing three features:\n \n1. nice formatting (historically code generation output has looked terrible; bad formatting, bad indentation, etc.)\n2. nice multi-line string support, via `appendLine(...).appendLine(...)` style methods.\n3. \"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.\n\nHowever, 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.\n\n","readmeFilename":"README.md"}