Cookbook

Summary

Common recipes on doing common operations inside the Dendron codebase

Packages

Install a new package

Because Dendron is packaged as a mono repo managed using lerna, you can't just do a regular yarn add to install new packages. This is because lerna symlinks mono-repo dependencies together and a yarn add would override that. Instead, use the following command:

lerna add {package-to-install} --scope @dendronhq/{package-to-install-into}

Because this is typescript, don't forget to also install the @types package if it exists

lerna add @types/{package-to-install} --scope @dendronhq/{package-to-install-into}
  • NOTE: watch out that you are installing dependencies in the right package. Missing dependencies will appear to work in development if that dependency is present in any of the other packages. The reason things work is because of the way the nodejs module resolution works and that we're in a monorepo. Dependencies are installed at the root of the monorepo and will be found there when the package doesn't have them. When we publish them as npm packages, these dependencies will show up as missing in their respective packages if its not included in the dependencies

Publish a new monorepo package

  • initialize repo
cd {workspace_dir}
cp -R /path/to/dendron-yeoman/node-ts packages/{new-package}
  • update package.json

    • change project name
  • publish the repo (needs to be done initially before running lerna publish)

npm publish --access public

Git

Adding a new husky hook

The following is an example of adding a hook that checks whether any imports have the form "../(common-frontend|...)" and fail to push if so

+++ b/hooks/pre-push.js
@@ -2,6 +2,7 @@ const {checkToken} = require("./common");
 const {exec} = require("./exec");

 function main() {
   // Where we would push if we ran `git push`
   let upstream;
   try {
@@ -9,15 +10,18 @@ function main() {
   } catch {
     // Fallback to first origin if none are set
     upstream = `${exec("git remote").stdout.trim().split("\n")[0]}/master`;
   }
   // The files that would get pushed
   const filesToPush = exec(`git diff --name-only ${upstream}`).stdout.split('\n');

   return checkToken({
     filesToCheck: filesToPush,
     forbiddenTokens: {
       ".only": { rgx: /(describe|it|test)\.only/, fileRgx: /\.spec\.ts$/ },
       "debugger;": { rgx: /(^|\s)debugger;/, fileRgx: /\.ts$/ },
+      "rel import of monorepo pkg": { rgx: /(\.\.\/(common-frontend|common-all|common-server|engine-server|dendron-cli|pods-core|api-server|common-test-utils|engine-test-utils|dendron-next-server))/, fileRgx: /\.ts[x]?$/ },
     }
   });
 }

Git Ignore Blame

In case of large refactorign changes, we want to not overwrite authorship and commit history.

In the project root, run the following after you have commited your styling changes to preserve the history.

echo $HASH_OF_COMMIT >> .git-blame-ignore-revs

You can see an explanation of how it works here

Config

Update JSON Config with comments

Dendron works with JSON with comments when working the a vscode workspace file, snippets file or the keybindings file. When making a change here, take care to both read and write to the file while preserving comments.

You'll want to make sure to use the following functions to read, assign and write json with comments

async function readJSONWithComments(fpath: string)
function assignJSONWithComment(jsonObj: any, dataToAdd: any) {
function writeJSONWithComments(fpath: string, data: any)

Adding a general configuration

See Add New Config

Utilities

Adding a new utility

When adding a new utility function, we want it to go in the most accessible location. In order of preference:

  • Common All

  • Common Server

  • Dendron Engine

  • user facing package (eg. dendron-cli, plugin-core, etc)

  • NOTE: when adding a utility that is configuration related, add it to ConfigUtils or create a {Module}ConfigUtils class to encapsulate that logic. see discussion here

Getting absolute path for a vault

Dendron has many vault types (Private) which means that getting the path to the vault is not as simple as path.join(wsRoot, vault.fsPath). To get the path of the vault relative to wsRoot, use the following:

// Path of vault relative to workspace root
path.join(wsRoot, VaultUtils.getRelPath(vault))

Synchronously loop through async results

await _.reduce(
  vaults,
  async (resp: any, vault: DVault) => {
    await resp;
    await wsService.createVault({ vault });
    return this.addVaultToWorkspace(vault);
  },
  Promise.resolve()
);

Views

Webviews, including as Preview, TreeViewV2, Calendar View

Preview Panel

Getting Access to Preview Panel

  • Within plugin-core code, to access the preview panel (in order to show it, for example), you should inject an instance of PreviewProxy rather than using the vscode.webviewPanel directly.
  • As an entry point from vscode UI contribution points (such as command palette, or context menus), the ShowPreview vscode command should be used.
    • If you want to open the preview with default preview behavior, such as having the preview show the contents of the in-focus Dendron note, call the command with no arguments
    • If you want to open the preview with specific contents (for example, showing a non-Dendron markdown file via a context menu), then pass in the information via a NoteProps object to the ShowPreview command.

Children
  1. Basics
  2. Common

Backlinks