Index: src/wp-admin/includes/file.php
===================================================================
--- src/wp-admin/includes/file.php	(revision 41739)
+++ src/wp-admin/includes/file.php	(working copy)
@@ -126,23 +126,42 @@
  * @return bool|array False on failure, Else array of files
  */
 function list_files( $folder = '', $levels = 100 ) {
-	if ( empty($folder) )
+	if ( empty( $folder ) ) {
 		return false;
+	}
 
-	if ( ! $levels )
+	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', 'vendor', 'bower_components' ) );
+
 	$files = array();
-	if ( $dir = @opendir( $folder ) ) {
-		while (($file = readdir( $dir ) ) !== false ) {
-			if ( in_array($file, array('.', '..') ) )
+	$dir = @opendir( $folder );
+	if ( $dir ) {
+		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 )
+				$files2 = list_files( $folder . '/' . $file, $levels - 1 );
+				if ( $files2 ) {
 					$files = array_merge($files, $files2 );
-				else
+				} else {
 					$files[] = $folder . '/' . $file . '/';
+				}
 			} else {
 				$files[] = $folder . '/' . $file;
 			}
Index: src/wp-admin/includes/plugin.php
===================================================================
--- src/wp-admin/includes/plugin.php	(revision 41739)
+++ src/wp-admin/includes/plugin.php	(working copy)
@@ -190,35 +190,30 @@
  * @param string $plugin Path to the main plugin file from plugins directory.
  * @return array List of files relative to the plugin root.
  */
-function get_plugin_files($plugin) {
+function get_plugin_files( $plugin ) {
 	$plugin_file = WP_PLUGIN_DIR . '/' . $plugin;
-	$dir = dirname($plugin_file);
-	$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 );
-		}
+	$dir = dirname( $plugin_file );
+
+	$data = get_plugin_data( $plugin_file );
+	$label = isset( $data['Version'] ) ? 'list_files_cache_' . $dir . '-' . $data['Version'] : 'list_files_cache_' . $dir;
+
+	$plugin_files = get_transient( $label );
+	if ( ! empty( $plugin_files ) ) {
+		return $plugin_files;
 	}
 
+	$plugin_files = array( $plugin );
+	if ( is_dir( $dir ) && WP_PLUGIN_DIR !== $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( $label, $plugin_files, HOUR_IN_SECONDS );
+
 	return $plugin_files;
 }
 
Index: src/wp-admin/theme-editor.php
===================================================================
--- src/wp-admin/theme-editor.php	(revision 41739)
+++ src/wp-admin/theme-editor.php	(working copy)
@@ -75,16 +75,16 @@
 foreach ( $file_types as $type ) {
 	switch ( $type ) {
 		case 'php':
-			$allowed_files += $theme->get_files( 'php', 1 );
+			$allowed_files += $theme->get_files( 'php', -1 );
 			$has_templates = ! empty( $allowed_files );
 			break;
 		case 'css':
-			$style_files = $theme->get_files( 'css' );
+			$style_files = $theme->get_files( 'css', -1 );
 			$allowed_files['style.css'] = $style_files['style.css'];
 			$allowed_files += $style_files;
 			break;
 		default:
-			$allowed_files += $theme->get_files( $type );
+			$allowed_files += $theme->get_files( $type, -1 );
 			break;
 	}
 }
Index: src/wp-includes/class-wp-theme.php
===================================================================
--- src/wp-includes/class-wp-theme.php	(revision 41739)
+++ src/wp-includes/class-wp-theme.php	(working copy)
@@ -981,14 +981,37 @@
 	 * @param int $depth Optional. How deep to search for files. Defaults to a flat scan (0 depth). -1 depth is infinite.
 	 * @param bool $search_parent Optional. Whether to return parent files. Defaults to false.
 	 * @return array Array of files, keyed by the path to the file relative to the theme's directory, with the values
-	 * 	             being absolute paths.
+	 *               being absolute paths.
 	 */
 	public function get_files( $type = null, $depth = 0, $search_parent = false ) {
-		$files = (array) self::scandir( $this->get_stylesheet_directory(), $type, $depth );
+		// get and cache all theme files to start with.
+		$label = 'list_files_cache_' . $this->get( 'Name' ) . '-' . $this->get( 'Version' );
+		$all_files = get_transient( $label );
+		if ( empty( $all_files ) ) {
+			$all_files = (array) self::scandir( $this->get_stylesheet_directory(), null, -1 );
 
-		if ( $search_parent && $this->parent() )
-			$files += (array) self::scandir( $this->get_template_directory(), $type, $depth );
+			if ( $search_parent && $this->parent() ) {
+				$all_files += (array) self::scandir( $this->get_template_directory(), null, -1 );
+			}
 
+			set_transient( $label, $all_files, HOUR_IN_SECONDS );
+		}
+
+		// Filter $all_files by $type & $depth.
+		$files = array();
+		if ( $type ) {
+			$type = (array) $type;
+			$_extensions = implode( '|', $type );
+		}
+		foreach ( $all_files as $key => $file ) {
+			if ( $depth >= 0 && substr_count( $key, '/' ) > $depth ) {
+				continue; // Filter by depth.
+			}
+			if ( ! $type || preg_match( '~\.(' . $_extensions . ')$~', $file ) ) { // Filter by type.
+				$files[ $key ] = $file;
+			}
+		}
+
 		return $files;
 	}
 
@@ -1107,8 +1130,9 @@
 	 *                     with `$relative_path`, with the values being absolute paths. False otherwise.
 	 */
 	private static function scandir( $path, $extensions = null, $depth = 0, $relative_path = '' ) {
-		if ( ! is_dir( $path ) )
+		if ( ! is_dir( $path ) ) {
 			return false;
+		}
 
 		if ( $extensions ) {
 			$extensions = (array) $extensions;
@@ -1116,8 +1140,9 @@
 		}
 
 		$relative_path = trailingslashit( $relative_path );
-		if ( '/' == $relative_path )
+		if ( '/' == $relative_path ) {
 			$relative_path = '';
+		}
 
 		$results = scandir( $path );
 		$files = array();
@@ -1125,11 +1150,11 @@
 		/**
 		 * Filters the array of excluded directories and files while scanning theme folder.
 		 *
- 		 * @since 4.7.4
+		 * @since 4.7.4
 		 *
 		 * @param array $exclusions Array of excluded directories and files.
 		 */
-		$exclusions = (array) apply_filters( 'theme_scandir_exclusions', array( 'CVS', 'node_modules' ) );
+		$exclusions = (array) apply_filters( 'theme_scandir_exclusions', array( 'CVS', 'node_modules', 'vendor', 'bower_components' ) );
 
 		foreach ( $results as $result ) {
 			if ( '.' == $result[0] || in_array( $result, $exclusions, true ) ) {
@@ -1136,8 +1161,9 @@
 				continue;
 			}
 			if ( is_dir( $path . '/' . $result ) ) {
-				if ( ! $depth )
+				if ( ! $depth ) {
 					continue;
+				}
 				$found = self::scandir( $path . '/' . $result, $extensions, $depth - 1 , $relative_path . $result );
 				$files = array_merge_recursive( $files, $found );
 			} elseif ( ! $extensions || preg_match( '~\.(' . $_extensions . ')$~', $result ) ) {
