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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
/**
 * 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 vote down vote up
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 vote down vote up
/** 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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
/** 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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 };
  }
}