11import * as fsx from 'fs-extra' ;
22import * as yaml from 'js-yaml' ;
33import * as os from 'os' ;
4+ import * as semver from 'semver' ;
45
56import Utilities from '../../../utilities/Utilities' ;
67import { BaseShrinkwrapFile } from '../base/BaseShrinkwrapFile' ;
@@ -114,7 +115,15 @@ export class PnpmShrinkwrapFile extends BaseShrinkwrapFile {
114115 }
115116
116117 /**
117- * abstract
118+ * Serializes the PNPM Shrinkwrap file
119+ */
120+ protected serialize ( ) : string {
121+ return yaml . safeDump ( this . _shrinkwrapJson , {
122+ sortKeys : true
123+ } ) ;
124+ }
125+
126+ /**
118127 * Gets the version number from the list of top-level dependencies in the "dependencies" section
119128 * of the shrinkwrap file
120129 */
@@ -123,10 +132,13 @@ export class PnpmShrinkwrapFile extends BaseShrinkwrapFile {
123132 }
124133
125134 /**
126- * abstract
127- * Gets the resolved version number of a dependency for a specific temp project
135+ * Gets the resolved version number of a dependency for a specific temp project.
136+ * For PNPM, we can reuse the version that another project is using.
137+ * Note that this function modifies the shrinkwrap data.
128138 */
129- protected getDependencyVersion ( dependencyName : string , tempProjectName : string ) : string | undefined {
139+ protected tryEnsureDependencyVersion ( dependencyName : string ,
140+ tempProjectName : string ,
141+ versionRange : string ) : string | undefined {
130142 // PNPM doesn't have the same advantage of NPM, where we can skip generate as long as the
131143 // shrinkwrap file puts our dependency in either the top of the node_modules folder
132144 // or underneath the package we are looking at.
@@ -135,22 +147,103 @@ export class PnpmShrinkwrapFile extends BaseShrinkwrapFile {
135147 // Because of this, we actually need to check for a version that this package is directly
136148 // linked to.
137149
138- // Example: "project1"
139- const unscopedTempProjectName : string = Utilities . parseScopedPackageName ( tempProjectName ) . name ;
140- const tempProjectDependencyKey : string = `file:projects/${ unscopedTempProjectName } .tgz` ;
150+ const tempProjectDependencyKey : string = this . _getTempProjectKey ( tempProjectName ) ;
151+ const packageDescription : IShrinkwrapDependencyJson | undefined =
152+ this . _getPackageDescription ( tempProjectDependencyKey ) ;
153+ if ( ! packageDescription ) {
154+ return undefined ;
155+ }
141156
142- const packageDescription : IShrinkwrapDependencyJson | undefined
143- = BaseShrinkwrapFile . tryGetValue ( this . _shrinkwrapJson . packages , tempProjectDependencyKey ) ;
157+ if ( ! packageDescription . dependencies . hasOwnProperty ( dependencyName ) ) {
158+ if ( versionRange ) {
159+ // this means the current temp project doesn't provide this dependency,
160+ // however, we may be able to use a different version. we prefer the latest version
161+ let latestVersion : string | undefined = undefined ;
144162
145- if ( ! packageDescription || ! packageDescription . dependencies ) {
163+ this . getTempProjectNames ( ) . forEach ( ( otherTempProject : string ) => {
164+ const otherVersion : string | undefined = this . _getDependencyVersion ( dependencyName , otherTempProject ) ;
165+ if ( otherVersion && semver . satisfies ( otherVersion , versionRange ) ) {
166+ if ( ! latestVersion || semver . gt ( otherVersion , latestVersion ) ) {
167+ latestVersion = otherVersion ;
168+ }
169+ }
170+ } ) ;
171+
172+ if ( latestVersion ) {
173+ // go ahead and fixup the shrinkwrap file to point at this
174+ const dependencies : { [ key : string ] : string } | undefined =
175+ this . _shrinkwrapJson . packages [ tempProjectDependencyKey ] . dependencies || { } ;
176+ dependencies [ dependencyName ] = latestVersion ;
177+ this . _shrinkwrapJson . packages [ tempProjectDependencyKey ] . dependencies = dependencies ;
178+
179+ return latestVersion ;
180+ }
181+ }
182+
183+ return undefined ;
184+ }
185+
186+ return this . _normalizeDependencyVersion ( dependencyName , packageDescription . dependencies [ dependencyName ] ) ;
187+ }
188+
189+ private constructor ( shrinkwrapJson : IShrinkwrapYaml ) {
190+ super ( ) ;
191+ this . _shrinkwrapJson = shrinkwrapJson ;
192+
193+ // Normalize the data
194+ if ( ! this . _shrinkwrapJson . registry ) {
195+ this . _shrinkwrapJson . registry = '' ;
196+ }
197+ if ( ! this . _shrinkwrapJson . dependencies ) {
198+ this . _shrinkwrapJson . dependencies = { } ;
199+ }
200+ if ( ! this . _shrinkwrapJson . specifiers ) {
201+ this . _shrinkwrapJson . specifiers = { } ;
202+ }
203+ if ( ! this . _shrinkwrapJson . packages ) {
204+ this . _shrinkwrapJson . packages = { } ;
205+ }
206+ }
207+
208+ /**
209+ * Returns the version of a dependency being used by a given project
210+ */
211+ private _getDependencyVersion ( dependencyName : string , tempProjectName : string ) : string | undefined {
212+ const tempProjectDependencyKey : string = this . _getTempProjectKey ( tempProjectName ) ;
213+ const packageDescription : IShrinkwrapDependencyJson | undefined =
214+ this . _getPackageDescription ( tempProjectDependencyKey ) ;
215+ if ( ! packageDescription ) {
146216 return undefined ;
147217 }
148218
149219 if ( ! packageDescription . dependencies . hasOwnProperty ( dependencyName ) ) {
150220 return undefined ;
151221 }
152222
153- const version : string = packageDescription . dependencies [ dependencyName ] ;
223+ return this . _normalizeDependencyVersion ( dependencyName , packageDescription . dependencies [ dependencyName ] ) ;
224+ }
225+
226+ /**
227+ * Gets the package description for a tempProject from the shrinkwrap file.
228+ */
229+ private _getPackageDescription ( tempProjectDependencyKey : string ) : IShrinkwrapDependencyJson | undefined {
230+ const packageDescription : IShrinkwrapDependencyJson | undefined
231+ = BaseShrinkwrapFile . tryGetValue ( this . _shrinkwrapJson . packages , tempProjectDependencyKey ) ;
232+
233+ if ( ! packageDescription || ! packageDescription . dependencies ) {
234+ return undefined ;
235+ }
236+
237+ return packageDescription ;
238+ }
239+
240+ private _getTempProjectKey ( tempProjectName : string ) : string {
241+ // Example: "project1"
242+ const unscopedTempProjectName : string = Utilities . parseScopedPackageName ( tempProjectName ) . name ;
243+ return `file:projects/${ unscopedTempProjectName } .tgz` ;
244+ }
245+
246+ private _normalizeDependencyVersion ( dependencyName : string , version : string ) : string | undefined {
154247 // version will be either:
155248 // A - the version (e.g. "0.0.5")
156249 // B - a peer dep version (e.g. "/gulp-karma/0.0.5/karma@0.13.22"
@@ -175,23 +268,4 @@ export class PnpmShrinkwrapFile extends BaseShrinkwrapFile {
175268 return undefined ;
176269 }
177270 }
178-
179- private constructor ( shrinkwrapJson : IShrinkwrapYaml ) {
180- super ( ) ;
181- this . _shrinkwrapJson = shrinkwrapJson ;
182-
183- // Normalize the data
184- if ( ! this . _shrinkwrapJson . registry ) {
185- this . _shrinkwrapJson . registry = '' ;
186- }
187- if ( ! this . _shrinkwrapJson . dependencies ) {
188- this . _shrinkwrapJson . dependencies = { } ;
189- }
190- if ( ! this . _shrinkwrapJson . specifiers ) {
191- this . _shrinkwrapJson . specifiers = { } ;
192- }
193- if ( ! this . _shrinkwrapJson . packages ) {
194- this . _shrinkwrapJson . packages = { } ;
195- }
196- }
197271}
0 commit comments