remark-speech
v0.10.0
Published
Remark plugin to add direct speech with metadata for speaker element in markdown
Maintainers
Readme
remark-speech
remark-speech is a remark plugin that adds direct-speech syntax with speaker metadata to Markdown. It includes parsing and serialization support so speech blocks can be read from and written back to Markdown consistently.
Sample
npm install remark-speech remark-parse remark-stringify unifiedimport { unified } from "unified";
import remarkParse from "remark-parse";
import remarkStringify from "remark-stringify";
import { remarkDirectSpeech } from "remark-speech";
const input = `
[[Fay]]"Es ist *Zeit*", säuselte eine helle Stimme, um sich
selbst dazu zu bringen, sich zu rühren.
`;
const file = await unified()
.use(remarkParse)
.use(remarkDirectSpeech)
.use(remarkStringify)
.process(input);
console.log(String(file));Input and output keep the same direct-speech block syntax, while the plugin adds AST support for working with speaker metadata.
Block speech
The above is used for "normal" direct speech that is used in most texts. The plugin also supports block speech, which is used for longer speeches or monologues. These can contain multiple paragraphs and generally all block elements. The syntax for block speech is similar to the block quote syntax and is as follows:
Dann holte er aus und begann zu sprechen:
| [[Johnathan]]
| Veränderung ist wie der Wind: Manchmal spürt man sie kaum, und doch bewegt sie ganze
| Bäume. Sie kommt leise, aber ihre Wirkung ist unaufhaltsam. Wer sich ihr widersetzt,
| wird irgendwann gebrochen. Wer sie annimmt, lernt zu fliegen. Die Frage ist nicht, ob
| wir uns ändern können, sondern ob wir den Mut haben, uns zu ändern – bevor das Leben es
| für uns tut.
|
| Ein einzelnes Licht kann die Dunkelheit erhellen, aber viele Lichter zusammen schaffen
| eine Sonne. Gemeinschaft bedeutet nicht, dass wir alle gleich sind, sondern dass
| wir uns gegenseitig stärken – in Schwäche, in Zweifel, in Freude. Denn niemand von
| uns ist so stark wie wir alle zusammen.
|
| …
Sein Redefluss sollte noch viele Stunden ungebrochen weitergehen,
und doch war es nur ein Anfang.The marker is configurable, depending on the context it may be more appropriate
to use a different marker than | for block speech.
The default is |, but if your markdown formatter complains you could go to >.
Configuration
Both inline and block speech support a requireSpeaker option.
Inline options
import { remarkDirectSpeech } from "remark-speech";
unified().use(remarkDirectSpeech, {
spoken: {
quote: '"',
closingQuote: '"',
requireSpeaker: "optional", // "always" | "never" | "optional"
},
});Block options
import { remarkDirectSpeechBlock } from "remark-speech";
unified().use(remarkDirectSpeechBlock, {
spoken: {
marker: "|",
requireSpeaker: "optional", // "always" | "never" | "optional"
},
});requireSpeaker behavior
always: a speaker in[[...]]is required.never: a speaker in[[...]]is not allowed.optional: both forms are allowed.
If requireSpeaker is omitted, the default is optional.
When a node has an empty speaker, serialization omits [[...]].
Error Reporting
The plugin validates speaker rules during processing and emits parser-visible diagnostics on the VFile.
- Violations are added to
file.messages. - Messages are marked as fatal (
message.fatal = true) so consuming programs can treat them as errors. - This is structured diagnostics, not log-only warnings.
Example:
import { unified } from "unified";
import remarkParse from "remark-parse";
import remarkStringify from "remark-stringify";
import { remarkDirectSpeech } from "remark-speech";
const file = unified()
.use(remarkParse)
.use(remarkDirectSpeech, {
spoken: {
quote: '"',
requireSpeaker: "always",
},
})
.use(remarkStringify)
.processSync('"No speaker"');
if (file.messages.length > 0) {
for (const message of file.messages) {
console.error(String(message));
}
}