Neues bare-Attr am transclusion-Node; $-Präfix in Markdown-Regel, Serializer und Autocomplete; HTML-Placeholder trägt data-transclusion-bare, Server-Expansion und NodeView lassen bei bare Rahmen und Titel weg. Gleiche Tiefen-/Zyklen-/Permission-Regeln, zählt weiter als Link. Unit- und DB-Tests ergänzt. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0155v2aT8AG1kZDQEZiCLBWC
92 lines
3.5 KiB
TypeScript
92 lines
3.5 KiB
TypeScript
import { Node } from 'prosemirror-model';
|
|
import { describe, expect, it } from 'vitest';
|
|
|
|
import { docToHtml } from './html';
|
|
import { docToMarkdown, markdownToDoc } from './markdown';
|
|
import { extractWikilinkSlugs } from './wikilinks';
|
|
|
|
function firstTransclusion(doc: Node): Node | null {
|
|
let found: Node | null = null;
|
|
doc.descendants((node) => {
|
|
if (!found && node.type.name === 'transclusion') found = node;
|
|
});
|
|
return found;
|
|
}
|
|
|
|
describe('page embed / transclusion (issue #135)', () => {
|
|
it('parses a lone ![[slug]] line to a transclusion block and round-trips', () => {
|
|
const doc = markdownToDoc('Intro\n\n![[rennrad]]\n\nOutro');
|
|
const node = firstTransclusion(doc);
|
|
expect(node).not.toBeNull();
|
|
expect(node!.attrs.targetSlug).toBe('rennrad');
|
|
expect(node!.attrs.displayText).toBeNull();
|
|
expect(docToMarkdown(doc)).toContain('![[rennrad]]');
|
|
});
|
|
|
|
it('supports optional display text and round-trips it', () => {
|
|
const doc = markdownToDoc('![[rennrad|Mein Rad]]');
|
|
const node = firstTransclusion(doc);
|
|
expect(node!.attrs.targetSlug).toBe('rennrad');
|
|
expect(node!.attrs.displayText).toBe('Mein Rad');
|
|
expect(docToMarkdown(doc).trim()).toBe('![[rennrad|Mein Rad]]');
|
|
});
|
|
|
|
it('renders a placeholder div carrying the slug', () => {
|
|
const doc = markdownToDoc('![[rennrad]]');
|
|
const html = docToHtml(doc);
|
|
expect(html).toContain('class="dt-transclusion"');
|
|
expect(html).toContain('data-transclusion="rennrad"');
|
|
});
|
|
|
|
it('registers the embed target as an outgoing link (backlinks/graph)', () => {
|
|
const doc = markdownToDoc('![[rennrad]]\n\n[[dota]]');
|
|
expect(extractWikilinkSlugs(doc)).toEqual(['rennrad', 'dota']);
|
|
});
|
|
|
|
it('only embeds a lone line — mid-paragraph ![[x]] is not a transclusion', () => {
|
|
const doc = markdownToDoc('see ![[rennrad]] inline');
|
|
expect(firstTransclusion(doc)).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('bare page embed `$[[…]]` (issue #146)', () => {
|
|
it('parses a lone $[[slug]] line to a bare transclusion and round-trips', () => {
|
|
const doc = markdownToDoc('Intro\n\n$[[rennrad]]\n\nOutro');
|
|
const node = firstTransclusion(doc);
|
|
expect(node).not.toBeNull();
|
|
expect(node!.attrs.targetSlug).toBe('rennrad');
|
|
expect(node!.attrs.bare).toBe(true);
|
|
expect(docToMarkdown(doc)).toContain('$[[rennrad]]');
|
|
});
|
|
|
|
it('keeps `![[…]]` non-bare and round-trips both prefixes side by side', () => {
|
|
const doc = markdownToDoc('![[dota]]\n\n$[[rennrad]]');
|
|
const markdown = docToMarkdown(doc);
|
|
expect(markdown).toContain('![[dota]]');
|
|
expect(markdown).toContain('$[[rennrad]]');
|
|
let bares = 0;
|
|
doc.descendants((node) => {
|
|
if (node.type.name === 'transclusion' && node.attrs.bare) bares += 1;
|
|
});
|
|
expect(bares).toBe(1);
|
|
});
|
|
|
|
it('marks the placeholder div so the server expansion can drop the frame', () => {
|
|
const html = docToHtml(markdownToDoc('$[[rennrad]]'));
|
|
expect(html).toContain('data-transclusion="rennrad"');
|
|
expect(html).toContain('data-transclusion-bare="1"');
|
|
// The framed variant must NOT carry the marker.
|
|
expect(docToHtml(markdownToDoc('![[rennrad]]'))).not.toContain('data-transclusion-bare');
|
|
});
|
|
|
|
it('registers the bare embed target as an outgoing link too', () => {
|
|
const doc = markdownToDoc('$[[rennrad]]');
|
|
expect(extractWikilinkSlugs(doc)).toEqual(['rennrad']);
|
|
});
|
|
|
|
it('does not treat mid-paragraph $[[x]] as a transclusion', () => {
|
|
const doc = markdownToDoc('costs $[[rennrad]] inline');
|
|
expect(firstTransclusion(doc)).toBeNull();
|
|
});
|
|
});
|