1 | const fs = require('fs')
|
2 | , path = require('path')
|
3 | , after = require('after')
|
4 | , cpr = require('cpr')
|
5 |
|
6 | function adjustForLang (orig, lang, callback) {
|
7 | if (!lang) {
|
8 | process.nextTick(function () {
|
9 | callback(null, orig)
|
10 | })
|
11 | return
|
12 | }
|
13 |
|
14 | var extName = path.extname(orig)
|
15 | , langAware = orig.slice(0, -extName.length) + '.' + lang + extName
|
16 |
|
17 | fs.open(langAware, 'r', function (err, fd) {
|
18 | if (!err && fd)
|
19 | fs.close(fd)
|
20 |
|
21 | callback(null, err ? orig : langAware)
|
22 | })
|
23 | }
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 | function findDestination (file, lang, callback) {
|
30 | if (lang)
|
31 | file = file.replace('.' + lang, '')
|
32 |
|
33 | file = path.basename(file)
|
34 |
|
35 | var f = path.join(process.cwd(), file)
|
36 |
|
37 | fs.exists(f, function (exists) {
|
38 | if (!exists)
|
39 | return callback(null, f)
|
40 |
|
41 | var ext = path.extname(file)
|
42 | , pfx = file.substring(0, file.length - ext.length)
|
43 |
|
44 | ;(function next (i) {
|
45 | f = path.join(process.cwd(), pfx + i + ext)
|
46 |
|
47 | fs.exists(f, function (exists) {
|
48 | if (!exists)
|
49 | return callback(null, f)
|
50 |
|
51 | next(i + 1)
|
52 | })
|
53 | }(1))
|
54 | })
|
55 | }
|
56 |
|
57 |
|
58 |
|
59 |
|
60 |
|
61 | function prepare (callback) {
|
62 | if (!this._boilerplate.length)
|
63 | return process.nextTick(callback)
|
64 |
|
65 | var done = after(this._boilerplate.length, callback)
|
66 | , out = this.boilerplateOut = {}
|
67 | , self = this
|
68 |
|
69 | this._boilerplate.forEach(function (src, index) {
|
70 | adjustForLang(src, self.lang, function(err, src) {
|
71 |
|
72 |
|
73 | if (err)
|
74 | return callback(err)
|
75 |
|
76 | self._boilerplate[index] = src
|
77 |
|
78 | findDestination(src, self.lang, function (err, dst) {
|
79 | if (err)
|
80 | return done(err)
|
81 |
|
82 | out[src] = out[path.basename(src)] = path.basename(dst)
|
83 |
|
84 | copy(src, dst, done)
|
85 | })
|
86 | })
|
87 | })
|
88 | }
|
89 |
|
90 |
|
91 | function copy (src, dst, callback) {
|
92 | function copyFile () {
|
93 | fs.createReadStream(src)
|
94 | .on('error', function (err) {
|
95 | callback && callback(err)
|
96 | })
|
97 | .pipe(fs.createWriteStream(dst))
|
98 | .on('error', function (err) {
|
99 | callback && callback(err)
|
100 | })
|
101 | .on('close', function () {
|
102 | callback && callback()
|
103 | })
|
104 | }
|
105 |
|
106 | fs.stat(src, function (err, stat) {
|
107 | if (err)
|
108 | return callback(err)
|
109 |
|
110 | if (stat.isFile())
|
111 | return copyFile()
|
112 |
|
113 | if (stat.isDirectory())
|
114 | return cpr(src, dst, { overwrite: true }, callback)
|
115 |
|
116 | return callback(new Error('Boilerplate source must be a regular file or directory'))
|
117 | })
|
118 | }
|
119 |
|
120 |
|
121 | function fix (exercise) {
|
122 | exercise._boilerplate = []
|
123 |
|
124 | exercise.addPrepare(prepare)
|
125 |
|
126 | exercise.addBoilerplate = function (file) {
|
127 | if (Array.isArray(file)) {
|
128 | return file.forEach(function (f) {
|
129 | exercise.addBoilerplate(f)
|
130 | })
|
131 | }
|
132 |
|
133 | if (typeof file != 'string')
|
134 | throw new TypeError('addBoilerplate must be provided with a path to a file or an array of paths')
|
135 |
|
136 | exercise._boilerplate.push(file)
|
137 | }
|
138 |
|
139 |
|
140 |
|
141 |
|
142 |
|
143 | var getExerciseText = exercise.getExerciseText
|
144 | exercise.getExerciseText = function (callback) {
|
145 | var boilerplateOut = this.boilerplateOut
|
146 |
|
147 | getExerciseText.call(this, function (err, type, contents) {
|
148 | if (err)
|
149 | return callback(err)
|
150 |
|
151 |
|
152 | contents = contents.replace(
|
153 | /\{boilerplate:([^}]+)\}/gi
|
154 | , function (match, boilerplateFile) {
|
155 | return boilerplateOut[boilerplateFile] || '(ERROR: Unknown boilerplate file)'
|
156 | }
|
157 | )
|
158 |
|
159 | callback(null, type, contents)
|
160 | })
|
161 | }
|
162 | }
|
163 |
|
164 |
|
165 | function boilerplate (exercise) {
|
166 | if (typeof exercise.addBoilerplate != 'function')
|
167 | fix(exercise)
|
168 | return exercise
|
169 | }
|
170 |
|
171 |
|
172 | module.exports = boilerplate
|