1
1
// mixin implementing the reify method
2
-
3
2
const onExit = require ( '../signal-handling.js' )
4
3
const pacote = require ( 'pacote' )
5
4
const AuditReport = require ( '../audit-report.js' )
@@ -10,8 +9,9 @@ const debug = require('../debug.js')
10
9
const walkUp = require ( 'walk-up-path' )
11
10
const log = require ( 'proc-log' )
12
11
const hgi = require ( 'hosted-git-info' )
12
+ const rpj = require ( 'read-package-json-fast' )
13
13
14
- const { dirname, resolve, relative } = require ( 'path' )
14
+ const { dirname, resolve, relative, join } = require ( 'path' )
15
15
const { depth : dfwalk } = require ( 'treeverse' )
16
16
const {
17
17
lstat,
@@ -106,6 +106,8 @@ const _resolvedAdd = Symbol.for('resolvedAdd')
106
106
const _usePackageLock = Symbol . for ( 'usePackageLock' )
107
107
const _formatPackageLock = Symbol . for ( 'formatPackageLock' )
108
108
109
+ const _createIsolatedTree = Symbol . for ( 'createIsolatedTree' )
110
+
109
111
module . exports = cls => class Reifier extends cls {
110
112
constructor ( options ) {
111
113
super ( options )
@@ -138,6 +140,8 @@ module.exports = cls => class Reifier extends cls {
138
140
139
141
// public method
140
142
async reify ( options = { } ) {
143
+ const linked = ( options . installStrategy || this . options . installStrategy ) === 'linked'
144
+
141
145
if ( this [ _packageLockOnly ] && this [ _global ] ) {
142
146
const er = new Error ( 'cannot generate lockfile for global packages' )
143
147
er . code = 'ESHRINKWRAPGLOBAL'
@@ -154,8 +158,22 @@ module.exports = cls => class Reifier extends cls {
154
158
process . emit ( 'time' , 'reify' )
155
159
await this [ _validatePath ] ( )
156
160
await this [ _loadTrees ] ( options )
161
+
162
+ const oldTree = this . idealTree
163
+ if ( linked ) {
164
+ // swap out the tree with the isolated tree
165
+ // this is currently technical debt which will be resolved in a refactor
166
+ // of Node/Link trees
167
+ log . warn ( 'reify' , 'The "linked" install strategy is EXPERIMENTAL and may contain bugs.' )
168
+ this . idealTree = await this [ _createIsolatedTree ] ( this . idealTree )
169
+ }
157
170
await this [ _diffTrees ] ( )
158
171
await this [ _reifyPackages ] ( )
172
+ if ( linked ) {
173
+ // swap back in the idealTree
174
+ // so that the lockfile is preserved
175
+ this . idealTree = oldTree
176
+ }
159
177
await this [ _saveIdealTree ] ( options )
160
178
await this [ _copyIdealToActual ] ( )
161
179
// This is a very bad pattern and I can't wait to stop doing it
@@ -634,44 +652,40 @@ module.exports = cls => class Reifier extends cls {
634
652
}
635
653
636
654
async [ _extractOrLink ] ( node ) {
637
- // in normal cases, node.resolved should *always* be set by now.
638
- // however, it is possible when a lockfile is damaged, or very old,
639
- // or in some other race condition bugs in npm v6, that a previously
640
- // bundled dependency will have just a version, but no resolved value,
641
- // and no 'bundled: true' setting.
642
- // Do the best with what we have, or else remove it from the tree
643
- // entirely, since we can't possibly reify it.
644
- let res = null
645
- if ( node . resolved ) {
646
- const registryResolved = this [ _registryResolved ] ( node . resolved )
647
- if ( registryResolved ) {
648
- res = `${ node . name } @${ registryResolved } `
649
- }
650
- } else if ( node . packageName && node . version ) {
651
- res = `${ node . packageName } @${ node . version } `
652
- }
653
-
654
- // no idea what this thing is. remove it from the tree.
655
- if ( ! res ) {
656
- const warning = 'invalid or damaged lockfile detected\n' +
657
- 'please re-try this operation once it completes\n' +
658
- 'so that the damage can be corrected, or perform\n' +
659
- 'a fresh install with no lockfile if the problem persists.'
660
- log . warn ( 'reify' , warning )
661
- log . verbose ( 'reify' , 'unrecognized node in tree' , node . path )
662
- node . parent = null
663
- node . fsParent = null
664
- this [ _addNodeToTrashList ] ( node )
665
- return
666
- }
667
-
668
655
const nm = resolve ( node . parent . path , 'node_modules' )
669
656
await this [ _validateNodeModules ] ( nm )
670
657
671
- if ( node . isLink ) {
672
- await rm ( node . path , { recursive : true , force : true } )
673
- await this [ _symlink ] ( node )
674
- } else {
658
+ if ( ! node . isLink ) {
659
+ // in normal cases, node.resolved should *always* be set by now.
660
+ // however, it is possible when a lockfile is damaged, or very old,
661
+ // or in some other race condition bugs in npm v6, that a previously
662
+ // bundled dependency will have just a version, but no resolved value,
663
+ // and no 'bundled: true' setting.
664
+ // Do the best with what we have, or else remove it from the tree
665
+ // entirely, since we can't possibly reify it.
666
+ let res = null
667
+ if ( node . resolved ) {
668
+ const registryResolved = this [ _registryResolved ] ( node . resolved )
669
+ if ( registryResolved ) {
670
+ res = `${ node . name } @${ registryResolved } `
671
+ }
672
+ } else if ( node . package . name && node . version ) {
673
+ res = `${ node . package . name } @${ node . version } `
674
+ }
675
+
676
+ // no idea what this thing is. remove it from the tree.
677
+ if ( ! res ) {
678
+ const warning = 'invalid or damaged lockfile detected\n' +
679
+ 'please re-try this operation once it completes\n' +
680
+ 'so that the damage can be corrected, or perform\n' +
681
+ 'a fresh install with no lockfile if the problem persists.'
682
+ log . warn ( 'reify' , warning )
683
+ log . verbose ( 'reify' , 'unrecognized node in tree' , node . path )
684
+ node . parent = null
685
+ node . fsParent = null
686
+ this [ _addNodeToTrashList ] ( node )
687
+ return
688
+ }
675
689
await debug ( async ( ) => {
676
690
const st = await lstat ( node . path ) . catch ( e => null )
677
691
if ( st && ! st . isDirectory ( ) ) {
@@ -688,7 +702,17 @@ module.exports = cls => class Reifier extends cls {
688
702
resolved : node . resolved ,
689
703
integrity : node . integrity ,
690
704
} )
705
+ // store nodes don't use Node class so node.package doesn't get updated
706
+ if ( node . isInStore ) {
707
+ const pkg = await rpj ( join ( node . path , 'package.json' ) )
708
+ node . package . scripts = pkg . scripts
709
+ }
710
+ return
691
711
}
712
+
713
+ // node.isLink
714
+ await rm ( node . path , { recursive : true , force : true } )
715
+ await this [ _symlink ] ( node )
692
716
}
693
717
694
718
async [ _symlink ] ( node ) {
0 commit comments