Browse Source

Improve node ID generation

master
buttercat1791 1 year ago
parent
commit
a19dc335db
  1. 251
      src/lib/parser.ts

251
src/lib/parser.ts

@ -57,7 +57,7 @@ export default class Pharos {
private ndk: NDK; private ndk: NDK;
private blockCounter: number = 0; private contextCounters: Map<string, number> = new Map<string, number>();
/** /**
* The HTML content of the converted document. * The HTML content of the converted document.
@ -69,11 +69,6 @@ export default class Pharos {
*/ */
private rootNodeId?: string; private rootNodeId?: string;
/**
* The ID of the root index event generated for the document.
*/
private rootIndexId?: string;
/** /**
* Metadata to be used to populate the tags on the root index event. * Metadata to be used to populate the tags on the root index event.
*/ */
@ -84,6 +79,11 @@ export default class Pharos {
*/ */
private nodes: Map<string, AbstractNode> = new Map<string, AbstractNode>(); private nodes: Map<string, AbstractNode> = new Map<string, AbstractNode>();
/**
* A map of event d tags to the events themselves.
*/
private events: Map<string, NDKEvent> = new Map<string, NDKEvent>();
/** /**
* A map of node IDs to the integer event kind that will be used to represent the node. * A map of node IDs to the integer event kind that will be used to represent the node.
*/ */
@ -123,9 +123,25 @@ export default class Pharos {
this.html = this.asciidoctor.convert(content, options) as string | Document | undefined; this.html = this.asciidoctor.convert(content, options) as string | Document | undefined;
} }
getEvents(pubkey: string): NDKEvent[] { /**
* Generates and stores Nostr events from the parsed AsciiDoc document. The events can be
* modified via the parser's API and retrieved via the `getEvents()` method.
* @param pubkey The public key (as a hex string) of the user that will sign and publish the
* events.
*/
generate(pubkey: string): void {
const stack = this.stackEventNodes(); const stack = this.stackEventNodes();
return this.generateEvents(stack, pubkey); this.generateEvents(stack, pubkey);
}
/**
* @param pubkey The public key (as a hex string) of the user generating the events.
* @returns An array of Nostr events generated from the parsed AsciiDoc document.
* @remarks This method returns the events as they are currently stored in the parser. If none
* are stored, they will be freshly generated.
*/
getEvents(pubkey: string): NDKEvent[] {
return Array.from(this.events.values());
} }
/** /**
@ -141,7 +157,7 @@ export default class Pharos {
* @remarks The root index ID may be used to retrieve metadata or children from the root index. * @remarks The root index ID may be used to retrieve metadata or children from the root index.
*/ */
getRootIndexId(): string { getRootIndexId(): string {
return this.rootNodeId!; return this.normalizeDTag(this.rootNodeId ?? '');
} }
/** /**
@ -198,10 +214,9 @@ export default class Pharos {
* Resets the parser to its initial state, removing any parsed data. * Resets the parser to its initial state, removing any parsed data.
*/ */
reset(): void { reset(): void {
this.blockCounter = 0; this.contextCounters.clear();
this.html = undefined; this.html = undefined;
this.rootNodeId = undefined; this.rootNodeId = undefined;
this.rootIndexId = undefined;
this.rootIndexMetadata = {}; this.rootIndexMetadata = {};
this.nodes.clear(); this.nodes.clear();
this.eventToKindMap.clear(); this.eventToKindMap.clear();
@ -220,12 +235,7 @@ export default class Pharos {
* - 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.rootNodeId = document.getId(); this.rootNodeId = this.generateNodeId(document);
if (!this.rootNodeId) {
this.rootNodeId = this.normalizeNodeId(document.getTitle() ?? 'root');
document.setId(this.rootNodeId);
}
this.nodes.set(this.rootNodeId, document); this.nodes.set(this.rootNodeId, document);
this.eventToKindMap.set(this.rootNodeId, 30040); this.eventToKindMap.set(this.rootNodeId, 30040);
this.indexToChildEventsMap.set(this.rootNodeId, new Set<string>()); this.indexToChildEventsMap.set(this.rootNodeId, new Set<string>());
@ -258,7 +268,7 @@ export default class Pharos {
private processSection(section: Section): AbstractNode[] { private processSection(section: Section): AbstractNode[] {
let sectionId = section.getId(); let sectionId = section.getId();
if (!sectionId) { if (!sectionId) {
sectionId = this.normalizeNodeId(section.getTitle() ?? `${section.getContext()}_${this.blockCounter++}`); sectionId = this.generateNodeId(section);
} }
// Prevent duplicates. // Prevent duplicates.
@ -295,7 +305,7 @@ export default class Pharos {
// Obtain or generate a unique ID for the block. // Obtain or generate a unique ID for the block.
let blockId = block.getId(); let blockId = block.getId();
if (!blockId) { if (!blockId) {
blockId = `${this.normalizeNodeId(block.getContext())}_${this.blockCounter++}`; blockId = this.generateNodeId(block) ;
block.setId(blockId); block.setId(blockId);
} }
@ -348,7 +358,7 @@ export default class Pharos {
* Generates Nostr events for each node in the given stack. * Generates Nostr events for each node in the given stack.
* @param nodeIdStack An array of node IDs ordered such that processing them in LIFO order will * @param nodeIdStack An array of node IDs ordered such that processing them in LIFO order will
* produce any child event before it is required by a parent index event. * produce any child event before it is required by a parent index event.
* @param pubkey The public key (not encoded in npub form) of the user generating the events. * @param pubkey The public key (as a hex string) of the user generating the events.
* @returns An array of Nostr events. * @returns An array of Nostr events.
*/ */
private generateEvents(nodeIdStack: string[], pubkey: string): NDKEvent[] { private generateEvents(nodeIdStack: string[], pubkey: string): NDKEvent[] {
@ -436,10 +446,7 @@ export default class Pharos {
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. this.events.set(nodeId, event);
if (nodeId === this.rootNodeId) {
this.rootIndexId = eventId;
}
return event; return event;
} }
@ -472,6 +479,8 @@ export default class Pharos {
this.eventIds.set(nodeId, eventId); this.eventIds.set(nodeId, eventId);
event.id = eventId; event.id = eventId;
this.events.set(nodeId, event);
return event; return event;
} }
@ -479,6 +488,198 @@ export default class Pharos {
// #region Utility Functions // #region Utility Functions
private generateNodeId(block: AbstractBlock): string {
let blockId: string = block.getId();
if (blockId != null && blockId.length > 0) {
return blockId;
}
blockId = this.normalizeNodeId(block.getTitle() ?? '');
// Use the provided title, if possible.
if (blockId != null && blockId.length > 0) {
return this.normalizeNodeId(blockId);
}
const documentId = this.rootNodeId;
let blockNumber: number;
const context = block.getContext();
switch (context) {
case 'admonition':
blockNumber = this.contextCounters.get('admonition') ?? 0;
blockId = `${documentId}_admonition_${blockNumber++}`;
this.contextCounters.set('admonition', blockNumber);
break;
case 'audio':
blockNumber = this.contextCounters.get('audio') ?? 0;
blockId = `${documentId}_audio_${blockNumber++}`;
this.contextCounters.set('audio', blockNumber);
break;
case 'colist':
blockNumber = this.contextCounters.get('colist') ?? 0;
blockId = `${documentId}_colist_${blockNumber++}`;
this.contextCounters.set('colist', blockNumber);
break;
case 'dlist':
blockNumber = this.contextCounters.get('dlist') ?? 0;
blockId = `${documentId}_dlist_${blockNumber++}`;
this.contextCounters.set('dlist', blockNumber);
break;
case 'document':
blockNumber = this.contextCounters.get('document') ?? 0;
blockId = `${documentId}_document_${blockNumber++}`;
this.contextCounters.set('document', blockNumber);
break;
case 'example':
blockNumber = this.contextCounters.get('example') ?? 0;
blockId = `${documentId}_example_${blockNumber++}`;
this.contextCounters.set('example', blockNumber);
break;
case 'floating_title':
blockNumber = this.contextCounters.get('floating_title') ?? 0;
blockId = `${documentId}_floating_title_${blockNumber++}`;
this.contextCounters.set('floating_title', blockNumber);
break;
case 'image':
blockNumber = this.contextCounters.get('image') ?? 0;
blockId = `${documentId}_image_${blockNumber++}`;
this.contextCounters.set('image', blockNumber);
break;
case 'list_item':
blockNumber = this.contextCounters.get('list_item') ?? 0;
blockId = `${documentId}_list_item_${blockNumber++}`;
this.contextCounters.set('list_item', blockNumber);
break;
case 'listing':
blockNumber = this.contextCounters.get('listing') ?? 0;
blockId = `${documentId}_listing_${blockNumber++}`;
this.contextCounters.set('listing', blockNumber);
break;
case 'literal':
blockNumber = this.contextCounters.get('literal') ?? 0;
blockId = `${documentId}_literal_${blockNumber++}`;
this.contextCounters.set('literal', blockNumber);
break;
case 'olist':
blockNumber = this.contextCounters.get('olist') ?? 0;
blockId = `${documentId}_olist_${blockNumber++}`;
this.contextCounters.set('olist', blockNumber);
break;
case 'open':
blockNumber = this.contextCounters.get('open') ?? 0;
blockId = `${documentId}_open_${blockNumber++}`;
this.contextCounters.set('open', blockNumber);
break;
case 'page_break':
blockNumber = this.contextCounters.get('page_break') ?? 0;
blockId = `${documentId}_page_break_${blockNumber++}`;
this.contextCounters.set('page_break', blockNumber);
break;
case 'paragraph':
blockNumber = this.contextCounters.get('paragraph') ?? 0;
blockId = `${documentId}_paragraph_${blockNumber++}`;
this.contextCounters.set('paragraph', blockNumber);
break;
case 'pass':
blockNumber = this.contextCounters.get('pass') ?? 0;
blockId = `${documentId}_pass_${blockNumber++}`;
this.contextCounters.set('pass', blockNumber);
break;
case 'preamble':
blockNumber = this.contextCounters.get('preamble') ?? 0;
blockId = `${documentId}_preamble_${blockNumber++}`;
this.contextCounters.set('preamble', blockNumber);
break;
case 'quote':
blockNumber = this.contextCounters.get('quote') ?? 0;
blockId = `${documentId}_quote_${blockNumber++}`;
this.contextCounters.set('quote', blockNumber);
break;
case 'section':
blockNumber = this.contextCounters.get('section') ?? 0;
blockId = `${documentId}_section_${blockNumber++}`;
this.contextCounters.set('section', blockNumber);
break;
case 'sidebar':
blockNumber = this.contextCounters.get('sidebar') ?? 0;
blockId = `${documentId}_sidebar_${blockNumber++}`;
this.contextCounters.set('sidebar', blockNumber);
break;
case 'table':
blockNumber = this.contextCounters.get('table') ?? 0;
blockId = `${documentId}_table_${blockNumber++}`;
this.contextCounters.set('table', blockNumber);
break;
case 'table_cell':
blockNumber = this.contextCounters.get('table_cell') ?? 0;
blockId = `${documentId}_table_cell_${blockNumber++}`;
this.contextCounters.set('table_cell', blockNumber);
break;
case 'thematic_break':
blockNumber = this.contextCounters.get('thematic_break') ?? 0;
blockId = `${documentId}_thematic_break_${blockNumber++}`;
this.contextCounters.set('thematic_break', blockNumber);
break;
case 'toc':
blockNumber = this.contextCounters.get('toc') ?? 0;
blockId = `${documentId}_toc_${blockNumber++}`;
this.contextCounters.set('toc', blockNumber);
break;
case 'ulist':
blockNumber = this.contextCounters.get('ulist') ?? 0;
blockId = `${documentId}_ulist_${blockNumber++}`;
this.contextCounters.set('ulist', blockNumber);
break;
case 'verse':
blockNumber = this.contextCounters.get('verse') ?? 0;
blockId = `${documentId}_verse_${blockNumber++}`;
this.contextCounters.set('verse', blockNumber);
break;
case 'video':
blockNumber = this.contextCounters.get('video') ?? 0;
blockId = `${documentId}_video_${blockNumber++}`;
this.contextCounters.set('video', blockNumber);
break;
default:
blockNumber = this.contextCounters.get('block') ?? 0;
blockId = `${documentId}_block_${blockNumber++}`;
this.contextCounters.set('block', blockNumber);
break;
}
block.setId(blockId);
return blockId;
}
private normalizeNodeId(input: string): string { private normalizeNodeId(input: string): string {
return input return input
.toLowerCase() .toLowerCase()
@ -494,7 +695,7 @@ export default class Pharos {
private normalizeDTag(input: string): string { private normalizeDTag(input: string): string {
return input return input
.toLowerCase() .toLowerCase()
.replace(/\s+/g, '-') // Replace spaces with hyphens. .replace(/[\s_]+/g, '-') // Replace spaces with hyphens.
.replace(/[^a-z0-9\-]/g, ''); // Remove non-alphanumeric characters except hyphens. .replace(/[^a-z0-9\-]/g, ''); // Remove non-alphanumeric characters except hyphens.
} }

Loading…
Cancel
Save