tsprose 0.0.1
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/LICENSE +21 -0
- package/README.md +152 -0
- package/dist/children.d.ts +3 -0
- package/dist/children.js +86 -0
- package/dist/default/mix.d.ts +10 -0
- package/dist/default/mix.js +6 -0
- package/dist/default/text.d.ts +10 -0
- package/dist/default/text.js +6 -0
- package/dist/document.d.ts +28 -0
- package/dist/document.js +61 -0
- package/dist/element.d.ts +33 -0
- package/dist/element.js +8 -0
- package/dist/elementUtils.d.ts +33 -0
- package/dist/elementUtils.js +92 -0
- package/dist/error.d.ts +3 -0
- package/dist/error.js +6 -0
- package/dist/externals.d.ts +57 -0
- package/dist/id.d.ts +3 -0
- package/dist/id.js +17 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +17 -0
- package/dist/json.d.ts +2 -0
- package/dist/json.js +46 -0
- package/dist/jsx-dev-runtime.d.ts +1 -0
- package/dist/jsx-dev-runtime.js +1 -0
- package/dist/jsx-runtime.d.ts +25 -0
- package/dist/jsx-runtime.js +24 -0
- package/dist/rawToProse.d.ts +19 -0
- package/dist/rawToProse.js +59 -0
- package/dist/schema.d.ts +25 -0
- package/dist/schema.js +7 -0
- package/dist/storage.d.ts +18 -0
- package/dist/storage.js +30 -0
- package/dist/tag.d.ts +45 -0
- package/dist/tag.js +53 -0
- package/dist/tagUtils.d.ts +12 -0
- package/dist/tagUtils.js +62 -0
- package/dist/unique.d.ts +25 -0
- package/dist/unique.js +81 -0
- package/dist/utils/hash.d.ts +1 -0
- package/dist/utils/hash.js +13 -0
- package/dist/walk.d.ts +13 -0
- package/dist/walk.js +72 -0
- package/package.json +49 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026-present Gwynerva
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
<img src="./.github/assets/banner.svg" alt="TSProse banner">
|
|
2
|
+
|
|
3
|
+
<p></p>
|
|
4
|
+
|
|
5
|
+
# TSProse
|
|
6
|
+
|
|
7
|
+
Write and structure prose content using TypeScript TSX with strong typing.
|
|
8
|
+
|
|
9
|
+
- Write prose using **TSX** syntax
|
|
10
|
+
- Define **your own** element schemas and tags
|
|
11
|
+
- Link any elements with **"uniques"** system (e.g. for cross-referencing)
|
|
12
|
+
- Convert raw TSX output into a stable, linkable prose tree
|
|
13
|
+
- **Storage** system for attaching async/heavy/reusable data to elements
|
|
14
|
+
|
|
15
|
+
TSProse is a **parser + structurer**: you write content in TSX, TSProse turns it into a typed element tree. Rendering, styling, and analysis are up to you.
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install tsprose
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Configure your `tsconfig.json` to use TSProse as the JSX runtime:
|
|
24
|
+
|
|
25
|
+
```jsonc
|
|
26
|
+
{
|
|
27
|
+
"compilerOptions": {
|
|
28
|
+
"jsx": "react-jsx",
|
|
29
|
+
"jsxImportSource": "tsprose",
|
|
30
|
+
},
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Core idea
|
|
35
|
+
|
|
36
|
+
The idea is simple — you define schemas and tags, use tags to write raw prose in TSX, convert raw prose to prose with stable IDs and collected uniques, then do whatever you want with the prose tree (render, analyze, serialize, etc):
|
|
37
|
+
|
|
38
|
+
1. **Schema** — Describes an element’s shape and rules (block vs inliner, linkable or not, typed data/children).
|
|
39
|
+
2. **Tag (TSX)** — The callable you use in TSX files; it creates/configures **raw elements** according to the schema.
|
|
40
|
+
3. **RawElement** — The initial, unprocessed elements produced by evaluating your TSX.
|
|
41
|
+
4. **ProseElement** (via **`rawToProse`** function) — raw elements are transformed into prose elements via `rawToProse()` which assigns stable IDs to linkable elements and collects uniques.
|
|
42
|
+
5. (optional) **Storage** — Attach async/heavy/reusable data to elements (e.g. computed metadata) when needed.
|
|
43
|
+
6. **Use the prose tree** — Render, analyze, serialize, etc.
|
|
44
|
+
|
|
45
|
+
## Get started (minimal example)
|
|
46
|
+
|
|
47
|
+
This example defines two elements:
|
|
48
|
+
|
|
49
|
+
- `paragraph` (block, linkable, optional data)
|
|
50
|
+
- `bold` (inliner, non-linkable)
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
import {
|
|
54
|
+
defineSchema,
|
|
55
|
+
defineTag,
|
|
56
|
+
defineDocument,
|
|
57
|
+
rawToProse,
|
|
58
|
+
ensureTagInlinerChildren,
|
|
59
|
+
ensureTagChildren,
|
|
60
|
+
type BlockSchema,
|
|
61
|
+
type InlinerSchema,
|
|
62
|
+
type TextSchema,
|
|
63
|
+
} from 'tsprose';
|
|
64
|
+
|
|
65
|
+
//
|
|
66
|
+
// 1. Schemas
|
|
67
|
+
//
|
|
68
|
+
|
|
69
|
+
interface ParagraphSchema extends BlockSchema {
|
|
70
|
+
name: 'paragraph';
|
|
71
|
+
type: 'block';
|
|
72
|
+
linkable: true;
|
|
73
|
+
Data: { center?: boolean } | undefined;
|
|
74
|
+
Storage: undefined;
|
|
75
|
+
Children: InlinerSchema[];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const paragraphSchema = defineSchema<ParagraphSchema>({
|
|
79
|
+
name: 'paragraph',
|
|
80
|
+
type: 'block',
|
|
81
|
+
linkable: true,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
interface BoldSchema extends InlinerSchema {
|
|
85
|
+
name: 'bold';
|
|
86
|
+
type: 'inliner';
|
|
87
|
+
linkable: false;
|
|
88
|
+
Data: undefined;
|
|
89
|
+
Storage: undefined;
|
|
90
|
+
Children: TextSchema[];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const boldSchema = defineSchema<BoldSchema>({
|
|
94
|
+
name: 'bold',
|
|
95
|
+
type: 'inliner',
|
|
96
|
+
linkable: false,
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
//
|
|
100
|
+
// 2. Tags
|
|
101
|
+
//
|
|
102
|
+
|
|
103
|
+
const P = defineTag({ tagName: 'P', schema: paragraphSchema })<{ center?: true }>(
|
|
104
|
+
({ tagName, props, children, element }) => {
|
|
105
|
+
ensureTagInlinerChildren(tagName, children);
|
|
106
|
+
element.children = children;
|
|
107
|
+
|
|
108
|
+
if (props.center) {
|
|
109
|
+
element.data = { center: true };
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
const B = defineTag({ tagName: 'B', schema: boldSchema })(({ tagName, children, element }) => {
|
|
115
|
+
// `text` is built-in; strings become text elements automatically.
|
|
116
|
+
ensureTagInlinerChildren(tagName, children);
|
|
117
|
+
element.children = children;
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
//
|
|
121
|
+
// 3. Document (TSX)
|
|
122
|
+
//
|
|
123
|
+
|
|
124
|
+
const document = defineDocument('my-document-id', {
|
|
125
|
+
uniques: { intro: P },
|
|
126
|
+
})(({ uniques }) => (
|
|
127
|
+
<>
|
|
128
|
+
<P $={uniques.intro} center>
|
|
129
|
+
Hello <B>world</B>
|
|
130
|
+
</P>
|
|
131
|
+
</>
|
|
132
|
+
));
|
|
133
|
+
|
|
134
|
+
//
|
|
135
|
+
// 4. Convert to prose (stable IDs + collected uniques)
|
|
136
|
+
//
|
|
137
|
+
|
|
138
|
+
const { prose, uniques } = await rawToProse({ rawProse: document.rawProse });
|
|
139
|
+
|
|
140
|
+
console.log(prose.schema.name); // "mix" (fragment)
|
|
141
|
+
console.log(uniques.intro.id); // "intro"
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Next steps
|
|
145
|
+
|
|
146
|
+
- Walk the tree (`walkPre*` / `walkPost*`) to render/analyze
|
|
147
|
+
- Serialize (`toJSON` / `fromJSON`) if you need persistence
|
|
148
|
+
- Attach async data via storage (`fillProseStorage`) when you need it
|
|
149
|
+
|
|
150
|
+
## License
|
|
151
|
+
|
|
152
|
+
[MIT](./LICENSE)
|
package/dist/children.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { mixSchema } from './default/mix.js';
|
|
2
|
+
import { textSchema } from './default/text.js';
|
|
3
|
+
import { isRawElement, makeRawElement } from './elementUtils.js';
|
|
4
|
+
import { isUnique } from './unique.js';
|
|
5
|
+
import { hash } from './utils/hash.js';
|
|
6
|
+
export function normalizeChildren(children, childStep) {
|
|
7
|
+
if (children === undefined) {
|
|
8
|
+
return undefined;
|
|
9
|
+
}
|
|
10
|
+
const childrenArray = Array.isArray(children) ? children : [children];
|
|
11
|
+
if (childrenArray.length === 0) {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
const normalizedChildren = [];
|
|
15
|
+
for (const child of childrenArray) {
|
|
16
|
+
//
|
|
17
|
+
// <Mix>
|
|
18
|
+
//
|
|
19
|
+
if (isRawElement(child, mixSchema)) {
|
|
20
|
+
const subNormalized = normalizeChildren(child.children, childStep);
|
|
21
|
+
if (subNormalized) {
|
|
22
|
+
normalizedChildren.push(...subNormalized);
|
|
23
|
+
}
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
//
|
|
27
|
+
// Raw Element
|
|
28
|
+
//
|
|
29
|
+
if (isRawElement(child)) {
|
|
30
|
+
const clone = structuredClone(child);
|
|
31
|
+
childStep?.(clone);
|
|
32
|
+
normalizedChildren.push(clone);
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
//
|
|
36
|
+
// Unique
|
|
37
|
+
//
|
|
38
|
+
if (isUnique(child)) {
|
|
39
|
+
const clone = structuredClone(child.rawElement);
|
|
40
|
+
clone.slug = clone.uniqueName;
|
|
41
|
+
delete clone.uniqueName;
|
|
42
|
+
childStep?.(clone);
|
|
43
|
+
normalizedChildren.push(clone);
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
//
|
|
47
|
+
// Array
|
|
48
|
+
//
|
|
49
|
+
if (Array.isArray(child)) {
|
|
50
|
+
const subNormalized = normalizeChildren(child, childStep);
|
|
51
|
+
if (subNormalized) {
|
|
52
|
+
normalizedChildren.push(...subNormalized);
|
|
53
|
+
}
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
//
|
|
57
|
+
// Skip falsy values (null, false, empty string, etc.) but not 0
|
|
58
|
+
//
|
|
59
|
+
if (child === undefined ||
|
|
60
|
+
child === null ||
|
|
61
|
+
child === false ||
|
|
62
|
+
child === '') {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
//
|
|
66
|
+
// Something else. Just stringify and maybe merge with previous text node if possible.
|
|
67
|
+
//
|
|
68
|
+
const strChild = String(child);
|
|
69
|
+
const textRawElement = makeRawElement({
|
|
70
|
+
schema: textSchema,
|
|
71
|
+
elementHandler: (element) => {
|
|
72
|
+
element.data = strChild;
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
childStep?.(textRawElement);
|
|
76
|
+
const lastNormalized = normalizedChildren.at(-1);
|
|
77
|
+
if (isRawElement(lastNormalized, textSchema)) {
|
|
78
|
+
lastNormalized.data += strChild;
|
|
79
|
+
lastNormalized.hash = hash(lastNormalized.data, 12);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
normalizedChildren.push(textRawElement);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return normalizedChildren;
|
|
86
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { RawElement } from './element.js';
|
|
2
|
+
import type { LinkableTag } from './tag.js';
|
|
3
|
+
import { type AutoUnique, type ToUnique } from './unique.js';
|
|
4
|
+
export declare const DOCUMENT_PREFIX = "__TSPROSE_document";
|
|
5
|
+
export declare const DOCUMENT_AUTO_ID = "__TSPROSE_documentAutoId";
|
|
6
|
+
export interface Document<UniquesTemplate extends DocumentUniquesTemplate = DocumentUniquesTemplate> {
|
|
7
|
+
[DOCUMENT_PREFIX]: true;
|
|
8
|
+
documentId: string;
|
|
9
|
+
uniques: DocumentUniques<UniquesTemplate>;
|
|
10
|
+
rawProse: RawElement;
|
|
11
|
+
}
|
|
12
|
+
export type DocumentUniquesTemplate<Template extends Record<string, LinkableTag> = Record<string, LinkableTag>> = Template;
|
|
13
|
+
export type DocumentUniques<UniquesTemplate extends DocumentUniquesTemplate = DocumentUniquesTemplate> = {
|
|
14
|
+
[UniqueName in keyof UniquesTemplate]: ToUnique<UniquesTemplate[UniqueName]>;
|
|
15
|
+
};
|
|
16
|
+
export type DocumentContentCreator<UniquesTemplate extends DocumentUniquesTemplate = DocumentUniquesTemplate> = (context: {
|
|
17
|
+
uniques: DocumentUniques<UniquesTemplate>;
|
|
18
|
+
autoUnique: () => AutoUnique;
|
|
19
|
+
}) => RawElement;
|
|
20
|
+
export type DocumentFinalizer<UniquesTemplate extends DocumentUniquesTemplate = DocumentUniquesTemplate> = (contentCreator: DocumentContentCreator<UniquesTemplate>) => Document<UniquesTemplate>;
|
|
21
|
+
export declare function defineDocument(): DocumentFinalizer<{}>;
|
|
22
|
+
export declare function defineDocument<UniquesTemplate extends DocumentUniquesTemplate>(documentParameters: {
|
|
23
|
+
uniques: UniquesTemplate;
|
|
24
|
+
}): DocumentFinalizer<UniquesTemplate>;
|
|
25
|
+
export declare function defineDocument<UniquesTemplate extends DocumentUniquesTemplate>(documentId: string, documentParameters: {
|
|
26
|
+
uniques: UniquesTemplate;
|
|
27
|
+
}): DocumentFinalizer<UniquesTemplate>;
|
|
28
|
+
export declare function injectDocumentId(documentId: string, injectTo: string, defineDocumentAlias?: string): string;
|
package/dist/document.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { TSProseError } from './error.js';
|
|
2
|
+
import { defineAutoUnique, defineUnique, } from './unique.js';
|
|
3
|
+
export const DOCUMENT_PREFIX = '__TSPROSE_document';
|
|
4
|
+
export const DOCUMENT_AUTO_ID = '__TSPROSE_documentAutoId';
|
|
5
|
+
export function defineDocument(arg1, arg2) {
|
|
6
|
+
const documentId = typeof arg1 === 'string' ? arg1 : DOCUMENT_AUTO_ID;
|
|
7
|
+
const uniquesTemplate = typeof arg1 === 'object' && 'uniques' in arg1
|
|
8
|
+
? arg1.uniques
|
|
9
|
+
: arg2 && 'uniques' in arg2
|
|
10
|
+
? arg2.uniques
|
|
11
|
+
: {};
|
|
12
|
+
const uniques = Object.fromEntries(Object.entries(uniquesTemplate).map(([key, value]) => [
|
|
13
|
+
key,
|
|
14
|
+
defineUnique({
|
|
15
|
+
documentId,
|
|
16
|
+
name: key,
|
|
17
|
+
tag: value,
|
|
18
|
+
}),
|
|
19
|
+
]));
|
|
20
|
+
let autoUniquesCounter = 1;
|
|
21
|
+
const autoUnique = () => defineAutoUnique({
|
|
22
|
+
documentId,
|
|
23
|
+
name: `auto-unique-${autoUniquesCounter++}`,
|
|
24
|
+
});
|
|
25
|
+
function finalizeDocument(contentCreator) {
|
|
26
|
+
const rawProse = contentCreator({ uniques, autoUnique });
|
|
27
|
+
for (const unique of Object.values(uniques)) {
|
|
28
|
+
try {
|
|
29
|
+
unique.rawElement;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
throw new TSProseError(`Unique "${unique.name}" for <${unique.tag.tagName}> was not used in the document content!\nAll uniques defined in the document must be used in the content creator function!`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
[DOCUMENT_PREFIX]: true,
|
|
37
|
+
documentId,
|
|
38
|
+
uniques,
|
|
39
|
+
rawProse,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
return finalizeDocument;
|
|
43
|
+
}
|
|
44
|
+
//
|
|
45
|
+
// injectDocumentId
|
|
46
|
+
//
|
|
47
|
+
export function injectDocumentId(documentId, injectTo, defineDocumentAlias) {
|
|
48
|
+
const funcName = defineDocumentAlias ?? 'defineDocument';
|
|
49
|
+
const escapedFuncName = funcName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
50
|
+
const escapedId = documentId.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
|
|
51
|
+
const regex = new RegExp(`\\b${escapedFuncName}\\((\\s*)(?!['"\`])`, 'g');
|
|
52
|
+
return injectTo.replace(regex, (match, whitespace, offset) => {
|
|
53
|
+
const afterChar = injectTo[offset + match.length];
|
|
54
|
+
if (afterChar === ')' || afterChar === undefined) {
|
|
55
|
+
// defineDocument() → defineDocument('id')
|
|
56
|
+
return `${funcName}(${whitespace}'${escapedId}'`;
|
|
57
|
+
}
|
|
58
|
+
// defineDocument({ → defineDocument('id', {
|
|
59
|
+
return `${funcName}('${escapedId}',${whitespace}`;
|
|
60
|
+
});
|
|
61
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { BlockSchema, InlinerSchema, LinkableSchema, Schema } from './schema.js';
|
|
2
|
+
export declare const RAW_ELEMENT_PREFIX = "__TSPROSE_rawElement";
|
|
3
|
+
export type ToRawElement<TSchema extends Schema, TagName extends string = string> = TSchema extends Schema ? {
|
|
4
|
+
[RAW_ELEMENT_PREFIX]: true;
|
|
5
|
+
schema: TSchema;
|
|
6
|
+
hash: string;
|
|
7
|
+
tagName?: TagName;
|
|
8
|
+
slug?: string;
|
|
9
|
+
uniqueName?: string;
|
|
10
|
+
data: TSchema['Data'];
|
|
11
|
+
storageKey: TSchema['Storage'] extends undefined ? undefined : string;
|
|
12
|
+
children: ToRawElementChildren<TSchema['Children']>;
|
|
13
|
+
} : never;
|
|
14
|
+
type ToRawElementChildren<TChildren> = TChildren extends (infer E extends Schema)[] ? ToRawElement<E>[] : TChildren extends undefined ? undefined : never;
|
|
15
|
+
export type RawElement = ToRawElement<Schema>;
|
|
16
|
+
export type BlockRawElement = ToRawElement<BlockSchema>;
|
|
17
|
+
export type InlinerRawElement = ToRawElement<InlinerSchema>;
|
|
18
|
+
export type LinkableRawElement = ToRawElement<LinkableSchema>;
|
|
19
|
+
export declare const PROSE_ELEMENT_PREFIX = "__TSPROSE_proseElement";
|
|
20
|
+
export type ToProseElement<TSchema extends Schema> = {
|
|
21
|
+
[PROSE_ELEMENT_PREFIX]: true;
|
|
22
|
+
schema: TSchema;
|
|
23
|
+
id: TSchema['linkable'] extends false ? undefined : string;
|
|
24
|
+
data: TSchema['Data'];
|
|
25
|
+
storageKey: TSchema['Storage'] extends undefined ? undefined : string;
|
|
26
|
+
children: ToProseElementChildren<TSchema['Children']>;
|
|
27
|
+
};
|
|
28
|
+
type ToProseElementChildren<TChildren> = TChildren extends (infer E extends Schema)[] ? ToProseElement<E>[] : TChildren extends undefined ? undefined : never;
|
|
29
|
+
export type ProseElement = ToProseElement<Schema>;
|
|
30
|
+
export type BlockProseElement = ToProseElement<BlockSchema>;
|
|
31
|
+
export type InlinerProseElement = ToProseElement<InlinerSchema>;
|
|
32
|
+
export type LinkableProseElement = ToProseElement<LinkableSchema>;
|
|
33
|
+
export {};
|
package/dist/element.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type BlockProseElement, type BlockRawElement, type InlinerProseElement, type InlinerRawElement, type ToProseElement, type ToRawElement } from './element.js';
|
|
2
|
+
import type { Schema } from './schema.js';
|
|
3
|
+
export declare function makeRawElement<TSchema extends Schema, ElementHandler extends (element: ToRawElement<TSchema, TagName>) => void, TagName extends string>(parameters: {
|
|
4
|
+
schema: TSchema;
|
|
5
|
+
tagName?: TagName;
|
|
6
|
+
elementHandler?: ElementHandler;
|
|
7
|
+
}): ToRawElement<TSchema, TagName>;
|
|
8
|
+
export declare function makeProseElement<TSchema extends Schema, ElementHandler extends (element: ToProseElement<TSchema>) => void>(parameters: {
|
|
9
|
+
schema: TSchema;
|
|
10
|
+
elementHandler?: ElementHandler;
|
|
11
|
+
}): ToProseElement<TSchema>;
|
|
12
|
+
export declare function isRawElement<TSchema extends Schema>(element: any, schema?: TSchema): element is ToRawElement<TSchema>;
|
|
13
|
+
export declare function isProseElement<TSchema extends Schema>(element: any, schema?: TSchema): element is ToProseElement<TSchema>;
|
|
14
|
+
export declare function ensureRawElement<TSchema extends Schema>(element: any, schema?: TSchema): asserts element is ToRawElement<TSchema>;
|
|
15
|
+
export declare function ensureProseElement<TSchema extends Schema>(element: any, schema?: TSchema): asserts element is ToProseElement<TSchema>;
|
|
16
|
+
export declare function isRawBlock(element: any): element is BlockRawElement;
|
|
17
|
+
export declare function isRawInliner(element: any): element is InlinerRawElement;
|
|
18
|
+
export declare function isProseBlock(element: any): element is BlockProseElement;
|
|
19
|
+
export declare function isProseInliner(element: any): element is InlinerProseElement;
|
|
20
|
+
export declare function ensureRawBlock(element: any): asserts element is BlockRawElement;
|
|
21
|
+
export declare function ensureRawInliner(element: any): asserts element is InlinerRawElement;
|
|
22
|
+
export declare function ensureProseBlock(element: any): asserts element is BlockProseElement;
|
|
23
|
+
export declare function ensureProseInliner(element: any): asserts element is InlinerProseElement;
|
|
24
|
+
export declare function hasChildren<T>(element: {
|
|
25
|
+
children?: T[];
|
|
26
|
+
}): element is {
|
|
27
|
+
children: [T, ...T[]];
|
|
28
|
+
};
|
|
29
|
+
export declare function ensureChildren<T>(element: {
|
|
30
|
+
children?: T[];
|
|
31
|
+
}): asserts element is {
|
|
32
|
+
children: [T, ...T[]];
|
|
33
|
+
};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { PROSE_ELEMENT_PREFIX, RAW_ELEMENT_PREFIX, } from './element.js';
|
|
2
|
+
import { TSProseError } from './error.js';
|
|
3
|
+
import { hash } from './utils/hash.js';
|
|
4
|
+
export function makeRawElement(parameters) {
|
|
5
|
+
const element = {
|
|
6
|
+
[RAW_ELEMENT_PREFIX]: true,
|
|
7
|
+
schema: parameters.schema,
|
|
8
|
+
tagName: parameters.tagName,
|
|
9
|
+
};
|
|
10
|
+
parameters.elementHandler?.(element);
|
|
11
|
+
element.hash = hash(element.schema.name +
|
|
12
|
+
JSON.stringify(element.data) +
|
|
13
|
+
JSON.stringify(element.children?.map((child) => child.hash).join()), 12);
|
|
14
|
+
return element;
|
|
15
|
+
}
|
|
16
|
+
export function makeProseElement(parameters) {
|
|
17
|
+
const element = {
|
|
18
|
+
[PROSE_ELEMENT_PREFIX]: true,
|
|
19
|
+
schema: parameters.schema,
|
|
20
|
+
};
|
|
21
|
+
parameters.elementHandler?.(element);
|
|
22
|
+
return element;
|
|
23
|
+
}
|
|
24
|
+
export function isRawElement(element, schema) {
|
|
25
|
+
return isElement(RAW_ELEMENT_PREFIX, element, schema);
|
|
26
|
+
}
|
|
27
|
+
export function isProseElement(element, schema) {
|
|
28
|
+
return isElement(PROSE_ELEMENT_PREFIX, element, schema);
|
|
29
|
+
}
|
|
30
|
+
function isElement(prefix, element, schema) {
|
|
31
|
+
if (element?.[prefix] === true) {
|
|
32
|
+
if (schema) {
|
|
33
|
+
return schema.name === element?.schema?.name;
|
|
34
|
+
}
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
export function ensureRawElement(element, schema) {
|
|
40
|
+
ensureElement(RAW_ELEMENT_PREFIX, element, schema);
|
|
41
|
+
}
|
|
42
|
+
export function ensureProseElement(element, schema) {
|
|
43
|
+
ensureElement(PROSE_ELEMENT_PREFIX, element, schema);
|
|
44
|
+
}
|
|
45
|
+
function ensureElement(prefix, element, schema) {
|
|
46
|
+
if (!isElement(prefix, element, schema)) {
|
|
47
|
+
throw new TSProseError(`Not a ${prefix === RAW_ELEMENT_PREFIX ? 'RawElement' : 'ProseElement'}${schema ? ` with "${schema.name}" schema` : ''}!\nProvided:${JSON.stringify(element)}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export function isRawBlock(element) {
|
|
51
|
+
return isType('raw', 'block', element);
|
|
52
|
+
}
|
|
53
|
+
export function isRawInliner(element) {
|
|
54
|
+
return isType('raw', 'inliner', element);
|
|
55
|
+
}
|
|
56
|
+
export function isProseBlock(element) {
|
|
57
|
+
return isType('prose', 'block', element);
|
|
58
|
+
}
|
|
59
|
+
export function isProseInliner(element) {
|
|
60
|
+
return isType('prose', 'inliner', element);
|
|
61
|
+
}
|
|
62
|
+
function isType(elementType, type, element) {
|
|
63
|
+
if (elementType === 'raw' ? isRawElement(element) : isProseElement(element)) {
|
|
64
|
+
return element.schema.type === type;
|
|
65
|
+
}
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
export function ensureRawBlock(element) {
|
|
69
|
+
ensureType('raw', 'block', element);
|
|
70
|
+
}
|
|
71
|
+
export function ensureRawInliner(element) {
|
|
72
|
+
ensureType('raw', 'inliner', element);
|
|
73
|
+
}
|
|
74
|
+
export function ensureProseBlock(element) {
|
|
75
|
+
ensureType('prose', 'block', element);
|
|
76
|
+
}
|
|
77
|
+
export function ensureProseInliner(element) {
|
|
78
|
+
ensureType('prose', 'inliner', element);
|
|
79
|
+
}
|
|
80
|
+
function ensureType(elementType, type, element) {
|
|
81
|
+
if (!isType(elementType, type, element)) {
|
|
82
|
+
throw new TSProseError(`Not a ${elementType} ${type}!\nProvided:${JSON.stringify(element)}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
export function hasChildren(element) {
|
|
86
|
+
return element.children !== undefined && element.children.length > 0;
|
|
87
|
+
}
|
|
88
|
+
export function ensureChildren(element) {
|
|
89
|
+
if (!hasChildren(element)) {
|
|
90
|
+
throw new TSProseError('Provided element has no children!');
|
|
91
|
+
}
|
|
92
|
+
}
|
package/dist/error.d.ts
ADDED
package/dist/error.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Images
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
declare module '*.png' {
|
|
6
|
+
const src: string;
|
|
7
|
+
export default src;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
declare module '*.jpg' {
|
|
11
|
+
const src: string;
|
|
12
|
+
export default src;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
declare module '*.jpeg' {
|
|
16
|
+
const src: string;
|
|
17
|
+
export default src;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
declare module '*.gif' {
|
|
21
|
+
const src: string;
|
|
22
|
+
export default src;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
declare module '*.bmp' {
|
|
26
|
+
const src: string;
|
|
27
|
+
export default src;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
declare module '*.webp' {
|
|
31
|
+
const src: string;
|
|
32
|
+
export default src;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
declare module '*.svg' {
|
|
36
|
+
const src: string;
|
|
37
|
+
export default src;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
//
|
|
41
|
+
// Videos
|
|
42
|
+
//
|
|
43
|
+
|
|
44
|
+
declare module '*.mp4' {
|
|
45
|
+
const src: string;
|
|
46
|
+
export default src;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
declare module '*.webm' {
|
|
50
|
+
const src: string;
|
|
51
|
+
export default src;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
declare module '*.ogg' {
|
|
55
|
+
const src: string;
|
|
56
|
+
export default src;
|
|
57
|
+
}
|
package/dist/id.d.ts
ADDED
package/dist/id.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const defaultIdMaker = (rawElement, takenIds) => {
|
|
2
|
+
takenIds ||= new Set();
|
|
3
|
+
let id = '';
|
|
4
|
+
const humanReadable = rawElement.uniqueName || rawElement.slug;
|
|
5
|
+
if (humanReadable) {
|
|
6
|
+
id = humanReadable;
|
|
7
|
+
}
|
|
8
|
+
else {
|
|
9
|
+
id = rawElement.schema.name + '-' + rawElement.hash.substring(0, 9);
|
|
10
|
+
}
|
|
11
|
+
let candidate = id;
|
|
12
|
+
let counter = 1;
|
|
13
|
+
while (takenIds.has(candidate)) {
|
|
14
|
+
candidate = `${id}-${counter++}`;
|
|
15
|
+
}
|
|
16
|
+
return candidate;
|
|
17
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export * from './default/mix.js';
|
|
2
|
+
export * from './default/text.js';
|
|
3
|
+
export * from './utils/hash.js';
|
|
4
|
+
export * from './children.js';
|
|
5
|
+
export * from './document.js';
|
|
6
|
+
export * from './element.js';
|
|
7
|
+
export * from './elementUtils.js';
|
|
8
|
+
export * from './error.js';
|
|
9
|
+
export * from './id.js';
|
|
10
|
+
export * from './json.js';
|
|
11
|
+
export * from './rawToProse.js';
|
|
12
|
+
export * from './schema.js';
|
|
13
|
+
export * from './storage.js';
|
|
14
|
+
export * from './tag.js';
|
|
15
|
+
export * from './tagUtils.js';
|
|
16
|
+
export * from './unique.js';
|
|
17
|
+
export * from './walk.js';
|