tsprose 0.0.1 → 1.0.0
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 +3 -0
- package/dist/document.d.ts +1 -0
- package/dist/document.js +6 -0
- package/dist/element.d.ts +10 -6
- package/dist/elementUtils.js +6 -0
- package/dist/externals.d.ts +57 -57
- package/dist/id.d.ts +7 -2
- package/dist/id.js +28 -8
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/rawToProse.d.ts +4 -3
- package/dist/rawToProse.js +9 -6
- package/dist/storage.d.ts +6 -2
- package/package.json +1 -1
- package/dist/json.d.ts +0 -2
- package/dist/json.js +0 -46
package/README.md
CHANGED
|
@@ -14,6 +14,9 @@ Write and structure prose content using TypeScript TSX with strong typing.
|
|
|
14
14
|
|
|
15
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
16
|
|
|
17
|
+
Think of TSProse as a TeX but written in TypeScript.
|
|
18
|
+
It is a foundation for writing prose. What tags you define and what you do with the prose (render to site or pdf, analyze, serialize, etc) is up to you.
|
|
19
|
+
|
|
17
20
|
## Installation
|
|
18
21
|
|
|
19
22
|
```bash
|
package/dist/document.d.ts
CHANGED
|
@@ -25,4 +25,5 @@ export declare function defineDocument<UniquesTemplate extends DocumentUniquesTe
|
|
|
25
25
|
export declare function defineDocument<UniquesTemplate extends DocumentUniquesTemplate>(documentId: string, documentParameters: {
|
|
26
26
|
uniques: UniquesTemplate;
|
|
27
27
|
}): DocumentFinalizer<UniquesTemplate>;
|
|
28
|
+
export declare function isDocument(document: any): document is Document;
|
|
28
29
|
export declare function injectDocumentId(documentId: string, injectTo: string, defineDocumentAlias?: string): string;
|
package/dist/document.js
CHANGED
|
@@ -42,6 +42,12 @@ export function defineDocument(arg1, arg2) {
|
|
|
42
42
|
return finalizeDocument;
|
|
43
43
|
}
|
|
44
44
|
//
|
|
45
|
+
// isDocument
|
|
46
|
+
//
|
|
47
|
+
export function isDocument(document) {
|
|
48
|
+
return Boolean(document?.[DOCUMENT_PREFIX] === true);
|
|
49
|
+
}
|
|
50
|
+
//
|
|
45
51
|
// injectDocumentId
|
|
46
52
|
//
|
|
47
53
|
export function injectDocumentId(documentId, injectTo, defineDocumentAlias) {
|
package/dist/element.d.ts
CHANGED
|
@@ -11,21 +11,25 @@ export type ToRawElement<TSchema extends Schema, TagName extends string = string
|
|
|
11
11
|
storageKey: TSchema['Storage'] extends undefined ? undefined : string;
|
|
12
12
|
children: ToRawElementChildren<TSchema['Children']>;
|
|
13
13
|
} : never;
|
|
14
|
-
type ToRawElementChildren<TChildren> = TChildren extends
|
|
14
|
+
type ToRawElementChildren<TChildren> = TChildren extends undefined ? undefined : TChildren extends readonly Schema[] ? {
|
|
15
|
+
[K in keyof TChildren]: TChildren[K] extends Schema ? ToRawElement<TChildren[K]> : TChildren[K];
|
|
16
|
+
} : never;
|
|
15
17
|
export type RawElement = ToRawElement<Schema>;
|
|
16
18
|
export type BlockRawElement = ToRawElement<BlockSchema>;
|
|
17
19
|
export type InlinerRawElement = ToRawElement<InlinerSchema>;
|
|
18
20
|
export type LinkableRawElement = ToRawElement<LinkableSchema>;
|
|
19
21
|
export declare const PROSE_ELEMENT_PREFIX = "__TSPROSE_proseElement";
|
|
20
|
-
export type ToProseElement<TSchema extends Schema> = {
|
|
22
|
+
export type ToProseElement<TSchema extends Schema> = TSchema extends Schema ? {
|
|
21
23
|
[PROSE_ELEMENT_PREFIX]: true;
|
|
22
24
|
schema: TSchema;
|
|
23
|
-
id: TSchema['linkable'] extends false ? undefined : string;
|
|
25
|
+
id: false extends TSchema['linkable'] ? TSchema['linkable'] extends false ? undefined : string | undefined : string;
|
|
24
26
|
data: TSchema['Data'];
|
|
25
|
-
storageKey: TSchema['Storage'] extends
|
|
27
|
+
storageKey: false extends TSchema['Storage'] ? TSchema['linkable'] extends false ? undefined : undefined | string : string;
|
|
26
28
|
children: ToProseElementChildren<TSchema['Children']>;
|
|
27
|
-
};
|
|
28
|
-
type ToProseElementChildren<TChildren> = TChildren extends
|
|
29
|
+
} : never;
|
|
30
|
+
type ToProseElementChildren<TChildren> = TChildren extends undefined ? undefined : TChildren extends readonly Schema[] ? {
|
|
31
|
+
[K in keyof TChildren]: TChildren[K] extends Schema ? ToProseElement<TChildren[K]> : TChildren[K];
|
|
32
|
+
} : never;
|
|
29
33
|
export type ProseElement = ToProseElement<Schema>;
|
|
30
34
|
export type BlockProseElement = ToProseElement<BlockSchema>;
|
|
31
35
|
export type InlinerProseElement = ToProseElement<InlinerSchema>;
|
package/dist/elementUtils.js
CHANGED
|
@@ -8,6 +8,9 @@ export function makeRawElement(parameters) {
|
|
|
8
8
|
tagName: parameters.tagName,
|
|
9
9
|
};
|
|
10
10
|
parameters.elementHandler?.(element);
|
|
11
|
+
if (Array.isArray(element.children) && element.children.length === 0) {
|
|
12
|
+
delete element.children;
|
|
13
|
+
}
|
|
11
14
|
element.hash = hash(element.schema.name +
|
|
12
15
|
JSON.stringify(element.data) +
|
|
13
16
|
JSON.stringify(element.children?.map((child) => child.hash).join()), 12);
|
|
@@ -19,6 +22,9 @@ export function makeProseElement(parameters) {
|
|
|
19
22
|
schema: parameters.schema,
|
|
20
23
|
};
|
|
21
24
|
parameters.elementHandler?.(element);
|
|
25
|
+
if (Array.isArray(element.children) && element.children.length === 0) {
|
|
26
|
+
delete element.children;
|
|
27
|
+
}
|
|
22
28
|
return element;
|
|
23
29
|
}
|
|
24
30
|
export function isRawElement(element, schema) {
|
package/dist/externals.d.ts
CHANGED
|
@@ -1,57 +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
|
-
}
|
|
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
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
-
import type { RawElement } from './element.js';
|
|
2
|
-
export type
|
|
1
|
+
import type { ProseElement, RawElement } from './element.js';
|
|
2
|
+
export type TakenIds = Map<string, ProseElement>;
|
|
3
|
+
export type IdMaker = (args: {
|
|
4
|
+
rawElement: RawElement;
|
|
5
|
+
takenIds?: TakenIds;
|
|
6
|
+
slugify?: (str: string) => string;
|
|
7
|
+
}) => string;
|
|
3
8
|
export declare const defaultIdMaker: IdMaker;
|
package/dist/id.js
CHANGED
|
@@ -1,17 +1,37 @@
|
|
|
1
|
-
export const defaultIdMaker = (rawElement, takenIds) => {
|
|
2
|
-
takenIds ||= new Set();
|
|
1
|
+
export const defaultIdMaker = ({ rawElement, takenIds, slugify }) => {
|
|
3
2
|
let id = '';
|
|
4
3
|
const humanReadable = rawElement.uniqueName || rawElement.slug;
|
|
5
4
|
if (humanReadable) {
|
|
6
|
-
id = humanReadable;
|
|
5
|
+
id = slugify ? slugify(humanReadable) : humanReadable;
|
|
7
6
|
}
|
|
8
7
|
else {
|
|
9
8
|
id = rawElement.schema.name + '-' + rawElement.hash.substring(0, 9);
|
|
10
9
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
if (takenIds) {
|
|
11
|
+
if (rawElement.uniqueName) {
|
|
12
|
+
// Unique elements keep their id; colliding elements get deduplicated ids
|
|
13
|
+
if (takenIds.has(id)) {
|
|
14
|
+
const collidingProse = takenIds.get(id);
|
|
15
|
+
takenIds.delete(id);
|
|
16
|
+
const newIdForColliding = collidingProse.schema.name + '-' + collidingProse.id;
|
|
17
|
+
let candidate = newIdForColliding;
|
|
18
|
+
let counter = 1;
|
|
19
|
+
while (takenIds.has(candidate)) {
|
|
20
|
+
candidate = `${newIdForColliding}-${counter++}`;
|
|
21
|
+
}
|
|
22
|
+
collidingProse.id = candidate;
|
|
23
|
+
takenIds.set(candidate, collidingProse);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
// Non-unique elements find the next available id
|
|
28
|
+
let candidate = id;
|
|
29
|
+
let counter = 1;
|
|
30
|
+
while (takenIds.has(candidate)) {
|
|
31
|
+
candidate = `${id}-${counter++}`;
|
|
32
|
+
}
|
|
33
|
+
id = candidate;
|
|
34
|
+
}
|
|
15
35
|
}
|
|
16
|
-
return
|
|
36
|
+
return id;
|
|
17
37
|
};
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/rawToProse.d.ts
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import type { LinkableProseElement, ProseElement, RawElement } from './element.js';
|
|
2
|
-
import { type IdMaker } from './id.js';
|
|
2
|
+
import { type IdMaker, type TakenIds } from './id.js';
|
|
3
3
|
export type RawToProseStep = (elements: {
|
|
4
4
|
rawElement: RawElement;
|
|
5
5
|
proseElement: ProseElement;
|
|
6
6
|
}) => void | Promise<void>;
|
|
7
7
|
export interface RawToProseResult {
|
|
8
8
|
prose: ProseElement;
|
|
9
|
-
takenIds:
|
|
9
|
+
takenIds: TakenIds;
|
|
10
10
|
uniques: Record<string, LinkableProseElement>;
|
|
11
11
|
}
|
|
12
12
|
export declare function rawToProse(args: {
|
|
13
13
|
rawProse: RawElement;
|
|
14
|
-
takenIds?:
|
|
14
|
+
takenIds?: TakenIds;
|
|
15
15
|
pre?: (rawElement: RawElement) => void | Promise<void>;
|
|
16
16
|
post?: (proseElement: ProseElement) => void | Promise<void>;
|
|
17
17
|
step?: RawToProseStep;
|
|
18
18
|
idMaker?: IdMaker;
|
|
19
|
+
slugify?: (str: string) => string;
|
|
19
20
|
}): Promise<RawToProseResult>;
|
package/dist/rawToProse.js
CHANGED
|
@@ -3,9 +3,9 @@ import { TSProseError } from './error.js';
|
|
|
3
3
|
import { defaultIdMaker } from './id.js';
|
|
4
4
|
import { isWalkStop, walkPost } from './walk.js';
|
|
5
5
|
export async function rawToProse(args) {
|
|
6
|
-
const { rawProse, pre, post, step } = args;
|
|
6
|
+
const { rawProse, pre, post, step, slugify } = args;
|
|
7
7
|
const idMaker = args.idMaker || defaultIdMaker;
|
|
8
|
-
const takenIds = new
|
|
8
|
+
const takenIds = new Map(args.takenIds);
|
|
9
9
|
const uniques = {};
|
|
10
10
|
const prose = await walkPost(rawProse, async (rawElement, children) => {
|
|
11
11
|
if (pre) {
|
|
@@ -20,19 +20,22 @@ export async function rawToProse(args) {
|
|
|
20
20
|
if (rawElement.storageKey) {
|
|
21
21
|
proseElement.storageKey = rawElement.storageKey;
|
|
22
22
|
}
|
|
23
|
-
if (children) {
|
|
23
|
+
if (children && children.length > 0) {
|
|
24
24
|
proseElement.children = children;
|
|
25
25
|
}
|
|
26
26
|
if (rawElement.schema.linkable) {
|
|
27
|
-
const elementId = idMaker(rawElement, takenIds);
|
|
27
|
+
const elementId = idMaker({ rawElement, takenIds, slugify });
|
|
28
|
+
if (!elementId.trim()) {
|
|
29
|
+
throw new TSProseError(`Empty ID generated from "${rawElement.schema.name}" element!\nThis might happen because of wrongly configured "idMaker" or obscure element data that was passed to create an ID!`);
|
|
30
|
+
}
|
|
28
31
|
if (takenIds.has(elementId)) {
|
|
29
32
|
throw new TSProseError(`Element ID collision: "${elementId}" is already taken!\nMake sure "idMaker" you are using generates non-repeating IDs!`);
|
|
30
33
|
}
|
|
31
34
|
proseElement.id = elementId;
|
|
32
|
-
takenIds.
|
|
35
|
+
takenIds.set(elementId, proseElement);
|
|
33
36
|
if (rawElement.uniqueName) {
|
|
34
37
|
if (uniques[rawElement.uniqueName]) {
|
|
35
|
-
throw new TSProseError(`Duplicate uniqueName: "${rawElement.uniqueName}" is already used by another element!\nIf you are using document prose, make sure not to directly insert imported or manually created external uniques as
|
|
38
|
+
throw new TSProseError(`Duplicate uniqueName: "${rawElement.uniqueName}" is already used by another element!\nIf you are using document prose, make sure not to directly insert imported or manually created external uniques as their names might intersect with document-level uniques names!`);
|
|
36
39
|
}
|
|
37
40
|
uniques[rawElement.uniqueName] = proseElement;
|
|
38
41
|
}
|
package/dist/storage.d.ts
CHANGED
|
@@ -5,10 +5,13 @@ export interface ProseWithStorage {
|
|
|
5
5
|
prose: ProseElement;
|
|
6
6
|
storage: ProseStorage;
|
|
7
7
|
}
|
|
8
|
-
|
|
8
|
+
interface _StorageCreatorMethod<TSchema extends Schema> {
|
|
9
|
+
creator(element: ToProseElement<TSchema>): null | TSchema['Storage'] | Promise<null | TSchema['Storage']>;
|
|
10
|
+
}
|
|
11
|
+
export type ElementStorageCreator<TSchema extends Schema> = TSchema['Storage'] extends undefined ? never : _StorageCreatorMethod<TSchema>['creator'];
|
|
9
12
|
export declare function fillProseStorage(args: {
|
|
10
13
|
prose: ProseElement;
|
|
11
|
-
storageCreators: Record<string, ElementStorageCreator<
|
|
14
|
+
storageCreators: Record<string, ElementStorageCreator<Schema>>;
|
|
12
15
|
alterValue?: (args: {
|
|
13
16
|
element: ToProseElement<Schema>;
|
|
14
17
|
storageKey: string;
|
|
@@ -16,3 +19,4 @@ export declare function fillProseStorage(args: {
|
|
|
16
19
|
}) => any | Promise<any>;
|
|
17
20
|
storage?: ProseStorage;
|
|
18
21
|
}): Promise<ProseStorage>;
|
|
22
|
+
export {};
|
package/package.json
CHANGED
package/dist/json.d.ts
DELETED
package/dist/json.js
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { PROSE_ELEMENT_PREFIX, RAW_ELEMENT_PREFIX } from './element.js';
|
|
2
|
-
import { SCHEMA_PREFIX } from './schema.js';
|
|
3
|
-
function encodeSchema(schema) {
|
|
4
|
-
return (schema.name +
|
|
5
|
-
(schema.type === 'block' ? '0' : '1') +
|
|
6
|
-
(schema.linkable === false ? '0' : schema.linkable === 'always' ? '2' : '1'));
|
|
7
|
-
}
|
|
8
|
-
function decodeSchema(encoded) {
|
|
9
|
-
const name = encoded.slice(0, -2);
|
|
10
|
-
const typeFlag = encoded.at(-2);
|
|
11
|
-
const linkFlag = encoded.at(-1);
|
|
12
|
-
return {
|
|
13
|
-
[SCHEMA_PREFIX]: true,
|
|
14
|
-
name,
|
|
15
|
-
type: typeFlag === '0' ? 'block' : 'inliner',
|
|
16
|
-
linkable: linkFlag === '0' ? false : linkFlag === '2' ? 'always' : true,
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
function isElement(obj) {
|
|
20
|
-
return obj[RAW_ELEMENT_PREFIX] === true || obj[PROSE_ELEMENT_PREFIX] === true;
|
|
21
|
-
}
|
|
22
|
-
export function toJSON(value, indent) {
|
|
23
|
-
return JSON.stringify(value, function (key, val) {
|
|
24
|
-
if (!isElement(this))
|
|
25
|
-
return val;
|
|
26
|
-
if (key === RAW_ELEMENT_PREFIX || key === PROSE_ELEMENT_PREFIX) {
|
|
27
|
-
const schema = this.schema;
|
|
28
|
-
return encodeSchema(schema);
|
|
29
|
-
}
|
|
30
|
-
if (key === 'schema') {
|
|
31
|
-
return undefined;
|
|
32
|
-
}
|
|
33
|
-
return val;
|
|
34
|
-
}, indent);
|
|
35
|
-
}
|
|
36
|
-
export function fromJSON(json) {
|
|
37
|
-
return JSON.parse(json, function (key, val) {
|
|
38
|
-
if ((key === RAW_ELEMENT_PREFIX || key === PROSE_ELEMENT_PREFIX) &&
|
|
39
|
-
typeof val === 'string') {
|
|
40
|
-
const schema = decodeSchema(val);
|
|
41
|
-
this.schema = schema;
|
|
42
|
-
return true;
|
|
43
|
-
}
|
|
44
|
-
return val;
|
|
45
|
-
});
|
|
46
|
-
}
|