fragtml 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -51
- package/index.d.ts +1 -1
- package/index.d.ts.map +1 -1
- package/index.js +2 -2
- package/lib/create-tag.d.ts +3 -1
- package/lib/create-tag.d.ts.map +1 -1
- package/lib/create-tag.js +13 -7
- package/lib/html-result.d.ts +3 -2
- package/lib/html-result.d.ts.map +1 -1
- package/lib/html-result.js +2 -1
- package/lib/html-types.d.ts +9 -0
- package/lib/html-types.d.ts.map +1 -0
- package/lib/html-types.ts +10 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -156,45 +156,15 @@ html`<button ?disabled="${loading}">Save</button>`
|
|
|
156
156
|
html`<button ?disabled='${loading}'>Save</button>`
|
|
157
157
|
```
|
|
158
158
|
|
|
159
|
-
##
|
|
160
|
-
|
|
161
|
-
`html(options)` returns a configured tag. This is useful for fragment rendering and for editor syntax highlighting, because many editors highlight tagged templates better when the tag is a simple identifier.
|
|
162
|
-
|
|
163
|
-
```js
|
|
164
|
-
import { createHtml, render } from 'fragtml'
|
|
165
|
-
|
|
166
|
-
export function view ({ fragmentId }) {
|
|
167
|
-
const html = createHtml({ fragmentId })
|
|
168
|
-
|
|
169
|
-
return render(html`
|
|
170
|
-
<main>...</main>
|
|
171
|
-
`)
|
|
172
|
-
}
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
`createHtml` is an alias of `html`:
|
|
176
|
-
|
|
177
|
-
```js
|
|
178
|
-
import html, { createHtml } from 'fragtml'
|
|
179
|
-
|
|
180
|
-
createHtml === html
|
|
181
|
-
```
|
|
159
|
+
## Fragments
|
|
182
160
|
|
|
183
|
-
|
|
161
|
+
Fragments mark named ranges inside a larger template. Calling `html(fragmentId)` on that template renders either the full template or one selected fragment:
|
|
184
162
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
163
|
+
- `html()` / `html(undefined)` renders the full template.
|
|
164
|
+
- `html('archive-ui')` renders only the `archive-ui` fragment.
|
|
165
|
+
- `html({ fragmentId: 'archive-ui' })` is the options-object form.
|
|
188
166
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
```js
|
|
192
|
-
const html = createHtml({ fragmentId: 'archive-ui' })
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
## Fragments
|
|
196
|
-
|
|
197
|
-
Fragments mark a named range inside a larger template. Rendering without `fragmentId` returns the whole template. Rendering with `fragmentId` returns only that fragment.
|
|
167
|
+
This lets one view function serve both full-page requests and htmx-style fragment requests by passing the requested fragment ID through to `html(fragmentId)`.
|
|
198
168
|
|
|
199
169
|
This mirrors the htmx article’s idea:
|
|
200
170
|
|
|
@@ -215,12 +185,10 @@ ${html.fragment.end}
|
|
|
215
185
|
### Example
|
|
216
186
|
|
|
217
187
|
```js
|
|
218
|
-
import {
|
|
188
|
+
import html, { render } from 'fragtml'
|
|
219
189
|
|
|
220
190
|
export function contactDetail ({ contact, fragmentId }) {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
return render(html`
|
|
191
|
+
return render(html(fragmentId)`
|
|
224
192
|
<html>
|
|
225
193
|
<body>
|
|
226
194
|
<div hx-target="this">
|
|
@@ -253,6 +221,51 @@ contactDetail({ contact, fragmentId: 'archive-ui' })
|
|
|
253
221
|
|
|
254
222
|
Fragment boundary tokens are not included in either output.
|
|
255
223
|
|
|
224
|
+
If you want a simple local tag name for editor highlighting or repeated use, `frag` is an alias of `html`:
|
|
225
|
+
|
|
226
|
+
```js
|
|
227
|
+
import { frag, render } from 'fragtml'
|
|
228
|
+
|
|
229
|
+
export function contactDetail ({ contact, fragmentId }) {
|
|
230
|
+
const html = frag(fragmentId)
|
|
231
|
+
|
|
232
|
+
return render(html`
|
|
233
|
+
${html.fragment.start('archive-ui')}
|
|
234
|
+
<button>${contact.archived ? 'Unarchive' : 'Archive'}</button>
|
|
235
|
+
${html.fragment.end}
|
|
236
|
+
`)
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
In TypeScript, you can use an explicit fragment-name union to type-check both incoming fragment IDs and declared fragment boundaries:
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
import { frag, render } from 'fragtml'
|
|
244
|
+
|
|
245
|
+
const contactFragments = {
|
|
246
|
+
archiveUi: 'archive-ui',
|
|
247
|
+
details: 'details'
|
|
248
|
+
} as const
|
|
249
|
+
|
|
250
|
+
type ContactFragment = typeof contactFragments[keyof typeof contactFragments]
|
|
251
|
+
|
|
252
|
+
export function contactDetail ({
|
|
253
|
+
contact,
|
|
254
|
+
fragmentId
|
|
255
|
+
}: {
|
|
256
|
+
contact: Contact
|
|
257
|
+
fragmentId?: ContactFragment
|
|
258
|
+
}) {
|
|
259
|
+
const html = frag<ContactFragment>(fragmentId)
|
|
260
|
+
|
|
261
|
+
return render(html`
|
|
262
|
+
${html.fragment.start(contactFragments.archiveUi)}
|
|
263
|
+
<button>${contact.archived ? 'Unarchive' : 'Archive'}</button>
|
|
264
|
+
${html.fragment.end}
|
|
265
|
+
`)
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
256
269
|
### Fragment rules
|
|
257
270
|
|
|
258
271
|
- Fragment IDs must be unique within a rendered template.
|
|
@@ -266,12 +279,10 @@ Fragment boundary tokens are not included in either output.
|
|
|
266
279
|
Nested fragments are supported with stack semantics. This is useful when a larger region can be re-rendered as a whole, but a smaller region inside it is also a valid htmx update target.
|
|
267
280
|
|
|
268
281
|
```js
|
|
269
|
-
import {
|
|
282
|
+
import html, { render } from 'fragtml'
|
|
270
283
|
|
|
271
284
|
export function page ({ fragmentId }) {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
return render(html`
|
|
285
|
+
return render(html(fragmentId)`
|
|
275
286
|
${html.fragment.start('outer')}
|
|
276
287
|
<section>
|
|
277
288
|
<h2>Outer</h2>
|
|
@@ -311,21 +322,21 @@ Safe-by-default template tag.
|
|
|
311
322
|
html`<p>${value}</p>`
|
|
312
323
|
```
|
|
313
324
|
|
|
314
|
-
|
|
325
|
+
Pass a fragment ID before the tagged template to render a selected fragment from that template:
|
|
315
326
|
|
|
316
327
|
```js
|
|
317
|
-
|
|
318
|
-
|
|
328
|
+
html('name')`...`
|
|
329
|
+
html({ fragmentId: 'name' })`...`
|
|
319
330
|
```
|
|
320
331
|
|
|
321
|
-
### `
|
|
332
|
+
### `frag`
|
|
322
333
|
|
|
323
|
-
Alias of `html`,
|
|
334
|
+
Alias of `html`, useful when you want a local tag name for editor highlighting or repeated use:
|
|
324
335
|
|
|
325
336
|
```js
|
|
326
|
-
import {
|
|
337
|
+
import { frag } from 'fragtml'
|
|
327
338
|
|
|
328
|
-
const html =
|
|
339
|
+
const html = frag(fragmentId)
|
|
329
340
|
```
|
|
330
341
|
|
|
331
342
|
### `render(value)`
|
package/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export default html;
|
|
2
2
|
import { html } from './lib/html.js';
|
|
3
|
-
export const
|
|
3
|
+
export const frag: import("./lib/create-tag.js").HtmlTag;
|
|
4
4
|
import { raw } from './lib/raw.js';
|
|
5
5
|
import { render } from './lib/render.js';
|
|
6
6
|
export { html, raw, render };
|
package/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.js"],"names":[],"mappings":";qBAAqB,eAAe;AAIpC
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.js"],"names":[],"mappings":";qBAAqB,eAAe;AAIpC,yDAAiB;oBAHG,cAAc;uBACX,iBAAiB"}
|
package/index.js
CHANGED
|
@@ -2,8 +2,8 @@ import { html } from './lib/html.js'
|
|
|
2
2
|
import { raw } from './lib/raw.js'
|
|
3
3
|
import { render } from './lib/render.js'
|
|
4
4
|
|
|
5
|
-
const
|
|
5
|
+
const frag = html
|
|
6
6
|
|
|
7
7
|
export default html
|
|
8
|
-
export {
|
|
8
|
+
export { frag, html, raw, render }
|
|
9
9
|
export { DuplicateFragmentError, FragmentBoundaryError, FragmentNotFoundError } from './lib/render.js'
|
package/lib/create-tag.d.ts
CHANGED
|
@@ -5,10 +5,12 @@ export type RenderOptions = {
|
|
|
5
5
|
export type CompiledTemplate = {
|
|
6
6
|
strings: readonly string[];
|
|
7
7
|
};
|
|
8
|
-
export type HtmlTag = ((strings:
|
|
8
|
+
export type HtmlTag = ((strings: TemplateStrings, ...substitutions: HtmlSubstitution[]) => HtmlResult) & ((options?: RenderOptions | string) => HtmlTag) & {
|
|
9
9
|
fragment: FragmentHelpers;
|
|
10
10
|
raw: (value: unknown) => RawHtml;
|
|
11
11
|
};
|
|
12
|
+
import type { TemplateStrings } from './html-types.js';
|
|
13
|
+
import type { HtmlSubstitution } from './html-types.js';
|
|
12
14
|
import { HtmlResult } from './html-result.js';
|
|
13
15
|
import type { FragmentHelpers } from './fragment.js';
|
|
14
16
|
import type { RawHtml } from './raw.js';
|
package/lib/create-tag.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-tag.d.ts","sourceRoot":"","sources":["create-tag.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"create-tag.d.ts","sourceRoot":"","sources":["create-tag.js"],"names":[],"mappings":"AAqFA,mBADW,OAAO,CACoB;;iBA1ExB,MAAM,GAAG,SAAS;;;aAKlB,SAAS,MAAM,EAAE;;sBAwElB,CAAC,CAAC,OAAO,EAAE,eAAe,EAAE,GAAG,aAAa,EAAE,gBAAgB,EAAE,KAAK,UAAU,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,aAAa,GAAG,MAAM,KAAK,OAAO,CAAC,GAAG;IAAE,QAAQ,EAAE,eAAe,CAAC;IAAC,GAAG,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAA;CAAE;qCAvF1J,iBAAiB;sCAAjB,iBAAiB;2BAI7C,kBAAkB;qCALR,eAAe;6BAEvB,UAAU"}
|
package/lib/create-tag.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/** @import { FragmentHelpers } from './fragment.js' */
|
|
2
|
+
/** @import { HtmlSubstitution, TemplateStrings } from './html-types.js' */
|
|
2
3
|
/** @import { RawHtml } from './raw.js' */
|
|
3
4
|
|
|
4
5
|
import { createFragmentHelpers } from './fragment.js'
|
|
@@ -56,8 +57,11 @@ function normalizeOptions (options) {
|
|
|
56
57
|
* @returns {HtmlTag}
|
|
57
58
|
*/
|
|
58
59
|
function createBoundTag (options) {
|
|
59
|
-
|
|
60
|
-
|
|
60
|
+
/**
|
|
61
|
+
* @param {TemplateStrings | RenderOptions | string | undefined} strings
|
|
62
|
+
* @param {...HtmlSubstitution} substitutions
|
|
63
|
+
*/
|
|
64
|
+
function tag (strings, ...substitutions) {
|
|
61
65
|
if (!isTemplateStrings(strings)) {
|
|
62
66
|
return createBoundTag(normalizeOptions(/** @type {RenderOptions | string | undefined} */ strings))
|
|
63
67
|
}
|
|
@@ -68,17 +72,19 @@ function createBoundTag (options) {
|
|
|
68
72
|
options,
|
|
69
73
|
renderResult
|
|
70
74
|
)
|
|
71
|
-
}
|
|
75
|
+
}
|
|
72
76
|
|
|
73
|
-
|
|
74
|
-
tag.raw = raw
|
|
77
|
+
const htmlTag = /** @type {HtmlTag} */ (tag)
|
|
75
78
|
|
|
76
|
-
|
|
79
|
+
htmlTag.fragment = fragment
|
|
80
|
+
htmlTag.raw = raw
|
|
81
|
+
|
|
82
|
+
return htmlTag
|
|
77
83
|
}
|
|
78
84
|
|
|
79
85
|
/** @type {HtmlTag} */
|
|
80
86
|
export const html = createBoundTag({})
|
|
81
87
|
|
|
82
88
|
/**
|
|
83
|
-
* @typedef {((strings:
|
|
89
|
+
* @typedef {((strings: TemplateStrings, ...substitutions: HtmlSubstitution[]) => HtmlResult) & ((options?: RenderOptions | string) => HtmlTag) & { fragment: FragmentHelpers, raw: (value: unknown) => RawHtml }} HtmlTag
|
|
84
90
|
*/
|
package/lib/html-result.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export function isHtmlResult(value: unknown): value is HtmlResult;
|
|
2
2
|
export const htmlResultSymbol: unique symbol;
|
|
3
3
|
export class HtmlResult {
|
|
4
|
-
constructor(compiled: CompiledTemplate, substitutions:
|
|
4
|
+
constructor(compiled: CompiledTemplate, substitutions: HtmlSubstitution[], options: RenderOptions, render: (result: HtmlResult) => string);
|
|
5
5
|
compiled: CompiledTemplate;
|
|
6
|
-
substitutions:
|
|
6
|
+
substitutions: HtmlSubstitution[];
|
|
7
7
|
options: RenderOptions;
|
|
8
8
|
render: (result: HtmlResult) => string;
|
|
9
9
|
toString(): string;
|
|
@@ -12,5 +12,6 @@ export class HtmlResult {
|
|
|
12
12
|
[htmlResultSymbol]: boolean;
|
|
13
13
|
}
|
|
14
14
|
import type { CompiledTemplate } from './create-tag.js';
|
|
15
|
+
import type { HtmlSubstitution } from './html-types.js';
|
|
15
16
|
import type { RenderOptions } from './create-tag.js';
|
|
16
17
|
//# sourceMappingURL=html-result.d.ts.map
|
package/lib/html-result.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"html-result.d.ts","sourceRoot":"","sources":["html-result.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"html-result.d.ts","sourceRoot":"","sources":["html-result.js"],"names":[],"mappings":"AAqCA,oCAHW,OAAO,GACL,KAAK,IAAI,UAAU,CAQ/B;AAxCD,6CAA4D;AAE5D;IAOE,sBALW,gBAAgB,iBAChB,gBAAgB,EAAE,WAClB,aAAa,UACb,CAAC,MAAM,EAAE,UAAU,KAAK,MAAM,EAQxC;IAJC,2BAAwB;IACxB,kCAAkC;IAClC,uBAAsB;IACtB,iBAPkB,UAAU,KAAK,MAAM,CAOnB;IAGtB,mBAEC;IAED,kBAEC;IAED,+BAEC;IAjBC,4BAA6B;CAkBhC;sCA/BoD,iBAAiB;sCAChC,iBAAiB;mCADF,iBAAiB"}
|
package/lib/html-result.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/** @import { CompiledTemplate, RenderOptions } from './create-tag.js' */
|
|
2
|
+
/** @import { HtmlSubstitution } from './html-types.js' */
|
|
2
3
|
|
|
3
4
|
export const htmlResultSymbol = Symbol('fragtml.htmlResult')
|
|
4
5
|
|
|
5
6
|
export class HtmlResult {
|
|
6
7
|
/**
|
|
7
8
|
* @param {CompiledTemplate} compiled
|
|
8
|
-
* @param {
|
|
9
|
+
* @param {HtmlSubstitution[]} substitutions
|
|
9
10
|
* @param {RenderOptions} options
|
|
10
11
|
* @param {(result: HtmlResult) => string} render
|
|
11
12
|
*/
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { FragmentBoundary } from './fragment.js';
|
|
2
|
+
import type { HtmlResult } from './html-result.js';
|
|
3
|
+
import type { RawHtml } from './raw.js';
|
|
4
|
+
export type HtmlPrimitiveSubstitution = string | number | bigint | boolean | null | undefined;
|
|
5
|
+
export type HtmlArrayScalarSubstitution = HtmlPrimitiveSubstitution | HtmlResult | RawHtml;
|
|
6
|
+
export type HtmlArraySubstitution = HtmlArrayScalarSubstitution | readonly HtmlArraySubstitution[];
|
|
7
|
+
export type HtmlSubstitution = HtmlArraySubstitution | FragmentBoundary;
|
|
8
|
+
export type TemplateStrings = TemplateStringsArray | readonly string[];
|
|
9
|
+
//# sourceMappingURL=html-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html-types.d.ts","sourceRoot":"","sources":["html-types.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AACrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;AAEvC,MAAM,MAAM,yBAAyB,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAA;AAC7F,MAAM,MAAM,2BAA2B,GAAG,yBAAyB,GAAG,UAAU,GAAG,OAAO,CAAA;AAC1F,MAAM,MAAM,qBAAqB,GAAG,2BAA2B,GAAG,SAAS,qBAAqB,EAAE,CAAA;AAClG,MAAM,MAAM,gBAAgB,GAAG,qBAAqB,GAAG,gBAAgB,CAAA;AACvE,MAAM,MAAM,eAAe,GAAG,oBAAoB,GAAG,SAAS,MAAM,EAAE,CAAA"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Type exports only. Do not add runtime implementations to this module.
|
|
2
|
+
import type { FragmentBoundary } from './fragment.js'
|
|
3
|
+
import type { HtmlResult } from './html-result.js'
|
|
4
|
+
import type { RawHtml } from './raw.js'
|
|
5
|
+
|
|
6
|
+
export type HtmlPrimitiveSubstitution = string | number | bigint | boolean | null | undefined
|
|
7
|
+
export type HtmlArrayScalarSubstitution = HtmlPrimitiveSubstitution | HtmlResult | RawHtml
|
|
8
|
+
export type HtmlArraySubstitution = HtmlArrayScalarSubstitution | readonly HtmlArraySubstitution[]
|
|
9
|
+
export type HtmlSubstitution = HtmlArraySubstitution | FragmentBoundary
|
|
10
|
+
export type TemplateStrings = TemplateStringsArray | readonly string[]
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fragtml",
|
|
3
3
|
"description": "WIP - nothing to see here",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.3",
|
|
5
5
|
"author": "Bret Comnes <bcomnes@gmail.com> (https://bret.io)",
|
|
6
6
|
"bugs": {
|
|
7
7
|
"url": "https://github.com/bcomnes/fragtml/issues"
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"build:declaration": "tsc -p declaration.tsconfig.json",
|
|
49
49
|
"clean": "run-p clean:*",
|
|
50
50
|
"clean:declarations-top": "rm -rf $(find . -maxdepth 1 -type f -name '*.d.ts*')",
|
|
51
|
-
"clean:declarations-lib": "rm -rf $(find lib -type f -name '*.d.ts*'
|
|
51
|
+
"clean:declarations-lib": "rm -rf $(find lib -type f -name '*.d.ts*')"
|
|
52
52
|
},
|
|
53
53
|
"funding": {
|
|
54
54
|
"type": "individual",
|