/** * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md. */ 'use strict'; /** * A CKEditor 5 release builder. * * @class Builder */ class Builder { constructor( target ) { /** * The target directory where to create the build. * * **Warning**: if existing, this directory will be deleted before processing. * * @property {String} */ this.target = target || 'build'; /** * The temporary directory to use for build processing. * * **Warning**: if existing, this directory will be deleted before processing. * * @property {String} */ this.tmp = 'build-tmp'; /** * The list of tasks to be executed by the `build()` method. Each entry is an Array containing the name of the * method inside `tasks` to execute and the message to show to the end user when executing it. * * @property {Array} */ this.taskList = [ [ 'cleanUp', 'Cleaning the "' + this.target + '" directory...' ], [ 'copyToTmp', 'Copying source files for manipulation...' ], [ 'removeAmdNamespace', 'AMD cleanup...' ], [ 'optimize', 'Creating the optimized code...' ], [ 'cleanUpTmp', 'Removing the "' + this.tmp + '" directory...' ] ]; } /** * Builds a CKEditor release based on the current development code. * * @param {Function} [callback] Function to be called when build finishes. It receives `false` on error. */ build( callback ) { const that = this; let stepCounter = 0; // Before starting, run the initial checkups. if ( !this.checkUp() ) { console.error( 'Build operation aborted.' ); callback( false ); return; } console.log( 'Building CKEditor into the "' + this.target + '" directory:' ); runNext(); function runNext() { const next = that.taskList.shift(); if ( next ) { stepCounter++; console.log( stepCounter + '. ' + next[ 1 ] ); Builder.tasks[ next[ 0 ] ].call( that, runNext ); } else { if ( callback ) { callback(); } } } } checkUp() { const fs = require( 'fs' ); // Stop if the tmp folder already exists. if ( fs.existsSync( this.tmp ) ) { console.error( 'The "' + this.tmp + '" directory already exists. Delete it and try again.' ); return false; } return true; } } /** * Holds individual methods for each task executed by the builder. * * All methods here MUST be called in the builder context by using * `builder.tasks.someMethod.call( builder, callback )`. */ Builder.tasks = { /** * Deletes the `target` and `tmp` directories. * * @param {Function} callback Function to be called when the task is done. * @returns {Object} The callback returned value. */ cleanUp( callback ) { const del = require( 'del' ); del.sync( this.target ); del.sync( this.tmp ); return callback(); }, /** * Copy the local source code of CKEditor and its dependencies to the `tmp` directory for processing. * * @param {Function} callback Function to be called when the task is done. * @returns {Object} The callback returned value. */ copyToTmp( callback ) { const ncp = require( 'ncp' ).ncp; const path = require( 'path' ); const fs = require( 'fs' ); const tmp = this.tmp; const deps = require( '../../../package.json' ).dependencies; const toCopy = Object.keys( deps ).filter( function( name ) { return name.indexOf( 'ckeditor5-' ) === 0; } ); fs.mkdirSync( tmp ); function copy() { const module = toCopy.shift(); if ( !module ) { return callback(); } const dest = path.join( tmp + '/', module ); if ( !fs.existsSync( dest ) ) { fs.mkdirSync( dest ); } // Copy the "src" directory only. ncp( path.join( 'node_modules', module, 'src' ), path.join( dest, 'src' ), { dereference: true }, function( err ) { if ( err ) { throw err; } copy(); } ); } copy(); }, /** * Removes the `CKEDITOR` namespace from AMD calls in the `tmp` copy of the source code. * * @param {Function} callback Function to be called when the task is done. * @returns {Object} The callback returned value. */ removeAmdNamespace( callback ) { const replace = require( 'replace' ); replace( { regex: /^\s*CKEDITOR\.(define|require)/mg, replacement: '$1', paths: [ this.tmp ], recursive: true, silent: true } ); callback(); }, /** * Creates the optimized release version of `ckeditor.js` in the `target` directory out of the `tmp` copy of the * source code. * * @param {Function} callback Function to be called when the task is done. * @returns {Object} The callback returned value. */ optimize( callback ) { const requirejs = require( 'requirejs' ); const config = { out: this.target + '/ckeditor.js', baseUrl: this.tmp + '/ckeditor5-core/src/', paths: { 'ckeditor': '../../../ckeditor', 'ckeditor-dev': '../../../src/ckeditor-dev', 'ckeditor-core': 'ckeditor' }, include: [ 'ckeditor' ], stubModules: [ 'ckeditor-dev' ], // optimize: 'none', optimize: 'uglify2', preserveLicenseComments: false, wrap: { startFile: [ 'src/build/start.frag', require.resolve( 'almond' ) ], endFile: 'src/build/end.frag' } }; requirejs.optimize( config, callback ); }, /** * Deletes `tmp` directory. * * @param {Function} callback Function to be called when the task is done. * @returns {Object} The callback returned value. */ cleanUpTmp( callback ) { const del = require( 'del' ); del.sync( this.tmp ); return callback(); } }; module.exports = Builder;