Index: src/wp-admin/includes/file.php
===================================================================
--- src/wp-admin/includes/file.php	(revision 41593)
+++ src/wp-admin/includes/file.php	(working copy)
@@ -70,7 +70,7 @@
  * @since 1.5.0
  *
  * @global array $wp_file_descriptions Theme file descriptions.
- * @global array $allowed_files        List of allowed files. 
+ * @global array $allowed_files        List of allowed files.
  * @param string $file Filesystem path or filename
  * @return string Description of file from $wp_file_descriptions or basename of $file if description doesn't exist.
  *                Appends 'Page Template' to basename of $file if the file is a page template
@@ -123,6 +123,7 @@
  *
  * @param string $folder Optional. Full path to folder. Default empty.
  * @param int    $levels Optional. Levels of folders to follow, Default 100 (PHP Loop limit).
+ * @param bool   $skip   Optional. Skip hidden files/folders, node_modules type folders. Default true.
  * @return bool|array False on failure, Else array of files
  */
 function list_files( $folder = '', $levels = 100 ) {
@@ -132,11 +133,24 @@
 	if ( ! $levels )
 		return false;
 
+	/**
+	 * Filters the array of excluded directories and files while scanning the folder.
+	 *
+	 * @since 4.9
+	 *
+	 * @param array $exclusions Array of excluded directories and files.
+	 */
+	$exclusions = (array) apply_filters( 'list_files_exclusions', array( 'CVS', 'node_modules' ) );
+
 	$files = array();
 	if ( $dir = @opendir( $folder ) ) {
 		while (($file = readdir( $dir ) ) !== false ) {
 			if ( in_array($file, array('.', '..') ) )
 				continue;
+
+			if ( '.' == $file[0] || in_array( $file, $exclusions, true ) )
+				continue;
+
 			if ( is_dir( $folder . '/' . $file ) ) {
 				$files2 = list_files( $folder . '/' . $file, $levels - 1);
 				if ( $files2 )
Index: src/wp-admin/includes/plugin.php
===================================================================
--- src/wp-admin/includes/plugin.php	(revision 41593)
+++ src/wp-admin/includes/plugin.php	(working copy)
@@ -193,32 +193,24 @@
 function get_plugin_files($plugin) {
 	$plugin_file = WP_PLUGIN_DIR . '/' . $plugin;
 	$dir = dirname($plugin_file);
+
+	$plugin_files = get_transient( 'list_files_cache_' . $dir );
+	if ( ! empty( $plugin_files ) )
+		return $plugin_files;
+
 	$plugin_files = array($plugin);
 	if ( is_dir($dir) && $dir != WP_PLUGIN_DIR ) {
-		$plugins_dir = @ opendir( $dir );
-		if ( $plugins_dir ) {
-			while (($file = readdir( $plugins_dir ) ) !== false ) {
-				if ( substr($file, 0, 1) == '.' )
-					continue;
-				if ( is_dir( $dir . '/' . $file ) ) {
-					$plugins_subdir = @ opendir( $dir . '/' . $file );
-					if ( $plugins_subdir ) {
-						while (($subfile = readdir( $plugins_subdir ) ) !== false ) {
-							if ( substr($subfile, 0, 1) == '.' )
-								continue;
-							$plugin_files[] = plugin_basename("$dir/$file/$subfile");
-						}
-						@closedir( $plugins_subdir );
-					}
-				} else {
-					if ( plugin_basename("$dir/$file") != $plugin )
-						$plugin_files[] = plugin_basename("$dir/$file");
-				}
-			}
-			@closedir( $plugins_dir );
-		}
+
+		$list_files = list_files( $dir );
+		$list_files = array_map( 'plugin_basename', $list_files );
+
+		$plugin_files += $list_files;
+		$plugin_files = array_unique( $plugin_files );
+
 	}
 
+	set_transient( 'list_files_cache_'. $dir, $plugin_files, MINUTE_IN_SECONDS );
+
 	return $plugin_files;
 }
 
