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