unist#Parent TypeScript Examples
The following examples show how to use
unist#Parent.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: utils.ts From dendron with GNU Affero General Public License v3.0 | 7 votes |
static bumpHeadings(root: Parent, baseDepth: number) {
const headings: Heading[] = [];
walk(root, (node: Node) => {
if (node.type === DendronASTTypes.HEADING) {
headings.push(node as Heading);
}
});
const minDepth = headings.reduce((memo, h) => {
return Math.min(memo, h.depth);
}, MAX_HEADING_DEPTH);
const diff = baseDepth + 1 - minDepth;
headings.forEach((h) => {
h.depth += diff;
});
}
Example #2
Source File: utils.ts From dendron with GNU Affero General Public License v3.0 | 7 votes |
function walk(node: Parent, fn: any) {
fn(node);
if (node.children) {
(node.children as Node[]).forEach((n) => {
// @ts-ignore
walk(n, fn);
});
}
}
Example #3
Source File: remark-toc-headings.ts From portfolio with MIT License | 6 votes |
export default function remarkTocHeadings(options) {
return (tree: Parent) =>
visit(tree, 'heading', node => {
const textContent = toString(node);
options.exportRef.push({
value: textContent,
url: '#' + slug(textContent),
depth: node.depth,
});
});
}
Example #4
Source File: dendronPub.ts From dendron with GNU Affero General Public License v3.0 | 6 votes |
/**
* Returns a new copy of children array where the first un-rendered
* reference ![[ref]] in children array is replaced with the given `data`. */
function replacedUnrenderedRefWithConvertedData(
data: Parent[],
children: Node[]
) {
if (children.length > 1) {
const idx = _.findIndex(children, RemarkUtils.isNoteRefV2);
const processedChildren = children
.slice(0, idx)
.concat(data)
.concat(children.slice(idx + 1));
return processedChildren;
} else {
return data;
}
}
Example #5
Source File: remark-code-title.ts From portfolio with MIT License | 6 votes |
export default function remarkCodeTitles() {
return (tree: Parent & { lang?: string }) =>
visit(
tree,
'code',
(node: Parent & { lang?: string }, index, parent: Parent) => {
const nodeLang = node.lang || '';
let language = '';
let title = '';
if (nodeLang.includes(':')) {
language = nodeLang.slice(0, nodeLang.search(':'));
title = nodeLang.slice(nodeLang.search(':') + 1, nodeLang.length);
}
if (!title) {
return;
}
const className = 'remark-code-title';
const titleNode = {
type: 'mdxJsxFlowElement',
name: 'div',
attributes: [
{ type: 'mdxJsxAttribute', name: 'className', value: className },
],
children: [{ type: 'text', value: title }],
data: { _xdmExplicitJsx: true },
};
parent.children.splice(index, 0, titleNode);
node.lang = language;
},
);
}
Example #6
Source File: utils.ts From dendron with GNU Affero General Public License v3.0 | 6 votes |
/** A simplified and adapted version of visitParents from unist-utils-visit-parents, that also keeps track of indices of the ancestors as well.
*
* The limitations are:
* * `test`, if used, can only be a string representing the type of the node that you want to visit
* * Adding or removing siblings is undefined behavior
* Please modify this function to add support for these if needed.
*/
static visitParentsIndices({
nodes,
test,
visitor,
}: {
nodes: Node[];
test?: string;
visitor: VisitorParentsIndices;
}) {
function recursiveTraversal(
nodes: Node[],
ancestors: ParentWithIndex[]
): boolean | undefined {
for (let i = 0; i < nodes.length; i++) {
// visit the current node
const node = nodes[i];
let action: boolean | undefined | "skip";
if (_.isUndefined(test) || node.type === test) {
action = visitor({ node, index: i, ancestors });
}
if (action === "skip") return; // don't traverse the children of this node
if (action === false) return false; // stop traversing completely
// visit the children of this node, if any
// @ts-ignore
if (node.children) {
const parent = node as Parent;
const newAncestors = [...ancestors, { ancestor: parent, index: i }];
const action = recursiveTraversal(parent.children, newAncestors);
if (action === false) return; // stopping traversal
}
}
return true; // continue traversal if needed
}
// Start recursion with no ancestors (everything is top level)
recursiveTraversal(nodes, []);
}
Example #7
Source File: utils.ts From dendron with GNU Affero General Public License v3.0 | 6 votes |
static genMDErrorMsg(msg: string): Parent {
return root(blockquote(text(msg)));
}
Example #8
Source File: utils.ts From dendron with GNU Affero General Public License v3.0 | 6 votes |
static genMDMsg(msg: string): Parent {
return root(paragraph(text(msg)));
}
Example #9
Source File: utils.ts From dendron with GNU Affero General Public License v3.0 | 6 votes |
getLinkCandidates = ({
ast,
note,
engine,
}: {
ast: DendronASTNode;
note: NoteProps;
engine: DEngineClient;
}) => {
const textNodes: Text[] = [];
visit(
ast,
[DendronASTTypes.TEXT],
(node: Text, _index: number, parent: Parent | undefined) => {
if (parent?.type === "paragraph" || parent?.type === "tableCell") {
textNodes.push(node);
}
}
);
const linkCandidates: DLink[] = [];
_.map(textNodes, (textNode: Text) => {
const value = textNode.value as string;
value.split(/\s+/).forEach((word) => {
const possibleCandidates = NoteUtils.getNotesByFnameFromEngine({
fname: word,
engine,
vault: note.vault,
}).filter((note) => note.stub !== true);
linkCandidates.push(
...possibleCandidates.map((candidate): DLink => {
const startColumn = value.indexOf(word) + 1;
const endColumn = startColumn + word.length;
const position: Position = {
start: {
line: textNode.position!.start.line,
column: startColumn,
offset: textNode.position!.start.offset
? textNode.position!.start.offset + startColumn - 1
: undefined,
},
end: {
line: textNode.position!.start.line,
column: endColumn,
offset: textNode.position!.start.offset
? textNode.position!.start.offset + endColumn - 1
: undefined,
},
};
return {
type: "linkCandidate",
from: NoteUtils.toNoteLoc(note),
value: value.trim(),
position,
to: {
fname: word,
vaultName: VaultUtils.getName(candidate.vault),
},
};
})
);
});
});
return linkCandidates;
}
Example #10
Source File: utils.ts From dendron with GNU Affero General Public License v3.0 | 6 votes |
static isParent(node: Node): node is Parent {
return _.isArray((node as Parent).children);
}
Example #11
Source File: utils.ts From dendron with GNU Affero General Public License v3.0 | 6 votes |
static isRoot(node: Node): node is Parent {
return node.type === DendronASTTypes.ROOT;
}
Example #12
Source File: noteRefsV2.ts From dendron with GNU Affero General Public License v3.0 | 6 votes |
function renderPrettyAST(opts: {
content: Parent;
title: string;
link?: string;
}) {
const { content, title, link } = opts;
const linkLine = _.isUndefined(link)
? ""
: `<a href=${link} class="portal-arrow">Go to text <span class="right-arrow">→</span></a>`;
const top = `<div class="portal-container">
<div class="portal-head">
<div class="portal-backlink" >
<div class="portal-title">From <span class="portal-text-title">${title}</span></div>
${linkLine}
</div>
</div>
<div id="portal-parent-anchor" class="portal-parent" markdown="1">
<div class="portal-parent-fader-top"></div>
<div class="portal-parent-fader-bottom"></div>`;
const bottom = `\n</div></div>`;
return paragraph([html(top)].concat([content]).concat([html(bottom)]));
}
Example #13
Source File: noteRefsV2.ts From dendron with GNU Affero General Public License v3.0 | 6 votes |
/** For references like `#^item:#^item`, only include a single list item and not it's children. */
function removeExceptSingleItem(nodes: ParentWithIndex[]) {
let closestListItem: Parent | undefined;
// Find the list item closest to the anchor
_.forEach(nodes, ({ ancestor }) => {
if (ancestor.type === DendronASTTypes.LIST_ITEM) {
closestListItem = ancestor;
}
});
if (_.isUndefined(closestListItem)) return;
// If this list item has any nested lists, remove them to get rid of the children
closestListItem.children = closestListItem.children.filter(
(node) => !(node.type === DendronASTTypes.LIST)
);
}
Example #14
Source File: remark-extract-frontmatter.ts From portfolio with MIT License | 5 votes |
export default function extractFrontmatter() {
return (tree: Parent, file: VFile) => {
visit(tree, 'yaml', (node: Parent) => {
//@ts-ignore
file.data.frontmatter = load(node.value);
});
};
}
Example #15
Source File: remark-img-to-jsx.ts From portfolio with MIT License | 5 votes |
export default function remarkImgToJsx() {
return (tree: Node) => {
visit(
tree,
// only visit p tags that contain an img element
(node: Parent): node is Parent =>
node.type === 'paragraph' &&
node.children.some(n => n.type === 'image'),
(node: Parent) => {
const imageNode = node.children.find(
n => n.type === 'image',
) as ImageNode;
// only local files
if (fs.existsSync(`${process.cwd()}/public${imageNode.url}`)) {
const dimensions = sizeOf(`${process.cwd()}/public${imageNode.url}`);
// Convert original node to next/image
(imageNode.type = 'mdxJsxFlowElement'),
(imageNode.name = 'Image'),
(imageNode.attributes = [
{ type: 'mdxJsxAttribute', name: 'alt', value: imageNode.alt },
{ type: 'mdxJsxAttribute', name: 'src', value: imageNode.url },
{
type: 'mdxJsxAttribute',
name: 'width',
value: dimensions.width,
},
{
type: 'mdxJsxAttribute',
name: 'height',
value: dimensions.height,
},
]);
// Change node type from p to div to avoid nesting error
node.type = 'div';
node.children = [imageNode];
}
},
);
};
}
Example #16
Source File: noteRefsV2.ts From dendron with GNU Affero General Public License v3.0 | 4 votes |
function convertNoteRefHelperAST(
opts: ConvertNoteRefHelperOpts & { procOpts: any }
): Required<RespV2<Parent>> {
const { proc, refLvl, link, note } = opts;
let noteRefProc: Processor;
// Workaround until all usages of MDUtilsV4 are removed
const engine =
MDUtilsV5.getProcData(proc).engine ||
MDUtilsV4.getEngineFromProc(proc).engine;
// Create a new proc to parse the reference; set the fname accordingly.
// NOTE: a new proc is created here instead of using the proc() copy
// constructor, as that is an expensive op since it deep clones the entire
// engine state in proc.data
noteRefProc = MDUtilsV5.procRemarkFull(
{
...MDUtilsV5.getProcData(proc),
insideNoteRef: true,
fname: note.fname,
vault: note.vault,
engine,
},
MDUtilsV5.getProcOpts(proc)
);
const wsRoot = engine.wsRoot;
noteRefProc = noteRefProc.data("fm", MDUtilsV5.getFM({ note, wsRoot }));
MDUtilsV5.setNoteRefLvl(noteRefProc, refLvl);
const bodyAST: DendronASTNode = noteRefProc.parse(
note.body
) as DendronASTNode;
// Make sure to get all footnote definitions, including ones not within the range, in case they are used inside the range
const footnotes = RemarkUtils.extractFootnoteDefs(bodyAST);
const { anchorStart, anchorEnd, anchorStartOffset } = _.defaults(link.data, {
anchorStartOffset: 0,
});
const { start, end, data, error } = prepareNoteRefIndices({
anchorStart,
anchorEnd,
bodyAST,
makeErrorData: (anchorName, anchorType) => {
return MdastUtils.genMDMsg(
`${anchorType} anchor ${anchorName} not found`
);
},
});
if (data) return { data, error };
// slice of interested range
try {
const out = root(
bodyAST.children.slice(
(start ? start.index : 0) + anchorStartOffset,
end ? end.index + 1 : undefined
)
);
// Add all footnote definitions back. We might be adding duplicates if the definition was already in range, but rendering handles this correctly.
// We also might be adding definitions that weren't used in this range, but rendering will simply ignore those.
out.children.push(...footnotes);
const data = noteRefProc.runSync(out) as Parent;
return {
error: null,
data,
};
} catch (err) {
console.log(
JSON.stringify({
ctx: "convertNoteRefHelperAST",
msg: "Failed to render note reference",
err,
})
);
return {
error: new DendronError({
message: "error processing note ref",
payload: err,
}),
data: MdastUtils.genMDMsg("error processing ref"),
};
}
}
Example #17
Source File: noteRefsV2.ts From dendron with GNU Affero General Public License v3.0 | 4 votes |
export function convertNoteRefASTV2(
opts: ConvertNoteRefOpts & { procOpts: any }
): { error: DendronError | undefined; data: Parent[] | undefined } {
/**
* Takes a note ref and processes it
* @param ref DNoteLoc (note reference) to process
* @param note actual note at the reference
* @param fname fname (either from the actual note ref, or inferred.)
* @returns process note references
*/
function processRef(ref: DNoteLoc, note: NoteProps, fname: string) {
try {
if (
shouldApplyPublishRules &&
!SiteUtils.canPublish({
note,
config: config!,
engine,
})
) {
// TODO: in the future, add 403 pages
return paragraph();
}
const body = note.body;
const { error, data } = convertNoteRefHelperAST({
body,
link,
refLvl: refLvl + 1,
proc,
compilerOpts,
procOpts,
note,
});
if (error) {
errors.push(error);
}
if (prettyRefs) {
let suffix = "";
let useId = wikiLinkOpts?.useId;
if (
useId === undefined &&
MDUtilsV5.isV5Active(proc) &&
dest === DendronASTDest.HTML
) {
useId = true;
}
let href = useId ? note.id : fname;
const title = getTitle({
config,
note,
loc: ref,
shouldApplyPublishRules,
});
if (dest === DendronASTDest.HTML) {
if (!MDUtilsV5.isV5Active(proc)) {
suffix = ".html";
}
if (note.custom.permalink === "/") {
href = "";
suffix = "";
}
}
if (dest === DendronASTDest.MD_ENHANCED_PREVIEW) {
suffix = ".md";
// NOTE: parsing doesn't work properly for first line, not sure why
// this HACK fixes it
data.children = [brk].concat(data.children);
}
let isPublished = true;
if (dest === DendronASTDest.HTML) {
// check if we need to check publishign rules
if (
MDUtilsV5.isV5Active(proc) &&
!MDUtilsV5.shouldApplyPublishingRules(proc)
) {
isPublished = true;
} else {
isPublished = SiteUtils.isPublished({
note,
config: config!,
engine,
});
}
}
const link = isPublished
? `"${wikiLinkOpts?.prefix || ""}${href}${suffix}"`
: undefined;
return renderPrettyAST({
content: data,
title,
link,
});
} else {
return paragraph(data);
}
} catch (err) {
const msg = `Error rendering note reference for ${note?.fname}`;
return MdastUtils.genMDErrorMsg(msg);
}
}
const errors: IDendronError[] = [];
const { link, proc, compilerOpts, procOpts } = opts;
const procData = MDUtilsV5.getProcData(proc);
const { noteRefLvl: refLvl } = procData;
// Needed for backwards compatibility until all MDUtilsV4 proc usages are removed
const engine = procData.engine || MDUtilsV4.getEngineFromProc(proc).engine;
// prevent infinite nesting.
if (refLvl >= MAX_REF_LVL) {
return {
error: new DendronError({ message: "too many nested note refs" }),
data: [MdastUtils.genMDErrorMsg("too many nested note refs")],
};
}
// figure out configs that change how we process the note reference
const { dest, config, vault: vaultFromProc, fname, vault } = procData;
// Needed for backwards compatibility until all MDUtilsV4 proc usages are removed
const shouldApplyPublishRules =
MDUtilsV5.shouldApplyPublishingRules(proc) ||
MDUtilsV4.getDendronData(proc).shouldApplyPublishRules;
const { wikiLinkOpts } = compilerOpts;
// The note that contains this reference might override the pretty refs option for references inside it.
const containingNote = NoteUtils.getNoteByFnameFromEngine({
fname,
vault,
engine,
});
let prettyRefs = ConfigUtils.getEnablePrettyRefs(config, {
shouldApplyPublishRules,
note: containingNote,
});
if (
prettyRefs &&
_.includes([DendronASTDest.MD_DENDRON, DendronASTDest.MD_REGULAR], dest)
) {
prettyRefs = false;
}
const publishingConfig = ConfigUtils.getPublishingConfig(config);
const duplicateNoteConfig = publishingConfig.duplicateNoteBehavior;
// process note references.
let noteRefs: DNoteLoc[] = [];
if (link.from.fname.endsWith("*")) {
// wildcard reference case
const vault = procData.vault;
const resp = engine.queryNotesSync({
qs: link.from.fname,
originalQS: link.from.fname,
vault,
});
const out = _.filter(resp.data, (ent) =>
DUtils.minimatch(ent.fname, link.from.fname)
);
noteRefs = _.sortBy(
out.map((ent) => NoteUtils.toNoteLoc(ent)),
"fname"
);
if (noteRefs.length === 0) {
const msg = `Error rendering note reference. There are no matches for \`${link.from.fname}\`.`;
return { error: undefined, data: [MdastUtils.genMDErrorMsg(msg)] };
}
const processedRefs = noteRefs.map((ref) => {
const fname = ref.fname;
const npath = DNodeUtils.getFullPath({
wsRoot: engine.wsRoot,
vault: vault as DVault,
basename: fname + ".md",
});
let note: NoteProps;
try {
note = file2Note(npath, vault as DVault);
} catch (err) {
const msg = `error reading file, ${npath}`;
return MdastUtils.genMDMsg(msg);
}
return processRef(ref, note, fname);
});
return { error: undefined, data: processedRefs };
} else {
// single reference case.
let note: NoteProps;
const { vaultName: vname } = link.data;
const { fname } = link.from;
const resp = tryGetNotes({
fname,
vname,
vaults: engine.vaults,
engine,
});
// check for edge cases
if (resp.error) {
return {
error: undefined,
data: [MdastUtils.genMDErrorMsg(resp.error.message)],
};
}
// multiple results
if (resp.data.length > 1) {
// applying publish rules but no behavior defined for duplicate notes
if (shouldApplyPublishRules && _.isUndefined(duplicateNoteConfig)) {
return {
error: undefined,
data: [
MdastUtils.genMDErrorMsg(
`Error rendering note reference. There are multiple notes with the name ${link.from.fname}. Please specify the vault prefix.`
),
],
};
}
// apply publish rules and do duplicate
if (shouldApplyPublishRules && !_.isUndefined(duplicateNoteConfig)) {
const maybeNote = SiteUtils.handleDup({
allowStubs: false,
dupBehavior: duplicateNoteConfig,
engine,
config,
fname: link.from.fname,
noteCandidates: resp.data,
noteDict: engine.notes,
});
if (!maybeNote) {
return {
error: undefined,
data: [
MdastUtils.genMDErrorMsg(
`Error rendering note reference for ${link.from.fname}`
),
],
};
}
note = maybeNote;
} else {
// no need to apply publish rules, try to pick the one that is in same vault
const _note = _.find(resp.data, (note) =>
VaultUtils.isEqual(note.vault, vaultFromProc, engine.wsRoot)
);
if (_note) {
note = _note;
} else {
note = resp.data[0];
}
}
} else {
note = resp.data[0];
}
noteRefs.push(link.from);
const processedRefs = noteRefs.map((ref) => {
const fname = note.fname;
return processRef(ref, note, fname);
});
return { error: undefined, data: processedRefs };
}
}