Procházet zdrojové kódy

Added local path handling to dev-install task.

Szymon Kupś před 10 roky
rodič
revize
03fbbfbc3d
3 změnil soubory, kde provedl 179 přidání a 78 odebrání
  1. 33 10
      dev/tasks/utils/dev-install.js
  2. 37 0
      dev/tasks/utils/tools.js
  3. 109 68
      dev/tests/dev-install.js

+ 33 - 10
dev/tasks/utils/dev-install.js

@@ -11,12 +11,12 @@ const path = require( 'path' );
 
 /**
  * This tasks install specified module in development mode. It can be executed by typing:
- * 		grunt dev-install --plugin <npm_name|git_hub_url>
+ * 		grunt dev-install --plugin <git_hub_url|npm_name|path_on_disk>
  *
- * It performs follwing steps:
- * 1. Get GitHub URL from NPM if module name is provided.
- * 2. Checks if repository is cloned already. If not - clones it.
- * 3. Checks out plugin repository to provided branch (`master` if no branch is specified).
+ * It performs following steps:
+ * 1. If GitHub URL is provided - clones the repository.
+ * 2. If NPM module name is provided - gets GitHub URL from NPM and clones the repository.
+ * 3. If path on disk is provided - it is used directly.
  * 4. Links plugin directory into `ckeditor5/node_modules/`.
  * 5. Adds dependency with local path to `ckeditor5/package.json`.
  * 6. Runs `npm install` in `ckeditor5/`.
@@ -27,12 +27,32 @@ const path = require( 'path' );
  * @param {Function} writeln Function used to report progress to the console.
  */
 module.exports = ( ckeditor5Path, workspaceRoot, name, writeln ) => {
-	let urlInfo = git.parseRepositoryUrl( name );
 	const workspaceAbsolutePath = path.join( ckeditor5Path, workspaceRoot );
 	let repositoryPath;
+	let urlInfo;
 
+	// First check if name is local path to repository.
+	repositoryPath = path.isAbsolute( name ) ? name : path.resolve( name );
+
+	if ( tools.isDirectory( repositoryPath ) ) {
+		const packageName = tools.readPackageName( repositoryPath );
+
+		if ( packageName ) {
+			writeln( `Plugin located at ${ repositoryPath }.` );
+			urlInfo = {
+				name: packageName
+			};
+		}
+	}
+
+	// Check if name is repository URL.
 	if ( !urlInfo ) {
-		writeln( `Not a GitHub URL. Trying to get GitHub URL from npm package...` );
+		urlInfo = git.parseRepositoryUrl( name );
+	}
+
+	// Check if name is NPM package.
+	if ( !urlInfo ) {
+		writeln( `Not a GitHub URL. Trying to get GitHub URL from NPM package...` );
 		const url = tools.getGitUrlFromNpm( name );
 
 		if ( url ) {
@@ -46,12 +66,15 @@ module.exports = ( ckeditor5Path, workspaceRoot, name, writeln ) => {
 		if ( tools.isDirectory( repositoryPath ) ) {
 			writeln( `Directory ${ repositoryPath } already exists.` );
 		} else {
-			writeln( `Cloning ${ urlInfo.name } into ${ repositoryPath }... ` );
+			writeln( `Cloning ${ urlInfo.name } into ${ repositoryPath }...` );
 			git.cloneRepository( urlInfo, workspaceAbsolutePath );
 		}
 
-		writeln( `Checking ${ urlInfo.name } to ${ urlInfo.branch }...` );
-		git.checkout( repositoryPath, urlInfo.branch );
+		// Checkout to specified branch if one is provided.
+		if ( urlInfo.branch ) {
+			writeln( `Checking ${ urlInfo.name } to ${ urlInfo.branch }...` );
+			git.checkout( repositoryPath, urlInfo.branch );
+		}
 
 		const linkPath = path.join( ckeditor5Path, 'node_modules', urlInfo.name );
 		writeln( `Linking ${ linkPath } to ${ repositoryPath }...` );

+ 37 - 0
dev/tasks/utils/tools.js

@@ -198,6 +198,7 @@ module.exports = {
 
 	/**
 	 * Returns true if path points to existing directory.
+	 *
 	 * @param {String} path
 	 * @returns {Boolean}
 	 */
@@ -211,6 +212,22 @@ module.exports = {
 		return false;
 	},
 
+	/**
+	 * Returns true if path points to existing file.
+	 *
+	 * @param {String} path
+	 * @returns {Boolean}
+	 */
+	isFile( path ) {
+		const fs = require( 'fs' );
+
+		try {
+			return fs.statSync( path ).isFile();
+		} catch ( e ) {}
+
+		return false;
+	},
+
 	/**
 	 * Returns all directories under specified path that match 'ckeditor5' pattern.
 	 *
@@ -239,6 +256,26 @@ module.exports = {
 		fs.writeFileSync( path, JSON.stringify( json, null, 2 ), 'utf-8' );
 	},
 
+	/**
+	 * Returns name of the NPM module located under provided path.
+	 *
+	 * @param {String} modulePath Path to NPM module.
+     */
+	readPackageName( modulePath ) {
+		const fs = require( 'fs' );
+		const path = require( 'path' );
+		const packageJSONPath = path.join( modulePath, 'package.json' );
+
+		if ( !this.isFile( packageJSONPath ) ) {
+			return null;
+		}
+
+		const contents = fs.readFileSync( packageJSONPath, 'utf-8' );
+		const json = JSON.parse( contents );
+
+		return json.name || null;
+	},
+
 	/**
 	 * Calls `npm install` command in specified path.
 	 *

+ 109 - 68
dev/tests/dev-install.js

@@ -23,109 +23,150 @@ describe( 'dev-install', () => {
 	const workspaceAbsolutePath = path.join( ckeditor5Path, workspacePath );
 
 	let toRestore;
-	beforeEach( () => toRestore = [] );
+	const spies = {};
+
+	beforeEach( () => {
+		toRestore = [];
+
+		spies.parseUrl = sinon.spy( git, 'parseRepositoryUrl' );
+		spies.isDirectory = sinon.stub( tools, 'isDirectory' );
+		spies.cloneRepository = sinon.stub( git, 'cloneRepository' );
+		spies.linkDirectories = sinon.stub( tools, 'linkDirectories' );
+		spies.updateJSON = sinon.stub( tools, 'updateJSONFile' );
+		spies.npmInstall = sinon.stub( tools, 'npmInstall' );
+		spies.checkout = sinon.stub( git, 'checkout' );
+		spies.getGitUrlFromNpm = sinon.stub( tools, 'getGitUrlFromNpm' );
+		spies.readPackageName = sinon.stub( tools, 'readPackageName' );
+	} );
 
 	afterEach( () => {
 		toRestore.forEach( item => item.restore() );
+		Object.keys( spies ).forEach( ( spy ) => spies[ spy ].restore() );
 	} );
 
-	it( 'should use GitHub url if provided', () => {
-		const parseUrlSpy = sinon.spy( git, 'parseRepositoryUrl' );
-		const isDirectoryStub = sinon.stub( tools, 'isDirectory' ).returns( false );
-		const cloneRepositoryStub = sinon.stub( git, 'cloneRepository' );
-		const linkDirectoriesStub = sinon.stub( tools, 'linkDirectories' );
-		const updateJSONstub = sinon.stub( tools, 'updateJSONFile' );
-		const npmInstallStub = sinon.stub( tools, 'npmInstall' );
-		const checkoutStub = sinon.stub( git, 'checkout' );
-
-		toRestore.push( parseUrlSpy, isDirectoryStub, cloneRepositoryStub, linkDirectoriesStub, updateJSONstub,
-						npmInstallStub, checkoutStub );
+	it( 'should use GitHub URL', () => {
+		spies.isDirectory.onFirstCall().returns( false );
+		spies.isDirectory.onSecondCall().returns( false );
 
-		installTask( ckeditor5Path, workspacePath, repositoryUrl, () => {}, () => {} );
+		installTask( ckeditor5Path, workspacePath, repositoryUrl, () => {} );
 
-		sinon.assert.calledOnce( parseUrlSpy );
-		sinon.assert.calledWithExactly( parseUrlSpy, repositoryUrl );
+		sinon.assert.calledTwice( spies.isDirectory );
+		sinon.assert.calledOnce( spies.parseUrl );
+		sinon.assert.calledWithExactly( spies.parseUrl, repositoryUrl );
 
-		const urlInfo = parseUrlSpy.firstCall.returnValue;
+		const urlInfo = spies.parseUrl.firstCall.returnValue;
 		const repositoryPath = path.join( workspaceAbsolutePath, urlInfo.name );
 
-		sinon.assert.calledOnce( isDirectoryStub );
-		sinon.assert.calledWithExactly( isDirectoryStub, repositoryPath );
+		sinon.assert.calledWithExactly( spies.isDirectory.secondCall, repositoryPath );
+		sinon.assert.calledOnce( spies.cloneRepository );
+		sinon.assert.calledWithExactly( spies.cloneRepository, urlInfo, workspaceAbsolutePath );
+		sinon.assert.calledOnce( spies.checkout );
+		sinon.assert.calledWithExactly( spies.checkout, repositoryPath, urlInfo.branch );
+
+		const linkPath = path.join( ckeditor5Path, 'node_modules', urlInfo.name );
+
+		sinon.assert.calledOnce( spies.linkDirectories );
+		sinon.assert.calledWithExactly( spies.linkDirectories, repositoryPath, linkPath );
+
+		const packageJsonPath = path.join( ckeditor5Path, 'package.json' );
 
-		sinon.assert.calledOnce( cloneRepositoryStub );
-		sinon.assert.calledWithExactly( cloneRepositoryStub, urlInfo, workspaceAbsolutePath );
+		sinon.assert.calledOnce( spies.updateJSON );
+		expect( spies.updateJSON.firstCall.args[ 0 ] ).to.equal( packageJsonPath );
+		const updateFn = spies.updateJSON.firstCall.args[ 1 ];
+		const json = updateFn( {} );
+		expect( json.dependencies ).to.be.a( 'object' );
+		expect( json.dependencies[ urlInfo.name ] ).to.equal( repositoryPath );
 
-		sinon.assert.calledOnce( checkoutStub );
-		sinon.assert.calledWithExactly( checkoutStub, repositoryPath, urlInfo.branch );
+		sinon.assert.calledOnce( spies.npmInstall );
+		sinon.assert.calledWithExactly( spies.npmInstall, ckeditor5Path );
+	} );
+
+	it( 'should use npm module name', () => {
+		spies.isDirectory.onFirstCall().returns( false );
+		spies.isDirectory.onSecondCall().returns( true );
+		spies.getGitUrlFromNpm.returns( repositoryUrl );
+
+		installTask( ckeditor5Path, workspacePath, moduleName, () => {} );
+
+		sinon.assert.calledTwice( spies.isDirectory );
+		sinon.assert.calledTwice( spies.parseUrl );
+		sinon.assert.calledWithExactly( spies.parseUrl.firstCall, moduleName );
+		expect( spies.parseUrl.firstCall.returnValue ).to.equal( null );
+		sinon.assert.calledOnce( spies.getGitUrlFromNpm );
+		sinon.assert.calledWithExactly( spies.getGitUrlFromNpm, moduleName );
+		sinon.assert.calledWithExactly( spies.parseUrl.secondCall, repositoryUrl );
+		const urlInfo = spies.parseUrl.secondCall.returnValue;
+		const repositoryPath = path.join( workspaceAbsolutePath, urlInfo.name );
+
+		sinon.assert.calledWithExactly( spies.isDirectory.secondCall, repositoryPath );
+		sinon.assert.notCalled( spies.cloneRepository );
+		sinon.assert.calledOnce( spies.checkout );
+		sinon.assert.calledWithExactly( spies.checkout, repositoryPath, urlInfo.branch );
 
 		const linkPath = path.join( ckeditor5Path, 'node_modules', urlInfo.name );
 
-		sinon.assert.calledOnce( linkDirectoriesStub );
-		sinon.assert.calledWithExactly( linkDirectoriesStub, repositoryPath, linkPath );
+		sinon.assert.calledOnce( spies.linkDirectories );
+		sinon.assert.calledWithExactly( spies.linkDirectories, repositoryPath, linkPath );
 
 		const packageJsonPath = path.join( ckeditor5Path, 'package.json' );
-		sinon.assert.calledOnce( updateJSONstub );
-		expect( updateJSONstub.firstCall.args[ 0 ] ).to.equal( packageJsonPath );
-		const updateFn = updateJSONstub.firstCall.args[ 1 ];
+
+		sinon.assert.calledOnce( spies.updateJSON );
+		expect( spies.updateJSON.firstCall.args[ 0 ] ).to.equal( packageJsonPath );
+		const updateFn = spies.updateJSON.firstCall.args[ 1 ];
 		const json = updateFn( {} );
 		expect( json.dependencies ).to.be.a( 'object' );
 		expect( json.dependencies[ urlInfo.name ] ).to.equal( repositoryPath );
 
-		sinon.assert.calledOnce( npmInstallStub );
-		sinon.assert.calledWithExactly( npmInstallStub, ckeditor5Path );
+		sinon.assert.calledOnce( spies.npmInstall );
+		sinon.assert.calledWithExactly( spies.npmInstall, ckeditor5Path );
 	} );
 
-	it( 'should use npm module name if provided', () => {
-		const parseUrlSpy = sinon.spy( git, 'parseRepositoryUrl' );
-		const isDirectoryStub = sinon.stub( tools, 'isDirectory' ).returns( true );
-		const getUrlFromNpmSpy = sinon.stub( tools, 'getGitUrlFromNpm' ).returns( repositoryUrl );
-		const cloneRepositoryStub = sinon.stub( git, 'cloneRepository' );
-		const linkDirectoriesStub = sinon.stub( tools, 'linkDirectories' );
-		const updateJSONstub = sinon.stub( tools, 'updateJSONFile' );
-		const npmInstallStub = sinon.stub( tools, 'npmInstall' );
-		const checkoutStub = sinon.stub( git, 'checkout' );
+	it( 'should use local relative path', () => {
+		spies.isDirectory.onFirstCall().returns( true );
+		spies.isDirectory.onSecondCall().returns( true );
+		spies.readPackageName.returns( moduleName );
 
-		toRestore.push( parseUrlSpy, isDirectoryStub, getUrlFromNpmSpy, cloneRepositoryStub, linkDirectoriesStub,
-						updateJSONstub, npmInstallStub, checkoutStub );
+		installTask( ckeditor5Path, workspacePath, '../ckeditor5-core', () => {} );
 
-		installTask( ckeditor5Path, workspacePath, moduleName, () => {}, () => {} );
+		sinon.assert.calledTwice( spies.isDirectory );
+		sinon.assert.calledOnce( spies.readPackageName );
+	} );
 
-		sinon.assert.calledTwice( parseUrlSpy );
-		sinon.assert.calledWithExactly( parseUrlSpy.firstCall, moduleName );
-		expect( parseUrlSpy.firstCall.returnValue ).to.equal( null );
+	it( 'should use local absolute path if provided', () => {
+		spies.isDirectory.onFirstCall().returns( true );
+		spies.isDirectory.onSecondCall().returns( true );
+		spies.readPackageName.returns( moduleName );
 
-		sinon.assert.calledOnce( getUrlFromNpmSpy );
-		sinon.assert.calledWithExactly( getUrlFromNpmSpy, moduleName );
+		installTask( ckeditor5Path, workspacePath, '/ckeditor5-core', () => {} );
 
-		sinon.assert.calledWithExactly( parseUrlSpy.secondCall, repositoryUrl );
-		const urlInfo = parseUrlSpy.secondCall.returnValue;
-		const repositoryPath = path.join( workspaceAbsolutePath, urlInfo.name );
+		sinon.assert.calledTwice( spies.isDirectory );
+		sinon.assert.calledOnce( spies.readPackageName );
+	} );
 
-		sinon.assert.calledOnce( isDirectoryStub );
-		sinon.assert.calledWithExactly( isDirectoryStub, repositoryPath );
+	it( 'should throw an exception when invalid name is provided', () => {
+		spies.isDirectory.onFirstCall().returns( false );
+		spies.isDirectory.onSecondCall().returns( true );
 
-		sinon.assert.notCalled( cloneRepositoryStub );
+		expect( () => {
+			installTask( ckeditor5Path, workspacePath, moduleName, () => {} );
+		} ).to.throw();
+
+		sinon.assert.calledOnce( spies.parseUrl );
+		sinon.assert.calledWithExactly( spies.parseUrl.firstCall, moduleName );
+		expect( spies.parseUrl.firstCall.returnValue ).to.equal( null );
 	} );
 
-	it( 'should throw an exception when invalid name is provided', () => {
-		const parseUrlSpy = sinon.spy( git, 'parseRepositoryUrl' );
-		const isDirectoryStub = sinon.stub( tools, 'isDirectory' ).returns( true );
-		const getUrlFromNpmSpy = sinon.stub( tools, 'getGitUrlFromNpm' ).returns( null );
-		const cloneRepositoryStub = sinon.stub( git, 'cloneRepository' );
-		const linkDirectoriesStub = sinon.stub( tools, 'linkDirectories' );
-		const updateJSONstub = sinon.stub( tools, 'updateJSONFile' );
-		const npmInstallStub = sinon.stub( tools, 'npmInstall' );
-		const checkoutStub = sinon.stub( git, 'checkout' );
-
-		toRestore.push( parseUrlSpy, isDirectoryStub, getUrlFromNpmSpy, cloneRepositoryStub, linkDirectoriesStub,
-			updateJSONstub, npmInstallStub, checkoutStub );
+	it( 'should throw an exception when package.json is not present', () => {
+		spies.isDirectory.onFirstCall().returns( true );
+		spies.isDirectory.onSecondCall().returns( true );
+		spies.readPackageName.returns( null );
 
 		expect( () => {
-			installTask( ckeditor5Path, workspacePath, moduleName, () => {}, () => {} );
+			installTask( ckeditor5Path, workspacePath, moduleName, () => {} );
 		} ).to.throw();
 
-		sinon.assert.calledOnce( parseUrlSpy );
-		sinon.assert.calledWithExactly( parseUrlSpy.firstCall, moduleName );
-		expect( parseUrlSpy.firstCall.returnValue ).to.equal( null );
+		sinon.assert.calledOnce( spies.parseUrl );
+		sinon.assert.calledWithExactly( spies.parseUrl.firstCall, moduleName );
+		expect( spies.parseUrl.firstCall.returnValue ).to.equal( null );
 	} );
 } );