Repository: serverless-heaven/serverless-webpack (js)

Issue:
Add support to keep output directory after package/deploy
<!--
1. Please check if an issue already exists so there are no duplicates
2. Fill out the whole template so we have a good overview on the issue
3. Do not remove any section of the template. If something is not applicable leave it empty but leave it in the Issue
4. Please follow the template, otherwise we'll have to ask you to update it
-->

# This is a Feature Proposal

## Description

We're trying to upload our sourcemaps to our bug tracker, Sentry. They provide a webpack plugin to do this, but it doesn't play well with multiple webpack configurations (it attempts to upload all source maps for each configuration). Instead of using the plugin, we'd like to use their cli tool. Unfortunately, serverless-webpack cleans up the output path before we have a chance to do this. 

I could write a serverless plugin to slot in before serverless-webpack cleans up probably, but it'd be nice if we could just disable the cleanup step in the configuration.

Thanks!

Pull request:
Allow to keep the output directory
<!--
1. Do not remove any section of the template. If something is not applicable leave it empty but leave it in the PR
2. Please follow the template, otherwise we'll have to ask you to update it and it will take longer until your PR is merged
-->

## What did you implement:

Closes #453 & #467

<!--
Briefly describe the feature if no issue exists for this PR. If possible only
submit PRs for existing issues. If the PR is trivial (like doc changes or simple
code fixes) it can be submitted without a related issue, but as soon as it adds
or changes functionality, a related issue should be present.
-->

## How did you implement it:
There's an option we can pass `keepOutputDirectory`, which will skip the cleanup process.
```yaml
# serverless.yml
custom:
  webpack:
    keepOutputDirectory: true
```

<!--
If this is a nontrivial change please briefly describe your implementation so its easy for us to understand and review your code.
-->

## How can we verify it:
1) As described above, just add the flag in `serverless.yml`.
2) Run `sls package`
3) Verify that `.webpack` is present

<!--
Add any applicable config, commands, screenshots or other resources
to make it easy for us to verify this works. The easier you make it for us
to review a PR, the faster we can review and merge it.

Examples:
* Step by step description, how to verify
* Screenshots - Showing the difference between your output and the master
* Other - Anything else that comes to mind to help us evaluate
-->

## Todos:

- [x] Write tests
- [x] Write documentation
- [x] Fix linting errors
- [x] Make sure code coverage hasn't dropped
- [x] Provide verification config / commands / resources
- [x] Enable "Allow edits from maintainers" for this PR
- [x] Update the messages below

***Is this ready for review?:*** YES
***Is it a breaking change?:*** NO


Patch:
diff --git a/README.md b/README.md
index 3904246..c3d5eb4 100644
--- a/README.md
+++ b/README.md
@@ -430,6 +430,23 @@ from a local folder (e.g. `"mymodule": "file:../../myOtherProject/mymodule"`).
 With that you can do test deployments from the local machine with different
 module versions or modules before they are published officially.
 
+#### Keep output directory after packaging
+
+You can keep the output directory (defaults to `.webpack`) from being removed
+after build.
+
+Just add `keepOutputDirectory: true`
+
+```yaml
+# serverless.yml
+custom:
+  webpack:
+    keepOutputDirectory: true
+```
+
+This can be useful, in case you want to upload the source maps to your Error
+reporting system, or just have it available for some post processing.
+
 #### Examples
 
 You can find an example setups in the [`examples`][link-examples] folder.
diff --git a/lib/Configuration.js b/lib/Configuration.js
index ba49252..75d81cd 100644
--- a/lib/Configuration.js
+++ b/lib/Configuration.js
@@ -13,6 +13,7 @@ const DefaultConfig = {
   includeModules: false,
   packager: 'npm',
   packagerOptions: {},
+  keepOutputDirectory: false,
   config: null
 };
 
@@ -66,6 +67,10 @@ class Configuration {
     return this._hasLegacyConfig;
   }
 
+  get keepOutputDirectory() {
+    return this._config.keepOutputDirectory;
+  }
+
   toJSON() {
     return _.omitBy(this._config, _.isNil);
   }
diff --git a/lib/cleanup.js b/lib/cleanup.js
index b71cc6d..ef34a96 100644
--- a/lib/cleanup.js
+++ b/lib/cleanup.js
@@ -7,10 +7,14 @@ module.exports = {
   cleanup() {
     const webpackOutputPath = this.webpackOutputPath;
 
-    this.options.verbose && this.serverless.cli.log(`Remove ${webpackOutputPath}`);
-
-    if (this.serverless.utils.dirExistsSync(webpackOutputPath)) {
-      fse.removeSync(webpackOutputPath);
+    const keepOutputDirectory = this.configuration.keepOutputDirectory;
+    if (!keepOutputDirectory) {
+      this.options.verbose && this.serverless.cli.log(`Remove ${webpackOutputPath}`);
+      if (this.serverless.utils.dirExistsSync(webpackOutputPath)) {
+        fse.removeSync(webpackOutputPath);
+      }
+    } else {
+      this.options.verbose && this.serverless.cli.log(`Keeping ${webpackOutputPath}`);
     }
 
     return BbPromise.resolve();


Test patch:
diff --git a/lib/Configuration.test.js b/lib/Configuration.test.js
index 6a6c5cd..6d61bdb 100644
--- a/lib/Configuration.test.js
+++ b/lib/Configuration.test.js
@@ -18,6 +18,7 @@ describe('Configuration', () => {
         includeModules: false,
         packager: 'npm',
         packagerOptions: {},
+        keepOutputDirectory: false,
         config: null
       };
     });
@@ -66,6 +67,7 @@ describe('Configuration', () => {
         includeModules: { forceInclude: ['mod1'] },
         packager: 'npm',
         packagerOptions: {},
+        keepOutputDirectory: false,
         config: null
       });
     });
@@ -85,6 +87,7 @@ describe('Configuration', () => {
         includeModules: { forceInclude: ['mod1'] },
         packager: 'npm',
         packagerOptions: {},
+        keepOutputDirectory: false,
         config: null
       });
     });
@@ -103,6 +106,7 @@ describe('Configuration', () => {
         includeModules: { forceInclude: ['mod1'] },
         packager: 'npm',
         packagerOptions: {},
+        keepOutputDirectory: false,
         config: null
       });
     });
diff --git a/tests/cleanup.test.js b/tests/cleanup.test.js
index e9aaf20..e7d9a6d 100644
--- a/tests/cleanup.test.js
+++ b/tests/cleanup.test.js
@@ -54,7 +54,8 @@ describe('cleanup', () => {
     module = _.assign({
       serverless,
       options: {},
-      webpackOutputPath: 'my/Output/Path'
+      webpackOutputPath: 'my/Output/Path',
+      configuration: {}
     }, baseModule);
   });
 
@@ -88,4 +89,19 @@ describe('cleanup', () => {
       return null;
     });
   });
+
+  it('should keep output dir if keepOutputDir = true', () => {
+    dirExistsSyncStub.returns(true);
+    fseMock.removeSync.reset();
+
+    const configuredModule = _.assign({}, module, {
+      configuration: { keepOutputDirectory: true }
+    });
+    return expect(configuredModule.cleanup()).to.be.fulfilled
+    .then(() => {
+      expect(dirExistsSyncStub).to.not.have.been.calledOnce;
+      expect(fseMock.removeSync).to.not.have.been.called;
+      return null;
+    });
+  })
 });