{"id":13508540,"url":"https://github.com/mreinstein/snabby","last_synced_at":"2025-09-10T06:31:36.540Z","repository":{"id":50090943,"uuid":"80340097","full_name":"mreinstein/snabby","owner":"mreinstein","description":"Use HTML template strings with snabbdom.","archived":false,"fork":false,"pushed_at":"2024-01-17T18:37:33.000Z","size":216,"stargazers_count":95,"open_issues_count":0,"forks_count":4,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-08-31T23:58:49.665Z","etag":null,"topics":["javascript","snabbdom","virtual-dom"],"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/mreinstein.png","metadata":{"files":{"readme":"readme.md","changelog":"CHANGELOG.md","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}},"created_at":"2017-01-29T10:27:46.000Z","updated_at":"2025-06-28T04:39:01.000Z","dependencies_parsed_at":"2024-06-02T16:34:18.240Z","dependency_job_id":"f7f30194-3664-4430-be82-b3ec350de079","html_url":"https://github.com/mreinstein/snabby","commit_stats":{"total_commits":89,"total_committers":5,"mean_commits":17.8,"dds":0.5730337078651686,"last_synced_commit":"7a5ccbc646bf34e988ff9d861025470f9ee6b536"},"previous_names":["jamen-archive/snabby","jamen/snabby"],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/mreinstein/snabby","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mreinstein%2Fsnabby","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mreinstein%2Fsnabby/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mreinstein%2Fsnabby/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mreinstein%2Fsnabby/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mreinstein","download_url":"https://codeload.github.com/mreinstein/snabby/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mreinstein%2Fsnabby/sbom","scorecard":{"id":503455,"data":{"date":"2025-08-11","repo":{"name":"github.com/mreinstein/snabby","commit":"1bde423ec9d4f293b2c15936b3b81daa62385ab3"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.4,"checks":[{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/main.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/main.yml:24: update your workflow using https://app.stepsecurity.io/secureworkflow/mreinstein/snabby/main.yml/main?enable=pin","Warn: npmCommand not pinned by hash: .github/workflows/main.yml:29","Info:   0 out of   1 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 npmCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}}]},"last_synced_at":"2025-08-19T22:38:07.176Z","repository_id":50090943,"created_at":"2025-08-19T22:38:07.176Z","updated_at":"2025-08-19T22:38:07.176Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274422154,"owners_count":25282123,"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","status":"online","status_checked_at":"2025-09-10T02:00:12.551Z","response_time":83,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["javascript","snabbdom","virtual-dom"],"created_at":"2024-08-01T02:00:54.528Z","updated_at":"2025-09-10T06:31:36.216Z","avatar_url":"https://github.com/mreinstein.png","language":"JavaScript","funding_links":["https://www.paypal.me/jamenmarz/5usd"],"categories":["JavaScript"],"sub_categories":[],"readme":"# snabby\n\nUse Snabbdom with template strings\n\n![tests](https://github.com/mreinstein/snabby/actions/workflows/main.yml/badge.svg)\n\n\n```js\nimport html from 'snabby'\n\n// Create vnodes:\nlet foo = html`\u003cdiv\u003eHello Earth\u003c/div\u003e`\nlet bar = html`\u003cdiv\u003eHello Mars\u003c/div\u003e`\n\n// Patch to DOM:\nhtml.update(document.body, foo)\n\n// Patch updates:\nhtml.update(foo, bar)\n```\n\nSnabby is for creating [Snabbdom](https://github.com/snabbdom/snabbdom) [virtual nodes](https://github.com/snabbdom/snabbdom#virtual-node) using template strings, and also patch the nodes using an [`update` function](#snabby_update) (inspired by [`yo-yo`](https://npmjs.com/yo-yo)).  It makes working with an [amazing virtual dom](https://github.com/snabbdom/snabbdom#features) very easy and fun\n\n\n## Installation\n\nSnabby version 2.x is a pure es module. It requires node \u003e= v12.17 or a browser that supports the es module format.\n\nIf you want the older commonjs build, use the `snabby@1.x` npm module\n\n\n## Usage\n\n### `snabby`\n\nA [tag function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_template_literals) that creates a node.  This function is usually required as `html` instead of `snabby`:\n\n```js\nimport html from 'snabby'\n\n// Function to create VNode from params:\nlet greet = name =\u003e html`\n  \u003cdiv class='greet'\u003eHello, ${name}!\u003c/div\u003e\n`\n\nlet node1 = greet('Jamen')\n// Hello, Jamen!\n\nlet node2 = greet('World')\n// Hello, World!\n```\n\nYou have all the [modules documented by Snabbdom](https://github.com/snabbdom/snabbdom#modules-documentation) loaded by default.  See [Directives](#directives) for how to use modules, and [`snabby/create`](#snabby_create) for loading custom modules\n\n\n### Directives\n\nDirectives are attributes that begin with `@`, and let you interact with Snabbdom modules.  In general, the form is `@\u003cname\u003e:[prop]:...`.\n\nHere is an example using the [`props`](https://github.com/snabbdom/snabbdom#the-props-module) module:\n\n```js\nhtml`\u003ca @props=${{ href: '/foo', textContent: 'Hello' }}\u003e\u003c/a\u003e`\n\n// Or using the `:prop` syntax:\nhtml`\u003ca @props:href='/foo' @props:textContent='Hello'\u003e\u003c/a\u003e`\n```\n\nFor the [`eventlisteners`](https://github.com/snabbdom/snabbdom#eventlisteners-module) module, you can use a shorthand by prefixing with `:` instead of `@on:`:\n\n```js\nhtml`\u003cdiv @on:click=${fn}\u003e...\u003c/div\u003e`\n\n// Shorthand:\nhtml`\u003cdiv :click=${fn}\u003e`\n```\n\nDirectives work with any module that makes use of `node.data`.  For example `@props:href` turns into `node.data.props.href`.\n\n\n### Delayed Style properties\n\nSnabbdom offers [`delayed style properties`](https://github.com/snabbdom/snabbdom#delayed-properties) which are set after the next frame.\n\nThis makes it easy to declaratively animate the entry of elements.\n\nThe `all` value of `transition-property` is not supported in snabbdom or snabby.\n\nusage:\n\n```javascript\nhtml```\u003cspan @style:transition=\"opacity 1s ease\"\n             @style:delayed=${ { opacity: '0' } }\u003e\n         hello, world!!\n       \u003c/span\u003e```\n```\n\n\n### `snabby.update(target, node)`\n\nIf you want to put a node on the DOM, or push updates on it (i.e. from events), you use this function.\n\nFirst things first, the Node has to be mounted to the DOM, _before_ you try and update it:\n\n```js\nimport html from 'snabby'\n\nlet visit = location =\u003e html`\n  \u003cdiv class='app'\u003eHello, ${location}!\u003c/div\u003e\n`\n\nlet node1 = visit('Earth')\n\n// Mount node to DOM\nhtml.update(document.body, node1)\n// Hello, Earth!\n```\n\nFrom there, you can patch updates:\n\n```js\nlet node2 = visit('TRAPPIST-1')\n\n// Patch updates to node1\nhtml.update(node1, node2)\n// Hello, TRAPPIST-1!\n```\n\n\n### `snabby/create`\n\nCreate a `snabby` tag function with your own modules.\n\nHere is an equivalent to `snabby` for example:\n\n```js\nimport create                   from './create.js'\nimport { attributesModule }     from 'https://cdn.jsdelivr.net/npm/snabbdom@3/build/package/modules/attributes.js';\nimport { classModule }          from 'https://cdn.jsdelivr.net/npm/snabbdom@3/build/package/modules/class.js';\nimport { propsModule }          from 'https://cdn.jsdelivr.net/npm/snabbdom@3/build/package/modules/props.js';\nimport { styleModule }          from 'https://cdn.jsdelivr.net/npm/snabbdom@3/build/package/modules/style.js';\nimport { eventListenersModule } from 'https://cdn.jsdelivr.net/npm/snabbdom@3/build/package/modules/eventlisteners.js';\n\n\nconst html = create([\n    attributesModule,\n    eventListenersModule,\n    classModule,\n    propsModule,\n    styleModule\n])\n\n```\n\nAs mentioned, you can use directives with 3rd party modules fine.  Open an issue if you can't!\n\n\n### `snabby.thunk(selector, key, renderFn, [stateArguments])`\n\nThe `thunk` function takes a selector, a key for identifying a thunk, a function that returns a vnode and a variable amount of state parameters. If invoked, the render function will receive the state arguments.\n\nThe `renderFn` is invoked only if the `renderFn` is changed or `[stateArguments]` array length or it's elements are changed.\n\n\n```js\nfunction counter (count) {\n\n    function numberView (n) {\n        return html`\u003cspan\u003eNumber is ${n}\u003c/span\u003e`\n    }\n\n    function rand () {\n        const randomInt = Math.ceil(Math.random() * 3)\n        html.update(view, counter(randomInt))\n    }\n\n    const view = html`\n        \u003cdiv class='main'\u003e\n          \u003cspan\u003e${html.thunk('num', numberView, [count])}\u003c/span\u003e\n          \u003cbutton @on:click=${rand}\u003erandom\u003c/button\u003e\n        \u003c/div\u003e`\n}\n\n```\n\nThis is identical to snabbdom's `thunk` function. See https://github.com/snabbdom/snabbdom#thunks for more details.\n\n\n\n## Prior Art\n\nThese ideas come from my time using:\n\n - [`yo-yo`](https://npmjs.com/yo-yo): Inspired some of the API here\n - [`hyperx`](https://npmjs.com/hyperx): Handles the template string parsing here\n - [`choo`](https://npmjs.com/choo): What inspired me to create this module, as I love the API, but not `morphdom` as much\n - [`bel`](https://npmjs.com/bel):  Notable mention.  It's like twin sister to this. DOM and VDOM\n - [`snabbdom`](https://npmjs.com/snabbdom): What gives this the speed\n - [`vue`](https://npmjs.com/vue): A front-end framework that uses `snabbdom` and loosely inspired me\n\n\n## License\n\nMIT © [Jamen Marz](https://git.io/jamen)\n\n---\n\n[![version](https://img.shields.io/npm/v/snabby.svg?style=flat-square)](https://npmjs.com/package/snabby) [![travis](https://img.shields.io/travis/snabby/jamen.svg?style=flat-square)](https://travis-ci.org/snabby/jamen) [![downloads/month](https://img.shields.io/npm/dm/snabby.svg?style=flat-square)](https://npmjs.com/package/snabby) [![downloads](https://img.shields.io/npm/dt/snabby.svg?style=flat-square)](https://npmjs.com/package/snabby) [![license](https://img.shields.io/npm/l/snabby.svg?style=flat-square)](https://npmjs.com/package/snabby) [![support me](https://img.shields.io/badge/support%20me-paypal-green.svg?style=flat-square)](https://www.paypal.me/jamenmarz/5usd) [![follow](https://img.shields.io/github/followers/jamen.svg?style=social\u0026label=Follow)](https://github.com/jamen)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmreinstein%2Fsnabby","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmreinstein%2Fsnabby","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmreinstein%2Fsnabby/lists"}