Skip to content

Generate: Quads to MD-LD

Fresh

The generate() function converts RDF quads back to deterministic, human-readable MD-LD text. It is the second half of the round-trip: parse() turns text into quads, generate() turns quads back into text.

Basic Usage

javascript
import { generate } from 'mdld-parse';

const { text, context, compactStats } = generate({
  quads,
  context: { ex: 'http://example.org/' }
});

console.log(text);
// [ex] <http://example.org/>
//
// # article {=ex:article .ex:Article}
// ...

Parameters

ParameterTypeDefaultDescription
quadsarrayrequiredRDF/JS Quads to convert
contextobject{}Prefix mappings
primarySubjectstringnullIRI to place first in output
compactInlinebooleanfalseEnable inline type/label compaction
renderReversebooleanfalseRender reverse connections as !p annotations
removearray[]Quads to retract (for diff generation)
langstringnullPreferred language for labels ('en', 'es', etc.)

Returns

javascript
{
  text:         string,  // Generated MD-LD markdown
  context:      object,  // Final context with prefixes
  compactStats: {
    compactedSubjects:  number, // Subjects compacted with inline types/labels
    skippedHeadings:    number, // Headings skipped due to compaction
    inlineAnnotations:  number  // Inline type/label annotations rendered
  }
}

Round-Trip Safety

Generate is designed for deterministic output. The same quads always produce the same text:

javascript
const { text: first } = generate({ quads, context });
const { text: second } = generate({ quads, context });

console.log(first === second); // always true

The round-trip is always safe:

javascript
const result = parse({ text: originalText });
const { text: regenerated } = generate({
  quads: result.quads,
  context: result.context
});

// regenerated is semantically equivalent to originalText
// (same quads when re-parsed, though formatting may differ)

Primary Subject Positioning

Use primarySubject to control which node appears first in output:

javascript
const { text } = generate({
  quads,
  context,
  primarySubject: 'http://example.org/main-entity'
});

When renderReverse: true is also set, the primary subject's incoming connections are rendered as !p annotations.

Inline Compaction

When compactInline: true, subjects that have only a type and label (and appear as objects elsewhere) can be rendered inline rather than getting their own heading section:

javascript
// Without compaction (default):
// # alice {=ex:alice .prov:Person label}
// [Alice] {ex:name}
// ...
// [alice] {+ex:alice ?ex:author}

// With compactInline: true, if alice has only type+label:
// [Alice] {+ex:alice .prov:Person label ?ex:author}

Language Preference

The lang parameter controls which language variant of labels and comments is preferred:

javascript
const { text } = generate({
  quads,
  lang: 'es'  // Prefer Spanish labels
});

Priority order: specified language, untagged literal, English, any available language.

Diff Generation

Pass remove alongside quads to generate a diff document that shows both additions and retractions:

javascript
import { generate, DataFactory } from 'mdld-parse';

const { namedNode, literal } = DataFactory;

const quads = [
  DataFactory.quad(
    namedNode('http://example.org/doc'),
    namedNode('http://example.org/author'),
    literal('Alice')
  )
];

const remove = [
  DataFactory.quad(
    namedNode('http://example.org/doc'),
    namedNode('http://example.org/author'),
    literal('Smith')
  )
];

const { text } = generate({
  quads,
  remove,
  context: { ex: 'http://example.org/' }
});

console.log(text);
// [ex] <http://example.org/>
//
// # doc {=ex:doc}
// [Alice] {ex:author}
// [Smith] {-ex:author}

Retractions appear as -predicate annotations. The diff document can be later parsed and merged to apply the changes.

Reverse Connections

When renderReverse: true, nodes that are objects of relationships get !predicate annotations showing what points to them:

javascript
const { text } = generate({
  quads,
  context,
  renderReverse: true
});

This is most useful in combination with primarySubject to render a node-centric view that includes all its incoming connections.

generateNode

For a node-centric view focused on a specific IRI:

javascript
import { generateNode } from 'mdld-parse';

const { text } = generateNode({
  quads,
  focusIRI: 'http://example.org/alice',
  context,
  compactInline: true,    // default: true
  renderReverse: true,    // default: true
  lang: 'en'
});

Safety: returns empty string if focusIRI is not found in the quads — prevents accidental full-database rendering.

Integration with Parse

Full round-trip workflow:

javascript
import { parse, generate } from 'mdld-parse';

// Step 1: Parse document
const parseResult = parse({ text: mdldDocument });

// Step 2: Transform quads (filter, augment, etc.)
const filteredQuads = parseResult.quads.filter(q =>
  q.subject.value.startsWith('http://example.org/person/')
);

// Step 3: Generate new document from transformed quads
const { text } = generate({
  quads: filteredQuads,
  context: parseResult.context
});

CRDT-Style Workflow

Generate diffs between document versions:

javascript
import { parse, generate, merge } from 'mdld-parse';

const v1 = parse({ text: version1 });
const v2 = parse({ text: version2 });

// Generate a diff document showing what changed
const { text: diffDoc } = generate({
  quads: v2.quads,
  remove: v2.remove,
  context: v2.context
});

// Later: merge v1 with the diff
const final = merge([version1, diffDoc]);
// final.quads contains the updated state