Completed
Push — master ( 854a25...f9c943 )
by Thomas
32s
created

lib/kits/Kit.js   B

Complexity

Total Complexity 38
Complexity/F 2.38

Size

Lines of Code 216
Function Count 16

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 0
c 1
b 1
f 0
nc 4
dl 0
loc 216
rs 8.3999
wmc 38
mnd 2
bc 42
fnc 16
bpm 2.625
cpm 2.375
noi 3

7 Functions

Rating   Name   Duplication   Size   Complexity  
B Kit.js ➔ extractZip 0 45 2
B Kit.create 0 56 5
A Kit.download 0 9 1
A Kit.checkRequirements 0 23 3
B Kit.js ➔ postProcess 0 23 5
A Kit.js ➔ downloadZip 0 14 1
A Kit.resolveName 0 16 3
1
'use strict'
2
3
const util = require('../util')
4
const output = require('../output')
5
const helpers = require('../run-helpers')
6
const pkg = require('../../package.json')
7
8
const fs = require('fs')
9
const os = require('os')
10
const path = require('path')
11
const https = require('https')
12
const name = require('git-user-name')
13
const email = require('git-user-email')
14
const child = require('child_process')
15
const unzip = require('unzip-stream')
16
const rimraf = require('rimraf')
17
const jsonfile = require('jsonfile')
18
const mkdirp = require('mkdirp')
19
const compareVersions = require('compare-versions')
0 ignored issues
show
Unused Code introduced by
The constant compareVersions seems to be never used. Consider removing it.
Loading history...
20
21
var Kit = {}
22
23
function extractZip (response, zipName, containerPath, callback, namespace, url) {
24
  // Create cache directory
25
  var tmpdir = path.join(os.tmpdir(), 'inc-v' + pkg.version, namespace)
26
  try {
27
    mkdirp.sync(tmpdir)
28
  } catch (e) {
29
    return callback(e)
30
  }
31
32
  return response
33
    .pipe(unzip.Extract({
34
      path: tmpdir
35
    }))
36
    .on('error', function (err) {
37
      callback(err)
38
    })
39
    .on('close', function () {
40
      setTimeout(function () {
41
        try {
42
          // At this point, there is a good possibility that all files got
43
          // extracted to a subdirectory, which we should correct
44
          var zipPath = path.join(tmpdir, zipName)
45
          if (!fs.existsSync(zipPath)) {
46
            return callback(new Error('Error extracting zip file'))
47
          }
48
49
          // Check requirements
50
          var json = jsonfile.readFileSync(path.join(zipPath, 'module.json'))
51
          var res = Kit.checkRequirements(json, namespace, url, zipPath)
52
          if (res !== true) {
53
            callback(res)
54
            rimraf.sync(zipPath)
55
            process.exit()
56
          }
57
58
          fs.renameSync(zipPath, path.resolve(containerPath))
59
60
          // Calling back home
61
          callback(null)
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
62
        } catch (e) {
63
          return callback(e)
64
        }
65
      }, 500)
66
    })
67
}
68
69
function downloadZip (zipUrl, zipName, containerPath, callback, namespace, origUrl) {
70
  https.get(zipUrl, function (response) {
71
    if (response.statusCode >= 200 && response.statusCode < 300) {
72
      return extractZip(response, zipName, containerPath, callback, namespace, origUrl || zipUrl)
73
    }
74
    if (response.headers.location) {
75
      return downloadZip(response.headers.location, zipName, containerPath, callback, namespace, zipUrl)
76
    }
77
78
    callback(new Error(response.statusCode + ' ' + response.statusMessage))
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
79
  }).on('error', function (err) {
80
    callback(err)
81
  })
82
}
83
84
function postProcess (slug) {
85
  // Remove stock files
86
  try { fs.unlinkSync(path.join(slug, 'LICENSE.md')) } catch (e) {}
87
  try { fs.unlinkSync(path.join(slug, 'LICENSE')) } catch (e) {}
88
  try { fs.unlinkSync(path.join(slug, 'README')) } catch (e) {}
89
  try { rimraf.sync(path.join(slug, '.git')) } catch (e) {}
90
91
  // Write readme.md
92
  fs.writeFileSync(path.join(slug, 'README.md'), '# ' +
93
    util.boxNameDisplay(slug) + '\n\nA Incluable app for ' + util.boxNameDisplay(slug) + '.', 'utf8')
94
95
  // Download IDE helper
96
  var ideSpinner = output.wait('Downloading code completion helper')
97
  util.downloadHelper(function () {
98
    ideSpinner(true)
99
100
    // Git init
101
    var gitSpinner = output.wait('Initializing local Git repository')
102
    child.exec('cd "' + slug + '" && git init', function (err) {
103
      gitSpinner(err ? err : true)
104
    })
105
  }, slug)
106
}
107
108
Kit.download = function (kitName, targetPath, callback) {
109
  var containerPath = path.resolve(targetPath)
110
  var kitNameResolved = this.resolveName(kitName)
111
  var namespace = kitNameResolved.split('/')[0]
112
  var kitNameSimplified = kitNameResolved.split('/')[1]
113
  var zipUrl = 'https://github.com/' + kitNameResolved + '/archive/master.zip'
114
115
  downloadZip(zipUrl, kitNameSimplified + '-master', containerPath, callback, namespace)
116
}
117
118
Kit.resolveName = function (kitName) {
119
  kitName = kitName.replace(/\s/ig, '')
120
121
  if (kitName.indexOf('/') > 0) {
122
    // Repo name already includes user/org, so skip any other processing
123
    return kitName
124
  }
125
126
  // Prepend 'starter-'
127
  if (kitName.indexOf('starter-') !== 0) {
128
    kitName = 'starter-' + kitName
129
  }
130
131
  // Prepend org name
132
  return 'includable-modules/' + kitName
133
}
134
135
Kit.checkRequirements = function (json, namespace, url, zipPath) {
136
  // If not exists, no need to check anything
137
  if (!('_starter' in json) || typeof json._starter !== 'object') {
138
    return true
139
  }
140
141
  // Check minimal CLI version to run this starter kit
142
  // if ('minVersion' in json._starter && compareVersions(json._starter.minVersion, pkg.version) === 1) {
143
  //   return 'Starter kit requires version ' + json._starter.minVersion + ' of ' +
144
  //     'Includable CLI, but you have ' + pkg.version + '. Please update!'
145
  // }
146
147
  // Save starter.json meta data
148
  mkdirp.sync(path.join(zipPath, '.inc', 'meta'))
149
  json._starter.sourceUrl = url
150
  json._starter.namespace = namespace
151
  jsonfile.writeFileSync(path.join(zipPath, '.inc', 'meta', 'starter.json'), json._starter)
152
153
  // Remove starter meta data
154
  delete json['_starter']
155
156
  return true
157
}
158
159
Kit.create = function (kitName, slug) {
160
  // Get slug
161
  var userName = (util.getSystemUsername() || 'user').toLowerCase()
162
  if (slug.match(/^[a-z0-9-]+\/[a-z0-9-]+$/)) {
163
    userName = slug.split('/')[0]
164
    slug = slug.split('/')[1]
165
  }
166
  if (!slug.match(/^[a-z0-9-]+$/)) {
167
    output.err('Module name should only contain lowercase letters, numbers and dashes.')
168
    return
169
  }
170
171
  // Check if directory doesn't already exist
172
  var stopSpinner = output.wait('Downloading template \'' + kitName + '\'')
173
  if (fs.existsSync(slug)) {
174
    stopSpinner('Directory \'' + slug + '\' already exists!')
175
    return
176
  }
177
178
  try {
179
    Kit.download(kitName, slug, function (err) {
180
      if (err) {
181
        stopSpinner(err)
182
        return
183
      }
184
      stopSpinner(true)
185
186
      // Write module.json
187
      var json = jsonfile.readFileSync(path.join(slug, 'module.json'))
188
      json = util.moduleJSON(slug, userName, name({
189
        type: 'global'
190
      }), email({
191
        type: 'global'
192
      }), json)
193
      fs.writeFileSync(path.join(slug, 'module.json'), JSON.stringify(json, null, 3), 'utf8')
194
195
      // Write composer.json
196
      if (!fs.existsSync(path.join(slug, 'composer.json'))) {
197
        fs.writeFileSync(path.join(slug, 'composer.json'), JSON.stringify({
198
          'require': {}
199
        }, null, 3), 'utf8')
200
      }
201
202
      // Write version file
203
      mkdirp.sync(path.join(slug, '.inc'))
204
      fs.writeFileSync(path.join(slug, '.inc', 'version'), '11', 'utf8')
205
206
      // Scripts
207
      helpers.startChildProcess('post-create', function () {
208
        postProcess(slug)
209
      }, slug)
210
    })
211
  } catch (e) {
212
    stopSpinner(e)
213
  }
214
}
215
216
module.exports = Kit
217