Compare commits

...

63 Commits

Author SHA1 Message Date
Jani Eväkallio
ac9aa741ef Prettier 2020-08-22 00:36:36 +01:00
Jani Eväkallio
8b7e6983bf Add support for basic link aliases 2020-08-21 23:58:40 +01:00
zbw8388
19140488ae Fixed Note ${noteId} was not added to NoteGraph
After #208, id for each note in NoteGraph is hashed. However, in generateReferenceList in file wikilink-reference-generation.ts, the id is still given by the file path, which compromised the core functionality. This PR fixed this bug.
2020-08-11 11:46:12 +02:00
Martin Laws
3297d8bd67 Fixed typos in foam workspace link (#214) 2020-08-07 07:57:19 +01:00
ingalless
5fabe08afa Add pre-defined user snippets to roadmap (#192)
Co-authored-by: Jonathan Ingall <jonny@mondago.com>
2020-08-05 18:35:50 +01:00
Riccardo
87f172d217 Decoupled note ID from note slug (#208)
* decoupled ID from note slug

* added vscode debug configuration to work with vscode-jest extension

* made things prettier

* Add a test for parsing empty frontmatter

* Use astPositionToVsCodePosition util function

* fixed a TODOs around URIs and added tests

* added tests around notes querying

* removing unused imports

* Fix typo

* implemented PR feedback

* fixed lint warnings and renamed method for consistency

* removed unnecessary checks and fix compilation error in tests

Co-authored-by: Ankit Tiwari <ankitt255@gmail.com>
2020-08-05 18:37:35 +02:00
allcontributors[bot]
3bebbcc45a docs: add dshemetov as a contributor (#210)
* docs: update docs/index.md [skip ci]

* docs: update readme.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2020-08-04 13:28:08 -07:00
Dmitry Shemetov
b4224659b7 Fill out [[Migrating from OneNote]] stub. (#200)
* Fill out Migrating from OneNote stub

* Update Summary and improve grammatic consistency

* Added terminal navigation instructions

* kebab-case and update roadmap links
2020-08-04 13:26:33 -07:00
Joe Previte
7f3ba7853e feat(foam-vscode): add jest for testing (#204)
* feat: setup jest for foam-vscode

* Add failing test (fails due to missing vscode dependency)

* feat: add vscode mock

Co-authored-by: Jani Eväkallio <jani.evakallio@gmail.com>
2020-08-04 13:27:11 +01:00
Ankit Tiwari
bcab41917a Parse frontmatter and use frontmatter.title as heading (fixes #174) (#191)
* Add frontmatter data to NoteGraph node

* Store document start position inside NoteGraph node

* Add heading below the frontmatter

* Show a progress bar while running Janitor

* Fix tests

* Update note start position to make it 1-indexed

* Initialize frontmatter by an empty object instead of null

* Remove console logs
2020-08-03 18:41:12 +02:00
allcontributors[bot]
f32b432e26 docs: add hooncp as a contributor (#207)
* docs: update docs/index.md [skip ci]

* docs: update readme.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2020-08-02 16:34:20 -07:00
hooncp
5383865d88 Update documentation by adding new FAQ page (#202)
* Update documentation by adding new FAQ page

* Update FAQ page according to review
2020-08-02 16:33:22 -07:00
allcontributors[bot]
c2c639b402 docs: add Klaudioz as a contributor (#205)
* docs: update docs/index.md [skip ci]

* docs: update readme.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2020-08-02 11:51:20 -07:00
Claudio Canales
f4a864ee7d Add how to update pushed Azure Wiki (#203)
* Add how to update pushed Azure Wiki

Add the way to push to GitHub and Azure with one command.

* Update docs/azure-devops-wiki.md

Co-authored-by: Joe Previte <jjprevite@gmail.com>

* Update azure-devops-wiki.md

Add suggested changes

* Update docs/azure-devops-wiki.md

Co-authored-by: Joe Previte <jjprevite@gmail.com>

Co-authored-by: Joe Previte <jjprevite@gmail.com>
2020-08-02 10:45:03 -07:00
hooncp
bf2aa599ef Remove brackets (#201) 2020-08-02 18:33:03 +01:00
Claudio Canales
24738e4390 Update azure-devops-wiki.md (#189)
I followed the entire guide so I think this addition can do it a little bit more friendly.
2020-07-30 21:40:33 +01:00
Jani Eväkallio
0e0355ae9f Fix VSCode Launch Task and Monorepo builds (#193)
* revise vscode task and launch configs

- Removed commands from sub-package:
  same can be achieved from the root
- Added build and no-build variants.
  In case separate watcher is already running,
  no need to build.
- Changed watch to build (no hot-reload in vscode by default)
- Show the build in console

Didn't update yarn.lock, seems to want to add
foam-core@0.3.0-alpha.0, but not really
related to this change

Note: the extension tests are failing due to
`Cannot find module '...foam-vscode/out/test/suite/index'`.
However that shouldn't be related to this change either.

* Simplify VS Code launch tasks (shift work to typescript compiler)

* Fix circular dependency index->janitor/index->index

* Use --build flag to use TSC in Build Mode
https://www.typescriptlang.org/docs/handbook/project-references.html#build-mode-for-typescript

This configures tsc to use multiple project roots (vscode, core) and run incremental builds on
changes to each projects. This will necessitate us running the core in commonjs build mode,
which is fine for testing and deployment to node environments, but won't work in the browser,
so foam-core will need a separate build config for UMD builds

* Build foam-core in commonjs mode by default (+cleanup unnecessary config)

* Create ES6 build config for distribution builds

* Cleanup yarn.lock

* Build package before running

* Fix missing lint paths and fix lint error

* Remove redundant .vscode settings files from foam-vscode project

* Add core test launch task

Co-authored-by: jojanaho <janne.ojanaho@iki.fi>
2020-07-30 21:39:16 +01:00
allcontributors[bot]
49ddc41d41 docs: add dshemetov as a contributor (#195)
* docs: update docs/index.md [skip ci]

* docs: update readme.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2020-07-30 10:13:10 +01:00
Dmitry Shemetov
5226917e35 Update creating-new-notes.md (#194)
* Update creating-new-notes.md

Added a non-intuitive hotkey for entering notes.

* Add instructions on how to remap the key binding

Co-authored-by: Jani Eväkallio <jani.evakallio@gmail.com>
2020-07-30 08:23:41 +01:00
allcontributors[bot]
ba6448ee89 docs: add vitaly-pevgonen as a contributor (#190)
* docs: update docs/index.md [skip ci]

* docs: update readme.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2020-07-29 08:38:17 -07:00
Jani Eväkallio
ad12962192 Restructure documentation page 2020-07-28 09:32:04 +01:00
Jani Eväkallio
6a1e61d93b Add documentation for Link Formatting and Markdown Notes Autocompletion (#180)
* Add recipe to fix autocompletion problems in markdown notes

* Writeup broader link formatting guide

* Reference new note from recipes

* Update foam workspace settings

* Fix typo

Co-authored-by: Jonathan Ingall <jonny@mondago.com>
2020-07-28 08:20:26 +01:00
allcontributors[bot]
4dbb04a364 docs: add Klaudioz as a contributor (#182)
* docs: update docs/index.md [skip ci]

* docs: update readme.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2020-07-27 23:57:57 +01:00
Claudio Canales
da5eb33bdd Some minor improvements to contribution-guide.md (#181) 2020-07-27 23:57:11 +01:00
Jani Eväkallio
b5b7f2dde0 Add epicfaace to all-contributors 2020-07-27 19:01:40 +01:00
allcontributors[bot]
a9ccd0fb6c docs: add johnlindquist as a contributor (#177)
* docs: update docs/index.md [skip ci]

* docs: update readme.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2020-07-27 18:58:43 +01:00
John Lindquist
0155f48518 Adding a section for Jekyll config to ignore _site (#176)
* Adding a section for Jekyll config to ignore _site

* Generalise explanation

Co-authored-by: Jani Eväkallio <jani.evakallio@gmail.com>
2020-07-27 18:56:24 +01:00
Ashwin Ramaswami
615b8d745f Fix spacing on readme (#172) 2020-07-27 17:49:51 +02:00
allcontributors[bot]
894441bedb docs: add MehraAkshay as a contributor (#168)
* docs: update docs/index.md [skip ci]

* docs: update readme.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2020-07-26 23:56:22 +01:00
Jani Eväkallio
36bf39e276 v0.3.1 2020-07-26 23:49:28 +01:00
Jani Eväkallio
ff55c7425d Prepare for 0.3.1 release 2020-07-26 23:48:48 +01:00
Akshay
d8512db7aa fixed issue with opening daily note on windows (#167)
* fixed issue with opening daily note on windows

fixed issue with opening daily note on windows

* Reset yarn.lock
2020-07-26 23:43:38 +01:00
Jani Eväkallio
16a0e94d3f Link template zip 2020-07-26 16:13:44 +01:00
Jani Eväkallio
60efdaa864 Run Janitor on docs/ 2020-07-25 18:35:23 +01:00
Jani Eväkallio
5418ea8bb2 v0.3.0 2020-07-25 18:28:52 +01:00
Jani Eväkallio
50dd3685cd Prepare for 0.3.0 release 2020-07-25 18:27:08 +01:00
Jani Eväkallio
0ae0ab6c50 Remove unnecessary file 2020-07-25 18:23:05 +01:00
Jani Eväkallio
e1f5fb06c6 Add missing Janitor image 2020-07-25 18:22:46 +01:00
Jani Eväkallio
c04b342a76 Document janitor 2020-07-25 18:22:33 +01:00
Jani Eväkallio
0fc9397ed8 foam-vscode changelog 2020-07-25 18:09:11 +01:00
Jani Eväkallio
11fa878ed8 Document withExtensions 2020-07-25 18:06:55 +01:00
Jani Eväkallio
2e3f02c5bc Document daily note feature (#157)
* Document daily note feature

* Update docs/daily-notes.md

Co-authored-by: Joe Previte <jjprevite@gmail.com>

* Update docs/daily-notes.md

Co-authored-by: Joe Previte <jjprevite@gmail.com>

* Update docs/daily-notes.md

Co-authored-by: Joe Previte <jjprevite@gmail.com>

* Update docs/recipes.md

Co-authored-by: Joe Previte <jjprevite@gmail.com>

* Update docs/daily-notes.md

Co-authored-by: Joe Previte <jjprevite@gmail.com>

Co-authored-by: Joe Previte <jjprevite@gmail.com>
2020-07-25 17:52:04 +01:00
Jani Eväkallio
7c6874d528 Use shared Foam instance in janitor 2020-07-25 17:41:54 +01:00
Jani Eväkallio
fe9c6461c4 Remove hello commoand from CLI 2020-07-25 17:28:06 +01:00
Thomas Koppelaar
1047443677 Update images-from-your-clipboard.md (#150) 2020-07-25 17:17:33 +01:00
CHIRAG SINGHAL
0cf2295c2a Implement janitor command to extension (#155)
* Move applyTextEdit to core

* WIP: Add janitor command in foam-vscode

* basic janitor command done

* unsaved markdown janitor solved

* export bootstrap getConfig functions

* fixed multi unsaved files text edits

* Move applyTextEdit to core

* WIP: Add janitor command in foam-vscode

* basic janitor command done

* unsaved markdown janitor solved

* export bootstrap getConfig functions

* fixed multi unsaved files text edits

* Be less greedy about definitions, only include defs at end of file

* Use link reference definitions in janitor

* Add headers/footers to tests

* Ensure janitor doesn't touch unrelated reference definitions

* Format document, convert promise trampoline to async/await for readability

* Add error handling and friendly output

* Use workspace settings for extension

* Add flag to janitor

* Use note.eol

* Fix tests

Co-authored-by: Ankit Tiwari <ankitt255@gmail.com>
Co-authored-by: Jani Eväkallio <jani.evakallio@gmail.com>
2020-07-25 17:17:03 +01:00
vitaly-pevgonen
9e7c3ffed8 Recipe for publishing to Azure DevOps wiki. (#147)
* Recipe for publishing to Azure DevOps wiki.

* Small fix: Devops -> DevOps in link references.

* Update docs/azure-devops-wiki.md

Co-authored-by: Joe Previte <jjprevite@gmail.com>

* Update docs/azure-devops-wiki.md

Co-authored-by: Joe Previte <jjprevite@gmail.com>

* Update docs/azure-devops-wiki.md

Co-authored-by: Joe Previte <jjprevite@gmail.com>

* Update docs/azure-devops-wiki.md

Co-authored-by: Joe Previte <jjprevite@gmail.com>

* Update docs/azure-devops-wiki.md

Co-authored-by: Joe Previte <jjprevite@gmail.com>

* Update azure-devops-wiki.md

Co-authored-by: Vitaly Pevgonen <vitaly.pevgonen@cgi.com>
Co-authored-by: Joe Previte <jjprevite@gmail.com>
2020-07-25 17:13:11 +01:00
nixsee
c69a2bb2b1 adding recipe for migrating from Onenote (#152)
* adding recipe for migrating from Onenote

* Update docs/roadmap.md

Co-authored-by: Joe Previte <jjprevite@gmail.com>

Co-authored-by: Joe Previte <jjprevite@gmail.com>
2020-07-25 17:10:58 +01:00
Jani Eväkallio
a3ab4f53a7 Update note graph and reference list when new notes are created (#156)
* Add notes to NoteGraph when they are created

* Add experimental events to foam-core to notify listeners about added/updated notes

* Update reference definitions in active editor when new note is added

* Update document in note graph before updating references
2020-07-25 16:50:44 +01:00
Mathieu Dutour
524ab15b12 add GH workflows (#145) 2020-07-25 12:19:15 +01:00
Ankit Tiwari
fd9fe12571 Move cursor to end of file when opening daily note (#137)
* Move cursor to end of file when opening daily note

* always move cursor to end of the file

* Only move cursor focus when file is newly created

Co-authored-by: Jani Eväkallio <jani.evakallio@gmail.com>
2020-07-25 12:11:23 +01:00
Jani Eväkallio
8d41d02ef4 Add thomaskoppelaar to contributors 2020-07-23 14:43:00 +01:00
Jani Eväkallio
8b578c1a1c Log ideas into inbox 2020-07-23 14:39:59 +01:00
ingalless
1e757aa7c4 Fix small typo (#148) 2020-07-23 13:54:27 +02:00
Riccardo
c88c24f97e DX: repo-wide build tasks (#144) 2020-07-22 00:11:04 +01:00
allcontributors[bot]
0c95ea7ca1 docs: add synesthesia as a contributor (#143)
* docs: update docs/index.md [skip ci]

* docs: update readme.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2020-07-21 07:49:30 +01:00
Julian Elve
00d1bd3a23 Add recipe for capturing notes from Drafts app on iOS (#132)
Co-authored-by: Jani Eväkallio <jani.evakallio@gmail.com>
2020-07-20 20:42:28 +01:00
allcontributors[bot]
a2e511ec6f docs: add lostintangent as a contributor (#142)
Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2020-07-20 20:13:24 +01:00
allcontributors[bot]
639a1ea21c docs: add chirag-singhal as a contributor (#141)
Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2020-07-20 20:11:24 +01:00
Jonathan Carter
271017c663 Adding GistPad recipe (#138)
Co-authored-by: Joe Previte <jjprevite@gmail.com>
Co-authored-by: Jani Eväkallio <jani.evakallio@gmail.com>
2020-07-20 20:10:42 +01:00
Ankit Tiwari
c7277383dd Initialise note.title only if heading exists (#136)
Co-authored-by: Jani Eväkallio <jani.evakallio@gmail.com>
2020-07-20 20:00:39 +01:00
Ankit Tiwari
339274309f Implement foam-cli janitor command (#112)
* Add Note.definitions and Note.end

* Use stringifyMarkdownLinkReferenceDefinition in foam-vscode

This commit also applies prettier to previously badly formatted files, so the diff is larger than necessary

* Add Note.end and Note.definitions to foam-core tests

* Export stringifyMarkdownLinkReferenceDefinition from foam-core

* Implement first version of generateLinkReferenceDefinitions janitor method

* Add partial tests for generateLinkReferenceDefinitions

* Setup Jest

* Add remove link definitions test to generateLinkReferences janitor method

* Add update link definitions test to generateLinkReferences janitor method

* export TextEdit interface

* Implement first version of applyText method

* Add no change  in link definitions test to generateLinkReferences janitor method

* Add  partial tests for writeFileToDisk method

* Implement generateHeading janitor method

Co-authored-by: CHIRAG SINGHAL <csinghal208@gmail.com>

* Move noteGraph scaffolding to utils

* Implement basic foam-cli janitor command

* kebab case file names while running janitor

* added generate Heading function to janitor

* added tests for generateHeading in janitor

* PR changes

* Add ora spinner

* Store endOfLine inside Note

* Use note.eol to append line endings

* check if given path is valid directory

* minor fixes

* added glob as dependency

* ignore link refrences with no file

* added tests files for migration

* Solves issue with roam migration

* fixed core tests

* Replace dashify by github-slugger

* used github slagger instead of dashify

* Add foam-core as dependency

* added foam migrate command

* updated foam janitor command

* minor fixes

* removed extra test files

* removed excess white space

* Refactor (PR changes)

1.  Renamed initializeNoteGraph.ts to initialize-note-graph.ts to be consistent with naming

2.  Refactored code in janitor and migrate commands

* Mock fs for tests

* Use Promise.resolve(null)

* Make fs tests no blocking by using promises

* Refactor renameFile to use path module

* Propagate the error from writeFileToDisk method

* Remove posttest command

* Run prettier before merge to get a cleaner diff

* Fix typo in variable name

* yarn lint --fix

* Update markdown-provider tests to support new API

* Add missing includeExtension argument

Co-authored-by: Jani Eväkallio <jani.evakallio@gmail.com>
Co-authored-by: chirag-singhal <csinghal208@gmail.com>
2020-07-20 19:28:41 +01:00
Riccardo
dc1c237c05 Paths in link definition section now include file extension (#133)
* Add .md extension when generating link definition section

The change in the definition allows links to be navigated in github
The changes in the JS inside _layouts maintains the html navigation when publishing to github pages.

For more info see https://foambubble.github.io/foam/link-reference-definition-improvements

* Updated existing docs to new link definition section

* better md file detection and consolidated code in parent template

* added mdx extension to markdown file detection

* added note about shortcut taken

* added configuration to toggle extensions in wikilinks + tests

* Revert "Updated existing docs to new link definition section"

This reverts commit 50e4a527e0.

We'll do this change once the version has been released

Co-authored-by: Jani Eväkallio <jani.evakallio@gmail.com>
2020-07-17 14:20:00 +01:00
118 changed files with 5364 additions and 869 deletions

View File

@@ -241,6 +241,107 @@
"code",
"doc"
]
},
{
"login": "chirag-singhal",
"name": "CHIRAG SINGHAL",
"avatar_url": "https://avatars3.githubusercontent.com/u/42653703?v=4",
"profile": "https://github.com/chirag-singhal",
"contributions": [
"code"
]
},
{
"login": "lostintangent",
"name": "Jonathan Carter",
"avatar_url": "https://avatars3.githubusercontent.com/u/116461?v=4",
"profile": "https://github.com/lostintangent",
"contributions": [
"doc"
]
},
{
"login": "synesthesia",
"name": "Julian Elve",
"avatar_url": "https://avatars3.githubusercontent.com/u/181399?v=4",
"profile": "https://www.synesthesia.co.uk",
"contributions": [
"doc"
]
},
{
"login": "thomaskoppelaar",
"name": "Thomas Koppelaar",
"avatar_url": "https://avatars3.githubusercontent.com/u/36331365?v=4",
"profile": "https://github.com/thomaskoppelaar",
"contributions": [
"question",
"code",
"userTesting"
]
},
{
"login": "MehraAkshay",
"name": "Akshay",
"avatar_url": "https://avatars1.githubusercontent.com/u/8671497?v=4",
"profile": "http://www.akshaymehra.com",
"contributions": [
"code"
]
},
{
"login": "johnlindquist",
"name": "John Lindquist",
"avatar_url": "https://avatars0.githubusercontent.com/u/36073?v=4",
"profile": "http://johnlindquist.com",
"contributions": [
"doc"
]
},
{
"login": "epicfaace",
"name": "Ashwin Ramaswami",
"avatar_url": "https://avatars2.githubusercontent.com/u/1689183?v=4",
"profile": "https://ashwin.run/",
"contributions": [
"doc"
]
},
{
"login": "Klaudioz",
"name": "Claudio Canales",
"avatar_url": "https://avatars1.githubusercontent.com/u/632625?v=4",
"profile": "https://github.com/Klaudioz",
"contributions": [
"doc"
]
},
{
"login": "vitaly-pevgonen",
"name": "vitaly-pevgonen",
"avatar_url": "https://avatars0.githubusercontent.com/u/6272738?v=4",
"profile": "https://github.com/vitaly-pevgonen",
"contributions": [
"doc"
]
},
{
"login": "dshemetov",
"name": "Dmitry Shemetov",
"avatar_url": "https://avatars0.githubusercontent.com/u/1810426?v=4",
"profile": "https://dshemetov.github.io",
"contributions": [
"doc"
]
},
{
"login": "hooncp",
"name": "hooncp",
"avatar_url": "https://avatars1.githubusercontent.com/u/48883554?v=4",
"profile": "https://github.com/hooncp",
"contributions": [
"doc"
]
}
],
"contributorsPerLine": 7,

25
.github/workflows/foam-cli.yml vendored Normal file
View File

@@ -0,0 +1,25 @@
name: Test foam-cli
on:
pull_request:
paths:
- 'packages/foam-cli/**'
push:
paths:
- 'packages/foam-cli/**'
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/setup-node@v1
- name: Install dependencies
run: yarn
# - name: Lint foam-lint
# run: yarn workspace foam-cli lint
- name: Test foam-cli
run: yarn workspace foam-cli test

25
.github/workflows/foam-core.yml vendored Normal file
View File

@@ -0,0 +1,25 @@
name: Test foam-core
on:
pull_request:
paths:
- 'packages/foam-core/**'
push:
paths:
- 'packages/foam-core/**'
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/setup-node@v1
- name: Install dependencies
run: yarn
- name: Lint foam-core
run: yarn workspace foam-core lint
- name: Test foam-core
run: yarn workspace foam-core test

29
.github/workflows/foam-vscode.yml vendored Normal file
View File

@@ -0,0 +1,29 @@
name: Test foam-vscode
on:
pull_request:
paths:
- 'packages/foam-vscode/**'
push:
paths:
- 'packages/foam-vscode/**'
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/setup-node@v1
- name: Install dependencies
run: yarn
- name: Lint foam-vscode
run: yarn workspace foam-vscode lint
- name: Test foam-vscode
run: yarn workspace foam-vscode test
# - name: Publish foam-vscode
# if: github.ref == 'refs/heads/master'
# run: yarn workspace foam-vscode publish-extension
# with:
# vsce_token: ${{ secrets.VSCE_TOKEN }}

49
.vscode/launch.json vendored
View File

@@ -6,10 +6,34 @@
"version": "0.2.0",
"configurations": [
{
// This task is also defined in packages/foam-vscode/.vscode/launch.json
// for when running separately outside of the monorepo environment
"name": "Run Extension",
"type": "extensionHost",
"type": "node",
"name": "vscode-jest-tests",
"request": "launch",
"runtimeArgs": ["workspace", "foam-core", "run", "test"], // ${yarnWorkspaceName} is what we're missing
"args": [
"--runInBand"
],
"runtimeExecutable": "yarn",
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"disableOptimisticBPs": true,
},
{
"name": "Debug Jest Tests",
"type": "node",
"request": "launch",
"runtimeArgs": [
"--inspect-brk",
"${workspaceRoot}/node_modules/.bin/tsdx",
"test",
],
"console": "integratedTerminal",
"cwd": "${workspaceFolder}/packages/foam-core",
"internalConsoleOptions": "neverOpen"
},
{
"name": "Run VSCode Extension",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
@@ -18,12 +42,14 @@
"outFiles": [
"${workspaceFolder}/packages/foam-vscode/out/**/*.js"
],
"preLaunchTask": "Build foam-vscode"
"preLaunchTask": "${defaultBuildTask}"
},
// @NOTE: This task is broken. VSCode e2e tests are currently disabled
// due to incompability of jest and mocha inside a typescript monorepo
// Contributions to fix this are welcome!
{
// This task is also defined in packages/foam-vscode/.vscode/launch.json
// for when running separately outside of the monorepo environment
"name": "Extension Tests",
"name": "Test VSCode Extension",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
@@ -34,16 +60,17 @@
"outFiles": [
"${workspaceFolder}/packages/foam-vscode/out/test/**/*.js"
],
"preLaunchTask": "Build foam-vscode"
"preLaunchTask": "${defaultBuildTask}"
},
{
"name": "Test Core",
"type": "node",
"request": "launch",
"name": "Workspace Manager tests",
"program": "${workspaceFolder}/node_modules/tsdx/dist/index.js",
"args": ["test"],
"cwd": "${workspaceFolder}/packages/foam-core",
"internalConsoleOptions": "openOnSessionStart"
"internalConsoleOptions": "openOnSessionStart",
"preLaunchTask": "${defaultBuildTask}"
}
]
}

29
.vscode/tasks.json vendored
View File

@@ -4,19 +4,28 @@
"version": "2.0.0",
"tasks": [
{
// This task is also defined in packages/foam-vscode/.vscode/tasks.json
// for when running separately outside of the monorepo environment
"type": "npm",
"script": "watch",
"label": "Build foam-vscode",
"path": "packages/foam-vscode",
"problemMatcher": "$tsc-watch",
"label": "watch: foam-vscode",
"type": "npm",
"script": "start:vscode",
"problemMatcher": "$tsc-watch",
"isBackground": true,
"presentation": {
"reveal": "silent",
"revealProblems": "onProblem",
"focus": true
"reveal": "always"
},
"group": {
"kind": "build",
"isDefault": true
}
},
{
"label": "test: all packages",
"type": "npm",
"script": "test",
"problemMatcher": [],
"group": {
"kind": "test",
"isDefault": true
},
}
]
}

View File

@@ -5,6 +5,7 @@
"editor.overviewRulerBorder": false,
"editor.lineHeight": 24,
"workbench.colorTheme": "Gray Matter Light",
"vscodeMarkdownNotes.noteCompletionConvention": "noExtension",
"[markdown]": {
"editor.quickSuggestions": {
"other": true,

35
docs/_layouts/foam.html Normal file
View File

@@ -0,0 +1,35 @@
---
layout: default
---
<script type="text/javascript">
// NOTE: this should be in sync with the settings/usage in the vscode extension
// atm it's just a wide superset of md extensions to cover a wide range of cases
var MD_EXT = ['.md', '.markdown', '.mdx', '.mdown', '.mkdn', '.mkd', '.mdwn', '.mdtxt', '.mdtext', '.text', '.Rmd'];
function normalizeMdLink(link) {
var url = new URL(link);
var mdFileExt = MD_EXT.find(ext => url.pathname.endsWith(ext));
if (mdFileExt) {
url.pathname = url.pathname.slice(0, mdFileExt.length * -1);
}
return url.toString();
}
window.addEventListener('DOMContentLoaded', (event) => {
document
.querySelectorAll(".markdown-body a[title]:not([href^=http])")
.forEach((a) => {
// Hack: Replace page-link with "Page Title"...
a.innerText = a.title;
// ...and normalize the links to allow html pages navigation
a.href = normalizeMdLink(a.href);
});
document.querySelectorAll(".github-only").forEach((el) => {
el.remove();
});
});
</script>
{{ content }}

View File

@@ -1,25 +1,16 @@
---
layout: default
layout: foam
---
<script async defer src="https://buttons.github.io/buttons.js"></script>
{{ content }}
<script type="text/javascript">
// Hack: Replace page-link with "Page Title"
document
.querySelectorAll(".markdown-body a[title]:not([href^=http])")
.forEach((a) => {
a.innerText = a.title;
});
document.querySelectorAll(".github-only").forEach((el) => {
el.remove();
});
<script>
window.addEventListener('DOMContentLoaded', (event) => {
var duplicateHeading = document.querySelector("h1:not(#foam)");
if (duplicateHeading && duplicateHeading.remove) {
duplicateHeading.remove();
}
});
</script>

View File

@@ -1,22 +1,9 @@
---
layout: default
layout: foam
---
{{ content }}
<script type="text/javascript">
// Hack: Replace page-link with "Page Title"
document
.querySelectorAll(".markdown-body a[title]:not([href^=http])")
.forEach((a) => {
a.innerText = a.title;
});
document.querySelectorAll(".github-only").forEach((el) => {
el.remove();
});
</script>
<script src="https://cdn.jsdelivr.net/npm/mathjax@2/MathJax.js?config=TeX-AMS-MML_HTMLorMML" type="text/javascript"></script>
<script type="text/x-mathjax-config">
MathJax.Hub.Config({

View File

@@ -1,18 +1,5 @@
---
layout: default
layout: foam
---
{{ content }}
<script type="text/javascript">
// Hack: Replace page-link with "Page Title"
document
.querySelectorAll(".markdown-body a[title]:not([href^=http])")
.forEach((a) => {
a.innerText = a.title;
});
document.querySelectorAll(".github-only").forEach((el) => {
el.remove();
});
</script>

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 521 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 407 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 KiB

63
docs/azure-devops-wiki.md Normal file
View File

@@ -0,0 +1,63 @@
# Azure DevOps Wiki
Publish your Foam workspace as an Azure DevOps wiki.
[Azure DevOps](https://azure.microsoft.com/en-us/services/devops/) is Microsoft's collaboration software for software development teams, formerly known as Team Foundation Server (TFS) and Visual Studio Team Services. It is available as an on-premise or SaaS version. The following recipe was tested with the SaaS version, but should work the same way for the on-premise.
The following recipe is written with the assumption that you already have an [Azure DevOps](https://azure.microsoft.com/en-us/services/devops/) project.
## Setup a Foam workspace
1. Generate a Foam workspace using the [foam-template project](https://github.com/foambubble/foam-template).
2. Change the remote to a git repository in Azure DevOps (Repos -> Import a Repository -> Add Clone URL with Authentication), or copy all the files into a new Azure DevOps git repository.
3. Define which document will be the wiki home page. To do that, create a file called `.order` in the Foam workspace root folder, with first line being the document filename without `.md` extension. For a project created from the Foam template, the file would look like this:
```
readme
```
4. Push the repository to remote in Azure DevOps.
## Publish repository to a wiki
1. Navigate to your Azure DevOps project in a web browser.
2. Choose **Overview** > **Wiki**. If you don't have wikis for your project, choose **Publish code as a wiki** on welcome page.
3. Choose repository with your Foam workspace, branch (usually `master` or `main`), folder (for workspace created from foam-template it is `/`), and wiki name, and press **Publish**.
A published workspace looks like this:
![Azure DevOps wiki](assets/images/azure-devops-wiki-demo.png)
There is default table of contents pane to the left of the wiki content. Here, you'll find a list of all directories that are present in your Foam workspace, and all wiki pages. Page names are derived from files names, and they are listed in alphabetical order. You may reorder pages by adding filenames without `.md` extension to `.order` file.
_Note that first entry in `.order` file defines wiki's home page._
## Update wiki
While you are pushing changes to GitHub, you won't see the wiki updated if you don't add Azure as a remote. You can push to multiple repositories simultaneously.
1. First open a terminal and check if Azure is added running: `git remote show origin`. If you don't see Azure add it in the output then follow these steps.
2. Rename your current remote (most likely named origin) to a different name by running: `git remote rename origin main`
3. You can then add the remote for your second remote repository, in this case, Azure. e.g `git remote add azure https://<YOUR_ID>@dev.azure.com/<YOUR_ID>/foam-notes/_git/foam-notes`. You can get it from: Repos->Files->Clone and copy the URL.
4. Now, you need to set up your origin remote to push to both of these. So run: `git config -e` and edit it.
5. Add the `remote origin` section to the bottom of the file with the URLs from each remote repository you'd like to push to. You'll see something like that:
```bash
[core]
...
(ignore this part)
...
[branch "master"]
remote = github
merge = refs/heads/master
[remote "github"]
url = git@github.com:username/repo.git
fetch = +refs/heads/*:refs/remotes/github/*
[remote "azure"]
url = https://<YOUR_ID>@dev.azure.com/<YOUR_ID>/foam-notes/_git/foam-notes
fetch = +refs/heads/*:refs/remotes/azure/*
[remote "origin"]
url = git@github.com:username/repo.git
url = https://<YOUR_ID>@dev.azure.com/<YOUR_ID>/foam-notes/_git/foam-notes
```
6. You can then push to both repositories by: `git push origin master` or a single one using: `git push github master` or `git push azure master`
For more information, read the [Azure DevOps documentation](https://docs.microsoft.com/en-us/azure/devops/project/wiki/publish-repo-to-wiki).

View File

@@ -9,6 +9,7 @@ When using [[wiki-links]], you can find all notes that link to a specific note i
[//begin]: # "Autogenerated link references for markdown compatibility"
[wiki-links]: wiki-links "Wiki Links"
[make-backlinks-more-prominent]: make-backlinks-more-prominent "Make Backlinks More Prominent"
[materialized-backlinks]: materialized-backlinks "Materialized Backlinks (stub)"
[roadmap]: roadmap "Roadmap"
[//end]: # "Autogenerated link references"

View File

@@ -0,0 +1,142 @@
# Capture Notes With Drafts Pro
## Context
* You use [Foam for VSCode](https://marketplace.visualstudio.com/items?itemName=foam.foam-vscode) to manage your notes
* You wish to adopt a practice such as [A writing inbox for transient and incomplete notes](https://notes.andymatuschak.org/A%20writing%20inbox%20for%20transient%20and%20incomplete%20notes)
* You wish to use [Drafts Pro](https://docs.getdrafts.com/) to capture quick notes into your Foam notes from your iOS device
## Required Extensions
* [Foam for VSCode](https://marketplace.visualstudio.com/items?itemName=foam.foam-vscode)
## Other tools
* We assume you are familiar with how to use GitHub (if you are using Foam this is implicit)
* You have an iOS device with [Drafts](https://getdrafts.com/)
* You have upgraded to [Drafts Pro](https://docs.getdrafts.com/draftspro) (needed to edit actions).
## Instructions
1. [Create a new action in Drafts](https://docs.getdrafts.com/docs/actions/editing-actions)
2. Add a single [step](https://docs.getdrafts.com/actions/steps/) of type Script
3. Edit the script adding the code from the block below
4. Edit settings at the top of the script to suit your preferences
5. Set other Action options in Drafts as you wish
6. Save the Action
7. In GitHub [create a Personal Access Token](https://github.com/settings/tokens) and give it `repo` scope - make a note of the token
8. In Drafts create a note
9. Select the action you created in steps 1-6
10. On the first run you will need to add the following information:
1. your GitHub username
2. the repository name of your Foam repo
3. the GitHub access token from step 7
4. An author name
11. Check your Github repo for a commit
12. If you are publishing your Foam to the web you may want to edit your publishing configuration to exclude inbox files - as publishing (and method) is a user choice that is beyond the scope of this recipe
## Code for Drafts Action
```javascript
// adapted from https://forums.getdrafts.com/t/script-step-post-to-github-without-working-copy/3594
// post to writing inbox in Foam digital garden
/*
* edit these lines to suit your preferences
*/
const inboxFolder = "inbox/"; // the folder in your Foam repo where notes are saved. MUST have trailing slash, except for root of repo use ''
const requiredTags = ['inbox']; // all documents will have these added in addition to tags from the Drafts app
const addLinkToInbox = true; // true = created note will have link to [[index]], false = no link
const addTimeStamp = true; // true = add a note of capture date/time at foot of note
/*
* stop editing
*/
const credential = Credential.create("GitHub garden repo", "The repo name, and its credentials, hosting your Foam notes");
credential.addTextField("username", "GitHub Username");
credential.addTextField('repo', 'Repo name');
credential.addPasswordField("key", "GitHub personal access token");
credential.addTextField('author', 'Author');
credential.authorize();
const githubKey = credential.getValue('key');
const githubUser = credential.getValue('username');
const repo = credential.getValue('repo');
const author = credential.getValue('author');
const http = HTTP.create(); // create HTTP object
const base = 'https://api.github.com';
const posttime = new Date();
const title = draft.title;
const txt = draft.processTemplate("[[line|3..]]");
const mergedTags = [...draft.tags, ...requiredTags];
const slugbase = title.toLowerCase().replace(/\s/g, "-");
const datestr = `${posttime.getFullYear()}-${pad(posttime.getMonth() + 1)}-${pad(posttime.getDate())}`;
const timestr = `${pad(posttime.getHours())}:${pad(posttime.getMinutes())}:00`;
const yr = `${posttime.getFullYear()}`;
const pdOffset = posttime.getTimezoneOffset();
const offsetChar = pdOffset >= 0 ? '-' : '+';
var pdHours = Math.floor(pdOffset/60);
console.log(pdHours);
pdHours = pdHours >= 0 ? pdHours : pdHours * -1;
console.log(pdHours);
const tzString = `${offsetChar}${pad(pdHours)}:00`;
const postdate = `${datestr}T${timestr}${tzString}`;
const slug = `${slugbase}`
const fn = `${slug}.md`;
let preamble = `# ${title} \n\n`;
mergedTags.forEach(function(item,index){
preamble += `#${item} `;
}
);
if (addLinkToInbox) {
preamble += "\n\n[[inbox]]\n";
}
preamble += "\n\n";
var doc = `${preamble}${txt}`;
if (addTimeStamp){
doc += `\n\nCaptured: ${postdate}\n`
}
const options = {
url: `https://api.github.com/repos/${githubUser}/${repo}/contents/${inboxFolder}${fn}`,
method: 'PUT',
data: {
message: `Inbox from Drafts ${datestr}`,
content: Base64.encode(doc)
},
headers: {
'Authorization': `token ${githubKey}`
}
};
var response = http.request(options);
if (response.success) {
// yay
} else {
console.log(response.statusCode);
console.log(response.error);
}
function pad(n) {
let str = String(n);
while (str.length < 2) {
str = `0${str}`;
}
return str;
}
```

View File

@@ -9,7 +9,5 @@ Create a CLI tool to allow running common Foam commands. These may include:
More commands to be added.
[//begin]: # "Autogenerated link references for markdown compatibility"
[todo]: todo "Todo"
[roadmap]: roadmap "Roadmap"
[workspace-janitor]: workspace-janitor "Workspace Janitor (stub)"
[workspace-janitor]: workspace-janitor "Janitor"
[//end]: # "Autogenerated link references"

View File

@@ -129,6 +129,4 @@ For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.
[//begin]: # "Autogenerated link references for markdown compatibility"
[todo]: todo "Todo"
[//end]: # "Autogenerated link references"

View File

@@ -2,12 +2,12 @@
> [[todo]] [[good-first-task]] This contribution guide itself could be improved 😅
Foam is open to contributions of any kind, including but not limited to code, documentation, ideas and feedback. Here are some general tips on how to get started on contributing to Foam:
Foam is open to contributions of any kind, including but not limited to code, documentation, ideas, and feedback. Here are some general tips on how to get started on contributing to Foam:
- Use Foam for yourself, figure out what could be improved.
- Check out [[roadmap]] to see what's already in the plans. I have thoughts about how to implement some of these, but open to ideas and code contributions!
- Read about our [[principles]] to understand Foam's philosophy and direction
- Read and act in accordance of our [[code-of-conduct]].
- Read and act in accordance with our [[code-of-conduct]].
- Feel free to open [GitHub issues](https://github.com/foambubble/foam/issues) to give me feedback and ideas for new features.
- Foam code and documentation live in the monorepo at [foambubble/foam](https://github.com/foambubble/foam/)
- [/docs](https://github.com/foambubble/foam/docs): documentation and [[recipes]]

View File

@@ -1,3 +1,5 @@
# Creating a New Language
## Creating a new language
What if we created a new language that made it more ergonomic to use VS Code as a Foam workspace

View File

@@ -1,7 +1,8 @@
# Creating New Notes
- Write out a new `[[wiki-link]]` and `Cmd` + `Click` to create a new file.
- `Cmd` + `Shift` + `P` (`Ctrl` + `Shift` + `P` for Windows), execute `New Note` from [VS Code Markdown Notes](<(https://marketplace.visualstudio.com/items?itemName=kortina.vscode-markdown-notes)>) and enter a **Title Case Name** to create `title-case-name.md`
- Write out a new `[[wiki-link]]` and `Cmd` + `Click` to create a new file and enter it.
- For keyboard navigation, use the 'Follow Definition' key `F12` (or [remap key binding](https://code.visualstudio.com/docs/getstarted/keybindings) to something more ergonomic)
- `Cmd` + `Shift` + `P` (`Ctrl` + `Shift` + `P` for Windows), execute `New Note` from [VS Code Markdown Notes](https://marketplace.visualstudio.com/items?itemName=kortina.vscode-markdown-notes) and enter a **Title Case Name** to create `title-case-name.md`
- Add a keyboard binding to make creating new notes easier.
- You shouldn't worry too much about categorising your notes. You can always [[search-for-notes]], and explore them using the [[graph-visualisation]].

10
docs/custom-snippets.md Normal file
View File

@@ -0,0 +1,10 @@
# Adding Custom Snippets
You can add custom snippets whilst the default set of snippets are decided by following the below steps:
1. `Cmd` + `Shift` + `P` (`Ctrl` + `Shift` + `P` for Windows), type `snippets` and select `Preferences: Configure User Snippets`.
2. The command palette will remain in focus. Search for `markdown` and select the entry entitled `markdown.json (Markdown)`.
3. A JSON file will open. You can author your own snippets using the [documentation](https://code.visualstudio.com/docs/editor/userdefinedsnippets#_create-your-own-snippets) to help you, or if you're using a snippet shared by another Foam user then you can copy and paste it in, as the below GIF demonstrates:
![Demonstrating adding a custom snippet](./assets/images/custom-snippet.gif)
To get started, you might consider replacing the entire contents of the `markdown.json` file opened by the steps above with the JSON in [this comment](https://github.com/foambubble/foam/pull/192#issuecomment-666736270).

View File

@@ -1,36 +1,46 @@
# Daily notes
The idea is to make it easier for people to be able to create Daily notes.
Automatically create a Daily Note by executing the "Foam: Open Daily Note" command. If a Daily Note for today's date already exists, the command opens the existing note.
This feature is open for discussion at [foambubble/foam#54](https://github.com/foambubble/foam/issues/54).
![Daily note feature in action](assets/images/daily-note.gif)
## Must have
## Keyboard shortcut
- Should exist as part of [foam-vscode](https://github.com/foambubble/foam/tree/master/packages/foam-vscode) extension
- A new command "Foam: Open Daily Note", which creates new file and focuses into it, or opens the existing file for today if it exists already.
- File should have a Level 1 `# Heading`
- Extension should define a hotkey for it (TBD? What do other apps do?)
The default keyboard shortcut for "Open Daily Note" is `alt`+`d`. This can be overridden using the [VS Code Keybindings editor](https://code.visualstudio.com/docs/getstarted/keybindings).
## Should have
## Configuration
- Settings to customise:
- Format of file name, default to `yyyy-mm-dd`
- Format of title
- Default to same as file name
- Allow override e.g. `MMMMM D, YYYY`, to July 8. 2020 (locale specific)
- Extension of file, default to `.md`
- Directory into which file should be created, default to `./` (workspace root)
- Should create directory if needed
By default, Daily Notes will be created in a file called `yyyy-mm-dd.md` in the workspace root, with a heading `yyyy-mm-dd`.
## Could have
- Automatically update a "daily notes" index file, newest first. Format TBD
- A custom note template .md file that could be stored in `.vscode/` directory (would supercede having to format the title)
- This could be useful for people who e.g. want there to be a format for every day's notes with fixed questions and stuff.
- Maybe we could do it with just back links or tags, by putting e.g. `[[daily]]` into the note template
- If files were named in alphabetic sortable order, and back links would display in reverse order, newest would always come on top
These settings can be overridden in your workspace or global `.vscode/settings.json` file, using the [**dateformat** date masking syntax](https://github.com/felixge/node-dateformat#mask-options):
[//begin]: # "Autogenerated link references for markdown compatibility"
[todo]: todo "Todo"
[roadmap]: roadmap "Roadmap"
[//end]: # "Autogenerated link references"
```json
"foam.openDailyNote.directory": "journal",
"foam.openDailyNote.filenameFormat": "'daily-note'-yyyy-mm-dd",
"foam.openDailyNote.fileExtension": "mdx",
"foam.openDailyNote.titleFormat": "'Journal Entry, ' dddd, mmmm d",
```
The above configuration would create a file `journal/note-2020-07-25.mdx`, with the heading `Journal Entry, Sunday, July 25`.
## Daily Note Templates
In the future, Foam may provide a functionality for specifying a template for new Daily Notes and other types of documents.
In the meantime, you can use [VS Code Snippets](https://code.visualstudio.com/docs/editor/userdefinedsnippets) for defining your own Daily Note template.
## Roam-style Automatic Daily Notes
In the future, Foam may provide an option for automatically opening your Daily Note when you open your Foam workspace.
If you want this behaviour now, you can use the excellent [Auto Run Command](https://marketplace.visualstudio.com/items?itemName=gabrielgrinberg.auto-run-command#review-details) extension to run the "Open Daily Note" command upon entering a Foam workspace by specifying the following configuration in your `.vscode/settings.json`:
```json
"auto-run-command.rules": [
{
"condition": "hasFile: .vscode/foam.json",
"command": "foam-vscode.open-daily-note",
"message": "Have a nice day!"
}
],
```

View File

@@ -12,7 +12,6 @@ You can use VS Code plugins such as [Mermaid](https://marketplace.visualstudio.c
[//begin]: # "Autogenerated link references for markdown compatibility"
[github-pages]: github-pages "Github Pages"
[good-first-task]: good-first-task "Good First Task"
[todo]: todo "Todo"
[contribution-guide]: contribution-guide "Contribution Guide"
[good-first-task]: good-first-task "Good First Task"
[//end]: # "Autogenerated link references"

View File

@@ -10,7 +10,7 @@ With this template you can
When you're ready to publish, import the GitHub repository you created with **foam-eleventy-template** into your Netlify account. (Create one if you don't have it already.)
Once that's done, all you have to do is make changes to your workspace in VS COde and push them to the main branch on GitHub. Netlify will recognize the changes, deploy them automatically and give you a link where your Foam is published.
Once that's done, all you have to do is make changes to your workspace in VS Code and push them to the main branch on GitHub. Netlify will recognize the changes, deploy them automatically and give you a link where your Foam is published.
That's it!

View File

@@ -96,10 +96,11 @@ Useful for knowing what needs to be supported. See [[feature-comparison]].
- [[foam-core-2020-07-11]]
[//begin]: # "Autogenerated link references for markdown compatibility"
[workspace-janitor]: workspace-janitor "Workspace Janitor"
[workspace-janitor]: workspace-janitor "Janitor"
[cli]: cli "Command Line Interface"
[build-vs-assemble]: build-vs-assemble "Build vs Assemble"
[wiki-links]: wiki-links "Wiki Links"
[link-reference-definitions]: link-reference-definitions "Link Reference Definitions"
[materialized-backlinks]: materialized-backlinks "Materialized Backlinks (stub)"
[todo]: todo "Todo"
[feature-comparison]: feature-comparison "Feature comparison"

View File

@@ -17,8 +17,6 @@ Here are a few specific constraints, mainly because our tooling is a bit fragmen
- **In addition to normal Markdown Links syntax you can use `[[media-wiki]]` links.** See [[wiki-links]] for more details.
[//begin]: # "Autogenerated link references for markdown compatibility"
[wiki-links]: wiki-links "Wiki Links"
[github-pages]: github-pages "Github Pages"
[decision-needed]: decision-needed "Decision Needed"
[daily-notes]: daily-notes "Daily notes"
[wiki-links]: wiki-links "Wiki Links"
[//end]: # "Autogenerated link references"

View File

@@ -0,0 +1,19 @@
# Frequently Asked Questions
> ⚠️ Foam is still in preview. Expect the experience to be a little rough.
- [Frequently Asked Questions](#frequently-asked-questions)
- [Links/Graphs/BackLinks don't work. How do I enable them?](#linksgraphsbacklinks-dont-work-how-do-i-enable-them)
## Links/Graphs/BackLinks don't work. How do I enable them?
- Ensure that you have all the [[recommended-extensions]] installed in Visual Studio Code
- Reload Visual Studio Code by running `Cmd` + `Shift` + `P` (`Ctrl` + `Shift` + `P` for Windows), type "reload" and run the **Developer: Reload Window** command to for the updated extensions take effect
- Check the formatting rules for links on [[foam-file-format]], [[wiki-links]] and [[link-formatting-and-autocompletion]]
[//begin]: # "Autogenerated link references for markdown compatibility"
[recommended-extensions]: recommended-extensions "Recommended Extensions"
[foam-file-format]: foam-file-format "Foam File Format"
[wiki-links]: wiki-links "Wiki Links"
[link-formatting-and-autocompletion]: link-formatting-and-autocompletion "Link Formatting and Autocompletion"
[//end]: # "Autogenerated link references"

51
docs/gistpad.md Normal file
View File

@@ -0,0 +1,51 @@
# GistPad
[GistPad](https://aka.ms/gistpad) is a Visual Studio Code extension that allows you to edit your GitHub gists and repos, without needing to clone anything locally. It provides support for editing Foam workspaces, complete with `[[link]]` [completion/navigation](https://github.com/vsls-contrib/gistpad#links), [daily pages](https://github.com/vsls-contrib/gistpad#daily-pages), [pasting images](https://github.com/vsls-contrib/gistpad#pasting-images-1) and [backlinks](https://github.com/vsls-contrib/gistpad#backlinks). If you'd like to persist your notes in a GitHub repository, and automatically sync changes without needing to manually commit/push/pull, then GistPad might be an option worth exploring.
<img width="700px" src="https://user-images.githubusercontent.com/116461/87234714-96ba9400-c388-11ea-92c3-544d9a3bb633.png" />
## Getting started
To start using GistPad for your Foam-based knowledge base, simply perform the following steps:
1. Download the [GistPad extension](https://aka.ms/gistpad) and then re-start Visual Studio Code
1. Run the `GistPad: Sign In` command, and provide a [GitHub token](https://github.com/settings/tokens/new) that includes the `repo` scope (and optionally `gist` and `delete_repo` scope, if you'd like to use GistPad for managing your GitHub content more holistically)
1. Run the `GistPad: Manage Repository` command and select the `Create repo from template...` or `Create private repo from template...` depending on your preference
1. Select the `Foam-style wiki` template, and then specify a name for your Foam workspace (e.g. `my-foam-notes`, `johns-knowledge-base`)
Your new repo will be created in your GitHub account, and the `Foam` welcome page will be automatically opened. If you already have an existing Foam workspace in GitHub, then when you run step #3 above, simply select or specify the name of the GitHub repository instead.
> Note: If you have any and all feedback on how GistPad can be improved to support your Foam workflow, please don't hesitate to [let us know](https://github.com/vsls-contrib/gistpad)! 👍
<img width="700px" src="https://user-images.githubusercontent.com/116461/87863222-c1b76180-c90d-11ea-87d9-04bee1c58a0d.png" />
## Managing your workspace
Once you've opened/created the Foam repository, it will appear in the `Repositories` view of the `GistPad` tab (the one with the little notebook icon). From this tree view, you can add/edit/delete/rename new pages, upload local files, as well as view the backlinks for each page (they appear as child notes of a page).
<img width="250px" src="https://user-images.githubusercontent.com/116461/87234704-83a7c400-c388-11ea-90a8-2a660bef4dc5.png" />
## Editing your workspace
When you create or open a page, you can edit the markdown content as usual, as well as [paste images](https://github.com/vsls-contrib/gistpad#pasting-images-1), and create [`[[links]]` to other pages](https://github.com/vsls-contrib/gistpad#links). When you type `[[`, you'll recieve auto-completion for the existing pages in your workspace, and you can also automatically create new pages by simply creating a link to it.
Since you're using the Visual Studio Code markdown editor, you can benefit from all of the rich language services (e.g. syntax highlighting, header collapsing), as well as the extension ecosystem (e.g. [Emojisense](https://marketplace.visualstudio.com/items?itemName=bierner.emojisense)).
## Navigating your workspace
When editing a file, you can easily navigate `[[links]]` by hovering over them to see a preview of their contents and/or `cmd+clicking` on them in order to jump to the respective page. Furthermore, when you add a link to a page, a [backlink](https://github.com/vsls-contrib/gistpad#backlinks) is automatically added to it.
You can view a page's backlinks using either of the following techniques:
1. Expanding the file's node in the `Repositories` tree, since it's child nodes will represent backlinks. This makes it easy to browse your pages and their backlinks in a single hierachical view.
1. Opening a file, and then viewing it's backlinks list at the bottom of the editor view. This makes it easy to read a page and then see its backlinks in a contextually rich way.
## Daily Pages
In addition to create arbitrary pages, you can also use GistPad for journaling or capturing [daily notes](https://github.com/vsls-contrib/gistpad#daily-pages). Simply click the calendar icon in the `Repositories` tree, which will open up the page that represents today. If the page doesn't already exist, then it will be created in the workspace before being opened.
<img width="700px" src="https://user-images.githubusercontent.com/116461/87234721-b356cc00-c388-11ea-946a-e7f9c92258a6.png" />

View File

@@ -5,5 +5,5 @@ See the backlinks of this page for good first contribution opportunities.
[[materialized-backlinks]] would help here.
[//begin]: # "Autogenerated link references for markdown compatibility"
[materialized-backlinks]: materialized-backlinks "Materialized Backlinks"
[materialized-backlinks]: materialized-backlinks "Materialized Backlinks (stub)"
[//end]: # "Autogenerated link references"

View File

@@ -6,7 +6,4 @@
![Demo of graph visualiser](assets/images/foam-navigation-demo.gif)
[//begin]: # "Autogenerated link references for markdown compatibility"
[wiki-links]: wiki-links "Wiki Links"
[todo]: todo "Todo"
[//end]: # "Autogenerated link references"

View File

@@ -1,5 +1,5 @@
# Images from your Clipboard
You can directly link and paste images that are copied to the clipboard using the [Paste
You can directly link and paste images that are copied to the clipboard using either the [Paste
Image](https://marketplace.visualstudio.com/items?itemName=mushan.vscode-paste-image)
extension.
extension, or the [Markdown Image](https://marketplace.visualstudio.com/items?itemName=hancel.markdown-image) extension. The former does not have MDX support (yet), the latter does.

View File

@@ -11,6 +11,10 @@ Uncategorised thoughts, to be added
- [Unotes](https://marketplace.visualstudio.com/items?itemName=ryanmcalister.Unotes)
- [vscode-memo](https://github.com/svsool/vscode-memo)
- [gistpad wiki](https://github.com/jevakallio/gistpad/tree/master/src/repos/wiki)
- Open in Foam
- When you want to open a Foam published website in your own VS Code, we could have a "Open in Foam" link that opens the link in VS Code via a url binding (if possible), downloads the github repo locally, and opens it as a Foam workspace.
- Every Foam could have a different theme even in the editor, so you'll see it like they see it
- UI and layout design of your workspace can become a thing
- Developer documentation
- GistPad has a good vs code contrib primer: https://github.com/jevakallio/gistpad/blob/master/CONTRIBUTING.md
- VS Code Notebooks API

View File

@@ -62,7 +62,7 @@ These instructions assume you have a GitHub account, and you have Visual Studio
<a class="github-button" href="https://github.com/foambubble/foam-template/generate" data-icon="octicon-repo-template" data-size="large" aria-label="Use this template foambubble/foam-template on GitHub">Use this template</a>
*If you want to keep your thoughts to yourself, remember to set the repository private, or if you don't want to use GitHub to host your workspace at all, choose **Download as ZIP** instead of **Use this template**.*
*If you want to keep your thoughts to yourself, remember to set the repository private, or if you don't want to use GitHub to host your workspace at all, choose [**Download as ZIP**](https://github.com/foambubble/foam-template/archive/master.zip) instead of **Use this template**.*
2. [Clone the repository locally](https://help.github.com/en/github/creating-cloning-and-archiving-repositories/cloning-a-repository) and open it in VS Code.
@@ -74,6 +74,8 @@ After setting up the repository, open `.vscode/settings.json` and edit, add or r
To learn more about how to use **Foam**, read the [[recipes]].
Getting stuck in the setup? Read the [[frequently-asked-questions]].
There are [[known-issues]], and I'm sure, many unknown issues! Please [report them on GitHub](http://github.com/foambubble/foam/issues)!
## Features
@@ -139,6 +141,21 @@ If that sounds like something you're interested in, I'd love to have you along o
<td align="center"><a href="https://github.com/nstafie"><img src="https://avatars1.githubusercontent.com/u/10801854?v=4" width="60px;" alt=""/><br /><sub><b>Nicholas Stafie</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=nstafie" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/francishamel"><img src="https://avatars3.githubusercontent.com/u/36383308?v=4" width="60px;" alt=""/><br /><sub><b>Francis Hamel</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=francishamel" title="Code">💻</a></td>
<td align="center"><a href="http://digiguru.co.uk"><img src="https://avatars1.githubusercontent.com/u/619436?v=4" width="60px;" alt=""/><br /><sub><b>digiguru</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=digiguru" title="Code">💻</a> <a href="https://github.com/foambubble/foam/commits?author=digiguru" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/chirag-singhal"><img src="https://avatars3.githubusercontent.com/u/42653703?v=4" width="60px;" alt=""/><br /><sub><b>CHIRAG SINGHAL</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=chirag-singhal" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/lostintangent"><img src="https://avatars3.githubusercontent.com/u/116461?v=4" width="60px;" alt=""/><br /><sub><b>Jonathan Carter</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=lostintangent" title="Documentation">📖</a></td>
<td align="center"><a href="https://www.synesthesia.co.uk"><img src="https://avatars3.githubusercontent.com/u/181399?v=4" width="60px;" alt=""/><br /><sub><b>Julian Elve</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=synesthesia" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/thomaskoppelaar"><img src="https://avatars3.githubusercontent.com/u/36331365?v=4" width="60px;" alt=""/><br /><sub><b>Thomas Koppelaar</b></sub></a><br /><a href="#question-thomaskoppelaar" title="Answering Questions">💬</a> <a href="https://github.com/foambubble/foam/commits?author=thomaskoppelaar" title="Code">💻</a> <a href="#userTesting-thomaskoppelaar" title="User Testing">📓</a></td>
<td align="center"><a href="http://www.akshaymehra.com"><img src="https://avatars1.githubusercontent.com/u/8671497?v=4" width="60px;" alt=""/><br /><sub><b>Akshay</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=MehraAkshay" title="Code">💻</a></td>
<td align="center"><a href="http://johnlindquist.com"><img src="https://avatars0.githubusercontent.com/u/36073?v=4" width="60px;" alt=""/><br /><sub><b>John Lindquist</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=johnlindquist" title="Documentation">📖</a></td>
<td align="center"><a href="https://ashwin.run/"><img src="https://avatars2.githubusercontent.com/u/1689183?v=4" width="60px;" alt=""/><br /><sub><b>Ashwin Ramaswami</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=epicfaace" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/Klaudioz"><img src="https://avatars1.githubusercontent.com/u/632625?v=4" width="60px;" alt=""/><br /><sub><b>Claudio Canales</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Klaudioz" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/vitaly-pevgonen"><img src="https://avatars0.githubusercontent.com/u/6272738?v=4" width="60px;" alt=""/><br /><sub><b>vitaly-pevgonen</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=vitaly-pevgonen" title="Documentation">📖</a></td>
<td align="center"><a href="https://dshemetov.github.io"><img src="https://avatars0.githubusercontent.com/u/1810426?v=4" width="60px;" alt=""/><br /><sub><b>Dmitry Shemetov</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=dshemetov" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/hooncp"><img src="https://avatars1.githubusercontent.com/u/48883554?v=4" width="60px;" alt=""/><br /><sub><b>hooncp</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=hooncp" title="Documentation">📖</a></td>
</tr>
</table>
@@ -155,11 +172,11 @@ If that sounds like something you're interested in, I'd love to have you along o
Foam is licensed under the [MIT license](license).
[//begin]: # "Autogenerated link references for markdown compatibility"
[wiki-links]: wiki-links "Wiki Links"
[graph-visualisation]: graph-visualisation "Graph visualisation"
[backlinking]: backlinking "Backlinking"
[recommended-extensions]: recommended-extensions "Recommended Extensions"
[recipes]: recipes "Recipes"
[frequently-asked-questions]: frequently-asked-questions "Frequently Asked Questions"
[known-issues]: known-issues "Known Issues"
[roadmap]: roadmap "Roadmap"
[principles]: principles "Principles"

View File

@@ -0,0 +1,75 @@
# Link Formatting and Autocompletion
When coming from Roam, Obsidian and such tools, you may be used to writing [[wiki-links]] in `[[Title Case Format]]`. Foam will eventually support this syntax (see: [foambubble/rfcs#3](https://github.com/foambubble/rfcs/pull/3)), but for the time being, we do not.
Foam relies heavily on our [[recommended-extensions]], and each extension has slightly different semantics and edge cases. This is a short guide on how to name your files, format your links, and configure your editor so that you can enjoy Foam until our implementation catches up with our aspirations.
## Short version
- Name your files in `lower-dash-case.md`.
- Read on: [How to name your files](#how-to-name-your-files)
- Use [[wiki-links]] that match the file name exactly, without file extension: `[[lower-dash-case]]`
- Read on: [How to format your links](#how-to-format-your-links)
- Read on: [How to autocomplete your links correctly](#how-to-autocomplete-your-links-correctly)
- Ensure every file has a `# Heading` element
- This will be used as your document title.
## How to name your files
As described in [[foam-file-format]], **Foam file names should not contain spaces.** Because of the [Markdown Notes](https://marketplace.visualstudio.com/items?itemName=kortina.vscode-markdown-notes) extension's default behaviour, we recommend naming your files in lower-dash-case: `foam-file-format.md`.
This means:
- All lowercase
- No spaces, punctuation or special characters
- Using dashes as word separators
- Ending with a `.md` extension
✅ Valid lower-dash-case file names include:
- `roadmap.md`
- `foam-file-format.md`
- `f-f-f-f-falling.md`
❌ Some invalid names include:
- `Roadmap.md` (Name should not be capitalised)
- `foam file format.md` (Name should not have spaces)
- `f-f-f-f-falling!.md` (Name should not have special characters)
Some of these file names may work for a subset of use cases (for example, if you don't publish your Foam site), but we'd still recommend following these rules.
As per [[foam-file-format]], we eventually want to make Foam a lot more lenient. As per our [[principles]], you should be able to focus on your work and not fight against Foam. We're not there yet, but we'll get there.
## How to format your links
Use [[wiki-links]] to link between files. Each link:
- **Should match file name exactly**: `[[foam-file-format]]`, not `[[Foam File Format]]`
- If you're reading this document on the Foam website, you might think: "That's not right!" What about links like [[foam-file-format]]! That uses spaces, capitalised letters, and everything!
- But if you look at the <a href="link-formatting-and-autocompletion.md">raw version of this document</a>, you'll see that the link is actually written as `[[foam-file-format]]`: we make it look nicer with a bit of web magic and a sprinkle of JavaScript.
- **Not include a file extension**: `[[foam-file-format]]`, not `[[foam-file-format.md]]`.
- You might ask: If I can't use the `.md` extension, why does Foam autocomplete it for me?
- The answer is: It's a setting. See [How to autocomplete your links correctly](#how-to-autocomplete-your-links-correctly) below.
## How to autocomplete your links correctly
Foam autocompletion is provided by [Markdown Notes](https://marketplace.visualstudio.com/items?itemName=kortina.vscode-markdown-notes). The default behaviour of Markdown Notes Autocomplete is to suffix `.md` to the end of suggestion, such as in the below screenshot:
![Autocomplete from Markdown Notes with file extension](./assets/images/md-notes-autocomplete-with-extension.png)
To change this behaviour, add the below to your `.vscode/settings.json` file:
```json
"vscodeMarkdownNotes.noteCompletionConvention": "noExtension"
```
Now your autocomplete will look like the below screenshot:
![Autocomplete from Markdown Notes without file extension](./assets/images/md-notes-autocomplete-no-extension.png)
If you created your Foam from the official [foam-template](https://github.com/foambubble/foam-template) project after 27th July 2020, this setting should be already correctly set.
[//begin]: # "Autogenerated link references for markdown compatibility"
[wiki-links]: wiki-links "Wiki Links"
[recommended-extensions]: recommended-extensions "Recommended Extensions"
[foam-file-format]: foam-file-format "Foam File Format"
[principles]: principles "Principles"
[//end]: # "Autogenerated link references"

View File

@@ -136,7 +136,6 @@ UI-wise, the publish targets could be picked in some similar fashion as the run/
[//begin]: # "Autogenerated link references for markdown compatibility"
[wiki-links]: wiki-links "Wiki Links"
[roadmap]: roadmap "Roadmap"
[link-reference-definitions]: link-reference-definitions "Link Reference Definitions"
[backlinking]: backlinking "Backlinking"
[//end]: # "Autogenerated link references"

View File

@@ -24,14 +24,50 @@ The three components of a link reference definition are `[link-label]: link-targ
- **link label:** The link text to match in the surrounding markdown document. This matches the inner bracket of the double-bracketed `[[wiki-link]]` notation
- **link destination** The target of the matched link
- Right now we generate link destinations without file extension. This is a choice, see [discussion here](https://foambubble.github.io/foam/wiki-links#why-dont-wiki-links-work-on-github).
- By default we generate links without extension. This can be overridden, see [Configuration](#configuration) below
- **"Link Title"** Optional title for link (The Foam template has a snippet of JavaScript to replace this on the website at runtime)
## Configuration
You can choose to generate link reference definitions with or without file extensions, depending on the target. As a rule of thumb:
- Links with file extensions work better with standard markdown-based tools, such as GitHub web UI.
- Links without file extensions work better with certain web publishing tools that treat links as literal urls and don't transform them automatically, such as the standard GitHub pages installation.
By default, Foam generates links without file extensions for legacy reasons, but this may change in future versions.
You can override this setting in your Foam workspace's `settings.json`:
- `"foam.edit.linkReferenceDefinitions": "withoutExtensions"` (default)
- `"foam.edit.linkReferenceDefinitions": "withExtensions"`
### Ignoring files
Sometimes, you may want to ignore certain files or folders, so that Foam doesn't generate link reference definitions to them.
For instance, if you're using a local instance of [Jekyll](https://jekyllrb.com/), you may find that it writes copies of each `.md` file into a `_site` directory, which may lead to Foam generating references to them instead of the original source notes.
You can ignore the `_site` directory by adding the following to your `.vscode/settings.json`:
```json
"files.exclude": {
"**/_site": true
},
"files.watcherExclude": {
"**/_site": true
},
```
After changing the setting in your workspace, you can run the [[workspace-janitor]] command to convert all existing definitions.
[[Todo]] _Implement a `foam.eclude
## Future improvements
See [[link-reference-definition-improvements]] for further discussion on current problems and potential solutions.
[//begin]: # "Autogenerated link references for markdown compatibility"
[wiki-links]: wiki-links "Wiki Links"
[roadmap]: roadmap "Roadmap"
[workspace-janitor]: workspace-janitor "Janitor"
[link-reference-definition-improvements]: link-reference-definition-improvements "Link Reference Definition Improvements"
[//end]: # "Autogenerated link references"
[//end]: # "Autogenerated link references"

View File

@@ -123,4 +123,5 @@ How others solve this:
[//begin]: # "Autogenerated link references for markdown compatibility"
[todo]: ../todo "Todo"
[Index]: ../index "Foam"
[//end]: # "Autogenerated link references"

View File

@@ -0,0 +1,36 @@
# Migrating from OneNote
This guide mostly duplicates the instructions at the repo for the PowerShell [script](https://github.com/nixsee/ConvertOneNote2MarkDown).
## Summary
The powershell script 'ConvertOneNote2MarkDown-v2.ps1' will utilize the OneNote Object Model on your workstation to convert all OneNote pages to Word documents and then utilizes PanDoc to convert the Word documents to Markdown (.md) format. It will also:
* Create a folder structure for your Notebooks and Sections.
* Process pages that are in sections at the Notebook, Section Group and 1st Nested Section Group levels.
* Allow you you choose between putting all Images in a central '/media' folder for each notebook, or in a separate '/media' folder in each folder of the hierarchy.
* Fix image references in the resulting .md files, generating relative references to the image files within the markdown document.
* A title, description, and date header will be added to each file as well.
* And more (see details at repo)!
## Usage
1. Start the OneNote application. All notebooks currently loaded in [OneNote](https://getonetastic.com/download) will be converted.
2. It is advised that you install [Onetastic](https://getonetastic.com/download) and the attached macro, which will automatically expand any collapsed paragraphs in the notebook. They won't be exported otherwise.
* To install the macro, click the New Macro Button within the Onetastic Toolbar and then select File -> Import and select the .xml macro included in the release.
* Run the macro for each Notebook that is open
3. For the next sections, it is highly recommended that you use VS Code, and its embedded PowerShell terminal, as this allows you to edit and run the script, as well as check the results of the .md output all in one window.
4. Whatever you choose, you will need to do the following:
1. Clone the script to your computer (see [here](https://git-scm.com/book/en/v2/Git-Basics-Getting-a-Git-Repository), if you're unfamiliar with git).
2. Once cloned, navigate to the repo folder. In VS Code, use File -> Add Folder to Workspace, right click on the folder in the left side bar and click [Open In Integrated Terminal](assets/images/migrating-one-note.png).
3. Run the script by executing
```.\ConvertOnenote2Markdown-v2```
* if you receive an error, try running this line to bypass security:
```Set-ExecutionPolicy Bypass -Scope Process```
* if you still have trouble, try running both Onenote and Powershell as an administrator.
5. It will ask you for the path to store the markdown folder structure. Please use an empty folder. If using VS Code, you might not be able to paste the filepath - right click on the blinking cursor and it will paste from clipboard. **Attention:** use a full absolute path for the destination.
6. Read the prompts carefully to select your desired options. If you aren't actively editing your pages in Onenote, it is HIGHLY recommended that you don't delete the intermediate word docs, as they take 80+% of the time to generate. They are stored in their own folder, out of the way. You can then quickly re-run the script with different parameters until you find what you like.
7. Sit back and wait until the process completes.
8. To stop the process at any time, press Ctrl+C.
9. If you like, you can inspect some of the .md files prior to completion. If you're not happy with the results, stop the process, delete the .md and re-run with different parameters.
10. At this point, you should be ready to load the new directory into Foam!

View File

@@ -41,9 +41,7 @@ Given the effort vs reward ratio, it's a low priority for core team, but if some
[//begin]: # "Autogenerated link references for markdown compatibility"
[todo]: todo "Todo"
[roadmap]: roadmap "Roadmap"
[build-vs-assemble]: build-vs-assemble "Build vs Assemble"
[wiki-links]: wiki-links "Wiki Links"
[workspace-janitor]: workspace-janitor "Workspace Janitor (stub)"
[workspace-janitor]: workspace-janitor "Janitor"
[//end]: # "Autogenerated link references"

View File

@@ -1,4 +0,0 @@
[//begin]: # "Autogenerated link references for markdown compatibility"
[unrelated]: unrelated "Unrelated"
[//end]: # "Autogenerated link references"

View File

@@ -0,0 +1,59 @@
# Pre-defined User Snippets
Having pre-defined user snippets would enable us to introduce Roam style commands to Foam. Consider the below snippets:
```json
{
"Zettelkasten Id": {
"scope": "markdown",
"prefix": "/id",
"description": "Zettelkasten Id",
"body": [
"${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE} ${CURRENT_HOUR}:${CURRENT_MINUTE}:${CURRENT_SECOND}"
]
},
"Current date": {
"scope": "markdown",
"prefix": "/date",
"description": "Current date",
"body": [
"${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE} ${CURRENT_HOUR}:${CURRENT_MINUTE}:${CURRENT_SECOND}"
]
}
}
```
Which would look like:
![GIF demonstrating User Snippets](./assets/images/snippets.gif)
Using snippets enables Foam users to add [[custom-snippets]] themselves so they live alongside the first-class `/commands`.
## Notes & Considerations
- VS Code supplies "commands" already via the command palette
- Consider the UX around this. Users less familiar with VS Code are more likely to be familiar with `/` to trigger a command menu. Experienced VS Code users may be more likely to favour the command palette.
- We can use `TabCompletionProvider` and `snippets` and mix them (maybe) via the following VS Code setting: `"editor.snippetSuggestions": "inline" | "top" | "bottom" | "none"`
- For more discussion, consult the PR [here](https://github.com/foambubble/foam/pull/192).
## Simplifying Markdown Syntax
Some markdown syntax is difficult for users who have never authored markdown before. Take for example a checkbox/todo. The following syntax is required:
```
- [ ] Something todo...
```
We could provide snippets that expand out into the associated markdown syntax, like in the below GIF:
![GIF demonstrating markdown snippets](./assets/images/markdown-snippets.gif)
The JSON for these snippets can be found [here](https://github.com/foambubble/foam/pull/192#issuecomment-666736270).
[//begin]: # 'Autogenerated link references for markdown compatibility'
[custom-snippets]: custom-snippets 'Adding Custom Snippets'
[//end]: # 'Autogenerated link references'
[//begin]: # 'Autogenerated link references for markdown compatibility'
[custom-snippets]: custom-snippets 'Adding Custom Snippets'
[//end]: # 'Autogenerated link references'
[//begin]: # 'Autogenerated link references for markdown compatibility'
[custom-snippets]: custom-snippets 'Adding Custom Snippets'
[//end]: # 'Autogenerated link references'

View File

@@ -36,7 +36,7 @@ This principle may seem like it contradicts [Foam wants you to own your thoughts
## Foam allows people to collaborate in discovering better ways to work, together.
- **Foam is a collection of ideas.** Foam was released to the public not to share the few good ideas in it, but to learn many good ideas from others. As you improve your own workflow, share your work on your own Foam blog.
- **Foam is open for contributions.** If you use a tool or workflow that you like that fits these principles, please contribute them back to the Foam template as [[recipes]], [[recommended-extensions]] or documentation in [this workspace](httpsL//github,com/foambubble/foam). See also: [[roadmap]] and [[contribution-guide]].
- **Foam is open for contributions.** If you use a tool or workflow that you like that fits these principles, please contribute them back to the Foam template as [[recipes]], [[recommended-extensions]] or documentation in [this workspace](https://github.com/foambubble/foam). See also: [[roadmap]] and [[contribution-guide]].
- **Foam is open source.** Feel free to fork it, improve it and remix it. Just don't sell it, as per our [license](license).
- **Foam is not Roam.** This project was inspired by Roam Research, but we're not limited by what Roam does. No idea is too big (though if it doesn't fit with Foam's core workflow, we might make it a [[recipes]] page instead).

View File

@@ -6,3 +6,7 @@
- [Dark mode](https://css-tricks.com/dark-modes-with-css/)
[//begin]: # "Autogenerated link references for markdown compatibility"
[Todo]: todo "Todo"
[//end]: # "Autogenerated link references"

View File

@@ -33,8 +33,9 @@ Guides, tips and strategies for getting the most out of your Foam workspace!
- Using [[backlinking]] for [[reference-lists]].
## Write
- Link documents with [[wiki-links]]
- Link documents with [[wiki-links]], using Foam's [[link-formatting-and-autocompletion]].
- Use shortcuts for [[creating-new-notes]]
- Instantly create and access your [[daily-notes]]
- Draw [[diagrams-in-markdown]]
- Prettify your links, [[automatically-expand-urls-to-well-titled-links]]
- Style your environment with [[custom-markdown-preview-styles]]
@@ -49,7 +50,8 @@ Guides, tips and strategies for getting the most out of your Foam workspace!
## Version control
- Quick commits with [[git-integration]]
- Quick commits with VS Code's built in [[git-integration]]
- Store your workspace in an auto-synced GitHub repo with [[gistpad]]
- Sync your GitHub repo automatically [[todo]].
## Publish
@@ -57,6 +59,7 @@ Guides, tips and strategies for getting the most out of your Foam workspace!
- Publish to [[github-pages]]
- Publish to [[gitlab-pages]]
- Publish your site with [[eleventy-and-netlify]]
- Publish to [[azure-devops-wiki]]
- Make the site your own by [[customising-styles]].
- Math support [[math-support]]
@@ -68,9 +71,7 @@ Guides, tips and strategies for getting the most out of your Foam workspace!
## Workflow
Workflow recipes wanted!
_See [[contribution-guide]] and [[how-to-write-recipes]]._
- Capture notes from Drafts app on iOS [[capture-notes-with-drafts-pro]]
## Creative ideas
@@ -95,15 +96,21 @@ _See [[contribution-guide]] and [[how-to-write-recipes]]._
[unlinked-references]: unlinked-references "Unlinked references (stub)"
[reference-lists]: reference-lists "Reference Lists"
[wiki-links]: wiki-links "Wiki Links"
[link-formatting-and-autocompletion]: link-formatting-and-autocompletion "Link Formatting and Autocompletion"
[creating-new-notes]: creating-new-notes "Creating New Notes"
[daily-notes]: daily-notes "Daily notes"
[diagrams-in-markdown]: diagrams-in-markdown "Diagrams in Markdown"
[automatically-expand-urls-to-well-titled-links]: automatically-expand-urls-to-well-titled-links "Automatically Expand URLs to Well-Titled Links"
[custom-markdown-preview-styles]: custom-markdown-preview-styles "Custom Markdown Preview Styles"
[images-from-your-clipboard]: images-from-your-clipboard "Images from your Clipboard"
[good-first-task]: good-first-task "Good First Task"
[git-integration]: git-integration "Git integration"
[gistpad]: gistpad "GistPad"
[github-pages]: github-pages "Github Pages"
[gitlab-pages]: gitlab-pages "GitLab Pages"
[eleventy-and-netlify]: eleventy-and-netlify "Eleventy and Netlify"
[azure-devops-wiki]: azure-devops-wiki "Azure DevOps Wiki"
[customising-styles]: customising-styles "Customising Styles"
[math-support]: math-support "Math Support"
[capture-notes-with-drafts-pro]: capture-notes-with-drafts-pro "Capture Notes With Drafts Pro"
[//end]: # "Autogenerated link references"

View File

@@ -12,5 +12,5 @@ Use [[backlinking]] for handy reference lists:
[//begin]: # "Autogenerated link references for markdown compatibility"
[backlinking]: backlinking "Backlinking"
[todo]: todo "Todo"
[materialized-backlinks]: materialized-backlinks "Materialized Backlinks"
[materialized-backlinks]: materialized-backlinks "Materialized Backlinks (stub)"
[//end]: # "Autogenerated link references"

View File

@@ -29,6 +29,6 @@
- Can [MDX](https://github.com/mdx-js/mdx) help us here?
[//begin]: # "Autogenerated link references for markdown compatibility"
[unlinked-references]: unlinked-references "Unlinked references"
[renaming-files]: renaming-files "Renaming files"
[unlinked-references]: unlinked-references "Unlinked references (stub)"
[renaming-files]: renaming-files "Renaming files (stub)"
[//end]: # "Autogenerated link references"

View File

@@ -35,7 +35,6 @@ If a roadmap item is a stub, **consider** opening a [GitHub issue](https://githu
- [[renaming-files]]
- [[unlinked-references]]
- [[daily-notes]]
- [[block-references]]
- [[improved-backlinking]]
- UX: [[make-backlinks-more-prominent]]
@@ -44,6 +43,7 @@ If a roadmap item is a stub, **consider** opening a [GitHub issue](https://githu
- [[git-flows-for-teams]]
- [[user-settings]]
- [[link-reference-definitions]]
- [[predefined-user-snippets]]
### Publishing
@@ -66,13 +66,18 @@ If a roadmap item is a stub, **consider** opening a [GitHub issue](https://githu
### Migration
The community is working on a number of automated scripts to help you migrate to Foam. The main work of developing such a method involves exporting your notes, converting them to the Markdown format, and then making sure that the links between notes (if you had those) still work.
- [[migrating-from-roam]]
- Discussion: [foam#55](https://github.com/foambubble/foam/issues/55)
- [[migrating-from-obsidian]]
- Discussion: [foam#46](https://github.com/foambubble/foam/issues/46)
- [[migrating-from-onenote]]
- Discussion: [foam#151](https://github.com/foambubble/foam/issues/151)
- _Migration from other tools..._
### integration
### Integration
- _Integrations to third party tools_...
### Wild ideas
@@ -84,7 +89,7 @@ If a roadmap item is a stub, **consider** opening a [GitHub issue](https://githu
[build-vs-assemble]: build-vs-assemble "Build vs Assemble"
[recipes]: recipes "Recipes"
[cli]: cli "Command Line Interface"
[workspace-janitor]: workspace-janitor "Workspace Janitor"
[workspace-janitor]: workspace-janitor "Janitor"
[contribution-guide]: contribution-guide "Contribution Guide"
[improve-default-workspace-settings]: improve-default-workspace-settings "Improve Default Workspace Settings (stub)"
[git-integration]: git-integration "Git integration"
@@ -92,12 +97,15 @@ If a roadmap item is a stub, **consider** opening a [GitHub issue](https://githu
[foam-file-format]: foam-file-format "Foam File Format"
[renaming-files]: renaming-files "Renaming files (stub)"
[unlinked-references]: unlinked-references "Unlinked references (stub)"
[daily-notes]: daily-notes "Daily notes"
[block-references]: block-references "Block References (stub)"
[improved-backlinking]: improved-backlinking "Improved Backlinking (stub)"
[make-backlinks-more-prominent]: make-backlinks-more-prominent "Make Backlinks More Prominent"
[materialized-backlinks]: materialized-backlinks "Materialized Backlinks (stub)"
[automatic-git-syncing]: automatic-git-syncing "Automatic Git Syncing (stub)"
[git-flows-for-teams]: git-flows-for-teams "Git Flows for Teams (stub)"
[user-settings]: user-settings "User Settings (stub)"
[link-reference-definitions]: link-reference-definitions "Link Reference Definitions"
[predefined-user-snippets]: predefined-user-snippets "Pre-defined User Snippets"
[officially-support-alternative-templates]: officially-support-alternative-templates "Officially Support Alternative Templates (stub)"
[improved-static-site-generation]: improved-static-site-generation "Improved Static Site Generation (stub)"
[mdx-by-default]: mdx-by-default "MDX by Default(stub)"
@@ -110,7 +118,7 @@ If a roadmap item is a stub, **consider** opening a [GitHub issue](https://githu
[web-editor]: web-editor "Web Editor (stub)"
[migrating-from-roam]: migrating-from-roam "Migrating from Roam (stub)"
[migrating-from-obsidian]: migrating-from-obsidian "Migrating from Obsidian (stub)"
[migrating-from-onenote]: migrating-from-onenote "Migrating from OneNote"
[foam-linter]: foam-linter "Foam Linter (stub)"
[refactoring-via-language-server-protocol]: refactoring-via-language-server-protocol "Refactoring via Language Server Protocol (stub)"
[make-backlinks-more-prominent]: make-backlinks-more-prominent "Make Backlinks More Prominent"
[//end]: # "Autogenerated link references"
[//end]: # "Autogenerated link references"

View File

@@ -2,8 +2,9 @@
Foam enables you to Link pages together using `[[file-name]]` annotations (i.e. `[[media-wiki]]` links).
- Both `[[file-name]]` and `[[file-name.md]]` work
- Type `[[` and start typing a file name for autocompletion.
- Note that your file names should be in `lower-dash-case.md`, and your wiki links should reference file names exactly: `[[lower-dash-case]]`, not `[[Lower Dash Case]]`.
- See [[link-formatting-and-autocompletion]] for more information, and how to setup your link autocompletions to make this easier.
- `Cmd` + `Click` ( `Ctrl` + `Click` on Windows ) on file name to navigate to file (`F12` also works while your cursor is on the file name)
- `Cmd` + `Click` ( `Ctrl` + `Click` on Windows ) on non-existent file to create that file in the workspace.
@@ -21,9 +22,11 @@ The [Foam for VSCode](https://marketplace.visualstudio.com/items?itemName=foam.f
## Read more
- [[foam-file-format]]
- [[link-formatting-and-autocompletion]]
- See [[link-reference-definition-improvements]] for further discussion on current problems and potential solutions.
[//begin]: # "Autogenerated link references for markdown compatibility"
[link-formatting-and-autocompletion]: link-formatting-and-autocompletion "Link Formatting and Autocompletion"
[link-reference-definitions]: link-reference-definitions "Link Reference Definitions"
[foam-file-format]: foam-file-format "Foam File Format"
[link-reference-definition-improvements]: link-reference-definition-improvements "Link Reference Definition Improvements"

View File

@@ -1,69 +1,40 @@
# Workspace Janitor
# Janitor
## What is it?
To store your personal knowledge graph in markdown files instead of a database, we need some additional tooling to create and maintain relationships with notes.
To store your personal knowledge graph in markdown files instead of a database, we need some additional tooling to create and maintain:
**Foam Janitor** (inspired by Andy Matuschak's [note-link-janitor](https://github.com/andymatuschak/note-link-janitor)) helps you migrate existing notes to Foam, and maintain your Foam's health over time.
- Relationships between notes
- link references for [[wiki-links]]/markdown compatibility
- [[materialized-backlinks]]
- etc.
- Format and structure of each note (e.g. title)
- Renaming and refactoring files and directories
- Linting related functionality
- File names
- Note links
- Zettelkasten linking
- Visibility for orphaned notes
Currently, Foam's Janitor helps you to:
- Ensure your [[link-reference-definitions]] are up to date
- Ensure every document has a well-formatted title (required for Markdown Links, Markdown Notes, and Foam Gatsby Template compatibility)
This is necessary:
In the future, Janitor can help you with
- Updating [[materialized-backlinks]]
- Lint, format and structure notes
- Rename and move notes around while keeping their references up to date.
- When migrating to Foam
- To maintain your workspace health over long period of time
## Using Janitor from VS Code (Experimental)
## Problem
Execute the "Foam: Run Janitor" command from the command palette.
Currently, the VS Code extension is very naive, and only updates the currently active editor. This isn't sufficient, because:
![Foam Janitor demo](assets/images/foam-janitor-demo.gif)
- For e.g. [[materialized-backlinks]] to work, files need to update in the background.
- Separation of clean vs generated workspace for publishing?
- Support for standard markdown tools
- Output to a /build directory
- Would have to implement a custom previewer
- [[todo]] **Janne Ojanaho! Write a short proposal for this.**
- If VS Code extension bugs our or is not ran, the workspace can lead in an inconsistent state
- In collaboration scenarios, two people may change the same file, causing incomplete updates
- If someone pushes changes from outside VS Code (Obsidian, Git Journal, etc.) links aren't updated
## Using Janitor from command line (Experimental)
## Proposal
> ⚠️ Improvements to this documentation are welcome!
Implement a note janitor (working title, named after Andy Matuschak's [note-link-janitor](https://github.com/andymatuschak/note-link-janitor)), which ensures all files are correctly linked and updated, no matter how changes happen.
The Janitor can be installed from [NPM](https://www.npmjs.com/) and executed as a standalone CLI tool:
Janitor should be runnable:
```sh
> npm install -g foam-cli
> foam janitor path/to/workspace
```
- From VS Code
- Replaces the existing logic in extension.ts
- In theory, we could do this using the [VS Code Tasks](https://code.visualstudio.com/docs/editor/tasks), exposing it from foam-vscode with a [TaskProvider](https://code.visualstudio.com/api/extension-guides/task-provider)
- It's not clear to me whether modifying the file in the active VS Code buffer from a background task is problematic for e.g. focus/selection management. I think this is how e.g. Prettier works, but not sure if there's some special case for the active editor.
- VS Code provides its own workspace watching functionality. Not sure it would be beneficial to use this over just chokidar-style file watching in the background
- As a pre-push Git Hook (is this a good idea?) to ensure we send fully formed note graphs, even if you had to do some editing outside vs code
- As a GitHub Action (for incoming changes via PRs and other apps like GitJournal)
- Run the "build" and push back to master/gh-pages
- As a NPM script/dependency for any other purpose
You can run the Janitor as a git hook on every commit to ensure your workspace links are up to date. This can be especially helpful if you edit your markdown documents from other apps.
## Software architecture
- It's not really clear to me whether the workspace janitor should be its own package, or if the Janitor should just be the [[cli]] package, or the [[foam-core]] package.
- Ideally the janitor should be lightweight so installing it on CI is quick
- It would be cool if it didn't have many weird node-specific dependencies, maybe it could be even ran INSIDE a mobile application?
- We don't want to pollute foam-core, but janitor might actually get quite diverse in use cases.
Decoupling things like the core janitor API from the CLI API would help potentially with situations where we might want to have a separate file watcher strategy for CLI and active VS Code workspaces (see above).
You can also run the Janitor from a GitHub action to ensure that all changes coming to your workspace are up to date. This can be useful when editing your Foam notes from mobile (i.e. via [GitJournal](https://gitjournal.io)), or your Foam has multiple contributors and you want to ensure that all changes are correctly integrated.
[//begin]: # "Autogenerated link references for markdown compatibility"
[wiki-links]: wiki-links "Wiki Links"
[link-reference-definitions]: link-reference-definitions "Link Reference Definitions"
[materialized-backlinks]: materialized-backlinks "Materialized Backlinks (stub)"
[cli]: cli "Command Line Interface"
[foam-core]: foam-core "Foam Core"
[todo]: todo "Todo"
[//end]: # "Autogenerated link references"

View File

@@ -4,5 +4,5 @@
],
"npmClient": "yarn",
"useWorkspaces": true,
"version": "0.2.0"
"version": "0.3.1"
}

View File

@@ -10,7 +10,15 @@
"packages/*"
],
"scripts": {
"watch": "yarn workspace foam-vscode watch"
"start:vscode": "yarn workspace foam-vscode vscode:start-debugging",
"build:core": "yarn workspace foam-core build",
"watch:core": "yarn workspace foam-core start",
"test:core": "yarn workspace foam-core test",
"clean": "lerna run clean",
"build": "lerna run build",
"test": "lerna run test",
"lint": "lerna run lint",
"watch": "lerna run watch --concurrency 20 --stream"
},
"devDependencies": {
"all-contributors-cli": "^6.16.1",

View File

@@ -19,7 +19,7 @@ $ npm install -g foam-cli
$ foam COMMAND
running command...
$ foam (-v|--version|version)
foam-cli/0.2.0 darwin-x64 node-v12.18.0
foam-cli/0.3.0 darwin-x64 node-v12.18.0
$ foam --help [COMMAND]
USAGE
$ foam COMMAND
@@ -28,28 +28,9 @@ USAGE
<!-- usagestop -->
# Commands
<!-- commands -->
* [`foam hello [FILE]`](#foam-hello-file)
* [`foam help [COMMAND]`](#foam-help-command)
## `foam hello [FILE]`
describe the command here
```
USAGE
$ foam hello [FILE]
OPTIONS
-f, --force
-h, --help show CLI help
-n, --name=name name to print
EXAMPLE
$ foam hello
hello world from ./src/hello.ts!
```
_See code: [src/commands/hello.ts](https://github.com/foambubble/foam/blob/v0.2.0/src/commands/hello.ts)_
* [`foam janitor [WORKSPACEPATH]`](#foam-janitor-workspacepath)
* [`foam migrate [WORKSPACEPATH]`](#foam-migrate-workspacepath)
## `foam help [COMMAND]`
@@ -67,6 +48,43 @@ OPTIONS
```
_See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v3.1.0/src/commands/help.ts)_
## `foam janitor [WORKSPACEPATH]`
Updates link references and heading across all the markdown files in the given workspaces
```
USAGE
$ foam janitor [WORKSPACEPATH]
OPTIONS
-h, --help show CLI help
-w, --without-extensions generate link reference definitions without extensions (for legacy support)
EXAMPLE
$ foam-cli janitor path-to-foam-workspace
```
_See code: [src/commands/janitor.ts](https://github.com/foambubble/foam/blob/v0.3.0/src/commands/janitor.ts)_
## `foam migrate [WORKSPACEPATH]`
Updates file names, link references and heading across all the markdown files in the given workspaces
```
USAGE
$ foam migrate [WORKSPACEPATH]
OPTIONS
-h, --help show CLI help
-w, --without-extensions generate link reference definitions without extensions (for legacy support)
EXAMPLE
$ foam-cli migrate path-to-foam-workspace
Successfully generated link references and heading!
```
_See code: [src/commands/migrate.ts](https://github.com/foambubble/foam/blob/v0.3.0/src/commands/migrate.ts)_
<!-- commandsstop -->
## Development

View File

@@ -0,0 +1,6 @@
module.exports = {
presets: [
['@babel/preset-env', {targets: {node: 'current'}}],
'@babel/preset-typescript',
],
};

View File

@@ -0,0 +1,188 @@
// For a detailed explanation regarding each configuration property, visit:
// https://jestjs.io/docs/en/configuration.html
module.exports = {
// All imported modules in your tests should be mocked automatically
// automock: false,
// Stop running tests after `n` failures
// bail: 0,
// The directory where Jest should store its cached dependency information
// cacheDirectory: "/private/var/folders/p6/5y5l8tbs1d32pq9b596lk48h0000gn/T/jest_dx",
// Automatically clear mock calls and instances between every test
clearMocks: true,
// Indicates whether the coverage information should be collected while executing the test
// collectCoverage: false,
// An array of glob patterns indicating a set of files for which coverage information should be collected
// collectCoverageFrom: undefined,
// The directory where Jest should output its coverage files
// coverageDirectory: undefined,
// An array of regexp pattern strings used to skip coverage collection
// coveragePathIgnorePatterns: [
// "/node_modules/"
// ],
// Indicates which provider should be used to instrument code for coverage
// coverageProvider: "babel",
// A list of reporter names that Jest uses when writing coverage reports
// coverageReporters: [
// "json",
// "text",
// "lcov",
// "clover"
// ],
// An object that configures minimum threshold enforcement for coverage results
// coverageThreshold: undefined,
// A path to a custom dependency extractor
// dependencyExtractor: undefined,
// Make calling deprecated APIs throw helpful error messages
// errorOnDeprecated: false,
// Force coverage collection from ignored files using an array of glob patterns
// forceCoverageMatch: [],
// A path to a module which exports an async function that is triggered once before all test suites
// globalSetup: undefined,
// A path to a module which exports an async function that is triggered once after all test suites
// globalTeardown: undefined,
// A set of global variables that need to be available in all test environments
// globals: {},
// The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
// maxWorkers: "50%",
// An array of directory names to be searched recursively up from the requiring module's location
// moduleDirectories: [
// "node_modules"
// ],
// An array of file extensions your modules use
// moduleFileExtensions: [
// "js",
// "json",
// "jsx",
// "ts",
// "tsx",
// "node"
// ],
// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
// moduleNameMapper: {},
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
// modulePathIgnorePatterns: [],
// Activates notifications for test results
// notify: false,
// An enum that specifies notification mode. Requires { notify: true }
// notifyMode: "failure-change",
// A preset that is used as a base for Jest's configuration
// preset: undefined,
// Run tests from one or more projects
// projects: undefined,
// Use this configuration option to add custom reporters to Jest
// reporters: undefined,
// Automatically reset mock state between every test
// resetMocks: false,
// Reset the module registry before running each individual test
// resetModules: false,
// A path to a custom resolver
// resolver: undefined,
// Automatically restore mock state between every test
// restoreMocks: false,
// The root directory that Jest should scan for tests and modules within
// rootDir: undefined,
// A list of paths to directories that Jest should use to search for files in
// roots: [
// "<rootDir>"
// ],
// Allows you to use a custom runner instead of Jest's default test runner
// runner: "jest-runner",
// The paths to modules that run some code to configure or set up the testing environment before each test
// setupFiles: [],
// A list of paths to modules that run some code to configure or set up the testing framework before each test
// setupFilesAfterEnv: [],
// A list of paths to snapshot serializer modules Jest should use for snapshot testing
// snapshotSerializers: [],
// The test environment that will be used for testing
testEnvironment: "node",
// Options that will be passed to the testEnvironment
// testEnvironmentOptions: {},
// Adds a location field to test results
// testLocationInResults: false,
// The glob patterns Jest uses to detect test files
// testMatch: [
// "**/__tests__/**/*.[jt]s?(x)",
// "**/?(*.)+(spec|test).[tj]s?(x)"
// ],
// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
// testPathIgnorePatterns: [
// "/node_modules/"
// ],
// The regexp pattern or array of patterns that Jest uses to detect test files
// testRegex: [],
// This option allows the use of a custom results processor
// testResultsProcessor: undefined,
// This option allows use of a custom test runner
// testRunner: "jasmine2",
// This option sets the URL for the jsdom environment. It is reflected in properties such as location.href
// testURL: "http://localhost",
// Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout"
// timers: "real",
// A map from regular expressions to paths to transformers
// transform: undefined,
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
// transformIgnorePatterns: [
// "/node_modules/"
// ],
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
// unmockedModulePathPatterns: undefined,
// Indicates whether each individual test should be reported during the run
// verbose: undefined,
// An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
// watchPathIgnorePatterns: [],
// Whether to use watchman for file crawling
// watchman: true,
};

View File

@@ -1,7 +1,7 @@
{
"name": "foam-cli",
"description": "Foam CLI",
"version": "0.2.0",
"version": "0.3.0",
"author": "Jani Eväkallio @jevakallio",
"bin": {
"foam": "./bin/run"
@@ -11,17 +11,24 @@
"@oclif/command": "^1",
"@oclif/config": "^1",
"@oclif/plugin-help": "^3",
"foam-core": "^0.3.0",
"ora": "^4.0.4",
"tslib": "^1"
},
"devDependencies": {
"@babel/core": "^7.10.4",
"@babel/preset-env": "^7.10.4",
"@babel/preset-typescript": "^7.10.4",
"@oclif/dev-cli": "^1",
"@types/node": "^10",
"babel-jest": "^26.1.0",
"chai": "^4",
"eslint": "^5.13",
"eslint-config-oclif": "^3.1",
"eslint-config-oclif-typescript": "^0.1",
"foam-core": "^0.2.0",
"globby": "^10",
"jest": "^26.1.0",
"mock-fs": "^4.12.0",
"ts-node": "^8",
"typescript": "^3.3"
},
@@ -52,11 +59,13 @@
},
"repository": "foambubble/foam",
"scripts": {
"cli": "./bin/run",
"clean": "rimraf tmp",
"build": "tsc -b",
"test": "jest",
"lint": "echo Missing lint task in CLI package",
"cli": "yarn build && ./bin/run",
"postpack": "rm -f oclif.manifest.json",
"posttest": "eslint . --ext .ts --config .eslintrc",
"prepack": "rm -rf lib && tsc -b && oclif-dev manifest && oclif-dev readme",
"test": "nyc --extension .ts mocha --forbid-only \"test/**/*.test.ts\"",
"version": "oclif-dev readme && git add README.md"
},
"types": "lib/index.d.ts"

View File

@@ -1,44 +0,0 @@
import {Command, flags} from '@oclif/command'
import { NoteGraph } from 'foam-core';
export default class Hello extends Command {
static description = 'describe the command here'
static examples = [
`$ foam hello
hello world from ./src/hello.ts!
`,
]
static flags = {
help: flags.help({char: 'h'}),
// flag with a value (-n, --name=VALUE)
name: flags.string({char: 'n', description: 'name to print'}),
// flag with no value (-f, --force)
force: flags.boolean({char: 'f'}),
}
static args = [{name: 'file'}]
async run() {
const {args, flags} = this.parse(Hello)
const name = flags.name ?? 'world'
// const wm = new NoteGraph();
// wm.addNoteFromMarkdown('page-a.md', `
// # Page A
// ## Section
// - [[page-b]]
// - [[page-c]];
// `);
// wm.addNoteFromMarkdown('page-a.md', `
// # Page B
// This references [[page-a]]`);
// console.log(wm.getNoteWithLinks('page-a'));
}
}

View File

@@ -0,0 +1,79 @@
import { Command, flags } from '@oclif/command';
import * as ora from 'ora';
import {
initializeNoteGraph,
generateLinkReferences,
generateHeading,
applyTextEdit
} from 'foam-core';
import { writeFileToDisk } from '../utils/write-file-to-disk';
import { isValidDirectory } from '../utils';
export default class Janitor extends Command {
static description =
'Updates link references and heading across all the markdown files in the given workspaces';
static examples = [
`$ foam-cli janitor path-to-foam-workspace
`,
];
static flags = {
'without-extensions': flags.boolean({
char: 'w',
description: 'generate link reference definitions without extensions (for legacy support)'
}),
help: flags.help({ char: 'h' }),
};
static args = [{ name: 'workspacePath' }];
async run() {
const spinner = ora('Reading Files').start();
const { args, flags } = this.parse(Janitor);
const { workspacePath = './' } = args;
if (isValidDirectory(workspacePath)) {
const graph = await initializeNoteGraph(workspacePath);
const notes = graph.getNotes().filter(Boolean); // removes undefined notes
spinner.succeed();
spinner.text = `${notes.length} files found`;
spinner.succeed();
// exit early if no files found.
if (notes.length === 0) {
this.exit();
}
spinner.text = 'Generating link definitions';
const fileWritePromises = notes.map(note => {
// Get edits
const heading = generateHeading(note);
const definitions = generateLinkReferences(note, graph, !flags['without-extensions']);
// apply Edits
let file = note.source.text;
file = heading ? applyTextEdit(file, heading) : file;
file = definitions ? applyTextEdit(file, definitions) : file;
if (heading || definitions) {
return writeFileToDisk(note.source.uri, file);
}
return Promise.resolve(null);
});
await Promise.all(fileWritePromises);
spinner.succeed();
spinner.succeed('Done!');
} else {
spinner.fail('Directory does not exist!');
}
}
}

View File

@@ -0,0 +1,106 @@
import { Command, flags } from '@oclif/command';
import * as ora from 'ora';
import {
initializeNoteGraph,
generateLinkReferences,
generateHeading,
getKebabCaseFileName,
applyTextEdit
} from 'foam-core';
import { writeFileToDisk } from '../utils/write-file-to-disk';
import { renameFile } from '../utils/rename-file';
import { isValidDirectory } from '../utils';
// @todo: Refactor 'migrate' and 'janitor' commands and avoid repeatition
export default class Migrate extends Command {
static description =
'Updates file names, link references and heading across all the markdown files in the given workspaces';
static examples = [
`$ foam-cli migrate path-to-foam-workspace
Successfully generated link references and heading!
`,
];
static flags = {
'without-extensions': flags.boolean({
char: 'w',
description: 'generate link reference definitions without extensions (for legacy support)'
}),
help: flags.help({ char: 'h' }),
};
static args = [{ name: 'workspacePath' }];
async run() {
const spinner = ora('Reading Files').start();
const { args, flags } = this.parse(Migrate);
const { workspacePath = './' } = args;
if (isValidDirectory(workspacePath)) {
let graph = await initializeNoteGraph(workspacePath);
let notes = graph.getNotes().filter(Boolean); // removes undefined notes
spinner.succeed();
spinner.text = `${notes.length} files found`;
spinner.succeed();
// exit early if no files found.
if (notes.length === 0) {
this.exit();
}
// Kebab case file names
const fileRename = notes.map(note => {
if (note.title != null) {
const kebabCasedFileName = getKebabCaseFileName(note.title);
if (kebabCasedFileName) {
return renameFile(note.source.uri, kebabCasedFileName);
}
}
return Promise.resolve(null);
});
await Promise.all(fileRename);
spinner.text = 'Renaming files';
// Reinitialize the graph after renaming files
graph = await initializeNoteGraph(workspacePath);
notes = graph.getNotes().filter(Boolean); // remove undefined notes
spinner.succeed();
spinner.text = 'Generating link definitions';
const fileWritePromises = await Promise.all(
notes.map(note => {
// Get edits
const heading = generateHeading(note);
const definitions = generateLinkReferences(note, graph, !flags['without-extensions']);
// apply Edits
let file = note.source.text;
file = heading ? applyTextEdit(file, heading) : file;
file = definitions ? applyTextEdit(file, definitions) : file;
if (heading || definitions) {
return writeFileToDisk(note.source.uri, file);
}
return Promise.resolve(null);
})
);
await Promise.all(fileWritePromises);
spinner.succeed();
spinner.succeed('Done!');
} else {
spinner.fail('Directory does not exist!');
}
}
}

View File

@@ -0,0 +1,4 @@
import * as fs from 'fs';
export const isValidDirectory = (path: string) =>
fs.existsSync(path) && fs.lstatSync(path).isDirectory();

View File

@@ -0,0 +1,15 @@
import * as fs from 'fs';
import * as path from 'path';
/**
*
* @param fileUri absolute path for the file that needs to renamed
* @param newFileName "new file name" without the extension
*/
export const renameFile = async (fileUri: string, newFileName: string) => {
const dirName = path.dirname(fileUri);
const extension = path.extname(fileUri);
const newFileUri = path.join(dirName, `${newFileName}${extension}`);
return fs.promises.rename(fileUri, newFileUri);
};

View File

@@ -0,0 +1,5 @@
import * as fs from 'fs';
export const writeFileToDisk = async (fileUri: string, data: string) => {
return fs.promises.writeFile(fileUri, data);
};

View File

@@ -0,0 +1,30 @@
import { renameFile } from '../src/utils/rename-file';
import * as fs from 'fs';
import mockFS from 'mock-fs';
const doesFileExist = path =>
fs.promises
.access(path)
.then(() => true)
.catch(() => false);
describe('renameFile', () => {
const fileUri = './test/oldFileName.md';
beforeAll(() => {
mockFS({ [fileUri]: '' });
});
afterAll(() => {
mockFS.restore();
});
it('should rename existing file', async () => {
expect(await doesFileExist(fileUri)).toBe(true);
renameFile(fileUri, 'new-file-name');
expect(await doesFileExist(fileUri)).toBe(false);
expect(await doesFileExist('./test/new-file-name.md')).toBe(true);
});
});

View File

@@ -0,0 +1,23 @@
import { writeFileToDisk } from '../src/utils/write-file-to-disk';
import * as fs from 'fs';
import mockFS from 'mock-fs';
describe('writeFileToDisk', () => {
const fileUri = './test-file.md';
beforeAll(() => {
mockFS({ [fileUri]: 'content in the existing file' });
});
afterAll(() => {
fs.unlinkSync(fileUri);
mockFS.restore();
});
it('should overrwrite existing file in the disk with the new data', async () => {
const expected = `content in the new file`;
await writeFileToDisk(fileUri, expected);
const actual = await fs.promises.readFile(fileUri, { encoding: 'utf8' });
expect(actual).toBe(expected);
});
});

View File

@@ -2,19 +2,21 @@
"name": "foam-core",
"author": "Jani Eväkallio",
"repository": "https://github.com/foambubble/foam",
"version": "0.2.0",
"version": "0.3.0",
"license": "MIT",
"files": [
"dist"
],
"scripts": {
"start": "tsdx watch",
"build": "tsdx build",
"clean": "rimraf dist",
"build": "tsdx build --tsconfig ./tsconfig.build.json",
"test": "tsdx test",
"lint": "tsdx lint",
"lint": "tsdx lint src test",
"watch": "tsdx watch",
"prepare": "tsdx build"
},
"devDependencies": {
"@types/github-slugger": "^1.3.0",
"@types/graphlib": "^2.1.6",
"@types/lodash": "^4.14.157",
"husky": "^4.2.5",
@@ -23,12 +25,18 @@
"typescript": "^3.9.5"
},
"dependencies": {
"detect-newline": "^3.1.0",
"github-slugger": "^1.3.0",
"glob": "^7.1.6",
"graphlib": "^2.1.8",
"lodash": "^4.17.19",
"remark-frontmatter": "^2.0.0",
"remark-parse": "^8.0.2",
"remark-wiki-link": "^0.0.4",
"title-case": "^3.0.2",
"unified": "^9.0.0",
"unist-util-visit": "^2.0.2"
"unist-util-visit": "^2.0.2",
"yaml": "^1.10.0"
},
"main": "dist/index.js",
"types": "dist/index.d.ts"

View File

@@ -0,0 +1,2 @@
export const LINK_REFERENCE_DEFINITION_HEADER = `[//begin]: # "Autogenerated link references for markdown compatibility"`;
export const LINK_REFERENCE_DEFINITION_FOOTER = `[//end]: # "Autogenerated link references"`;

View File

@@ -3,20 +3,37 @@ import { NoteGraph, Note, NoteLink } from './note-graph';
export {
createNoteFromMarkdown,
createMarkdownReferences,
stringifyMarkdownLinkReferenceDefinition,
} from './markdown-provider';
export { NoteGraph, Note, NoteLink }
export {
TextEdit,
generateHeading,
generateLinkReferences,
getKebabCaseFileName,
} from './janitor';
export { applyTextEdit } from './janitor/apply-text-edit';
export { initializeNoteGraph } from './initialize-note-graph';
export { NoteGraph, Note, NoteLink };
export {
LINK_REFERENCE_DEFINITION_HEADER,
LINK_REFERENCE_DEFINITION_FOOTER,
} from './definitions';
export interface FoamConfig {
// TODO
}
export interface Foam {
notes: NoteGraph
notes: NoteGraph;
// config: FoamConfig
}
export const createFoam = (config: FoamConfig) => ({
notes: new NoteGraph(),
config: config,
})
});

View File

@@ -0,0 +1,30 @@
import glob from 'glob';
import { promisify } from 'util';
import fs from 'fs';
import os from 'os';
import detectNewline from 'detect-newline';
import { NoteGraph } from './note-graph';
import { createNoteFromMarkdown } from './markdown-provider';
const findAllFiles = promisify(glob);
export const initializeNoteGraph = async (workspacePath: string) => {
// remove trailing slash from workspacePath if exists
if (workspacePath.substr(-1) === '/')
workspacePath = workspacePath.slice(0, -1);
const files = await findAllFiles(`${workspacePath}/**/*.md`, {});
const graph = new NoteGraph();
await Promise.all(
(await files).map(f => {
return fs.promises.readFile(f).then(data => {
const markdown = (data || '').toString();
const eol = detectNewline(markdown) || os.EOL;
graph.setNote(createNoteFromMarkdown(f, markdown, eol));
});
})
);
return graph;
};

View File

@@ -0,0 +1,18 @@
import { TextEdit } from '../index';
/**
*
* @param text text on which the textEdit will be applied
* @param textEdit
* @returns {string} text with the applied textEdit
*/
export const applyTextEdit = (text: string, textEdit: TextEdit): string => {
const characters = text.split('');
const startOffset = textEdit.range.start.offset || 0;
const endOffset = textEdit.range.end.offset || 0;
const deleteCount = endOffset - startOffset;
const textToAppend = `${textEdit.newText}`;
characters.splice(startOffset, deleteCount, textToAppend);
return characters.join('');
};

View File

@@ -0,0 +1,125 @@
import { Position } from 'unist';
import GithubSlugger from 'github-slugger';
import { Note, GraphNote, NoteGraph } from '../note-graph';
import {
LINK_REFERENCE_DEFINITION_HEADER,
LINK_REFERENCE_DEFINITION_FOOTER,
} from '../definitions';
import {
createMarkdownReferences,
stringifyMarkdownLinkReferenceDefinition,
} from '../markdown-provider';
import { getHeadingFromFileName } from '../utils';
const slugger = new GithubSlugger();
export interface TextEdit {
range: Position;
newText: string;
}
export const generateLinkReferences = (
note: GraphNote,
ng: NoteGraph,
includeExtensions: boolean
): TextEdit | null => {
if (!note) {
return null;
}
const markdownReferences = createMarkdownReferences(
ng,
note.id,
includeExtensions
);
const newReferences =
markdownReferences.length === 0
? ''
: [
LINK_REFERENCE_DEFINITION_HEADER,
...markdownReferences.map(stringifyMarkdownLinkReferenceDefinition),
LINK_REFERENCE_DEFINITION_FOOTER,
].join(note.source.eol);
if (note.definitions.length === 0) {
if (newReferences.length === 0) {
return null;
}
const padding =
note.source.end.column === 1
? note.source.eol
: `${note.source.eol}${note.source.eol}`;
return {
newText: `${padding}${newReferences}`,
range: {
start: note.source.end,
end: note.source.end,
},
};
} else {
const first = note.definitions[0];
const last = note.definitions[note.definitions.length - 1];
const oldReferences = note.definitions
.map(stringifyMarkdownLinkReferenceDefinition)
.join(note.source.eol);
if (oldReferences === newReferences) {
return null;
}
return {
// @todo: do we need to ensure new lines?
newText: `${newReferences}`,
range: {
start: first.position!.start,
end: last.position!.end,
},
};
}
};
export const generateHeading = (note: Note): TextEdit | null => {
if (!note) {
return null;
}
if (note.title) {
return null;
}
const frontmatterExists = note.source.contentStart.line !== 1;
const newLineExistsAfterFrontmatter =
frontmatterExists &&
note.source.text.split(note.source.eol)[note.source.contentStart.line - 1]
.length === 0;
const paddingStart = frontmatterExists ? note.source.eol : '';
const paddingEnd = newLineExistsAfterFrontmatter
? note.source.eol
: `${note.source.eol}${note.source.eol}`;
return {
newText: `${paddingStart}# ${getHeadingFromFileName(
note.slug
)}${paddingEnd}`,
range: {
start: note.source.contentStart,
end: note.source.contentStart,
},
};
};
/**
*
* @param fileName
* @returns null if file name is already in kebab case otherise returns
* the kebab cased file name
*/
export const getKebabCaseFileName = (fileName: string) => {
const kebabCasedFileName = slugger.slug(fileName);
return kebabCasedFileName === fileName ? null : kebabCasedFileName;
};

View File

@@ -1,57 +1,151 @@
import unified from 'unified';
import markdownParse from 'remark-parse';
import wikiLinkPlugin from 'remark-wiki-link';
import frontmatterPlugin from 'remark-frontmatter';
import { parse as parseYAML } from 'yaml';
import visit, { CONTINUE, EXIT } from 'unist-util-visit';
import { Node, Parent } from 'unist';
import { Node, Parent, Point } from 'unist';
import * as path from 'path';
import { Note, NoteLink, NoteGraph } from './note-graph';
import { dropExtension } from './utils';
import { NoteLink, NoteLinkDefinition, NoteGraph, Note } from './note-graph';
import { dropExtension, uriToSlug } from './utils';
import { ID } from './types';
let processor: unified.Processor | null = null;
const ALIAS_DIVIDER_CHAR = '|';
function parse(markdown: string): Node {
processor =
processor ||
unified()
.use(markdownParse, { gfm: true })
.use(wikiLinkPlugin);
.use(frontmatterPlugin, ['yaml'])
.use(wikiLinkPlugin, { aliasDivider: ALIAS_DIVIDER_CHAR });
return processor.parse(markdown);
}
export function createNoteFromMarkdown(uri: string, markdown: string): Note {
const filename = path.basename(uri);
const id = path.parse(filename).name;
export function createNoteFromMarkdown(
uri: string,
markdown: string,
eol: string
): Note {
const tree = parse(markdown);
let title = id;
let title: string | null = null;
visit(tree, node => {
if (node.type === 'heading' && node.depth === 1) {
title = ((node as Parent)!.children[0].value as string) || title;
}
return title === id ? CONTINUE : EXIT;
return title === null ? CONTINUE : EXIT;
});
const links: NoteLink[] = [];
const linkDefinitions: NoteLinkDefinition[] = [];
let frontmatter: any = {};
let start: Point = { line: 1, column: 1, offset: 0 }; // start position of the note
visit(tree, node => {
if (node.type === 'yaml') {
frontmatter = parseYAML(node.value as string) ?? {}; // parseYAML returns null if the frontmatter is empty
// Update the start position of the note by exluding the metadata
start = {
line: node.position!.end.line! + 1,
column: 1,
offset: node.position!.end.offset! + 1,
};
}
if (node.type === 'wikiLink') {
// links can be either in format [[text]] or [[text|alias]].
const text = node.value as string;
const alias = node.data?.alias as string;
const literalContent = markdown.substring(
node.position!.start.offset! + 2,
node.position!.end.offset! - 2
);
const hasAlias =
literalContent !== text && literalContent.includes(ALIAS_DIVIDER_CHAR);
links.push({
to: node.value as string,
text: node.value as string,
position: node.position!
type: 'wikilink',
slug: text.trim(),
alias: hasAlias ? alias.trim() : null,
literalContent,
position: node.position!,
});
}
if (node.type === 'definition') {
linkDefinitions.push({
label: node.label as string,
url: node.url as string,
title: node.title as string,
position: node.position,
});
}
});
return new Note(id, title, links, uri, markdown);
// Give precendence to the title from the frontmatter if it exists
title = frontmatter.title ?? title;
const end = tree.position!.end;
const definitions = getFoamDefinitions(linkDefinitions, end);
return {
properties: frontmatter,
slug: uriToSlug(uri),
title: title,
links: links,
definitions: definitions,
source: {
uri: uri,
text: markdown,
contentStart: start,
end: end,
eol: eol,
},
};
}
interface MarkdownReference {
linkText: string;
wikiLink: string;
pageTitle: string;
function getFoamDefinitions(
defs: NoteLinkDefinition[],
fileEndPoint: Point
): NoteLinkDefinition[] {
let previousLine = fileEndPoint.line;
let foamDefinitions = [];
// walk through each definition in reverse order
// (last one first)
for (const def of defs.reverse()) {
// if this definition is more than 2 lines above the
// previous one below it (or file end), that means we
// have exited the trailing definition block, and should bail
const start = def.position!.start.line;
if (start < previousLine - 2) {
break;
}
foamDefinitions.unshift(def);
previousLine = def.position!.end.line;
}
return foamDefinitions;
}
export function stringifyMarkdownLinkReferenceDefinition(
definition: NoteLinkDefinition
) {
let text = `[${definition.label}]: ${definition.url}`;
if (definition.title) {
text = `${text} "${definition.title}"`;
}
return text;
}
export function createMarkdownReferences(
graph: NoteGraph,
noteId: string
): MarkdownReference[] {
noteId: ID,
includeExtension: boolean
): NoteLinkDefinition[] {
const source = graph.getNote(noteId);
// Should never occur since we're already in a file,
@@ -63,33 +157,78 @@ export function createMarkdownReferences(
return [];
}
return graph
.getForwardLinks(noteId)
.map(link => {
const target = graph.getNote(link.to);
// if note doesn't exist, we can't find its links
const note = graph.getNote(noteId);
if (!note) {
return [];
}
// We are dropping links to non-existent notes here,
// but int the future we may want to surface these too
if (!target) {
console.log(
`Link '${link.to}' in '${noteId}' points to a non-existing note.`
const forwardLinks = graph.getForwardLinks(noteId);
// Try to generate a definition for each [[link]] in the note.
//
// A note may have multiple [[link]] expressions to the same target
// note with different aliases.s
//
// - note.links contains all the [[link]] expressions
// - forwardLinks contains all the edges (links) between documents
return (
note.links
.map(linkExpression => {
// find the link between this and other document
const link = forwardLinks.find(
note => note.link.slug === linkExpression.slug
);
return null;
}
const relativePath = path.relative(
path.dirname(source.path),
target.path
);
const relativePathWithoutExtension = dropExtension(relativePath);
// if other document is not found, bail
if (!link) {
return null;
}
// [wiki-link-text]: wiki-link "Page title"
return {
linkText: link.to,
wikiLink: relativePathWithoutExtension,
pageTitle: target.title,
};
})
.filter(Boolean)
.sort() as MarkdownReference[];
// find the target document
let target = graph.getNote(link.to);
// if we don't find the target by ID we search the graph by slug
if (!target) {
const candidates = graph.getNotes({ slug: linkExpression.slug });
if (candidates.length > 1) {
console.log(
`Warning: Slug ${linkExpression.slug} matches ${candidates.length} documents. Picking one.`
);
}
target = candidates.length > 0 ? candidates[0] : null;
}
// We are dropping links to non-existent notes here,
// but in the future we may want to surface these too
if (!target) {
console.log(
`Warning: Link '${link.to}' in '${noteId}' points to a non-existing note.`
);
return null;
}
const relativePath = path.relative(
path.dirname(source.source.uri),
target.source.uri
);
const pathToNote = includeExtension
? relativePath
: dropExtension(relativePath);
// [wiki-link-text]: path/to/file.md "Page title"
return {
label: linkExpression.literalContent,
url: pathToNote,
title: linkExpression.alias || target.title || target.slug,
};
})
// remove empty items
.filter(Boolean)
// sort by label, ascending
.sort((a, b) => (a!.label < b!.label ? -1 : 1)) as NoteLinkDefinition[]
);
}

View File

@@ -1,93 +1,153 @@
import { Graph, Edge } from 'graphlib';
import { Position } from 'unist';
import { Graph } from 'graphlib';
import { EventEmitter } from 'events';
import { Position, Point, URI, ID } from './types';
import { hashURI, computeRelativeURI } from './utils';
type ID = string;
export interface Link {
from: ID;
to: ID;
export interface NoteSource {
uri: URI;
text: string;
contentStart: Point;
end: Point;
eol: string;
}
export interface NoteLink {
to: ID;
text: string;
export interface WikiLink {
type: 'wikilink';
slug: string;
alias: string | null;
literalContent: string;
position: Position;
}
export class Note {
public id: ID;
public title: string;
public source: string;
public path: string;
public links: NoteLink[];
// at the moment we only model wikilink
export type NoteLink = WikiLink;
constructor(
id: ID,
title: string,
links: NoteLink[],
path: string,
source: string
) {
this.id = id;
this.title = title;
this.source = source;
this.path = path;
this.links = links;
}
export interface NoteLinkDefinition {
label: string;
url: string;
title?: string;
position?: Position;
}
export interface Note {
title: string | null;
slug: string; // note: this slug is not necessarily unique
properties: object;
// sections: NoteSection[]
// tags: NoteTag[]
links: NoteLink[];
definitions: NoteLinkDefinition[];
source: NoteSource;
}
export type GraphNote = Note & {
id: ID;
};
export interface GraphConnection {
from: ID;
to: ID;
link: NoteLink;
}
export type NoteGraphEventHandler = (e: { note: GraphNote }) => void;
export type NotesQuery = { slug: string } | { title: string };
export class NoteGraph {
private graph: Graph;
private events: EventEmitter;
private createIdFromURI: (uri: URI) => ID;
constructor() {
this.graph = new Graph();
this.events = new EventEmitter();
this.createIdFromURI = hashURI;
}
public setNote(note: Note) {
if (this.graph.hasNode(note.id)) {
(this.graph.outEdges(note.id) || []).forEach(edge => {
public setNote(note: Note): GraphNote {
const id = this.createIdFromURI(note.source.uri);
const noteExists = this.graph.hasNode(id);
if (noteExists) {
(this.graph.outEdges(id) || []).forEach(edge => {
this.graph.removeEdge(edge);
});
}
this.graph.setNode(note.id, note);
const graphNote: GraphNote = {
...note,
id: id,
};
this.graph.setNode(id, graphNote);
note.links.forEach(link => {
this.graph.setEdge(note.id, link.to, link.text);
const relativePath =
note.definitions.find(def => def.label === link.slug)?.url ?? link.slug;
const targetPath = computeRelativeURI(note.source.uri, relativePath);
const targetId = this.createIdFromURI(targetPath);
const connection: GraphConnection = {
from: graphNote.id,
to: targetId,
link: link,
};
this.graph.setEdge(graphNote.id, targetId, connection);
});
this.events.emit(noteExists ? 'update' : 'add', { note: graphNote });
return graphNote;
}
public getNotes(): Note[] {
return this.graph.nodes().map(id => this.graph.node(id));
public getNotes(query?: NotesQuery): GraphNote[] {
// prettier-ignore
const filterFn =
query == null ? (note: Note | null) => note != null
: 'slug' in query ? (note: Note | null) => note?.slug === query.slug
: 'title' in query ? (note: Note | null) => note?.title === query.title
: (note: Note | null) => note != null;
return this.graph
.nodes()
.map(id => this.graph.node(id))
.filter(filterFn);
}
public getNote(noteId: ID): Note | void {
if (this.graph.hasNode(noteId)) {
return this.graph.node(noteId);
}
throw new Error(`Note with ID [${noteId}] not found`);
public getNote(noteId: ID): GraphNote | null {
return this.graph.node(noteId) ?? null;
}
public getAllLinks(noteId: ID): Link[] {
public getNoteByURI(uri: URI): GraphNote | null {
return this.getNote(this.createIdFromURI(uri));
}
public getAllLinks(noteId: ID): GraphConnection[] {
return (this.graph.nodeEdges(noteId) || []).map(edge =>
convertEdgeToLink(edge)
this.graph.edge(edge.v, edge.w)
);
}
public getForwardLinks(noteId: ID): Link[] {
public getForwardLinks(noteId: ID): GraphConnection[] {
return (this.graph.outEdges(noteId) || []).map(edge =>
convertEdgeToLink(edge)
this.graph.edge(edge.v, edge.w)
);
}
public getBacklinks(noteId: ID): Link[] {
public getBacklinks(noteId: ID): GraphConnection[] {
return (this.graph.inEdges(noteId) || []).map(edge =>
convertEdgeToLink(edge)
this.graph.edge(edge.v, edge.w)
);
}
public unstable_onNoteAdded(callback: NoteGraphEventHandler) {
this.events.addListener('add', callback);
}
public unstable_onNoteUpdated(callback: NoteGraphEventHandler) {
this.events.addListener('update', callback);
}
public unstable_removeEventListener(callback: NoteGraphEventHandler) {
this.events.removeListener('add', callback);
this.events.removeListener('update', callback);
}
public dispose() {
this.events.removeAllListeners();
}
}
const convertEdgeToLink = (edge: Edge): Link => ({
from: edge.v,
to: edge.w,
text: edge.name || edge.w,
});

4
packages/foam-core/src/types.d.ts vendored Normal file
View File

@@ -0,0 +1,4 @@
export { Position, Point } from 'unist';
export type URI = string;
export type ID = string;

View File

@@ -1,5 +1,46 @@
import path from 'path';
import crypto from 'crypto';
import { titleCase } from 'title-case';
import GithubSlugger from 'github-slugger';
import { URI, ID } from 'types';
export const hash = (text: string) =>
crypto
.createHash('sha1')
.update(text)
.digest('hex');
export const uriToSlug = (noteUri: URI): string => {
return GithubSlugger.slug(path.parse(noteUri).name);
};
export const hashURI = (uri: URI): ID => {
return hash(path.normalize(uri));
};
export const computeRelativeURI = (
reference: URI,
relativeSlug: string
): URI => {
// if no extension is provided, use the same extension as the source file
const slug =
path.extname(relativeSlug) !== ''
? relativeSlug
: `${relativeSlug}${path.extname(reference)}`;
return path.normalize(path.join(path.dirname(reference), slug));
};
export function dropExtension(path: string): string {
const parts = path.split('.');
parts.pop();
return parts.join('.');
}
/**
*
* @param filename
* @returns title cased heading after removing special characters
*/
export const getHeadingFromFileName = (filename: string): string => {
return titleCase(filename.replace(/[^\w\s]/gi, ' '));
};

View File

@@ -0,0 +1,3 @@
# Roam Document
[[Second Roam Document]]

View File

@@ -0,0 +1 @@
# Second Roam Document

View File

@@ -0,0 +1 @@
This file is missing a title

View File

@@ -0,0 +1,11 @@
# First Document
Here's some [unrelated] content.
[unrelated]: http://unrelated.com 'This link should not be changed'
[[file-without-title]]
[//begin]: # 'Autogenerated link references for markdown compatibility'
[second-document]: second-document 'Second Document'
[//end]: # 'Autogenerated link references'

View File

@@ -0,0 +1,9 @@
# Index
This file is intentionally missing the link reference definitions
[[first-document]]
[[second-document]]
[[file-without-title]]

View File

@@ -0,0 +1,9 @@
# Second Document
This is just a link target for now.
We can use it for other things later if needed.
[//begin]: # 'Autogenerated link references for markdown compatibility'
[first-document]: first-document 'First Document'
[//end]: # 'Autogenerated link references'

View File

@@ -0,0 +1,12 @@
# Third Document
All the link references are correct in this file.
[[first-document]]
[[second-document]]
[//begin]: # "Autogenerated link references for markdown compatibility"
[first-document]: first-document "First Document"
[second-document]: second-document "Second Document"
[//end]: # "Autogenerated link references"

View File

@@ -1,151 +1,187 @@
import { NoteGraph, Note } from '../src/note-graph';
import { NoteGraph, NoteLinkDefinition, Note } from '../src/note-graph';
import { uriToSlug } from '../src/utils';
const position = {
start: { line: 0, column: 0},
end: { line: 0, column: 0}
start: { line: 1, column: 1 },
end: { line: 1, column: 1 },
};
const documentStart = position.start;
const documentEnd = position.end;
const eol = '\n';
const createTestNote = (params: {
uri: string;
title?: string;
definitions?: NoteLinkDefinition[];
links?: { slug: string }[];
text?: string;
}): Note => {
return {
properties: {},
title: params.title ?? null,
slug: uriToSlug(params.uri),
definitions: params.definitions ?? [],
links: params.links
? params.links.map(link => ({
type: 'wikilink',
slug: link.slug,
position: position,
text: 'link text',
}))
: [],
source: {
eol: eol,
end: documentEnd,
contentStart: documentStart,
uri: params.uri,
text: params.text ?? '',
},
};
};
describe('Note graph', () => {
it('Adds notes to graph', () => {
const graph = new NoteGraph();
graph.setNote(new Note('page-a', 'page-a', [], '/page-a.md', ''));
graph.setNote(new Note('page-b', 'page-b', [], '/page-b.md', ''));
graph.setNote(new Note('page-c', 'page-c', [], '/page-c.md', ''));
graph.setNote(createTestNote({ uri: '/page-a.md' }));
graph.setNote(createTestNote({ uri: '/page-b.md' }));
graph.setNote(createTestNote({ uri: '/page-c.md' }));
expect(
graph
.getNotes()
.map(n => n.id)
.map(n => n.slug)
.sort()
).toEqual(['page-a', 'page-b', 'page-c']);
});
it('Detects forward links', () => {
const graph = new NoteGraph();
graph.setNote(new Note('page-a', 'page-a', [], '/page-a.md', ''));
graph.setNote(
new Note(
'page-b',
'page-b',
[{ to: 'page-a', text: 'go', position }],
'/page-b.md',
''
)
graph.setNote(createTestNote({ uri: '/page-a.md' }));
const noteB = graph.setNote(
createTestNote({
uri: '/page-b.md',
links: [{ slug: 'page-a' }],
})
);
graph.setNote(new Note('page-c', 'page-c', [], '/page-c.md', ''));
graph.setNote(createTestNote({ uri: '/page-c.md' }));
expect(
graph
.getForwardLinks('page-b')
.map(link => link.to)
.sort()
graph.getForwardLinks(noteB.id).map(link => graph.getNote(link.to)!.slug)
).toEqual(['page-a']);
});
it('Detects backlinks', () => {
const graph = new NoteGraph();
graph.setNote(new Note('page-a', 'page-a', [], '/page-a.md', ''));
const noteA = graph.setNote(createTestNote({ uri: '/page-a.md' }));
graph.setNote(
new Note(
'page-b',
'page-b',
[{ to: 'page-a', text: 'go', position }],
'/page-b.md',
''
)
createTestNote({
uri: '/page-b.md',
links: [{ slug: 'page-a' }],
})
);
graph.setNote(new Note('page-c', 'page-c', [], '/page-c.md', ''));
graph.setNote(createTestNote({ uri: '/page-c.md' }));
expect(
graph
.getBacklinks('page-a')
.map(link => link.from)
.sort()
graph.getBacklinks(noteA.id).map(link => graph.getNote(link.from)!.slug)
).toEqual(['page-b']);
});
it('Fails when accessing non-existing node', () => {
expect(() => {
const graph = new NoteGraph();
graph.setNote(new Note('page-a', 'page-a', [], '/path-b.md', ''));
graph.getNote('non-existing');
}).toThrow();
it('Returns null when accessing non-existing node', () => {
const graph = new NoteGraph();
graph.setNote(createTestNote({ uri: 'page-a' }));
expect(graph.getNote('non-existing')).toBeNull();
});
it('Allows adding edges to non-existing documents', () => {
const graph = new NoteGraph();
graph.setNote(
new Note(
'page-a',
'page-a',
[{ to: 'non-existing', text: 'does not exist', position }],
'/path-b.md',
''
)
createTestNote({
uri: '/page-a.md',
links: [{ slug: 'non-existing' }],
})
);
expect(graph.getNote('non-existing')).toBeUndefined();
expect(graph.getNote('non-existing')).toBeNull();
});
it('Updates links when modifying note', () => {
const graph = new NoteGraph();
graph.setNote(new Note('page-a', 'page-a', [], '/page-a.md', ''));
graph.setNote(
new Note(
'page-b',
'page-b',
[{ to: 'page-a', text: 'go', position }],
'/page-b.md',
''
)
const noteA = graph.setNote(createTestNote({ uri: '/page-a.md' }));
const noteB = graph.setNote(
createTestNote({
uri: '/page-b.md',
links: [{ slug: 'page-a' }],
})
);
graph.setNote(new Note('page-c', 'page-c', [], '/page-c.md', ''));
const noteC = graph.setNote(createTestNote({ uri: '/page-c.md' }));
expect(
graph
.getForwardLinks('page-b')
.map(link => link.to)
.sort()
graph.getForwardLinks(noteB.id).map(link => graph.getNote(link.to)?.slug)
).toEqual(['page-a']);
expect(
graph
.getBacklinks('page-a')
.map(link => link.from)
.sort()
graph.getBacklinks(noteA.id).map(link => graph.getNote(link.from)?.slug)
).toEqual(['page-b']);
expect(
graph
.getBacklinks('page-c')
.map(link => link.from)
.sort()
graph.getBacklinks(noteC.id).map(link => graph.getNote(link.from)?.slug)
).toEqual([]);
graph.setNote(
new Note(
'page-b',
'page-b',
[{ to: 'page-c', text: 'go', position }],
'/path-2b.md',
''
)
createTestNote({
uri: '/page-b.md',
links: [{ slug: 'page-c' }],
})
);
expect(
graph
.getForwardLinks('page-b')
.map(link => link.to)
.sort()
graph.getForwardLinks(noteB.id).map(link => graph.getNote(link.to)?.slug)
).toEqual(['page-c']);
expect(
graph
.getBacklinks('page-a')
.map(link => link.from)
.sort()
graph.getBacklinks(noteA.id).map(link => graph.getNote(link.from)?.slug)
).toEqual([]);
expect(
graph
.getBacklinks('page-c')
.map(link => link.from)
.sort()
graph.getBacklinks(noteC.id).map(link => graph.getNote(link.from)?.slug)
).toEqual(['page-b']);
});
});
describe('Graph querying', () => {
it('returns empty set if no note is found', () => {
const graph = new NoteGraph();
graph.setNote(createTestNote({ uri: '/page-a.md' }));
expect(graph.getNotes({ slug: 'non-existing' })).toEqual([]);
expect(graph.getNotes({ title: 'non-existing' })).toEqual([]);
});
it('finds the note by slug', () => {
const graph = new NoteGraph();
const note = graph.setNote(createTestNote({ uri: '/page-a.md' }));
expect(graph.getNotes({ slug: note.slug }).length).toEqual(1);
});
it('finds a note by slug when there is more than one', () => {
const graph = new NoteGraph();
graph.setNote(createTestNote({ uri: '/dir1/page-a.md' }));
graph.setNote(createTestNote({ uri: '/dir2/page-a.md' }));
expect(graph.getNotes({ slug: 'page-a' }).length).toEqual(2);
});
it('finds a note by title', () => {
const graph = new NoteGraph();
const note = graph.setNote(
createTestNote({ uri: '/dir1/page-a.md', title: 'My Title' })
);
expect(graph.getNotes({ title: 'My Title' }).length).toEqual(1);
});
it('finds a note by title when there are several', () => {
const graph = new NoteGraph();
graph.setNote(
createTestNote({ uri: '/dir1/page-a.md', title: 'My Title' })
);
graph.setNote(
createTestNote({ uri: '/dir3/page-b.md', title: 'My Title' })
);
expect(graph.getNotes({ title: 'My Title' }).length).toEqual(2);
});
});

View File

@@ -0,0 +1,81 @@
import { applyTextEdit } from '../../src/janitor/apply-text-edit';
describe('applyTextEdit', () => {
it('should return text with applied TextEdit in the end of the string', () => {
const textEdit = {
newText: `\n 4. this is fourth line`,
range: {
start: { line: 3, column: 1, offset: 79 },
end: { line: 3, column: 1, offset: 79 },
},
};
const text = `
1. this is first line
2. this is second line
3. this is third line
`;
const expected = `
1. this is first line
2. this is second line
3. this is third line
4. this is fourth line
`;
const actual = applyTextEdit(text, textEdit);
expect(actual).toBe(expected);
});
it('should return text with applied TextEdit at the top of the string', () => {
const textEdit = {
newText: `\n 1. this is first line`,
range: {
start: { line: 0, column: 0, offset: 0 },
end: { line: 0, column: 0, offset: 0 },
},
};
const text = `
2. this is second line
3. this is third line
`;
const expected = `
1. this is first line
2. this is second line
3. this is third line
`;
const actual = applyTextEdit(text, textEdit);
expect(actual).toBe(expected);
});
it('should return text with applied TextEdit in the middle of the string', () => {
const textEdit = {
newText: `\n 2. this is the updated second line`,
range: {
start: { line: 0, column: 0, offset: 26 },
end: { line: 0, column: 0, offset: 53 },
},
};
const text = `
1. this is first line
2. this is second line
3. this is third line
`;
const expected = `
1. this is first line
2. this is the updated second line
3. this is third line
`;
const actual = applyTextEdit(text, textEdit);
expect(actual).toBe(expected);
});
});

View File

@@ -0,0 +1,44 @@
import * as path from 'path';
import { NoteGraph } from '../../src/note-graph';
import { generateHeading } from '../../src/janitor';
import { initializeNoteGraph } from '../../src/initialize-note-graph';
describe('generateHeadings', () => {
let _graph: NoteGraph;
beforeAll(async () => {
_graph = await initializeNoteGraph(path.join(__dirname, '../__scaffold__'));
});
it('should add heading to a file that does not have them', () => {
const note = _graph.getNotes({ slug: 'file-without-title' })[0];
const expected = {
newText: `# File without Title
`,
range: {
start: {
line: 1,
column: 1,
offset: 0,
},
end: {
line: 1,
column: 1,
offset: 0,
},
},
};
const actual = generateHeading(note);
expect(actual!.range.start).toEqual(expected.range.start);
expect(actual!.range.end).toEqual(expected.range.end);
expect(actual!.newText).toEqual(expected.newText);
});
it('should not cause any changes to a file that has a heading', () => {
const note = _graph.getNotes({ slug: 'index' })[0];
expect(generateHeading(note)).toBeNull();
});
});

View File

@@ -0,0 +1,110 @@
import * as path from 'path';
import { NoteGraph } from '../../src/note-graph';
import { generateLinkReferences } from '../../src/janitor';
import { initializeNoteGraph } from '../../src/initialize-note-graph';
describe('generateLinkReferences', () => {
let _graph: NoteGraph;
beforeAll(async () => {
_graph = await initializeNoteGraph(path.join(__dirname, '../__scaffold__'));
});
it('initialised test graph correctly', () => {
expect(_graph.getNotes().length).toEqual(5);
});
it('should add link references to a file that does not have them', () => {
const note = _graph.getNotes({ slug: 'index' })[0];
const expected = {
newText: `
[//begin]: # "Autogenerated link references for markdown compatibility"
[first-document]: first-document "First Document"
[second-document]: second-document "Second Document"
[file-without-title]: file-without-title "file-without-title"
[//end]: # "Autogenerated link references"`,
range: {
start: {
line: 10,
column: 1,
offset: 140,
},
end: {
line: 10,
column: 1,
offset: 140,
},
},
};
const actual = generateLinkReferences(note, _graph, false);
expect(actual!.range.start).toEqual(expected.range.start);
expect(actual!.range.end).toEqual(expected.range.end);
expect(actual!.newText).toEqual(expected.newText);
});
it('should remove link definitions from a file that has them, if no links are present', () => {
const note = _graph.getNotes({ slug: 'second-document' })[0];
const expected = {
newText: '',
range: {
start: {
line: 7,
column: 1,
offset: 105,
},
end: {
line: 9,
column: 43,
offset: 269,
},
},
};
const actual = generateLinkReferences(note, _graph, false);
expect(actual!.range.start).toEqual(expected.range.start);
expect(actual!.range.end).toEqual(expected.range.end);
expect(actual!.newText).toEqual(expected.newText);
});
it('should update link definitions if they are present but changed', () => {
const note = _graph.getNotes({ slug: 'first-document' })[0];
const expected = {
newText: `[//begin]: # "Autogenerated link references for markdown compatibility"
[file-without-title]: file-without-title "file-without-title"
[//end]: # "Autogenerated link references"`,
range: {
start: {
line: 9,
column: 1,
offset: 145,
},
end: {
line: 11,
column: 43,
offset: 312,
},
},
};
const actual = generateLinkReferences(note, _graph, false);
expect(actual!.range.start).toEqual(expected.range.start);
expect(actual!.range.end).toEqual(expected.range.end);
expect(actual!.newText).toEqual(expected.newText);
});
it('should not cause any changes if link reference definitions were up to date', () => {
const note = _graph.getNotes({ slug: 'third-document' })[0];
const expected = null;
const actual = generateLinkReferences(note, _graph, false);
expect(actual).toEqual(expected);
});
});

View File

@@ -1,4 +1,7 @@
import { createNoteFromMarkdown } from '../src/markdown-provider';
import {
createNoteFromMarkdown,
createMarkdownReferences,
} from '../src/markdown-provider';
import { NoteGraph } from '../src/note-graph';
const pageA = `
@@ -18,33 +21,161 @@ const pageC = `
# Page C
`;
const pageD = `
This file has no heading.
`;
const pageE = `
---
title: Note Title
date: 20-12-12
---
# Other Note Title
`;
const pageF = `
---
---
# Empty Frontmatter
`;
describe('Markdown loader', () => {
it('Converts markdown to notes', () => {
const graph = new NoteGraph();
graph.setNote(createNoteFromMarkdown('page-a', pageA));
graph.setNote(createNoteFromMarkdown('page-b', pageB));
graph.setNote(createNoteFromMarkdown('page-c', pageC));
graph.setNote(createNoteFromMarkdown('/page-a.md', pageA, '\n'));
graph.setNote(createNoteFromMarkdown('/page-b.md', pageB, '\n'));
graph.setNote(createNoteFromMarkdown('/page-c.md', pageC, '\n'));
expect(
graph
.getNotes()
.map(n => n.id)
.map(n => n.slug)
.sort()
).toEqual(['page-a', 'page-b', 'page-c']);
});
it('Parses wikilinks correctly', () => {
const graph = new NoteGraph();
graph.setNote(createNoteFromMarkdown('page-a', pageA));
graph.setNote(createNoteFromMarkdown('page-b', pageB));
graph.setNote(createNoteFromMarkdown('page-c', pageC));
const noteA = graph.setNote(
createNoteFromMarkdown('/page-a.md', pageA, '\n')
);
const noteB = graph.setNote(
createNoteFromMarkdown('/page-b.md', pageB, '\n')
);
graph.setNote(createNoteFromMarkdown('/page-c.md', pageC, '\n'));
expect(graph.getBacklinks('page-b').map(link => link.from)).toEqual([
'page-a',
]);
expect(graph.getForwardLinks('page-a').map(link => link.to)).toEqual([
'page-b',
'page-c',
expect(
graph.getBacklinks(noteB.id).map(link => graph.getNote(link.from)!.slug)
).toEqual(['page-a']);
expect(
graph.getForwardLinks(noteA.id).map(link => graph.getNote(link.to)!.slug)
).toEqual(['page-b', 'page-c']);
});
});
describe('Note Title', () => {
it('should initialize note title if heading exists', () => {
const graph = new NoteGraph();
const note = graph.setNote(
createNoteFromMarkdown('/page-a.md', pageA, '\n')
);
const pageANoteTitle = graph.getNote(note.id)!.title;
expect(pageANoteTitle).toBe('Page A');
});
it('should not initialize note title if heading does not exist', () => {
const graph = new NoteGraph();
const note = graph.setNote(
createNoteFromMarkdown('/page-d.md', pageD, '\n')
);
const pageANoteTitle = graph.getNote(note.id)!.title;
expect(pageANoteTitle).toBe(null);
});
it('should give precedence to frontmatter title over other headings', () => {
const graph = new NoteGraph();
const note = graph.setNote(
createNoteFromMarkdown('/page-e.md', pageE, '\n')
);
const pageENoteTitle = graph.getNote(note.id)!.title;
expect(pageENoteTitle).toBe('Note Title');
});
});
describe('frontmatter', () => {
it('should parse yaml frontmatter', () => {
const graph = new NoteGraph();
const note = graph.setNote(
createNoteFromMarkdown('/page-e.md', pageE, '\n')
);
const expected = {
title: 'Note Title',
date: '20-12-12',
};
const actual: any = graph.getNote(note.id)!.properties;
expect(actual.title).toBe(expected.title);
expect(actual.date).toBe(expected.date);
});
it('should parse empty frontmatter', () => {
const graph = new NoteGraph();
const note = graph.setNote(
createNoteFromMarkdown('/page-f.md', pageF, '\n')
);
const expected = {};
const actual = graph.getNote(note.id)!.properties;
expect(actual).toEqual(expected);
});
});
describe('wikilinks definitions', () => {
it('can generate links without file extension when includeExtension = false', () => {
const graph = new NoteGraph();
const noteA = graph.setNote(
createNoteFromMarkdown('/dir1/page-a.md', pageA, '\n')
);
graph.setNote(createNoteFromMarkdown('/dir1/page-b.md', pageB, '\n'));
graph.setNote(createNoteFromMarkdown('/dir1/page-c.md', pageC, '\n'));
const noExtRefs = createMarkdownReferences(graph, noteA.id, false);
expect(noExtRefs.map(r => r.url)).toEqual(['page-b', 'page-c']);
});
it('can generate links with file extension when includeExtension = true', () => {
const graph = new NoteGraph();
const noteA = graph.setNote(
createNoteFromMarkdown('/dir1/page-a.md', pageA, '\n')
);
graph.setNote(createNoteFromMarkdown('/dir1/page-b.md', pageB, '\n'));
graph.setNote(createNoteFromMarkdown('/dir1/page-c.md', pageC, '\n'));
const extRefs = createMarkdownReferences(graph, noteA.id, true);
expect(extRefs.map(r => r.url)).toEqual(['page-b.md', 'page-c.md']);
});
it('use relative paths', () => {
const graph = new NoteGraph();
const noteA = graph.setNote(
createNoteFromMarkdown('/dir1/page-a.md', pageA, '\n')
);
graph.setNote(createNoteFromMarkdown('/dir2/page-b.md', pageB, '\n'));
graph.setNote(createNoteFromMarkdown('/dir3/page-c.md', pageC, '\n'));
const extRefs = createMarkdownReferences(graph, noteA.id, true);
expect(extRefs.map(r => r.url)).toEqual([
'../dir2/page-b.md',
'../dir3/page-c.md',
]);
});
});

View File

@@ -0,0 +1,30 @@
import { uriToSlug, hashURI, computeRelativeURI } from '../src/utils';
describe('URI utils', () => {
it('supports various cases', () => {
expect(uriToSlug('/this/is/a/path.md')).toEqual('path');
expect(uriToSlug('../a/relative/path.md')).toEqual('path');
expect(uriToSlug('another/relative/path.md')).toEqual('path');
expect(uriToSlug('no-directory.markdown')).toEqual('no-directory');
expect(uriToSlug('many.dots.name.markdown')).toEqual('manydotsname');
});
it('normalizes URI before hashing', () => {
expect(hashURI('/this/is/a/path.md')).toEqual(
hashURI('/this/has/../is/a/path.md')
);
expect(hashURI('this/is/a/path.md')).toEqual(
hashURI('this/has/../is/a/path.md')
);
});
it('computes a relative uri using a slug', () => {
expect(computeRelativeURI('/my/file.md', '../hello.md')).toEqual(
'/hello.md'
);
expect(computeRelativeURI('/my/file.md', '../hello')).toEqual('/hello.md');
expect(computeRelativeURI('/my/file.markdown', '../hello')).toEqual(
'/hello.markdown'
);
});
});

View File

@@ -0,0 +1,6 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "es6"
}
}

View File

@@ -1,30 +1,29 @@
{
"extends": "../../tsconfig.base.json",
"include": ["src", "types"],
"compilerOptions": {
"baseUrl": "src",
"composite": true,
"declaration": true,
"declarationMap": true,
// to override config from tsconfig.base.json
"esModuleInterop": true,
"importHelpers": true,
// commonjs module format is used so that the incremental
// tsc build-mode ran during development can replace individual
// files (as opposed to generate the .cjs.development.js bundle.
//
// this is overridden in tsconfig.build.json for distribution
"module": "commonjs",
"moduleResolution": "node",
"outDir": "dist",
"rootDir": "./src",
// for references
"baseUrl": "src",
"lib": ["esnext"],
"module": "esnext",
"importHelpers": true,
"sourceMap": true,
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"moduleResolution": "node",
"esModuleInterop": true
// "paths": {
// "*": ["src/*", "node_modules/*"]
// },
// "jsx": "react",
"lib": [
"esnext"
]
},
}
"include": [
"src",
"types"
]
}

View File

@@ -1 +1 @@
declare module 'remark-wiki-link';
declare module 'remark-wiki-link';

View File

@@ -1,7 +0,0 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"dbaeumer.vscode-eslint"
]
}

View File

@@ -1,40 +0,0 @@
// A launch configuration that compiles the extension and then opens it inside a new window
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
{
"version": "0.2.0",
"configurations": [
{
// This task is also defined in ${workspaceFolder}/.vscode/launch.json
// for when running in a monorepo environment
"name": "Run Extension",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}"
],
"outFiles": [
"${workspaceFolder}/out/**/*.js"
],
"preLaunchTask": "${defaultBuildTask}"
},
{
// This task is also defined in ${workspaceFolder}/.vscode/launch.json
// for when running in a monorepo environment
"name": "Extension Tests",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}",
"--extensionTestsPath=${workspaceFolder}/out/test/suite/index"
],
"outFiles": [
"${workspaceFolder}/out/test/**/*.js"
],
"preLaunchTask": "${defaultBuildTask}"
}
]
}

Some files were not shown because too many files have changed in this diff Show More