]> git.gir.st - VimFx.git/blob - gulpfile.coffee
Don't include the en-US description twice
[VimFx.git] / gulpfile.coffee
1 ###
2 # Copyright Simon Lydell 2014.
3 #
4 # This file is part of VimFx.
5 #
6 # VimFx is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
10 #
11 # VimFx is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with VimFx. If not, see <http://www.gnu.org/licenses/>.
18 ###
19
20 fs = require('fs')
21 path = require('path')
22 gulp = require('gulp')
23 coffee = require('gulp-coffee')
24 coffeelint = require('gulp-coffeelint')
25 git = require('gulp-git')
26 header = require('gulp-header')
27 mustache = require('gulp-mustache')
28 tap = require('gulp-tap')
29 zip = require('gulp-zip')
30 marked = require('marked')
31 merge = require('merge2')
32 precompute = require('require-precompute')
33 request = require('request')
34 rimraf = require('rimraf')
35 runSequence = require('run-sequence')
36 pkg = require('./package.json')
37
38 DEST = 'build'
39 XPI = 'VimFx.xpi'
40 LOCALE = 'extension/locale'
41 TEST = 'extension/test'
42
43 BASE_LOCALE = 'en-US'
44
45 test = '--test' in process.argv or '-t' in process.argv
46 ifTest = (value) -> if test then [value] else []
47
48 { join } = path
49 read = (filepath) -> fs.readFileSync(filepath).toString()
50 template = (data) -> mustache(data, {extension: ''})
51
52 gulp.task('default', ['push'])
53
54 gulp.task('clean', (callback) ->
55 rimraf(DEST, callback)
56 )
57
58 gulp.task('copy', ->
59 gulp.src(['extension/**/!(*.coffee|*.tmpl)', 'COPYING', 'LICENSE'])
60 .pipe(gulp.dest(DEST))
61 )
62
63 gulp.task('node_modules', ->
64 dependencies = (name for name of pkg.dependencies)
65 # Note! When installing or updating node modules, make sure that the following
66 # glob does not include too much or too little!
67 gulp.src("node_modules/+(#{ dependencies.join('|') })/\
68 {LICENSE*,{,**/!(test|examples)/}!(*min|*test*|*bench*).js}")
69 .pipe(gulp.dest("#{ DEST }/node_modules"))
70 )
71
72 gulp.task('coffee', ->
73 gulp.src([
74 'extension/bootstrap.coffee'
75 'extension/lib/**/*.coffee'
76 ifTest('extension/test/**/*.coffee')...
77 ], {base: 'extension'})
78 .pipe(coffee({bare: true}))
79 .pipe(gulp.dest(DEST))
80 )
81
82 gulp.task('chrome.manifest', ->
83 gulp.src('extension/chrome.manifest.tmpl')
84 .pipe(template({locales: fs.readdirSync(LOCALE).map((locale) -> {locale})}))
85 .pipe(gulp.dest(DEST))
86 )
87
88 gulp.task('install.rdf', ->
89 [ [ { name: creator } ], developers, contributors, translators ] =
90 read('PEOPLE.md').trim().replace(/^#.+\n|^\s*-\s*/mg, '').split('\n\n')
91 .map((block) -> block.split('\n').map((name) -> {name}))
92
93 getDescription = (locale) -> read(join(LOCALE, locale, 'description')).trim()
94
95 descriptions = fs.readdirSync(LOCALE)
96 .filter((locale) -> locale != BASE_LOCALE)
97 .map((locale) -> {
98 locale: locale
99 description: getDescription(locale)
100 })
101
102 gulp.src('extension/install.rdf.tmpl')
103 .pipe(template({
104 version: pkg.version
105 minVersion: pkg.firefoxVersions.min
106 maxVersion: pkg.firefoxVersions.max
107 creator, developers, contributors, translators
108 defaultDescription: getDescription(BASE_LOCALE)
109 descriptions
110 }))
111 .pipe(gulp.dest(DEST))
112 )
113
114 gulp.task('require-data', ['node_modules'], ->
115 data = JSON.stringify(precompute('.'), null, 2)
116 gulp.src('extension/require-data.js.tmpl')
117 .pipe(template({data}))
118 .pipe(gulp.dest(DEST))
119 )
120
121 gulp.task('tests-list', ->
122 list = JSON.stringify(fs.readdirSync(TEST)
123 .map((name) -> name.match(/^(test-.+)\.coffee$/)?[1])
124 .filter(Boolean)
125 )
126 gulp.src("#{ TEST }/tests-list.js.tmpl", {base: 'extension'})
127 .pipe(template({list}))
128 .pipe(gulp.dest(DEST))
129 )
130
131 gulp.task('templates', [
132 'chrome.manifest'
133 'install.rdf'
134 'require-data'
135 ifTest('tests-list')...
136 ])
137
138 gulp.task('build', (callback) ->
139 runSequence(
140 'clean',
141 ['copy', 'node_modules', 'coffee', 'templates'],
142 callback
143 )
144 )
145
146 gulp.task('xpi', ['build'], ->
147 gulp.src("#{ DEST }/**/*")
148 .pipe(zip(XPI, {compress: false}))
149 .pipe(gulp.dest(DEST))
150 )
151
152 gulp.task('push', ['xpi'], ->
153 body = fs.readFileSync(join(DEST, XPI))
154 request.post({url: 'http://localhost:8888', body })
155 )
156
157 gulp.task('lint', ->
158 gulp.src(['extension/**/*.coffee', 'gulpfile.coffee'])
159 .pipe(coffeelint())
160 .pipe(coffeelint.reporter())
161 )
162
163 gulp.task('release', (callback) ->
164 { version } = pkg
165 message = "VimFx v#{ version }"
166 today = new Date().toISOString()[...10]
167 merge([
168 gulp.src('package.json'),
169 gulp.src('CHANGELOG.md')
170 .pipe(header("### #{ version } (#{ today })\n\n"))
171 .pipe(gulp.dest('.'))
172 ])
173 .pipe(git.commit(message))
174 .on('end', ->
175 git.tag("v#{ version }", message, callback)
176 )
177 )
178
179 gulp.task('changelog', ->
180 num = 1
181 for arg in process.argv when /^-[1-9]$/.test(arg)
182 num = Number(arg[1])
183 entries = read('CHANGELOG.md').split(/^### .+/m)[1..num].join('')
184 process.stdout.write(marked(entries))
185 )
186
187 gulp.task('readme', ->
188 process.stdout.write(marked(read('README.md')))
189 )
190
191 gulp.task('faster', ->
192 gulp.src('gulpfile.coffee')
193 .pipe(coffee({bare: true}))
194 .pipe(gulp.dest('.'))
195 )
196
197 gulp.task('sync-locales', ->
198 baseLocale = BASE_LOCALE
199 for arg in process.argv when arg[...2] == '--'
200 baseLocale = arg[2..]
201 results = fs.readdirSync(join(LOCALE, baseLocale))
202 .filter((file) -> path.extname(file) == '.properties')
203 .map(syncLocale.bind(null, baseLocale))
204 if baseLocale == BASE_LOCALE
205 report = []
206 for {fileName, translatedCount, total} in results
207 report.push("#{ fileName }:")
208 for localeName, count of translatedCount
209 paddedName = "#{ localeName }: "[...6]
210 percentage = Math.round((count / total) * 100)
211 report.push(" #{ paddedName } #{ percentage }%")
212 process.stdout.write(report.join('\n') + '\n')
213 )
214
215 syncLocale = (baseLocaleName, fileName) ->
216 basePath = join(LOCALE, baseLocaleName, fileName)
217 base = parseLocaleFile(read(basePath))
218 oldBasePath = "#{basePath}.old"
219 if fs.existsSync(oldBasePath)
220 oldBase = parseLocaleFile(read(oldBasePath))
221 translatedCount = {}
222 for localeName in fs.readdirSync(LOCALE) when localeName != baseLocaleName
223 localePath = join(LOCALE, localeName, fileName)
224 locale = parseLocaleFile(read(localePath))
225 translatedCount[localeName] = 0
226 newLocale = base.template.map((line) ->
227 if Array.isArray(line)
228 [ key ] = line
229 oldValue = oldBase?.keys[key]
230 value =
231 if (oldValue? and oldValue != base.keys[key]) or
232 key not of locale.keys
233 base.keys[key]
234 else
235 locale.keys[key]
236 translatedCount[localeName]++ if value != base.keys[key] or value == ''
237 return "#{ key }=#{ value }"
238 else
239 return line
240 )
241 fs.writeFileSync(localePath, newLocale.join(base.newline))
242 return {fileName, translatedCount, total: Object.keys(base.keys).length}
243
244 parseLocaleFile = (fileContents) ->
245 keys = {}
246 lines = []
247 [ newline ] = fileContents.match(/\r?\n/)
248 for line in fileContents.split(newline)
249 line = line.trim()
250 [ match, key, value ] = line.match(///^ ([^=]+) = (.*) $///) ? []
251 if match
252 keys[key] = value
253 lines.push([key])
254 else
255 lines.push(line)
256 return {keys, template: lines, newline}
257
258 helpHTMLFile = 'help.html'
259 gulp.task(helpHTMLFile, ->
260 unless fs.existsSync(helpHTMLFile)
261 console.log("""
262 First enable the “Copy to clipboard” line in help.coffee, show the help
263 dialog and finally dump the clipboard into #{ helpHTMLFile }.
264 """)
265 return
266 gulp.src('help.html')
267 .pipe(tap((file) ->
268 file.contents = new Buffer(generateHelpHTML(file.contents.toString()))
269 ))
270 .pipe(gulp.dest('.'))
271 )
272
273 helpHTMLPrelude = '''
274 <!doctype html>
275 <meta charset=utf-8>
276 <title>VimFx help</title>
277 <style>
278 * { margin: 0; }
279 body > :first-child { min-height: 100vh; }
280 </style>
281 <link rel=stylesheet href=extension/skin/style.css>
282 '''
283
284 generateHelpHTML = (dumpedHTML) ->
285 return helpHTMLPrelude + dumpedHTML
286 .replace(/^<\w+ xmlns="[^"]+"/, '<div')
287 .replace(/\w+>$/, 'div>')
288 .replace(/<(\w+)([^>]*)\/>/g, '<$1$2></$1>')
Imprint / Impressum