Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
6f6b82f
consolidate constructor
wraithgar Oct 13, 2025
7302a2d
inline #resolveLinks
wraithgar Oct 13, 2025
bebecf7
roll load-virtual up into build-ideal-tree
wraithgar Oct 13, 2025
9cd02d7
tech debt alert
wraithgar Oct 13, 2025
37e2c03
move to internal property
wraithgar Oct 13, 2025
ab8adb1
consolidate constructor
wraithgar Oct 13, 2025
295aa7a
roll load-actual up into build-ideal-tree
wraithgar Oct 13, 2025
b1d8ca3
move to internal property
wraithgar Oct 13, 2025
9b8a5ce
inline single use function
wraithgar Oct 13, 2025
e4c5c98
remove unused param from function call
wraithgar Oct 14, 2025
8e9ae56
linting
wraithgar Oct 14, 2025
03b86f3
inline #linkFromSpec
wraithgar Oct 14, 2025
9cca501
inline #resolveLinks (the other one)
wraithgar Oct 14, 2025
c0ddb10
inline #resolveNodes
wraithgar Oct 14, 2025
4405457
comment cleanup, require cleanup
wraithgar Oct 14, 2025
1537143
skip optional check for optional-set
wraithgar Oct 14, 2025
47eccd4
roll rebuild up into reify
wraithgar Oct 14, 2025
2cab6a7
move scriptsRun to constructor, move trashList to internal property
wraithgar Oct 14, 2025
00b2317
move registry normalization to main constructor
wraithgar Oct 14, 2025
455386c
move usePackageLock to this.options
wraithgar Oct 14, 2025
982701f
move more public attributes to main class
wraithgar Oct 14, 2025
f745e01
move _createBundledTree to private method
wraithgar Oct 14, 2025
cec4af5
inline comments
wraithgar Oct 14, 2025
04fe63f
move reifyPackages to private method
wraithgar Oct 15, 2025
c8b1b2e
remove unused private attributes
wraithgar Oct 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
811 changes: 711 additions & 100 deletions workspaces/arborist/lib/arborist/build-ideal-tree.js

Large diffs are not rendered by default.

59 changes: 33 additions & 26 deletions workspaces/arborist/lib/arborist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,25 @@
// - virtual
// - ideal
//
// The actual tree is what's present on disk in the node_modules tree
// and elsewhere that links may extend.
// The actual tree is what's present on disk in the node_modules tree and elsewhere that links may extend.
//
// The virtual tree is loaded from metadata (package.json and lock files).
//
// The ideal tree is what we WANT that actual tree to become. This starts
// with the virtual tree, and then applies the options requesting
// add/remove/update actions.
// The ideal tree is what we WANT that actual tree to become. This starts with the virtual tree, and then applies the options requesting add/remove/update actions.
//
// To reify a tree, we calculate a diff between the ideal and actual trees,
// and then turn the actual tree into the ideal tree by taking the actions
// required. At the end of the reification process, the actualTree is
// updated to reflect the changes.
// To reify a tree, we calculate a diff between the ideal and actual trees, and then turn the actual tree into the ideal tree by taking the actions required.
// At the end of the reification process, the actualTree is updated to reflect the changes.
//
// Each tree has an Inventory at the root. Shrinkwrap is tracked by Arborist
// instance. It always refers to the actual tree, but is updated (and written
// to disk) on reification.
// Each tree has an Inventory at the root.
// Shrinkwrap is tracked by Arborist instance.
// It always refers to the actual tree, but is updated (and written to disk) on reification.

// Each of the mixin "classes" adds functionality, but are not dependent on
// constructor call order. So, we just load them in an array, and build up
// the base class, so that the overall voltron class is easier to test and
// cover, and separation of concerns can be maintained.
// Each of the mixin "classes" adds functionality, but are not dependent on constructor call order.
// So, we just load them in an array, and build up the base class, so that the overall voltron class is easier to test and cover, and separation of concerns can be maintained.
// Eventually, each mixin shared enough `Symbol.for` declarations that separation of concerns was not actually happening very well.
// Unit testing was also masking bugs that surfaced when the code was used as a whole, so tests all require Arborist in full now.
// So, this is now moving towards a single large class so that it's easier to weed out duplication in logic and efforts.
// Tests are still separated by main instance method.

const { resolve } = require('node:path')
const { homedir } = require('node:os')
Expand All @@ -39,9 +36,6 @@
const mixins = [
require('../tracker.js'),
require('./build-ideal-tree.js'),
require('./load-actual.js'),
require('./load-virtual.js'),
require('./rebuild.js'),
require('./reify.js'),
require('./isolated-reifier.js'),
]
Expand All @@ -68,6 +62,20 @@
constructor (options = {}) {
const timeEnd = time.start('arborist:ctor')
super(options)

if (options.workspaces?.length && options.global) {
throw new Error('Cannot operate on workspaces in global mode')
}
// normalize trailing slash
const registry = options.registry || 'https://registry.npmjs.org'
options.registry = this.registry = registry.replace(/\/+$/, '') + '/'

Check failure

Code scanning / CodeQL

Polynomial regular expression used on uncontrolled data High

This
regular expression
that depends on
library input
may run slow on strings with many repetitions of '/'.
This
regular expression
that depends on
library input
may run slow on strings with many repetitions of '/'.
This
regular expression
that depends on
library input
may run slow on strings with many repetitions of '/'.
This
regular expression
that depends on
library input
may run slow on strings with many repetitions of '/'.

// the tree of nodes on disk
this.actualTree = options.actualTree
this.idealTree = 'idealTree' in options ? options.idealTree : null
this.installLinks = 'installLinks' in options ? options.installLinks : false
this.legacyPeerDeps = 'legacyPeerDeps' in options ? options.legacyPeerDeps : false

this.options = {
nodeVersion: process.version,
...options,
Expand All @@ -88,12 +96,12 @@
replaceRegistryHost: options.replaceRegistryHost,
savePrefix: 'savePrefix' in options ? options.savePrefix : '^',
scriptShell: options.scriptShell,
usePackageLock: 'packageLock' in options ? options.packageLock : true,
workspaces: options.workspaces || [],
workspacesEnabled: options.workspacesEnabled !== false,
}
// TODO we only ever look at this.options.replaceRegistryHost, not
// this.replaceRegistryHost. Defaulting needs to be written back to
// this.options to work properly
// TODO we only ever look at this.options.replaceRegistryHost, not this.replaceRegistryHost.
// Defaulting needs to be written back to this.options to work properly
this.replaceRegistryHost = this.options.replaceRegistryHost =
(!this.options.replaceRegistryHost || this.options.replaceRegistryHost === 'npmjs') ?
'registry.npmjs.org' : this.options.replaceRegistryHost
Expand All @@ -104,14 +112,13 @@
this.cache = resolve(this.options.cache)
this.diff = null
this.path = resolve(this.options.path)
this.scriptsRun = new Set()
timeEnd()
}

// TODO: We should change these to static functions instead
// of methods for the next major version
// TODO: We should change these to static functions instead of methods for the next major version

// Get the actual nodes corresponding to a root node's child workspaces,
// given a list of workspace names.
// Get the actual nodes corresponding to a root node's child workspaces, given a list of workspace names.
workspaceNodes (tree, workspaces) {
const wsMap = tree.workspaces
if (!wsMap) {
Expand Down
5 changes: 2 additions & 3 deletions workspaces/arborist/lib/arborist/isolated-reifier.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
const _makeIdealGraph = Symbol('makeIdealGraph')
const _createIsolatedTree = Symbol.for('createIsolatedTree')
const _createBundledTree = Symbol('createBundledTree')
const { mkdirSync } = require('node:fs')
const pacote = require('pacote')
const { join } = require('node:path')
Expand Down Expand Up @@ -162,7 +161,7 @@ module.exports = cls => class IsolatedReifier extends cls {
result.hasInstallScript = node.hasInstallScript
}

async [_createBundledTree] () {
async #createBundledTree () {
// TODO: make sure that idealTree object exists
const idealTree = this.idealTree
// TODO: test workspaces having bundled deps
Expand Down Expand Up @@ -217,7 +216,7 @@ module.exports = cls => class IsolatedReifier extends cls {

const proxiedIdealTree = this.idealGraph

const bundledTree = await this[_createBundledTree]()
const bundledTree = await this.#createBundledTree()

const treeHash = (startNode) => {
// generate short hash based on the dependency tree
Expand Down
Loading
Loading