├── .gitignore ├── LICENSE ├── README.md ├── full-stack.gif ├── package.json ├── release.md ├── src ├── app.js ├── config │ ├── base-project │ │ ├── .gitignore │ │ ├── .jsbeautifyrc │ │ ├── README.md │ │ ├── app.ts │ │ ├── bin │ │ │ └── www.ts │ │ ├── client │ │ │ ├── app.ts │ │ │ ├── components │ │ │ │ ├── account-details │ │ │ │ │ ├── account-details.component.css │ │ │ │ │ ├── account-details.component.html │ │ │ │ │ └── account-details.component.ts │ │ │ │ ├── accounts │ │ │ │ │ ├── account.model.ts │ │ │ │ │ ├── accounts.component.css │ │ │ │ │ ├── accounts.component.html │ │ │ │ │ ├── accounts.component.ts │ │ │ │ │ └── accounts.service.ts │ │ │ │ ├── app │ │ │ │ │ ├── app.component.css │ │ │ │ │ ├── app.component.html │ │ │ │ │ ├── app.component.ts │ │ │ │ │ ├── app.module.ts │ │ │ │ │ └── app.routing.ts │ │ │ │ └── menu │ │ │ │ │ ├── menu.component.css │ │ │ │ │ ├── menu.component.html │ │ │ │ │ └── menu.component.ts │ │ │ ├── index.html │ │ │ ├── polyfills.ts │ │ │ └── vendor.ts │ │ ├── config │ │ │ ├── build.js │ │ │ ├── settings.json │ │ │ ├── webpack.client.js │ │ │ └── webpack.server.js │ │ ├── package.json │ │ ├── server │ │ │ ├── controllers │ │ │ │ └── accounts.ts │ │ │ ├── models │ │ │ │ ├── accounts-schema.ts │ │ │ │ └── accounts.ts │ │ │ ├── routes │ │ │ │ └── routes.ts │ │ │ └── views │ │ │ │ ├── error.ejs │ │ │ │ ├── index.ejs │ │ │ │ └── partials │ │ │ │ └── json-output.ejs │ │ ├── tsconfig.json │ │ └── typings.json │ └── directories.js └── generators │ └── mean-stack.js └── tests └── app.js /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | build/ 3 | typings/ 4 | node_modules/ 5 | .DS* 6 | ._.* 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 JSecademy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Scaffold your project in seconds using the MEAN Stack 2 | 3 | ## This project is made possible thanks to all of the supporters that are moving their career forward with the [MEAN Stack 2 [Node.js, Express, MongoDB, Angular 2]](https://list.codewithintent.com/full-stack?list=codewithintent&source=github-email-course&form=28&dest=full-stack-confirm). 4 | 5 |  6 | 7 | To all of you, that have no one on your team... 8 | 9 | To all those who have failed and have not given up... 10 | 11 | To all that know that if you are willing to work, you too can achieve... 12 | 13 | I welcome you! 14 | 15 | This tool was designed and created for Entrepreneurs, Innovators, and Consultants. 16 | 17 | This project strongly believes in the Agile Software Development practices. 18 | 19 | * **Individuals and interactions** over processes and tools 20 | * **Working software** over comprehensive documentation 21 | * **Customer collaboration** over contract negotiation 22 | * **Responding to change** over following a plan 23 | 24 | But more than just a practice, this project stands behind even stronger values. 25 | 26 | * This project is safe, meets specifications, passes appropriate tests, and does not diminish the quality of life or diminish privacy. 27 | * The ultimate effect of this project is to do the public good 28 | * This project strives for high quality, acceptable cost and a reasonable schedule, ensuring significant tradeoffs are clear to and accepted by all of the core team members. 29 | 30 | **This is not the *perfect* arrangement of the MEAN Stack. It exists primarily to get you started quickly with learning and prototyping using the MEAN Stack.** 31 | 32 | ### Getting Started 33 | 34 | ``` 35 | sudo npm install full-stack -g 36 | full-stack init myProjectName 37 | ``` 38 | 39 | You can learn more about the tool at [MEANStack.net](https://www.meanstack.net/) 40 | 41 | If you are completely new to the MEAN Stack it might be a bit frustrating trying to understand how all of this pieces are put together. I have created a FREE 20-day email course that walks you through the entire stack in just 1 message per day for 20 days. 42 | 43 | ## [MEAN Stack 2 Email Course](https://list.codewithintent.com/full-stack?list=codewithintent&source=github-email-course&form=28&dest=full-stack-confirm) 44 | -------------------------------------------------------------------------------- /full-stack.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsecademy/full-stack/dee5b3405d42153c5ec96b45132649a70e9a2336/full-stack.gif -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "full-stack", 3 | "version": "1.0.2", 4 | "description": "Built for Entrepreneurs, Innovators, and Consultants. Scaffold a project in seconds using the MEAN Stack 2", 5 | "bin": { 6 | "full-stack": "./src/app.js" 7 | }, 8 | "directories": { 9 | "test": "tests" 10 | }, 11 | "scripts": { 12 | "test": "echo \"Error: no test specified\" && exit 1" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/jsecademy/full-stack.git" 17 | }, 18 | "keywords": [ 19 | "MEAN Stack", 20 | "MEAN Stack 2.0" 21 | ], 22 | "author": "Rick Hernandez", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/jsecademy/full-stack/issues" 26 | }, 27 | "homepage": "https://github.com/jsecademy/full-stack#readme", 28 | "dependencies": { 29 | "chalk": "^1.1.3", 30 | "inquirer": "^1.2.2", 31 | "minimist": "^1.2.0", 32 | "progress": "^1.1.8", 33 | "shelljs": "^0.7.5" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /release.md: -------------------------------------------------------------------------------- 1 | npm version patch 2 | npm publish 3 | -------------------------------------------------------------------------------- /src/app.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | "use strict"; 3 | var shell = require('shelljs'); 4 | var chalk = require('chalk'); 5 | var minimist = require('minimist'); 6 | var mean_stack_1 = require('./generators/mean-stack'); 7 | var log = console.log; 8 | var args = minimist(process.argv.slice(2)); 9 | if (inArgs('help') || (args['_'].length === 0) && Object.keys(args).length === 1) { 10 | displayHelp(); 11 | process.exit(); 12 | } 13 | if (inArgs('version')) { 14 | mean_stack_1.MEANStack.getGlobalPath().then(function (path) { 15 | var version = mean_stack_1.MEANStack.getVersion(path + "/full-stack"); 16 | log(chalk.yellow("" + version)); 17 | }); 18 | } 19 | if (inArgs('serve')) { 20 | mean_stack_1.MEANStack.serveLocalInstance(process.cwd(), function (data) { 21 | // Live feed running from process 22 | process.stdout.write(data); 23 | }).then(function (stdout) { 24 | // All done 25 | process.exit(); 26 | }).catch(function (error) { 27 | log(chalk.red('Not able to find a a valid package.json file.')); 28 | process.exit(); 29 | }); 30 | } 31 | var projectName = args['_'][1]; // Get the Project Name 32 | if (inArgs('init') && projectName) { 33 | mean_stack_1.MEANStack.getGlobalPath().then(function (globalPath) { 34 | mean_stack_1.MEANStack.writableDirectory(process.cwd()).then(function () { 35 | mean_stack_1.MEANStack.initializeCore(globalPath).then(function () { 36 | (function generateApplication() { 37 | var options = { 38 | type: 'input', 39 | name: 'name', 40 | message: 'Name your application:', 41 | default: projectName, 42 | validate: function (name) { 43 | return !!name.trim() || 'Name is required'; 44 | } 45 | }; 46 | clear(); 47 | mean_stack_1.MEANStack.askQuestion(options).then(function (question) { 48 | mean_stack_1.MEANStack.directoryExist(process.cwd() + "/" + question.name).then(function () { 49 | // Folder exists 50 | clear(); 51 | log(chalk.white.bold.bgRed('That folder name is already taken:') + chalk.cyan(" " + process.cwd() + "/" + question.name)); 52 | var options = { 53 | type: 'list', 54 | name: 'action', 55 | message: 'What would you like to do?:', 56 | choices: ['Rename the application', ("Delete this directory: " + process.cwd() + "/" + question.name)] 57 | }; 58 | mean_stack_1.MEANStack.askQuestion(options).then(function (answer) { 59 | if (answer.action === 'Rename the application') { 60 | return generateApplication(); 61 | } 62 | if (answer.action === "Delete this directory: " + process.cwd() + "/" + question.name) { 63 | var options_1 = { 64 | type: 'confirm', 65 | name: 'remove', 66 | message: "Are you sure you want to delete this directory?", 67 | default: false 68 | }; 69 | mean_stack_1.MEANStack.askQuestion(options_1).then(function (answer) { 70 | if (answer.remove) { 71 | // Delete Directory 72 | shell.rm('-rf', process.cwd() + "/" + question.name); 73 | log(chalk.cyan('Deleted Directory: ') + chalk.red(process.cwd() + "/" + question.name)); 74 | // Directory does not exists generate app 75 | mean_stack_1.MEANStack.generateApplication(process.cwd(), question.name).then(function () { 76 | displayPostInstallMessage(question.name); 77 | }); 78 | } 79 | else { 80 | // Rename Directory 81 | return generateApplication(); 82 | } 83 | }); 84 | } 85 | }); 86 | }).catch(function (error) { 87 | // Directory does not exists generate app 88 | mean_stack_1.MEANStack.generateApplication(process.cwd(), question.name).then(function () { 89 | displayPostInstallMessage(question.name); 90 | }); 91 | }); 92 | }); 93 | }()); 94 | }).catch(function (error) { 95 | log(chalk.white.bold.bgRed('Core is MISSING!!!')); 96 | process.exit(); 97 | }); 98 | }).catch(function (error) { 99 | // Directory is not writable 100 | log(chalk.white.bgRed.bold('Parent directory is not writable:') + chalk.cyan(" " + process.cwd())); 101 | log(chalk.green.bold('Change Permissions of directory: ') + chalk.cyan('sudo chmod 0750 .')); 102 | process.exit(); 103 | }); 104 | }).catch(function (err) { 105 | log(chalk.white.bgRed('Global path for NPM can not be found!')); 106 | log(chalk.white.bgRed(err)); 107 | process.exit(); 108 | }); 109 | } 110 | if (inArgs('init') && !projectName) { 111 | displayHelp(); 112 | log(chalk.white.bgRed('init command requires a project name')); 113 | process.exit(); 114 | } 115 | function inArgs(name) { 116 | if (args['_'] && args['_'].length > 0) { 117 | // Example full-stack init 118 | return args['_'].find(function (item) { return item === name.toString(); }) !== undefined; 119 | } 120 | if (args[name.toString()] !== undefined) { 121 | // Example full-stack --init 122 | return true; 123 | } 124 | return false; 125 | } 126 | function displayHelp() { 127 | // Output help and exit 128 | log(chalk.cyan('\n Usage: ') + chalk.yellow('full-stack [options] [value]')); 129 | log(chalk.cyan('\n Options:')); 130 | log(chalk.yellow('\n init, --init') + chalk.cyan(' Creates a base project using the MEAN Stack')); 131 | log(chalk.yellow('\n serve, --serve') + chalk.cyan(' Serves the local instance the application.')); 132 | log(chalk.yellow('\n version, --version') + chalk.cyan(' Displays version')); 133 | log(chalk.yellow('\n help, --help') + chalk.cyan(' Displays the description for each of the flags available')); 134 | log(chalk.cyan('\n Read the README.md for more detailed options')); 135 | log(chalk.cyan('\n Example:')); 136 | log(chalk.cyan('\n $ ') + chalk.yellow('full-stack init myProjectName\n')); 137 | } 138 | function displayPostInstallMessage(name) { 139 | clear(); 140 | log(chalk.cyan("\n Application ") + chalk.cyan.bold("" + name) + chalk.cyan(" created")); 141 | log(chalk.cyan('\n Download dependencies:')); 142 | log(chalk.yellow(" $ cd " + name + " && npm install")); 143 | log(chalk.cyan('\n Start the application')); 144 | log(chalk.yellow(" $ full-stack serve\n")); 145 | } 146 | function requirements() { 147 | // Required Programs for the MEAN Stack 148 | if (!shell.which('git')) { 149 | log(chalk.white.bgRed.bold('Git is required! Install git: https://git-scm.com/')); 150 | process.exit(); 151 | } 152 | if (!shell.which('mongo')) { 153 | log(chalk.white.bgRed.bold('MongoDB is required! Install MongoDB: https://docs.mongodb.com/master/installation/')); 154 | process.exit(); 155 | } 156 | } 157 | function clear() { 158 | // process.stdout.write('\x1Bc'); 159 | } 160 | -------------------------------------------------------------------------------- /src/config/base-project/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | build/ 3 | typings/ 4 | node_modules/ 5 | .DS* 6 | ._.* 7 | npm-debug.log -------------------------------------------------------------------------------- /src/config/base-project/.jsbeautifyrc: -------------------------------------------------------------------------------- 1 | { 2 | "brace_style": "collapse-preserve-inline" 3 | } -------------------------------------------------------------------------------- /src/config/base-project/README.md: -------------------------------------------------------------------------------- 1 | ### [--ApplicationName--] -------------------------------------------------------------------------------- /src/config/base-project/app.ts: -------------------------------------------------------------------------------- 1 | import * as express from 'express'; 2 | import * as logger from 'morgan'; 3 | import * as bodyParser from 'body-parser'; 4 | import * as root from 'app-root-path'; 5 | import * as cookieParser from 'cookie-parser'; 6 | import * as session from 'express-session'; 7 | import * as routes from './server/routes/routes'; 8 | import * as mongoose from 'mongoose'; 9 | 10 | const settings = require('./config/settings.json'); 11 | const db = settings.database; 12 | 13 | mongoose.connect(`mongodb://localhost:${db.port}/${db.name}`); 14 | 15 | const app = express(); 16 | 17 | // setup sessions 18 | app.use(session({ 19 | secret: "hello-secret-hash", 20 | resave: false, 21 | saveUninitialized: false 22 | })); 23 | 24 | // view engine setup 25 | app.set('views', `${root.path}/server/views/`); 26 | app.set('view engine', 'ejs'); 27 | 28 | app.use(logger('dev')); 29 | app.use(bodyParser.json()); 30 | app.use(bodyParser.urlencoded({ 31 | extended: false 32 | })); 33 | 34 | app.use(cookieParser()); 35 | app.use('/api', routes); 36 | 37 | // catch 404 and forward to error handler 38 | app.use((req: express.Request, res: express.Response, next: express.NextFunction) => { 39 | let err: any = new Error('Not Found'); 40 | err.status = 404; 41 | next(err); 42 | }); 43 | 44 | // error handlers 45 | // development error handler 46 | // will print stacktrace 47 | if (app.get('env') === 'development') { 48 | app.use((err: any, req: express.Request, res: express.Response, next: express.NextFunction) => { 49 | res.status(err.status || 500); 50 | res.render('error', { 51 | message: err.message, 52 | error: err 53 | }); 54 | }); 55 | } 56 | 57 | app.use(function(err: any, req: express.Request, res: express.Response, next: Function) { 58 | res.status(err.status || 500); 59 | res.render('error', { 60 | message: err.message, 61 | error: {} 62 | }); 63 | }); 64 | 65 | export = app; -------------------------------------------------------------------------------- /src/config/base-project/bin/www.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | import * as app from '../app'; 5 | import * as http from 'http'; 6 | import * as debug from 'debug'; 7 | import * as mongoose from 'mongoose'; 8 | 9 | // binding to console 10 | let log = debug('modern-express:server'); 11 | log.log = console.log.bind(console); 12 | 13 | /** 14 | * Get port from environment and store in Express. 15 | */ 16 | 17 | let PORT = process.env.PORT || '4000'; 18 | 19 | function getPort(val) { 20 | /** 21 | * Normalize a port into a number, string, or false. 22 | */ 23 | const port = parseInt(val, 10); 24 | if (isNaN(port)) { 25 | // named pipe 26 | return val; 27 | } 28 | if (port >= 0) { 29 | // port number 30 | return port; 31 | } 32 | return false; 33 | } 34 | 35 | app.set('port', PORT); 36 | 37 | /** 38 | * Create HTTP server. 39 | */ 40 | const server = http.createServer(app); 41 | 42 | /** 43 | * Listen on provided port, on all network interfaces. 44 | */ 45 | server.listen(PORT); 46 | 47 | server.on('error', (error) => { 48 | /** 49 | * Event listener for HTTP server "error" event. 50 | */ 51 | if (error.syscall !== 'listen') { 52 | throw error; 53 | } 54 | const bind = typeof PORT === 'string' ? `Port:${PORT} is being used ` : `Port ${PORT}`; 55 | // handle specific listen errors with friendly messages 56 | switch (error.code) { 57 | case 'EACCES': 58 | console.error(bind + ' requires elevated privileges'); 59 | process.exit(1); 60 | break; 61 | case 'EADDRINUSE': 62 | console.error(bind + ' is already in use'); 63 | process.exit(1); 64 | break; 65 | default: 66 | throw error; 67 | } 68 | }); 69 | 70 | server.on('listening', () => { 71 | /** 72 | * Event listener for HTTP server "listening" event. 73 | */ 74 | const addr = server.address(); 75 | const bind = (typeof addr === 'string' ? `port ${addr}` : `port ${addr.port}`); 76 | log(`listening on ${bind}`); 77 | }); -------------------------------------------------------------------------------- /src/config/base-project/client/app.ts: -------------------------------------------------------------------------------- 1 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 2 | import { enableProdMode } from '@angular/core'; 3 | import { AppModule } from './components/app/app.module'; 4 | if (process.env.ENV === 'production') { 5 | enableProdMode(); 6 | } 7 | platformBrowserDynamic().bootstrapModule(AppModule); 8 | -------------------------------------------------------------------------------- /src/config/base-project/client/components/account-details/account-details.component.css: -------------------------------------------------------------------------------- 1 | .account { 2 | width: 550px; 3 | margin: 0 auto; 4 | padding: 15px; 5 | } 6 | 7 | h5 { 8 | margin: 0px; 9 | } 10 | 11 | .account p { 12 | text-align: left; 13 | font-size: 30px; 14 | } 15 | 16 | .form-item { 17 | font-size: 20px; 18 | text-align: left; 19 | margin-bottom: 15px; 20 | } 21 | 22 | form { 23 | margin-top: 30px; 24 | } 25 | 26 | input { 27 | width: 100%; 28 | height: 40px; 29 | font-size: 2em; 30 | } 31 | 32 | .item { 33 | border: none; 34 | } 35 | 36 | .item:disabled { 37 | color: gray !important; 38 | cursor: not-allowed !important; 39 | } 40 | 41 | input:focus { 42 | outline: none; 43 | } -------------------------------------------------------------------------------- /src/config/base-project/client/components/account-details/account-details.component.html: -------------------------------------------------------------------------------- 1 |
Id: {{account._id}}
3 |Created: {{account.created | date:'M/d/y h:m:s a'}}
4 |Full Name: {{account.name.first}} {{account.name.last}}
5 |Account Number: {{account.accountNumber}}
6 |Balance: ${{account.balance}}
7 |Id: {{lastViewed._id}}
13 |Created: {{lastViewed.created | date:'M/d/y h:m:s a'}}
14 |Full Name: {{lastViewed.name.first}} {{lastViewed.name.last}}
15 |Account Number: {{lastViewed.accountNumber}}
16 |Balance: ${{lastViewed.balance}}
17 |Id | 5 |Account Number | 6 |Balance | 7 |Created | 8 |Full Name | 9 |Action | 10 |
---|---|---|---|---|---|
{{account._id}} | 13 |{{account.accountNumber}} | 14 |${{account.balance}} | 15 |{{account.created | date:'M/d/y h:m:s a'}} | 16 |{{account.name.first}} {{account.name.last}} | 17 |18 | |
<%= error.stack %>-------------------------------------------------------------------------------- /src/config/base-project/server/views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
Account Number: <%= account.account %>
18 |Account Name: <%= account.name %> <%= account.lastName %>
19 |Account Balance: $<%= account.balance %>
20 | <% } %> 21 | 22 | -------------------------------------------------------------------------------- /src/config/base-project/server/views/partials/json-output.ejs: -------------------------------------------------------------------------------- 1 |2 | <% if(typeof data !== 'undefined') { %> 3 |
4 | <%= '\n' + JSON.stringify(data, null, 2).replace('[', '').replace(']', '')%> 5 |6 | <% } %> 7 | -------------------------------------------------------------------------------- /src/config/base-project/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es5", 5 | "noImplicitAny": false, 6 | "moduleResolution": "node", 7 | "sourceMap": true, 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "removeComments": false, 11 | "suppressImplicitAnyIndexErrors": true, 12 | "outDir": "build" 13 | }, 14 | "exclude": [ 15 | "node_modules", 16 | "config", 17 | "build", 18 | "release" 19 | ] 20 | } -------------------------------------------------------------------------------- /src/config/base-project/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": { 3 | "main": "typings/" 4 | }, 5 | "globalDependencies": { 6 | "app-root-path": "registry:dt/app-root-path#1.2.1+20160719232327", 7 | "body-parser": "registry:dt/body-parser#0.0.0+20160619023215", 8 | "cookie-parser": "registry:dt/cookie-parser#1.3.4+20160316155526", 9 | "core-js": "registry:dt/core-js#0.0.0+20160725163759", 10 | "debug": "registry:dt/debug#0.0.0+20160317120654", 11 | "express": "registry:dt/express#4.0.0+20160708185218", 12 | "express-serve-static-core": "registry:dt/express-serve-static-core#4.0.0+20160829034835", 13 | "express-session": "registry:dt/express-session#0.0.0+20160819134112", 14 | "mime": "registry:dt/mime#0.0.0+20160316155526", 15 | "mongodb": "registry:dt/mongodb#2.1.0+20160602142941", 16 | "mongoose": "registry:dt/mongoose#4.5.9+20160826204928", 17 | "morgan": "registry:dt/morgan#1.7.0+20160524142355", 18 | "node": "registry:dt/node#6.0.0+20160830141956", 19 | "serve-static": "registry:dt/serve-static#0.0.0+20160606155157" 20 | } 21 | } -------------------------------------------------------------------------------- /src/config/directories.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var names = { 3 | core: 'base-project', 4 | ignore: ['.git', 'node_modules', 'typings', 'build'] 5 | }; 6 | exports.names = names; 7 | -------------------------------------------------------------------------------- /src/generators/mean-stack.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var shell = require('shelljs'); 3 | var inquirer = require('inquirer'); 4 | var fs = require('fs'); 5 | var path = require('path'); 6 | var directories = require('../config/directories'); 7 | var child_process = require('child_process'); 8 | /** 9 | * MEAN Stack Generator 10 | */ 11 | var MEANStack = (function () { 12 | function MEANStack() { 13 | } 14 | /* 15 | * Creates Everything Required for the MEAN Stack 16 | */ 17 | MEANStack.generateApplication = function (parent, base) { 18 | MEANStack.coreTokens['ApplicationName'] = base; 19 | var promise = new Promise(function (resolve, reject) { 20 | MEANStack.generateDirectories(parent, base).then(function () { 21 | MEANStack.generateFiles(parent, base).then(function () { 22 | resolve(); 23 | }); 24 | }); 25 | }); 26 | return promise; 27 | }; 28 | /* 29 | * Create Base Directories for the MEAN Stack 30 | */ 31 | MEANStack.generateDirectories = function (parent, base) { 32 | var promise = new Promise(function (resolve, reject) { 33 | var projectDir = parent + "/" + base; 34 | try { 35 | shell.mkdir('-p', projectDir); // Project Directory 36 | var core_1 = directories.names.core; 37 | MEANStack.coreDirectories.forEach(function (name) { 38 | name = name.substr(name.lastIndexOf(core_1) + core_1.length + 1, name.length); 39 | try { 40 | shell.mkdir('-p', projectDir + "/" + name); 41 | } 42 | catch (err) { 43 | reject(err); 44 | } 45 | }); 46 | resolve(); 47 | } 48 | catch (error) { 49 | reject(error); 50 | } 51 | }); 52 | return promise; 53 | }; 54 | /* 55 | * Create Generic Files for the MEAN Stack 56 | */ 57 | MEANStack.generateFiles = function (parent, base) { 58 | var projectDir = parent + "/" + base; 59 | var promise = new Promise(function (resolve, reject) { 60 | var core = directories.names.core; 61 | MEANStack.coreFiles.forEach(function (name) { 62 | var fileName = name.substr(name.lastIndexOf(core) + core.length + 1, name.length); 63 | try { 64 | shell.touch(projectDir + "/" + fileName); 65 | MEANStack.parseFile(name, projectDir + "/" + fileName); 66 | } 67 | catch (err) { 68 | reject(err); 69 | } 70 | }); 71 | resolve(); 72 | }); 73 | return promise; 74 | }; 75 | /** 76 | * Parses original file 77 | */ 78 | MEANStack.parseFile = function (source, destination) { 79 | try { 80 | var data = fs.readFileSync(source, 'utf8'); 81 | if (data.indexOf('[--') !== -1 && data.indexOf('--]') !== -1) { 82 | // This file contains a token 83 | var lines = data.split('\n'); 84 | for (var line in lines) { 85 | var start = lines[line].indexOf('[--'); 86 | var end = lines[line].indexOf('--]'); 87 | if (start !== -1 && end !== -1) { 88 | // This line has a token 89 | var value = lines[line].substring(start + 3, end); 90 | if (MEANStack.coreTokens[value]) { 91 | // This is a valid token replace the value 92 | var tokenized = lines[line] 93 | .replace(value, MEANStack.coreTokens[value]) 94 | .replace('[--', '') 95 | .replace('--]', ''); 96 | lines[line] = tokenized; 97 | } 98 | } 99 | } 100 | var joinedLines = lines.join('\n'); 101 | fs.writeFileSync(destination, joinedLines, 'utf8'); 102 | } 103 | else { 104 | // No Tokens founds just copy over code 105 | fs.writeFileSync(destination, data, 'utf8'); 106 | } 107 | } 108 | catch (err) { 109 | throw err; 110 | } 111 | }; 112 | MEANStack.askQuestion = function (options) { 113 | return inquirer.prompt(options); 114 | }; 115 | MEANStack.writableDirectory = function (directory) { 116 | var promise = new Promise(function (resolve, reject) { 117 | try { 118 | fs.accessSync(directory, fs['W_OK'] | fs['R_OK']); 119 | resolve(); 120 | } 121 | catch (err) { 122 | reject(err); 123 | } 124 | }); 125 | return promise; 126 | }; 127 | MEANStack.directoryExist = function (directory) { 128 | var promise = new Promise(function (resolve, reject) { 129 | fs.lstat(directory, function (err, stat) { 130 | if (!err && stat.isDirectory()) { 131 | // Folder or File Exists 132 | resolve(stat.isDirectory()); 133 | } 134 | else { 135 | // Directory does not Exists 136 | if (!err && stat.isFile()) { 137 | // Check if it's a file 138 | reject(stat.isFile()); 139 | } 140 | else { 141 | reject(err); 142 | } 143 | } 144 | }); 145 | }); 146 | return promise; 147 | }; 148 | /** 149 | * Initialize the core code and registers it to this instance. 150 | **/ 151 | MEANStack.initializeCore = function (base) { 152 | var promise = new Promise(function (resolve, reject) { 153 | var project = '/full-stack/src/config/base-project'; 154 | MEANStack.directoryExist("" + base + project).then(function () { 155 | // Found Base Project 156 | MEANStack.registerFiles("" + base + project); 157 | resolve(); // Analyzed Project 158 | }).catch(function (error) { 159 | reject('Core Is missing!'); 160 | }); 161 | }); 162 | return promise; 163 | }; 164 | MEANStack.registerFiles = function (directory) { 165 | var ignore = directories.names.ignore; 166 | var files = fs.readdirSync(directory); 167 | files.forEach(function (file) { 168 | var destination = directory + "/" + file; 169 | var ignoreDestination = ignore.find(function (item) { 170 | return item === file; 171 | }) !== undefined; 172 | if (!ignoreDestination) { 173 | // Valid Directory or File 174 | var stat = fs.lstatSync(destination); 175 | if (stat.isFile()) { 176 | MEANStack.coreFiles.push(destination); 177 | } 178 | if (stat.isDirectory()) { 179 | MEANStack.coreDirectories.push(destination); 180 | MEANStack.registerFiles(destination); 181 | } 182 | } 183 | }); 184 | }; 185 | /** 186 | * Finds the Global path for npm modules 187 | **/ 188 | MEANStack.getGlobalPath = function () { 189 | var promise = new Promise(function (resolve, reject) { 190 | try { 191 | var binary = shell.which('npm'); 192 | var globalDir = path.resolve(binary['stdout'] + '/../../lib/node_modules'); 193 | resolve(globalDir); 194 | } 195 | catch (err) { 196 | reject(err); 197 | } 198 | }); 199 | return promise; 200 | }; 201 | MEANStack.serveLocalInstance = function (parent, cb) { 202 | var promise = new Promise(function (resolve, reject) { 203 | MEANStack.directoryExist(parent + "/package.json").then(function () { 204 | // This is a directory! ERROR out 205 | reject(); 206 | }).catch(function (isFile) { 207 | if (isFile === true) { 208 | // This is a valid file 209 | try { 210 | fs.accessSync(parent + "/package.json", fs['R_OK']); 211 | } 212 | catch (err) { 213 | // Not able to read in JSON file 214 | reject(err); 215 | } 216 | var config = require(parent + "/package.json"); 217 | if (config && config.scripts && config.scripts.start && config.scripts.start !== '') { 218 | // Valid start command 219 | var child = child_process.exec(config.scripts.start); 220 | child.stdout.on('data', function (data) { 221 | // Runs callback with live feed 222 | cb(data); 223 | }); 224 | child.stdout.on('close', function (data) { 225 | // All done! 226 | resolve(); 227 | }); 228 | } 229 | else { 230 | reject(); 231 | } 232 | } 233 | else { 234 | // ERROR Thrown 235 | reject(isFile); 236 | } 237 | }); 238 | }); 239 | return promise; 240 | }; 241 | MEANStack.getVersion = function (parent) { 242 | var config = require(parent + "/package.json"); 243 | return config.version; 244 | }; 245 | MEANStack.coreTokens = {}; 246 | MEANStack.coreFiles = []; 247 | MEANStack.coreDirectories = []; 248 | return MEANStack; 249 | }()); 250 | exports.MEANStack = MEANStack; 251 | -------------------------------------------------------------------------------- /tests/app.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsecademy/full-stack/dee5b3405d42153c5ec96b45132649a70e9a2336/tests/app.js --------------------------------------------------------------------------------