diff --git a/includes/common.inc b/includes/common.inc
index 34fa9b9..475f65a 100644
--- a/includes/common.inc
+++ b/includes/common.inc
@@ -5512,12 +5512,22 @@ function drupal_system_listing($mask, $directory, $key = 'name', $min_depth = 1)
     $searchdir[] = "$config/$directory";
   }
 
+  // By default, do not check for files in common special-purpose directories.
+  // The folders here are front-end related and they have been added to avoid
+  // issues with Drupal recursive scanning. In this case, we added node_modules
+  // and bower_components. This also improves performance on frontend builds.
+  $ignore_directories = variable_get('drupal_file_scan_ignore_directories', array(
+    'node_modules',
+    'bower_components',
+  ));
+  $no_mask = '/^(\..*)|' . implode('|', $ignore_directories) . '$/';
+
   // Get current list of items.
   if (!function_exists('file_scan_directory')) {
     require_once DRUPAL_ROOT . '/includes/file.inc';
   }
   foreach ($searchdir as $dir) {
-    $files_to_add = file_scan_directory($dir, $mask, array('key' => $key, 'min_depth' => $min_depth));
+    $files_to_add = file_scan_directory($dir, $mask, array('key' => $key, 'min_depth' => $min_depth, 'nomask' => $no_mask));
 
     // Duplicate files found in later search directories take precedence over
     // earlier ones, so we want them to overwrite keys in our resulting
diff --git a/includes/file.inc b/includes/file.inc
index ba3da06..1b6a6d0 100644
--- a/includes/file.inc
+++ b/includes/file.inc
@@ -2104,9 +2104,18 @@ function file_download_access($uri) {
  *   'filename', and 'name' members corresponding to the matching files.
  */
 function file_scan_directory($dir, $mask, $options = array(), $depth = 0) {
+  // By default, do not check for files in common special-purpose directories.
+  // The folders here are front-end related and they have been added to avoid
+  // issues with Drupal recursive scanning. In this case, we added node_modules
+  // and bower_components. This also improves performance on frontend builds.
+  $ignore_directories = variable_get('drupal_file_scan_ignore_directories', array(
+    'node_modules',
+    'bower_components',
+  ));
+  $no_mask = '/^(\..*)|' . implode('|', $ignore_directories) . '$/';
   // Merge in defaults.
   $options += array(
-    'nomask' => '/(\.\.?|CVS)$/',
+    'nomask' => $no_mask,
     'callback' => 0,
     'recurse' => TRUE,
     'key' => 'uri',
diff --git a/includes/theme.inc b/includes/theme.inc
index 1accdce..5a22607 100644
--- a/includes/theme.inc
+++ b/includes/theme.inc
@@ -1325,8 +1325,19 @@ function drupal_find_theme_templates($cache, $extension, $path) {
 
   // Escape the periods in the extension.
   $regex = '/' . str_replace('.', '\.', $extension) . '$/';
+
+  // By default, do not check for files in common special-purpose directories.
+  // The folders here are front-end related and they have been added to avoid
+  // issues with Drupal recursive scanning. In this case, we added node_modules
+  // and bower_components. This also improves performance on frontend builds.
+  $ignore_directories = variable_get('drupal_file_scan_ignore_directories', array(
+    'node_modules',
+    'bower_components',
+  ));
+  $no_mask = '/^(\..*)|' . implode('|', $ignore_directories) . '$/';
+
   // Get a listing of all template files in the path to search.
-  $files = drupal_system_listing($regex, $path, 'name', 0);
+  $files = file_scan_directory($path, $regex, array('key' => 'name', 'nomask' => $no_mask));
 
   // Find templates that implement registered theme hooks and include that in
   // what is returned so that the registry knows that the theme has this
