├── .editorconfig ├── .gitattributes ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .npmignore ├── .nvmrc ├── LICENSE ├── README.md ├── examples ├── aggregation │ ├── components │ │ └── my-component.webc │ ├── page.js │ └── page.webc ├── attributes │ ├── components │ │ └── my-component.webc │ ├── page.js │ └── page.webc ├── custom-transforms │ ├── package.json │ ├── page.js │ └── page.webc ├── helpers │ ├── page.js │ └── page.webc ├── html │ ├── page.js │ └── page.webc ├── imports │ ├── components │ │ ├── my-component.webc │ │ ├── my-dynamic-component.webc │ │ └── my-remapped-component.webc │ ├── page.js │ └── page.webc ├── lookup-attributes │ ├── components │ │ └── my-component.webc │ ├── page.js │ └── page.webc ├── page │ ├── page.js │ └── page.webc ├── properties │ ├── components │ │ └── my-component.webc │ ├── page.js │ └── page.webc ├── raw │ ├── page.js │ └── page.webc ├── render │ ├── components │ │ ├── add-banner-to-css.webc │ │ └── img.webc │ ├── page.js │ └── page.webc ├── scoped-css │ ├── components │ │ ├── my-component-prefixed.webc │ │ └── my-component.webc │ ├── page.js │ └── page.webc ├── slots │ ├── components │ │ └── my-component.webc │ ├── page.js │ └── page.webc └── stream │ ├── page.js │ └── page.webc ├── package.json ├── src ├── assetManager.js ├── ast.js ├── astCache.js ├── astModify.js ├── astQuery.js ├── attributeSerializer.js ├── componentManager.js ├── css.js ├── dataCascade.js ├── fasterVmContext.cjs ├── fsCache.js ├── looping.js ├── moduleResolution.js ├── moduleScript.cjs ├── path.js ├── proxyData.cjs ├── streams.js └── util.js ├── test ├── attributeSerializerTest.js ├── classAttributeTest.js ├── comments-test.js ├── contentPropsTest.js ├── cssTest.js ├── flowcontrolTest.js ├── globalComponentsTest.js ├── issue-104-test.js ├── issue-115-test.js ├── issue-118-test.js ├── issue-135-test.js ├── issue-138-test.js ├── issue-145-test.js ├── issue-152-test.js ├── issue-154-test.js ├── issue-78-test.js ├── issue-79-test.js ├── issue-80-test.js ├── issue-83-test.js ├── issue-84-test.js ├── issue-85-test.js ├── issue-88-test.js ├── issue-91-test.js ├── issue-94-test.js ├── issue-98-test.js ├── looping-test.js ├── moduleResolutionTest.js ├── parserTest.js ├── props-versus-text-test.js ├── proxyDataTest.js ├── renderFunctionJsTest.js ├── scoped-type-js-test.js ├── shadowdom-test.js ├── streamTest.js └── stubs │ ├── alias-paragraph.webc │ ├── asset-buckets.webc │ ├── attribute-quotes.webc │ ├── bucket-inherit-attrs │ ├── component.webc │ └── index.webc │ ├── bucket-inherit │ ├── child.webc │ ├── component.webc │ └── index.webc │ ├── bucket-noinherit │ ├── component.webc │ └── index.webc │ ├── class-mixins.webc │ ├── comment.webc │ ├── component-html-assets.webc │ ├── component-html-resolve.webc │ ├── component-in-page-mode.webc │ ├── component-script-html.webc │ ├── components-list.webc │ ├── components-order.webc │ ├── components │ ├── assets.webc │ ├── child-circular.webc │ ├── child-circular2.webc │ ├── child-css-js-a.webc │ ├── child-css-js-b.webc │ ├── child-css-js-c.webc │ ├── child-css-js-d.webc │ ├── child-css-js-e.webc │ ├── child-css-js-f.webc │ ├── child-root-empty-class.webc │ ├── child-root.webc │ ├── clientside.webc │ ├── component-data.webc │ ├── html-evaluating-props-nothis.webc │ ├── html-evaluating-props.webc │ ├── img-as-root.webc │ ├── img-plain.webc │ ├── img-props.webc │ ├── img.webc │ ├── nested-child-empty.webc │ ├── nested-child-namedslot-style.webc │ ├── nested-child-namedslot.webc │ ├── nested-child-script.webc │ ├── nested-child-slot-before-after.webc │ ├── nested-child-slot-style.webc │ ├── nested-child-slot.webc │ ├── nested-child-style-keep.webc │ ├── nested-child-style-only.webc │ ├── nested-child-style-script-both-empty.webc │ ├── nested-child-style.webc │ ├── nested-child.webc │ ├── override-parent-scoped.webc │ ├── override-parent.webc │ ├── render-css-keep.webc │ ├── render-css-root-override.webc │ ├── render-css-root.webc │ ├── render-css.webc │ ├── render-slots-raw.webc │ ├── render-slots.webc │ ├── root-style.webc │ ├── scoped-override-collision-a.webc │ ├── scoped-override-collision-b.webc │ ├── scoped-override.webc │ ├── scoped-style.webc │ ├── script-html.webc │ ├── shadowroot.webc │ ├── shadowrootmode.webc │ ├── text-link-slot.webc │ ├── with-uid-root.webc │ └── with-uid.webc │ ├── deep-root │ └── component.webc │ ├── defined-style-noroot.webc │ ├── defined-style.webc │ ├── dynamic-attributes-host-component-data │ ├── lol.webc │ └── page.webc │ ├── dynamic-bucket │ ├── component.webc │ └── index.webc │ ├── empty-class.webc │ ├── empty.webc │ ├── externals │ ├── externals-dynamic.webc │ ├── externals-keep.webc │ ├── externals-scoped.webc │ ├── externals-urls-keep.webc │ ├── externals-urls.webc │ ├── externals.webc │ ├── my-script.js │ ├── my-style-scoped.css │ └── my-style.css │ ├── fake_node_modules │ └── @11ty │ │ └── test │ │ └── syntax-highlighter.webc │ ├── global-components │ ├── my-custom-element.webc │ ├── other-custom-element.webc │ └── subfolder │ │ └── my-custom-element.webc │ ├── global-data.webc │ ├── head │ ├── custom-head.webc │ ├── head-is-component.webc │ ├── head.webc │ └── meta.webc │ ├── html-number.webc │ ├── html.webc │ ├── if-component-style.webc │ ├── img-to-img.webc │ ├── img.webc │ ├── import-alias-suffix.webc │ ├── import-alias.webc │ ├── import-keep.webc │ ├── import-twice.webc │ ├── issue-104 │ ├── attrs-component.webc │ ├── attrs-object.webc │ ├── component.webc │ ├── root-attrs-component.webc │ ├── root-component.webc │ ├── root-override-attrs-component.webc │ └── setup-component.webc │ ├── issue-105 │ ├── test-p.webc │ └── test.js │ ├── issue-115 │ └── page.webc │ ├── issue-118 │ ├── oh-no.webc │ └── page.webc │ ├── issue-135 │ ├── component.webc │ └── page.webc │ ├── issue-138 │ ├── img.webc │ └── page.webc │ ├── issue-152 │ ├── child.webc │ ├── parent-nohtml.webc │ └── parent.webc │ ├── issue-154 │ ├── c-blue.webc │ ├── c-red.webc │ └── page.webc │ ├── issue-3 │ ├── my-article.webc │ └── page.webc │ ├── issue-67 │ ├── meta-social.webc │ └── page.webc │ ├── issue-78 │ ├── a-component.webc │ ├── add-banner-to-css-render.webc │ ├── add-banner-to-css-root.webc │ ├── add-banner-to-css.webc │ ├── img.webc │ └── page.webc │ ├── issue-79 │ ├── components │ │ ├── my-component.webc │ │ └── other-component.webc │ └── pages │ │ └── articles │ │ └── my-page.webc │ ├── issue-80-b │ ├── b.webc │ ├── c.webc │ └── page.webc │ ├── issue-80 │ ├── b.webc │ ├── c.webc │ └── page.webc │ ├── issue-85 │ └── page.webc │ ├── issue-91 │ ├── img.webc │ └── page.webc │ ├── issue-94 │ └── component.webc │ ├── issue-98 │ ├── component.webc │ └── page.webc │ ├── looping │ ├── array-object-keys.webc │ ├── array-object-values.webc │ ├── array-value.webc │ ├── array.webc │ ├── complex │ │ ├── data.js │ │ └── entry-point.webc │ ├── components │ │ ├── card-actions.webc │ │ ├── card-content.webc │ │ ├── card-header.webc │ │ ├── card-thing.webc │ │ └── component.webc │ ├── issue-139.webc │ ├── object-key.webc │ ├── object.webc │ ├── scoped-data.webc │ └── script-setup-data.webc │ ├── nested-alias-reference.webc │ ├── nested-alias.webc │ ├── nested-content-with-attr.webc │ ├── nested-content.webc │ ├── nested-link.webc │ ├── nested-multiple-slots-raw.webc │ ├── nested-multiple-slots.webc │ ├── nested-no-shadowdom.webc │ ├── nested-reference.webc │ ├── nested-twice.webc │ ├── nested-webc-keep.webc │ ├── nested-webc-raw.webc │ ├── nested.webc │ ├── no-template.webc │ ├── page-capital-doctype-issue-24.webc │ ├── page.webc │ ├── plaintext-transform.webc │ ├── props-missing-nothis.webc │ ├── props-missing.webc │ ├── props-no-this.webc │ ├── props-versus-text │ └── page.webc │ ├── props.webc │ ├── render-async.webc │ ├── render-async2.webc │ ├── render-child-component.webc │ ├── render-raw-nokeep.webc │ ├── render-raw.webc │ ├── render-require.webc │ ├── render-slots-parent.webc │ ├── render.webc │ ├── render2.webc │ ├── render3.webc │ ├── sample-require.cjs │ ├── scoped-override-collisions.webc │ ├── scoped-override.webc │ ├── scoped-top.webc │ ├── scoped.webc │ ├── script-type.webc │ ├── setup-script │ ├── component.webc │ └── test.js │ ├── slot-fallback-content.webc │ ├── slot-keep.webc │ ├── slot-named-fallback.webc │ ├── slot-named.webc │ ├── slot-nested-2.webc │ ├── slot-nested-3.webc │ ├── slot-nested.webc │ ├── slot-raw.webc │ ├── slot-unused-2.webc │ ├── slot-unused-default.webc │ ├── slot-unused.webc │ ├── slot.webc │ ├── style-merge.webc │ ├── style-override.webc │ ├── style.webc │ ├── template-custom-keep.webc │ ├── template-custom-nested.webc │ ├── template-custom-notype.webc │ ├── template-custom.webc │ ├── template.webc │ ├── two-style.webc │ ├── using-css-keep.webc │ ├── using-css-root-override.webc │ ├── using-css-root.webc │ ├── using-css.webc │ ├── using-img-plain.webc │ ├── using-img.webc │ ├── using-uid.webc │ ├── webc-raw-html-prop.webc │ └── webc-raw-prop.webc └── webc.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | 9 | [/test/**/*] 10 | trim_trailing_whitespace = false 11 | insert_final_newline = unset -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Templates 2 | *.webc text diff=html 3 | 4 | # Reclassify .webc files as HTML: 5 | *.webc linguist-language=HTML -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - main 5 | pull_request: 6 | types: [opened, reopened, synchronize] 7 | 8 | env: 9 | YARN_GPG: no 10 | 11 | jobs: 12 | build: 13 | runs-on: ${{ matrix.os }} 14 | strategy: 15 | matrix: 16 | os: ["ubuntu-latest", "macos-latest", "windows-latest"] 17 | node: ["14", "16", "18"] 18 | name: Node.js ${{ matrix.node }} on ${{ matrix.os }} 19 | steps: 20 | - uses: actions/checkout@v3 21 | 22 | - name: Setup node 23 | uses: actions/setup-node@v3 24 | with: 25 | node-version: ${{ matrix.node }} 26 | 27 | - name: Cache node_modules 28 | uses: actions/cache@v3 29 | id: cache 30 | with: 31 | path: node_modules 32 | key: ${{ runner.os }}-node_modules-${{ hashFiles('**/package.json') }} 33 | 34 | - run: npm install 35 | 36 | - run: npm test 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | playground 4 | .idea 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | test 2 | playground 3 | examples 4 | .* 5 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 14.18 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022–2023 Zach Leatherman @zachleat 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/aggregation/components/my-component.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/aggregation/page.js: -------------------------------------------------------------------------------- 1 | import { WebC } from "../../webc.js"; 2 | 3 | let page = new WebC(); 4 | 5 | page.setInputPath("page.webc"); 6 | page.defineComponents("components/my-component.webc"); 7 | 8 | let { html, css, js, components } = await page.compile(); 9 | console.log({ html, css, js, components }); -------------------------------------------------------------------------------- /examples/aggregation/page.webc: -------------------------------------------------------------------------------- 1 | Default slot -------------------------------------------------------------------------------- /examples/attributes/components/my-component.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/attributes/page.js: -------------------------------------------------------------------------------- 1 | import { WebC } from "../../webc.js"; 2 | 3 | let page = new WebC(); 4 | 5 | page.setInputPath("page.webc"); 6 | page.defineComponents("components/my-component.webc"); 7 | 8 | let { html, css, js, components } = await page.compile(); 9 | console.log({ html, css, js, components }); -------------------------------------------------------------------------------- /examples/attributes/page.webc: -------------------------------------------------------------------------------- 1 | This is the default slot -------------------------------------------------------------------------------- /examples/custom-transforms/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "dependencies": { 4 | "markdown-it": "^13.0.1" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /examples/custom-transforms/page.js: -------------------------------------------------------------------------------- 1 | import MarkdownIt from "markdown-it"; 2 | import { WebC } from "../../webc.js"; 3 | 4 | let page = new WebC(); 5 | 6 | page.setInputPath("page.webc"); 7 | 8 | let md = new MarkdownIt({ html: true }); 9 | 10 | page.setTransform("md", (content) => { 11 | return md.render(content); 12 | }); 13 | 14 | let { html, css, js, components } = await page.compile(); 15 | console.log({ html, css, js, components }); -------------------------------------------------------------------------------- /examples/custom-transforms/page.webc: -------------------------------------------------------------------------------- 1 | 31 | 32 | -------------------------------------------------------------------------------- /examples/helpers/page.js: -------------------------------------------------------------------------------- 1 | import { WebC } from "../../webc.js"; 2 | 3 | let page = new WebC(); 4 | 5 | page.setInputPath("page.webc"); 6 | page.setHelper("alwaysBlue", () => { 7 | return "I'm blue, da ba dee da ba di" 8 | }); 9 | 10 | let { html, css, js, components } = await page.compile(); 11 | console.log({ html, css, js, components }); 12 | -------------------------------------------------------------------------------- /examples/helpers/page.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/html/page.js: -------------------------------------------------------------------------------- 1 | import { WebC } from "../../webc.js"; 2 | 3 | let page = new WebC(); 4 | 5 | page.setInputPath("page.webc"); 6 | 7 | let { html, css, js, components } = await page.compile({ 8 | data: { 9 | dataProperty: "dataValue", 10 | }, 11 | }); 12 | console.log({ html, css, js, components }); 13 | -------------------------------------------------------------------------------- /examples/html/page.webc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /examples/imports/components/my-component.webc: -------------------------------------------------------------------------------- 1 | Components don’t need a root element, y’all. 2 | -------------------------------------------------------------------------------- /examples/imports/components/my-dynamic-component.webc: -------------------------------------------------------------------------------- 1 | This other component is dynamically imported!!! -------------------------------------------------------------------------------- /examples/imports/components/my-remapped-component.webc: -------------------------------------------------------------------------------- 1 | And this other component is a remapped component! -------------------------------------------------------------------------------- /examples/imports/page.js: -------------------------------------------------------------------------------- 1 | import { WebC } from "../../webc.js"; 2 | 3 | let page = new WebC(); 4 | 5 | page.setInputPath("page.webc"); 6 | 7 | // Pass in a glob, using the file name as component name 8 | page.defineComponents("components/**.webc"); 9 | 10 | // Array of file names, using file name as component name 11 | // page.defineComponents(["components/my-component.webc"]); 12 | 13 | // Object maps component name to file name 14 | // page.defineComponents({ 15 | // "my-component": "components/my-component.webc" 16 | // }); 17 | 18 | let { html } = await page.compile(); 19 | console.log({ html }); -------------------------------------------------------------------------------- /examples/imports/page.webc: -------------------------------------------------------------------------------- 1 | WebC Example 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | -------------------------------------------------------------------------------- /examples/lookup-attributes/components/my-component.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/lookup-attributes/page.js: -------------------------------------------------------------------------------- 1 | import { WebC } from "../../webc.js"; 2 | 3 | let page = new WebC(); 4 | 5 | page.setInputPath("page.webc"); 6 | page.defineComponents("components/my-component.webc"); 7 | 8 | let { html, css, js, components } = await page.compile(); 9 | console.log({ html, css, js, components }); -------------------------------------------------------------------------------- /examples/lookup-attributes/page.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/page/page.js: -------------------------------------------------------------------------------- 1 | import { WebC } from "../../webc.js"; 2 | 3 | let page = new WebC(); 4 | 5 | page.setInputPath("page.webc"); 6 | 7 | let { html, css, js, components } = await page.compile(); 8 | console.log({ html, css, js, components }); -------------------------------------------------------------------------------- /examples/page/page.webc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebC Example 6 | 11 | 14 | 17 | 18 | 19 | 24 | WebC *is* HTML. 25 | 26 | 27 | -------------------------------------------------------------------------------- /examples/properties/components/my-component.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/properties/page.js: -------------------------------------------------------------------------------- 1 | import { WebC } from "../../webc.js"; 2 | 3 | let page = new WebC(); 4 | 5 | page.setInputPath("page.webc"); 6 | page.defineComponents("components/my-component.webc"); 7 | 8 | let { html, css, js, components } = await page.compile(); 9 | console.log({ html, css, js, components }); -------------------------------------------------------------------------------- /examples/properties/page.webc: -------------------------------------------------------------------------------- 1 | > -------------------------------------------------------------------------------- /examples/raw/page.js: -------------------------------------------------------------------------------- 1 | import { WebC } from "../../webc.js"; 2 | 3 | let page = new WebC(); 4 | 5 | page.setInputPath("page.webc"); 6 | 7 | let { html, css, js, components } = await page.compile(); 8 | console.log({ html, css, js, components }); 9 | -------------------------------------------------------------------------------- /examples/raw/page.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/render/components/add-banner-to-css.webc: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /examples/render/components/img.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/render/page.js: -------------------------------------------------------------------------------- 1 | import { WebC } from "../../webc.js"; 2 | 3 | let page = new WebC(); 4 | 5 | page.setInputPath("page.webc"); 6 | page.defineComponents({ 7 | "avatar-image": "components/img.webc", 8 | "add-banner-to-css": "components/add-banner-to-css.webc" 9 | }); 10 | 11 | let { html, css, js, components } = await page.compile(); 12 | console.log({ html, css, js, components }); -------------------------------------------------------------------------------- /examples/render/page.webc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | /* Some other CSS content */ 5 | -------------------------------------------------------------------------------- /examples/scoped-css/components/my-component-prefixed.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/scoped-css/components/my-component.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/scoped-css/page.js: -------------------------------------------------------------------------------- 1 | import { WebC } from "../../webc.js"; 2 | 3 | let page = new WebC(); 4 | 5 | page.setInputPath("page.webc"); 6 | page.defineComponents("components/**.webc"); 7 | 8 | let { html, css, js, components } = await page.compile(); 9 | console.log({ html, css, js, components }); -------------------------------------------------------------------------------- /examples/scoped-css/page.webc: -------------------------------------------------------------------------------- 1 | Default slot 2 | Prefixed slot -------------------------------------------------------------------------------- /examples/slots/components/my-component.webc: -------------------------------------------------------------------------------- 1 |

Hello World!

2 | 3 | 4 | 5 |
6 | 7 |
8 | 9 | 14 | 15 | 18 | -------------------------------------------------------------------------------- /examples/slots/page.js: -------------------------------------------------------------------------------- 1 | import { WebC } from "../../webc.js"; 2 | 3 | let page = new WebC(); 4 | 5 | page.setInputPath("page.webc"); 6 | page.defineComponents("components/my-component.webc"); 7 | 8 | let { html } = await page.compile(); 9 | console.log({ html }); 10 | -------------------------------------------------------------------------------- /examples/slots/page.webc: -------------------------------------------------------------------------------- 1 | WebC Example 2 | 3 | 4 | 5 | 6 | 7 | This is the default slot 8 | 9 | 10 | 11 |
This is a named slot
12 |
13 | 14 | 15 | 16 |
This is another named slot with slot tag
17 |
-------------------------------------------------------------------------------- /examples/stream/page.js: -------------------------------------------------------------------------------- 1 | import { WebC } from "../../webc.js"; 2 | 3 | let page = new WebC(); 4 | 5 | page.setInputPath("page.webc"); 6 | 7 | let { html, css, js } = await page.stream(); 8 | 9 | // html stream 10 | await printStream(html); 11 | 12 | // css stream 13 | await printStream(css); 14 | 15 | // js stream 16 | await printStream(js); 17 | 18 | async function printStream(stream) { 19 | const chunks = []; 20 | for await (const chunk of stream) { 21 | chunks.push(Buffer.from(chunk)); 22 | } 23 | console.log(Buffer.concat(chunks).toString("utf-8")); 24 | } -------------------------------------------------------------------------------- /examples/stream/page.webc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebC Example 6 | 14 | 17 | 18 | 19 | WebC *is* HTML. 20 | 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@11ty/webc", 3 | "version": "0.11.4", 4 | "description": "Single File Web Components", 5 | "main": "webc.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "npx ava" 9 | }, 10 | "publishConfig": { 11 | "access": "public" 12 | }, 13 | "license": "MIT", 14 | "engines": { 15 | "node": ">=14.18" 16 | }, 17 | "funding": { 18 | "type": "opencollective", 19 | "url": "https://opencollective.com/11ty" 20 | }, 21 | "keywords": [ 22 | "web-components", 23 | "custom-elements", 24 | "html" 25 | ], 26 | "author": { 27 | "name": "Zach Leatherman", 28 | "email": "zachleatherman@gmail.com", 29 | "url": "https://zachleat.com/" 30 | }, 31 | "ava": { 32 | "failFast": false, 33 | "files": [ 34 | "./test/*Test.js", 35 | "./test/**/*test.js" 36 | ] 37 | }, 38 | "dependencies": { 39 | "@11ty/eleventy-utils": "^1.0.1", 40 | "css-tree": "^2.3.1", 41 | "dependency-graph": "^0.11.0", 42 | "entities": "^4.4.0", 43 | "fast-glob": "^3.2.12", 44 | "is-glob": "^4.0.3", 45 | "nanoid": "^4.0.1", 46 | "node-retrieve-globals": "^2.0.7", 47 | "parse5": "^7.1.2" 48 | }, 49 | "devDependencies": { 50 | "@11ty/eleventy-img": "^3.0.0", 51 | "ava": "^5.2.0", 52 | "markdown-it": "^13.0.1", 53 | "typescript": "^5.0.2" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/assetManager.js: -------------------------------------------------------------------------------- 1 | class AssetManager { 2 | constructor(depGraph, bucketsGraph) { 3 | this.graph = depGraph; 4 | this.bucketsGraph = bucketsGraph; 5 | } 6 | 7 | get orderedComponentList() { 8 | if(!this._ordered) { 9 | this._ordered = this.graph.overallOrder().reverse(); 10 | } 11 | return this._ordered; 12 | } 13 | 14 | get orderedComponentListForAssets() { 15 | let foundIslands = false; 16 | let deps = this.graph.overallOrder().filter(entry => { 17 | if(!foundIslands && entry.endsWith("./node_modules/@11ty/is-land/is-land.webc")) { 18 | foundIslands = entry; 19 | return false; 20 | } 21 | return true; 22 | }); 23 | if(foundIslands) { 24 | deps.unshift(foundIslands); 25 | } 26 | return deps; 27 | } 28 | 29 | getBundledAssets(assets) { 30 | let codeCheck = {}; 31 | let buckets = {}; 32 | 33 | for(let type in assets.buckets) { 34 | buckets[type] = {}; 35 | 36 | for(let bucketName of assets.buckets[type]) { 37 | buckets[type][bucketName] = this.getOrderedAssetsSet(assets[type], bucketName); 38 | } 39 | 40 | for(let bucketName in buckets[type]) { 41 | // filter out duplicate code and elevate to the next upstream bucket 42 | for(let bucketCodeEntry of buckets[type][bucketName]) { 43 | if(!codeCheck[bucketCodeEntry]) { 44 | codeCheck[bucketCodeEntry] = bucketName; 45 | continue; 46 | } 47 | let deps = this.bucketsGraph.dependenciesOf(bucketName); 48 | let newBucketName = deps[deps.length - 1]; 49 | 50 | // delete from previous bucket even if newBucketName is not defined 51 | // e.g. already in `async` and `default`, delete from `async` keep in `default` 52 | let previousBucketName = codeCheck[bucketCodeEntry]; 53 | buckets[type][previousBucketName].delete(bucketCodeEntry); 54 | 55 | if(newBucketName) { 56 | // delete current bucket duplicate 57 | buckets[type][bucketName].delete(bucketCodeEntry); 58 | 59 | // move to next upstream inherited bucket 60 | if(!buckets[type][newBucketName]) { 61 | buckets[type][newBucketName] = new Set(); 62 | } 63 | buckets[type][newBucketName].add(bucketCodeEntry); 64 | } 65 | } 66 | } 67 | 68 | // iterate over buckets to make arrays 69 | for(let bucketName in buckets[type]) { 70 | buckets[type][bucketName] = Array.from(buckets[type][bucketName]); 71 | 72 | // no empty buckets (except default) 73 | if(bucketName !== "default" && buckets[type][bucketName].length === 0) { 74 | delete buckets[type][bucketName]; 75 | } 76 | } 77 | } 78 | 79 | let returnObject = { buckets }; 80 | 81 | // default buckets for js and css are returned separately { css, js, buckets: {} } 82 | if(buckets?.css?.default) { 83 | returnObject.css = buckets.css.default; 84 | delete buckets.css.default; 85 | } 86 | if(buckets?.js?.default) { 87 | returnObject.js = buckets.js.default; 88 | delete buckets.js.default; 89 | } 90 | 91 | return returnObject; 92 | } 93 | 94 | getOrderedAssetsSet(assetObject, bucket = "default") { 95 | let assets = new Set(); 96 | let components = this.orderedComponentListForAssets; 97 | 98 | for(let component of components) { 99 | if(assetObject[component] && assetObject[component][bucket]) { 100 | for(let entry of assetObject[component][bucket]) { 101 | assets.add(entry); 102 | } 103 | } 104 | } 105 | return assets; 106 | } 107 | } 108 | 109 | export { AssetManager }; -------------------------------------------------------------------------------- /src/astCache.js: -------------------------------------------------------------------------------- 1 | import { parse } from "parse5"; 2 | 3 | class AstCache { 4 | constructor() { 5 | this.ast = {}; 6 | } 7 | 8 | get(contents) { 9 | if(!this.ast[contents]) { 10 | this.ast[contents] = parse(contents, { 11 | scriptingEnabled: true, 12 | sourceCodeLocationInfo: true, 13 | }); 14 | } 15 | 16 | return this.ast[contents]; 17 | } 18 | } 19 | 20 | export { AstCache }; -------------------------------------------------------------------------------- /src/astModify.js: -------------------------------------------------------------------------------- 1 | // Take extreme care when using these utilities, they mutate the Live AST 2 | 3 | class AstModify { 4 | static addAttribute(node, name, value) { 5 | node.attrs.push({ name, value }); 6 | } 7 | 8 | // Not in use 9 | // static removeAttribute(node, name) { 10 | // let index = node.attrs.findIndex(attr => attr.name === name); 11 | // if(index !== -1) { 12 | // node.attrs.splice(index, 1); 13 | // } 14 | // } 15 | } 16 | 17 | export { AstModify }; -------------------------------------------------------------------------------- /src/astQuery.js: -------------------------------------------------------------------------------- 1 | 2 | import { AstSerializer } from "./ast.js"; 3 | 4 | class AstQuery { 5 | // List from the parse5 serializer 6 | // https://github.com/inikulin/parse5/blob/3955dcc158031cc773a18517d2eabe8b17107aa3/packages/parse5/lib/serializer/index.ts 7 | static voidElements = { 8 | area: true, 9 | base: true, 10 | basefont: true, 11 | bgsound: true, 12 | br: true, 13 | col: true, 14 | embed: true, 15 | frame: true, 16 | hr: true, 17 | img: true, 18 | input: true, 19 | keygen: true, 20 | link: true, 21 | meta: true, 22 | param: true, 23 | source: true, 24 | track: true, 25 | wbr: true, 26 | }; 27 | 28 | /* Tag Names */ 29 | static getTagName(node) { 30 | let is = AstQuery.getAttributeValue(node, AstSerializer.attrs.IS); 31 | if(is) { 32 | return is; 33 | } 34 | 35 | return node.tagName; 36 | } 37 | 38 | static isVoidElement(tagName) { 39 | return AstQuery.voidElements[tagName] || false; 40 | } 41 | 42 | /* Specific queries */ 43 | static getSlotTargets(node) { 44 | let targetNodes = AstQuery.findAllElements(node, "slot"); 45 | let map = {}; 46 | for(let target of targetNodes) { 47 | let name = AstQuery.getAttributeValue(target, "name") || "default"; 48 | map[name] = true; 49 | } 50 | return map; 51 | } 52 | 53 | static isLinkStylesheetNode(tagName, node) { 54 | return tagName === "link" && AstQuery.getAttributeValue(node, "rel") === "stylesheet"; 55 | } 56 | 57 | // filter out webc:setup 58 | static isScriptNode(tagName, node) { 59 | return tagName === "script" && !AstQuery.hasAttribute(node, AstSerializer.attrs.SETUP); 60 | } 61 | 62 | static getExternalSource(tagName, node) { 63 | if(AstQuery.isLinkStylesheetNode(tagName, node)) { 64 | return AstQuery.getAttributeValue(node, "href"); 65 | } 66 | 67 | if(AstQuery.isScriptNode(tagName, node)) { 68 | return AstQuery.getAttributeValue(node, "src"); 69 | } 70 | } 71 | 72 | /* Attributes */ 73 | static hasAttribute(node, attributeName) { 74 | return (node.attrs || []).find(({name}) => name === attributeName) !== undefined; 75 | } 76 | 77 | static hasAnyAttribute(node, attributes) { 78 | let lookup = {}; 79 | for(let name of attributes) { 80 | lookup[name] = true; 81 | } 82 | return (node.attrs || []).find(({name}) => lookup[name]) !== undefined; 83 | } 84 | 85 | static getAttributeValue(node, attributeName) { 86 | let nameAttr = (node.attrs || []).find(({name}) => name === attributeName); 87 | 88 | if(!nameAttr) { 89 | // Same as Element.getAttribute 90 | // https://developer.mozilla.org/en-US/docs/Web/API/Element/getAttribute 91 | return null; 92 | } 93 | 94 | return nameAttr?.value; 95 | } 96 | 97 | static getRootNodeMode(node) { 98 | // override is when child component definitions override the host component tag 99 | let rootAttributeValue = AstQuery.getAttributeValue(node, AstSerializer.attrs.ROOT); 100 | if(rootAttributeValue) { 101 | return rootAttributeValue; 102 | } 103 | // merge is when webc:root attributes flow up to the host component (and the child component tag is ignored) 104 | if(rootAttributeValue === "") { 105 | return "merge"; 106 | } 107 | return false; 108 | } 109 | 110 | static getRootAttributes(component, scopedStyleHash) { 111 | let attrs = []; 112 | 113 | // webc:root Attributes 114 | let tops = AstQuery.getTopLevelNodes(component, [], [AstSerializer.attrs.ROOT]); 115 | for(let root of tops) { 116 | for(let attr of root.attrs) { 117 | if(attr.name !== AstSerializer.attrs.ROOT) { 118 | attrs.push({ name: attr.name, value: attr.value }); 119 | } 120 | } 121 | } 122 | 123 | if(scopedStyleHash) { 124 | // it’s okay if there are other `class` attributes, we merge them later 125 | attrs.push({ name: "class", value: scopedStyleHash }); 126 | } 127 | 128 | return attrs; 129 | } 130 | 131 | /* Declarative Shadow DOM */ 132 | static isDeclarativeShadowDomNode(node) { 133 | let tagName = AstQuery.getTagName(node); 134 | return tagName === "template" && (AstQuery.hasAttribute(node, "shadowroot") || AstQuery.hasAttribute(node, "shadowrootmode")) 135 | } 136 | 137 | static hasDeclarativeShadowDomChild(node) { 138 | return AstQuery.findElement(node, "template", ["shadowroot", "shadowrootmode"]); 139 | } 140 | 141 | /* Content */ 142 | static getTextContent(node) { // used for style hashes 143 | let content = []; 144 | for(let child of node.childNodes || []) { 145 | if(child.nodeName === "#text") { 146 | content.push(child.value); 147 | } 148 | } 149 | return content; 150 | } 151 | 152 | static hasTextContent(node) { 153 | return AstQuery.getTextContent(node).find(entry => entry.trim().length > 0) !== undefined; 154 | } 155 | 156 | 157 | /* Shallow element finds */ 158 | static getImplicitRootNodes(node) { 159 | return [ 160 | AstQuery.findElement(node, "body"), 161 | AstQuery.findElement(node, "head") 162 | ].filter(node => !!node); 163 | } 164 | 165 | static getTopLevelNodes(node, tagNames = [], webcAttrs = []) { 166 | let roots = AstQuery.getImplicitRootNodes(node); 167 | if(roots.length === 0) { 168 | throw new Error("Unable to find component root, expected an implicit or "); 169 | } 170 | 171 | let children = []; 172 | for(let root of roots) { 173 | for(let child of AstQuery.getChildren(root, tagNames, webcAttrs)) { 174 | children.push(child); 175 | } 176 | } 177 | return children; 178 | } 179 | 180 | static getChildren(parentNode, tagNames = [], attrCheck = []) { 181 | if(!parentNode) { 182 | return []; 183 | } 184 | if(typeof tagNames === "string") { 185 | tagNames = [tagNames]; 186 | } 187 | if(!tagNames || Array.isArray(tagNames)) { 188 | tagNames = new Set(tagNames); 189 | } 190 | 191 | let results = []; 192 | for(let child of parentNode.childNodes || []) { 193 | let tagName = AstQuery.getTagName(child); 194 | if(tagNames.size === 0 || tagNames.has(tagName)) { 195 | if(attrCheck.length === 0 || attrCheck.find(attr => AstQuery.hasAttribute(child, attr))) { 196 | results.push(child); 197 | } 198 | } 199 | } 200 | 201 | return results; 202 | } 203 | 204 | static getFirstTopLevelNode(node, tagName, attrName) { 205 | let roots = AstQuery.getImplicitRootNodes(node); 206 | if(roots.length === 0) { 207 | throw new Error("Unable to find component root, expected an implicit or "); 208 | } 209 | 210 | for(let root of roots) { 211 | let match = AstQuery.findFirstChild(root, tagName, attrName); 212 | if(match) { 213 | return match; 214 | } 215 | } 216 | } 217 | 218 | static findFirstChild(parentNode, tagName, attrName) { 219 | for(let child of parentNode?.childNodes || []) { 220 | if(!tagName || tagName === AstQuery.getTagName(child)) { 221 | if(!attrName || AstQuery.hasAttribute(child, attrName)) { 222 | return child; 223 | } 224 | } 225 | } 226 | } 227 | 228 | /* Deep element finds */ 229 | static findAllElements(root, tagName) { 230 | let results = []; 231 | let rootTagName = AstQuery.getTagName(root); 232 | if(rootTagName === tagName) { 233 | results.push(root); 234 | } 235 | for(let child of root.childNodes || []) { 236 | for(let node of AstQuery.findAllElements(child, tagName)) { 237 | results.push(node); 238 | } 239 | } 240 | 241 | return results; 242 | } 243 | 244 | static findElement(root, tagName, attrCheck = []) { 245 | let rootTagName = AstQuery.getTagName(root); 246 | if(rootTagName === tagName) { 247 | if(attrCheck.length === 0 || attrCheck.find(attr => AstQuery.hasAttribute(root, attr))) { 248 | return root; 249 | } 250 | } 251 | for(let child of root.childNodes || []) { 252 | let node = AstQuery.findElement(child, tagName, attrCheck); 253 | if(node) { 254 | return node; 255 | } 256 | } 257 | } 258 | } 259 | 260 | export { AstQuery }; -------------------------------------------------------------------------------- /src/attributeSerializer.js: -------------------------------------------------------------------------------- 1 | import { ModuleScript } from "./moduleScript.cjs"; 2 | import { escapeAttribute } from 'entities/lib/escape.js'; 3 | 4 | class AttributeSerializer { 5 | static prefixes = { 6 | prop: "@", 7 | dynamic: ":", 8 | dynamicProp: ":@", 9 | } 10 | 11 | // Merge multiple style/class attributes into a single one, operates on :dynamic and @prop but with transformed values 12 | // Usage by: `getString` function when writing attributes to the HTML tag output 13 | // Usage by: when generating data object for render functions 14 | static mergeAttributes(attrs = []) { 15 | let merged = { 16 | style: { 17 | value: [], 18 | splitDelimiter: ";", 19 | joinDelimiter: "; " 20 | }, 21 | class: { 22 | value: [], // de-dupe individual classes 23 | splitDelimiter: " ", 24 | joinDelimiter: " ", 25 | deduplicate: true, 26 | } 27 | }; 28 | 29 | for(let j = 0, k = attrs.length; j { 78 | if(j === 0) { 79 | return entry; 80 | } 81 | return entry.slice(0, 1).toUpperCase() + entry.slice(1); 82 | }).join(""); 83 | } 84 | return name; 85 | } 86 | 87 | static peekAttribute(name) { 88 | if(name.startsWith(AttributeSerializer.prefixes.dynamicProp)) { 89 | return { 90 | name: name.slice(AttributeSerializer.prefixes.dynamicProp.length), 91 | privacy: "private", // property 92 | evaluation: "script", 93 | }; 94 | } 95 | 96 | if(name.startsWith(AttributeSerializer.prefixes.prop)) { 97 | return { 98 | name: name.slice(AttributeSerializer.prefixes.prop.length), 99 | privacy: "private", // property 100 | evaluation: false, 101 | }; 102 | } 103 | 104 | if(name.startsWith(AttributeSerializer.prefixes.dynamic)) { 105 | return { 106 | name: name.slice(AttributeSerializer.prefixes.dynamic.length), 107 | privacy: "public", 108 | evaluation: "script", 109 | }; 110 | } 111 | 112 | return { 113 | name, 114 | privacy: name.startsWith("webc:") ? "private" : "public", 115 | evaluation: false, 116 | }; 117 | } 118 | 119 | // Remove props prefixes, swaps dash to camelcase 120 | // Keeps private entries (used in data) 121 | static async normalizeAttributesForData(attrs) { 122 | let newData = {}; 123 | 124 | for(let name in attrs) { 125 | let value = attrs[name]; 126 | 127 | // prop does nothing 128 | // prop-name becomes propName 129 | // @prop-name and :prop-name prefixes should already be removed 130 | newData[AttributeSerializer.camelCaseAttributeName(name)] = value; 131 | 132 | // Maintain privacy in new object 133 | let privacy = attrs[`${name}___webc_privacy`]; 134 | if(privacy) { 135 | AttributeSerializer.setKeyPrivacy(newData, name, privacy); 136 | } 137 | } 138 | 139 | return newData; 140 | } 141 | 142 | static getPublicAttributesAsObject(attrs) { 143 | let newData = {}; 144 | 145 | for(let name in attrs) { 146 | // Maintain privacy in new object 147 | let privacy = attrs[`${name}___webc_privacy`]; 148 | if(privacy !== "private") { 149 | newData[name] = attrs[name]; 150 | } 151 | } 152 | 153 | return newData; 154 | } 155 | 156 | static async evaluateAttribute(rawName, value, data, scriptContextKey) { 157 | let {name, evaluation, privacy} = AttributeSerializer.peekAttribute(rawName); 158 | let evaluatedValue = value; 159 | if(evaluation === "script") { 160 | let { returns } = await ModuleScript.evaluateScriptInline(value, data, `Evaluating a dynamic ${rawName.startsWith(AttributeSerializer.prefixes.dynamicProp) ? 'prop' : 'attribute' } failed: \`${rawName}="${value}"\`.`, scriptContextKey); 161 | evaluatedValue = returns; 162 | } 163 | 164 | return { 165 | name, 166 | rawName, 167 | value: evaluatedValue, 168 | rawValue: value, 169 | evaluation, 170 | privacy, 171 | }; 172 | } 173 | 174 | // attributesArray: parse5 format, Array of [{name, value}] 175 | // returns: same array with additional properties added 176 | static async evaluateAttributesArray(attributesArray, data, scriptContextKey) { 177 | let evaluated = []; 178 | for(let attr of attributesArray) { 179 | evaluated.push(AttributeSerializer.evaluateAttribute(attr.name, attr.value, data, scriptContextKey).then((result) => { 180 | let { name, rawName, value, rawValue, evaluation, privacy } = result; 181 | let entry = {}; 182 | entry.rawName = rawName; 183 | entry.rawValue = rawValue; 184 | 185 | entry.name = name; 186 | entry.value = value; 187 | entry.privacy = privacy; 188 | entry.evaluation = evaluation; 189 | return entry; 190 | })); 191 | } 192 | return Promise.all(evaluated); 193 | } 194 | 195 | static setKeyPrivacy(obj, name, privacy) { 196 | Object.defineProperty(obj, `${name}___webc_privacy`, { 197 | value: privacy || "private" 198 | }); 199 | } 200 | 201 | static getString(finalAttributesObject) { 202 | let str = []; 203 | 204 | for(let name in finalAttributesObject) { 205 | let value = finalAttributesObject[name]; 206 | 207 | // Filter out private props (including webc:) 208 | if(finalAttributesObject[`${name}___webc_privacy`] === "private") { 209 | continue; 210 | } 211 | 212 | // don’t cast `undefined`, `false` to "undefined", "false" 213 | if(value || value === "") { 214 | value = `${value}`; 215 | } 216 | 217 | // Note that AST from parse5 returns <* attrName> as { attrName: "" } instead of undefined 218 | if (value && value !== "") { 219 | // Note that backslash does *not* escape nested quotes in HTML 220 | // e.g. <* attrName="\"test"> parses as <* attrName="\" test"=""> 221 | // via https://github.com/inikulin/parse5/blob/159ef28fb287665b118c71e1c5c65aba58979e40/packages/parse5-html-rewriting-stream/lib/index.ts 222 | value = `="${escapeAttribute(value)}"` 223 | } 224 | 225 | if(value || value === "") { 226 | str.push(` ${name}${value}`); 227 | } 228 | } 229 | return str.join(""); 230 | } 231 | } 232 | 233 | export { AttributeSerializer }; 234 | -------------------------------------------------------------------------------- /src/componentManager.js: -------------------------------------------------------------------------------- 1 | import { createHash } from "crypto"; 2 | 3 | import { WebC } from "../webc.js"; 4 | import { AstQuery } from "./astQuery.js"; 5 | import { AstModify } from "./astModify.js"; 6 | import { AstSerializer } from "./ast.js"; 7 | import { ModuleScript } from "./moduleScript.cjs"; 8 | 9 | class ComponentManager { 10 | constructor() { 11 | this.parsingPromises = {}; 12 | this.components = {}; 13 | this.hashOverrides = {}; 14 | } 15 | 16 | static getNewLineStartIndeces(content) { 17 | let lineStarts = []; 18 | let sum = 0; 19 | let lineEnding = "\n"; 20 | // this should work okay with \r\n too, \r will just be treated as another character 21 | for(let line of content.split(lineEnding)) { 22 | lineStarts.push(sum); 23 | sum += line.length + lineEnding.length; 24 | } 25 | return lineStarts; 26 | } 27 | 28 | async getSetupScriptValue(component, filePath, dataCascade) { 29 | // Test`); 144 | 145 | let { html, css, js, components } = await component.compile(); 146 | 147 | t.deepEqual(js, []); 148 | t.deepEqual(css, []); 149 | t.deepEqual(components, []); 150 | 151 | t.is(html, `Test`); 152 | }); 153 | 154 | test("Using webc:if=true on a style", async t => { 155 | let component = new WebC(); 156 | 157 | component.setBundlerMode(true); 158 | component.setContent(`Test`); 159 | 160 | let { html, css, js, components } = await component.compile(); 161 | 162 | t.deepEqual(js, []); 163 | t.deepEqual(css, [`* { color: red }`]); 164 | t.deepEqual(components, []); 165 | 166 | t.is(html, `Test`); 167 | }); 168 | 169 | test("Using webc:if on a style based on host component attribute (true)", async t => { 170 | let component = new WebC(); 171 | 172 | component.setBundlerMode(true); 173 | component.setContent(``); 174 | component.defineComponents("./test/stubs/if-component-style.webc"); 175 | 176 | let { html, css, js, components } = await component.compile(); 177 | 178 | t.deepEqual(js, []); 179 | t.deepEqual(css, [`* { color: red }`]); 180 | 181 | t.is(html, `Test`); 182 | }); 183 | 184 | test("Using webc:if on a style based on host component attribute (false)", async t => { 185 | let component = new WebC(); 186 | 187 | component.setBundlerMode(true); 188 | component.setContent(``); 189 | component.defineComponents("./test/stubs/if-component-style.webc"); 190 | 191 | let { html, css, js, components } = await component.compile(); 192 | 193 | t.deepEqual(js, []); 194 | t.deepEqual(css, []); 195 | 196 | t.is(html, `Test`); 197 | }); 198 | 199 | test("Using webc:if=false web:else", async t => { 200 | let component = new WebC(); 201 | 202 | component.setContent(` 203 | 204 | 205 |
Hi
206 |
Bye
207 | 208 | `); 209 | 210 | let { html, css, js, components } = await component.compile({ 211 | data: {} 212 | }); 213 | 214 | t.deepEqual(js, []); 215 | t.deepEqual(css, []); 216 | t.deepEqual(components, []); 217 | 218 | t.is(html, ` 219 | 220 | 221 | 222 |
Bye
223 | 224 | 225 | `); 226 | }); 227 | 228 | test("Using webc:if=true web:else", async t => { 229 | let component = new WebC(); 230 | 231 | component.setContent(` 232 | 233 | 234 |
Hi
235 |
Bye
236 | 237 | `); 238 | 239 | let { html, css, js, components } = await component.compile({ 240 | data: {} 241 | }); 242 | 243 | t.deepEqual(js, []); 244 | t.deepEqual(css, []); 245 | t.deepEqual(components, []); 246 | 247 | t.is(html, ` 248 | 249 | 250 |
Hi
251 | 252 | 253 | 254 | `); 255 | }); 256 | 257 | test("Using webc:if=true webc:elseif=false web:else", async t => { 258 | let component = new WebC(); 259 | 260 | component.setContent(` 261 | 262 | 263 |
Hi
264 |
Yo
265 |
Bye
266 | 267 | `); 268 | 269 | let { html, css, js, components } = await component.compile({ 270 | data: {} 271 | }); 272 | 273 | t.deepEqual(js, []); 274 | t.deepEqual(css, []); 275 | t.deepEqual(components, []); 276 | 277 | t.is(html, ` 278 | 279 | 280 |
Hi
281 | 282 | 283 | 284 | 285 | `); 286 | }); 287 | 288 | test("Using webc:if=false webc:elseif=true web:else", async t => { 289 | let component = new WebC(); 290 | 291 | component.setContent(` 292 | 293 | 294 |
Hi
295 |
Yo
296 |
Bye
297 | 298 | `); 299 | 300 | let { html, css, js, components } = await component.compile({ 301 | data: {} 302 | }); 303 | 304 | t.deepEqual(js, []); 305 | t.deepEqual(css, []); 306 | t.deepEqual(components, []); 307 | 308 | t.is(html, ` 309 | 310 | 311 | 312 |
Yo
313 | 314 | 315 | 316 | `); 317 | }); 318 | 319 | test("Using webc:if=false webc:elseif=false web:else", async t => { 320 | let component = new WebC(); 321 | 322 | component.setContent(` 323 | 324 | 325 |
Hi
326 |
Yo
327 |
Bye
328 | 329 | `); 330 | 331 | let { html, css, js, components } = await component.compile({ 332 | data: {} 333 | }); 334 | 335 | t.deepEqual(js, []); 336 | t.deepEqual(css, []); 337 | t.deepEqual(components, []); 338 | 339 | t.is(html, ` 340 | 341 | 342 | 343 | 344 |
Bye
345 | 346 | 347 | `); 348 | }); 349 | 350 | test("Using webc:if=true webc:elseif=true web:else", async t => { 351 | let component = new WebC(); 352 | 353 | component.setContent(` 354 | 355 | 356 |
Hi
357 |
Yo
358 |
Bye
359 | 360 | `); 361 | 362 | let { html, css, js, components } = await component.compile({ 363 | data: {} 364 | }); 365 | 366 | t.deepEqual(js, []); 367 | t.deepEqual(css, []); 368 | t.deepEqual(components, []); 369 | 370 | t.is(html, ` 371 | 372 | 373 |
Hi
374 | 375 | 376 | 377 | 378 | `); 379 | }); 380 | 381 | test("Solo webc:else throws an error", async t => { 382 | let component = new WebC(); 383 | 384 | component.setContent(` 385 | 386 | 387 |
Hi
388 |
Yo
389 |
Bye
390 | 391 | `); 392 | 393 | await t.throwsAsync(component.compile(), { 394 | message: `webc:else expected an webc:if or webc:elseif on the previous sibling!` 395 | }); 396 | }); 397 | 398 | test("Solo webc:elseif throws an error", async t => { 399 | let component = new WebC(); 400 | 401 | component.setContent(` 402 | 403 | 404 |
Hi
405 |
Yo
406 |
Bye
407 | 408 | `); 409 | 410 | await t.throwsAsync(component.compile(), { 411 | message: `webc:elseif expected an webc:if or webc:elseif on the previous sibling!` 412 | }); 413 | }); 414 | 415 | test("Flow control works fine with comments in the middle", async t => { 416 | let component = new WebC(); 417 | 418 | component.setContent(` 419 | 420 | 421 |
Hi
422 | 423 |
Bye
424 | 425 | `); 426 | 427 | let { html, css, js, components } = await component.compile({ 428 | data: {} 429 | }); 430 | 431 | t.deepEqual(js, []); 432 | t.deepEqual(css, []); 433 | t.deepEqual(components, []); 434 | 435 | t.is(html, ` 436 | 437 | 438 | 439 | 440 |
Bye
441 | 442 | 443 | `); 444 | }); -------------------------------------------------------------------------------- /test/globalComponentsTest.js: -------------------------------------------------------------------------------- 1 | import test from "ava"; 2 | 3 | import { WebC } from "../webc.js"; 4 | 5 | test("Uses a global component (register by glob)", async t => { 6 | let component = new WebC(); 7 | component.setContent(``); 8 | component.defineComponents("./test/stubs/global-components/*"); 9 | 10 | let { html, css, js, components } = await component.compile(); 11 | 12 | t.is(html, `This is a global component.`); 13 | 14 | t.deepEqual(js, []); 15 | t.deepEqual(css, []); 16 | t.deepEqual(components, ["./test/stubs/global-components/my-custom-element.webc"]); 17 | }); 18 | 19 | test("Uses a global component (registered explicitly with object)", async t => { 20 | let component = new WebC(); 21 | component.setContent(``); 22 | 23 | component.defineComponents({ 24 | "my-custom-element": "./test/stubs/global-components/my-custom-element.webc" 25 | }); 26 | 27 | let { html, css, js, components } = await component.compile(); 28 | 29 | t.is(html, `This is a global component.`); 30 | 31 | t.deepEqual(js, []); 32 | t.deepEqual(css, []); 33 | t.deepEqual(components, ["./test/stubs/global-components/my-custom-element.webc"]); 34 | }); 35 | 36 | test("Uses a global component (registered explicitly with Array)", async t => { 37 | let component = new WebC(); 38 | component.setContent(``); 39 | 40 | component.defineComponents(["./test/stubs/global-components/my-custom-element.webc"]); 41 | 42 | let { html, css, js, components } = await component.compile(); 43 | 44 | t.is(html, `This is a global component.`); 45 | 46 | t.deepEqual(js, []); 47 | t.deepEqual(css, []); 48 | t.deepEqual(components, ["./test/stubs/global-components/my-custom-element.webc"]); 49 | }); 50 | 51 | 52 | 53 | test("Uses a global component with CSS and JS", async t => { 54 | let component = new WebC(); 55 | component.setContent(``); 56 | component.defineComponents("./test/stubs/global-components/*"); 57 | component.setBundlerMode(true); 58 | 59 | let { html, css, js, components } = await component.compile(); 60 | 61 | t.is(html, `

This is another global component.

62 | 63 |
`); 64 | 65 | t.deepEqual(js, [` 66 | alert("hi"); 67 | `]); 68 | t.deepEqual(css, [` 69 | p { color: blue; } 70 | `]); 71 | t.deepEqual(components, ["./test/stubs/global-components/other-custom-element.webc"]); 72 | }); 73 | 74 | test("Naming collision errors", async t => { 75 | let component = new WebC(); 76 | t.throws(() => component.defineComponents("./test/stubs/global-components/**")); 77 | }); 78 | -------------------------------------------------------------------------------- /test/issue-104-test.js: -------------------------------------------------------------------------------- 1 | import test from "ava"; 2 | import { WebC } from "../webc.js"; 3 | 4 | test("Easy way to render all public attributes in script #104", async t => { 5 | let component = new WebC(); 6 | 7 | component.defineComponents("./test/stubs/issue-104/component.webc"); 8 | 9 | component.setContent(``); 10 | 11 | let { html } = await component.compile(); 12 | 13 | t.is(html.trim(), ``); 14 | }); 15 | 16 | test("@attributes with object #114", async t => { 17 | let component = new WebC(); 18 | 19 | component.defineComponents("./test/stubs/issue-104/attrs-object.webc"); 20 | 21 | component.setContent(``); 22 | 23 | let { html } = await component.compile(); 24 | 25 | t.is(html.trim(), ``); 26 | }); 27 | 28 | test("Easy way to render all public attributes with webc:root #104", async t => { 29 | let component = new WebC(); 30 | 31 | component.defineComponents("./test/stubs/issue-104/root-component.webc"); 32 | 33 | component.setContent(``); 34 | 35 | let { html } = await component.compile(); 36 | 37 | t.is(html.trim(), ``); 38 | }); 39 | 40 | test("Easy way to render all public attributes with @attributes without webc:root #104", async t => { 41 | let component = new WebC(); 42 | 43 | component.defineComponents("./test/stubs/issue-104/attrs-component.webc"); 44 | 45 | component.setContent(``); 46 | 47 | let { html } = await component.compile(); 48 | 49 | t.is(html.trim(), `
`); 50 | }); 51 | 52 | test("Easy way to render all public attributes with @attributes with webc:root #104", async t => { 53 | let component = new WebC(); 54 | 55 | component.defineComponents("./test/stubs/issue-104/root-attrs-component.webc"); 56 | 57 | component.setContent(``); 58 | 59 | let { html } = await component.compile(); 60 | 61 | t.is(html.trim(), ``); 62 | }); 63 | 64 | test("Easy way to render all public attributes with @attributes with webc:root=override #104", async t => { 65 | let component = new WebC(); 66 | 67 | component.defineComponents("./test/stubs/issue-104/root-override-attrs-component.webc"); 68 | 69 | component.setContent(``); 70 | 71 | let { html } = await component.compile(); 72 | 73 | t.is(html.trim(), ``); 74 | }); 75 | 76 | 77 | // TODO Requires per-instance webc:setup to have access to attributes 78 | test.skip("Easy way to render all public attributes in script with webc:setup #104", async t => { 79 | let component = new WebC(); 80 | 81 | component.defineComponents("./test/stubs/issue-104/setup-component.webc"); 82 | 83 | component.setContent(``); 84 | 85 | let { html } = await component.compile(); 86 | 87 | t.is(html.trim(), ``); 88 | }); -------------------------------------------------------------------------------- /test/issue-115-test.js: -------------------------------------------------------------------------------- 1 | import test from "ava"; 2 | import { WebC } from "../webc.js"; 3 | 4 | test("New line at beginning issue #115", async t => { 5 | let component = new WebC(); 6 | 7 | component.setInputPath("./test/stubs/issue-115/page.webc"); 8 | 9 | let { html } = await component.compile(); 10 | 11 | t.is(html.trim(), ` 12 | 13 | 14 | 15 | 16 | 17 | Test 18 | 19 | 20 |

Hi, this is a test

21 | 22 | 23 | 24 | `); 25 | }); 26 | -------------------------------------------------------------------------------- /test/issue-118-test.js: -------------------------------------------------------------------------------- 1 | import test from "ava"; 2 | import { WebC } from "../webc.js"; 3 | 4 | test("Slottable webc:type transform #118", async t => { 5 | t.plan(2); 6 | let component = new WebC(); 7 | 8 | component.setInputPath("./test/stubs/issue-118/page.webc"); 9 | component.defineComponents("./test/stubs/issue-118/oh-no.webc"); 10 | component.setTransform("override", (content) => { 11 | t.truthy( content ); 12 | return `This is an override`; 13 | }); 14 | 15 | let { html } = await component.compile(); 16 | 17 | t.is(html.trim(), `
This is an override
`); 18 | }); -------------------------------------------------------------------------------- /test/issue-135-test.js: -------------------------------------------------------------------------------- 1 | import test from "ava"; 2 | import { WebC } from "../webc.js"; 3 | 4 | test("script render function implied template should still be HTML-only #135", async t => { 5 | let component = new WebC(); 6 | 7 | component.setInputPath("./test/stubs/issue-135/page.webc"); 8 | component.defineComponents("./test/stubs/issue-135/component.webc"); 9 | 10 | let { html } = await component.compile(); 11 | 12 | t.is(html.trim(), `

hello

`); 13 | }); 14 | -------------------------------------------------------------------------------- /test/issue-138-test.js: -------------------------------------------------------------------------------- 1 | import test from "ava"; 2 | import { WebC } from "../webc.js"; 3 | 4 | test("Circular dep error #138", async t => { 5 | let component = new WebC(); 6 | 7 | component.setInputPath("./test/stubs/issue-138/page.webc"); 8 | component.defineComponents("./test/stubs/issue-138/img.webc"); 9 | 10 | let { html } = await component.compile(); 11 | 12 | t.is(html.trim(), `An excited Zach is trying to finish this documentation`); 13 | }); 14 | -------------------------------------------------------------------------------- /test/issue-145-test.js: -------------------------------------------------------------------------------- 1 | import test from "ava"; 2 | import { WebC } from "../webc.js"; 3 | 4 | test("@keyframes percentage webc:scoped #145", async t => { 5 | let component = new WebC(); 6 | 7 | component.setContent(``); 13 | 14 | let { html } = await component.compile(); 15 | 16 | t.is(html.trim(), ``); 17 | }); 18 | 19 | test("@keyframes from/to webc:scoped #145", async t => { 20 | let component = new WebC(); 21 | 22 | component.setContent(``); 28 | 29 | let { html } = await component.compile(); 30 | 31 | t.is(html.trim(), ``); 32 | }); -------------------------------------------------------------------------------- /test/issue-152-test.js: -------------------------------------------------------------------------------- 1 | import test from "ava"; 2 | import { WebC } from "../webc.js"; 3 | 4 | test("Slotted global data access #152", async t => { 5 | let component = new WebC(); 6 | 7 | component.setBundlerMode(true); 8 | component.setContent(`Hello I am in the default slot.`); 9 | component.defineComponents("./test/stubs/issue-152/parent.webc"); 10 | 11 | let { html } = await component.compile({ 12 | data: { 13 | globalData: "Hello" 14 | } 15 | }); 16 | 17 | t.is(html.trim(), `

Hello I am in the default slot.Hello

`); 18 | }); 19 | 20 | test("Slotted global data access nested #152", async t => { 21 | let component = new WebC(); 22 | 23 | component.setBundlerMode(true); 24 | component.setContent(``); 25 | component.defineComponents("./test/stubs/issue-152/parent.webc"); 26 | component.defineComponents("./test/stubs/issue-152/child.webc"); 27 | 28 | let { html } = await component.compile({ 29 | data: { 30 | globalData: "Hello" 31 | } 32 | }); 33 | 34 | t.is(html.trim(), `

Hello

`); 35 | }); 36 | 37 | test("Slotted global data access even more nested #152", async t => { 38 | let component = new WebC(); 39 | 40 | component.setBundlerMode(true); 41 | component.setContent(``); 42 | component.defineComponents("./test/stubs/issue-152/parent.webc"); 43 | component.defineComponents("./test/stubs/issue-152/child.webc"); 44 | 45 | let { html } = await component.compile({ 46 | data: { 47 | globalData: "Hello" 48 | } 49 | }); 50 | 51 | t.is(html.trim(), `

Hello

`); 52 | }); 53 | 54 | test("Slotted global data access parent without slot #152", async t => { 55 | let component = new WebC(); 56 | 57 | component.setBundlerMode(true); 58 | component.setContent(``); 59 | component.defineComponents("./test/stubs/issue-152/parent-nohtml.webc"); 60 | component.defineComponents("./test/stubs/issue-152/child.webc"); 61 | 62 | let { html } = await component.compile({ 63 | data: { 64 | globalData: "Hello" 65 | } 66 | }); 67 | 68 | t.is(html.trim(), `

Hello

`); 69 | }); 70 | 71 | test("Slotted global data access parent without slot even nestier #152", async t => { 72 | let component = new WebC(); 73 | 74 | component.setBundlerMode(true); 75 | component.setContent(``); 76 | component.defineComponents("./test/stubs/issue-152/parent-nohtml.webc"); 77 | component.defineComponents("./test/stubs/issue-152/child.webc"); 78 | 79 | let { html } = await component.compile({ 80 | data: { 81 | globalData: "Hello" 82 | } 83 | }); 84 | 85 | t.is(html.trim(), `

Hello

`); 86 | }); 87 | -------------------------------------------------------------------------------- /test/issue-154-test.js: -------------------------------------------------------------------------------- 1 | import test from "ava"; 2 | import { WebC } from "../webc.js"; 3 | 4 | test("Style scoping bug #154", async t => { 5 | let component = new WebC(); 6 | 7 | component.setBundlerMode(true); 8 | component.setInputPath("./test/stubs/issue-154/page.webc"); 9 | component.defineComponents("./test/stubs/issue-154/c-red.webc"); 10 | component.defineComponents("./test/stubs/issue-154/c-blue.webc"); 11 | 12 | let { html, css } = await component.compile(); 13 | 14 | t.deepEqual(css.sort(), [ `.wla6sc-lx{background-color:blue}`, `.wwcxvoco3{background-color:red}` ]); 15 | t.is(html.trim(), `
Hi I am red
16 | 17 | 18 |
Hi I am blue
19 | 20 | 21 | 22 | 23 |
24 | I am red. 25 |
Hi I am blue
26 | 27 | 28 | 29 | still red 30 |
31 | 32 | 33 | 34 |
35 | I am blue. 36 |
Hi I am red
37 | 38 | 39 | still blue 40 |
`); 41 | }); -------------------------------------------------------------------------------- /test/issue-78-test.js: -------------------------------------------------------------------------------- 1 | import test from "ava"; 2 | import { WebC } from "../webc.js"; 3 | 4 | test("webc:import in Components should be relative to component file #78", async t => { 5 | let component = new WebC(); 6 | 7 | component.setInputPath("./test/stubs/issue-78/page.webc"); 8 | component.defineComponents("./test/stubs/issue-78/a-component.webc"); 9 | 10 | let { html } = await component.compile(); 11 | 12 | t.is(html.trim(), `

normal component

13 | woah i am component. i do things. 14 | 15 |

js

16 |

hi

17 | 18 |

js with component

19 | woah i am component. i do things.`); 20 | }); 21 | 22 | test("stock webc:type=js #78", async t => { 23 | let component = new WebC(); 24 | 25 | component.setContent(``); 28 | 29 | let { html } = await component.compile(); 30 | 31 | t.is(html.trim(), `hello`); 32 | }); 33 | 34 | test("stock webc:type=js with template #78", async t => { 35 | let component = new WebC(); 36 | 37 | component.setContent(``); 40 | 41 | let { html } = await component.compile(); 42 | 43 | t.is(html.trim(), `hello`); 44 | }); 45 | 46 | test("stock webc:type=js with template/keep #78", async t => { 47 | let component = new WebC(); 48 | 49 | component.setContent(``); 52 | 53 | let { html } = await component.compile(); 54 | 55 | t.is(html.trim(), ``); 56 | }); 57 | 58 | test("stock webc:type=js with webc:is #78", async t => { 59 | let component = new WebC(); 60 | 61 | component.setContent(``); 64 | 65 | let { html } = await component.compile(); 66 | 67 | t.is(html.trim(), ``); 68 | }); 69 | 70 | test("Docs image example #78", async t => { 71 | let component = new WebC(); 72 | 73 | component.setContent(`An excited Zach is trying to finish this documentation`); 74 | component.defineComponents("./test/stubs/issue-78/img.webc"); 75 | 76 | let { html } = await component.compile(); 77 | 78 | t.is(html.trim(), `An excited Zach is trying to finish this documentation`); 79 | }); 80 | 81 | test("Docs css example #78", async t => { 82 | let component = new WebC(); 83 | 84 | component.setContent(` 85 | p { color: rebeccapurple; } 86 | `); 87 | component.defineComponents("./test/stubs/issue-78/add-banner-to-css.webc"); 88 | 89 | let { html } = await component.compile(); 90 | 91 | t.is(html.trim(), ``); 95 | }); 96 | 97 | test("Docs css example using wrapper element #78", async t => { 98 | let component = new WebC(); 99 | 100 | component.setContent(``); 103 | component.defineComponents("./test/stubs/issue-78/add-banner-to-css-root.webc"); 104 | 105 | let { html } = await component.compile(); 106 | 107 | t.is(html.trim(), ``); 113 | }); 114 | 115 | test("Docs css example using render #78", async t => { 116 | let component = new WebC(); 117 | 118 | component.setContent(``); 121 | component.defineComponents("./test/stubs/issue-78/add-banner-to-css-render.webc"); 122 | 123 | let { html } = await component.compile(); 124 | 125 | t.is(html.trim(), ``); 131 | }); 132 | -------------------------------------------------------------------------------- /test/issue-79-test.js: -------------------------------------------------------------------------------- 1 | import test from "ava"; 2 | import { WebC } from "../webc.js"; 3 | 4 | test("webc:import in Components should be relative to component file #79", async t => { 5 | let component = new WebC(); 6 | 7 | component.setInputPath("./test/stubs/issue-79/pages/articles/my-page.webc"); 8 | component.defineComponents("./test/stubs/issue-79/components/**.webc"); 9 | 10 | let { html } = await component.compile(); 11 | 12 | t.is(html.trim(), ``); 13 | }); 14 | 15 | -------------------------------------------------------------------------------- /test/issue-80-test.js: -------------------------------------------------------------------------------- 1 | import test from "ava"; 2 | import { WebC } from "../webc.js"; 3 | 4 | test("Dynamic attributes in components (attribute -> dynamic -> dynamic) #80", async t => { 5 | let component = new WebC(); 6 | 7 | component.setInputPath("./test/stubs/issue-80/page.webc"); 8 | component.defineComponents("./test/stubs/issue-80/b.webc"); 9 | component.defineComponents("./test/stubs/issue-80/c.webc"); 10 | 11 | let { html } = await component.compile(); 12 | 13 | t.is(html, `
14 | 15 |
`); 16 | }); 17 | 18 | test("Dynamic attributes in components (dynamic -> dynamic -> dynamic) #80", async t => { 19 | let component = new WebC(); 20 | 21 | component.setInputPath("./test/stubs/issue-80-b/page.webc"); 22 | component.defineComponents("./test/stubs/issue-80-b/b.webc"); 23 | component.defineComponents("./test/stubs/issue-80-b/c.webc"); 24 | 25 | let { html } = await component.compile(); 26 | 27 | t.is(html, `
28 | 29 |
`); 30 | }); 31 | -------------------------------------------------------------------------------- /test/issue-83-test.js: -------------------------------------------------------------------------------- 1 | import test from "ava"; 2 | import { WebC } from "../webc.js"; 3 | 4 | // This doesn’t technically test console.log output but it’s close enough for now 😅 5 | test("console log in webc:type=js #83", async t => { 6 | t.plan(2); 7 | 8 | let component = new WebC(); 9 | component.setContent(``); 14 | 15 | let { html } = await component.compile({ 16 | data: { 17 | console: { 18 | log: function(message) { 19 | t.is(message, 2); 20 | } 21 | } 22 | } 23 | }); 24 | 25 | t.is(html, `hello 2`); 26 | }); 27 | -------------------------------------------------------------------------------- /test/issue-84-test.js: -------------------------------------------------------------------------------- 1 | import test from "ava"; 2 | import { WebC } from "../webc.js"; 3 | 4 | // This doesn’t technically test console.log output but it’s close enough for now 😅 5 | test("Non string output from webc:type=js #84", async t => { 6 | let component = new WebC(); 7 | component.setContent(``); 10 | 11 | let { html } = await component.compile(); 12 | 13 | t.is(html, `1`); 14 | }); 15 | -------------------------------------------------------------------------------- /test/issue-85-test.js: -------------------------------------------------------------------------------- 1 | import test from "ava"; 2 | import { WebC } from "../webc.js"; 3 | 4 | test("Missing props #85", async t => { 5 | let component = new WebC(); 6 | 7 | component.setInputPath("./test/stubs/issue-85/page.webc"); 8 | 9 | let { html } = await component.compile(); 10 | 11 | t.is(html.trim(), `Your image didn’t have an alt so you get this link instead.`); 12 | }); 13 | -------------------------------------------------------------------------------- /test/issue-88-test.js: -------------------------------------------------------------------------------- 1 | import test from "ava"; 2 | import { WebC } from "../webc.js"; 3 | 4 | test("webc:type=js #88", async t => { 5 | let component = new WebC(); 6 | 7 | component.setHelper("alwaysBlue", () => { 8 | return "I'm blue, da ba dee da ba di"; 9 | }); 10 | 11 | component.setContent(``); 14 | 15 | let { html } = await component.compile(); 16 | 17 | t.is(html.trim(), `I'm blue, da ba dee da ba di`); 18 | }); 19 | 20 | test("webc:type=js #88 (with `this`)", async t => { 21 | let component = new WebC(); 22 | 23 | component.setHelper("alwaysBlue", () => { 24 | return "I'm blue, da ba dee da ba di"; 25 | }); 26 | 27 | component.setContent(``); 30 | 31 | let { html } = await component.compile(); 32 | 33 | t.is(html.trim(), `I'm blue, da ba dee da ba di`); 34 | }); 35 | 36 | 37 | test("webc:type=render #88", async t => { 38 | let component = new WebC(); 39 | 40 | component.setHelper("alwaysBlue", () => { 41 | return "I'm blue, da ba dee da ba di"; 42 | }); 43 | 44 | component.setContent(``); 49 | 50 | let { html } = await component.compile(); 51 | 52 | t.is(html.trim(), `I'm blue, da ba dee da ba di`); 53 | }); 54 | -------------------------------------------------------------------------------- /test/issue-91-test.js: -------------------------------------------------------------------------------- 1 | import test from "ava"; 2 | import { WebC } from "../webc.js"; 3 | 4 | test("Issue #91 render functions require template", async t => { 5 | let component = new WebC(); 6 | 7 | component.setBundlerMode(true); 8 | component.setInputPath("./test/stubs/issue-91/page.webc"); 9 | component.defineComponents("./test/stubs/issue-91/img.webc"); 10 | 11 | let { html, css, js, components } = await component.compile(); 12 | 13 | t.is(html, `An excited Zach is trying to finish this documentation`); 14 | }); 15 | -------------------------------------------------------------------------------- /test/issue-94-test.js: -------------------------------------------------------------------------------- 1 | import test from "ava"; 2 | import { WebC } from "../webc.js"; 3 | 4 | test("non-string props #94", async t => { 5 | let component = new WebC(); 6 | 7 | component.defineComponents("./test/stubs/issue-94/component.webc"); 8 | 9 | component.setContent(`|`); 10 | 11 | let { html } = await component.compile(); 12 | 13 | t.is(html.trim(), `number|number`); 14 | }); -------------------------------------------------------------------------------- /test/issue-98-test.js: -------------------------------------------------------------------------------- 1 | import test from "ava"; 2 | import { WebC } from "../webc.js"; 3 | 4 | test("Issue #3 slot inconsistency", async t => { 5 | let component = new WebC(); 6 | 7 | component.setInputPath("./test/stubs/issue-98/page.webc"); 8 | component.defineComponents("./test/stubs/issue-98/component.webc"); 9 | 10 | let { html, css, js, components } = await component.compile(); 11 | 12 | t.deepEqual(components, [ 13 | "./test/stubs/issue-98/page.webc", 14 | "./test/stubs/issue-98/component.webc", 15 | ]); 16 | 17 | t.is(html, `
`); 18 | }); -------------------------------------------------------------------------------- /test/looping-test.js: -------------------------------------------------------------------------------- 1 | import test from "ava"; 2 | import { WebC } from "../webc.js"; 3 | 4 | test("Basic webc:for (complex key) over Array", async t => { 5 | let component = new WebC(); 6 | 7 | component.setInputPath("./test/stubs/looping/array.webc"); 8 | 9 | let { html } = await component.compile(); 10 | 11 | t.is(html.trim(), `
2-1
12 |
3-2
13 |
4-3
`); 14 | }); 15 | 16 | test("Basic webc:for (simple key) over Array", async t => { 17 | let component = new WebC(); 18 | 19 | component.setInputPath("./test/stubs/looping/array-value.webc"); 20 | 21 | let { html } = await component.compile(); 22 | 23 | t.is(html.trim(), `
2-undefined
24 |
3-undefined
25 |
4-undefined
`); 26 | }); 27 | 28 | test("webc:for over Array has injected data available on child nodes", async t => { 29 | let component = new WebC(); 30 | 31 | component.setInputPath("./test/stubs/looping/scoped-data.webc"); 32 | 33 | let { html } = await component.compile(); 34 | 35 | t.is(html.trim(), `
1-0
36 |
2-1
37 |
3-2
38 |
4-3
`); 39 | }); 40 | 41 | 42 | test("Basic webc:for (complex key) over Object", async t => { 43 | let component = new WebC(); 44 | 45 | component.setInputPath("./test/stubs/looping/object.webc"); 46 | 47 | let { html } = await component.compile(); 48 | 49 | t.is(html.trim(), `
a-1-0
50 |
c-4-2
`); 51 | }); 52 | 53 | test("Basic webc:for (simple key) over Object", async t => { 54 | let component = new WebC(); 55 | 56 | component.setInputPath("./test/stubs/looping/object-key.webc"); 57 | 58 | let { html } = await component.compile(); 59 | 60 | t.is(html.trim(), `
a-undefined
61 |
c-undefined
`); 62 | }); 63 | 64 | test("webc:for using Object.keys to convert to Array", async t => { 65 | let component = new WebC(); 66 | 67 | component.setInputPath("./test/stubs/looping/array-object-keys.webc"); 68 | 69 | let { html } = await component.compile(); 70 | 71 | t.is(html.trim(), `
a
72 |
c
`); 73 | }); 74 | 75 | test("webc:for using Object.values to convert to Array", async t => { 76 | let component = new WebC(); 77 | 78 | component.setInputPath("./test/stubs/looping/array-object-values.webc"); 79 | 80 | let { html } = await component.compile(); 81 | 82 | t.is(html.trim(), `
1
83 |
4
`); 84 | }); 85 | 86 | test("webc:for issue #139", async t => { 87 | let component = new WebC(); 88 | 89 | component.setInputPath("./test/stubs/looping/issue-139.webc"); 90 | component.defineComponents("./test/stubs/looping/components/component.webc"); 91 | 92 | let { html } = await component.compile(); 93 | 94 | t.is(html.trim(), `1 95 | 2 96 | 3 97 | 1 98 | 2 99 | 3`); 100 | }); 101 | 102 | test("script webc:setup feeds data for looping", async t => { 103 | let component = new WebC(); 104 | 105 | component.setInputPath("./test/stubs/looping/script-setup-data.webc"); 106 | 107 | let { html } = await component.compile(); 108 | 109 | t.is(html.trim(), `1 110 | 2 111 | 3`); 112 | }); 113 | 114 | test("nesting webc:for over component hierarchy", async t => { 115 | let component = new WebC(); 116 | let {contacts} = await import("./stubs/looping/complex/data.js"); 117 | 118 | component.defineComponents("./test/stubs/looping/components/*.webc"); 119 | component.setInputPath("./test/stubs/looping/complex/entry-point.webc"); 120 | 121 | let { html } = await component.compile({data:{contacts}}); 122 | 123 | t.true(html.indexOf(``) > -1) 124 | t.true(html.indexOf(`
  • Ross - 1
  • `) > -1) 125 | t.true(html.indexOf(`
    `) > -1) 126 | t.true(html.indexOf(`
    Chandler
    `) > -1) 127 | t.true(html.indexOf(`border: 1px solid green;`) > -1) 128 | 129 | }) 130 | -------------------------------------------------------------------------------- /test/moduleResolutionTest.js: -------------------------------------------------------------------------------- 1 | import test from "ava"; 2 | 3 | import { ModuleResolution } from "../src/moduleResolution.js"; 4 | 5 | test("Resolve component path in dependency", async t => { 6 | let m = new ModuleResolution(); 7 | m.setTagName("my-tag"); 8 | 9 | t.is(m.resolve("npm:pretend"), "./node_modules/pretend/my-tag.webc"); 10 | t.is(m.resolve("npm:pretend/real.webc"), "./node_modules/pretend/real.webc"); 11 | t.is(m.resolve("npm:pretend/pretend"), "./node_modules/pretend/pretend/my-tag.webc"); 12 | t.is(m.resolve("npm:pretend/pretend/real.webc"), "./node_modules/pretend/pretend/real.webc"); 13 | t.is(m.resolve("npm:@11ty/pretend"), "./node_modules/@11ty/pretend/my-tag.webc"); 14 | t.is(m.resolve("npm:@11ty/pretend/real"), "./node_modules/@11ty/pretend/real/my-tag.webc"); 15 | t.is(m.resolve("npm:@11ty/pretend/real/real.webc"), "./node_modules/@11ty/pretend/real/real.webc"); 16 | }); 17 | 18 | test("Resolve component path in dependency with aliases", async t => { 19 | let m = new ModuleResolution(); 20 | m.setTagName("my-tag"); 21 | m.setAliases({ 22 | "npm": "./test/fake_modules/" 23 | }); 24 | 25 | t.is(m.resolve("npm:pretend"), "./test/fake_modules/pretend/my-tag.webc"); 26 | t.is(m.resolve("npm:pretend/real.webc"), "./test/fake_modules/pretend/real.webc"); 27 | t.is(m.resolve("npm:pretend/pretend"), "./test/fake_modules/pretend/pretend/my-tag.webc"); 28 | t.is(m.resolve("npm:pretend/pretend/real.webc"), "./test/fake_modules/pretend/pretend/real.webc"); 29 | t.is(m.resolve("npm:@11ty/pretend"), "./test/fake_modules/@11ty/pretend/my-tag.webc"); 30 | t.is(m.resolve("npm:@11ty/pretend/real"), "./test/fake_modules/@11ty/pretend/real/my-tag.webc"); 31 | t.is(m.resolve("npm:@11ty/pretend/real.webc"), "./test/fake_modules/@11ty/pretend/real.webc"); 32 | t.is(m.resolve("npm:@11ty/pretend/pretend/real.webc"), "./test/fake_modules/@11ty/pretend/pretend/real.webc"); 33 | }); 34 | 35 | test("Resolve component path in dependency with aliases and a tagName", async t => { 36 | let m = new ModuleResolution(); 37 | m.setTagName("syntax-highlight"); 38 | m.setAliases({ 39 | "npm": "./test/fake_modules/" 40 | }); 41 | 42 | t.is(m.resolve("npm:pretend"), "./test/fake_modules/pretend/syntax-highlight.webc"); 43 | t.is(m.resolve("npm:pretend/pretend"), "./test/fake_modules/pretend/pretend/syntax-highlight.webc"); 44 | t.is(m.resolve("npm:pretend/pretend/real.webc"), "./test/fake_modules/pretend/pretend/real.webc"); 45 | t.is(m.resolve("npm:@11ty/pretend"), "./test/fake_modules/@11ty/pretend/syntax-highlight.webc"); 46 | t.is(m.resolve("npm:@11ty/pretend/real"), "./test/fake_modules/@11ty/pretend/real/syntax-highlight.webc"); 47 | t.is(m.resolve("npm:@11ty/pretend/real/real.webc"), "./test/fake_modules/@11ty/pretend/real/real.webc"); 48 | }); 49 | 50 | 51 | test("Alias outside of the project throws error", async t => { 52 | let m = new ModuleResolution(); 53 | m.setAliases({ 54 | "npm": "../test/" 55 | }); 56 | 57 | t.throws(() => { 58 | m.resolve("npm:pretend") 59 | }, { 60 | message: "Invalid import reference (must be in the project root), received: ../test/pretend" 61 | }); 62 | }); 63 | 64 | test("Import outside of the project throws error", async t => { 65 | let m = new ModuleResolution(); 66 | 67 | t.throws(() => { 68 | m.resolve("../pretend.webc") 69 | }, { 70 | message: "Invalid import reference (must be in the project root), received: ../pretend.webc" 71 | }); 72 | }); 73 | -------------------------------------------------------------------------------- /test/props-versus-text-test.js: -------------------------------------------------------------------------------- 1 | import test from "ava"; 2 | import { WebC } from "../webc.js"; 3 | 4 | test("Props versus @text", async t => { 5 | let component = new WebC(); 6 | 7 | component.setInputPath("./test/stubs/props-versus-text/page.webc"); 8 | 9 | let { html } = await component.compile({ 10 | data: { 11 | pdf: "hello" 12 | } 13 | }); 14 | 15 | t.is(html.trim(), ``); 16 | }); 17 | -------------------------------------------------------------------------------- /test/proxyDataTest.js: -------------------------------------------------------------------------------- 1 | import test from "ava"; 2 | import { ProxyData } from "../src/proxyData.cjs"; 3 | 4 | test("Data is proxied", t => { 5 | let p = new ProxyData(); 6 | p.addTarget({ 7 | global1: 1, 8 | global2: 2, 9 | }); 10 | 11 | let data = p.getData(); 12 | 13 | t.is(data.global1, 1); 14 | t.is(data.global2, 2); 15 | }); 16 | 17 | test("Data is cascaded", t => { 18 | let p = new ProxyData(); 19 | p.addTarget({ 20 | global1: 3, 21 | global2: 4, 22 | global3: 5, 23 | }); 24 | // later additions override previous additions 25 | p.addTarget({ 26 | global1: 1, 27 | global2: 2, 28 | }); 29 | 30 | let data = p.getData(); 31 | 32 | t.is(data.global1, 1); 33 | t.is(data.global2, 2); 34 | t.is(data.global3, 5); 35 | }); -------------------------------------------------------------------------------- /test/renderFunctionJsTest.js: -------------------------------------------------------------------------------- 1 | import test from "ava"; 2 | import { WebC } from "../webc.js"; 3 | 4 | test("Using webc:type=js", async t => { 5 | let component = new WebC(); 6 | 7 | // identical to 8 | component.setContent(` 9 | 10 | 11 | `); 12 | 13 | let { html, css, js, components } = await component.compile({ 14 | data: { 15 | myArray: [1,2,3,4] 16 | } 17 | }); 18 | 19 | t.deepEqual(js, []); 20 | t.deepEqual(css, []); 21 | t.deepEqual(components, []); 22 | 23 | t.is(html, ` 24 | 25 | 1/2/3/4 26 | 27 | `); 28 | }); 29 | 30 | test("Using webc:type=js with require", async t => { 31 | let component = new WebC(); 32 | 33 | // identical to 34 | component.setContent(` 35 | 36 | 37 | 41 | 42 | `); 43 | 44 | let { html, css, js, components } = await component.compile(); 45 | 46 | t.deepEqual(js, []); 47 | t.deepEqual(css, []); 48 | t.deepEqual(components, []); 49 | 50 | t.is(html, ` 51 | 52 | 53 | Imported 54 | 55 | 56 | `); 57 | }); 58 | 59 | // Seems like this ignores webc:type and outputs the 66 | `); 67 | 68 | let { html, css, js, components } = await component.compile({ 69 | data: { 70 | myArray: [1,2,3,4] 71 | } 72 | }); 73 | 74 | t.deepEqual(js, []); 75 | t.deepEqual(css, []); 76 | t.deepEqual(components, []); 77 | 78 | t.is(html, ` 79 | 80 | 1/2/3/4 81 | 82 | `); 83 | }); 84 | 85 | test("Using webc:type=js and promises", async t => { 86 | let component = new WebC(); 87 | 88 | component.setContent(` 89 | 90 | 91 | 98 | 99 | `); 100 | 101 | let { html, css, js, components } = await component.compile({ 102 | data: { 103 | myArray: [1,2,3,4] 104 | } 105 | }); 106 | 107 | t.deepEqual(js, []); 108 | t.deepEqual(css, []); 109 | t.deepEqual(components, []); 110 | 111 | t.is(html, ` 112 | 113 | 114 | 1,2,3,4 115 | 116 | 117 | `); 118 | }); 119 | 120 | test("Using webc:type=js and async function", async t => { 121 | let component = new WebC(); 122 | 123 | component.setContent(` 124 | 125 | 126 | 137 | 138 | `); 139 | 140 | let { html, css, js, components } = await component.compile({ 141 | data: { 142 | myArray: [1,2,3,4] 143 | } 144 | }); 145 | 146 | t.deepEqual(js, []); 147 | t.deepEqual(css, []); 148 | t.deepEqual(components, []); 149 | 150 | t.is(html, ` 151 | 152 | 153 | 1,2,3,4 154 | 155 | 156 | `); 157 | }); 158 | 159 | 160 | test("Using webc:type=js and if true", async t => { 161 | let component = new WebC(); 162 | 163 | component.setContent(` 164 | 165 | 166 | 173 | 174 | `); 175 | 176 | let { html, css, js, components } = await component.compile(); 177 | 178 | t.deepEqual(js, []); 179 | t.deepEqual(css, []); 180 | t.deepEqual(components, []); 181 | 182 | t.is(html, ` 183 | 184 | 185 | true 186 | 187 | 188 | `); 189 | }); 190 | 191 | test("Using webc:type=js and if false", async t => { 192 | let component = new WebC(); 193 | 194 | component.setContent(` 195 | 196 | 197 | 204 | 205 | `); 206 | 207 | let { html, css, js, components } = await component.compile(); 208 | 209 | t.deepEqual(js, []); 210 | t.deepEqual(css, []); 211 | t.deepEqual(components, []); 212 | 213 | t.is(html, ` 214 | 215 | 216 | false 217 | 218 | 219 | `); 220 | }); 221 | -------------------------------------------------------------------------------- /test/scoped-type-js-test.js: -------------------------------------------------------------------------------- 1 | import test from "ava"; 2 | import { WebC } from "../webc.js"; 3 | 4 | test("webc:scoped with webc:type=js", async t => { 5 | let component = new WebC(); 6 | component.setBundlerMode(true); 7 | component.setContent(``); 10 | 11 | let { html, css } = await component.compile(); 12 | 13 | t.is(html.trim(), ""); 14 | t.deepEqual(css, [".wybdy2xfp div{color:red}"]); 15 | }); 16 | 17 | 18 | test("webc:scoped webc:keep with webc:type=js", async t => { 19 | let component = new WebC(); 20 | component.setBundlerMode(true); 21 | component.setContent(``); 24 | 25 | let { html, css } = await component.compile(); 26 | 27 | t.is(html.trim(), ``); 28 | t.deepEqual(css, []); 29 | }); 30 | 31 | test("webc:scoped webc:keep with webc:type=js with wrapper element", async t => { 32 | let component = new WebC(); 33 | component.setBundlerMode(true); 34 | component.setContent(`
    35 | 38 |
    `); 39 | 40 | await t.throwsAsync(async () => component.compile(), { 41 | message: "Could not find any top level \`; 50 | `); 51 | 52 | let { html, css } = await component.compile(); 53 | 54 | t.is(html.trim(), ``); 55 | t.deepEqual(css, ["div { color: red; }"]); 56 | }); 57 | 58 | test("nested content, style webc:scoped from a webc:type=js", async t => { 59 | let component = new WebC(); 60 | component.setBundlerMode(true); 61 | component.setContent(``); 64 | 65 | await t.throwsAsync(async () => component.compile(), { 66 | message: "Could not find any top level 35 | Hello World! 36 | 37 | After`); 38 | }); 39 | 40 | test("Using a web component with a declarative shadow root using shadowrootmode", async t => { 41 | let { html, css, js, components } = await testGetResultFor("./test/stubs/nested.webc", { 42 | "web-component": "./test/stubs/components/shadowrootmode.webc" 43 | }, {}, { globalData: "World" }); 44 | 45 | t.deepEqual(js, []); 46 | t.deepEqual(css, []); 47 | t.deepEqual(components, [ 48 | "./test/stubs/nested.webc", 49 | "./test/stubs/components/shadowrootmode.webc" 50 | ]); 51 | t.is(html, `Before 52 | 58 | After`); 59 | }); -------------------------------------------------------------------------------- /test/stubs/alias-paragraph.webc: -------------------------------------------------------------------------------- 1 | Before 2 |

    This is a paragraph—we still cool?

    3 | After -------------------------------------------------------------------------------- /test/stubs/asset-buckets.webc: -------------------------------------------------------------------------------- 1 |

    Hi

    2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /test/stubs/attribute-quotes.webc: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 |
    5 |
    6 |
    7 |
    8 |
    -------------------------------------------------------------------------------- /test/stubs/bucket-inherit-attrs/component.webc: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/stubs/bucket-inherit-attrs/index.webc: -------------------------------------------------------------------------------- 1 |
    2 | 3 | 4 |
    -------------------------------------------------------------------------------- /test/stubs/bucket-inherit/child.webc: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/stubs/bucket-inherit/component.webc: -------------------------------------------------------------------------------- 1 |

    Hi

    2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /test/stubs/bucket-inherit/index.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/bucket-noinherit/component.webc: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/stubs/bucket-noinherit/index.webc: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/stubs/class-mixins.webc: -------------------------------------------------------------------------------- 1 | Before 2 | 3 | After -------------------------------------------------------------------------------- /test/stubs/comment.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/component-html-assets.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/component-html-resolve.webc: -------------------------------------------------------------------------------- 1 |

    -------------------------------------------------------------------------------- /test/stubs/component-in-page-mode.webc: -------------------------------------------------------------------------------- 1 |
    Test
    -------------------------------------------------------------------------------- /test/stubs/component-script-html.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/components-list.webc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CHILD CONTENT 5 | 6 | 7 | AUNT CONTENT 8 | 9 | 10 | -------------------------------------------------------------------------------- /test/stubs/components-order.webc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CHILD CONTENT 5 | 6 | SIBLING CONTENT 7 | 8 | AUNT CONTENT 9 | -------------------------------------------------------------------------------- /test/stubs/components/assets.webc: -------------------------------------------------------------------------------- 1 | HTML 2 |

    3 | 4 | -------------------------------------------------------------------------------- /test/stubs/components/child-circular.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/components/child-circular2.webc: -------------------------------------------------------------------------------- 1 | test -------------------------------------------------------------------------------- /test/stubs/components/child-css-js-a.webc: -------------------------------------------------------------------------------- 1 | BeforeAfter -------------------------------------------------------------------------------- /test/stubs/components/child-css-js-b.webc: -------------------------------------------------------------------------------- 1 | BeforeAfter -------------------------------------------------------------------------------- /test/stubs/components/child-css-js-c.webc: -------------------------------------------------------------------------------- 1 | BeforeAfter -------------------------------------------------------------------------------- /test/stubs/components/child-css-js-d.webc: -------------------------------------------------------------------------------- 1 | BeforeAfter -------------------------------------------------------------------------------- /test/stubs/components/child-css-js-e.webc: -------------------------------------------------------------------------------- 1 | BeforeAfter -------------------------------------------------------------------------------- /test/stubs/components/child-css-js-f.webc: -------------------------------------------------------------------------------- 1 | BeforeAfter -------------------------------------------------------------------------------- /test/stubs/components/child-root-empty-class.webc: -------------------------------------------------------------------------------- 1 |
    SSR content
    -------------------------------------------------------------------------------- /test/stubs/components/child-root.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/components/clientside.webc: -------------------------------------------------------------------------------- 1 | This is the web component content. 2 | -------------------------------------------------------------------------------- /test/stubs/components/component-data.webc: -------------------------------------------------------------------------------- 1 |
    -------------------------------------------------------------------------------- /test/stubs/components/html-evaluating-props-nothis.webc: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/stubs/components/html-evaluating-props.webc: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/stubs/components/img-as-root.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/components/img-plain.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/components/img-props.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/components/img.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/components/nested-child-empty.webc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/11ty/webc/a2f548c23490929aa8c9cd25159549eba3e869c7/test/stubs/components/nested-child-empty.webc -------------------------------------------------------------------------------- /test/stubs/components/nested-child-namedslot-style.webc: -------------------------------------------------------------------------------- 1 | SSR contentAfter slot content -------------------------------------------------------------------------------- /test/stubs/components/nested-child-namedslot.webc: -------------------------------------------------------------------------------- 1 | SSR contentAfter slot content -------------------------------------------------------------------------------- /test/stubs/components/nested-child-script.webc: -------------------------------------------------------------------------------- 1 | SSR content -------------------------------------------------------------------------------- /test/stubs/components/nested-child-slot-before-after.webc: -------------------------------------------------------------------------------- 1 | BeforeAfter -------------------------------------------------------------------------------- /test/stubs/components/nested-child-slot-style.webc: -------------------------------------------------------------------------------- 1 | SSR contentAfter slot content -------------------------------------------------------------------------------- /test/stubs/components/nested-child-slot.webc: -------------------------------------------------------------------------------- 1 | SSR contentAfter slot content -------------------------------------------------------------------------------- /test/stubs/components/nested-child-style-keep.webc: -------------------------------------------------------------------------------- 1 | SSR content -------------------------------------------------------------------------------- /test/stubs/components/nested-child-style-only.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/components/nested-child-style-script-both-empty.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/components/nested-child-style.webc: -------------------------------------------------------------------------------- 1 | SSR content -------------------------------------------------------------------------------- /test/stubs/components/nested-child.webc: -------------------------------------------------------------------------------- 1 | SSR content -------------------------------------------------------------------------------- /test/stubs/components/override-parent-scoped.webc: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/stubs/components/override-parent.webc: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/stubs/components/render-css-keep.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/components/render-css-root-override.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/components/render-css-root.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/components/render-css.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/components/render-slots-raw.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/components/render-slots.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/components/root-style.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/components/scoped-override-collision-a.webc: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /test/stubs/components/scoped-override-collision-b.webc: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /test/stubs/components/scoped-override.webc: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /test/stubs/components/scoped-style.webc: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /test/stubs/components/script-html.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/components/shadowroot.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/components/shadowrootmode.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/components/text-link-slot.webc: -------------------------------------------------------------------------------- 1 | Default link text 2 | -------------------------------------------------------------------------------- /test/stubs/components/with-uid-root.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/components/with-uid.webc: -------------------------------------------------------------------------------- 1 |
    -------------------------------------------------------------------------------- /test/stubs/deep-root/component.webc: -------------------------------------------------------------------------------- 1 |
    Some component content
    -------------------------------------------------------------------------------- /test/stubs/defined-style-noroot.webc: -------------------------------------------------------------------------------- 1 | 9 | This will be green at first and then switch to red when JS has registered the component. -------------------------------------------------------------------------------- /test/stubs/defined-style.webc: -------------------------------------------------------------------------------- 1 | 9 |
    This will be green at first and then switch to red when JS has registered the component.
    -------------------------------------------------------------------------------- /test/stubs/dynamic-attributes-host-component-data/lol.webc: -------------------------------------------------------------------------------- 1 |
    Test
    -------------------------------------------------------------------------------- /test/stubs/dynamic-attributes-host-component-data/page.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/dynamic-bucket/component.webc: -------------------------------------------------------------------------------- 1 |

    Hi

    2 | 3 | -------------------------------------------------------------------------------- /test/stubs/dynamic-bucket/index.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/empty-class.webc: -------------------------------------------------------------------------------- 1 | Light dom -------------------------------------------------------------------------------- /test/stubs/empty.webc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/11ty/webc/a2f548c23490929aa8c9cd25159549eba3e869c7/test/stubs/empty.webc -------------------------------------------------------------------------------- /test/stubs/externals/externals-dynamic.webc: -------------------------------------------------------------------------------- 1 |

    This is another global component.

    2 | 3 | -------------------------------------------------------------------------------- /test/stubs/externals/externals-keep.webc: -------------------------------------------------------------------------------- 1 |

    This is another global component.

    2 | 3 | -------------------------------------------------------------------------------- /test/stubs/externals/externals-scoped.webc: -------------------------------------------------------------------------------- 1 |

    This is another global component.

    2 | -------------------------------------------------------------------------------- /test/stubs/externals/externals-urls-keep.webc: -------------------------------------------------------------------------------- 1 |

    This is another global component.

    2 | 3 | -------------------------------------------------------------------------------- /test/stubs/externals/externals-urls.webc: -------------------------------------------------------------------------------- 1 |

    This is another global component.

    2 | 3 | -------------------------------------------------------------------------------- /test/stubs/externals/externals.webc: -------------------------------------------------------------------------------- 1 |

    This is another global component.

    2 | 3 | -------------------------------------------------------------------------------- /test/stubs/externals/my-script.js: -------------------------------------------------------------------------------- 1 | /* This is the external script */ -------------------------------------------------------------------------------- /test/stubs/externals/my-style-scoped.css: -------------------------------------------------------------------------------- 1 | :host { color: red; } -------------------------------------------------------------------------------- /test/stubs/externals/my-style.css: -------------------------------------------------------------------------------- 1 | /* This is some CSS */ -------------------------------------------------------------------------------- /test/stubs/fake_node_modules/@11ty/test/syntax-highlighter.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/global-components/my-custom-element.webc: -------------------------------------------------------------------------------- 1 | This is a global component. -------------------------------------------------------------------------------- /test/stubs/global-components/other-custom-element.webc: -------------------------------------------------------------------------------- 1 |

    This is another global component.

    2 | 5 | -------------------------------------------------------------------------------- /test/stubs/global-components/subfolder/my-custom-element.webc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/11ty/webc/a2f548c23490929aa8c9cd25159549eba3e869c7/test/stubs/global-components/subfolder/my-custom-element.webc -------------------------------------------------------------------------------- /test/stubs/global-data.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/head/custom-head.webc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /test/stubs/head/head-is-component.webc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | This is a title 6 | -------------------------------------------------------------------------------- /test/stubs/head/head.webc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | This is a title 5 | -------------------------------------------------------------------------------- /test/stubs/head/meta.webc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /test/stubs/html-number.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/html.webc: -------------------------------------------------------------------------------- 1 |

    2 |

    3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /test/stubs/if-component-style.webc: -------------------------------------------------------------------------------- 1 | Test -------------------------------------------------------------------------------- /test/stubs/img-to-img.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/img.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/import-alias-suffix.webc: -------------------------------------------------------------------------------- 1 | Before 2 |

    This is a paragraph—we still cool?

    3 | After -------------------------------------------------------------------------------- /test/stubs/import-alias.webc: -------------------------------------------------------------------------------- 1 | Before 2 |

    This is a paragraph—we still cool?

    3 | After -------------------------------------------------------------------------------- /test/stubs/import-keep.webc: -------------------------------------------------------------------------------- 1 | Before 2 | 3 | After -------------------------------------------------------------------------------- /test/stubs/import-twice.webc: -------------------------------------------------------------------------------- 1 | Before 2 | Light dom content 3 | Light dom content 4 | After -------------------------------------------------------------------------------- /test/stubs/issue-104/attrs-component.webc: -------------------------------------------------------------------------------- 1 |
    2 | -------------------------------------------------------------------------------- /test/stubs/issue-104/attrs-object.webc: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/stubs/issue-104/component.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/issue-104/root-attrs-component.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/issue-104/root-component.webc: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/stubs/issue-104/root-override-attrs-component.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/issue-104/setup-component.webc: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /test/stubs/issue-105/test-p.webc: -------------------------------------------------------------------------------- 1 |

    I am an HTML-only component

    -------------------------------------------------------------------------------- /test/stubs/issue-105/test.js: -------------------------------------------------------------------------------- 1 | import test from "ava"; 2 | import { WebC } from "../../../webc.js"; 3 | 4 | test("Template content issue #105", async t => { 5 | let component = new WebC(); 6 | 7 | component.defineComponents("./test/stubs/issue-105/test-p.webc"); 8 | 9 | component.setContent(``); 10 | 11 | let { html } = await component.compile(); 12 | 13 | t.is(html.trim(), ``); 14 | }); -------------------------------------------------------------------------------- /test/stubs/issue-115/page.webc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Test 9 | 10 | 11 |

    Hi, this is a test

    12 | 13 | 14 | -------------------------------------------------------------------------------- /test/stubs/issue-118/oh-no.webc: -------------------------------------------------------------------------------- 1 |
    -------------------------------------------------------------------------------- /test/stubs/issue-118/page.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/issue-135/component.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/issue-135/page.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/issue-138/img.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/issue-138/page.webc: -------------------------------------------------------------------------------- 1 | An excited Zach is trying to finish this documentation -------------------------------------------------------------------------------- /test/stubs/issue-152/child.webc: -------------------------------------------------------------------------------- 1 |

    -------------------------------------------------------------------------------- /test/stubs/issue-152/parent-nohtml.webc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/11ty/webc/a2f548c23490929aa8c9cd25159549eba3e869c7/test/stubs/issue-152/parent-nohtml.webc -------------------------------------------------------------------------------- /test/stubs/issue-152/parent.webc: -------------------------------------------------------------------------------- 1 |

    -------------------------------------------------------------------------------- /test/stubs/issue-154/c-blue.webc: -------------------------------------------------------------------------------- 1 |
    2 | 3 | 8 | -------------------------------------------------------------------------------- /test/stubs/issue-154/c-red.webc: -------------------------------------------------------------------------------- 1 |
    2 | 3 | -------------------------------------------------------------------------------- /test/stubs/issue-154/page.webc: -------------------------------------------------------------------------------- 1 | Hi I am red 2 | Hi I am blue 3 | 4 | 5 | I am red. 6 | Hi I am blue 7 | still red 8 | 9 | 10 | 11 | I am blue. 12 | Hi I am red 13 | still blue 14 | -------------------------------------------------------------------------------- /test/stubs/issue-3/my-article.webc: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | 4 | 5 |
    6 |
    Shadow dom slot
    7 | 8 |
    -------------------------------------------------------------------------------- /test/stubs/issue-3/page.webc: -------------------------------------------------------------------------------- 1 | 2 |

    Article Title

    3 |

    Subtitle

    4 |

    Content

    5 |
    -------------------------------------------------------------------------------- /test/stubs/issue-67/meta-social.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/issue-67/page.webc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

    My First Heading

    8 |

    My first paragraph.

    9 | 10 | -------------------------------------------------------------------------------- /test/stubs/issue-78/a-component.webc: -------------------------------------------------------------------------------- 1 | woah i am component. i do things. -------------------------------------------------------------------------------- /test/stubs/issue-78/add-banner-to-css-render.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/issue-78/add-banner-to-css-root.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/issue-78/add-banner-to-css.webc: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /test/stubs/issue-78/img.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/issue-78/page.webc: -------------------------------------------------------------------------------- 1 |

    normal component

    2 | 3 | 4 |

    js

    5 | 8 | 9 |

    js with component

    10 | -------------------------------------------------------------------------------- /test/stubs/issue-79/components/my-component.webc: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/stubs/issue-79/components/other-component.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/issue-79/pages/articles/my-page.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/issue-80-b/b.webc: -------------------------------------------------------------------------------- 1 |
    2 | 3 |
    -------------------------------------------------------------------------------- /test/stubs/issue-80-b/c.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/issue-80-b/page.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/issue-80/b.webc: -------------------------------------------------------------------------------- 1 |
    2 | 3 |
    -------------------------------------------------------------------------------- /test/stubs/issue-80/c.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/issue-80/page.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/issue-85/page.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/issue-91/img.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/issue-91/page.webc: -------------------------------------------------------------------------------- 1 | An excited Zach is trying to finish this documentation -------------------------------------------------------------------------------- /test/stubs/issue-94/component.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/issue-98/component.webc: -------------------------------------------------------------------------------- 1 |
    -------------------------------------------------------------------------------- /test/stubs/issue-98/page.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/looping/array-object-keys.webc: -------------------------------------------------------------------------------- 1 |
    -------------------------------------------------------------------------------- /test/stubs/looping/array-object-values.webc: -------------------------------------------------------------------------------- 1 |
    -------------------------------------------------------------------------------- /test/stubs/looping/array-value.webc: -------------------------------------------------------------------------------- 1 |
    -------------------------------------------------------------------------------- /test/stubs/looping/array.webc: -------------------------------------------------------------------------------- 1 |
    -------------------------------------------------------------------------------- /test/stubs/looping/complex/data.js: -------------------------------------------------------------------------------- 1 | export const contacts = [ 2 | { 3 | "name":"Joey", 4 | "color":"blue", 5 | "numbers":[1,2,3] 6 | }, 7 | { 8 | "name":"Phoebe", 9 | "color":"pink", 10 | "numbers":[1,2,3] 11 | }, 12 | { 13 | "name":"Chandler", 14 | "color":"orange", 15 | "numbers":[1,2,3] 16 | }, 17 | { 18 | "name":"Rachel", 19 | "color":"violet", 20 | "numbers":[1,2,3] 21 | }, 22 | { 23 | "name":"Monica", 24 | "color":"green", 25 | "numbers":[1,2,3] 26 | }, 27 | { 28 | "name":"Ross", 29 | "color":"red", 30 | "numbers":[1,2,3] 31 | } 32 | ] -------------------------------------------------------------------------------- /test/stubs/looping/complex/entry-point.webc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /test/stubs/looping/components/card-actions.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/looping/components/card-content.webc: -------------------------------------------------------------------------------- 1 | 2 |
    3 |
      4 |
    • 5 |
    -------------------------------------------------------------------------------- /test/stubs/looping/components/card-header.webc: -------------------------------------------------------------------------------- 1 |
    -------------------------------------------------------------------------------- /test/stubs/looping/components/card-thing.webc: -------------------------------------------------------------------------------- 1 | 2 |
    3 | 4 |
    5 | -------------------------------------------------------------------------------- /test/stubs/looping/components/component.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/looping/issue-139.webc: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/stubs/looping/object-key.webc: -------------------------------------------------------------------------------- 1 |
    -------------------------------------------------------------------------------- /test/stubs/looping/object.webc: -------------------------------------------------------------------------------- 1 |
    -------------------------------------------------------------------------------- /test/stubs/looping/scoped-data.webc: -------------------------------------------------------------------------------- 1 |
    -------------------------------------------------------------------------------- /test/stubs/looping/script-setup-data.webc: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /test/stubs/nested-alias-reference.webc: -------------------------------------------------------------------------------- 1 | Before 2 | 3 | After -------------------------------------------------------------------------------- /test/stubs/nested-alias.webc: -------------------------------------------------------------------------------- 1 | Before 2 | 3 | After -------------------------------------------------------------------------------- /test/stubs/nested-content-with-attr.webc: -------------------------------------------------------------------------------- 1 | Before 2 | Child content 3 | After -------------------------------------------------------------------------------- /test/stubs/nested-content.webc: -------------------------------------------------------------------------------- 1 | Before 2 | Child content 3 | After -------------------------------------------------------------------------------- /test/stubs/nested-link.webc: -------------------------------------------------------------------------------- 1 | ParentChild -------------------------------------------------------------------------------- /test/stubs/nested-multiple-slots-raw.webc: -------------------------------------------------------------------------------- 1 | Before 2 | 3 |

    Before slot content!

    4 |

    Slot 1 content

    5 |
    6 | 7 |

    Slot 2 content

    8 |
    9 |

    After slot content!

    10 |
    11 | After -------------------------------------------------------------------------------- /test/stubs/nested-multiple-slots.webc: -------------------------------------------------------------------------------- 1 | Before 2 | 3 |

    Before slot content!

    4 |

    Slot 1 content

    5 |
    6 | 7 |

    Slot 2 content

    8 |
    9 |

    After slot content!

    10 |
    11 | After -------------------------------------------------------------------------------- /test/stubs/nested-no-shadowdom.webc: -------------------------------------------------------------------------------- 1 | Before 2 | 3 | Child content 4 | 5 | 6 | After -------------------------------------------------------------------------------- /test/stubs/nested-reference.webc: -------------------------------------------------------------------------------- 1 | Before 2 | 3 | After -------------------------------------------------------------------------------- /test/stubs/nested-twice.webc: -------------------------------------------------------------------------------- 1 | Before 2 | 3 | Child content 4 | 5 | 6 | After -------------------------------------------------------------------------------- /test/stubs/nested-webc-keep.webc: -------------------------------------------------------------------------------- 1 | Before 2 | 3 | After -------------------------------------------------------------------------------- /test/stubs/nested-webc-raw.webc: -------------------------------------------------------------------------------- 1 | Before 2 | 3 | After -------------------------------------------------------------------------------- /test/stubs/nested.webc: -------------------------------------------------------------------------------- 1 | Before 2 | 3 | After -------------------------------------------------------------------------------- /test/stubs/no-template.webc: -------------------------------------------------------------------------------- 1 |
    -------------------------------------------------------------------------------- /test/stubs/page-capital-doctype-issue-24.webc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /test/stubs/page.webc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /test/stubs/plaintext-transform.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/props-missing-nothis.webc: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/stubs/props-missing.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/props-no-this.webc: -------------------------------------------------------------------------------- 1 |

    2 |

    3 |

    4 |

    5 |

    6 |

    7 |

    8 |

    9 |

    10 |

    11 |

    12 |

    13 |

    14 |

    15 |

    -------------------------------------------------------------------------------- /test/stubs/props-versus-text/page.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/props.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/render-async.webc: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | 8 |
    -------------------------------------------------------------------------------- /test/stubs/render-async2.webc: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | 8 |
    -------------------------------------------------------------------------------- /test/stubs/render-child-component.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/render-raw-nokeep.webc: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | 8 |
    -------------------------------------------------------------------------------- /test/stubs/render-raw.webc: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | 8 |
    -------------------------------------------------------------------------------- /test/stubs/render-require.webc: -------------------------------------------------------------------------------- 1 |
    2 | -------------------------------------------------------------------------------- /test/stubs/render-slots-parent.webc: -------------------------------------------------------------------------------- 1 | 2 | This is content that I want to be raw in JS -------------------------------------------------------------------------------- /test/stubs/render.webc: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | 8 |
    -------------------------------------------------------------------------------- /test/stubs/render2.webc: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | 8 |
    -------------------------------------------------------------------------------- /test/stubs/render3.webc: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | 8 |
    -------------------------------------------------------------------------------- /test/stubs/sample-require.cjs: -------------------------------------------------------------------------------- 1 | module.exports = "Imported"; -------------------------------------------------------------------------------- /test/stubs/scoped-override-collisions.webc: -------------------------------------------------------------------------------- 1 | Light dom content 2 | Light dom content -------------------------------------------------------------------------------- /test/stubs/scoped-override.webc: -------------------------------------------------------------------------------- 1 | Light dom content -------------------------------------------------------------------------------- /test/stubs/scoped-top.webc: -------------------------------------------------------------------------------- 1 | 13 |
    Testing testing
    -------------------------------------------------------------------------------- /test/stubs/scoped.webc: -------------------------------------------------------------------------------- 1 | Light dom content -------------------------------------------------------------------------------- /test/stubs/script-type.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/setup-script/component.webc: -------------------------------------------------------------------------------- 1 |
    2 | 9 |
    -------------------------------------------------------------------------------- /test/stubs/setup-script/test.js: -------------------------------------------------------------------------------- 1 | import test from "ava"; 2 | import { WebC } from "../../../webc.js"; 3 | 4 | test("webc:setup #87", async t => { 5 | let component = new WebC(); 6 | 7 | component.setContent(`
    8 | 15 |
    `); 16 | 17 | let { html } = await component.compile(); 18 | 19 | t.is(html.trim(), `
    1
    20 | 21 |
    blue
    `); 22 | }); 23 | 24 | test("webc:setup with a helper #87", async t => { 25 | let component = new WebC(); 26 | 27 | component.setHelper("alwaysYellow", () => "yellow"); 28 | 29 | component.setContent(` 34 |
    `); 35 | 36 | let { html } = await component.compile(); 37 | 38 | t.is(html.trim(), `
    yellow
    `); 39 | }); 40 | 41 | test("webc:setup with global data #87", async t => { 42 | let component = new WebC(); 43 | 44 | component.setHelper("alwaysYellow", () => "yellow"); 45 | 46 | component.setContent(` 51 |
    `); 52 | 53 | let { html } = await component.compile({ 54 | data: { 55 | globalDataValue: "hello" 56 | } 57 | }); 58 | 59 | t.is(html.trim(), `
    hello
    `); 60 | }); 61 | 62 | 63 | test("webc:setup with child component #87", async t => { 64 | let component = new WebC(); 65 | component.setHelper("globalFunction", (a) => a); 66 | 67 | component.defineComponents("./test/stubs/setup-script/component.webc"); 68 | 69 | component.setContent(`
    70 | 77 | 78 |
    `); 79 | 80 | let { html } = await component.compile(); 81 | 82 | t.is(html.trim(), `
    1
    83 | 84 |
    2
    85 | 86 |
    red
    87 |
    blue
    `); 88 | }); -------------------------------------------------------------------------------- /test/stubs/slot-fallback-content.webc: -------------------------------------------------------------------------------- 1 |
    Fallback content
    -------------------------------------------------------------------------------- /test/stubs/slot-keep.webc: -------------------------------------------------------------------------------- 1 |
    Fallback content
    -------------------------------------------------------------------------------- /test/stubs/slot-named-fallback.webc: -------------------------------------------------------------------------------- 1 |
    Fallback content
    -------------------------------------------------------------------------------- /test/stubs/slot-named.webc: -------------------------------------------------------------------------------- 1 |
    -------------------------------------------------------------------------------- /test/stubs/slot-nested-2.webc: -------------------------------------------------------------------------------- 1 |
    TextText
    -------------------------------------------------------------------------------- /test/stubs/slot-nested-3.webc: -------------------------------------------------------------------------------- 1 |
    TextText
    -------------------------------------------------------------------------------- /test/stubs/slot-nested.webc: -------------------------------------------------------------------------------- 1 |
    TextText
    -------------------------------------------------------------------------------- /test/stubs/slot-raw.webc: -------------------------------------------------------------------------------- 1 |
    Fallback content
    -------------------------------------------------------------------------------- /test/stubs/slot-unused-2.webc: -------------------------------------------------------------------------------- 1 |
    Text

    -------------------------------------------------------------------------------- /test/stubs/slot-unused-default.webc: -------------------------------------------------------------------------------- 1 |
    Text
    -------------------------------------------------------------------------------- /test/stubs/slot-unused.webc: -------------------------------------------------------------------------------- 1 |
    Text
    -------------------------------------------------------------------------------- /test/stubs/slot.webc: -------------------------------------------------------------------------------- 1 |
    -------------------------------------------------------------------------------- /test/stubs/style-merge.webc: -------------------------------------------------------------------------------- 1 | Before 2 | 3 | After -------------------------------------------------------------------------------- /test/stubs/style-override.webc: -------------------------------------------------------------------------------- 1 | 13 |
    Testing testing
    -------------------------------------------------------------------------------- /test/stubs/style.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/template-custom-keep.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/template-custom-nested.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/template-custom-notype.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/template-custom.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/template.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/two-style.webc: -------------------------------------------------------------------------------- 1 | Light dom content 2 | Light dom content -------------------------------------------------------------------------------- /test/stubs/using-css-keep.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/using-css-root-override.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/using-css-root.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/using-css.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/using-img-plain.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/using-img.webc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/stubs/using-uid.webc: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /test/stubs/webc-raw-html-prop.webc: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/stubs/webc-raw-prop.webc: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /webc.js: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import fastglob from "fast-glob"; 3 | import isGlob from "is-glob"; 4 | import path from "path"; 5 | 6 | import { Path } from "./src/path.js"; 7 | import { AstSerializer } from "./src/ast.js"; 8 | import { ModuleScript } from "./src/moduleScript.cjs"; 9 | import { AstCache } from "./src/astCache.js"; 10 | import { ModuleResolution } from "./src/moduleResolution.js"; 11 | import { ComponentManager } from "./src/componentManager.js"; 12 | 13 | const localAstCache = new AstCache(); 14 | 15 | class WebC { 16 | constructor(options = {}) { 17 | let { file, input } = options; 18 | 19 | this.customTransforms = {}; 20 | this.customHelpers = {}; 21 | this.customScopedHelpers = {}; 22 | this.globalComponents = {}; 23 | this.astOptions = {}; 24 | this.bundlerMode = false; 25 | this.ignores = options.ignores || []; 26 | 27 | if(input || input === "") { 28 | this.rawInput = input; 29 | } 30 | if(file) { 31 | this.setInputPath(file); 32 | } 33 | } 34 | 35 | setInputPath(file) { 36 | file = Path.normalizePath(file); 37 | this.filePath = file; 38 | this.astOptions.filePath = file; 39 | } 40 | 41 | setContent(input, filePath) { 42 | this.rawInput = input; 43 | 44 | if(filePath) { 45 | this.astOptions.filePath = filePath; 46 | } 47 | } 48 | 49 | setGlobalComponentManager(manager) { 50 | this.globalComponentManager = manager; 51 | } 52 | 53 | getRenderingMode(content) { 54 | if(!content.startsWith(") 82 | if(mode === "component" || !content.startsWith("${content}`; 84 | } 85 | 86 | return { 87 | content, 88 | mode, 89 | }; 90 | } 91 | 92 | static async getASTFromString(string) { 93 | let wc = new WebC({ 94 | input: string 95 | }); 96 | let { content } = wc.getContent(); 97 | return wc.getAST(content); 98 | } 99 | 100 | // @deprecated for getFromFilePath 101 | static async getASTFromFilePath(filePath) { 102 | let wc = new WebC({ 103 | file: filePath 104 | }); 105 | let { content } = wc.getContent(); 106 | return wc.getAST(content); 107 | } 108 | 109 | static async getFromFilePath(filePath) { 110 | let wc = new WebC({ 111 | file: filePath 112 | }); 113 | let { content, mode } = wc.getContent(); 114 | 115 | return { 116 | content, 117 | ast: await wc.getAST(content), 118 | mode, 119 | }; 120 | } 121 | 122 | getAST(content) { 123 | if(!content) { 124 | throw new Error("WebC.getAST() expects a content argument."); 125 | } 126 | 127 | return localAstCache.get(content); 128 | } 129 | 130 | setTransform(key, callback) { 131 | this.customTransforms[key] = callback; 132 | } 133 | 134 | setHelper(key, callback, isScoped = false) { 135 | if(isScoped) { 136 | this.customScopedHelpers[key] = callback; 137 | } else { 138 | this.customHelpers[key] = callback; 139 | } 140 | } 141 | 142 | setAlias(key, folder) { 143 | if(!this.aliases) { 144 | this.aliases = {}; 145 | } 146 | 147 | this.aliases[key] = folder; 148 | } 149 | 150 | async _defineComponentsObject(obj = {}) { 151 | for(let name in obj) { 152 | let file = obj[name]; 153 | if(this.globalComponents[name]) { 154 | throw new Error(`Global component name collision on "${name}" between: ${this.globalComponents[name]} and ${file}`) 155 | } 156 | this.globalComponents[name] = file; 157 | } 158 | } 159 | 160 | static findGlob(glob, ignores = []) { 161 | return fastglob.sync(glob, { 162 | ignore: ignores, 163 | caseSensitiveMatch: false, 164 | dot: false, 165 | }); 166 | } 167 | 168 | static getComponentsMap(globOrObject, ignores) { 169 | let rawFiles = globOrObject; 170 | 171 | // Passing in a single string is assumed to be a glob 172 | if(typeof rawFiles === "string") { 173 | rawFiles = WebC.findGlob(globOrObject, ignores); 174 | } 175 | 176 | if(Array.isArray(rawFiles)) { 177 | let moduleResolver = new ModuleResolution(); 178 | let resolvedFiles = new Set(); 179 | for(let file of rawFiles) { 180 | // Resolve `npm:` aliases 181 | let hasValidAlias = moduleResolver.hasValidAlias(file); 182 | if(hasValidAlias) { 183 | file = moduleResolver.resolveAliases(file); 184 | } 185 | 186 | // Multiple glob searches 187 | if(isGlob(file)) { 188 | let globResults = WebC.findGlob(file, ignores); 189 | for(let globFile of globResults) { 190 | resolvedFiles.add(globFile); 191 | } 192 | } else { 193 | resolvedFiles.add(file); 194 | } 195 | } 196 | 197 | let obj = {}; 198 | for(let file of resolvedFiles) { 199 | let {name} = path.parse(file); 200 | if(obj[name]) { 201 | throw new Error(`Global component name collision on "${name}" between: ${obj[name]} and ${file}`) 202 | } 203 | obj[name] = file; 204 | } 205 | 206 | return obj; 207 | } 208 | 209 | return globOrObject; 210 | } 211 | 212 | defineComponents(globOrObject) { 213 | this._defineComponentsObject(WebC.getComponentsMap(globOrObject, this.ignores)); 214 | } 215 | 216 | setUidFunction(fn) { 217 | this.uidFn = fn; 218 | } 219 | 220 | async setup(options = {}) { 221 | let { content, mode } = this.getContent(); 222 | let rawAst = this.getAST(content); 223 | 224 | let ast = new AstSerializer(this.astOptions); 225 | ast.setComponentManager(this.globalComponentManager); 226 | ast.setBundlerMode(this.bundlerMode); 227 | ast.setMode(mode); 228 | ast.setContent(content); 229 | ast.setData(options.data); 230 | 231 | if(this.aliases && Object.keys(this.aliases).length) { 232 | ast.setAliases(this.aliases); 233 | } 234 | 235 | if(this.uidFn) { 236 | ast.setUidFunction(this.uidFn); 237 | } 238 | 239 | for(let name in this.customTransforms) { 240 | ast.setTransform(name, this.customTransforms[name]); 241 | } 242 | 243 | for(let name in this.customHelpers) { 244 | ast.setHelper(name, this.customHelpers[name], false); 245 | } 246 | for(let name in this.customScopedHelpers) { 247 | ast.setHelper(name, this.customScopedHelpers[name], true); 248 | } 249 | 250 | await ast.setComponentsByFilePath(this.globalComponents); 251 | await ast.setComponentsByFilePath(options.components); 252 | 253 | return { 254 | ast: rawAst, 255 | serializer: ast, 256 | }; 257 | } 258 | 259 | getComponents(setup) { 260 | let { ast, serializer } = setup; 261 | let obj = serializer.getComponentList(ast); 262 | return Object.keys(obj); 263 | } 264 | 265 | setBundlerMode(mode) { 266 | this.bundlerMode = !!mode; 267 | } 268 | 269 | async stream(options = {}) { 270 | let { ast, serializer } = await this.setup(options); 271 | 272 | serializer.streams.start(); 273 | 274 | serializer.compile(ast, options.slots).catch(() => { 275 | // Node requires this to avoid unhandled rejection errors (yes, even with `finally`) 276 | serializer.streams.end(); 277 | }).finally(() => { 278 | serializer.streams.end(); 279 | }); 280 | 281 | return serializer.streams.get(); 282 | } 283 | 284 | async compile(options = {}) { 285 | let { ast, serializer } = await this.setup(options); 286 | 287 | return serializer.compile(ast, options.slots); 288 | } 289 | } 290 | 291 | export { WebC, ModuleScript, ComponentManager }; --------------------------------------------------------------------------------