{"id":15407547,"url":"https://github.com/jarred-sumner/decky","last_synced_at":"2025-04-18T05:02:32.264Z","repository":{"id":43808155,"uuid":"340760140","full_name":"Jarred-Sumner/decky","owner":"Jarred-Sumner","description":"Zero-bundle-size decorators for TypeScript","archived":false,"fork":false,"pushed_at":"2021-02-24T00:51:39.000Z","size":2328,"stargazers_count":42,"open_issues_count":2,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-06T12:12:17.250Z","etag":null,"topics":["decorators","esbuild-plugin","typescript"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/Jarred-Sumner.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}},"created_at":"2021-02-20T21:46:43.000Z","updated_at":"2025-01-30T23:38:01.000Z","dependencies_parsed_at":"2022-08-29T22:50:13.518Z","dependency_job_id":null,"html_url":"https://github.com/Jarred-Sumner/decky","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/Jarred-Sumner%2Fdecky","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jarred-Sumner%2Fdecky/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jarred-Sumner%2Fdecky/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jarred-Sumner%2Fdecky/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Jarred-Sumner","download_url":"https://codeload.github.com/Jarred-Sumner/decky/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248345268,"owners_count":21088244,"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":["decorators","esbuild-plugin","typescript"],"created_at":"2024-10-01T16:29:03.918Z","updated_at":"2025-04-18T05:02:32.228Z","avatar_url":"https://github.com/Jarred-Sumner.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# decky\n\nUse experimental decorators with zero runtime cost and without increasing your bundle size.\n\ndecky strives for full compatiblity with TypeScript, Prettier, and the rest of the JavaScript ecosystem.\n\n![](./demo.gif)\n\n## Installation\n\n`decky` is an esbuild plugin.\n\n```bash\nnpm install decky\n```\n\nIn your esbuild configuration:\n\n```ts\nconst { build } = require(\"esbuild\");\nconst { load } = require(\"decky\");\n\nbuild({\n  // ...rest of your esbuild config\n  plugins: [await load()];\n})\n```\n\n## Usage\n\nThe [`GraphQLSchema.decorator`](./examples/GraphQLSchema.decorator.ts) example lets you write GraphQL types inline with zero runtime overhead:\n\n```ts\nimport { auto, field, type } from \"./GraphQLSchema.decorator\";\n\n@type(\"Person\")\nexport class Person {\n  @field(\"ID\", \"user id number\")\n  id: number;\n\n  @auto\n  username: string;\n\n  @field(\"number\")\n  signUpTimestamp: number;\n}\n```\n\nAt build-time, it outputs the GraphQL schema to a file:\n\n```graphql\ntype Person {\n  signUpTimestamp: number\n  username: string\n  # user id number\n  id: ID\n}\n```\n\nTo the bundler, there are no decorators. They're removed at build-time.\n\n```ts\nexport class Person {\n  id: number;\n  username: string;\n  signUpTimestamp: number;\n}\n```\n\nWhat if we wanted JSON Schema instead of GraphQL? If the interface was the same but you had a [`JSONSchema.decorator`](./examples/JSONSchema.decorator.ts):\n\n```patch\n+import { auto, field, type } from \"./GraphQLSchema.decorator\";\n-import { auto, field, type } from \"./JSONSchema.decorator\";\n\n@type(\"Person\")\nexport class Person {\n// ...rest of file\n\n```\n\nYou'd get this:\n\n```json\n{\n  \"Person\": {\n    \"signUpTimestamp\": {\n      \"type\": \"number\"\n    },\n    \"username\": {\n      \"type\": \"string\"\n    },\n    \"id\": {\n      \"type\": \"number\",\n      \"description\": \"user id number\"\n    }\n  }\n}\n```\n\n### Writing decorators\n\nDecorators are run at build-time. This uses a handcrafted bespoke not-JavaScript AST. The syntax looks like decorators enough to fool TypeScript's type checker, but under the hood, its entirely different.\n\nDecorator imports are removed during tree-shaking, leaving no trace.\n\nBy default, files that write new decorators need to end in any of these extensions:\n\n- `.decorator.ts`\n- `.decorator.tsx`\n- `.decky.ts`\n- `.decky.tsx`\n- `.dec.ts`\n- `.dec.tsx`\n\nAnd it needs to export `decorators` which is an object where the `key` is the function name and the value is the decorator function (`property`, `propertyVoid` or `klass`).\n\n#### Property Decorator:\n\nWith no arguments:\n\n```ts\nimport { propertyVoid } from \"decky\";\n\n// void means the decorator accepts no arguments from the code calling it\nexport const debugOnly = propertyVoid(() =\u003e {\n  if (!process.env.DEBUG) {\n    return \"\";\n  }\n});\n\nexport const decorators = { debugOnly };\n```\n\nYou use it like this:\n\n```ts\nimport { debugOnly } from \"./debugOnly.decorator\";\n\nexport class Task {\n  @debugOnly\n  shouldLog = true;\n\n  run() {\n    // ... code in here\n  }\n}\n```\n\nThen, when `!DEBUG`, Task is compiled as:\n\n```ts\nexport class Task {\n  run() {\n    // ... code in here\n  }\n}\n```\n\nWhat we return in `property` or `propertyVoid` replaces from the `@` to the next two lines. If we don't return anything or return `undefined`, it just deletes the line containing the @ symbol.\n\nYou can use decky to edit code at build-time or for generating metadata for code.\n\n#### Class Decorator:\n\nTODO example\n\n### Performance\n\nTLDR: not bad\n\nTo log timings, set `process.env.DECKY_TIMINGS` to something truthy.\n\nYou can reproduce all the timings mentioned below by running `node build.js` in this project.\n\nFor simple files like the Propery Decorator `@debugOnly` example, the numbers look like this:\n\n```bash\n# How long it took to call the @debugOnly decorator\n[decky] debugOnly.debugOnly(): 0.025ms\n# How long it took to parse the section of the file relevant to @debugOnly\n[decky] -\u003e debugOnly: examples/debugOnlyExample.ts: 0.108ms\n# How long it took ducky to process the entire file end-to-end\n[decky] ./examples/debugOnlyExample.ts: 0.288ms\n```\n\nSince `@debugOnly` does very little, its a reasonable approximation of what the cost of `ducky` itself is. End-to-end it took `0.288ms` for decky to process the file. That's honestly faster than I expected at the time of writing.\n\nThis is a small file, but what about a larger one?\n\n```bash\n[decky] JSONSchema.field(number): 0.162ms\n[decky] -\u003e field: examples/JSONSchema.ts: 0.336ms\n[decky] JSONSchema.auto(): 0.059ms\n[decky] -\u003e auto: examples/JSONSchema.ts: 0.131ms\n[decky] JSONSchema.field(ID, user id number): 0.02ms\n[decky] -\u003e field: examples/JSONSchema.ts: 0.107ms\nSaved JSON schema to /Users/jarredsumner/Code/decky/examples/JSONSchema.json\n[decky] JSONSchema.type(Person): 1.07ms\n[decky] ./examples/JSONSchema.ts: 2.932ms\n```\n\nFor the JSONSchema example, it took `2.932ms`. But, some of that was writing and stringifying JSON – code specific to the JSONSchema example (rather than decky). If we subtract all those function calls:\n\n```\n[decky] JSONSchema.field(number): 0.162ms\n[decky] JSONSchema.field(ID, user id number): 0.02ms\n[decky] JSONSchema.type(Person): 1.07ms\n```\n\nThat means it took `2.932ms` - `1.252ms`, or: `1.68ms` for one file with several decorators.\n\nThis can be optimized some, feel free to open an issue if you're running into perf issues. Also note that decky eagerly ignores files that don't have any decorators in them.\n\n## Changelog\n\n- `1.1.0`: Rewrite parsing, add logging, fix multiple decorators for same property\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjarred-sumner%2Fdecky","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjarred-sumner%2Fdecky","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjarred-sumner%2Fdecky/lists"}