{"id":18582106,"url":"https://github.com/lorefnon/ingenr","last_synced_at":"2025-04-10T11:35:59.759Z","repository":{"id":43007842,"uuid":"151983264","full_name":"lorefnon/InGenR","owner":"lorefnon","description":"Lightweight language-agnostic inline code-generation","archived":false,"fork":false,"pushed_at":"2024-09-06T06:58:08.000Z","size":1778,"stargazers_count":19,"open_issues_count":39,"forks_count":7,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-05T00:47:57.066Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lorefnon.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"code-of-conduct.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2018-10-07T20:38:10.000Z","updated_at":"2024-03-07T19:15:08.000Z","dependencies_parsed_at":"2024-02-29T06:42:48.368Z","dependency_job_id":null,"html_url":"https://github.com/lorefnon/InGenR","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lorefnon%2FInGenR","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lorefnon%2FInGenR/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lorefnon%2FInGenR/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lorefnon%2FInGenR/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lorefnon","download_url":"https://codeload.github.com/lorefnon/InGenR/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248208688,"owners_count":21065205,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-07T00:09:14.506Z","updated_at":"2025-04-10T11:35:54.743Z","avatar_url":"https://github.com/lorefnon.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![InGenR](https://raw.githubusercontent.com/lorefnon/InGenR/master/assets/banner.png)](https://github.com/lorefnon/InGenR)\n\n## InGenR (pronounced *in-gen-are*) is a generic utility for inline code generation.\n\nWhen working with large codebases, esp. those involving (one or more) type systems it is often the case that reusing code (while retaining end-to-end type-safety) becomes difficult and repetitive boilerplate is required in some cases to satisfy the type system. Not sure what this means ? Check out the [typescript definitions](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/df80e09009547e5556c09a16f3c715ecf9aff325/types/node/index.d.ts#L6839-L6858) for some node APIs, the [repetitive boilerplate](https://github.com/rtfeldman/elm-css/blob/master/src/Css.elm) in elm-css, etc.\n\nFeatures like [Higher kinded polymorphism](https://sidburn.github.io/blog/2016/03/24/higher-kinded-polymorphism) largely alleviate this problem, but if your language of choice doesn't have such features, then you are pretty much stuck. It is not always feasible or practical to switch to a language with an advanced type system to eliminate redundancy in some parts of your application.\n\nInGenR aims to be a simple generic utility that solves this through a much simpler and crude approach: **code generation**. For many use cases this is a much more practical and simple solution. You can [get started](#how-does-it-work-) in a matter of seconds, or browse  available [features](#features).\n\n## InGenR cares about the developer experience\n\n  - Clear unambiguous error messages.\n\n  - Minimal Configuration: no surprises, no magic.\n\n  - Plays well with the tools (linters, type-checkers, loaders, etc.) which you already have in place.\n\n  It is heavily inspired by [Crystal Macros](https://crystal-lang.org/docs/syntax_and_semantics/macros.html) and [Sinaps](https://github.com/janestreet/cinaps).\n\n## Development Status\n\n:warning: Beta\n\n[![Build Status](https://travis-ci.org/lorefnon/InGenR.svg?branch=master)](https://travis-ci.org/lorefnon/InGenR) \n[![Known Vulnerabilities](https://snyk.io/test/github/lorefnon/InGenR/badge.svg?targetFile=package.json)](https://snyk.io/test/github/lorefnon/InGenR?targetFile=package.json)\n[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Florefnon%2FInGenR.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Florefnon%2FInGenR?ref=badge_shield)\n\n[![Greenkeeper badge](https://badges.greenkeeper.io/lorefnon/InGenR.svg)](https://greenkeeper.io/) \n[![GitHub](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/lorefnon/InGenR/blob/master/LICENSE) \n[![Join the chat at https://gitter.im/InGenR/Lobby](https://badges.gitter.im/InGenR/Lobby.svg)](https://gitter.im/InGenR/Lobby?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge)\n\n\n## How does it work ?\n\n1. **Add InGenR directives to your source files in comment blocks:**\n\n    Eg. In `src/data-layer/users.ts`:\n\n    ```typescript\n    /*! InGenR:expand knex-dal\n    * ---\n    * tableName: users\n    * columns:\n    *   - name: user\n    *     type: string\n    *   - name: email\n    *     type: string\n    */\n    /*! InGenR:end */\n    ```\n\n    An InGenR directive specifies the name of generator (knex-dal) and arguments passed to the generator (in YAML or JSON formats).\n\n2. **Write/install your code generator:**\n\n    The code generator specified by your directive (here `knex-dal`) can either reside locally (in a `\u003cproject-root\u003e/ingenr-generators` directory) or in an npm package.\n\n    A generator can be a simple doT template, eg. In `ingenr-generators/knex-dal.dot`:\n\n    ```dot\n    interface {{= it.interfaceName || it.tableName }} {\n        {{~ it.columns :c}}\n        {{= c.fieldName || c.name }}: {{= c.tsType || c.type }};\n        {{~}}\n    }\n\n    const createTable = () =\u003e\n        knex.schema.createTable(\"{{= it.tableName }}\", (table) =\u003e {\n            table.uuid(\"id\").primary();\n            {{~ it.columns :c}}\n            table.{{= c.colType || c.type}}(\"{{= c.columnName || c.name }}\")\n            {{~}}\n        })\n    ```\n\n    Or a plain javascript module, eg. in `ingenr-generators/knex-dal.js`:\n\n    ```js\n    export default async () =\u003e {\n        const result = await getListOfRowsFromDB();\n        return result.toJSON();\n    }\n    ```\n\n    Note that our generator can be asynchronous and can do anything that is possible through node.js eg. connect to databases, connect to external resources, use your favorite templating library etc. This is powerful :sunglasses: because you can use any language that compiles to javascript and typecheck or test your generators  \n\n    See [configuration](#configuration) below to change the location of the directory where InGenR will look for generators.\n\n    Also this generator doesn't have to be local to your project. If a local generator was not found InGenR will try to require `knex-dal`. This means that you can create and share your generators through npm modules and use them across projects.\n\n3. **Run the generator:**\n\n    ```sh\n    $ npx ingenr run ./src/**/*\n    ```\n\n    Don't already have npx ? Read more [here](https://www.npmjs.com/package/npx). \n\n    InGenR will find all the files with InGenR directives like the above, and expand them in place.\n\n    So after running this, `src/data-layer/users.ts` will contain:\n\n    ```typescript\n    /*! InGenR:expand knex-dal\n     * ---\n     * tableName: users\n     * - columns:\n     *   - name: user\n     *     type: string\n     *   - name: email\n     *     type: string\n     */\n    interface IUser {\n        user: string;\n        email: string;\n    }\n\n    const createTable = () =\u003e\n        knex.schema.createTable(\"users\", (table) =\u003e {\n            table.uuid(\"id\").primary();\n            table.string(\"user\")\n            table.string(\"email\")\n        })\n\n    /*! InGenR:end */\n    ```\n\n    The result of the generator will be injected right into the source file between the InGenR directive comment blocks.\n\n    Note that running the generator again will have no effect. InGenR checks the contents within the `InGenR:expand` and `InGenR:end` blocks and if the content matches what the generator would have generated, nothing will happen. If there is a mismatch - either because the template (or its arguments) have changed or the generated content has been edited manually, InGenR will replace the content within the block entirely.\n\n    You can commit the generated code, and verify the correctness of generated code through any linters, type checkers etc. that you are already familiar with.\n    \n## Features\n\nThe usage outlined above is pretty much all you need to use and get productive with InGenR. \n\nA few additional features are summarized below:\n\n### In place expansion or external targets: \n\nMost of the use cases are served well through in place expansion (result of template is injected within the source file where the directive is present). However, in some cases (eg. when you are dealing with multiple languages, your framework expects a specific directory structure etc.) it is desirable that the directives expand into new files. \n\nThis is possible by specifying `targetFilePath` (relative to path of current file):\n\n```typescript\n/*! InGenR:expand knex-dal\n *\n * targetFilePath: users-table.ts\n * ---\n * tableName: users\n * columns:\n *   - name: name\n *     type: string\n *   - name: email\n *     type: string\n */\n/*! InGenR:end */\n```\n\nThis will populate users-table.ts file with the result of the generator.\n\nNote that the arguments before `---` are arguments to the directive itself, where as the arguments after `---` are passed on to the generator.\n\n### Running multiple generators with same arguments\n\nIt is sometimes convenient to invoke multiple generators with the same set of arguments in a single directive. For this we can provide a comma separated list of generator names. \n\n```typescript\n/*! InGenR:expand foo, bar\n * ---\n * tableName: users\n * columns:\n *   - name: name\n *     type: string\n *   - name: email\n *     type: string\n */\n/*! InGenR:end */\n```\n\n### Passing template arguments through external file\n\n```typescript\n/*! InGenR:expand knex-dal config.yml */\n/*! InGenR:end */\n```\n\nThis is helpful for sharing config among multiple directives.\n\n### Embedded templates\n\nIn some cases, for some very small templates it is convenient to embed the template in place within the comment.\n\nWhile this is available as a convenience feature, it is recommended that larger templates reside in their own files.\n\n```typescript\n/*! InGenR:expand\n* ---\n* name: lorefnon\n* ---\n* \u003cdiv\u003e{{=it.name}}\u003c/div\u003e\n*/\n/*! InGenR:end */\n```\n\n## Caveats\n\n- To be safe, ensure that your files are checked in before running the generator. While InGenR is in beta, we don't recommend running it in pre-commit hooks or as a part of automated pipelines. \n\n  Please [report](https://github.com/lorefnon/InGenR/issues) any bugs or unexpected behavior that you encounter.\n\n- InGenR does not run the generators in a sandboxed environment. If you are using external generators, make sure you trust their authors.\n\n- InGenR doesn't sanitize the input, or validate the output because doing this in a way that works across languages is hard. Please make sure that you review the inputs that are passed to the templates and validate the outputs through a linter or type-checker.\n\n- You should not manually edit any code generated by InGenR. When InGenR will be re-run all manual edits will be obliterated.\n\n- Configuration assumes that referenced filenames don't have spaces\n\n## Non-goals\n\nInGenR templates are **not** reentrant. You can not nest InGenR templates. You can not generate InGenR templates through InGenR templates and expect them to be evaluated. There is no way to compose InGenR expand directives or make them inter-dependant.\n\n**This is by design.** PRs to change this behavior are not welcome.\n\nInGenR strongly assumes that code generation should be used only as a last resort and the generated code should be minimal and understandable. \n\nWhile it is possible to re-use doT templates across generators, but *seriously*, if you have a use case that requires complex code generation routines, then please consider using a language that supports compile time hygenic macros or a different solution. \n\n## Contributing\n\nWe welcome your contributions. Read more [here](https://github.com/lorefnon/InGenR/blob/master/CONTRIBUTING.md).\n\n## Potentially Interesting Alternatives\n\n- [ttyscript](https://github.com/cevek/ttypescript) (For typescript)\n- [M4](https://www.gnu.org/software/m4/manual/m4.html)\n- [babel-plugin-macros](https://github.com/kentcdodds/babel-plugin-macros)\n\n## FAQs\n\n### Isn't modifying source files risky ?\n\nNot really !\n\nInGenR does not modify any code outside of annotated expand blocks. It is safe to run it multiple times against the same source.\n\nHaving generated code live along-side source code in same file often simplifies use cases where expansions are desirable in nested scopes, within class declarations etc.\n\nFor small templates (primary use case) it also improves the readability and conveys the intent better. If you want the generated code to reside in dedicated files separate from your source directory, you are more than welcome to do so.\n\n### Is InGenR type-safe / hygenic ?\n\nNo. InGenR is entirely unaware of what is being templated / replaced / generated. It is entirely language agnostic.\n\nHowever, it makes it easy to retain your existing linters or type checkers and use them to check the safety of generated code.\n\n### Is InGenR similar to C/C++ Macros ?\n\nYes, in the sense that both are text replacement utilities.\n\nNo, in the sense that InGenR is much more **explicit** and does not try to abstract away the transformations. The generated code is injected right in the source files and is expected to be checked-in.\n\nIt is expected that this discourages overuse of macros/templates for things which can be easily achieved through language features.\n\n### Why not write babel plugins or Sweet.js macros instead ?\n\nWriting AST transformations is often more cumbersome than text based templates.\n\nMaking them play well with TypeScript (or other type systems) and static analyzers can be difficult.\n\n### Supported languages\n\nInGenR itself is language agnostic and can be used with any language.\n\nHowever, if your language natively supports compile time macros (eg. Haxe, Scala, Clojure etc.) or higher order polymorphism (eg. Haskell), or you are happy with dynamic metaprogramming (eg. Ruby, Lisp) then this project may not be useful for you.\n\nIt is strongly advised that generated code be run through a linter / syntax-checker after generation because InGenR does not guarantee syntactic validity of generated code.\n\n### If at some point I realize that InGenR is not for me. Am I locked in ? \n\nNot at all. Run InGenR once and after your code has been generated, feel free to just remove all (or some) InGenR directives.\nYou are now free to edit and modify the generated code manually.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Florefnon%2Fingenr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Florefnon%2Fingenr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Florefnon%2Fingenr/lists"}