Repository: ansible-collections/community.general

Issue:
Add --no-optional to the npm plugin
<!--- Verify first that your feature was not already discussed on GitHub -->
<!--- Complete *all* sections as described, this form is processed automatically -->

##### SUMMARY
<!--- Describe the new feature/improvement briefly below -->

community.general.npm should support the `--no-optional` argument.

##### ISSUE TYPE
- Feature Idea

##### COMPONENT NAME
<!--- Write the short name of the module, plugin, task or feature below, use your best guess if unsure -->
community.general.npm


##### ADDITIONAL INFORMATION
<!--- Describe how the feature would be used, why it is needed and what it would solve -->

The `npm` plugin should support the `--no-optional` argument of the `npm` command. It is helpful in solving certain issues with package installation.

For example, using the `npm` module to install `statsd` (at least when installing it globally), it pulls in `modern-syslog` as an optional dependency. That causes `node-gyp rebuild` to be run, and that in turn exits with 1 for an unknown reason, causing the `npm` module to fail during a playbook run. In reality, the actual `statsd` package has been successfully installed. (This is on an Ubuntu 20.04 system.)

I don't have the time nor the expertise to debug why `node-gyp` is causing issues, but I could circumvent it cleanly if `--no-optional` was supported.


Discussion:
Files identified in the description:
None

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
!component =plugins/modules/packaging/language/npm.py
Files identified in the description:
* [`plugins/modules/packaging/language/npm.py`](https://github.com/ansible-collections/community.general/blob/master/plugins/modules/packaging/language/npm.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @shane-walker @xcambar
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->

Resolving patch:
diff --git a/plugins/modules/packaging/language/npm.py b/plugins/modules/packaging/language/npm.py
--- a/plugins/modules/packaging/language/npm.py
+++ b/plugins/modules/packaging/language/npm.py
@@ -76,6 +76,12 @@
     type: str
     default: present
     choices: [ "present", "absent", "latest" ]
+  no_optional:
+    description:
+      - Use the C(--no-optional) flag when installing.
+    type: bool
+    default: no
+    version_added: 2.0.0
 requirements:
     - npm installed in bin path (recommended /usr/local/bin)
 '''
@@ -144,6 +150,7 @@ def __init__(self, module, **kwargs):
         self.ignore_scripts = kwargs['ignore_scripts']
         self.unsafe_perm = kwargs['unsafe_perm']
         self.state = kwargs['state']
+        self.no_optional = kwargs['no_optional']
 
         if kwargs['executable']:
             self.executable = kwargs['executable'].split(' ')
@@ -172,6 +179,8 @@ def _exec(self, args, run_in_check_mode=False, check_rc=True):
             if self.registry:
                 cmd.append('--registry')
                 cmd.append(self.registry)
+            if self.no_optional:
+                cmd.append('--no-optional')
 
             # If path is specified, cd into that path and run the command.
             cwd = None
@@ -245,6 +254,7 @@ def main():
         ignore_scripts=dict(default=False, type='bool'),
         unsafe_perm=dict(default=False, type='bool'),
         ci=dict(default=False, type='bool'),
+        no_optional=dict(default=False, type='bool'),
     )
     arg_spec['global'] = dict(default=False, type='bool')
     module = AnsibleModule(
@@ -263,6 +273,7 @@ def main():
     ignore_scripts = module.params['ignore_scripts']
     unsafe_perm = module.params['unsafe_perm']
     ci = module.params['ci']
+    no_optional = module.params['no_optional']
 
     if not path and not glbl:
         module.fail_json(msg='path must be specified when not using global')
@@ -271,7 +282,7 @@ def main():
 
     npm = Npm(module, name=name, path=path, version=version, glbl=glbl, production=production,
               executable=executable, registry=registry, ignore_scripts=ignore_scripts,
-              unsafe_perm=unsafe_perm, state=state)
+              unsafe_perm=unsafe_perm, state=state, no_optional=no_optional)
 
     changed = False
     if ci:


Test patch: