Repository: serverless/serverless (JavaScript) — Feature

Issue:
Remove defaults property and deprecation note
# This is a Feature Proposal

## Description

The `defaults` property in  the `serverless.yml` file should be removed alongside the deprecation notice.


Discussion:

Resolving patch:
diff --git a/lib/classes/Service.js b/lib/classes/Service.js
index 9d721cde7c2..02d408e2024 100644
--- a/lib/classes/Service.js
+++ b/lib/classes/Service.js
@@ -13,8 +13,7 @@ class Service {
 
     // Default properties
     this.service = null;
-    this.provider = {};
-    this.defaults = {
+    this.provider = {
       stage: 'dev',
       region: 'us-east-1',
       variableSyntax: '\\${([ :a-zA-Z0-9._,\\-\\/\\(\\)]+?)}',
@@ -93,12 +92,15 @@ class Service {
         }
 
         that.service = serverlessFile.service;
-        that.provider = serverlessFile.provider;
         that.custom = serverlessFile.custom;
         that.plugins = serverlessFile.plugins;
         that.resources = serverlessFile.resources;
         that.functions = serverlessFile.functions || {};
 
+        // merge so that the default settings are still in place and
+        // won't be overwritten
+        that.provider = _.merge(that.provider, serverlessFile.provider);
+
         if (serverlessFile.package) {
           that.package.individually = serverlessFile.package.individually;
           that.package.artifact = serverlessFile.package.artifact;
@@ -106,53 +108,6 @@ class Service {
           that.package.include = serverlessFile.package.include;
         }
 
-        if (serverlessFile.defaults && serverlessFile.defaults.stage) {
-          this.defaults.stage = serverlessFile.defaults.stage;
-        }
-        if (serverlessFile.defaults && serverlessFile.defaults.region) {
-          this.defaults.region = serverlessFile.defaults.region;
-        }
-        if (serverlessFile.defaults && serverlessFile.defaults.variableSyntax) {
-          this.defaults.variableSyntax = serverlessFile.defaults.variableSyntax;
-        }
-
-        // load defaults property for backward compatibility
-        if (serverlessFile.defaults) {
-          const warningMessage = [
-            'Deprecation Notice: the "defaults" property in serverless.yml',
-            ' is deprecated. The "stage", "region" & "variableSyntax" properties',
-            ' has been moved to the "provider" property instead. Please update',
-            ' your serverless.yml file asap. For more info, you can check our docs.',
-          ].join('');
-          this.serverless.cli.log(warningMessage);
-
-          if (serverlessFile.defaults.stage) {
-            this.defaults.stage = serverlessFile.defaults.stage;
-          }
-          if (serverlessFile.defaults.region) {
-            this.defaults.region = serverlessFile.defaults.region;
-          }
-          if (serverlessFile.defaults.variableSyntax) {
-            this.defaults.variableSyntax = serverlessFile.defaults.variableSyntax;
-          }
-        }
-
-        // if exists, move provider to defaults for backward compatibility
-        if (serverlessFile.provider.stage) {
-          this.defaults.stage = serverlessFile.provider.stage;
-        }
-        if (serverlessFile.provider.region) {
-          this.defaults.region = serverlessFile.provider.region;
-        }
-        if (serverlessFile.provider.variableSyntax) {
-          this.defaults.variableSyntax = serverlessFile.provider.variableSyntax;
-        }
-
-        // make sure provider obj is in sync with default for backward compatibility
-        this.provider.stage = this.defaults.stage;
-        this.provider.region = this.defaults.region;
-        this.provider.variableSyntax = this.defaults.variableSyntax;
-
         // setup function.name property
         const stageNameForFunction = options.stage || this.provider.stage;
         _.forEach(that.functions, (functionObj, functionName) => {
diff --git a/lib/classes/Utils.js b/lib/classes/Utils.js
index ec57f161c33..0dead50140b 100644
--- a/lib/classes/Utils.js
+++ b/lib/classes/Utils.js
@@ -282,12 +282,6 @@ class Utils {
 
       let hasCustomVariableSyntaxDefined = false;
       const defaultVariableSyntax = '\\${([ :a-zA-Z0-9._,\\-\\/\\(\\)]+?)}';
-      // check if the variableSyntax in the defaults section is defined
-      if (serverless.service.defaults &&
-        serverless.service.defaults.variableSyntax &&
-        serverless.service.defaults.variableSyntax !== defaultVariableSyntax) {
-        hasCustomVariableSyntaxDefined = true;
-      }
 
       // check if the variableSyntax in the provider section is defined
       if (serverless.service.provider &&
diff --git a/lib/classes/Variables.js b/lib/classes/Variables.js
index 1e95bf03e9a..5c567858298 100644
--- a/lib/classes/Variables.js
+++ b/lib/classes/Variables.js
@@ -19,7 +19,7 @@ class Variables {
   }
 
   loadVariableSyntax() {
-    this.variableSyntax = RegExp(this.service.defaults.variableSyntax, 'g');
+    this.variableSyntax = RegExp(this.service.provider.variableSyntax, 'g');
   }
 
   populateService(processedOptions) {
@@ -28,10 +28,9 @@ class Variables {
 
     this.loadVariableSyntax();
 
-    const variableSyntaxProperty = this.service.defaults.variableSyntax;
+    const variableSyntaxProperty = this.service.provider.variableSyntax;
 
     // temporally remove variable syntax from service otherwise it'll match
-    this.service.defaults.variableSyntax = true;
     this.service.provider.variableSyntax = true;
 
     /*
@@ -48,7 +47,6 @@ class Variables {
       }
     });
 
-    this.service.defaults.variableSyntax = variableSyntaxProperty;
     this.service.provider.variableSyntax = variableSyntaxProperty;
     return this.service;
   }
diff --git a/lib/plugins/aws/lib/validate.js b/lib/plugins/aws/lib/validate.js
index 038754327ee..b0333ff95d5 100644
--- a/lib/plugins/aws/lib/validate.js
+++ b/lib/plugins/aws/lib/validate.js
@@ -10,10 +10,10 @@ module.exports = {
     }
 
     this.options.stage = this.options.stage
-      || (this.serverless.service.defaults && this.serverless.service.defaults.stage)
+      || (this.serverless.service.provider && this.serverless.service.provider.stage)
       || 'dev';
     this.options.region = this.options.region
-      || (this.serverless.service.defaults && this.serverless.service.defaults.region)
+      || (this.serverless.service.provider && this.serverless.service.provider.region)
       || 'us-east-1';
 
     return BbPromise.resolve();


Test patch:
diff --git a/lib/classes/Service.test.js b/lib/classes/Service.test.js
index 3f167655932..67d044e7e10 100644
--- a/lib/classes/Service.test.js
+++ b/lib/classes/Service.test.js
@@ -22,8 +22,7 @@ describe('Service', () => {
       const serviceInstance = new Service(serverless);
 
       expect(serviceInstance.service).to.be.equal(null);
-      expect(serviceInstance.provider).to.deep.equal({});
-      expect(serviceInstance.defaults).to.deep.equal({
+      expect(serviceInstance.provider).to.deep.equal({
         stage: 'dev',
         region: 'us-east-1',
         variableSyntax: '\\${([ :a-zA-Z0-9._,\\-\\/\\(\\)]+?)}',
@@ -121,8 +120,8 @@ describe('Service', () => {
       const SUtils = new Utils();
       const serverlessYml = {
         service: 'new-service',
-        provider: 'aws',
-        defaults: {
+        provider: {
+          name: 'aws',
           stage: 'dev',
           region: 'us-east-1',
           variableSyntax: '\\${{([\\s\\S]+?)}}',
@@ -156,7 +155,7 @@ describe('Service', () => {
       return serviceInstance.load().then(() => {
         expect(serviceInstance.service).to.be.equal('new-service');
         expect(serviceInstance.provider.name).to.deep.equal('aws');
-        expect(serviceInstance.defaults.variableSyntax).to.equal('\\${{([\\s\\S]+?)}}');
+        expect(serviceInstance.provider.variableSyntax).to.equal('\\${{([\\s\\S]+?)}}');
         expect(serviceInstance.plugins).to.deep.equal(['testPlugin']);
         expect(serviceInstance.resources.aws).to.deep.equal({ resourcesProp: 'value' });
         expect(serviceInstance.resources.azure).to.deep.equal({});
@@ -204,8 +203,8 @@ describe('Service', () => {
       const SUtils = new Utils();
       const serverlessYml = {
         service: 'new-service',
-        provider: 'aws',
-        defaults: {
+        provider: {
+          name: 'aws',
           stage: 'dev',
           region: 'us-east-1',
           variableSyntax: '\\${{([\\s\\S]+?)}}',
diff --git a/lib/classes/Utils.test.js b/lib/classes/Utils.test.js
index 03f438d1b2c..3c338b4880e 100644
--- a/lib/classes/Utils.test.js
+++ b/lib/classes/Utils.test.js
@@ -396,8 +396,6 @@ describe('Utils', () => {
         provider: {
           name: 'aws',
           runtime: 'nodejs4.3',
-        },
-        defaults: {
           stage: 'dev',
           region: 'us-east-1',
           variableSyntax: '\\${foo}',
diff --git a/lib/classes/Variables.test.js b/lib/classes/Variables.test.js
index 6d929008291..213ae101402 100644
--- a/lib/classes/Variables.test.js
+++ b/lib/classes/Variables.test.js
@@ -28,7 +28,7 @@ describe('Variables', () => {
     it('should set variableSyntax', () => {
       const serverless = new Serverless();
 
-      serverless.service.defaults.variableSyntax = '\\${{([\\s\\S]+?)}}';
+      serverless.service.provider.variableSyntax = '\\${{([\\s\\S]+?)}}';
 
       serverless.variables.loadVariableSyntax();
       expect(serverless.variables.variableSyntax).to.be.a('RegExp');
@@ -55,7 +55,6 @@ describe('Variables', () => {
       const fooValue = '${clientId()}';
       const barValue = 'test';
 
-      serverless.service.defaults.variableSyntax = variableSyntax;
       serverless.service.provider.variableSyntax = variableSyntax;
 
       serverless.service.custom = {
@@ -68,7 +67,7 @@ describe('Variables', () => {
       };
 
       serverless.variables.populateService();
-      expect(serverless.service.defaults.variableSyntax).to.equal(variableSyntax);
+      expect(serverless.service.provider.variableSyntax).to.equal(variableSyntax);
       expect(serverless.service.resources.foo).to.equal(fooValue);
       expect(serverless.service.resources.bar).to.equal(barValue);
     });
@@ -352,14 +351,13 @@ describe('Variables', () => {
       const serverless = new Serverless();
       serverless.variables.service = {
         service: 'testService',
-        provider: 'testProvider',
-        defaults: serverless.service.defaults,
+        provider: serverless.service.provider,
       };
 
       serverless.variables.loadVariableSyntax();
 
-      const valueToPopulate = serverless.variables.getValueFromSelf('self:provider');
-      expect(valueToPopulate).to.be.equal('testProvider');
+      const valueToPopulate = serverless.variables.getValueFromSelf('self:service');
+      expect(valueToPopulate).to.be.equal('testService');
     });
   });
 
@@ -554,7 +552,7 @@ describe('Variables', () => {
           },
           anotherVar: '${self:custom.var}',
         },
-        defaults: serverless.service.defaults,
+        provider: serverless.service.provider,
       };
 
       serverless.variables.loadVariableSyntax();
diff --git a/lib/plugins/aws/lib/validate.test.js b/lib/plugins/aws/lib/validate.test.js
index 1489cbdc04b..d0c6a8dbff5 100644
--- a/lib/plugins/aws/lib/validate.test.js
+++ b/lib/plugins/aws/lib/validate.test.js
@@ -39,9 +39,9 @@ describe('#validate', () => {
       });
     });
 
-    it('should use the service.defaults stage if present', () => {
+    it('should use the service.provider stage if present', () => {
       awsPlugin.options.stage = false;
-      awsPlugin.serverless.service.defaults = {
+      awsPlugin.serverless.service.provider = {
         stage: 'some-stage',
       };
 
@@ -57,9 +57,9 @@ describe('#validate', () => {
       });
     });
 
-    it('should use the service.defaults region if present', () => {
+    it('should use the service.provider region if present', () => {
       awsPlugin.options.region = false;
-      awsPlugin.serverless.service.defaults = {
+      awsPlugin.serverless.service.provider = {
         region: 'some-region',
       };