{"id":48380740,"url":"https://github.com/unstoppablecarl/unplugin-inline","last_synced_at":"2026-04-05T20:00:55.017Z","repository":{"id":344659073,"uuid":"1181015973","full_name":"unstoppablecarl/unplugin-inline","owner":"unstoppablecarl","description":"an unplugin to inline pure functions","archived":false,"fork":false,"pushed_at":"2026-03-31T20:58:09.000Z","size":133,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-31T22:31:51.203Z","etag":null,"topics":["esbuild-plugins","performance","tsup-plugin","unplugin","vite-plugin","webpack-plugin"],"latest_commit_sha":null,"homepage":"","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/unstoppablecarl.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-13T16:53:41.000Z","updated_at":"2026-03-31T20:57:51.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/unstoppablecarl/unplugin-inline","commit_stats":null,"previous_names":["unstoppablecarl/unplugin-inline"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/unstoppablecarl/unplugin-inline","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unstoppablecarl%2Funplugin-inline","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unstoppablecarl%2Funplugin-inline/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unstoppablecarl%2Funplugin-inline/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unstoppablecarl%2Funplugin-inline/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/unstoppablecarl","download_url":"https://codeload.github.com/unstoppablecarl/unplugin-inline/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unstoppablecarl%2Funplugin-inline/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31448216,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-05T15:22:31.103Z","status":"ssl_error","status_checked_at":"2026-04-05T15:22:00.205Z","response_time":75,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["esbuild-plugins","performance","tsup-plugin","unplugin","vite-plugin","webpack-plugin"],"created_at":"2026-04-05T20:00:54.448Z","updated_at":"2026-04-05T20:00:55.012Z","avatar_url":"https://github.com/unstoppablecarl.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# unplugin-inline\n\nAST-driven unplugin that inlines pure functions at build time across Vite, Rollup, Webpack, and esbuild.\n\n---\n\n## Why\n\nV8 refuses to inline functions whose bytecode exceeds ~460 instructions or ~600 bytes, even on the hot path—every call still pays full frame\nsetup cost at runtime. Marking a function with `/* @__INLINE__ */` moves that cost to build time instead, flattening the\nlogic directly into the caller.\n\n## Installation\n\n```bash\npnpm add -D unplugin-inline\n# or\nnpm install -D unplugin-inline\n# or\nyarn add -D unplugin-inline\n```\n\n## Example Usage\n\n### 1. Standard Block Inlining (`@__INLINE__`)\n\nConsider a physics simulation where `transformPoint` is called millions of times per frame. The function is large enough\nthat V8 refuses to inline it automatically.\n\n#### Source Code\n\n**`src/physics.ts`**\n\n```ts\n/* @__INLINE__ */\nfunction transformPoint(x: number, y: number, z: number, matrix: Float32Array): number {\n  const m = matrix;\n  const tx = m[0] * x + m[4] * y + m[8] * z + m[12]\n  const ty = m[1] * x + m[5] * y + m[9] * z + m[13]\n  const tz = m[2] * x + m[6] * y + m[10] * z + m[14]\n  const tw = m[3] * x + m[7] * y + m[11] * z + m[15]\n\n  return Math.sqrt(tx * tx + ty * ty + tz * tz) / tw\n}\n\n// Called millions of times per frame — no function call overhead in the output\nexport function processVertices(vertices: Float32Array, matrix: Float32Array): number {\n  let sum = 0\n  for (let i = 0; i \u003c vertices.length; i += 3) {\n    sum += transformPoint(vertices[i], vertices[i + 1], vertices[i + 2], matrix)\n  }\n  return sum\n}\n```\n\n#### Compiled Output\n\nThe compiled output has no `transformPoint` declaration. Its body is placed directly at the call site as a flat labeled\nblock:\n\n**`dist/physics.js`**\n\n```js\nfunction processVertices(vertices, matrix) {\n  let sum = 0;\n  for (let i = 0; i \u003c vertices.length; i += 3) {\n    let _transformPointResult;\n    _transformPointLabel: {\n      const _x = vertices[i];\n      const _y = vertices[i + 1];\n      const _z = vertices[i + 2];\n      const _matrix = matrix;\n      const tx = _matrix[0] * _x + _matrix[4] * _y + _matrix[8] * _z + _matrix[12];\n      const ty = _matrix[1] * _x + _matrix[5] * _y + _matrix[9] * _z + _matrix[13];\n      const tz = _matrix[2] * _x + _matrix[6] * _y + _matrix[10] * _z + _matrix[14];\n      const tw = _matrix[3] * _x + _matrix[7] * _y + _matrix[11] * _z + _matrix[15];\n      _transformPointResult = Math.sqrt(tx * tx + ty * ty + tz * tz) / tw;\n    }\n    sum += _transformPointResult;\n  }\n  return sum;\n}\n```\n\n#### 2. Macro Expression Inlining (`@__INLINE_MACRO__`)\n\nFor extremely hot, small math utility functions, generating block scopes can bloat the bundle and create unnecessary extra variables. Using `@__INLINE_MACRO__` bypasses block generation entirely, performing a direct AST expression substitution wrapped in parentheses to preserve operator precedence.\n\n**`src/math.ts`**\n\n```ts\n/** @__INLINE_MACRO__ */\nconst blendAlpha = (a: number, b: number) =\u003e (a * b + 128) \u003e\u003e 8;\n\nexport const color = blendAlpha(100, 255) * 2;\n```\n\n**`dist/math.js`**\n\n```js\nexport const color = (((100) * (255) + 128) \u003e\u003e 8) * 2;\n```\n\n*Both block (`/* @__INLINE__ */`) and line (`// @__INLINE__`) comment styles are recognized.*\n\n## Bundler Configuration\n\n### Vite\n\n**`vite.config.ts`**\n\n```ts\nimport { defineConfig } from 'vite'\nimport { vitePlugin } from 'unplugin-inline'\n\nexport default defineConfig({\n  plugins: [vitePlugin()]\n})\n```\n\n### esbuild\n\n**`build.js`**\n\n```js\nimport esbuild from 'esbuild'\nimport { esbuildPlugin } from 'unplugin-inline'\n\nesbuild.build({\n  entryPoints: ['input.js'],\n  bundle: true,\n  plugins: [esbuildPlugin()],\n})\n```\n\n### tsup\n\n**`tsup.config.ts`**\n\n```ts\nimport { defineConfig } from 'tsup'\nimport { esbuildPlugin } from 'unplugin-inline'\n\nexport default defineConfig({\n  esbuildPlugins: [esbuildPlugin()],\n})\n```\n\n### Rollup\n\n**`rollup.config.js`**\n\n```js\nimport { rollupPlugin } from 'unplugin-inline'\n\nexport default {\n  input: 'input.js',\n  plugins: [rollupPlugin()],\n}\n```\n\n### Webpack\n\n**`webpack.config.js`**\n\n```js\nconst { webpackPlugin } = require('unplugin-inline')\n\nmodule.exports = {\n  plugins: [webpackPlugin()],\n}\n```\n\n## Configuration Options\n\n| Option                     | Type       | Default                                                         | Description                                                                                                                                                                                                             |\n|----------------------------|------------|-----------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `inlineIdentifier`         | `string`   | `'@__INLINE__'`                                                 | The comment string used to mark functions for standard block inlining.                                                                                                                                                  |\n| `inlineMacroIdentifier`    | `string`   | `'@__INLINE_MACRO__'`                                           | The comment string used to mark functions for direct AST expression substitution (Macros).                                                                                                                              |\n| `autoConvertInlineToMacro` | `boolean`  | `true`                                                          | If `true`, the plugin will attempt to automatically upgrade standard `@__INLINE__` functions to macros if they meet all safety requirements, falling back to block-scoping if they don't or if passed impure arguments. |\n| `allowedGlobals`           | `string[]` | [See Defaults](https://www.google.com/search?q=src/defaults.ts) | Global identifiers available inside inlined functions.                                                                                                                                                                  |\n| `fileExtensions`           | `string[]` | [See Defaults](https://www.google.com/search?q=src/defaults.ts) | File extensions the plugin will process.                                                                                                                                                                                |\n\n## ⚠️ Requirements \u0026 Restrictions\n\nA function must be **pure** to be inlinable. The plugin enforces strict AST analysis and will throw build errors if you\nviolate these rules:\n\n* **No async or generators** — `async`/`await` and `function*` alter execution timing.\n* **No `this` or `arguments**` — both bind to the caller after inlining, producing unpredictable results.\n* **No outer scope mutations** — cannot reassign variables declared outside the function's own block.\n* **No outer scope references** — cannot read variables from an outer scope. Standard globals (`Math`, `JSON`, etc.) are\n  permitted — see `allowedGlobals` for the full default list.\n* **No recursive functions** — recursion cannot be unrolled at the call site.\n* **No call expressions in conditionals** — the function must be called as a standalone statement or direct assignment,\n  not inside a ternary or `if` condition. Assign the result first:\n\n```js\n// ❌ Not allowed\nif (processValue(x) \u003e 100) { // ...\n}\n\n// ✅ Assign first, then use\nconst processed = processValue(x)\nif (processed \u003e 100) { // ...\n}\n```\n\n### Specific Requirements for Macros (`@__INLINE_MACRO__`)\n\nBecause macros perform direct AST substitution rather than creating a lexical block scope, they have additional strict requirements:\n\n* **Must resolve to a single pure expression** — Macros cannot contain multiple statements, variable declarations, or block-level logic (like `if` statements). Arrow functions with implicit returns or standard functions with a single `return` statement are allowed.\n* **No Side-Effect Duplication (Multiple Evaluation Bug)** — If an argument with side-effects (e.g., `i++`, `Math.random()`, `getPixel()`) is passed into a macro, the plugin analyzes how many times that parameter is referenced in the macro body. If the parameter is referenced more than once, expanding the macro would cause the side-effect to be evaluated multiple times. The build will throw a safety validation error.\n\n## Benchmarks\n\n```bash\nnpm run bench\n```\n\n## Release Automation\n\n* update `package.json` file version (example: `1.0.99`)\n* manually create a github release with a tag matching the `package.json` version prefixed with `v` (example: `v1.0.99`)\n* npm should be updated automatically\n\n## License\n\nMIT","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funstoppablecarl%2Funplugin-inline","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Funstoppablecarl%2Funplugin-inline","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funstoppablecarl%2Funplugin-inline/lists"}