{"id":17880991,"url":"https://github.com/apaleslimghost/excise","last_synced_at":"2025-03-22T10:31:00.064Z","repository":{"id":143878555,"uuid":"69822245","full_name":"apaleslimghost/excise","owner":"apaleslimghost","description":"a react-like toolkit for web components","archived":false,"fork":false,"pushed_at":"2021-01-20T17:09:11.000Z","size":50,"stargazers_count":7,"open_issues_count":0,"forks_count":3,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-03-17T14:07:42.796Z","etag":null,"topics":["custom-elements","diffhtml","react","shadow-dom"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/apaleslimghost.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,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-10-02T21:08:47.000Z","updated_at":"2021-05-07T15:43:56.000Z","dependencies_parsed_at":"2023-04-17T20:33:12.799Z","dependency_job_id":null,"html_url":"https://github.com/apaleslimghost/excise","commit_stats":null,"previous_names":["quarterto/excise"],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/apaleslimghost%2Fexcise","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/apaleslimghost%2Fexcise/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/apaleslimghost%2Fexcise/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/apaleslimghost%2Fexcise/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/apaleslimghost","download_url":"https://codeload.github.com/apaleslimghost/excise/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244943690,"owners_count":20536279,"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":["custom-elements","diffhtml","react","shadow-dom"],"created_at":"2024-10-28T12:32:52.536Z","updated_at":"2025-03-22T10:31:00.059Z","avatar_url":"https://github.com/apaleslimghost.png","language":"JavaScript","readme":"# excise\n\na toolkit for writing functional-style (react-like) web components, based on [diffhtml](https://github.com/tbranyen/diffhtml)\n\n## install\n\n```sh\nnpm install excise\n```\n\nOr give it a try using [this JSBin](https://jsbin.com/qecuhigisu/edit?html,js,output) in Chrome.\n\n## usage\n\n### basic\n\nyou'll need a browser that supports custom elements v1 and shadow dom v1. chrome 55 will work.\n\n```js\nconst {define, html} = require('excise');\n\ndefine('x-hello-world', () =\u003e html`\u003ch1\u003eit works!\u003c/h1\u003e`);\n\ndocument.body.innerHTML = '\u003cx-hello-world\u003e\u003c/x-hello-world\u003e';\n```\n\n#### polyfills\n\nthe only polyfills i've found that work are [custom-elements](https://github.com/webcomponents/custom-elements) and [shadydom](https://github.com/webcomponents/shadydom). they're not fully on npm yet but they can be installed with:\n\n```sh\nnpm install --save webcomponents/shadydom webcomponents/custom-elements\n```\n\nand then include them with\n\n```js\nrequire('@webcomponents/shadydom');\nrequire('@webcomponents/custom-elements');\n```\n\nor as entry points to your bundle\n\n#### bundling\n\ni've found it difficult to get this working with webpack because it thinks `define` means AMD and even if you disable that plugin it breaks. just use browserify, and don't bother with babel because we need to natively extend `HTMLElement`.\n\n### props\n\nelement attributes are passed as an object to the render function\n\n```js\nconst {define, html} = require('excise');\n\ndefine('x-count', ({count}) =\u003e html`\u003ch1\u003e${count}\u003c/h1\u003e`);\n\ndocument.body.innerHTML = '\u003cx-count count=\"5\"\u003e\u003c/x-count\u003e';\n```\n\npass a third argument to `define` to specify the types of the props, for coercion:\n\n```js\nconst {define, html, types} = require('excise');\n\ndefine('x-increment', ({count}) =\u003e html`\u003ch1\u003e${count + 1}\u003c/h1\u003e`, {count: types.number});\n\ndocument.body.innerHTML = '\u003cx-increment count=\"5\"\u003e\u003c/x-increment\u003e';\n```\n\n### server rendering\n\ncall `renderToString` with an `html` block:\n\n```js\nconst {define, html, renderToString} = require('excise');\n\ndefine('x-foo', () =\u003e html`\u003ch1\u003ehello\u003c/h1\u003e`);\n\nrenderToString(html`\u003cx-foo\u003e\u003c/x-foo\u003e`); //⇒ \u003cx-foo\u003e\u003cspan slot=\"__excise_rendered\"\u003e\u003ch1\u003ehello\u003c/h1\u003e\u003c/span\u003e\u003c/x-foo\u003e\n```\n\nthe extra `span` allows you to reuse the server-rendered markup on the client. when your elements are defined, the `__excise_rendered` slot is ignored.\n\n### events\n\n`on*` attributes are attached to elements as event handlers:\n\n```js\nconst {define, html} = require('excise');\n\ndefine('x-button', () =\u003e html`\u003cbutton onclick=${() =\u003e alert('hello!')}\u003eclick me\u003c/button\u003e`);\n\ndocument.body.innerHTML = '\u003cx-button\u003e\u003c/x-button\u003e';\n```\n\n### state\n\n`StatefulComponent` watches its attributes and rerenders when they change:\n\n```js\nconst {StatefulComponent, html, types} = require('excise');\n\nStatefulComponent.define('x-counter',\n\t(props, el) =\u003e html`\u003cb\u003e${props.count}\u003c/b\u003e \u003cbutton onclick=${() =\u003e el.setAttribute('count', props.count + 1)}\u003eclick me}\u003c/button\u003e`,\n\t{count: types.number}\n);\n\ndocument.body.innerHTML = '\u003cx-counter count=\"1\"\u003e\u003c/x-counter\u003e';\n```\n\nthere's a little bit of oddness here. we can't destructure `count` here like in previous examples, because of how diffhtml renders the button. the actual dom of the button never changes (it's the same structure) so diffhtml leaves it alone when `count` updates. this means the `onclick` handler still closes over `count` from the first run of the render function, and the button only works once.\n\n### shadow dom/slots\n\n```js\nconst {define, html} = require('excise');\n\ndefine('x-shadow', () =\u003e html`\u003cslot name=\"title\" /\u003e \u003ch2\u003eslots work\u003c/h2\u003e`);\n\ndocument.body.innerHTML = '\u003cx-shadow\u003e\u003ch1 slot=\"title\"\u003eit works!\u003c/h1\u003e\u003c/x-shadow\u003e';\n```\n\n### classes\n\n```js\nconst {Component, html, types} = require('excise');\n\ndefine(class XClassComponent extends Component {\n\tstatic get propTypes() {\n\t\treturn {\n\t\t\tgreeting: types.string,\n\t\t};\n\t}\n\n\trender() {\n\t\treturn html`\u003ch1\u003eit works! ${this.props.greeting}\u003c/h1\u003e`;\n\t}\n});\n\ndocument.body.innerHTML = '\u003cx-class-component greeting=\"hello!\"\u003e\u003c/x-class-component\u003e'\n```\n\n## licence\n\nISC. \u0026copy; kara brightwell\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fapaleslimghost%2Fexcise","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fapaleslimghost%2Fexcise","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fapaleslimghost%2Fexcise/lists"}