UNPKG

15.2 kBJavaScriptView Raw
1var util = require('util');
2var path = require('path');
3var spawn = require('child_process').spawn;
4var compInstall = require('./component_install.js');
5var fs = require('fs');
6var _ = require('underscore');
7
8var folderMount = function folderMount(connect, point) {
9 return connect.static(path.resolve(point));
10};
11
12var folderDir = function folderDir(connect, point){
13 return connect.directory(path.resolve(point));
14}
15
16var config = require(__dirname + "/config.json");
17module.exports = function(grunt){
18 // generate file names for prod
19 var appJsProd = 'app' + Date.now() + ".js";
20 var appCssProd = 'app' + Date.now() + ".css";
21 var indexTemplate = __dirname + '/index.html';
22
23 var base = grunt.option('targetBase');
24 var cmd = grunt.option('cmd').toString();
25 process.env.targetBase = base;
26
27 var destination = grunt.option('destination');
28 var serverPath = grunt.option('server');
29
30 //override index html
31 if(grunt.file.exists(base + "/index.html")){
32 indexTemplate = base + "/index.html";
33 }
34
35 // build ignore source pattern
36 var ignore = ['components','dist','node_modules'];
37 var packageJson = grunt.file.readJSON(base + '/package.json');
38 if(packageJson.compy && packageJson.compy.paths) {
39 ignore = ignore.concat(packageJson.compy.paths);
40 }
41 var ignoreString = '(' + ignore.join('|') + ')';
42 //prepare projects karma plugins
43 // mocha, jasmine, sinon - whatewer
44 // set'em up if they are declated in config
45 var customTestSetup = (packageJson.compy && packageJson.compy.tests)?packageJson.compy.tests:{};
46 if(customTestSetup.plugins && customTestSetup.plugins.length > 0){
47 // we can error handle here and check if those plugins are installed in project's folder
48 // also we can check if they are installed in compy's node_modules folder
49 customTestSetup.plugins = _(customTestSetup.plugins).map(function(plugin){
50 return base + "/node_modules/" + plugin;
51 });
52 }
53
54
55 var compyGruntConfig = {
56 pkg: packageJson,
57 dest: destination,
58 targetBase: base,
59 // this is like component.json contents for component
60 componentConfig:{
61 name: '<%= pkg.name %>',
62 main: '<%= pkg.compy.main %>',
63 author: '<%= pkg.author %>',
64 description: '<%= pkg.description %>',
65 dependencies: '<%= pkg.compy.dependencies %>',
66 development: '<%= pkg.compy.development %>',
67 version: '<%= pkg.version %>',
68 license: '<%= pkg.license %>',
69 scripts:'<%= src.scripts %>',
70 styles: '<%= src.styles %>',
71 images: '<%= src.images %>',
72 fonts: '<%= src.fonts %>',
73 templates: '<%= src.templates %>',
74 paths: '<%= pkg.compy.paths %>',
75 local: '<%= pkg.compy.local %>',
76 remotes: '<%= pkg.compy.remotes %>',
77 keywords: '<%= pkg.keywords %>',
78 repo: (function(){
79 if(packageJson.repository && packageJson.repository.type === "git"){
80 var repo = /([^\/]*?\/[^\/]*?)\.git/.exec(packageJson.repository.url);
81 return repo && repo[1];
82 }
83 return null;
84 })()
85 },
86 // we-re taking all sources from here
87 src:{
88 scripts:[ base + '{/!'+ignoreString+'/**/*.js,/*.js}'],
89 styles:[ base + '{/!'+ignoreString+'/**/*.css,/*.css}'],
90 images:[ base+ '/!'+ignoreString+'/**/*.{jpg,png,gif,icn}', base+ '/*.{jpg,png,gif,icn}'],
91 fonts:[ base+ '/!'+ignoreString+'/**/*.{ttf,eof}', base + '/*.{ttf,eof}'],
92 templates: [ base+ '{/!'+ignoreString+'/**/*.html,/*.html}', '!' + base + '/index.html'],
93 tests:[ base + '{/!'+ignoreString+'/**/*.spec.js,/*.spec.js}']
94 },
95 // we clean up generated source
96 clean: {
97 options:{force:true},
98 dist:['<%= dest %>']
99 },
100 // we use customized component build grunt task
101 component_constructor:{
102 options:{
103 output:'<%= dest %>',
104 config:'<%= componentConfig %>',
105 base: base,
106 sourceUrls: false
107 },
108 app:{
109 options:{
110 target: 'app',
111 standalone: grunt.option('isStandaloneLib'),
112 configure: function(builder){
113 // we overwrite dependencies to be able to hot component reload while watch
114 var pkg = grunt.file.readJSON(base + '/package.json');
115 if(pkg.compy.dependencies){
116 builder.config.dependencies = pkg.compy.dependencies;
117 }
118 ignoreSources(builder.config, grunt.config('src.tests'));
119 usePlugins(base, builder);
120 }
121 }
122 },
123 test: {
124 options:{
125 dev: true,
126 configure: function(builder){
127 // we overwrite dependencies to be able to hot component reload while watch
128 var pkg = grunt.file.readJSON(base + '/package.json');
129 if(pkg.compy.dependencies){
130 builder.config.dependencies = pkg.compy.dependencies;
131 }
132 ignoreSources(builder.config);
133 usePlugins(base, builder);
134 }
135 }
136 },
137 styles_dev: {
138 options:{
139 name: 'app',
140 assetType: 'styles',
141 configure: function(builder){
142 // we overwrite dependencies to be able to hot component reload while watch
143 var pkg = grunt.file.readJSON(base + '/package.json');
144 if(pkg.compy.dependencies){
145 builder.config.dependencies = pkg.compy.dependencies;
146 }
147 ignoreSources(builder.config);
148 usePlugins(base, builder);
149 }
150 }
151 },
152 scripts_dev: {
153 options:{
154 name: 'app',
155 assetType: 'scripts',
156 configure: function(builder){
157 // we overwrite dependencies to be able to hot component reload while watch
158 var pkg = grunt.file.readJSON(base + '/package.json');
159 if(pkg.compy.dependencies){
160 builder.config.dependencies = pkg.compy.dependencies;
161 }
162 ignoreSources(builder.config);
163 usePlugins(base, builder);
164 }
165 }
166 },
167 templates_dev: {
168 options:{
169 name: 'app',
170 assetType: 'templates',
171 configure: function(builder){
172 // we overwrite dependencies to be able to hot component reload while watch
173 var pkg = grunt.file.readJSON(base + '/package.json');
174 if(pkg.compy.dependencies){
175 builder.config.dependencies = pkg.compy.dependencies;
176 }
177 ignoreSources(builder.config);
178 usePlugins(base, builder);
179 }
180 }
181 }
182 },
183 watch: {
184 options:{
185 livereload: config.livereloadPort,
186 nospawn: true
187 },
188 // we watch sources independantly, but that doesn't makes much sense
189 js: {
190 files: '<%= src.scripts %>',
191 tasks: ['component_constructor:scripts_dev','concat:dist']
192 },
193 css:{
194 files: '<%= src.styles %>',
195 tasks: ['component_constructor:app', 'concat:dist']
196 },
197 html:{
198 files: '<%= src.templates %>',
199 tasks: ['component_constructor:templates_dev','concat:dist']
200 }
201 },
202 connect: {
203 options: {
204 port: 8080,
205 base: '<%= dest %>',
206 hostname: null,
207 },
208 server:{
209 options: {
210 keepalive: false,
211 middleware: function(connect, options){
212 return [
213 require('connect-livereload')({port:config.livereloadPort}),
214 folderMount(connect, destination),
215 folderDir(connect, destination)]
216 }
217 }
218 },
219 alive:{
220 options: {
221 keepalive: true,
222 }
223 }
224 },
225 // preprocess used to build proper index.html
226 preprocess:{
227 html:{
228 options:{
229 context:{
230 name: '<%= pkg.name %>',
231 main: '<%= pkg.compy.main %>',
232 description: '<%= pkg.description %>',
233 title: '<%= pkg.title %>',
234 appdest: 'app.js',
235 appcss: 'app.css'
236 }
237 },
238 src: indexTemplate,
239 dest:'<%= dest %>/index.html'
240 },
241 build:{
242 options:{
243 context:{
244 name: '<%= pkg.name %>',
245 main: '<%= pkg.compy.main %>',
246 description: '<%= pkg.description %>',
247 title: '<%= pkg.title %>',
248 appdest: appJsProd,
249 appcss: appCssProd
250 }
251 },
252 src: indexTemplate,
253 dest:'<%= dest %>/index.html'
254 }
255 },
256 // concat is used to add component runner to the app
257 concat: {
258 options:{
259 banner: '<%= pkg.compy.banner %>'
260 },
261 dist: {
262 src: ['<%= dest %>/app.js', __dirname + '/tmpl/runner.js'],
263 dest: '<%= dest%>/app.js'
264 }
265 },
266 uglify: {
267 build: {
268 src: ['<%= dest%>/app.js'],
269 dest: '<%= dest%>/' + appJsProd
270 }
271 },
272 cssmin: {
273 build: {
274 src: ['<%= dest%>/app.css'],
275 dest: '<%= dest%>/' + appCssProd
276 }
277 },
278 karma: {
279 unit: _({
280 autoWatch:false,
281 browsers:["PhantomJS"],
282 colors: true,
283 configFile: __dirname + '/tmpl/karma.config.js',
284 reporters:['dots'],
285 singleRun: true,
286 plugins: [
287 'karma-phantomjs-launcher',
288 'karma-chrome-launcher',
289 'karma-firefox-launcher'
290 ]
291 }).extend(customTestSetup)
292 }
293 }
294 var matchPlugins = require('./component-plugins-matching.js');
295 matchPlugins(compyGruntConfig, getPlugins(base));
296
297 function usePlugins(baseDir, builder){
298 var plugins = getPlugins(baseDir);
299 plugins.forEach(function(plugin){
300 var pluginModule = require(baseDir + "/node_modules/" + plugin);
301 if(matchPlugins.config[plugin] && matchPlugins.config[plugin].run){
302 return matchPlugins.config[plugin].run(pluginModule, builder);
303 }
304 builder.use(pluginModule);
305 })
306 }
307 /*
308 * getPlugins is getting plugins from users project node_modules folder
309 */
310 function getPlugins(baseDir){
311 var componentPlugins = [];
312 if(!fs.existsSync(baseDir + "/node_modules")) return componentPlugins;
313 var nodeModules = fs.readdirSync(baseDir + "/node_modules");
314 nodeModules.forEach(function(module){
315 if(!/^component-/.test(module)) return;
316 componentPlugins.push(module);
317 });
318 return componentPlugins;
319 }
320
321
322 // this dark magic allows to extend Gruntfiles
323 if(grunt.file.exists(base + '/Gruntfile.js')){
324 var innerGruntfile = require(base + '/Gruntfile.js');
325 var oldInit = grunt.initConfig;
326 var pseudoGrunt = grunt;
327 pseudoGrunt.initConfig = initConfig;
328 function initConfig(configObject){
329 for(var key in configObject){
330 compyGruntConfig[key] = configObject[key];
331 }
332 oldInit.call(this, compyGruntConfig);
333 }
334 innerGruntfile(pseudoGrunt);
335
336 var oldReg = grunt.registerTask
337 grunt.registerTask = function(){
338 if(grunt.task._tasks[arguments[0]]) return;
339 oldReg.apply(this, arguments);
340 }
341 }else{
342 grunt.initConfig(compyGruntConfig);
343 }
344 function ignoreSources(config, ignorePatterns){
345 ['images','fonts','scripts','styles','templates'].forEach(function(asset){
346 var testFor = new RegExp('^(' + ignore.join('|') + ')\\/');
347 var ignoreFiles = [];
348 if(ignorePatterns){
349 ignoreFiles = ignoreFiles.concat(grunt.file.expand(ignorePatterns));
350 }
351 var remap = [];
352 if(!config[asset]) return;
353 config[asset].forEach(function(filepath){
354 if(!!~ignoreFiles.indexOf(filepath)) return;
355 var relPath = path.relative(base, filepath);
356 if(testFor.test(relPath)) return;
357 remap.push(relPath.replace(/\\/g,"/"));// windows hackin
358 })
359 config[asset] = remap;
360 })
361 }
362 grunt.config.set('utils.ignoreSources', ignoreSources);
363
364 if(!!~['build','compile','server','test','watch'].indexOf(cmd)) grunt.loadTasks(__dirname + '/node_modules/grunt-component-constructor/tasks');
365 if(!!~['server'].indexOf(cmd)) grunt.loadTasks(__dirname + '/node_modules/grunt-contrib-connect/tasks');
366 if(!!~['server','watch'].indexOf(cmd)) grunt.loadTasks(__dirname + '/node_modules/grunt-contrib-watch/tasks');
367 if(!!~['build','compile','test'].indexOf(cmd)) grunt.loadTasks(__dirname + '/node_modules/grunt-contrib-clean/tasks');
368 if(!!~['build','compile'].indexOf(cmd)) grunt.loadTasks(__dirname + '/node_modules/grunt-preprocess/tasks');
369 if(!!~['build','compile','server'].indexOf(cmd)) grunt.loadTasks(__dirname + '/node_modules/grunt-contrib-concat/tasks');
370 if(!!~['build'].indexOf(cmd)) grunt.loadTasks(__dirname + '/node_modules/grunt-contrib-uglify/tasks');
371 if(!!~['build'].indexOf(cmd)) grunt.loadTasks(__dirname + '/node_modules/grunt-contrib-cssmin/tasks');
372 if(!!~['test'].indexOf(cmd)) grunt.loadTasks(__dirname + '/node_modules/grunt-karma/tasks');
373
374
375 grunt.registerTask('install', 'Install component', function(){
376 var config = grunt.config('componentConfig');
377 ['images','fonts','scripts','styles','templates'].forEach(function(asset){
378 if(config[asset]){
379 config[asset] = grunt.file.expand(config[asset]);
380 }
381 });
382 var done = this.async();
383
384 ignoreSources(config);
385 var args = [];
386 var pkgCheck = process.argv.slice(-1)[0].split(':');
387 if(pkgCheck.length > 1){
388 pkgCheck.shift();
389 args = args.concat(pkgCheck);
390 }
391 if(!config.dependencies) config.dependencies = {};
392 var dev = grunt.option('devInstall');
393
394 compInstall(config, {args: args, out: base + "/components", force: grunt.option('force'), dev: dev}, installed);
395
396 function installed(err, deps){
397 if(args.length === 0) return done();
398 var pkg = grunt.file.readJSON(base + '/package.json');
399 pkg.compy.dependencies = deps;
400 grunt.file.write(base + '/package.json', JSON.stringify(pkg, null, 2));
401 done();
402 }
403 })
404
405
406 require('./src/publish.js')(grunt);
407
408
409 grunt.registerTask('generate-tests-runner', function(){
410 var specFiles = grunt.config('src.tests');
411 specFiles = grunt.file.expand(specFiles);
412 var source = "";
413 specFiles.forEach(function(file){
414 var runModule = [path.basename(base), path.relative(base, file)].join('/');
415 source += "require('"+runModule+"');\n";
416 });
417 grunt.file.write(path.normalize(base + '/' + grunt.config('dest') + "/runner.js"), source);
418 })
419
420 grunt.registerTask('server', 'run server', function(arg){
421 if(arg =="watch"){
422 serverPath ? require(serverPath) : grunt.task.run('connect:server');
423 return grunt.task.run('watch');
424 }else{
425 serverPath ? require(serverPath) : grunt.task.run('connect:alive');
426 }
427 });
428
429 grunt.registerTask('compy-compile', ['clean:dist', 'component_constructor:app','concat:dist','preprocess:html']);
430
431 grunt.registerTask('compy-build', ['clean:dist', 'component_constructor:app','concat:dist','preprocess:build', 'uglify', 'cssmin']);
432
433 grunt.registerTask('compy-test', ['clean:dist', 'component_constructor:test','generate-tests-runner' ,'karma'])
434
435 grunt.registerTask('compile', ['compy-compile']);
436
437 grunt.registerTask('build', ['compy-build']);
438
439 grunt.registerTask('test', ['compy-test']);
440
441 grunt.registerTask('default', ['compile']);
442}
443