├── .github
└── workflows
│ ├── latest.yml
│ └── release.yml
├── .gitignore
├── .npmignore
├── .vscode
└── launch.json
├── LICENSE
├── README.md
├── dev
├── context.json
├── index.html
├── sample.html
└── sample2.html
├── examples
├── cdn
│ ├── basic.html
│ └── context.html
└── npm
│ ├── basic.html
│ └── context.html
├── index.js
├── index.min.js
├── package.json
└── src
└── wc-template.js
/.github/workflows/latest.yml:
--------------------------------------------------------------------------------
1 | name: Latest
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | verify:
7 | name: Verify
8 | runs-on: ubuntu-latest
9 | steps:
10 | - name: Checkout
11 | uses: actions/checkout@v1
12 | - name: Setup
13 | uses: actions/setup-node@v1
14 | with:
15 | node-version: 14
16 | - name: Cache
17 | uses: actions/cache@v1
18 | with:
19 | path: node_modules
20 | key: ${{ runner.OS }}-npm-cache-${{ hashFiles('**/package.json') }}
21 | restore-keys: |
22 | ${{ runner.OS }}-npm-cache-
23 | - name: Install
24 | run: npm i
25 | - name: Test
26 | run: npm run test --if-present
27 | - name: Lint
28 | run: npm run lint --if-present
29 | - name: Types
30 | run: npm run types --if-present
31 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | push:
5 | tags:
6 | - 'v*'
7 |
8 | jobs:
9 | check:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Checkout
13 | uses: actions/checkout@master
14 | - name: Setup
15 | uses: actions/setup-node@v1
16 | with:
17 | node-version: 14
18 | - name: Cache
19 | uses: actions/cache@v1
20 | with:
21 | path: node_modules
22 | key: ${{ runner.OS }}-npm-cache-${{ hashFiles('**/package.json') }}
23 | restore-keys: |
24 | ${{ runner.OS }}-npm-cache-
25 | - name: Verify
26 | run: |
27 | npm i
28 | npm run preversion
29 | npm:
30 | runs-on: ubuntu-latest
31 | needs: check
32 | steps:
33 | - name: Checkout
34 | uses: actions/checkout@master
35 | - name: Publish
36 | run: |
37 | echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_AUTH_TOKEN }}" > ~/.npmrc
38 | npm publish --access public
39 | gh:
40 | runs-on: ubuntu-latest
41 | needs: check
42 | steps:
43 | - name: Checkout
44 | uses: actions/checkout@master
45 | - name: Publish
46 | run: |
47 | echo "//npm.pkg.github.com/:_authToken=${{ secrets.GITHUB_TOKEN }}" >> ~/.npmrc
48 | ORG="$(echo '${{ github.repository }}' | cut -d'/' -f1)"
49 | echo "registry=https://npm.pkg.github.com/$ORG" > .npmrc
50 | npm publish
51 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | package/
3 | *.tgz
4 | package-lock.json
5 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .github/
2 | .vscode/
3 | dev/
4 | *.tgz
5 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "type": "chrome",
6 | "request": "launch",
7 | "name": "Chrome",
8 | "url": "http://localhost:5500/dev",
9 | "webRoot": "${workspaceFolder}",
10 | "pathMapping": {
11 | "/dev/": "${workspaceFolder}/dev/"
12 | }
13 | },
14 | {
15 | "type": "browser-preview",
16 | "request": "launch",
17 | "name": "Browser Preview",
18 | "url": "http://localhost:5500/dev/",
19 | "webRoot": "${workspaceFolder}",
20 | "pathMapping": {
21 | "/dev/": "${workspaceFolder}/dev/"
22 | }
23 | }
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2020 VanillaWC
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining
6 | a copy of this software and associated documentation files (the
7 | 'Software'), to deal in the Software without restriction, including
8 | without limitation the rights to use, copy, modify, merge, publish,
9 | distribute, sublicense, and/or sell copies of the Software, and to
10 | permit persons to whom the Software is furnished to do so, subject to
11 | the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be
14 | included in all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
<wc-template> Tagged Template Literals in HTML
2 |
3 |
4 |

5 |

6 |

7 |

8 |

9 |
10 |

11 |

12 |
13 |
14 | ## Installation
15 |
16 | *Installation*
17 | ```sh
18 | npm i @vanillawc/wc-template
19 | ```
20 |
21 | *Import from NPM*
22 | ```html
23 |
24 | ```
25 |
26 | *Import from CDN*
27 | ```html
28 |
29 | ```
30 |
31 | ## Demo
32 |
33 | Try it on [WebComponents.dev](https://webcomponents.dev/edit/BggRSZ5D4tmsyce94mpB?sv=1&pm=1)
34 |
35 | ## Usage
36 |
37 | **Attributes**
38 |
39 | - `src` - load an external source file
40 | - `context` - load the tags from an external endpoint
41 |
42 | ### Basic Usage
43 |
44 | ```html
45 |
46 | ```
47 |
48 | ## Context
49 |
50 | If the template is a tagged template literal, context can be provided via an external endpoint.
51 |
52 | ```html
53 |
54 | ```
55 |
56 | ## Contributing
57 |
58 | See [CONTRIBUTING.md](https://github.com/vanillawc/vanillawc/blob/main/CONTRIBUTING.md)
59 |
--------------------------------------------------------------------------------
/dev/context.json:
--------------------------------------------------------------------------------
1 | {
2 | "names": [
3 | "John",
4 | "Jean",
5 | "Red",
6 | "Jerome"
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/dev/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/dev/sample.html:
--------------------------------------------------------------------------------
1 | This is a sample template
2 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Voluptates culpa sint expedita magni, quasi eos illo dolor, optio, nisi placeat sunt inventore laboriosam iusto recusandae iste asperiores? Quibusdam, iusto cum?
3 |
--------------------------------------------------------------------------------
/dev/sample2.html:
--------------------------------------------------------------------------------
1 |
2 | ${names.map(name => `
3 | - ${name}
4 | `).join('\n')}
5 |
--------------------------------------------------------------------------------
/examples/cdn/basic.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/examples/cdn/context.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/examples/npm/basic.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/examples/npm/context.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | // node_modules/@vanillaes/interpolate/index.js
2 | function interpolate(template, tags = {}) {
3 | const keys = Object.keys(tags);
4 | const values = Object.values(tags);
5 | try {
6 | return new Function(...keys, `return \`${template}\`;`)(...values);
7 | } catch (e) {
8 | throw new TemplateException(template, tags, e);
9 | }
10 | }
11 | var TemplateException = class extends Error {
12 | constructor(template, tags, message) {
13 | super();
14 | this.name = "TemplateError";
15 | let msg = "\n------------------\n";
16 | msg += `Template: \`${template}\``;
17 | msg += "\n------------------\n";
18 | msg += `Tags: ${JSON.stringify(tags, null, 2)}`;
19 | msg += "\n------------------\n";
20 | msg += message;
21 | this.message = msg;
22 | }
23 | };
24 |
25 | // src/wc-template.js
26 | var WCTemplate = class extends HTMLElement {
27 | static get observedAttributes() {
28 | return ["src", "context"];
29 | }
30 | attributeChangedCallback(name, oldValue, newValue) {
31 | if (!this.__initialized) {
32 | return;
33 | }
34 | if (oldValue !== newValue) {
35 | this[name] = newValue;
36 | }
37 | }
38 | get src() {
39 | return this.getAttribute("src");
40 | }
41 | set src(value) {
42 | this.setAttribute("src", value);
43 | this.setSrc();
44 | this.render();
45 | }
46 | get context() {
47 | return this.getAttribute("context");
48 | }
49 | set context(value) {
50 | this.setAttribute("context", value);
51 | this.setContext();
52 | this.render();
53 | }
54 | constructor() {
55 | super();
56 | this.__initialized = false;
57 | this.__template = "";
58 | this.__context = {};
59 | }
60 | async connectedCallback() {
61 | if (this.hasAttribute("src")) {
62 | await this.setSrc();
63 | }
64 | if (this.hasAttribute("context")) {
65 | await this.setContext();
66 | }
67 | this.render();
68 | this.__initialized = true;
69 | }
70 | async setSrc() {
71 | const path = this.getAttribute("src");
72 | this.__template = await this.fetchSrc(path);
73 | }
74 | async fetchSrc(src) {
75 | const response = await fetch(src);
76 | if (response.status !== 200)
77 | throw Error(`ERR ${response.status}: ${response.statusText}`);
78 | return response.text();
79 | }
80 | async setContext() {
81 | const path = this.getAttribute("context");
82 | this.__context = await this.fetchContext(path);
83 | }
84 | async fetchContext(src) {
85 | const response = await fetch(src);
86 | if (response.status !== 200)
87 | throw Error(`ERR ${response.status}: ${response.statusText}`);
88 | return response.json();
89 | }
90 | render() {
91 | this.innerHTML = interpolate(this.__template, this.__context);
92 | }
93 | };
94 | customElements.define("wc-template", WCTemplate);
95 | export {
96 | WCTemplate
97 | };
98 |
--------------------------------------------------------------------------------
/index.min.js:
--------------------------------------------------------------------------------
1 | function c(i,t={}){let e=Object.keys(t),r=Object.values(t);try{return new Function(...e,`return \`${i}\`;`)(...r)}catch(s){throw new n(i,t,s)}}var n=class extends Error{constructor(t,e,r){super();this.name="TemplateError";let s=`
2 | ------------------
3 | `;s+=`Template: \`${t}\``,s+=`
4 | ------------------
5 | `,s+=`Tags: ${JSON.stringify(e,null,2)}`,s+=`
6 | ------------------
7 | `,s+=r,this.message=s}};var a=class extends HTMLElement{static get observedAttributes(){return["src","context"]}attributeChangedCallback(t,e,r){!this.__initialized||e!==r&&(this[t]=r)}get src(){return this.getAttribute("src")}set src(t){this.setAttribute("src",t),this.setSrc(),this.render()}get context(){return this.getAttribute("context")}set context(t){this.setAttribute("context",t),this.setContext(),this.render()}constructor(){super();this.__initialized=!1,this.__template="",this.__context={}}async connectedCallback(){this.hasAttribute("src")&&await this.setSrc(),this.hasAttribute("context")&&await this.setContext(),this.render(),this.__initialized=!0}async setSrc(){let t=this.getAttribute("src");this.__template=await this.fetchSrc(t)}async fetchSrc(t){let e=await fetch(t);if(e.status!==200)throw Error(`ERR ${e.status}: ${e.statusText}`);return e.text()}async setContext(){let t=this.getAttribute("context");this.__context=await this.fetchContext(t)}async fetchContext(t){let e=await fetch(t);if(e.status!==200)throw Error(`ERR ${e.status}: ${e.statusText}`);return e.json()}render(){this.innerHTML=c(this.__template,this.__context)}};customElements.define("wc-template",a);export{a as WCTemplate};
8 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@vanillawc/wc-template",
3 | "version": "1.0.25",
4 | "license": "MIT",
5 | "author": "Evan Plaice (https://evanplaice.com/)",
6 | "description": "Template HTML with tagged template literals",
7 | "keywords": [
8 | "web-components",
9 | "vanilla",
10 | "tagged",
11 | "template",
12 | "literal"
13 | ],
14 | "repository": "https://github.com/vanillawc/wc-template/",
15 | "main": "index.js",
16 | "scripts": {
17 | "start": "npx live-server --no-browser --port=5500 --open=dev",
18 | "lint": "esmtk lint",
19 | "build": "npm run build:esm && npm run build:min",
20 | "build:esm": "esmtk bundle src/wc-template.js index.js",
21 | "build:min": "esmtk minify src/wc-template.js index.min.js",
22 | "package": "npx rimraf package && npm pack | tail -n 1 | xargs tar -xf",
23 | "preversion": "npm run lint",
24 | "postversion": "git push --follow-tags"
25 | },
26 | "devDependencies": {
27 | "@vanillaes/interpolate": "^2.0.4",
28 | "esmtk": "^0.5.6"
29 | },
30 | "standard": {
31 | "ignore": [
32 | "index.js"
33 | ]
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/wc-template.js:
--------------------------------------------------------------------------------
1 | /* eslint no-undef: 0 */
2 | import { interpolate } from '../node_modules/@vanillaes/interpolate/index.js'
3 |
4 | export class WCTemplate extends HTMLElement {
5 | static get observedAttributes () {
6 | return ['src', 'context']
7 | }
8 |
9 | attributeChangedCallback (name, oldValue, newValue) {
10 | if (!this.__initialized) { return }
11 | if (oldValue !== newValue) {
12 | this[name] = newValue
13 | }
14 | }
15 |
16 | get src () { return this.getAttribute('src') }
17 | set src (value) {
18 | this.setAttribute('src', value)
19 | this.setSrc()
20 | this.render()
21 | }
22 |
23 | get context () { return this.getAttribute('context') }
24 | set context (value) {
25 | this.setAttribute('context', value)
26 | this.setContext()
27 | this.render()
28 | }
29 |
30 | constructor () {
31 | super()
32 | this.__initialized = false
33 | this.__template = ''
34 | this.__context = {}
35 | }
36 |
37 | async connectedCallback () {
38 | if (this.hasAttribute('src')) {
39 | await this.setSrc()
40 | }
41 |
42 | if (this.hasAttribute('context')) {
43 | await this.setContext()
44 | }
45 |
46 | this.render()
47 | this.__initialized = true
48 | }
49 |
50 | async setSrc () {
51 | const path = this.getAttribute('src')
52 | this.__template = await this.fetchSrc(path)
53 | }
54 |
55 | async fetchSrc (src) {
56 | const response = await fetch(src)
57 | if (response.status !== 200) throw Error(`ERR ${response.status}: ${response.statusText}`)
58 | return response.text()
59 | }
60 |
61 | async setContext () {
62 | const path = this.getAttribute('context')
63 | this.__context = await this.fetchContext(path)
64 | }
65 |
66 | async fetchContext (src) {
67 | const response = await fetch(src)
68 | if (response.status !== 200) throw Error(`ERR ${response.status}: ${response.statusText}`)
69 | return response.json()
70 | }
71 |
72 | render () {
73 | this.innerHTML = interpolate(this.__template, this.__context)
74 | }
75 | }
76 |
77 | customElements.define('wc-template', WCTemplate)
78 |
--------------------------------------------------------------------------------