Browse Source

Update variable names and parse document metadata

master
buttercat1791 2 years ago
parent
commit
b04e73ca34
  1. 129
      src/lib/parser.ts

129
src/lib/parser.ts

@ -10,6 +10,17 @@ import {
type ProcessorOptions type ProcessorOptions
} from 'asciidoctor'; } from 'asciidoctor';
interface IndexMetadata {
authors?: string[];
version?: string;
edition?: string;
isbn?: string;
publicationDate?: string;
publisher?: string;
summary?: string;
coverImage?: string;
}
/** /**
* @classdesc Pharos is an extension of the Asciidoctor class that adds Nostr Knowledge Base (NKB) * @classdesc Pharos is an extension of the Asciidoctor class that adds Nostr Knowledge Base (NKB)
* features to core Asciidoctor functionality. Asciidoctor is used to parse an AsciiDoc document * features to core Asciidoctor functionality. Asciidoctor is used to parse an AsciiDoc document
@ -49,7 +60,14 @@ export default class Pharos extends Asciidoctor {
/** /**
* The ID of the root node in the document. * The ID of the root node in the document.
*/ */
private rootId?: string; private rootNodeId?: string;
/**
* The ID of the root index event generated for the document.
*/
private rootIndexId?: string;
private rootIndexMetadata: IndexMetadata = {};
/** /**
* A map of node IDs to the nodes themselves. * A map of node IDs to the nodes themselves.
@ -64,7 +82,7 @@ export default class Pharos extends Asciidoctor {
/** /**
* A map of index IDs to the IDs of the nodes they reference. * A map of index IDs to the IDs of the nodes they reference.
*/ */
private indexToChildrenMap: Map<string, Set<string>> = new Map<string, Set<string>>(); private indexToChildEventsMap: Map<string, Set<string>> = new Map<string, Set<string>>();
/** /**
* A map of node IDs to the Nostr event IDs of the events they generate. * A map of node IDs to the Nostr event IDs of the events they generate.
@ -94,7 +112,7 @@ export default class Pharos extends Asciidoctor {
} }
getEvents(pubkey: string): NDKEvent[] { getEvents(pubkey: string): NDKEvent[] {
const stack = this.stackEventNodes(this.rootId!); const stack = this.stackEventNodes();
return this.generateEvents(stack, pubkey); return this.generateEvents(stack, pubkey);
} }
@ -111,23 +129,23 @@ export default class Pharos extends Asciidoctor {
* - Each ID of a node containing children is mapped to the set of IDs of its children. * - Each ID of a node containing children is mapped to the set of IDs of its children.
*/ */
private treeProcessor(treeProcessor: Extensions.TreeProcessor, document: Document) { private treeProcessor(treeProcessor: Extensions.TreeProcessor, document: Document) {
this.rootId = document.getId(); this.rootNodeId = document.getId();
this.nodes.set(this.rootId, document); this.nodes.set(this.rootNodeId, document);
this.eventToKindMap.set(this.rootId, 30040); this.eventToKindMap.set(this.rootNodeId, 30040);
this.indexToChildrenMap.set(this.rootId, new Set<string>()); this.indexToChildEventsMap.set(this.rootNodeId, new Set<string>());
/** FIFO queue (uses `Array.push()` and `Array.shift()`). */ /** FIFO queue (uses `Array.push()` and `Array.shift()`). */
const queue: AbstractNode[] = document.getBlocks(); const nodeQueue: AbstractNode[] = document.getBlocks();
while (queue.length > 0) { while (nodeQueue.length > 0) {
const block = queue.shift(); const block = nodeQueue.shift();
if (!block) { if (!block) {
continue; continue;
} }
if (block instanceof Section) { if (block instanceof Section) {
const children = this.processSection(block); const children = this.processSection(block);
queue.push(...children); nodeQueue.push(...children);
} else { } else {
this.processBlock(block as Block); this.processBlock(block as Block);
} }
@ -139,19 +157,19 @@ export default class Pharos extends Asciidoctor {
* @param section The section to process. * @param section The section to process.
* @returns An array of the section's child nodes. If there are no child nodes, returns an empty * @returns An array of the section's child nodes. If there are no child nodes, returns an empty
* array. * array.
* @remarks Sections are mapped as kind 30040 indexToChildrenMap by default. * @remarks Sections are mapped as kind 30040 indexToChildEventsMap by default.
*/ */
private processSection(section: Section): AbstractNode[] { private processSection(section: Section): AbstractNode[] {
const id = section.getId(); const sectionId = section.getId();
// Prevent duplicates. // Prevent duplicates.
if (this.nodes.has(id)) { if (this.nodes.has(sectionId)) {
return []; return [];
} }
this.nodes.set(id, section); this.nodes.set(sectionId, section);
this.eventToKindMap.set(id, 30040); // Sections are indexToChildrenMap by default. this.eventToKindMap.set(sectionId, 30040); // Sections are indexToChildEventsMap by default.
this.indexToChildrenMap.set(id, new Set<string>()); this.indexToChildEventsMap.set(sectionId, new Set<string>());
const parentId = section.getParent()?.getId(); const parentId = section.getParent()?.getId();
if (!parentId) { if (!parentId) {
@ -159,7 +177,7 @@ export default class Pharos extends Asciidoctor {
} }
// Add the section to its parent index. // Add the section to its parent index.
this.indexToChildrenMap.get(parentId)?.add(id); this.indexToChildEventsMap.get(parentId)?.add(sectionId);
// Limit to 5 levels of section depth. // Limit to 5 levels of section depth.
if (section.getLevel() >= 5) { if (section.getLevel() >= 5) {
@ -175,15 +193,15 @@ export default class Pharos extends Asciidoctor {
* @remarks Blocks are mapped as kind 30041 zettels by default. * @remarks Blocks are mapped as kind 30041 zettels by default.
*/ */
private processBlock(block: Block): void { private processBlock(block: Block): void {
const id = block.getId(); const blockId = block.getId();
// Prevent duplicates. // Prevent duplicates.
if (this.nodes.has(id)) { if (this.nodes.has(blockId)) {
return; return;
} }
this.nodes.set(id, block); this.nodes.set(blockId, block);
this.eventToKindMap.set(id, 30041); // Blocks are zettels by default. this.eventToKindMap.set(blockId, 30041); // Blocks are zettels by default.
const parentId = block.getParent()?.getId(); const parentId = block.getParent()?.getId();
if (!parentId) { if (!parentId) {
@ -191,7 +209,7 @@ export default class Pharos extends Asciidoctor {
} }
// Add the block to its parent index. // Add the block to its parent index.
this.indexToChildrenMap.get(parentId)?.add(id); this.indexToChildEventsMap.get(parentId)?.add(blockId);
} }
//#endregion //#endregion
@ -201,26 +219,25 @@ export default class Pharos extends Asciidoctor {
/** /**
* Generates a stack of node IDs such that processing them in LIFO order will generate any events * Generates a stack of node IDs such that processing them in LIFO order will generate any events
* used by an index before generating that index itself. * used by an index before generating that index itself.
* @param rootNodeId The ID of the node from which to start processing.
* @returns An array of node IDs in the order they should be processed to generate events. * @returns An array of node IDs in the order they should be processed to generate events.
*/ */
private stackEventNodes(rootNodeId: string): string[] { private stackEventNodes(): string[] {
const traversalStack: string[] = [this.rootId!]; const tempNodeIdStack: string[] = [this.rootNodeId!];
const eventStack: string[] = []; const nodeIdStack: string[] = [];
while (traversalStack.length > 0) { while (tempNodeIdStack.length > 0) {
const parentId = traversalStack.pop()!; const parentId = tempNodeIdStack.pop()!;
eventStack.push(parentId); nodeIdStack.push(parentId);
if (!this.indexToChildrenMap.has(parentId)) { if (!this.indexToChildEventsMap.has(parentId)) {
continue; continue;
} }
const childIds = Array.from(this.indexToChildrenMap.get(parentId)!); const childIds = Array.from(this.indexToChildEventsMap.get(parentId)!);
traversalStack.push(...childIds); tempNodeIdStack.push(...childIds);
} }
return eventStack; return nodeIdStack;
} }
/** /**
@ -235,7 +252,6 @@ export default class Pharos extends Asciidoctor {
while (nodeIdStack.length > 0) { while (nodeIdStack.length > 0) {
const nodeId = nodeIdStack.pop(); const nodeId = nodeIdStack.pop();
let event: NDKEvent;
switch (this.eventToKindMap.get(nodeId!)) { switch (this.eventToKindMap.get(nodeId!)) {
case 30040: case 30040:
@ -244,7 +260,7 @@ export default class Pharos extends Asciidoctor {
case 30041: case 30041:
default: default:
// Kind 30041 is currently the default contentful kind. // Kind 30041 (zettel) is currently the default kind for contentful events.
events.push(this.generateZettelEvent(nodeId!, pubkey)); events.push(this.generateZettelEvent(nodeId!, pubkey));
break; break;
} }
@ -263,7 +279,7 @@ export default class Pharos extends Asciidoctor {
*/ */
private generateIndexEvent(nodeId: string, pubkey: string): NDKEvent { private generateIndexEvent(nodeId: string, pubkey: string): NDKEvent {
const title = (this.nodes.get(nodeId)! as AbstractBlock).getTitle(); const title = (this.nodes.get(nodeId)! as AbstractBlock).getTitle();
const childTags = Array.from(this.indexToChildrenMap.get(nodeId)!) const childTags = Array.from(this.indexToChildEventsMap.get(nodeId)!)
.map(id => ['#e', this.eventIds.get(id)!]); .map(id => ['#e', this.eventIds.get(id)!]);
const event = new NDKEvent(this.ndk); const event = new NDKEvent(this.ndk);
@ -277,11 +293,50 @@ export default class Pharos extends Asciidoctor {
event.created_at = Date.now(); event.created_at = Date.now();
event.pubkey = pubkey; event.pubkey = pubkey;
// Add optional metadata to the root index event.
if (nodeId === this.rootNodeId) {
const document = this.nodes.get(nodeId) as Document;
// Store the metadata so it is available if we need it later.
this.rootIndexMetadata = {
authors: document
.getAuthors()
.map(author => author.getName())
.filter(name => name != null),
version: document.getRevisionNumber(),
edition: document.getRevisionRemark(),
publicationDate: document.getRevisionDate(),
};
if (this.rootIndexMetadata.authors) {
event.tags.push(['author', ...this.rootIndexMetadata.authors!]);
}
if (this.rootIndexMetadata.version || this.rootIndexMetadata.edition) {
event.tags.push(
[
'version',
this.rootIndexMetadata.version!,
this.rootIndexMetadata.edition!
].filter(value => value != null)
);
}
if (this.rootIndexMetadata.publicationDate) {
event.tags.push(['published_on', this.rootIndexMetadata.publicationDate!]);
}
}
// Event ID generation must be the last step. // Event ID generation must be the last step.
const eventId = event.getEventHash(); const eventId = event.getEventHash();
this.eventIds.set(nodeId, eventId); this.eventIds.set(nodeId, eventId);
event.id = eventId; event.id = eventId;
// Store the root index ID in case we need it later.
if (nodeId === this.rootNodeId) {
this.rootIndexId = eventId;
}
return event; return event;
} }

Loading…
Cancel
Save