Refactoring: removed S.config.projectPath, Project instance is created at correct moments, common project helpers moved to Project

This commit is contained in:
Kamil Burzynski 2016-02-09 15:14:17 +01:00
parent 4ff2af67ae
commit e0e027dcd3
29 changed files with 211 additions and 209 deletions

View File

@ -7,10 +7,9 @@ let argv = require('minimist')(process.argv.slice(2));
if (argv.debug) process.env.DEBUG = '*';
let S = require('../lib/Serverless'),
SUtils = require('../lib/utils/index'),
serverless = new S({
interactive: typeof process.env.CI !== 'undefined' ? false : true,
projectPath: SUtils.getProjectPath(process.cwd())
Project = require('../lib/ServerlessProject'),
serverless = new S( Project.findProject(process.cwd()), {
interactive: typeof process.env.CI !== 'undefined' ? false : true
});
serverless.command(argv);

View File

@ -25,7 +25,7 @@ BbPromise.longStackTraces();
class Serverless {
constructor(config) {
constructor(project, config) {
// Add version
this._version = require('./../package.json').version;
@ -36,10 +36,11 @@ class Serverless {
interactive: false,
awsAdminKeyId: null,
awsAdminSecretKey: null,
projectPath: null,
serverlessPath: __dirname
};
this.setProject( project );
// Add Config Settings
this.updateConfig(config);
@ -48,6 +49,7 @@ class Serverless {
this.hooks = {};
this.commands = {};
this.classes = {
// TODO: add Runtime, Stage, Region
State: require('./ServerlessState'),
Meta: require('./ServerlessMeta'),
Project: require('./ServerlessProject'),
@ -58,13 +60,13 @@ class Serverless {
this.cli = null;
this.state = new this.classes.State(this);
// If project
if (this.config.projectPath) {
if (this.hasProject()) {
// TODO: move this to Project class, along with credentials
// Load Admin ENV information
require('dotenv').config({
silent: true, // Don't display dotenv load failures for admin.env if we already have the required environment variables
path: path.join(this.config.projectPath, 'admin.env')
path: path.join(this.getProject().getRootPath(), 'admin.env')
});
this._setCredentials();
@ -73,12 +75,22 @@ class Serverless {
// Load Plugins: Framework Defaults
let defaults = require('./Actions.json');
this._loadPlugins(__dirname, defaults.plugins);
this.loadProjectPlugins();
}
// Load Plugins: Project
if (this.config.projectPath && SUtils.fileExistsSync(path.join(this.config.projectPath, 's-project.json'))) {
let projectJson = require(path.join(this.config.projectPath, 's-project.json'));
if (projectJson.plugins) this._loadPlugins(this.config.projectPath, projectJson.plugins);
}
getProject() {
return this.project;
}
hasProject() {
return this.project != undefined;
}
setProject( project ) {
this.project = project;
// TODO: get rid of this? Seems like responsibility of Serverless and Project classes is too tangled together
project.setServerless( this );
}
/**
@ -138,7 +150,7 @@ class Serverless {
options: extend(_this.cli.options, _this.cli.params)
};
if (_this.config.projectPath) return _this.init();
if (_this.hasProject()) return _this.init();
} else {
@ -219,6 +231,12 @@ class Serverless {
this.config = extend(this.config, config);
}
loadProjectPlugins() {
if( this.hasProject() ) {
this._loadPlugins( this.getProject().getRootPath(), this.getProject().getPlugins() );
}
}
/**
* Load Plugins
* - @param relDir string path to start from when rel paths are specified
@ -315,7 +333,7 @@ class Serverless {
}
// if not in project root and not creating project, throw error
if (!this.config.projectPath && _this.cli.context != 'project') {
if (!this.hasProject() && _this.cli.context != 'project') {
return BbPromise.reject(new SError('This command can only be run inside a Serverless project.'));
}

View File

@ -54,8 +54,8 @@ class ServerlessComponent {
}
// Make full path
if (this._S.config.projectPath && this._config.sPath) {
this._config.fullPath = path.join(this._S.config.projectPath, this._config.sPath);
if (this._S.hasProject() && this._config.sPath) {
this._config.fullPath = this._S.getProject().getFilePath(this._config.sPath);
}
}
@ -87,7 +87,7 @@ class ServerlessComponent {
return BbPromise.try(function() {
// Validate: Check project path is set
if (!_this._S.config.projectPath) throw new SError('Component could not be loaded because no project path has been set on Serverless instance');
if (!_this._S.hasProject()) throw new SError('Component could not be loaded because no project path has been set on Serverless instance');
// Validate: Check component exists
if (!SUtils.fileExistsSync(path.join(_this._config.fullPath, 's-component.json'))) {
@ -214,7 +214,7 @@ class ServerlessComponent {
if (!options.stage || !options.region) throw new SError('Both "stage" and "region" params are required');
// Validate: Check project path is set
if (!_this._S.config.projectPath) throw new SError('Component could not be populated because no project path has been set on Serverless instance');
if (!_this._S.hasProject()) throw new SError('Component could not be populated because no project path has been set on Serverless instance');
// Populate
let clone = _this.get();
@ -253,7 +253,7 @@ class ServerlessComponent {
return new BbPromise.try(function() {
// Validate: Check project path is set
if (!_this._S.config.projectPath) throw new SError('Component could not be saved because no project path has been set on Serverless instance');
if (!_this._S.hasProject()) throw new SError('Component could not be saved because no project path has been set on Serverless instance');
// Create if does not exist
if (!SUtils.fileExistsSync(path.join(_this._config.fullPath, 's-component.json'))) {
@ -344,7 +344,7 @@ class ServerlessComponent {
*/
getProject() {
return this._S.state.project;
return this._S.getProject();
}
}

View File

@ -69,11 +69,8 @@ class ServerlessEndpoint {
}
// Make full path
if (this._S.config.projectPath && this._config.sPath) {
this._config.fullPath = path.join(
this._S.config.projectPath,
this._config.sPath.split('@')[0].split('/').join(path.sep)
);
if (this._S.hasProject() && this._config.sPath) {
this._config.fullPath = this._S.getProject().getFilePath( this._config.sPath.split('@')[0].split('/') );
}
}
@ -90,7 +87,7 @@ class ServerlessEndpoint {
return new BbPromise(function(resolve) {
// Validate: Check project path is set
if (!_this._S.config.projectPath) throw new SError('Endpoint could not be loaded because no project path has been set on Serverless instance');
if (!_this._S.hasProject()) throw new SError('Endpoint could not be loaded because no project path has been set on Serverless instance');
// Validate: Check function exists
if (!SUtils.fileExistsSync(path.join(_this._config.fullPath, 's-function.json'))) {
@ -152,7 +149,7 @@ class ServerlessEndpoint {
if (!options.stage || !options.region) throw new SError('Both "stage" and "region" params are required');
// Validate: Check project path is set
if (!_this._S.config.projectPath) throw new SError('Endpoint could not be populated because no project path has been set on Serverless instance');
if (!_this._S.hasProject()) throw new SError('Endpoint could not be populated because no project path has been set on Serverless instance');
// Populate
let clone = _this.get();
@ -187,7 +184,7 @@ class ServerlessEndpoint {
return new BbPromise(function(resolve) {
// Validate: Check project path is set
if (!_this._S.config.projectPath) throw new SError('Endpoint could not be saved because no project path has been set on Serverless instance');
if (!_this._S.hasProject()) throw new SError('Endpoint could not be saved because no project path has been set on Serverless instance');
// Validate: Check function exists
if (!SUtils.fileExistsSync(path.join(_this._config.fullPath, 's-function.json'))) {
@ -219,7 +216,7 @@ class ServerlessEndpoint {
*/
getProject() {
return this._S.state.project;
return this._S.getProject();
}
/**

View File

@ -97,8 +97,8 @@ class ServerlessFunction {
}
// Make full path
if (this._S.config.projectPath && this._config.sPath) {
this._config.fullPath = path.join(this._S.config.projectPath, this._config.sPath.split('/').join(path.sep));
if (this._S.hasProject() && this._config.sPath) {
this._config.fullPath = this._S.getProject().getFilePath( this._config.sPath.split('/') );
}
}
@ -116,7 +116,7 @@ class ServerlessFunction {
return BbPromise.try(function() {
// Validate: Check project path is set
if (!_this._S.config.projectPath) throw new SError('Function could not be loaded because no project path has been set on Serverless instance');
if (!_this._S.hasProject()) throw new SError('Function could not be loaded because no project path has been set on Serverless instance');
// Validate: Check function exists
if (!SUtils.fileExistsSync(path.join(_this._config.fullPath, 's-function.json'))) {
@ -215,8 +215,7 @@ class ServerlessFunction {
if (this._config.sPath.split('/').length == 3) {
// Check if s-module.json exists in subfolder
if (SUtils.fileExistsSync(path.join(
this._S.config.projectPath,
if (SUtils.fileExistsSync(this._S.getProject().getFilePath(
this._config.sPath.split('/').splice(0, 2).join(path.sep),
's-module.json'))) {
name = name + '-' + this._config.sPath.split('/')[1];
@ -252,7 +251,7 @@ class ServerlessFunction {
if (!options.stage || !options.region) throw new SError('Both "stage" and "region" params are required');
// Validate: Check project path is set
if (!_this._S.config.projectPath) throw new SError('Function could not be populated because no project path has been set on Serverless instance');
if (!_this._S.hasProject()) throw new SError('Function could not be populated because no project path has been set on Serverless instance');
// Populate
let clone = _this.get();
@ -291,7 +290,7 @@ class ServerlessFunction {
return new BbPromise.try(function() {
// Validate: Check project path is set
if (!_this._S.config.projectPath) throw new SError('Function could not be saved because no project path has been set on Serverless instance');
if (!_this._S.hasProject()) throw new SError('Function could not be saved because no project path has been set on Serverless instance');
// Create if does not exist
if (!SUtils.fileExistsSync(path.join(_this._config.fullPath, 's-function.json'))) {
@ -382,7 +381,7 @@ class ServerlessFunction {
*/
getProject() {
return this._S.state.project;
return this._S.getProject();
}
/**

View File

@ -16,7 +16,6 @@ class ServerlessMeta
/**
* Constructor
* - options.projectPath: absolute path to project
*/
constructor(Serverless) {
@ -40,16 +39,16 @@ class ServerlessMeta
return BbPromise.try(function() {
// Validate: Check project path is set
if (!_this._S.config.projectPath) throw new SError('No project path has been set on Serverless instance');
if (!_this._S.hasProject()) throw new SError('No project path has been set on Serverless instance');
// Validate: Check variables exist
if (SUtils.dirExistsSync(path.join(_this._S.config.projectPath, '_meta', 'variables'))) {
if (SUtils.dirExistsSync(this._S.getProject().getFilePath('_meta', 'variables'))) {
let variableFiles = fs.readdirSync(path.join(_this._S.config.projectPath, '_meta', 'variables'));
let variableFiles = fs.readdirSync(this._S.getProject().getFilePath('_meta', 'variables'));
for (let i = 0; i < variableFiles.length; i++) {
let variableFile = SUtils.readAndParseJsonSync(path.join(_this._S.config.projectPath, '_meta', 'variables', variableFiles[i]));
let variableFile = SUtils.readAndParseJsonSync(this._S.getProject().getFilePath('_meta', 'variables', variableFiles[i]));
// Parse file name to get stage/region
let file = variableFiles[i].replace('s-variables-', '').replace('.json', '');
@ -57,7 +56,7 @@ class ServerlessMeta
if (file === 'common') {
// Set Common variables
_this.variables = SUtils.readAndParseJsonSync(path.join(_this._S.config.projectPath, '_meta', 'variables', variableFiles[i]));
_this.variables = SUtils.readAndParseJsonSync(this._S.getProject().getFilePath('_meta', 'variables', variableFiles[i]));
} else {
@ -71,7 +70,7 @@ class ServerlessMeta
if (file.length === 1) {
// Set Stage Variables
_this.stages[file[0]].variables = SUtils.readAndParseJsonSync(path.join(_this._S.config.projectPath, '_meta', 'variables', variableFiles[i]));
_this.stages[file[0]].variables = SUtils.readAndParseJsonSync(this._S.getProject().getFilePath('_meta', 'variables', variableFiles[i]));
} else if (file.length === 2) {
@ -82,7 +81,7 @@ class ServerlessMeta
if (file[1] === 'euwest1') region = 'eu-west-1';
if (file[1] === 'apnortheast1') region = 'ap-northeast-1';
if (!_this.stages[file[0]].regions[region]) _this.stages[file[0]].regions[region] = {
variables: SUtils.readAndParseJsonSync(path.join(_this._S.config.projectPath, '_meta', 'variables', variableFiles[i]))
variables: SUtils.readAndParseJsonSync(this._S.getProject().getFilePath('_meta', 'variables', variableFiles[i]))
};
}
}
@ -151,25 +150,25 @@ class ServerlessMeta
return BbPromise.try(function() {
// Validate: Check project path is set
if (!_this._S.config.projectPath) throw new SError('Meta could not be saved because no project path has been set on Serverless instance');
if (!_this._S.hasProject()) throw new SError('Meta could not be saved because no project path has been set on Serverless instance');
// Create meta folder if does not exist
if (!SUtils.dirExistsSync(path.join(_this._S.config.projectPath, '_meta'))) {
fs.mkdirSync(path.join(_this._S.config.projectPath, '_meta'));
if (!SUtils.dirExistsSync(this._S.getProject().getFilePath('_meta'))) {
fs.mkdirSync(this._S.getProject().getFilePath('_meta'));
}
// Create meta/resources folder, if does not exist
if (!SUtils.dirExistsSync(path.join(_this._S.config.projectPath, '_meta', 'resources'))) {
fs.mkdirSync(path.join(_this._S.config.projectPath, '_meta', 'resources'));
if (!SUtils.dirExistsSync(this._S.getProject().getFilePath('_meta', 'resources'))) {
fs.mkdirSync(this._S.getProject().getFilePath('_meta', 'resources'));
}
// Create meta/variables folder, if does not exist
if (!SUtils.dirExistsSync(path.join(_this._S.config.projectPath, '_meta', 'variables'))) {
fs.mkdirSync(path.join(_this._S.config.projectPath, '_meta', 'variables'));
if (!SUtils.dirExistsSync(this._S.getProject().getFilePath('_meta', 'variables'))) {
fs.mkdirSync(this._S.getProject().getFilePath('_meta', 'variables'));
}
// Save Common Variables
fs.writeFileSync(path.join(_this._S.config.projectPath, '_meta', 'variables', 's-variables-common.json'),
fs.writeFileSync(this._S.getProject().getFilePath('_meta', 'variables', 's-variables-common.json'),
JSON.stringify(clone.variables, null, 2));
// Save Stage & Region Variables
@ -178,12 +177,12 @@ class ServerlessMeta
let stage = clone.stages[Object.keys(clone.stages)[i]];
// Save Stage Variables
fs.writeFileSync(path.join(_this._S.config.projectPath, '_meta', 'variables', 's-variables-' + Object.keys(clone.stages)[i] + '.json'),
fs.writeFileSync(this._S.getProject().getFilePath('_meta', 'variables', 's-variables-' + Object.keys(clone.stages)[i] + '.json'),
JSON.stringify(stage.variables, null, 2));
// Save Stage Region Variables
for (let j = 0; j < Object.keys(stage.regions).length; j++) {
fs.writeFileSync(path.join(_this._S.config.projectPath, '_meta', 'variables', 's-variables-' + Object.keys(clone.stages)[i] + '-' + Object.keys(stage.regions)[j].replace(/-/g, '') + '.json'),
fs.writeFileSync(this._S.getProject().getFilePath('_meta', 'variables', 's-variables-' + Object.keys(clone.stages)[i] + '-' + Object.keys(stage.regions)[j].replace(/-/g, '') + '.json'),
JSON.stringify(stage.regions[Object.keys(stage.regions)[j]].variables, null, 2));
}
}

View File

@ -19,10 +19,11 @@ class ServerlessProject {
* Constructor
*/
constructor(Serverless) {
constructor(rootPath) {
let _this = this;
this._S = Serverless;
_this.rootPath = rootPath;
// Default properties
_this.name = 'serverless' + SUtils.generateShortId(6);
@ -35,6 +36,56 @@ class ServerlessProject {
_this.components = {};
_this.templates = {};
_this.plugins = [];
_this.load();
}
//TODO: get rid of this?
setServerless( Serverless ) {
this._S = Serverless;
}
static findProject( startDir ){
let jsonName = 's-project.json';
// Check up to 10 parent levels
let previous = '.',
project = undefined,
i = 10;
while( i >= 0 ) {
let fullPath = path.resolve(startDir, previous);
if (SUtils.fileExistsSync(path.join(fullPath, jsonName))) {
let projectJson = require(path.join(fullPath, jsonName));
if (typeof projectJson.name !== 'undefined') {
project = new ServerlessProject( fullPath );
break;
}
}
previous = path.join(previous, '..');
}
return project;
}
getPlugins(){
return this.plugins;
}
getRootPath(){
return this.rootPath;
}
getFilePath(){
let args = _.toArray( arguments );
args.unshift( this.getRootPath() );
return path.apply( path, args );
}
getName(){
return this.name;
}
/**
@ -50,19 +101,15 @@ class ServerlessProject {
projectContents;
return BbPromise.try(function () {
// Validate: Check project path is set
if (!_this._S.config.projectPath) throw new SError('Project could not be loaded because no project path has been set on Serverless instance');
// Validate: Check project exists
if (!SUtils.fileExistsSync(path.join(_this._S.config.projectPath, 's-project.json'))) {
if (!SUtils.fileExistsSync(_this.getFilePath('s-project.json'))) {
throw new SError('Project could not be loaded because it does not exist');
}
projectJson = SUtils.readAndParseJsonSync(path.join(_this._S.config.projectPath, 's-project.json'));
projectJson = SUtils.readAndParseJsonSync(_this.getFilePath('s-project.json'));
projectJson.components = {};
projectJson.templates = {};
projectContents = fs.readdirSync(_this._S.config.projectPath);
projectContents = fs.readdirSync(_this.getRootPath());
return projectContents;
})
@ -70,12 +117,12 @@ class ServerlessProject {
// If template, load template
if (c.indexOf('s-template') !== -1) {
projectJson.templates = _.assign(projectJson.templates, SUtils.readAndParseJsonSync(path.join(_this._S.config.projectPath, c)));
projectJson.templates = _.assign(projectJson.templates, SUtils.readAndParseJsonSync(_this.getFilePath(c)));
return;
}
// If component, load component
if (SUtils.fileExistsSync(path.join(_this._S.config.projectPath, c, 's-component.json'))) {
if (SUtils.fileExistsSync(_this.getFilePath(c, 's-component.json'))) {
let component = new _this._S.classes.Component(_this._S, {
sPath: c
@ -150,9 +197,6 @@ class ServerlessProject {
// Validate: Check Stage & Region
if (!options.stage || !options.region) throw new SError('Both "stage" and "region" params are required');
// Validate: Check project path is set
if (!_this._S.config.projectPath) throw new SError('Project could not be populated because no project path has been set on Serverless instance');
// Populate components
let clone = _this.get();
clone = SUtils.populate(_this._S.state.getMeta(), _this.getTemplates(), clone, options.stage, options.region);
@ -185,8 +229,8 @@ class ServerlessProject {
options = options || {};
// Check for s-resources-cf.json
if (SUtils.fileExistsSync(path.join(_this._S.config.projectPath, 's-resources-cf.json'))) {
resources = SUtils.readAndParseJsonSync(path.join(_this._S.config.projectPath, 's-resources-cf.json'));
if (SUtils.fileExistsSync(_this.getFilePath('s-resources-cf.json'))) {
resources = SUtils.readAndParseJsonSync(_this.getFilePath('s-resources-cf.json'));
if (options.populate) {
return BbPromise.resolve(SUtils.populate(_this._S.state.getMeta(), _this.getTemplates(), resources, options.stage, options.region));
@ -273,14 +317,11 @@ class ServerlessProject {
let _this = this,
files = [];
// Validate: Check project path is set
if (!_this._S.config.projectPath) throw new SError('Project could not be saved because no project path has been set on Serverless instance');
return new BbPromise.try(function () {
// If project folder does not exist, create it
if (!SUtils.dirExistsSync(path.join(_this._S.config.projectPath))) {
fs.mkdirSync(path.join(_this._S.config.projectPath));
if (!SUtils.dirExistsSync(_this.getRootPath())) {
fs.mkdirSync(_this.getRootPath());
}
// Save all nested components
@ -302,7 +343,7 @@ class ServerlessProject {
if (clone.templates) delete clone.templates;
// Save s-project.json
files.push(SUtils.writeFile(path.join(_this._S.config.projectPath, 's-project.json'),
files.push(SUtils.writeFile(_this.getFilePath('s-project.json'),
JSON.stringify(clone, null, 2)));
// Write files

View File

@ -34,7 +34,7 @@ class ServerlessState {
let _this = this;
return _this.project.load()
return _this.S.getProject().load()
.then(function() {
return _this.meta.load();
});
@ -49,7 +49,7 @@ class ServerlessState {
let _this = this;
return _this.project.save({ deep: true })
return _this.S.getProject().save({ deep: true })
.then(function() {
return _this.meta.save({ deep: true });
});
@ -62,25 +62,23 @@ class ServerlessState {
set(data) {
this.meta = data.meta ? this.meta.set(data.meta) : this.meta;
this.project = data.project ? this.project.set(data.project, { deep: true }) : this.project;
this.project = data.project ? this.S.getProject().set(data.project, { deep: true }) : this.S.getProject();
return this;
}
/**
* Set Asset
* - Add or replace an asset to the state
* - Accepts a class instance of: Project, Component, Function, Endpoint
* - Accepts a class instance of: Component, Function, Endpoint
*/
setAsset(data) {
if (data instanceof this._S.classes.Project) {
this.project = data;
} else if (data instanceof this._S.classes.Component) {
this.project.components[data.name] = data;
if (data instanceof this._S.classes.Component) {
this.S.getProject().components[data.name] = data;
} else if (data instanceof this._S.classes.Function) {
this.project.components[data._config.sPath.split('/')[0]].functions[data._config.sPath] = data;
this.S.getProject().components[data._config.sPath.split('/')[0]].functions[data._config.sPath] = data;
} else if (data instanceof this._S.classes.Endpoint) {
let func = this.project.components[data._config.sPath.split('/')[0]].functions[data._config.sPath.split('@')[0]];
let func = this.S.getProject().components[data._config.sPath.split('/')[0]].functions[data._config.sPath.split('@')[0]];
let added = false;
for (let i = 0; i < func.endpoints.length; i++) {
if (func.endpoints[i].path === data.path && func.endpoints[i].method === data.method) {
@ -90,7 +88,7 @@ class ServerlessState {
}
if (!added) func.endpoints.push(data);
} else {
return new SError('State.setAsset() failed because you did not submit an instance of a Project, Component, Function or Endpoint class.');
return new SError('State.setAsset() failed because you did not submit an instance of a Component, Function or Endpoint class.');
}
}
@ -102,7 +100,7 @@ class ServerlessState {
get() {
return {
meta: this.meta.get(),
project: this.project.get()
project: this.S.getProject().get()
}
}
@ -120,7 +118,7 @@ class ServerlessState {
return {
meta: this.meta.get(),
project: this.project.getPopulated(options)
project: this.S.getProject().getPopulated(options)
}
}
@ -139,7 +137,7 @@ class ServerlessState {
*/
getProject() {
return this.project;
return this.S.getProject();
}
/**
@ -148,7 +146,7 @@ class ServerlessState {
*/
getResources(options) {
return this.project.getResources(options);
return this.S.getProject().getResources(options);
}
/**
@ -183,8 +181,8 @@ class ServerlessState {
foundComponents = [];
// Get all
for (let i = 0; i < Object.keys(_this.project.components).length; i++) {
allComponents.push(_this.project.components[Object.keys(_this.project.components)[i]]);
for (let i = 0; i < Object.keys(_this.S.getProject().components).length; i++) {
allComponents.push(_this.S.getProject().components[Object.keys(_this.S.getProject().components)[i]]);
}
// Return if no options specified
@ -227,8 +225,8 @@ class ServerlessState {
foundFunctions = [];
// Get all
for (let i = 0; i < Object.keys(_this.project.components).length; i++) {
let component = _this.project.components[Object.keys(_this.project.components)[i]];
for (let i = 0; i < Object.keys(_this.S.getProject().components).length; i++) {
let component = _this.S.getProject().components[Object.keys(_this.S.getProject().components)[i]];
for (let j = 0; j < Object.keys(component.functions).length; j++) {
allFunctions.push(component.functions[Object.keys(component.functions)[j]]);
}
@ -275,8 +273,8 @@ class ServerlessState {
foundEndpoints = [];
// Get all functions
for (let i = 0; i < Object.keys(_this.project.components).length; i++) {
let component = _this.project.components[Object.keys(_this.project.components)[i]];
for (let i = 0; i < Object.keys(_this.S.getProject().components).length; i++) {
let component = _this.S.getProject().components[Object.keys(_this.S.getProject().components)[i]];
if (!component.functions) continue;
for (let k = 0; k < Object.keys(component.functions).length; k++) {
let func = component.functions[Object.keys(component.functions)[k]];

View File

@ -140,7 +140,7 @@ module.exports = function(SPlugin, serverlessPath) {
// Set Dist Dir
let d = new Date();
_this.pathDist = path.join(_this.S.config.projectPath, '_meta', '_tmp', _this.function.name + '@' + d.getTime());
_this.pathDist = _this.S.getProject().getFilePath('_meta', '_tmp', _this.function.name + '@' + d.getTime());
// Status
SUtils.sDebug(`"${_this.evt.options.stage} - ${_this.evt.options.region} - ${_this.function.name}": Copying in dist dir ${_this.pathDist}`);

View File

@ -152,7 +152,7 @@ usage: serverless component create`,
}
// If component exists in project, throw error
if (SUtils.doesComponentExist(this.evt.options.sPath, this.S.config.projectPath)) {
if (SUtils.doesComponentExist(this.evt.options.sPath, this.S.getProject().getRootPath())) {
return BbPromise.reject(new SError(
'Component ' + this.evt.options.sPath + ' already exists',
SError.errorCodes.INVALID_PROJECT_SERVERLESS
@ -185,14 +185,14 @@ usage: serverless component create`,
if (_this.evt.options.runtime === 'nodejs') {
SCli.log('Installing "serverless-helpers" for this component via NPM...');
SCli.log(`-----------------`);
SUtils.npmInstall(path.join(this.S.config.projectPath, this.evt.options.sPath));
SUtils.npmInstall(this.S.getProject().getFilePath(this.evt.options.sPath));
SCli.log(`-----------------`);
} else if (_this.evt.options.runtime === 'python2.7') {
SCli.log("Installing default python dependencies with pip...");
SCli.log(`-----------------`);
SUtils.pipPrefixInstall(
path.join(this.S.config.projectPath, this.evt.options.sPath, 'requirements.txt'),
path.join(this.S.config.projectPath, this.evt.options.sPath, 'vendored')
path.join(this.S.getProject().getFilePath(this.evt.options.sPath, 'requirements.txt'),
path.join(this.S.getProject().getFilePath(this.evt.options.sPath, 'vendored')
);
SCli.log(`-----------------`);
}

View File

@ -150,7 +150,7 @@ module.exports = function(SPlugin, serverlessPath) {
}
// Get all functions in CWD
let sPath = _this.getSPathFromCwd(_this.S.config.projectPath);
let sPath = _this.getSPathFromCwd(_this.S.getProject().getRootPath());
_this.components = _this.S.state.getComponents({
paths: sPath ? [sPath] : []

View File

@ -174,7 +174,7 @@ module.exports = function(SPlugin, serverlessPath) {
!_this.evt.options.all) {
// Get all functions in CWD
let sPath = _this.getSPathFromCwd(_this.S.config.projectPath);
let sPath = _this.getSPathFromCwd(_this.S.getProject().getRootPath());
if (!sPath) {
throw new SError(`You must be in a component to deploy. Otherwise, use the command "serverless dash deploy"`);

View File

@ -185,7 +185,7 @@ usage: serverless env set`,
});
if (_this.evt.options.stage == 'local') {
putEnvQ.push(SUtils.writeFile(path.join(this.S.config.projectPath, '.env'), contents));
putEnvQ.push(SUtils.writeFile(this.S.getProject().getFilePath('.env'), contents));
} else {
let projectName = _this.S.state.meta.get().variables.project,
bucketName = _this.S.state.meta.get().variables.projectBucket,

View File

@ -176,7 +176,7 @@ usage: serverless env unset`,
});
if (_this.evt.options.stage == 'local') {
putEnvQ.push(SUtils.writeFile(path.join(_this.S.config.projectPath, '.env'), contents));
putEnvQ.push(SUtils.writeFile(_this.S.getProject().getFilePath('.env'), contents));
} else {
let projectName = _this.S.state.meta.get().variables.project,
bucketName = _this.S.state.meta.get().variables.projectBucket,

View File

@ -93,7 +93,7 @@ usage: serverless function create <function>`,
if (!_this.S.config.interactive || _this.evt.options.sPath) return BbPromise.resolve();
// Get sPath
_this.evt.options.sPath = _this.getSPathFromCwd(_this.S.config.projectPath);
_this.evt.options.sPath = _this.getSPathFromCwd(_this.S.getProject().getRootPath());
// Validate
if (!_this.evt.options.sPath) {
@ -129,7 +129,7 @@ usage: serverless function create <function>`,
// Validate: If interactive and no sPath, check they are in a component, and get sPath
if (_this.S.config.interactive && !_this.evt.options.sPath) {
_this.evt.options.sPath = _this.getSPathFromCwd(_this.S.config.projectPath);
_this.evt.options.sPath = _this.getSPathFromCwd(_this.S.getProject().getRootPath());
if (!_this.evt.options.sPath) {
return BbPromise.reject(new SError('You must be in a component or two subfolders max in a component to create a function'));
}
@ -163,11 +163,11 @@ usage: serverless function create <function>`,
let dir = _this.evt.options.sPath.split('/');
dir.pop();
let c = dir.shift();
if (dir[0] && !SUtils.dirExistsSync(path.join(_this.S.config.projectPath, c, dir[0]))) {
fs.mkdirSync(path.join(_this.S.config.projectPath, c, dir[0]));
if (dir[0] && !SUtils.dirExistsSync(_this.S.project.getFilePath(c, dir[0]))) {
fs.mkdirSync(_this.S.project.getFilePath(c, dir[0]));
}
if (dir[1] && !SUtils.dirExistsSync(path.join(_this.S.config.projectPath, c, dir[0], dir[1]))) {
fs.mkdirSync(path.join(_this.S.config.projectPath, c, dir[0], dir[1]));
if (dir[1] && !SUtils.dirExistsSync(_this.S.project.getFilePath(c, dir[0], dir[1]))) {
fs.mkdirSync(_this.S.project.getFilePath(c, dir[0], dir[1]));
}
}

View File

@ -187,7 +187,7 @@ module.exports = function(SPlugin, serverlessPath) {
!_this.evt.options.all) {
// Get all functions in CWD
let sPath = _this.getSPathFromCwd(_this.S.config.projectPath);
let sPath = _this.getSPathFromCwd(_this.S.getProject().getRootPath());
if (!sPath) {
throw new SError(`You must be in a component or function folder to deploy. Otherwise, use the command "serverless dash deploy"`);
@ -211,8 +211,8 @@ module.exports = function(SPlugin, serverlessPath) {
}
// Ensure tmp folder exists in _meta
if (!SUtils.dirExistsSync(path.join(_this.S.config.projectPath, '_meta', '_tmp'))) {
fs.mkdirSync(path.join(_this.S.config.projectPath, '_meta', '_tmp'));
if (!SUtils.dirExistsSync(_this.S.project.getFilePath('_meta', '_tmp'))) {
fs.mkdirSync(_this.S.project.getFilePath('_meta', '_tmp'));
}
return BbPromise.resolve();

View File

@ -111,7 +111,7 @@ module.exports = function(SPlugin, serverlessPath) {
// If CLI and path is not specified, deploy from CWD if Function
if (this.S.cli && !this.evt.options.path) {
// Get all functions in CWD
let sPath = this.getSPathFromCwd(this.S.config.projectPath);
let sPath = this.getSPathFromCwd(this.S.getProject().getRootPath());
if (!sPath) {
throw new SError(`You must be in a function folder to run it`);
}

View File

@ -102,12 +102,12 @@ module.exports = function(SPlugin, serverlessPath) {
let _this = this;
// Instantiate Classes
_this.project = _this.S.state.project.get();
_this.project = _this.S.getProject().get();
// If CLI and path is not specified, deploy from CWD if Function
if (_this.S.cli && !_this.evt.options.path) {
// Get all functions in CWD
let sPath = _this.getSPathFromCwd(_this.S.config.projectPath);
let sPath = _this.getSPathFromCwd(_this.S.getProject().getRootPath());
if (!sPath || sPath.split('/').length == 1) {
return BbPromise.reject(new SError(`You must be in a function folder to run it`));
}

View File

@ -173,10 +173,10 @@ usage: serverless module install <github-url>`,
}
_this.evt.data.module = srcModuleJson.name;
_this.pathModule = path.join(this.S.config.projectPath, 'back', 'modules', _this.evt.data.module);
_this.pathModule = _this.S.project.getFilePath('back', 'modules', _this.evt.data.module);
// if same module name exists, throw error
if (SUtils.doesModuleExist(srcModuleJson.name, this.S.config.projectPath)) {
if (SUtils.doesModuleExist(srcModuleJson.name, this.S.getProject().getRootPath())) {
return BbPromise.reject(new SError(
'Module ' + _this.evt.data.module + ' already exists',
SError.errorCodes.INVALID_PROJECT_SERVERLESS

View File

@ -128,7 +128,6 @@ module.exports = function(SPlugin, serverlessPath) {
* Return EVT
*/
_this.evt.data.projectPath = _this.S.config.projectPath;
return _this.evt;
});
}
@ -145,15 +144,11 @@ module.exports = function(SPlugin, serverlessPath) {
// fixes the double generated domain id bug
if (_this.evt.options.name) isName = true;
// Check if s-project.json exists
if (_this.S.config.projectPath && SUtils.fileExistsSync(path.join(_this.S.config.projectPath, 's-project.json'))) {
// Check if project exists
if (_this.S.getProject()) {
// Set temp name
let projectJson = SUtils.readAndParseJsonSync(path.join(_this.S.config.projectPath, 's-project.json'));
name = _this.evt.options.name ? _this.evt.options.name : (projectJson.name + '-' + SUtils.generateShortId(6)).toLowerCase();
name = _this.evt.options.name ? _this.evt.options.name : (_this.S.getProject().getName() + '-' + SUtils.generateShortId(6)).toLowerCase();
} else {
// Set temp name
name = _this.evt.options.name ? _this.evt.options.name : ('serverless-' + SUtils.generateShortId(6)).toLowerCase();
}
@ -351,10 +346,9 @@ module.exports = function(SPlugin, serverlessPath) {
return BbPromise.try(function() {
// Update Global Serverless Instance
if (!_this.S.config.projectPath) {
_this.S.updateConfig({
projectPath: path.resolve(path.join(path.dirname('.'), _this.evt.options.name))
});
if( !_this.S.hasProject() ) {
let projectPath = path.resolve(path.join(path.dirname('.'), _this.evt.options.name));
_this.S.setProject( new _this.S.classes.Project( projectPath ) );
}
// Fill in meta attributes
@ -365,18 +359,15 @@ module.exports = function(SPlugin, serverlessPath) {
_this.meta.variables.notificationEmail = _this.evt.options.notificationEmail;
// Fill in project attributes
_this.project = _this.S.state.getProject();
_this.project = _this.S.getProject();
_this.project.name = _this.evt.options.name;
// Create s-resources-cf.json
if (!_this.project.cloudFormation && // In case initializing older project w/ cloudFormation property
!SUtils.fileExistsSync(
path.join(
_this.S.config.projectPath,
's-resources-cf.json'))) {
!SUtils.fileExistsSync( _.this.project.getFilePath( 's-resources-cf.json' ))) {
files.push(
SUtils.writeFile(
path.join(_this.S.config.projectPath, 's-resources-cf.json'),
_this.project.getFilePath( 's-resources-cf.json' ) ),
JSON.stringify(
SUtils.readAndParseJsonSync(path.join(_this.S.config.serverlessPath, 'templates', 's-resources-cf.json')),
null, 2)
@ -394,31 +385,31 @@ module.exports = function(SPlugin, serverlessPath) {
// Create other scaffolding
// If admin.env does not exist, save it
if (!SUtils.fileExistsSync(path.join(_this.S.config.projectPath, 'admin.env'))) {
if (!SUtils.fileExistsSync(_this.project.getFilePath( 'admin.env' ))) {
let adminEnv = 'SERVERLESS_ADMIN_AWS_ACCESS_KEY_ID=' + _this.S.config.awsAdminKeyId + os.EOL
+ 'SERVERLESS_ADMIN_AWS_SECRET_ACCESS_KEY=' + _this.S.config.awsAdminSecretKey + os.EOL;
files.push(fs.writeFileAsync(path.join(_this.S.config.projectPath, 'admin.env'), adminEnv));
files.push(fs.writeFileAsync(_this.project.getFilePath( 'admin.env' ), adminEnv));
}
// If package.json does not exist, save it
let packageJson;
if (!SUtils.fileExistsSync(path.join(_this.S.config.projectPath, 'package.json'))) {
if (!SUtils.fileExistsSync(_this.project.getFilePath( 'package.json' ))) {
// Prepare new package.json
packageJson = SUtils.readAndParseJsonSync(path.join(_this.S.config.serverlessPath, 'templates', 'nodejs', 'package.json'));
packageJson.name = _this.project.name;
packageJson.name = _this.project.getName();
packageJson.description = 'A Serverless Project and its Serverless Plugin dependencies.';
packageJson.private = false;
packageJson.dependencies = {};
if (packageJson.devDependencies) delete packageJson.devDependencies;
if (packageJson.keywords) delete packageJson.keywords;
files.push(fs.writeFileAsync(path.join(_this.S.config.projectPath, 'package.json'), JSON.stringify(packageJson, null, 2)))
files.push(fs.writeFileAsync(_this.project.getFilePath( 'package.json' ), JSON.stringify(packageJson, null, 2)))
} else {
// Modify existing package.json
packageJson = SUtils.readAndParseJsonSync(path.join(_this.S.config.projectPath, 'package.json'));
packageJson.name = _this.project.name;
packageJson = SUtils.readAndParseJsonSync(_this.project.getFilePath( 'package.json' ));
packageJson.name = _this.project.getName();
// Delete unnecessary package.json properties, if they exist
if (packageJson.readme) delete packageJson.readme;
@ -444,25 +435,25 @@ module.exports = function(SPlugin, serverlessPath) {
if (packageJson._shrinkwrap || packageJson._shrinkwrap === null) delete packageJson._shrinkwrap;
if (packageJson._spec) delete packageJson._spec;
if (packageJson._where) delete packageJson._where;
files.push(fs.writeFileAsync(path.join(_this.S.config.projectPath, 'package.json'), JSON.stringify(packageJson, null, 2)))
files.push(fs.writeFileAsync(_this.project.getFilePath( 'package.json' ), JSON.stringify(packageJson, null, 2)))
}
// If README.md does not exist, save it
if (!SUtils.fileExistsSync(path.join(_this.S.config.projectPath, 'README.md'))) {
if (!SUtils.fileExistsSync(_this.project.getFilePath( 'README.md' ))) {
let readme = '#' + _this.project.name;
files.push(fs.writeFileAsync(path.join(_this.S.config.projectPath, 'README.md'), readme));
files.push(fs.writeFileAsync(_this.project.getFilePath( 'README.md' ), readme));
}
// If .gitignore does not exist, save it
if (!SUtils.fileExistsSync(path.join(_this.S.config.projectPath, '.gitignore'))) {
files.push(fs.writeFileAsync(path.join(_this.S.config.projectPath, '.gitignore'), fs.readFileSync(path.join(_this.S.config.serverlessPath, 'templates', 'gitignore'))));
if (!SUtils.fileExistsSync(_this.project.getFilePath( '.gitignore' ))) {
files.push(fs.writeFileAsync(_this.project.getFilePath( '.gitignore' ), fs.readFileSync(path.join(_this.S.config.serverlessPath, 'templates', 'gitignore'))));
}
// If .env does not exist, save it
if (!SUtils.fileExistsSync(path.join(_this.S.config.projectPath, '.env'))) {
if (!SUtils.fileExistsSync(_this.project.getFilePath( '.env' ))) {
files.push(SUtils.writeFile(
path.join(_this.S.config.projectPath, '.env'),
_this.project.getFilePath( '.env' ),
'SERVERLESS_STAGE=' + _this.evt.options.stage
+ '\nSERVERLESS_DATA_MODEL_STAGE=' + _this.evt.options.stage
+ '\nSERVERLESS_PROJECT_NAME=' + _this.project.name
@ -501,11 +492,11 @@ module.exports = function(SPlugin, serverlessPath) {
components.forEach(function (component) {
SCli.log(`Installing ${component.runtime} dependencies for component: ${component.name}...`);
if (component.runtime === 'nodejs') {
SUtils.npmInstall(path.join(_this.S.config.projectPath, component.name));
SUtils.npmInstall(_this.S.getProject().getFilePath( component.name ));
} else if (component.runtime === 'python2.7') {
SUtils.pipPrefixInstall(
path.join(_this.S.config.projectPath, component.name, 'requirements.txt'),
path.join(_this.S.config.projectPath, component.name, 'vendored')
_this.S.getProject().getFilePath( component.name, 'requirements.txt' ),
_this.S.getProject().getFilePath( component.name, 'vendored' )
);
}
return BbPromise.resolve();

View File

@ -208,13 +208,12 @@ module.exports = function(SPlugin, serverlessPath) {
let _this = this;
// Update Global Serverless Instance
if (!_this.S.config.projectPath) {
_this.S.updateConfig({
projectPath: path.resolve(path.join(path.dirname('.'), _this.evt.options.name ? _this.evt.options.name : _this.evt.options.project))
});
if( !_this.S.hasProject() ) {
let projectPath = path.resolve(path.join(path.dirname('.'), _this.evt.options.name ? _this.evt.options.name : _this.evt.options.project);
_this.S.setProject( new _this.S.classes.Project( projectPath ) );
}
// Load state again now that the projectPath is set
// Load state again now that the project is set
return _this.S.state.load()
.then(function() {

View File

@ -197,7 +197,7 @@ usage: serverless region remove`,
_removeVariables() {
let fileName = `s-variables-${this.evt.options.stage}-${this.evt.options.region.replace(/-/g, '')}.json`;
return fs.unlinkAsync(path.join(this.S.config.projectPath, '_meta', 'variables', fileName));
return fs.unlinkAsync(this.S.project.getFilePath('_meta', 'variables', fileName));
}

View File

@ -167,7 +167,7 @@ usage: serverless resources deploy`,
console.log(resources);
// Create CloudFormation template in _meta folder
return SUtils.writeFile(
path.join(_this.S.config.projectPath, '_meta', 'resources', 's-resources-cf-' + _this.evt.options.stage + '-' + replaceall('-', '', _this.evt.options.region) + '.json'),
_this.S.project.getFilePath('_meta', 'resources', 's-resources-cf-' + _this.evt.options.stage + '-' + replaceall('-', '', _this.evt.options.region) + '.json'),
JSON.stringify(_this.cfTemplate, null, 2))
})
@ -208,7 +208,6 @@ usage: serverless resources deploy`,
// Upload to S3 Bucket
return _this.S3.sPutCfFile(
_this.S.state.getMeta().variables.projectBucket,
_this.S.config.projectPath,
_this.S.state.getProject().name,
_this.evt.options.stage,
_this.evt.options.region,

View File

@ -159,7 +159,6 @@ usage: serverless resources remove`,
region = _this.evt.options.region,
regionVars = _this.S.state.getMeta().stages[stage].regions[region].variables,
projectBucket = _this.S.state.getMeta().variables.projectBucket,
projectPath = _this.S.config.projectPath,
projectName = _this.S.state.getProject().name;
// Config AWS Services
@ -184,7 +183,7 @@ usage: serverless resources remove`,
let removeLocalResourceFile = function() {
let fileName = `s-resources-cf-${stage}-${region.replace(/-/g, '')}.json`;
let resourcesLocalFilePath = path.join(projectPath, '_meta', 'resources', fileName);
let resourcesLocalFilePath = _this.S.project.getFilePath('_meta', 'resources', fileName);
if (SUtils.fileExistsSync(resourcesLocalFilePath)) {
SUtils.sDebug(`Removing resources file "${fileName}"`);

View File

@ -123,7 +123,7 @@ usage: serverless stage remove`,
_removeVariables() {
let fileName = `s-variables-${this.evt.options.stage}.json`;
return fs.unlinkAsync(path.join(this.S.config.projectPath, '_meta', 'variables', fileName));
return fs.unlinkAsync(this.S.project.getFilePath('_meta', 'variables', fileName));
}
_removeMeta() {

View File

@ -121,7 +121,7 @@ module.exports.getEnvFileAsMap = function(Serverless, region, stage) {
let deferred;
if (stage == 'local') {
deferred = Promise.resolve(fs.readFileSync(path.join(Serverless.config.projectPath, '.env')));
deferred = Promise.resolve(fs.readFileSync(Serverless.project.getFilePath( '.env' )));
} else {
let projectName = Serverless.state.meta.get().variables.project,
bucketName = Serverless.state.meta.get().variables.projectBucket,

View File

@ -159,13 +159,12 @@ module.exports = function(config) {
* Put CF File On S3
*/
S3.sPutCfFile = function(bucketName, projectPath, projectName, stage, region, cfTemplate) {
S3.sPutCfFile = function(bucketName, projectName, stage, region, cfTemplate) {
// Set S3 Config To Project Bucket
S3.sSetProjectBucketConfig(bucketName);
let d = new Date(),
cfPath = path.join(projectPath, 'meta', 'private', 'resources', 's-resources-cf.json'),
key = ['serverless', projectName, stage, region, 'resources/' + 's-resources-cf'].join('/') + '@' + d.getTime() + '.json',
params = {
Bucket: bucketName,

View File

@ -92,42 +92,6 @@ exports.parseSPath = function(sPath) {
}
};
/**
* Get Project Path
* - Returns path string
*/
exports.getProjectPath = function(startDir) {
let _this = this;
// Check if startDir is root
if (_this.fileExistsSync(path.join(startDir, 's-project.json'))) {
let jawsJsonInDir = require(path.join(startDir, 's-project.json'));
if (typeof jawsJsonInDir.name !== 'undefined') return path.resolve(startDir);
}
// Check up to 10 parent levels
let previous = './',
projRootPath = false;
for (let i = 0; i < 10; i++) {
previous = path.join(previous, '../');
let fullPath = path.resolve(startDir, previous);
if (_this.fileExistsSync(path.join(fullPath, 's-project.json'))) {
let jawsJson = require(path.join(fullPath, 's-project.json'));
if (typeof jawsJson.name !== 'undefined') {
projRootPath = fullPath;
break;
}
}
}
return projRootPath;
};
/**
* Read Recursively
*/

View File

@ -51,7 +51,7 @@ describe('Test action: Resources Deploy', function() {
SUtils.sDebug('Adding test bucket resource');
let newProject = serverless.state.project.get();
let newProject = serverless.project.get();
// Adding new Module resource
newProject.cloudFormation.Resources['testBucket' + (new Date).getTime().toString()] = { "Type" : "AWS::S3::Bucket" };