Skip to content

Commit

Permalink
feat: avoid circular refs by omitting the "refs" property of files wh…
Browse files Browse the repository at this point in the history
…en accessed from another file's refs.
  • Loading branch information
webketje committed Feb 15, 2024
1 parent 6541744 commit 1ea336d
Show file tree
Hide file tree
Showing 13 changed files with 68 additions and 7 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ When @metalsmith/refs runs, it will substitute the refs with the actual files:
}
```

The references are _references_, and not _clones_ of the targets they refer to, i.e. they will stay in sync when other plugins alter them later.
The file references are _shallowly cloned_ from the targets they refer to _at the moment they are accessed_, i.e. they will be in sync when other plugins alter them later. To avoid circular references, you cannot access `file.refs.otherFile.refs`.

As the example above shows, @metalsmith/refs will also add an `id` property to each processed file, unless you define an [explicit id](#custom-ids). By default the `id` is the file's path relative to metalsmith.source(), and with backslashes converted to forward slashes for cross-platform compatibility.

Expand Down
29 changes: 26 additions & 3 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,35 @@ function refs(options) {
file.refs[name] = resolved
break
case 'file':
resolved = srcRelPath(dirname(path), lookup)
file.refs[name] = files[resolved]
resolved = files[srcRelPath(dirname(path), lookup)]
if (resolved) {
Object.defineProperty(file.refs, name, {
get() {
// eslint-disable-next-line no-unused-vars
const { refs, ...allOthers } = resolved
return allOthers
},
set(newRef) {
resolved = newRef
}
})
}
break
case 'id':
resolved = fileList.find((f) => f[1].id === lookup)
file.refs[name] = resolved[1]
if (resolved) {
resolved = resolved[1]
Object.defineProperty(file.refs, name, {
get() {
// eslint-disable-next-line no-unused-vars
const { refs, ...allOthers } = resolved
return allOthers
},
set(newRef) {
resolved = newRef
}
})
}
break
default:
done(
Expand Down
7 changes: 6 additions & 1 deletion test/fixtures/fixed-ref/expected/one-deep/index.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
{
"id": "some-custom-id"
"id": "some-custom-id",
"refs": {
"mainscript": {
"id": "script"
}
}
}
4 changes: 2 additions & 2 deletions test/fixtures/fixed-ref/expected/script.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"id": "script",
"refs": {
"oneDeep": {
"id": "some-custom-id"
}
},
"id": "script.js"
}
}
2 changes: 2 additions & 0 deletions test/fixtures/fixed-ref/src/one-deep/index.html
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
---
id: some-custom-id
refs:
mainscript: id:script
---
1 change: 1 addition & 0 deletions test/fixtures/fixed-ref/src/script.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
id: script
# a ref to the HTML page contents
refs:
oneDeep: id:some-custom-id
Expand Down
3 changes: 3 additions & 0 deletions test/fixtures/relative-refs/expected/about/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
"refs": {
"home": {
"id": "index.html"
},
"deeplyNested": {
"id": "some/deeply/nested/file.html"
}
},
"id": "about/index.html"
Expand Down
5 changes: 5 additions & 0 deletions test/fixtures/relative-refs/expected/index.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
{
"refs": {
"deeplyNested": {
"id": "some/deeply/nested/file.html"
}
},
"id": "index.html"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"refs": {
"about": {
"id": "about/index.html"
}
},
"id": "some/deeply/nested/file.html"
}
1 change: 1 addition & 0 deletions test/fixtures/relative-refs/src/about/index.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
refs:
home: file:../index.html
deeplyNested: ../some/deeply/nested/file.html
---
4 changes: 4 additions & 0 deletions test/fixtures/relative-refs/src/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
refs:
deeplyNested: some/deeply/nested/file.html
---
4 changes: 4 additions & 0 deletions test/fixtures/relative-refs/src/some/deeply/nested/file.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
refs:
about: ../../../about/index.html
---
5 changes: 5 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ describe('@metalsmith/refs', function () {
equals(fixture('absolute-refs/build'), fixture('absolute-refs/expected'))
})

it('should resolve relative and circular refs', async function () {
await Metalsmith(fixture('relative-refs')).env('DEBUG', process.env.DEBUG).use(plugin()).use(toJSON).build()
equals(fixture('relative-refs/build'), fixture('relative-refs/expected'))
})

it('should resolve refs as "file:" when no protocol is given', async function () {
await Metalsmith(fixture('protocol-file-shortcut'))
.env('DEBUG', process.env.DEBUG)
Expand Down

0 comments on commit 1ea336d

Please sign in to comment.