@@ -7,6 +7,10 @@ import Git from '../util/git.js';
77import * as fsUtil from '../util/fs.js' ;
88import * as constants from '../constants.js' ;
99import * as crypto from '../util/crypto.js' ;
10+ import { install } from '../cli/commands/install.js' ;
11+ import Lockfile from '../lockfile/wrapper.js' ;
12+ import Config from '../config.js' ;
13+ import { packTarball } from '../cli/commands/pack.js' ;
1014
1115const tarFs = require ( 'tar-fs' ) ;
1216const url = require ( 'url' ) ;
@@ -15,6 +19,8 @@ const fs = require('fs');
1519
1620const invariant = require ( 'invariant' ) ;
1721
22+ const PACKED_FLAG = '1' ;
23+
1824export default class GitFetcher extends BaseFetcher {
1925 async getLocalAvailabilityStatus ( ) : Promise < boolean > {
2026 // Some mirrors might still have files named "./reponame" instead of "./reponame-commit"
@@ -78,11 +84,7 @@ export default class GitFetcher extends BaseFetcher {
7884 }
7985
8086 return new Promise ( ( resolve , reject ) => {
81- const untarStream = tarFs . extract ( this . dest , {
82- dmode : 0o555 , // all dirs should be readable
83- fmode : 0o444 , // all files should be readable
84- chown : false , // don't chown. just leave as it is
85- } ) ;
87+ const untarStream = this . _createUntarStream ( this . dest ) ;
8688
8789 const hashStream = new crypto . HashStream ( ) ;
8890
@@ -117,22 +119,131 @@ export default class GitFetcher extends BaseFetcher {
117119 const gitUrl = Git . npmUrlToGitUrl ( this . reference ) ;
118120 const git = new Git ( this . config , gitUrl , hash ) ;
119121 await git . init ( ) ;
120- await git . clone ( this . dest ) ;
122+
123+ const manifestFile = await git . getFile ( 'package.json' ) ;
124+ if ( ! manifestFile ) {
125+ throw new MessageError ( this . reporter . lang ( 'couldntFindPackagejson' , gitUrl ) ) ;
126+ }
127+ const scripts = JSON . parse ( manifestFile ) . scripts ;
128+ const hasPrepareScript = Boolean ( scripts && scripts . prepare ) ;
129+
130+ if ( hasPrepareScript ) {
131+ await this . fetchFromInstallAndPack ( git ) ;
132+ } else {
133+ await this . fetchFromGitArchive ( git ) ;
134+ }
135+
136+ return {
137+ hash,
138+ } ;
139+ }
140+
141+ async fetchFromInstallAndPack ( git : Git ) : Promise < void > {
142+ const prepareDirectory = this . config . getTemp (
143+ `${ crypto . hash ( git . gitUrl . repository ) } .${ git . hash } .prepare` ) ;
144+ await fsUtil . unlink ( prepareDirectory ) ;
145+
146+ await git . clone ( prepareDirectory ) ;
147+
148+ const [
149+ prepareConfig ,
150+ prepareLockFile ,
151+ ] = await Promise . all ( [
152+ Config . create ( {
153+ cwd : prepareDirectory ,
154+ gitDependency : true ,
155+ } , this . reporter ) ,
156+ Lockfile . fromDirectory ( prepareDirectory , this . reporter ) ,
157+ ] ) ;
158+ await install ( prepareConfig , this . reporter , { } , prepareLockFile ) ;
121159
122160 const tarballMirrorPath = this . getTarballMirrorPath ( ) ;
123161 const tarballCachePath = this . getTarballCachePath ( ) ;
124162
163+ if ( tarballMirrorPath ) {
164+ await this . _packToTarball ( prepareConfig , tarballMirrorPath ) ;
165+ }
166+ if ( tarballCachePath ) {
167+ await this . _packToTarball ( prepareConfig , tarballCachePath ) ;
168+ }
169+
170+ await this . _packToDirectory ( prepareConfig , this . dest ) ;
171+
172+ await fsUtil . unlink ( prepareDirectory ) ;
173+ }
174+
175+ async _packToTarball ( config : Config , path : string ) : Promise < void > {
176+ const tarballStream = await this . _createTarballStream ( config ) ;
177+ await new Promise ( ( resolve , reject ) => {
178+ const writeStream = fs . createWriteStream ( path ) ;
179+ tarballStream . on ( 'error' , reject ) ;
180+ writeStream . on ( 'error' , reject ) ;
181+ writeStream . on ( 'end' , resolve ) ;
182+ writeStream . on ( 'open' , ( ) => {
183+ tarballStream . pipe ( writeStream ) ;
184+ } ) ;
185+ writeStream . once ( 'finish' , resolve ) ;
186+ } ) ;
187+ }
188+
189+ async _packToDirectory ( config : Config , dest : string ) : Promise < void > {
190+ const tarballStream = await this . _createTarballStream ( config ) ;
191+ await new Promise ( ( resolve , reject ) => {
192+ const untarStream = this . _createUntarStream ( dest ) ;
193+ tarballStream . on ( 'error' , reject ) ;
194+ untarStream . on ( 'error' , reject ) ;
195+ untarStream . on ( 'end' , resolve ) ;
196+ untarStream . once ( 'finish' , resolve ) ;
197+ tarballStream . pipe ( untarStream ) ;
198+ } ) ;
199+ }
200+
201+ _createTarballStream ( config : Config ) : Promise < stream$Duplex > {
202+ let savedPackedHeader = false ;
203+ return packTarball ( config , {
204+ mapHeader ( header : Object ) : Object {
205+ if ( ! savedPackedHeader ) {
206+ savedPackedHeader = true ;
207+ header . pax = header . pax || { } ;
208+ // add a custom data on the first header
209+ // in order to distinguish a tar from "git archive" and a tar from "pack" command
210+ header . pax . packed = PACKED_FLAG ;
211+ }
212+ return header ;
213+ } ,
214+ } ) ;
215+ }
216+
217+ _createUntarStream ( dest : string ) : stream$Writable {
218+ const PREFIX = 'package/' ;
219+ let isPackedTarball = undefined ;
220+ return tarFs . extract ( dest , {
221+ dmode : 0o555 , // all dirs should be readable
222+ fmode : 0o444 , // all files should be readable
223+ chown : false , // don't chown. just leave as it is
224+ map : header => {
225+ if ( isPackedTarball === undefined ) {
226+ isPackedTarball = header . pax && header . pax . packed === PACKED_FLAG ;
227+ }
228+ if ( isPackedTarball ) {
229+ header . name = header . name . substr ( PREFIX . length ) ;
230+ }
231+ } ,
232+ } ) ;
233+ }
234+
235+ async fetchFromGitArchive ( git : Git ) : Promise < void > {
236+ await git . clone ( this . dest ) ;
237+ const tarballMirrorPath = this . getTarballMirrorPath ( ) ;
238+ const tarballCachePath = this . getTarballCachePath ( ) ;
239+
125240 if ( tarballMirrorPath ) {
126241 await git . archive ( tarballMirrorPath ) ;
127242 }
128243
129244 if ( tarballCachePath ) {
130245 await git . archive ( tarballCachePath ) ;
131246 }
132-
133- return {
134- hash,
135- } ;
136247 }
137248
138249 async _fetch ( ) : Promise < FetchedOverride > {
0 commit comments