├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── cli.ts
├── package.json
├── scripts
├── du.js
├── hash.sh
├── size.sh
└── update-hash.sh
├── template
├── .gitignore
├── README.md
├── package.json
├── public
│ ├── app.html
│ ├── base.js
│ ├── examples
│ │ ├── article.html
│ │ ├── bind.html
│ │ ├── counter.html
│ │ ├── data-show.html
│ │ ├── edit.html
│ │ ├── form.html
│ │ ├── fragment.html
│ │ ├── icon-list.html
│ │ ├── list.html
│ │ └── search.html
│ ├── footer.html
│ ├── header.html
│ ├── index.html
│ └── style.css
├── server
│ ├── api.ts
│ ├── env.ts
│ ├── error.ts
│ └── main.ts
└── tsconfig.json
└── tsconfig.json
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | package-lock.json
3 | pnpm-lock.yaml
4 | yarn.lock
5 | *.min.js
6 | *.gz
7 | *.tgz
8 | dist/
9 | tsconfig.tsbuildinfo
10 | cli.js
11 | base.js
12 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .dccache
2 | *.tgz
3 | pnpm-lock.yaml
4 | cli.ts
5 | scripts/
6 | tsconfig.json
7 | !template/tsconfig.json
8 | tsconfig.tsbuildinfo
9 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 2-Clause License
2 |
3 | Copyright (c) [2022], [Beeno Tung (Tung Cheung Leong)]
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # data-template
2 |
3 | Lightweight and minimal HTML template helpers powered by native DOM
4 |
5 | [](https://www.npmjs.com/package/data-template)
6 | [](https://bundlephobia.com/package/data-template)
7 | [](https://bundlephobia.com/package/data-template)
8 |
9 | Server-side-rendering (SSR) mode available via [node-data-template](https://github.com/beenotung/node-data-template)
10 |
11 | ## Installation (with CDN)
12 |
13 | Drop below line in your html with automatic patch updates:
14 |
15 | ```html
16 |
17 | ```
18 |
19 |
20 | Or specify the exact version with integrity protection:
21 |
22 | ```html
23 |
28 | ```
29 |
30 |
31 |
32 |
33 | You can use the minified version as well:
34 |
35 | ```html
36 |
41 | ```
42 |
43 |
44 |
45 | ## Get Started (with template project)
46 |
47 | For new project without existing files, you can use the `data-template` cli to setup a simple project from template.
48 |
49 | ```bash
50 | npx data-template my-app
51 | cd my-app
52 | # then see the guides in the console output and README.md file
53 | ```
54 |
55 | ## Features
56 |
57 | - [x] apply data into dom based on dataset (`data-*`) attributes
58 | - [x] auto repeat elements if the value is an array
59 | - [x] support fragments with [nested template](./template/public/examples/fragment.html#:L14)
60 | - [x] fetch and cache html template and api response with localStorage
61 | - [x] helper functions to do ajax and input format (date/time)
62 | - [x] lightweight, [1KB minified and gzipped](#size)
63 |
64 | **Supported `data-*` attributes**:
65 |
66 | | category | attributes |
67 | | -------- | -------------------------------------------- |
68 | | general | text, class, id, title |
69 | | link | href |
70 | | media | src, alt |
71 | | display | hidden, show, if |
72 | | input | value, checked, selected, disabled, readonly |
73 | | dialog | open |
74 | | form | action, onsubmit |
75 | | event | onclick |
76 |
77 | ## Quick Example with CDN
78 |
79 | (For script tag with exact version and integrity checksum, see [above section](#installation-with-cdn))
80 |
81 | ```html
82 |
83 |
84 |
85 |
86 |
87 | loading articles...
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 | Details
98 |
99 |
100 |
101 |
124 | ```
125 |
126 | More examples see [template/public](template/public)
127 |
128 | ## Functions
129 |
130 | **Render Functions**:
131 |
132 | ```javascript
133 | // render data-* attributes
134 | function renderData(container, values);
135 |
136 | // render template on specific host element
137 | function renderTemplate(hostElement, binds);
138 |
139 | // recursive scan for templates and render them
140 | function scanTemplates(rootElement, binds);
141 |
142 | // populate the form using values from the object
143 | function fillForm(form, object);
144 | ```
145 |
146 | **Format Functions**:
147 |
148 | ```javascript
149 | // prepend '0' of the number is less than ten
150 | function d2(number);
151 |
152 | // convert to 'YYYY-MM-DD' format for input[type=date]
153 | function toInputDate(date_or_time_or_string);
154 |
155 | // convert to 'HH:mm' format for input[type=time]
156 | function toInputTime(date_or_time_or_string);
157 | ```
158 |
159 | **AJAX Functions**:
160 |
161 | ```javascript
162 | // return promise of string, cached with localStorage
163 | function getText(url, options, callback);
164 |
165 | // return promise of json value, cached with localStorage
166 | function getJSON(url, options, callback);
167 |
168 | // submit form with ajax request in application/json
169 | function submitJSON(event_or_form): Promise
170 |
171 | // submit form with ajax request in application/x-www-form-urlencoded or url search parameters
172 | function submitForm(event_or_form): Promise
173 |
174 | // submit form with ajax request in multipart/form-data
175 | function uploadForm(event_or_form): Promise
176 |
177 | // send ajax request in application/json
178 | function postJSON(url, body): Promise
179 | function patchJSON(url, body): Promise
180 | function putJSON(url, body): Promise
181 |
182 | // send ajax request with DELETE method
183 | function del(url): Promise
184 | ```
185 |
186 | For the `getText()` and `getJSON()` functions, the `options` and `cb` arguments are optional.
187 |
188 | The `options` object is the second argument passed to the `fetch` function.
189 |
190 | The `callback` function will be called with cached _and/or_ fetched data [(details)](#when-will-the-callback-function-be-called).
191 |
192 | If is recommended to provide `{ cache: 'reload' }` in the `options` or use callback function to receive the data if you want to avoid staled view.
193 |
194 | The returned promise can be used to do error handling.
195 |
196 | ### When will the callback function be called
197 |
198 | If the fetching data is already cached by url, the callback will be called immediately.
199 | Then the data will be fetched no matter cached or not.
200 | If the newly fetched data is different from the cached data, the callback will be called again.
201 |
202 | ## Size
203 |
204 | | Format | File Size |
205 | | -------------- | --------- |
206 | | base.js | 5.3 KB |
207 | | base.min.js | 2.9 KB |
208 | | base.min.js.gz | 1.4 KB |
209 |
210 | ## License
211 |
212 | This project is licensed with [BSD-2-Clause](./LICENSE)
213 |
214 | This is free, libre, and open-source software. It comes down to four essential freedoms [[ref]](https://seirdy.one/2021/01/27/whatsapp-and-the-domestication-of-users.html#fnref:2):
215 |
216 | - The freedom to run the program as you wish, for any purpose
217 | - The freedom to study how the program works, and change it so it does your computing as you wish
218 | - The freedom to redistribute copies so you can help others
219 | - The freedom to distribute copies of your modified versions to others
220 |
--------------------------------------------------------------------------------
/cli.ts:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | import { readFileSync, writeFileSync } from 'fs'
3 | import { copyTemplate, getDest, hasExec } from 'npm-init-helper'
4 | import { basename, join } from 'path'
5 |
6 | async function main() {
7 | let srcDir = join(__dirname, 'template')
8 | let dest = await getDest()
9 | await copyTemplate({
10 | srcDir,
11 | dest,
12 | updatePackageJson: true,
13 | verbose: true,
14 | })
15 | let name = basename(dest)
16 | if (name !== 'my-app') {
17 | // update project name in README.md
18 | let file = join(dest, 'README.md')
19 | let text = readFileSync(file)
20 | .toString()
21 | .replace(/my-app/g, name)
22 | writeFileSync(file, text)
23 | }
24 | console.log(
25 | `
26 | Done.
27 |
28 | Get started by typing:
29 |
30 | cd ${dest}
31 | `.trim(),
32 | )
33 |
34 | if (hasExec('pnpm')) {
35 | console.log(` pnpm i`)
36 | console.log(` npm run dev`)
37 | } else if (hasExec('yarn')) {
38 | console.log(` yarn install`)
39 | console.log(` yarn dev`)
40 | } else {
41 | console.log(` npm i`)
42 | console.log(` npm run dev`)
43 | }
44 | }
45 | main().catch(e => console.error(e))
46 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "data-template",
3 | "version": "1.10.1",
4 | "description": "Lightweight and minimal HTML template helpers powered by native DOM",
5 | "main": "base.js",
6 | "bin": {
7 | "data-template": "cli.js"
8 | },
9 | "scripts": {
10 | "test": "tsc --noEmit",
11 | "build": "run-p tsc base",
12 | "base": "bash scripts/size.sh && cp template/public/base.*js .",
13 | "tsc": "tsc -p ."
14 | },
15 | "repository": {
16 | "type": "git",
17 | "url": "git+https://github.com/beenotung/data-template.git"
18 | },
19 | "keywords": [
20 | "html",
21 | "template",
22 | "native",
23 | "dom",
24 | "data",
25 | "dataset",
26 | "attribute",
27 | "render",
28 | "lightweight",
29 | "minimal",
30 | "ajax",
31 | "html-template",
32 | "data-template",
33 | "data-attribute",
34 | "data-binding"
35 | ],
36 | "author": "",
37 | "license": "BSD-2-Clause",
38 | "bugs": {
39 | "url": "https://github.com/beenotung/data-template/issues"
40 | },
41 | "homepage": "https://github.com/beenotung/data-template#readme",
42 | "devDependencies": {
43 | "@types/node": "^16.18.52",
44 | "npm-run-all": "^4.1.5",
45 | "ts-node": "^10.9.1",
46 | "typescript": "^5.2.2"
47 | },
48 | "dependencies": {
49 | "npm-init-helper": "^1.5.0"
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/scripts/du.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | // using readFile instead of stat to be compatible to MacOS
4 | let { readFileSync } = require('fs')
5 | let { argv } = process
6 |
7 | let files = argv.slice(2)
8 |
9 | let maxFileLength = Math.max(0, ...files.map(file => file.length))
10 |
11 | for (let file of files) {
12 | let size = readFileSync(file).length
13 | let padding = ' '.repeat(maxFileLength - file.length)
14 | let line = file + padding + '\t' + size
15 | if (size >= 1024) {
16 | line += `\t(${(size / 1024).toFixed(1)} KB)`
17 | }
18 | console.log(line)
19 | }
20 |
--------------------------------------------------------------------------------
/scripts/hash.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # generate integrity attribute
3 | set -e
4 | set -o pipefail
5 | hash=$(shasum -b -a 384 $1 | awk '{print $1}' | xxd -r -p | base64)
6 | echo ""
11 |
--------------------------------------------------------------------------------
/scripts/size.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 | set -o pipefail
4 |
5 | cd template/public
6 | npx --yes esbuild base.js --minify > base.min.js
7 | gzip -f -k base.min.js
8 |
9 | ../../scripts/du.js base.*
10 |
--------------------------------------------------------------------------------
/scripts/update-hash.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 | set -o pipefail
4 | ./scripts/size.sh
5 | cp template/public/base.js .
6 | cp template/public/base.min.js .
7 | ./scripts/hash.sh base.js >> README.md
8 | ./scripts/hash.sh base.min.js >> README.md
9 |
--------------------------------------------------------------------------------
/template/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | package-lock.json
3 | pnpm-lock.yaml
4 | yarn.lock
5 | *.tgz
6 | dist/
7 | .env
8 |
--------------------------------------------------------------------------------
/template/README.md:
--------------------------------------------------------------------------------
1 | # my-app
2 |
3 | Powered by [data-template](https://github.com/beenotung/data-template)
4 |
5 | ## Get Started
6 |
7 | ### Install dependencies
8 |
9 | Run: `npm install`
10 |
11 | Tips, you can also use below alternative installers:
12 |
13 | - `pnpm i`
14 | - `yarn install`
15 | - `slnpm`
16 |
17 | ### Start development server
18 |
19 | Run: `npm run dev`
20 |
21 | You will see output like below:
22 |
23 | ```
24 | listening on http://localhost:8100
25 | listening on http://127.0.0.1:8100 (lo)
26 | listening on http://192.168.1.2:8100 (wlp3s0)
27 | ```
28 |
29 | Then you can open http://localhost:8100 with a browser
30 |
31 | The port number may be changed by the `PORT` variable in the `.env` file
32 |
33 | ### Deploy production server
34 |
35 | 1. Run `npm run build` to compile the typescript project and minify the `base.js`
36 | 2. Run `npm start` to start the node.js server
37 |
38 | If you have installed pm2, you can start the server with: `pm2 start --name my-app dist/main.js`
39 |
40 | In the production mode, the server will enable compression (e.g. gzip) when the client supports it.
41 |
42 | It will also use the minified `base.min.js` when requested `base.min`, so you don't have to change the `src` attribute in the `
16 |
20 |
24 |
25 |
26 |
27 |
28 |
29 | Articles
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
43 |
44 |
45 |
46 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | Cancel
63 |
64 | Add Article
65 |
66 | Submit
67 |
68 |
69 |
70 |
71 |
122 |
123 |
124 |
125 |
126 |
127 |
128 | Close
129 |
130 | Details
131 |
132 |
133 |
134 |
135 |
136 |
143 |
144 |