Преглед изворни кода

Resolve symlinks before passing paths to the watcher.

Piotrek Koszuliński пре 10 година
родитељ
комит
0d80d035e9
2 измењених фајлова са 93 додато и 37 уклоњено
  1. 49 12
      dev/tasks/gulp/build.js
  2. 44 25
      dev/tasks/gulp/utils.js

+ 49 - 12
dev/tasks/gulp/build.js

@@ -19,6 +19,7 @@ const KNOWN_OPTIONS = {
 	}
 };
 
+const fs = require( 'fs' );
 const path = require( 'path' );
 const gulp = require( 'gulp' );
 const del = require( 'del' );
@@ -28,7 +29,6 @@ const gutil = require( 'gulp-util' );
 const minimist = require( 'minimist' );
 const utils = require( './utils' );
 
-const sep = path.sep;
 const options = minimist( process.argv.slice( 2 ), KNOWN_OPTIONS[ process.argv[ 2 ] ] );
 
 module.exports = ( config ) => {
@@ -56,37 +56,74 @@ module.exports = ( config ) => {
 			/**
 			 * Returns a stream with just the main file (`ckeditor5/ckeditor.js`).
 			 *
-			 * @param {Boolean} [watch] Whether the files should be watched.
+			 * @param {Boolean} [watch] Whether to watch the files.
 			 * @returns {Stream}
 			 */
 			main( watch ) {
-				return utils.src( config.ROOT_DIR, 'ckeditor.js', watch );
+				const glob = path.join( config.ROOT_DIR, 'ckeditor.js' );
+
+				return gulp.src( glob )
+					.pipe( watch ? utils.watch( glob ) : utils.noop() );
 			},
 
 			/**
 			 * Returns a stream of all source files from CKEditor 5.
 			 *
-			 * @param {Boolean} [watch] Whether the files should be watched.
+			 * @param {Boolean} [watch] Whether to watch the files.
 			 * @returns {Stream}
 			 */
 			ckeditor5( watch ) {
-				return utils.src( config.ROOT_DIR, 'src/**/*.js', watch )
+				const glob = path.join( config.ROOT_DIR, 'src', '**', '*.js' );
+
+				return gulp.src( glob )
+					.pipe( watch ? utils.watch( glob ) : utils.noop() )
 					.pipe( utils.wrapCKEditor5Module() );
 			},
 
 			/**
 			 * Returns a stream of all source files from CKEditor 5 dependencies.
 			 *
-			 * @param {Boolean} [watch] Whether the files should be watched.
+			 * @param {Boolean} [watch] Whether to watch the files.
 			 * @returns {Stream}
 			 */
 			modules( watch ) {
-				// For an odd reason file.dirname does not contain `node_modules/`. Maybe the base dir
-				// is automatically set to only the varying piece of the path.
-				const modulePathPattern = new RegExp( `(ckeditor5-[^${ sep }]+)${ sep }src` );
-
-				return utils.src( config.ROOT_DIR, 'node_modules/ckeditor5-*/src/**/*.js', watch )
-					.pipe( utils.unpackModules( modulePathPattern ) );
+				// Find all CKEditor5 package directories. Resolve symlinks so we watch real directories
+				// in order to workaround https://github.com/paulmillr/chokidar/issues/419.
+				const dirs = fs.readdirSync( path.join( config.ROOT_DIR, 'node_modules' ) )
+					// Look for ckeditor5-* directories.
+					.filter( ( fileName ) => fileName.indexOf( 'ckeditor5-' ) === 0 )
+					// Resolve symlinks and keep only directories.
+					.map( ( fileName ) => {
+						let filePath = path.join( config.ROOT_DIR, 'node_modules', fileName );
+						let stat = fs.lstatSync( filePath );
+
+						if ( stat.isSymbolicLink() ) {
+							filePath = fs.realpathSync( filePath );
+							stat = fs.lstatSync( filePath );
+						}
+
+						if ( stat.isDirectory() ) {
+							return filePath;
+						}
+
+						// Filter...
+						return false;
+					} )
+					// 					...those out.
+					.filter( ( filePath ) => filePath );
+
+				const streams = dirs.map( ( dirPath ) => {
+					const glob = path.join( dirPath, 'src', '**', '*.js' );
+					// Use parent as a base so we get paths starting with 'ckeditor5-*/src/*' in the stream.
+					const baseDir = path.parse( dirPath ).dir;
+					const opts = { base: baseDir };
+
+					return gulp.src( glob, opts )
+						.pipe( watch ? utils.watch( glob, opts ) : utils.noop() );
+				} );
+
+				return merge.apply( null, streams )
+					.pipe( utils.unpackModules() );
 			}
 		}
 	};

+ 44 - 25
dev/tasks/gulp/utils.js

@@ -10,30 +10,31 @@ const gulpWatch = require( 'gulp-watch' );
 const gulpPlumber = require( 'gulp-plumber' );
 const gutil = require( 'gulp-util' );
 const multipipe = require( 'multipipe' );
-
-const sep = path.sep;
+const PassThrough = require( 'stream' ).PassThrough;
 
 const utils = {
 	/**
-	 * Returns a stream of files matching the given glob pattern.
+	 * Creates a pass-through stream.
 	 *
-	 * @param {String} root The root directory.
-	 * @param {String} glob The glob pattern.
-	 * @param {Boolean} [watch] Whether to watch the files.
 	 * @returns {Stream}
 	 */
-	src( root, glob, watch ) {
-		const srcDir = path.join( root, glob );
-		let stream = gulp.src( srcDir );
-
-		if ( watch ) {
-			stream = stream
-				// Let's use plumber only when watching. In other cases we should fail quickly and loudly.
-				.pipe( gulpPlumber() )
-				.pipe( gulpWatch( srcDir ) );
-		}
+	noop() {
+		return new PassThrough( { objectMode: true } );
+	},
 
-		return stream;
+	/**
+	 * Transforms a stream of files into a watched stream of files.
+	 *
+	 * @param {String} glob The glob pattern to watch.
+	 * @param {Object} opts Gulp watcher opts.
+	 * @returns {Stream}
+	 */
+	watch( glob, opts ) {
+		return multipipe(
+			// Note: we're using plumber only when watching. In other cases we should fail quickly and loudly.
+			gulpPlumber(),
+			gulpWatch( glob, opts )
+		);
 	},
 
 	/**
@@ -67,7 +68,16 @@ const utils = {
 		}
 
 		return babel( {
-			plugins: [ `transform-es2015-modules-${ babelModuleTranspiler }` ],
+			plugins: [
+				// Note: When plugin is specified by its name, Babel loads it from a context of a
+				// currently transpiled file (in our case - e.g. from ckeditor5-core/src/foo.js).
+				// Obviously that fails, since we have all the plugins installed only in ckeditor5/
+				// and we want to have them only there to avoid installing them dozens of times.
+				//
+				// Anyway, I haven't found in the docs that you can also pass a plugin instance here,
+				// but it works... so let's hope it will.
+				require( `babel-plugin-transform-es2015-modules-${ babelModuleTranspiler }` )
+			],
 			// Ensure that all paths ends with '.js' because Require.JS (unlike Common.JS/System.JS)
 			// will not add it to module names which look like paths.
 			resolveModuleSource: ( source ) => {
@@ -125,19 +135,28 @@ const utils = {
 	},
 
 	/**
-	 * Moves files out of `node_modules/ckeditor5-xxx/src/*` directories to `ckeditor5-xxx/*`.
+	 * Moves files out of `ckeditor5-xxx/src/*` directories to `ckeditor5-xxx/*`.
 	 *
-	 * @param {RegExp} modulePathPattern
 	 * @returns {Stream}
 	 */
-	unpackModules( modulePathPattern ) {
+	unpackModules() {
 		return rename( ( file ) => {
-			file.dirname = file.dirname.replace( modulePathPattern, `${ sep }$1${ sep }` );
+			const dir = file.dirname.split( path.sep );
+
+			// Validate the input for the clear conscious.
+
+			if ( dir[ 0 ].indexOf( 'ckeditor5-' ) !== 0 ) {
+				throw new Error( 'Path should start with "ckeditor5-".' );
+			}
 
-			// Remove now empty src/ dirs.
-			if ( !file.extname && file.basename == 'src' ) {
-				file.basename = '';
+			if ( dir[ 1 ] != 'src' ) {
+				throw new Error( 'Path should start with "ckeditor5-*/src".' );
 			}
+
+			// Remove 'src'.
+			dir.splice( 1, 1 );
+
+			file.dirname = path.join.apply( null, dir );
 		} );
 	},