{"id":15407562,"url":"https://github.com/jarred-sumner/atbuild","last_synced_at":"2025-06-24T22:35:41.924Z","repository":{"id":142271853,"uuid":"303250302","full_name":"Jarred-Sumner/atbuild","owner":"Jarred-Sumner","description":"Use JavaScript to generate JavaScript","archived":false,"fork":false,"pushed_at":"2020-11-22T00:07:55.000Z","size":4931,"stargazers_count":35,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-06-19T12:45:46.385Z","etag":null,"topics":["babel","build-tool","code-generation","codegen","esbuild","preprocessor","webpack"],"latest_commit_sha":null,"homepage":"atbuild.vercel.app","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Jarred-Sumner.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2020-10-12T01:46:26.000Z","updated_at":"2024-01-22T01:46:05.000Z","dependencies_parsed_at":"2024-01-27T09:39:26.720Z","dependency_job_id":"79b91a04-5c0f-4fa4-bae3-6acd8af28d12","html_url":"https://github.com/Jarred-Sumner/atbuild","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Jarred-Sumner/atbuild","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jarred-Sumner%2Fatbuild","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jarred-Sumner%2Fatbuild/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jarred-Sumner%2Fatbuild/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jarred-Sumner%2Fatbuild/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Jarred-Sumner","download_url":"https://codeload.github.com/Jarred-Sumner/atbuild/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jarred-Sumner%2Fatbuild/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261768315,"owners_count":23206980,"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":["babel","build-tool","code-generation","codegen","esbuild","preprocessor","webpack"],"created_at":"2024-10-01T16:29:05.282Z","updated_at":"2025-06-24T22:35:41.901Z","avatar_url":"https://github.com/Jarred-Sumner.png","language":"TypeScript","readme":"# AtBuild – Experimental JavaScript Preprocessor\n\nAtBuild is a programmable code generation tool for JavaScript. It lets you write JavaScript that writes JavaScript.\n\nUse it for:\n\n- Easy, editable code generation with full TypeScript support\n- Write high-performance JavaScript libraries by removing the runtime\n- Determinstic dead code elimination\n- Move slow code from runtime to buildtime\n\nContributions \u0026 feedback are very welcome – feel free to file an issue.\n\n\u003ca target=\"_blank\" href=\"https://atbuild.vercel.app/bundle/date-formatter.tsb\"\u003e\n  \u003cstrong\u003eTry the playground\u003c/strong\u003e\n\n  \u003cimg src=\"https://atbuild.vercel.app/screenshot.png\" width=\"100%\" /\u003e\n\u003c/a\u003e\n\n# Installation\n\nWith yarn:\n\n```bash\nyarn add atbuild\n```\n\nnpm:\n\n```bash\nnpm install atbuild\n```\n\n## Next.js integration\n\nTo use with Next.js, add the following to your `next.config.js`;\n\n```js\nmodule.exports = require(\"atbuild/with-nextjs\");\n```\n\nIf you have an existing Next.js config, then use it like this:\n\n```js\nconst withAtBuild = require(\"atbuild/with-nextjs\");\n\nmodule.exports = withAtBuild({\n  webpack(config, options) {\n    // your webpack config here:\n  },\n});\n```\n\n## esbuild integration\n\nIf you're using esbuild, you can use AtBuild's esbuild plugin like this:\n\n```js\nconst { build } = require(\"esbuild\");\n\nmodule.exports = build({\n  // Add `require(\"atbuild/esbuild\")` to the list of esbuild plugins\n  plugins: [require(\"atbuild/esbuild\")],\n  // Add .tsb and .jsb to the list of extensions to resolve\n  resolveExtensions: [\".tsb\", \".jsb\", \".ts\", \".js\"],\n});\n```\n\nFor esbuild, the entrypoint can't be a `.tsb`/`.jsb` file due to https://github.com/evanw/esbuild/issues/546.\n\n## Webpack Loader\n\nBuildtime code is run through a [high performance bundler](https://esbuild.github.io/) for you automatically, so you can write your buildtime code using the same modern JavaScript as the rest of your code. This also means you can import other modules, and those modules don't have to be `.jsb` files - they can be any other file in your codebase (so long as it runs in Node after bundling).\n\nRuntime code is passed through webpack as regular JavaScript – so you can still use babel-loader as normal.\n\n```\n// Webpack config\nmodule.exports = {\n  // ...\n  module: {\n    // ...\n    rules: [\n      // ...\n\n      // AtBuild.js Webpack Loader\n      {\n        test: /\\.(jsb|js|ts|tsx|jsx|tsb|@js)$/,\n        exclude: /node_modules/,\n        enforce: \"pre\",\n        use: [\n          {\n            loader: \"atbuild/webpack-loader,\n            options: {\n              // Generate a .d.ts file automatically so your IDE can more easily interop with AtBuild files.\n              typescript: true,\n            }\n          },\n        ]\n      },\n    ],\n  }\n  // ...\n}\n```\n\n## CLI\n\n`atbuild` has a small CLI you can use.\n\n```bash\natbuild ./input.jsb --print\n```\n\n```bash\natbuild ./input.jsb ./output.js --no-bundle\n```\n\n```bash\natbuild ./input.js ./output.js --ast\n```\n\n```bash\natbuild ./*.js --outdir=./out\n```\n\n```bash\natbuild ./*.ts --types\n```\n\n# How it works\n\nThere are two flavors of AtBuild.\n\n1. AtBuild Light: compatible with current JavaScript syntax\n2. AtBuild Full: a powerful JavaScript-based templating language for generating code. It's close to but not quite JavaScript, which is why it has it's own file extension: `.jsb`/`.tsb`\n\n### Atbuild Light\n\nAtbuild Light preprocesses your JavaScript \u0026 TypeScript files by setting three conventions:\n\n1. Code inside of `$(buildTimeCodeInHere)` will be run \u0026 replaced at buildtime\n2. Code fenced within `// $$` will be moved to buildtime\n3. Lines ending with `// $` with be moved to buildtime\n\n#### Small example\n\n`input.js`:\n\n```js\nimport { $ } from \"atbuild\";\n\n// $$\n\nconst didRemoveBuildTimeCode = false;\n\n// $$-\n\nexport const isRemoved = $(!didRemoveBuildTimeCode);\n```\n\n⌨️ `atbuild ./input.js ./output.js`\n\n`output.js`:\n\n```js\nconst isRemoved = true;\nexport { isRemoved };\n```\n\nNote: the `import {$}` is there for convience so your editor doesn't get mad. Any function call starting with `$` is assumed to be a build-time function.\n\nUnlike other buildtime code generation tools, you can `import` from `node_modules`, and even import other modules in your codebase (so long as it runs without a `window` object). The input is transformed using `esbuild`.\n\n`input.js`:\n\n```ts\nimport { $createDateFormatter } from \"atbuild/demo/date-formatter\"; // $\n\nexport const formatTime = $createDateFormatter(\"hh:mm:ss\");\n```\n\n[⌨️ `atbuild ./input.js ./output.js`](https://atbuild.vercel.app/bundle/date-formatter.tsb)\n\n`output.js`:\n\n```js\nexport const formatTime = function (date: Date) {\n  let formattedDate = \"\";\n  var hours = date.getUTCHours() % 12;\n  hours = hours || 12;\n  formattedDate += hours.toString(10).padStart(2, \"0\");\n  formattedDate += \":\";\n  formattedDate += date.getUTCMinutes().toString(10).padStart(2, \"0\");\n  formattedDate += \":\";\n  formattedDate += date.getUTCSeconds().toString(10).padStart(2, \"0\");\n  return formattedDate;\n};\n```\n\nAnd it supports types.\n\nFor compatibility reasons, exporting build time code from JavaScript/TypeScript outside of the file is not supported. But, that's why there's Atbuild Full, which lets you write libraries for proceedurally generating code at build time.\n\n### AtBuild Full\n\nAtbuild Full adds a few new keywords to JavaScript. It lets you evaluate \u0026 generate code at build time using JavaScript.\n\n[More documentation](./docs-md/full.mdx)\n\n1. `@build`: code contained inside `@build` will be run at build-time instead of runtime\n\n```js\n@build\n  const yourBrowserDoesntKnowAboutThisCode = true;\n@end\n```\n\n2. `@run`: code contained inside `@run` will be included at runtime.\n\n```js\n@run\n  console.log(\"This code will be included at runtime\");\n@end\n```\n\n3. `@run` and `@build` can be nested. `@()` is like string interpolation but for generating code.\n\n```js\n@build\n  // This for loop isn't included in the runtime code.\n  for (let i = 0; i \u003c 3;i++) {\n    @run\n      console.log(\"This code will be included at runtime @(i)\");\n    @end\n  }\n@end\n```\n\nAnd this is the output:\n\n```js\nconsole.log(\"This code will be included at runtime 0\");\nconsole.log(\"This code will be included at runtime 1\");\nconsole.log(\"This code will be included at runtime 2\");\n```\n\n4. `@export function $FunctionNameGoesHere(arguments, in, here)` adds a build-time exported function that can be called from regular JavaScript/TypeScript files. Before it reaches the browser, the function call is replaced with the code generated from the function call.\n\nYou write some of your JavaScript in `.jsb` files, and by default, all the code in the file will be evaluated at runtime.\n\nThe code evaluated at buildtime is also JavaScript.\n\nAll of this works with your bundler, so you can import React components and generates type definitions.\n\n## Contrived example:\n\n```js\n// contrived-api-endpoint-codegenerator.jsb.\n@build\n\nimport { kebabCase, startCase, toLower} from 'lodash';\nconst titleize = str =\u003e startCase(toLower(str));\nconst BASE_URL = `http://example.com`;\n\n@end\n\ntype BaseType = {\n  id: number;\n}\n\n@build\n  for (let objectName of [\"Post\", \"User\", \"Like\", \"PasswordResetToken\"]) {\n    @run\n      export type @(objectName) = BaseType \u0026 {\n        object: \"@(kebabCase(objectName))\";\n\n        @build\n        switch(objectName) {\n          case \"PasswordResetToken\": {\n            @run\n              used: boolean;\n              expiry: Date;\n            @end\n          }\n        }\n        @end\n      }\n\n      export function build@(objectName)FromJSON(json: Object): @(objectName) {\n        return json;\n      }\n\n      export async function fetch@(objectName)ById(id: number): Promise\u003c@(objectName)\u003e {\n        @build\n          var base = BASE_URL + `/${kebabCase(objectName)}s/`;\n        @end\n\n        const body = (await fetch(\"@(base)\" + id)).body()\n        const json = await body.json()\n        return build@(objectName)FromJSON(json);\n      }\n    @end\n  }\n@end\n```\n\nAfter we run it through `atbuild ./contrived-api-endpoint-codegenerator.jsb`, it becomes:\n\n```js\n// contrived-api-endpoint-codegenerator.js\nfunction buildPostFromJSON(json) {\n  return json;\n}\nasync function fetchPostById(id) {\n  const body = (await fetch(\"http://example.com/posts/\" + id)).body();\n  const json = await body.json();\n  return buildPostFromJSON(json);\n}\nfunction buildUserFromJSON(json) {\n  return json;\n}\nasync function fetchUserById(id) {\n  const body = (await fetch(\"http://example.com/users/\" + id)).body();\n  const json = await body.json();\n  return buildUserFromJSON(json);\n}\nfunction buildLikeFromJSON(json) {\n  return json;\n}\nasync function fetchLikeById(id) {\n  const body = (await fetch(\"http://example.com/likes/\" + id)).body();\n  const json = await body.json();\n  return buildLikeFromJSON(json);\n}\nfunction buildPasswordResetTokenFromJSON(json) {\n  return json;\n}\nasync function fetchPasswordResetTokenById(id) {\n  const body = (\n    await fetch(\"http://example.com/password-reset-tokens/\" + id)\n  ).body();\n  const json = await body.json();\n  return buildPasswordResetTokenFromJSON(json);\n}\nexport {\n  buildLikeFromJSON,\n  buildPasswordResetTokenFromJSON,\n  buildPostFromJSON,\n  buildUserFromJSON,\n  fetchLikeById,\n  fetchPasswordResetTokenById,\n  fetchPostById,\n  fetchUserById,\n};\n```\n\nThis also generates a `contrived-api-endpoint-codegenerator.ts.d` file:\n\n```ts\ndeclare type BaseType = {\n  id: number;\n};\nexport declare type Post = BaseType \u0026 {\n  object: \"post\";\n};\nexport declare function buildPostFromJSON(json: Object): Post;\nexport declare function fetchPostById(id: number): Promise\u003cPost\u003e;\nexport declare type User = BaseType \u0026 {\n  object: \"user\";\n};\nexport declare function buildUserFromJSON(json: Object): User;\nexport declare function fetchUserById(id: number): Promise\u003cUser\u003e;\nexport declare type Like = BaseType \u0026 {\n  object: \"like\";\n};\nexport declare function buildLikeFromJSON(json: Object): Like;\nexport declare function fetchLikeById(id: number): Promise\u003cLike\u003e;\nexport declare type PasswordResetToken = BaseType \u0026 {\n  object: \"password-reset-token\";\n  used: boolean;\n  expiry: Date;\n};\nexport declare function buildPasswordResetTokenFromJSON(\n  json: Object\n): PasswordResetToken;\nexport declare function fetchPasswordResetTokenById(\n  id: number\n): Promise\u003cPasswordResetToken\u003e;\nexport {};\n```\n\n## Changelog\n\n**November 21st**: Publish esbuild plugin.\n\n**November 9th**: Bump to latest esbuild with plugin API.\n\n**November 6th**: New syntax for Atbuild Full, and a new parser to go with it.\n\n**October 30th, 2020**: Added support for nested buildtime modules to export functions that are only available at buildtime. This allows you to write zero-runtime libraries.\n\n**October 30th, 2020**: Added support for nested buildtime modules in the webpack-loader, so you can import jsb files from inside jsb files and it will work as expected (buildtime code is executed, runtime code is generated)\n\n**October 29th, 2020**: Added support for bundling buildtime code in the webpack loader, meaning you can use the same syntax for buildtime code and runtime code. This also makes it easy to import runtime modules at buildtime. The webpack-loader uses [esbuild](https://esbuild.github.io/) for bundling the backend code.\n\n**October 28th, 2020**: Extremely WIP VSCode extension.\n\n**October 28th, 2020**: Added support for `require` in buildtime code. Runtime code works like normal and is run through Babel or any other loaders you use. ~Buildtime code isn't run through babel, but this might be implemented later via webpack's `this._compilation_.createChildCompiler`, which would run buildtime and runtime code both through webpack.~ Fixed\n\n## Why?\n\n\u003cimg alt=\"Y U Do Dis meme\" src=\"./explain/y.png\" height=\"120\" /\u003e\n\nExtremely fast native languages like Rust \u0026 C often use [inline expansion](https://en.wikipedia.org/wiki/Inline_expansion) and [loop unrolling](https://en.wikipedia.org/wiki/Loop_unrolling) to move work from runtime to buildtime. For code that doesn't change much, this can be a massive performance improvement.\n\nUnfortunately, since JavaScript is a dynamic language, that's not natively supported. High performance JavaScript libraries like [ndarray](https://github.com/scijs/ndarray) and [Kiwi](https://github.com/evanw/kiwi) resort to [writing code inside code](https://github.com/scijs/ndarray/blob/master/ndarray.js#L123) by [adding strings together](https://github.com/evanw/kiwi/blob/1a82ea6592ff25f26e35ca69e58c98852072eae9/js/js.ts#L11), which is hard for humans to read whats going on.\n\nNowadays, much of the JavaScript we write is already behind [seven](https://webpack.js.org/) [different](https://babeljs.io/) [compilers](https://v8.dev/docs/turbofan), so why not add another?\n\n### What can I use this for?\n\nI wrote AtBuild because I needed to improve the performance for some parts of a game I'm building.\n\nBut, here are some other ways you could use this:\n\n- Zero-runtime SQL ORM. Instead of a general-purpose SQL query builder evaluated at runtime (what Sequelize and every other JavaScript SQL ORM does), a SQL ORM built with AtBuild could compile down to a handful of functions that format the specific SQL strings used in the application, while being just as easy to use as Sequelize. You'd get the performance of hand-rolling your SQL, with the developer experience of a SQL ORM.\n- Edit autogenerated code for calling API endpoints. If you use something like [OpenAPI Generator](https://github.com/OpenAPITools/openapi-generator) and you want to edit the generated code, you often end up writing wrappers for the generated code. Wouldn't it be better if you could just edit the generated code instead?\n- Preprocessing data ahead of time, so that it only is sent once instead of re-evaluated at runtime\n- Server-side rendering. Theoretically, you could use this for server-side rendering. AtBuild is a lot like [EJS](https://ejs.co/) but with different syntax.\n\nHow is this different than [Prepack](https://prepack.io/)?\n\nLike AtBuild, Prepack inlines \u0026 prevaluates code. But, AtBuild lets you choose what code runs at runtime and what code runs at buildtime, and use that to generate code. Loops that conditionally add or remove runtime code are not possible with Prepack or with [`babel-plugin-codegen`](https://github.com/kentcdodds/babel-plugin-codegen).\n\n### Tests\n\nIf you want to use AtBuild Full with Jest, add the Jest transform at `\"\u003crootDir\u003e/node_modules/atbuild/jest.js\"`.\n\nFor example:\n\n```js\nmodule.exports = {\n  transform: {\n    \"^.+\\\\.(@ts|@js|jsb|tsb)$\": \"\u003crootDir\u003e/node_modules/atbuild/jest.js\",\n  },\n};\n```\n\nIf you're using `babel-jest`, add the Jest transform from `\"\u003crootDir\u003e/node_modules/atbuild/jest-with-babel.js\"` instead.\n\n```js\nmodule.exports = {\n  transform: {\n    \"^.+\\\\.(@ts|@js|jsb|tsb)$\":\n      \"\u003crootDir\u003e/node_modules/atbuild/jest-with-babel.js\",\n  },\n};\n```\n\nThis skips bundling and assumes Jest will know to deal with it.\n\n### JavaScript API\n\nTo invoke AtBuild Full programatically:\n\n```js\nconst { AtBuild } = require(\"atbuild\");\n\nconst contents = AtBuild.transformAST(\n  // filepath is optional, but is helpful for error messages.\n  AtBuild.buildAST(source, filepath),\n  source\n);\n```\n\nTo invoke AtBuild Light programatically:\n\n```js\nconst { transformAST, buildAST } = require(\"atbuild/light\");\n\nconst contents = transformAST(\n  // filepath is optional, but is helpful for error messages.\n  buildAST(source, filepath),\n  source\n);\n```\n\nThis won't run the bundler or write to disk.\n\nTo do that, you'll want to look at \u003ca href=\"./src/bundle.ts\"\u003e./src/bundle.ts\u003c/a\u003e.\n\n### Syntax highlighting\n\nThere's a half implemented syntax highlighter in `atbuild-vscode`. Contributions are very welcome.\n\n### Ignore file\n\nif you want to make sure AtBuild Light does not run on a specific file, stick this at the top of the file.\n\n```js\n// atbuild-ignore-file\n```\n\nThis is helpful if you're bundling already minified files that use `$` as a function call.\n\nThe Next.js integration automatically excludes `node_modules` and file paths with `vendor` or `.min.` in the filename.\n\n## Alternatives\n\n- [`babel-plugin-codegen`](https://github.com/kentcdodds/babel-plugin-codegen) makes it easy to run build scripts, but it gets tough if you want to do some things at buildtime and some other things at run-time for the same code.\n","funding_links":[],"categories":["Generators"],"sub_categories":["Meetups"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjarred-sumner%2Fatbuild","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjarred-sumner%2Fatbuild","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjarred-sumner%2Fatbuild/lists"}