Repository: plotly/dash

Issue:
Allow excluding file patterns when generating component definitions 
Thanks a lot for making Dash -- we have found it very useful for building dashboards.

We ran into an issue migrating to the new `dash-generate-components` utility.

In [dash-bootstrap-components](https://github.com/ASIDataScience/dash-bootstrap-components), we keep the components in `src/components`. Besides components files, this also includes a few unit test files called, e.g. `src/components/__tests__/DropdownMenu.test.js`. When we run `dash-generate-components ./src/components <output>`, these test files also get picked up. This leads to a traceback in the build logs: 

```
Error with path src/components/__tests__/DropdownMenu.test.jsError: No suitable component definition found.
Error: No suitable component definition found.
at parse (/project/pascal/dash-bootstrap-components/node_modules/react-docgen/dist/parse.js:84:9)
at Object.defaultParse [as parse] (/project/pascal/dash-bootstrap-components/node_modules/react-docgen/dist/main.js:66:30)
at parseFile (/opt/anaconda/envs/Python3/lib/python3.6/site-packages/dash/extract-meta.js:64:51)
at dirs.forEach.filename (/opt/anaconda/envs/Python3/lib/python3.6/site-packages/dash/extract-meta.js:84:17)
at Array.forEach (<anonymous>)
at collectMetadataRecursively (/opt/anaconda/envs/Python3/lib/python3.6/site-packages/dash/extract-meta.js:79:14)
at dirs.forEach.filename (/opt/anaconda/envs/Python3/lib/python3.6/site-packages/dash/extract-meta.js:82:17)
at Array.forEach (<anonymous>)
at collectMetadataRecursively (/opt/anaconda/envs/Python3/lib/python3.6/site-packages/dash/extract-meta.js:79:14)
at componentPaths.forEach.componentPath (/opt/anaconda/envs/Python3/lib/python3.6/site-packages/dash/extract-meta.js:15:5)
```

While the error is, of course, legitimate, it would be nice to be able to suppress these tracebacks. I can see three routes:
- do nothing -- after all, this doesn't stop us from building `metadata.json`, it just makes it slightly more confusing.
- in `dash/extract-meta.js`, we could explicitly blacklist `__tests__` directories in the same way that files that don't end in `jsx?` are black-listed. AFAICT, the `__tests__` directory structure is the default with [jest](https://jestjs.io/docs/en/configuration.html#testmatch-array-string). [react-docgen](https://github.com/reactjs/react-docgen#cli) ignores `node_modules`, `__tests__` and `__mocks__` by default, so there is definitely a precedent.
- add a `--ignore` argument to `dash-generate-components` that allows passing file globs to be excluded. 

Very happy to submit a PR if you decide on what the best course of action is.


Discussion:

Resolving patch:
diff --git a/dash/development/component_generator.py b/dash/development/component_generator.py
--- a/dash/development/component_generator.py
+++ b/dash/development/component_generator.py
@@ -22,14 +22,17 @@ class _CombinedFormatter(argparse.ArgumentDefaultsHelpFormatter,
 
 # pylint: disable=too-many-locals
 def generate_components(components_source, project_shortname,
-                        package_info_filename='package.json'):
+                        package_info_filename='package.json',
+                        ignore='^_'):
     is_windows = sys.platform == 'win32'
 
     extract_path = pkg_resources.resource_filename('dash', 'extract-meta.js')
 
     os.environ['NODE_PATH'] = 'node_modules'
-    cmd = shlex.split('node {} {}'.format(extract_path, components_source),
-                      posix=not is_windows)
+    cmd = shlex.split(
+        'node {} {} {}'.format(extract_path, ignore, components_source),
+        posix=not is_windows
+    )
 
     shutil.copyfile('package.json',
                     os.path.join(project_shortname, package_info_filename))
@@ -83,10 +86,18 @@ def cli():
         default='package.json',
         help='The filename of the copied `package.json` to `project_shortname`'
     )
+    parser.add_argument(
+        '-i', '--ignore',
+        default='^_',
+        help='Files/directories matching the pattern will be ignored'
+    )
 
     args = parser.parse_args()
-    generate_components(args.components_source, args.project_shortname,
-                        package_info_filename=args.package_info_filename)
+    generate_components(
+        args.components_source, args.project_shortname,
+        package_info_filename=args.package_info_filename,
+        ignore=args.ignore
+    )
 
 
 if __name__ == '__main__':
diff --git a/dash/version.py b/dash/version.py
--- a/dash/version.py
+++ b/dash/version.py
@@ -1 +1 @@
-__version__ = '0.33.0'
+__version__ = '0.34.0'


Test patch: