Skip to content

Commit 4253c85

Browse files
authored
Merge pull request #21 from photogabble/v1.1.0
Release 1.1.0
2 parents 654e247 + adcbeb5 commit 4253c85

File tree

86 files changed

+3758
-4376
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

86 files changed

+3758
-4376
lines changed

.github/workflows/node-test.yml

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
2-
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
3-
41
name: Node.js CI
52

63
on:
@@ -11,19 +8,17 @@ on:
118

129
jobs:
1310
build:
14-
15-
runs-on: ubuntu-latest
16-
11+
runs-on: ${{ matrix.os }}
1712
strategy:
1813
matrix:
19-
node-version: [14.x, 16.x]
20-
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
14+
node: [18.x, 20.x, 21.x]
15+
os: ["ubuntu-latest", "macos-latest", "windows-latest"]
2116
steps:
22-
- uses: actions/checkout@v2
23-
- name: Use Node.js ${{ matrix.node-version }}
24-
uses: actions/setup-node@v2
17+
- uses: actions/checkout@v4
18+
- name: Use Node.js ${{ matrix.node }}
19+
uses: actions/setup-node@v4
2520
with:
26-
node-version: ${{ matrix.node-version }}
21+
node-version: ${{ matrix.node }}
2722
cache: 'npm'
2823
- run: npm ci
2924
- run: npm test

.github/workflows/npm-publish.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,21 @@ jobs:
1111
build:
1212
runs-on: ubuntu-latest
1313
steps:
14-
- uses: actions/checkout@v3
15-
- uses: actions/setup-node@v3
14+
- uses: actions/checkout@v4
15+
- uses: actions/setup-node@v4
1616
with:
17-
node-version: 16
17+
node-version: 20
1818
- run: npm ci
1919
- run: npm test
2020

2121
publish-npm:
2222
needs: build
2323
runs-on: ubuntu-latest
2424
steps:
25-
- uses: actions/checkout@v3
26-
- uses: actions/setup-node@v3
25+
- uses: actions/checkout@v4
26+
- uses: actions/setup-node@v4
2727
with:
28-
node-version: 16
28+
node-version: 20
2929
registry-url: https://registry.npmjs.org/
3030
- run: npm ci
3131
- run: npm publish

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ node_modules/
22
npm-debug.log
33
coverage/
44
.idea
5+
index.cjs

.npmignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
.idea
2+
.gitignore
3+
.editorconfig
4+
.github/workflows/
5+
tests/
6+
coverage/

CHANGELOG.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
- Make the stub post destination configurable (#59)
11+
- Fix compatibility with Eleventy v3 (#60)
12+
- Check that Wikilinks do not contain new lines (#55)
13+
- Fix crashing bug when embedded file changed while in `--watch` mode (#56)
14+
- Wikilinks should not contain new lines (#54)
15+
- On resolving fn lookup failure, only throw error if page not found (#52)
16+
- Clear internal state before each 11ty build (#51)
17+
- Make dead link report configurable (#49)
18+
- Remove internal dependency upon slugify (#48)
19+
- Add support for custom rendering functions (#47)
20+
- Add support for referencing files by path (#44)
21+
- Bugfix use alias as link text if it's the lookup source (#42)
22+
- Bugfix HTML encode link titles (#40)
23+
- Bugfix broken dead-links lookup due to typo (#38)
24+
- Bugfix do not render embeds if the page linked doesn't exist (#35)
25+
- Bugfix do not parse links in pages excluded from collections (#30)
26+
- Bugfix do not exclude root index page
27+
- Bugfix pass 11ty page object to embed compiler function (#29)
28+
- Add inclusion of html internal links to backlink computation (#22)
29+
- Add detailed bad link report (#26)
30+
1031
## [1.0.6]
1132

1233
- Bugfix ensuring aliases value is array when treated as one (#17)
@@ -46,3 +67,4 @@ First release
4667
[1.0.4]: https://github.com/photogabble/eleventy-plugin-font-subsetting/releases/tag/v1.0.4
4768
[1.0.5]: https://github.com/photogabble/eleventy-plugin-font-subsetting/releases/tag/v1.0.5
4869
[1.0.5]: https://github.com/photogabble/eleventy-plugin-font-subsetting/releases/tag/v1.0.6
70+
[Unreleased]: https://github.com/photogabble/eleventy-plugin-interlinker/tree/v1.1.0

README.md

Lines changed: 107 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
I use [Obsidian.md](https://obsidian.md/) to draft my posts before they are published on PhotoGabble. One feature of #Obsidian that I love is interlinking between notes and being able to see the connectivity graph of each note.
55

6-
In January 2023 I wrote about how I [added Wiki Links support to Eleventy.js](https://www.photogabble.co.uk/noteworthy/adding-wiki-links-to-11ty/) and in doing so this plugin was borne. It has since been updated to include support for Obsidians [embedding files](https://help.obsidian.md/Linking+notes+and+files/Embedding+files).
6+
In January 2023 I wrote about how I [added Wiki Links support to Eleventy.js](https://www.photogabble.co.uk/noteworthy/adding-wiki-links-to-11ty/) and in doing so this plugin was borne. It has since been updated to include support for Obsidian's [embedding files](https://help.obsidian.md/Linking+notes+and+files/Embedding+files).
77

88
## Install
99

@@ -40,9 +40,12 @@ type EleventyPluginInterlinkOptions = {
4040
// that returns [UNABLE TO LOCATE EMBED].
4141
unableToLocateEmbedFn?: ErrorRenderFn,
4242

43-
// slugifyFn is used to slugify strings. If a function
44-
// isn't set then the default 11ty slugify filter is used.
45-
slugifyFn?: SlugifyFn
43+
// deadLinkReport is the desired output format of the dead link report, by default its set to 'console'
44+
deadLinkReport?: 'console' | 'json' | 'none',
45+
46+
// resolvingFns contains functions used for resolving a wikilinks output.
47+
// see the Custom Resolving Functions section below
48+
resolvingFns?: Map<string, (link: WikilinkMeta, currentPage: any, interlinker: Interlinker) => Promise<string>>
4649
}
4750
```
4851
@@ -62,16 +65,42 @@ module.exports = (eleventyConfig) => {
6265

6366
### Internal Links / Wikilinks
6467

65-
This plugin will now parse all Wiki Links formatted for example, `[[Eleventy.js Interlink Plugin]]` appears as [Eleventy.js Interlink Plugin](https://photogabble.co.uk/projects/eleventyjs-interlink-plugin/).
68+
This plugin will parse both Wikilinks and internal anchor links to build each pages inbound and outbound internal links.
69+
70+
The Wikilink format is a **page reference** wrapped in double square brackets, for example: `[[Eleventy.js Interlink Plugin]]` will appear as [Eleventy.js Interlink Plugin](https://photogabble.co.uk/projects/eleventyjs-interlink-plugin/).
71+
72+
> **NOTE**: By default this plugin will use the `title` front-matter attribute of your pages or one of the aliases (as detailed below) as the **page reference**.
6673
6774
Using the vertical bar (`|`) you can change the text used to display a link. This can be useful when you want to work a link into a sentence without using the title of the file, for example: `[[Eleventy.js Interlink Plugin|custom display text]]` appears as [custom display text](https://www.photogabble.co.uk/projects/eleventyjs-interlink-plugin/).
6875

69-
> NOTE: By default this plugin will use the `title` front-matter attribute of your pages or one of the aliases (as detailed below).
76+
### Linking to fragment identifiers
77+
78+
If you're using a plugin such as [markdown-it-anchor](https://www.npmjs.com/package/markdown-it-anchor) to add _anchor links_ to your headings, or have otherwise added them yourself. You can link to these in your pages by adding a `#` symbol to your page reference.
79+
80+
For example, `[[Three laws of motion#Second law]]`.
81+
82+
In cases where you have the `#` in the title of a page you're linking to you can escape using `/` foe example, `[[Programming in /#C, an introduction]]`.
83+
84+
### Linking to files by path
85+
86+
You can link to pages by their project path, or a path relative to the linking page, for example: `[[/blog/post-1234.md]]` would link to the page found at `/blog/post-1234` relative to the project root path, While `[[../../something.md]]` would link to a page two directories up.
7087

7188
### Aliases
7289

7390
Aliases provide you a way of referencing a file using different names, use the `aliases` property in your font matter to list one or more aliases that can be used to reference the file from a Wiki Link. For example, you might add _AI_ as an alias of a file titled _Artificial Intelligence_ which would then be linkable via `[[AI]]`.
7491

92+
These can be defined as either an array as shown below or a single alias via `aliaes: AI`.
93+
94+
```yaml
95+
---
96+
title: Artificial Intelligence
97+
aliases:
98+
- AI
99+
---
100+
```
101+
102+
Aliases should be unique identifiers, this plugin will halt the build with an error if it finds two pages sharing the same alias.
103+
75104
### Linking to Pagination generated pages
76105

77106
A common use of pagination in 11ty is [pagination of an object](https://www.11ty.dev/docs/pagination/#paging-an-object) or data file, by default these generated pages aren't included in the all pages collection and therefore are invisible to this plugin unless you set `addAllPagesToCollections: true`.
@@ -92,6 +121,29 @@ eleventyComputed:
92121
---
93122
```
94123

124+
### Custom Resolving Functions
125+
126+
Custom resolving functions can be considered pluggable extensions to the wikilink lookup and rendering logic and can be invoked by usage of a `:` character in a wikilink prefixed by the functions name, for example: `[[issue:19]]`.
127+
128+
These functions are added to the interlinker via its `resolvingFns` configuration options, for example:
129+
130+
```javascript
131+
const config = {
132+
resolvingFns: new Map([
133+
['howdy', (link, currentPage) => `Hello ${link.name}!`],
134+
['issue', (link, currentPage) => `<a href="${currentPage.data.github}/issues/${link.name}">#${link.name}</a>`],
135+
]),
136+
};
137+
```
138+
139+
When invoked the resolving function will be passed three arguments, the parsed Wikilink object (see _Wikilink Data Structure_ section below.) The linking page object from 11ty and the interlinker class instance.
140+
141+
The plugin has three internal resolving functions which are defined only if not already via the plugin config:
142+
143+
- `default`, this is the default resolving function and converts the Wikilink Data Structure directly into an HTML link
144+
- `default-embed`, this is the default embed resolving function
145+
- `404-embed`, this is invoked when the embed template is not found. This currently invokes the `unableToLocateEmbedFn` however, in a future version it will replace that config option entirely
146+
95147
### Embedding
96148

97149
Embedding files allows you to reuse content across your website while tracking what pages have used it.
@@ -144,10 +196,57 @@ You can then display this information in any way you would like, I use the below
144196
{% endif %}
145197
```
146198

199+
### Dead link Report
200+
201+
The default behaviour of this plugin is to report to the console every broken Wikilink and internal link. This behaviour is configurable via the `deadLinkReport` config option. This option accepts three values: `none`, `console` and `json` with `console` being the default.
202+
203+
Setting the value to `none` will disable the dead link report while setting it to `json` will silence console output instead writing to `.dead-links.json` within the project root folder.
204+
205+
### Page lookup logic
206+
207+
This plugin will attempt to identify the page being linked using the following steps in order:
208+
209+
1. if is path link, return `filePathStem` match state
210+
2. match file url to link href
211+
3. match file title to link identifier (name)
212+
4. match file slug to link identifier (name)
213+
5. match file based upon alias
214+
215+
### Pages Excluded from Collections
216+
217+
Due to how this plugin obtains a pages template content, all pages with `eleventyExcludeFromCollections:true` set will **NOT** be parsed by the interlinker.
218+
219+
## Wikilink Data Structure
220+
221+
```typescript
222+
type WikilinkMeta = {
223+
title: string | null
224+
name: string
225+
anchor: string | null
226+
link: string
227+
slug: string
228+
isEmbed: boolean
229+
isPath: boolean
230+
231+
// If linked page has been found in the all collection exists will be
232+
// true and page will be the 11ty page object.
233+
exists: boolean
234+
page?: any
235+
236+
// name of the resolving fn, if set it must exist
237+
resolvingFnName?: string
238+
// the resulting HTML of the resolving function
239+
content?: string
240+
241+
// href and path are loaded from the linked page
242+
href?: string
243+
path?: string
244+
}
245+
```
246+
147247
## Known Caveats
148248
149-
- This plugin doesn't implement all [Obsidians wikilink support](https://help.obsidian.md/Linking+notes+and+files/Internal+links) for example linking to a block in a note and linking to a heading in a note is not currently supported by this plugin
150-
- Doesn't identify regular internal links e.g `[Link](/some/file.md)`
249+
- This plugin doesn't implement all [Obsidian's wikilink support](https://help.obsidian.md/Linking+notes+and+files/Internal+links) for example linking to a block in a note and linking to a heading in a note is not currently supported by this plugin
151250
- Only supports embedding one note inside another, no other Obsidian file embedding functionality is currently supported by this plugin
152251
153252
## Roadmap

index.d.ts

Lines changed: 75 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,34 @@
1+
interface DeadLinks {
2+
gravestones: Map<string, Array<string>>
3+
fileSrc: string
4+
5+
setFileSrc(fileSrc: string): void
6+
7+
add(link: string): void
8+
9+
report(): void
10+
}
11+
12+
interface Parser {
13+
parseSingle(link: string, pageDirectory: PageDirectoryService, filePathStem: undefined | string): WikilinkMeta
14+
15+
parseMultiple(link: string, pageDirectory: PageDirectoryService, filePathStem: undefined | string): Array<WikilinkMeta>
16+
17+
find(document: string, pageDirectory: PageDirectoryService, filePathStem: undefined | string): Array<WikilinkMeta>
18+
}
19+
20+
interface Interlinker {
21+
opts: EleventyPluginInterlinkOptions
22+
deadLinks: DeadLinks
23+
templateConfig: any
24+
extensionMap: any
25+
rm: any,
26+
wikilinkParser: Parser & { wikiLinkRegExp: string }
27+
HTMLLinkParser: Parser & { internalLinkRegex: string }
28+
29+
compute(data: any): Promise<Array<any>>
30+
}
31+
132
type EleventyPluginInterlinkOptions = {
233
// defaultLayout is the optional default layout you would like to use for wrapping your embeds.
334
defaultLayout?: string,
@@ -10,6 +41,11 @@ type EleventyPluginInterlinkOptions = {
1041
// that embed. This will always default to `embedLayout`.
1142
layoutKey?: string,
1243

44+
// stubUrl is the href you want wikilinks to link to if their linking page is not found. By default this is set
45+
// to /stubs. Passing false will disable stub url output resulting in the wikilink being displayed directly
46+
// in the html without transformation into a html link.
47+
stubUrl?: string|false,
48+
1349
// layoutTemplateLangKey informs the template renderer which engines to use for rendering an embed's layout. This
1450
// defaults to your 11ty projects default, typically: liquid,md
1551
layoutTemplateLangKey?: string,
@@ -18,24 +54,54 @@ type EleventyPluginInterlinkOptions = {
1854
// slug that you are using. This defaults to a function that returns [UNABLE TO LOCATE EMBED].
1955
unableToLocateEmbedFn?: ErrorRenderFn,
2056

21-
// slugifyFn is used to slugify strings. If a function isn't set then the default 11ty slugify filter is used.
22-
slugifyFn?: SlugifyFn
57+
// deadLinkReport is the desired output format of the dead link report, by default its set to 'console'
58+
deadLinkReport?: 'console' | 'json' | 'none',
59+
60+
// resolvingFns is a list of resolving functions. These are invoked by a wikilink containing a `:` character
61+
// prefixed by the fn name. The page in this case is the linking page.
62+
resolvingFns?: Map<string, (link: WikilinkMeta, currentPage: any, interlinker: Interlinker) => Promise<string>>,
2363
}
2464

2565
interface ErrorRenderFn {
2666
(slug: string): string;
2767
}
2868

29-
interface SlugifyFn {
30-
(input: string): string;
69+
// Data structure for internal links identified by HTMLLinkParser.
70+
// This is a subset of WikilinkMeta.
71+
type LinkMeta = {
72+
href: string
73+
isEmbed: false
3174
}
3275

76+
// Data structure for wikilinks identified by WikiLinkParser.
3377
type WikilinkMeta = {
34-
title: string|null,
35-
name: string,
36-
link: string,
37-
slug: string,
78+
title: string | null
79+
name: string
80+
anchor: string | null
81+
link: string
3882
isEmbed: boolean
83+
isPath: boolean
84+
85+
// If linked page has been found in the all collection exists will be
86+
// true and page will be the 11ty page object.
87+
exists: boolean
88+
page?: any
89+
90+
// name of the resolving fn, if set it must exist
91+
resolvingFnName?: string
92+
// the resulting HTML of the resolving function
93+
content?: string
94+
95+
// href and path are loaded from the linked page, if the href is
96+
// false then it disables the transformation of wikilink into html link.
97+
href?: string|false
98+
path?: string
99+
}
100+
101+
interface PageDirectoryService {
102+
findByLink(link: WikilinkMeta | LinkMeta): { page: any, found: boolean, foundByAlias: boolean };
103+
104+
findByFile(file: any): any;
39105
}
40106

41-
export {EleventyPluginInterlinkOptions, SlugifyFn, WikilinkMeta};
107+
export {EleventyPluginInterlinkOptions, WikilinkMeta, LinkMeta, PageDirectoryService};

0 commit comments

Comments
 (0)