@@ -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 setupMirrorFromCache ( ) : Promise < ?string > {
2026 const tarballMirrorPath = this . getTarballMirrorPath ( ) ;
@@ -90,11 +96,7 @@ export default class GitFetcher extends BaseFetcher {
9096 }
9197
9298 return new Promise ( ( resolve , reject ) => {
93- const untarStream = tarFs . extract ( this . dest , {
94- dmode : 0o555 , // all dirs should be readable
95- fmode : 0o444 , // all files should be readable
96- chown : false , // don't chown. just leave as it is
97- } ) ;
99+ const untarStream = this . _createUntarStream ( this . dest ) ;
98100
99101 const hashStream = new crypto . HashStream ( ) ;
100102
@@ -131,22 +133,130 @@ export default class GitFetcher extends BaseFetcher {
131133 const gitUrl = Git . npmUrlToGitUrl ( this . reference ) ;
132134 const git = new Git ( this . config , gitUrl , hash ) ;
133135 await git . init ( ) ;
134- await git . clone ( this . dest ) ;
136+
137+ const manifestFile = await git . getFile ( 'package.json' ) ;
138+ if ( ! manifestFile ) {
139+ throw new MessageError ( this . reporter . lang ( 'couldntFindPackagejson' , gitUrl ) ) ;
140+ }
141+ const scripts = JSON . parse ( manifestFile ) . scripts ;
142+ const hasPrepareScript = Boolean ( scripts && scripts . prepare ) ;
143+
144+ if ( hasPrepareScript ) {
145+ await this . fetchFromInstallAndPack ( git ) ;
146+ } else {
147+ await this . fetchFromGitArchive ( git ) ;
148+ }
149+
150+ return {
151+ hash,
152+ } ;
153+ }
154+
155+ async fetchFromInstallAndPack ( git : Git ) : Promise < void > {
156+ const prepareDirectory = this . config . getTemp ( `${ crypto . hash ( git . gitUrl . repository ) } .${ git . hash } .prepare` ) ;
157+ await fsUtil . unlink ( prepareDirectory ) ;
158+
159+ await git . clone ( prepareDirectory ) ;
160+
161+ const [ prepareConfig , prepareLockFile ] = await Promise . all ( [
162+ Config . create (
163+ {
164+ cwd : prepareDirectory ,
165+ disablePrepublish : true ,
166+ } ,
167+ this . reporter ,
168+ ) ,
169+ Lockfile . fromDirectory ( prepareDirectory , this . reporter ) ,
170+ ] ) ;
171+ await install ( prepareConfig , this . reporter , { } , prepareLockFile ) ;
135172
136173 const tarballMirrorPath = this . getTarballMirrorPath ( ) ;
137174 const tarballCachePath = this . getTarballCachePath ( ) ;
138175
176+ if ( tarballMirrorPath ) {
177+ await this . _packToTarball ( prepareConfig , tarballMirrorPath ) ;
178+ }
179+ if ( tarballCachePath ) {
180+ await this . _packToTarball ( prepareConfig , tarballCachePath ) ;
181+ }
182+
183+ await this . _packToDirectory ( prepareConfig , this . dest ) ;
184+
185+ await fsUtil . unlink ( prepareDirectory ) ;
186+ }
187+
188+ async _packToTarball ( config : Config , path : string ) : Promise < void > {
189+ const tarballStream = await this . _createTarballStream ( config ) ;
190+ await new Promise ( ( resolve , reject ) => {
191+ const writeStream = fs . createWriteStream ( path ) ;
192+ tarballStream . on ( 'error' , reject ) ;
193+ writeStream . on ( 'error' , reject ) ;
194+ writeStream . on ( 'end' , resolve ) ;
195+ writeStream . on ( 'open' , ( ) => {
196+ tarballStream . pipe ( writeStream ) ;
197+ } ) ;
198+ writeStream . once ( 'finish' , resolve ) ;
199+ } ) ;
200+ }
201+
202+ async _packToDirectory ( config : Config , dest : string ) : Promise < void > {
203+ const tarballStream = await this . _createTarballStream ( config ) ;
204+ await new Promise ( ( resolve , reject ) => {
205+ const untarStream = this . _createUntarStream ( dest ) ;
206+ tarballStream . on ( 'error' , reject ) ;
207+ untarStream . on ( 'error' , reject ) ;
208+ untarStream . on ( 'end' , resolve ) ;
209+ untarStream . once ( 'finish' , resolve ) ;
210+ tarballStream . pipe ( untarStream ) ;
211+ } ) ;
212+ }
213+
214+ _createTarballStream ( config : Config ) : Promise < stream$Duplex > {
215+ let savedPackedHeader = false ;
216+ return packTarball ( config , {
217+ mapHeader ( header : Object ) : Object {
218+ if ( ! savedPackedHeader ) {
219+ savedPackedHeader = true ;
220+ header . pax = header . pax || { } ;
221+ // add a custom data on the first header
222+ // in order to distinguish a tar from "git archive" and a tar from "pack" command
223+ header . pax . packed = PACKED_FLAG ;
224+ }
225+ return header ;
226+ } ,
227+ } ) ;
228+ }
229+
230+ _createUntarStream ( dest : string ) : stream$Writable {
231+ const PREFIX = 'package/' ;
232+ let isPackedTarball = undefined ;
233+ return tarFs . extract ( dest , {
234+ dmode : 0o555 , // all dirs should be readable
235+ fmode : 0o444 , // all files should be readable
236+ chown : false , // don't chown. just leave as it is
237+ map : header => {
238+ if ( isPackedTarball === undefined ) {
239+ isPackedTarball = header . pax && header . pax . packed === PACKED_FLAG ;
240+ }
241+ if ( isPackedTarball ) {
242+ header . name = header . name . substr ( PREFIX . length ) ;
243+ }
244+ } ,
245+ } ) ;
246+ }
247+
248+ async fetchFromGitArchive ( git : Git ) : Promise < void > {
249+ await git . clone ( this . dest ) ;
250+ const tarballMirrorPath = this . getTarballMirrorPath ( ) ;
251+ const tarballCachePath = this . getTarballCachePath ( ) ;
252+
139253 if ( tarballMirrorPath ) {
140254 await git . archive ( tarballMirrorPath ) ;
141255 }
142256
143257 if ( tarballCachePath ) {
144258 await git . archive ( tarballCachePath ) ;
145259 }
146-
147- return {
148- hash,
149- } ;
150260 }
151261
152262 async _fetch ( ) : Promise < FetchedOverride > {
0 commit comments