{"id":15405207,"url":"https://github.com/fnando/seagull","last_synced_at":"2025-04-17T00:57:52.221Z","repository":{"id":36984863,"uuid":"446099334","full_name":"fnando/seagull","owner":"fnando","description":"Minimal template engine with compiled output for JavaScript.","archived":false,"fork":false,"pushed_at":"2023-05-22T02:05:02.000Z","size":73,"stargazers_count":6,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-04-20T08:03:03.018Z","etag":null,"topics":["javascript","template-engine","template-engine-js"],"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/fnando.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["fnando"],"custom":["https://paypal.me/nandovieira/🍕"]}},"created_at":"2022-01-09T13:41:14.000Z","updated_at":"2023-03-04T07:35:50.000Z","dependencies_parsed_at":"2024-10-01T16:15:28.940Z","dependency_job_id":"7f16c94b-4222-46c1-968e-6e741c79d2cb","html_url":"https://github.com/fnando/seagull","commit_stats":{"total_commits":38,"total_committers":2,"mean_commits":19.0,"dds":"0.13157894736842102","last_synced_commit":"d2da33b2b580f7a848e8d947e20e051af7c97837"},"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fnando%2Fseagull","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fnando%2Fseagull/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fnando%2Fseagull/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fnando%2Fseagull/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fnando","download_url":"https://codeload.github.com/fnando/seagull/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249295890,"owners_count":21246213,"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":["javascript","template-engine","template-engine-js"],"created_at":"2024-10-01T16:15:25.819Z","updated_at":"2025-04-17T00:57:52.205Z","avatar_url":"https://github.com/fnando.png","language":"TypeScript","funding_links":["https://github.com/sponsors/fnando","https://paypal.me/nandovieira/🍕"],"categories":[],"sub_categories":[],"readme":"# @fnando/seagull\n\n[![Tests](https://github.com/fnando/seagull/workflows/node-tests/badge.svg)](https://github.com/fnando/seagull)\n[![NPM](https://img.shields.io/npm/v/@fnando/seagull.svg)](https://npmjs.org/package/@fnando/seagull)\n[![NPM](https://img.shields.io/npm/dt/@fnando/seagull.svg)](https://npmjs.org/package/@fnando/seagull)\n\nMinimal template engine with compiled output for JavaScript.\n\n## Installation\n\nThis package is available as a NPM package. To install it, use the following\ncommand:\n\n```bash\nnpm install @fnando/seagull --save\n```\n\nIf you're using Yarn (and you should):\n\n```bash\nyarn add @fnando/seagull\n```\n\n## Usage\n\n### Syntax Highlighting\n\n- Sublime Text: \u003chttps://sublime.fnando.com\u003e\n\n### Precompiling templates\n\nTo compile templates into JavaScript, use the CLI:\n\n```console\n# You can use either a file path (e.g. src/templates/hello.sea) or a glob\n# pattern (e.g. src/**/*.sea) as the input source.\n$ seagull compile --input hello.sea --output hello.js\n\n# To export individual files, use a directory path without the `.js` extension.\n$ seagull compile --input 'src/**/*.sea' --output 'src/helpers'\n```\n\n### Compiling templates in runtime\n\n```js\nimport { compile } from \"@fnando/seagull\";\n\nconst render = compile(\"Hello there, {name}.\");\n\nrender({ name: \"John\" });\n//=\u003e Hello there, John.\n```\n\n### Syntax\n\n#### Variables\n\n```seagull\nHello there, {name}.\nHello there, {person.name}.\n```\n\n#### Conditionals\n\n```seagull\n{if isReady}\n  Ready!\n{/if}\n\n{unless isReady}\n  Pending!\n{/unless}\n\n{when status=\"ready\"}Ready!{/when}\n{when status='ready'}Ready!{/when}\n{when status=readyStatus}Ready!{/when}\n{when status=statuses.ready}Ready!{/when}\n```\n\n#### Iteration\n\nIterating arrays:\n\n```seagull\n{each person in people}\n  Hi there, {person.name}.\n{/each}\n\n{each person, index in people}\n  {index}: {person.name}\n{/each}\n```\n\nIterating dictionaries (objects with key value):\n\n```seagull\n{each id =\u003e person in peopleMap}\n  Hello, {person.name}. Your id is {id}.\n{/each}\n\n{each id =\u003e person, index in peopleMap}\n  {index}: {person.name} ({id})\n{/each}\n```\n\n#### Helpers\n\nHelpers that receive one single positional argument must be called by pipeling\nthe parameter into the helper.\n\n```seagull\nYou're name in reverse is {name | upcase | reverse}.\n```\n\nHelpers need to be registered as part of the context when calling the rendering\nfunction. **Seagull doesn't bundle any helpers.**\n\n```js\ntemplate({\n  name: \"John\",\n  upcase: (input) =\u003e input.toUpperCase(),\n  reverse: (input) =\u003e\n    input\n      .split(\"\")\n      .reduce((memo, char) =\u003e [char].concat(memo), [])\n      .join(\"\"),\n});\n```\n\nThe `if` and `unless` blocks also accept helper piping.\n\n```seagull\n{if emails | isEmpty}\n  You have no mail!\n{/if}\n```\n\n```js\ntemplate({\n  emails: [],\n  isEmpty: (input) =\u003e\n    input \u0026\u0026\n    typeof input !== \"boolean\" \u0026\u0026\n    \"length\" in input \u0026\u0026\n    input.length === 0,\n});\n```\n\nFinally, you can also pipe strings to helpers.\n\n```seagull\n{\"seagull_is_nice\" | i18n}\n{'seagull_is_nice' | i18n}\n```\n\nIf you're function requires multiple parameters, then you can use the named\nparameter call.\n\n```seagull\n{i18n path=\"messages.hello\" name=user.name}\n```\n\nThis will translate to a call like\n`i18n({path: \"message.hello\", name: user.name})`.\n\n#### HTML Escaping\n\nAll interpolations are escaped by default. If you need to decode the output for\ntests, you can import the `decode` function.\n\n```js\nimport { compile, decode } from \"@fnando/seagull\";\n\ntest(\"renders template\", () =\u003e {\n  const template = compile(`{message}`);\n  const output = template({ message: \"\u003cscript\u003ealert(1);\u003c/script\u003e\" });\n\n  expect(output).toEqual(\n    \"\u0026#0060;script\u0026#0062;alert\u0026#0040;1\u0026#0041;\u0026#0059;\u0026#0060;\u0026#0047;script\u0026#0062;\",\n  );\n  expect(decode(output)).toEqual(\"\u003cscript\u003ealert(1);\u003c/script\u003e\");\n});\n```\n\n### Using TypeScript\n\nSeagull doesn't have direct TypeScript support, but that doesn't mean you can't\nuse typed template functions.\n\nThe way I like to do it is by creating a file called `templates.d.ts` somewhere\n(e.g. if I export the templates to `src/helpers/templates.js`, then I use\n`src/helpers/templates.d.ts`). This file will hold all function types.\n\nLet's say I have a template function that works like `hello({name: \"John\"})`; in\nthis case, my module declaration would look like this:\n\n```typescript\ndeclare module \"src/helpers/templates\" {\n  export function hello(params: { firstName: string }): string;\n  export function goodbye(params: { lastName: string }): string;\n}\n```\n\nIf you're using helpers, tem you can also type an intermediary `template`\nfunction.\n\n```typescript\nimport * as templates from \"src/helpers/templates\";\n\n// Add your helpers here\n// You don't have to inline them (e.g. use `const helpers = {helper}` instead).\nconst helpers = {};\n\nexport function template\u003c\n  T extends keyof typeof templates,\n  P = Parameters\u003ctypeof templates[T]\u003e[0],\n\u003e(name: T, params: P): string {\n  // @ts-expect-error injecting helpers\n  return templates[name]({ ...params, ...helpers });\n}\n```\n\nTo call the templates using this function:\n\n```typescript\nimport { template } from \"src/helpers/template;\n\nconsole.log(template(\"hello\", {firstName: \"John\"}));\nconsole.log(template(\"goodbye\", {lastName: \"Doe\"}));\n```\n\n## Maintainer\n\n- [Nando Vieira](https://github.com/fnando)\n\n## Contributors\n\n- \u003chttps://github.com/fnando/seagull/contributors\u003e\n\n## Contributing\n\nFor more details about how to contribute, please read\n\u003chttps://github.com/fnando/seagull/blob/main/CONTRIBUTING.md\u003e.\n\n## License\n\nThe gem is available as open source under the terms of the\n[MIT License](https://opensource.org/licenses/MIT). A copy of the license can be\nfound at \u003chttps://github.com/fnando/seagull/blob/main/LICENSE.md\u003e.\n\n## Code of Conduct\n\nEveryone interacting in the seagull project's codebases, issue trackers, chat\nrooms and mailing lists is expected to follow the\n[code of conduct](https://github.com/fnando/seagull/blob/main/CODE_OF_CONDUCT.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffnando%2Fseagull","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffnando%2Fseagull","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffnando%2Fseagull/lists"}