Skip to content
This repository has been archived by the owner on Jan 20, 2022. It is now read-only.

feat: implement overrides #354

Merged
merged 2 commits into from
Dec 2, 2021
Merged

feat: implement overrides #354

merged 2 commits into from
Dec 2, 2021

Conversation

nlf
Copy link
Contributor

@nlf nlf commented Nov 22, 2021

This pull request implements overrides as described in the spec

There are a couple of todo notes for myself in the code that I'm going through my notes to find context on, so this is a draft until I get those sorted out. I wasn't able to figure out what the notes were meant to address. Since tests tell a positive story here, I'm leaving the comments as they are in case of future issues and this is ready for review

this allows a user to specify an override as $packageName where
'packageName' is a direct dependency of the root package and that
reference will be resolved to the range of the referenced dep
@nlf nlf marked this pull request as ready for review December 1, 2021 19:49
@nlf nlf requested a review from a team as a code owner December 1, 2021 19:49

const fooBazEdge = bazEdge.to.edgesOut.get('foo')
t.equal(fooBazEdge.valid, true, 'cyclical foo is valid')
t.equal(fooBazEdge.to.version, '2.0.0', 'override broke the cycle')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

way to break the cycle, buddy!

Comment on lines 39 to +42
...(edgeTo ? { to: edgeTo } : {}),
...(edge.error ? { error: edge.error } : {}),
...(edge.peerConflicted ? { peerConflicted: true } : {}),
...(override ? { overridden: override } : {}),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fwiw, this would be ever so slightly more performant:

Suggested change
...(edgeTo ? { to: edgeTo } : {}),
...(edge.error ? { error: edge.error } : {}),
...(edge.peerConflicted ? { peerConflicted: true } : {}),
...(override ? { overridden: override } : {}),
...(edgeTo && { to: edgeTo }),
...(edge.error && { error: edge.error }),
...(edge.peerConflicted && { peerConflicted: true }),
...(override && { overridden: override }),

but obv is a bit unrelated to this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good note, but yes unrelated. arborist in general could use some refactoring for things like this

Comment on lines +113 to +116
...(this.rawSpec !== this.spec ? {
rawSpec: this.rawSpec,
overridden: true,
} : {}),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
...(this.rawSpec !== this.spec ? {
rawSpec: this.rawSpec,
overridden: true,
} : {}),
...(this.rawSpec !== this.spec && {
rawSpec: this.rawSpec,
overridden: true,
}),

Comment on lines +200 to +204
if (Object.keys(overrides).length > 0) {
this.overrides = new OverrideSet({
overrides: this[_package].overrides,
})
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this mean that overrides will not always be present? it seems easier to deal with if it's always an OverrideSet, just sometimes empty.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was largely due to the nature of tests written in arborist. adding an empty OverrideSet to every node regardless of if overrides are in use breaks dozens of test snapshots. to avoid churn, i opted to omit the overrides unless they're in use. i agree it's weird though, and i do plan to remove that guard and redo the snapshots in a future pull request where it can be isolated

get spec () {
if (this.overrides && this.overrides.value && this.overrides.name === this.name) {
if (this.overrides.value.startsWith('$')) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems like a strange sentinel/prefix approach. are you envisioning that an override can't ever start with $, even tho that might be a legitimate bundledDep folder name inside node_modules?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

npm-package-arg defines $ as an invalid character in a package name, anywhere we attempt to parse a spec will explode. i'd be surprised if it worked as a bundled dep name, have you seen it work that way?

i chose the $ prefix specifically because npm-package-arg will throw when encountering it, but if that assumption doesn't hold water then we'll definitely have to come up with something else

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nope! just thinking about possibilities.

Will every prior version of npa throw on it?

It's a fine assumption, I'm just not sure why a stringly typed interface is the ideal choice.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

afaict all version of npa will throw on it.

using a string was a gut call. there are most definitely other approaches we could use here, but this was simple to parse and i knew it would be invalid as a "normal" package spec which is what i wanted.

Copy link
Contributor

@fritzy fritzy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This entire PR is ridiculously clean. Some of the testing is not ideal, but that's more of a reflection of what's already there to work with.

})

t.test('overrides a nested dependency that also exists as a direct dependency', async (t) => {
generateNocks(t, {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You use this same Nock fixture quite a bit. It could be done once and referenced.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i liked keeping the calls to generateNocks inline in the test, it's handy to have the context of what a package's dependency tree looks like right in the test that exercises it. part of the struggles with the current testing that i experienced was having to go read fixtures to find out what the dep tree was even supposed to be.

it's a first pass at introducing nock here though, and i'd wager there's lots of improvements to be made

@@ -1,5 +1,6 @@
const util = require('util')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This entire thing is just one test with a lot of assertions. Is this how we want to test data component classes?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lol it is not. i deemed it out of scope, however, to completely refactor the existing tests so i chose to add my tests in the same pattern as what existed and plan on a future refactor

@nlf nlf merged commit 02f295f into main Dec 2, 2021
@nlf nlf deleted the nlf/overrides-support branch December 2, 2021 22:37
@Haprog
Copy link

Haprog commented Dec 14, 2021

Thanks a lot for implementing this!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants