diff --git a/cli.js b/cli.js new file mode 100644 index 0000000..789d867 --- /dev/null +++ b/cli.js @@ -0,0 +1,53 @@ +#!/usr/bin/env node + +var fs = require('fs'); +var meteorBuildClient = require('./index.js'); +var packageJson = require('./package.json'); + +// CLI Options +var program = require('commander'); + +// VARIABLES +var argPath = process.argv[2]; + + +program + .version(packageJson.version) + .usage(' [options]') + .option('-i, --input ', 'The path to the meteor project.') + .option('-p, --path ', 'The path used to link the files, default is "/", pass "" to link relative to the index.html.') + .option('-t, --template ', 'Provide a custom index.html template. Use {{> head}}, {{> css}} and {{> scripts}} to place the meteor resources.') + .option('-s, --settings ', 'Set optional data for Meteor.settings in your application.') + .option('-u, --url ', 'The Root URL of your app. If "default", Meteor will try to connect to the Server where it was served from. Default is: "" (empty string)') + // .option('-d, --ddp ', 'The URL of your Meteor DDP server, e.g. "ddp+sockjs://ddp.myapp.com/sockjs". If you don\'t add any it will also add call "Meteor.disconnect();" to prevent the app from conneting.') + .parse(process.argv); + + +// RUN TASKS + +if(!argPath) { + console.error('You need to provide a path for the build output, for example:'); + console.error('$ meteor-build-client myBuildFolder'); + +} else { + + // start building the meteor client + meteorBuildClient({ + input: program.input, + output: argPath, + template: program.template, + settings: program.settings, + url: program.url, + path: program.path, + feedback: true, + }, function(err) { + if (err) { + console.error(err); + } + }); +} + + + + + diff --git a/index.html b/index.html index 7432677..df3dce9 100644 --- a/index.html +++ b/index.html @@ -4,6 +4,7 @@ {{> css}} {{> scripts}} {{> head}} + diff --git a/index.js b/index.js new file mode 100644 index 0000000..2577164 --- /dev/null +++ b/index.js @@ -0,0 +1,96 @@ +var fs = require('fs'); +var path = require('path'); +var Queue = require('./queue'); +var meteor = require('./meteor.js'); +var _ = require('underscore'); + +function meteorBuildClient(config, done) { + config = _.clone(config || {}); + done = _.isFunction(done) ? done : function(){}; + + _.defaults(config, { + input: './', + output: './.meteor-build', + template: null, + settings: null, // path or object + url: null, + path: "/", + feedback: false, // output console messages + }); + + config.input = path.resolve(config.input); + config.output = path.resolve(config.output); + + if (_.isString(config.template)) + config.template = path.resolve(config.template); + if (_.isString(config.settings)) + config.settings = path.resolve(config.settings); + + config.bundleName = path.basename(config.input); + + // Build queue syncron + var queue = new Queue(); + + + // check if in meteor folder + try { + if(!fs.lstatSync(path.join(config.input, '.meteor')).isDirectory()) + throw new Error(); + + } catch(e) { + done('You\'re not in a Meteor app folder or inside a sub folder of your app.'); + return; + } + + // check template file + if(config.template) { + try { + if(!fs.lstatSync(config.template).isFile()) + throw new Error(); + + } catch(e) { + done('The template file "'+ config.template +'" doesn\'t exist or is not a valid template file'); + return; + } + } + + // build meteor + queue.add(function(callback){ + if (config.feedback) + console.log('Bundling Meteor app...'); + meteor.build(config, callback); + }); + + // move the files into the build folder + queue.add(function(callback){ + if (config.feedback) + console.log('Generating the index.html...'); + meteor.move(config, callback); + }); + + // create the index.html + queue.add(function(callback){ + meteor.addIndexFile(config, callback); + }); + + // delete unecessary fiels + queue.add(function(callback){ + meteor.cleanUp(config, function(){ + if (config.feedback) { + console.log('Done!'); + console.log('-----'); + console.log('You can find your files in "'+ config.output +'".'); + } + callback(); + }); + }); + + // done + queue.add(function() { + done(null); + }); + + queue.run(); +} + +module.exports = meteorBuildClient; \ No newline at end of file diff --git a/main.js b/main.js deleted file mode 100644 index 6d7a7ba..0000000 --- a/main.js +++ /dev/null @@ -1,100 +0,0 @@ -#!/usr/bin/env node - -var fs = require('fs'); - -// CLI Options -var program = require('commander'); -// Queue -var Queue = require('./queue'); -// Build queue syncron -var queue = new Queue(); - -var packageJson = require('./package.json'); - -// VARIABLES -var argPath = process.argv[2]; -var meteor = require('./meteor.js'); - -program - .version(packageJson.version) - .usage(' [options]') - .option('-p, --path ', 'The path used to link the files, default is "/", pass "" to link relative to the index.html.') - .option('-t, --template ', 'Provide a custom index.html template. Use {{> head}}, {{> css}} and {{> scripts}} to place the meteor resources.') - .option('-s, --settings ', 'Set optional data for Meteor.settings in your application.') - .option('-u, --url ', 'The Root URL of your app. If "default", Meteor will try to connect to the Server where it was served from. Default is: "" (empty string)') - // .option('-d, --ddp ', 'The URL of your Meteor DDP server, e.g. "ddp+sockjs://ddp.myapp.com/sockjs". If you don\'t add any it will also add call "Meteor.disconnect();" to prevent the app from conneting.') - .parse(process.argv); - - -// RUN TASKS - -// TODO: get meteor apps basepath and set it as cwd -// console.log(process.cwd()); -// process.chdir('new cwd'); - -if(!argPath) { - console.error('You need to provide a path for the build output, for example:'); - console.error('$ meteor-build-client myBuildFolder'); - -} else { - - (function(){ - - // check if in meteor folder - try { - if(!fs.lstatSync('./.meteor').isDirectory()) - throw new Error(); - - } catch(e) { - console.error('You\'re not in a Meteor app folder or inside a sub folder of your app.'); - return; - } - - // check template file - if(program.template) { - try { - if(!fs.lstatSync(program.template).isFile()) - throw new Error(); - - } catch(e) { - console.error('The template file "'+ program.template +'" doesn\'t exist or is not a valid template file'); - return; - } - } - - // build meteor - queue.add(function(callback){ - console.log('Bundling Meteor app...'); - meteor.build(program, callback); - }); - - // move the files into the build folder - queue.add(function(callback){ - console.log('Generating the index.html...'); - meteor.move(callback); - }); - - // create the index.html - queue.add(function(callback){ - meteor.addIndexFile(program, callback); - }); - - // delete unecessary fiels - queue.add(function(callback){ - meteor.cleanUp(function(){ - console.log('Done!'); - console.log('-----'); - console.log('You can find your files in "'+ require('path').resolve(argPath) +'".'); - - callback(); - }); - }); - - queue.run(); - })() -} - - - - - diff --git a/meteor.js b/meteor.js index 8e302bd..2c77fbf 100644 --- a/meteor.js +++ b/meteor.js @@ -3,28 +3,26 @@ var fs = require('fs'); var path = require('path'); var _ = require('underscore'); var spinner = require('simple-spinner'); +var exec = require('child_process').exec; -// VARIABLES -var argPath = process.argv[2], - basePath = './', - buildPath = path.resolve(argPath), - bundleName = path.basename(path.resolve(basePath)); // execute shell scripts -var execute = function(command, name, complete) { - var exec = require('child_process').exec; +var execute = function(config, command, name, complete) { + var completeFunc = (typeof complete === 'function') ? complete : function(){}; - var completeFunc = (typeof complete === 'function') ? complete : console.log; + if (config.feedback) + spinner.start(); - spinner.start(); exec(command, { - cwd: basePath + cwd: config.input },function(err, res) { - spinner.stop(); + if (config.feedback) + spinner.stop(); //process error if(err){ - console.log(err.message); + if (config.feedback) + console.log(err.message); completeFunc(err); } else { @@ -50,38 +48,39 @@ var deleteFolderRecursive = function(path) { }; + module.exports = { - build: function(program, callback){ + build: function(config, callback){ // remove the bundle folder - deleteFolderRecursive(buildPath); + deleteFolderRecursive(config.output); - var command = 'meteor build '+ argPath + ' --directory'; + var command = 'meteor build '+ config.output + ' --directory'; - if(program.url) - command += ' --server '+ program.url; + if(config.url) + command += ' --server '+ config.url; - // if(program.settings) - // command += ' --mobile-settings '+ program.settings; + // if(config.settings) + // command += ' --mobile-settings '+ config.settings; // console.log('Running: '+ command); - execute(command, 'build the app, are you in your meteor apps folder?', callback); + execute(config, command, 'build the app, are you in your meteor apps folder?', callback); }, - move: function(callback){ - + move: function(config, callback){ try { _.each([ '/bundle/programs/web.browser', '/bundle/programs/web.browser/app' ], function(givenPath){ - var clientPath = path.join(buildPath, givenPath); + var clientPath = path.join(config.output, givenPath); + if (!fs.existsSync(clientPath)) return; var rootFolder = fs.readdirSync(clientPath); rootFolder = _.without(rootFolder, 'app'); rootFolder.forEach( function (file) { var curSource = path.join(clientPath, file); - fs.renameSync(path.join(clientPath, file), path.join(buildPath, file)); + fs.renameSync(path.join(clientPath, file), path.join(config.output, file)); }); }); } catch(e) { @@ -90,24 +89,32 @@ module.exports = { callback(); }, - addIndexFile: function(program, callback){ - var starJson = require(path.resolve(buildPath) + '/bundle/star.json'); - var settingsJson = program.settings ? require(path.resolve(program.settings)) : {}; + addIndexFile: function(config, callback){ + var starJson = require(path.resolve(config.output) + '/bundle/star.json'); + var settingsJson; + if (!config.settings) { + settingsJson = {}; + } else if (_.isString(config.settings)) { + settingsJson = require(path.resolve(config.settings)); + } else { + settingsJson = _.clone(config.settings); + } - var content = fs.readFileSync(program.template || path.resolve(__dirname, 'index.html'), {encoding: 'utf-8'}); + var content = fs.readFileSync(config.template || path.resolve(__dirname, 'index.html'), {encoding: 'utf-8'}); var head; try{ - head = fs.readFileSync(path.join(buildPath, 'head.html'), {encoding: 'utf8'}); + head = fs.readFileSync(path.join(config.output, 'head.html'), {encoding: 'utf-8'}); } catch(e) { head = ''; - console.log('No found in Meteor app...'); + if (config.feedback) + console.log('No found in Meteor app...'); } // ADD HEAD content = content.replace(/{{ *> *head *}}/,head); // get the css and js files var files = {}; - _.each(fs.readdirSync(buildPath), function(file){ + _.each(fs.readdirSync(config.output), function(file){ if(/^[a-z0-9]{40}\.css$/.test(file)) files['css'] = file; if(/^[a-z0-9]{40}\.js$/.test(file)) @@ -115,19 +122,19 @@ module.exports = { }); // MAKE PATHS ABSOLUTE - if(_.isString(program.path)) { + if(_.isString(config.path)) { // fix paths in the CSS file if(!_.isEmpty(files['css'])) { - var cssFile = fs.readFileSync(path.join(buildPath, files['css']), {encoding: 'utf8'}); - cssFile = cssFile.replace(/url\(\'\//g, 'url(\''+ program.path).replace(/url\(\//g, 'url('+ program.path); - fs.unlinkSync(path.join(buildPath, files['css'])); - fs.writeFileSync(path.join(buildPath, files['css']), cssFile, {encoding: 'utf8'}); + var cssFile = fs.readFileSync(path.join(config.output, files['css']), {encoding: 'utf-8'}); + cssFile = cssFile.replace(/url\(\'\//g, 'url(\''+ config.path).replace(/url\(\//g, 'url('+ config.path); + fs.unlinkSync(path.join(config.output, files['css'])); + fs.writeFileSync(path.join(config.output, files['css']), cssFile, {encoding: 'utf-8'}); - files['css'] = program.path + files['css']; + files['css'] = config.path + files['css']; } - files['js'] = program.path + files['js']; + files['js'] = config.path + files['js']; } else { if(!_.isEmpty(files['css'])) files['css'] = '/'+ files['css']; @@ -148,41 +155,41 @@ module.exports = { 'meteorRelease': starJson.meteorRelease, 'ROOT_URL_PATH_PREFIX': '', meteorEnv: { NODE_ENV: 'production' }, - // 'DDP_DEFAULT_CONNECTION_URL': program.url || '', // will reload infinite if Meteor.disconnect is not called + // 'DDP_DEFAULT_CONNECTION_URL': config.url || '', // will reload infinite if Meteor.disconnect is not called // 'appId': process.env.APP_ID || null, // 'autoupdateVersion': null, // "ecf7fcc2e3d4696ea099fdd287dfa56068a692ec" // 'autoupdateVersionRefreshable': null, // "c5600e68d4f2f5b920340f777e3bfc4297127d6e" // 'autoupdateVersionCordova': null }; // on url = "default", we dont set the ROOT_URL, so Meteor chooses the app serving url for its DDP connection - if(program.url !== 'default') - settings.ROOT_URL = program.url || ''; + if(config.url !== 'default') + settings.ROOT_URL = config.url || ''; if(settingsJson.public) settings.PUBLIC_SETTINGS = settingsJson.public; scripts = scripts.replace('__meteor_runtime_config__', ''); - + // add Meteor.disconnect() when no server is given - if(!program.url) + if(!config.url) scripts += ' '; content = content.replace(/{{ *> *scripts *}}/, scripts); // write the index.html - fs.writeFile(path.join(buildPath, 'index.html'), content, callback); + fs.writeFile(path.join(config.output, 'index.html'), content, callback); }, - cleanUp: function(callback) { - + cleanUp: function(config, callback) { // remove files - deleteFolderRecursive(path.join(buildPath, 'bundle')); - fs.unlinkSync(path.join(buildPath, 'program.json')); + deleteFolderRecursive(path.join(config.output, 'bundle')); + fs.unlinkSync(path.join(config.output, 'program.json')); try{ - fs.unlinkSync(path.join(buildPath, 'head.html')); + fs.unlinkSync(path.join(config.output, 'head.html')); } catch (e){ - console.log("Didn't unlink head.html; doesn't exist."); + if (config.feedback) + console.log("Didn't unlink head.html; doesn't exist."); } callback(); - } + }, } diff --git a/package.json b/package.json index 2672731..64f94a0 100644 --- a/package.json +++ b/package.json @@ -26,9 +26,9 @@ "browser-only" ], "bin": { - "meteor-build-client": "main.js" + "meteor-build-client": "cli.js" }, - "main": "main.js", + "main": "index.js", "dependencies": { "commander": "^2.8.1", "simple-spinner": "0.0.3",