glitch-canvas
Version:
JavaScript library for applying a glitch effect to a canvas element
353 lines (285 loc) • 9.38 kB
JavaScript
var fs = require( 'fs' );
var { rollup } = require( 'rollup' );
var buble = require( '@rollup/plugin-buble' );
var UglifyJS = require( 'uglify-js' );
var UglifyES = require( 'uglify-es' );
var replace = require( 'rollup-plugin-replace' );
var commonjs = require( 'rollup-plugin-commonjs' );
var nodeResolve = require( 'rollup-plugin-node-resolve' );
var program = require( 'commander' );
var version = require('./package.json').version;
program
.version( version )
.option('-b, --browser', 'generate browser version (node is default)' )
.option('-e, --es6', 'export es6 code' )
.option('-m, --minify', 'minify output' )
.option('-u, --umd', 'export UMD Bundle (optional for es6 builds)' )
.option('-p, --polyfill', 'add polyfills (for Promise and Object.assign)' )
.parse( process.argv );
var env = program.browser ? 'browser' : 'node';
var isBrowser = env === 'browser';
var es5Build = ! program.es6 || env === 'node';
var minifyBuild = !! program.minify;
var bundleUMD = !! program.umd;
var polyfill = !! program.polyfill;
var globalPath = 'src/';
var buildPaths = [ 'dist/' ];
var minifyExtension = 'min';
var es6Extension = 'es6';
var umdExtension = 'umd';
var moduleName = 'glitch';
var fileName = 'glitch-canvas-{APPENDIX}';
var mainFilePath = isBrowser ? 'browser.js' : 'index.js';
if ( isBrowser ) {
buildPaths.push( 'glitch-canvas-browser/' );
}
var stringsToReplace = {
browser: {
"import Canvas from './node-canvas.js'": "import Canvas from './browser.js'",
"// PROMISE_POLYFILL_HERE": ''
},
node: {
"import Canvas from 'canvas'": "var Canvas = require( 'canvas' );"
}
};
if ( polyfill ) {
stringsToReplace.browser[ 'const objectAssign = Object.assign' ] = "import objectAssign from 'object-assign'";
var promisePolyfillStr = `
import p from 'es6-promise';
p.polyfill();
`;
stringsToReplace.browser[ '// PROMISE_POLYFILL_HERE' ] = promisePolyfillStr;
}
console.log( 'building with options: env:', env, 'es6:', ! es5Build, 'minify:', minifyBuild, 'umd', bundleUMD );
buildPaths.forEach( buildPath => {
createES6Bundle( globalPath + mainFilePath, buildPath )
.then( fileContent => {
console.log( 'build complete. file saved to ' + buildPath + getOutputFileName( mainFilePath ) );
} );
} );
function createES6Bundle ( filePath, buildPath ) {
const format = ( es5Build || bundleUMD ) ? 'umd' : 'es';
return processES6File( filePath, format, moduleName )
.then( fileContent => {
return processFileContent( fileContent );
} )
.then( fileContent => {
return saveFile( buildPath + getOutputFileName( mainFilePath ), fileContent );
} );
}
function processES6File ( filePath, format = 'es', moduleName ) {
const rollupPlugins = [ ];
if ( stringsToReplace[env] && Object.keys( stringsToReplace[env] ).length ) {
const replaceOptions = {
values: stringsToReplace[env],
delimiters: [ '', '' ]
};
rollupPlugins.push( replace( replaceOptions ) );
}
rollupPlugins.push(
nodeResolve(),
commonjs()
);
if ( es5Build ) {
rollupPlugins.push( buble() );
}
const rollupOptions = {
input: filePath,
plugins: rollupPlugins
};
return rollup( rollupOptions )
.then( bundle => {
const bundleOpts = { format };
if ( moduleName ) {
bundleOpts.name = moduleName;
}
return bundle.generate( bundleOpts )
.then( bundleData => {
return bundleData.output[0].code;
} );
} );
}
function processFileContent ( fileContent ) {
return replaceImportedScripts ( fileContent )
.then( fileContent => {
return isBrowser ? workersToBlobURL( fileContent ) : workersToWorkerFunction( fileContent );
} )
.then( fileContent => {
if ( minifyBuild ) {
return compressFileContent( fileContent );
} else {
return fileContent;
}
} );
}
function loadFile ( path ) {
return new Promise( function ( resolve, reject ) {
fs.readFile( path, 'utf8', ( err, data ) => {
if ( err ) {
reject( err );
} else {
resolve( data );
}
} );
} );
}
function saveFile ( filePath, fileContent ) {
return new Promise( function ( resolve, reject ) {
fs.writeFile( filePath, fileContent, 'utf8', ( err, res ) => {
if ( err ) {
reject( err );
} else {
resolve( fileContent );
}
} );
} );
}
function compressFileContent ( fileContent ) {
let res;
if ( es5Build ) {
res = UglifyJS.minify( fileContent );
} else {
res = UglifyES.minify( fileContent );
}
if ( res.error ) {
console.log( res.error );
}
return res.code;
}
function replaceImportedScripts ( fileContent ) {
let scriptPaths = getImportedScriptPaths( fileContent );
let loadScripts = scriptPaths.map( ( scriptPath ) => {
return loadFile( globalPath + scriptPath );
} );
return Promise.all( loadScripts )
.then( ( scriptContents, scriptIndex ) => {
return scriptContents.reduce( ( fileContent, scriptContent, scriptContentIndex ) => {
return replaceImportedScript ( fileContent, scriptPaths[scriptContentIndex], scriptContent );
}, fileContent );
} );
}
function workersToBlobURL ( fileContent ) {
const workerPaths = getWorkerPaths( fileContent );
return Promise.all( workerPaths.map( ( workerPath ) => {
const p = workerPath.indexOf( globalPath ) === 0 ? workerPath : globalPath + workerPath;
return processES6File( p );
// return processWorkerFile( p );
} ) )
.then( ( workerContents ) => {
return workerContents.map( ( workerContent, index ) => {
return fileToBlobURL( workerContent );
} );
} )
.then( ( blobURLs ) => {
return blobURLs.reduce( ( fileContent, blobUrl, workerIndex ) => {
let sanitizedScriptPath = sanitizePathForRegEx( workerPaths[workerIndex] );
let pattern = "[\'\"]" + sanitizedScriptPath + '[\'\"]';
let regex = new RegExp( pattern, 'mig' );
return fileContent.replace( regex, blobUrl );
}, fileContent );
} );
}
function workersToWorkerFunction ( fileContent ) {
const workerPaths = getWorkerPaths( fileContent );
return Promise.all( workerPaths.map( ( workerPath ) => {
const p = workerPath.indexOf( globalPath ) === 0 ? workerPath : globalPath + workerPath;
return processES6File( p );
} ) )
.then( ( workerContents ) => {
return workerContents.map( ( workerContent, index ) => {
return fileToWorkerFunction( workerContent );
} );
} )
.then( ( workerFunctionStrings ) => {
return workerFunctionStrings.reduce( ( fileContent, workerFunctionString, workerIndex ) => {
let sanitizedScriptPath = sanitizePathForRegEx( workerPaths[workerIndex] );
let pattern = "[\'\"]" + sanitizedScriptPath + '[\'\"]';
let regex = new RegExp( pattern, 'mig' );
return fileContent.replace( regex, workerFunctionString );
}, fileContent );
} );
}
function fileToBlobURL ( fileContent, type = 'text/javascript' ) {
if ( minifyBuild ) {
fileContent = compressFileContent( fileContent );
}
const fileContentStr = JSON.stringify( fileContent );
return "URL.createObjectURL(new Blob([" + fileContentStr + "],{type:'" + type + "'}))";
}
function fileToWorkerFunction ( fileContent ) {
if ( minifyBuild ) {
fileContent = compressFileContent( fileContent );
}
return "function(){\n" + fileContent + "\n}";
}
function getWorkerPaths ( fileContent ) {
// let regex = /[\'\”](.*Worker.js)[\'\”]/mig;
const regex = /new Worker\s?\(\s?([\"\'][a-zA-Z0-9\/.+=-]+)[\"\']\s?\)/g;
let matches;
let result = [ ];
while ( ( matches = regex.exec( fileContent ) ) !== null ) {
// This is necessary to avoid infinite loops with zero-width matches
if ( matches.index === regex.lastIndex ) {
regex.lastIndex++;
}
// The result can be accessed through the `m`-variable.
matches.forEach( ( match, groupIndex ) => {
if ( groupIndex === 1 ) {
result.push( match );
}
} );
}
return result.map( path => {
return path.replace( /[\'\"]/g, '' );
});
}
function getImportedScriptPaths ( fileContent ) {
// let regex = /(importScripts|\(|"|')([a-zA-Z0-9\/\_\-]*\.js)/mig;
let regex = /importScripts\([\"\'\s]+([a-zA-Z0-9\/\_\-]*\.js)[\"\'\s]+\)/mig;
let matches;
let result = [ ];
while ( ( matches = regex.exec( fileContent ) ) !== null ) {
// This is necessary to avoid infinite loops with zero-width matches
if ( matches.index === regex.lastIndex ) {
regex.lastIndex++;
}
// The result can be accessed through the `m`-variable.
matches.forEach( ( match, groupIndex ) => {
if ( groupIndex === 1 ) {
result.push( match );
}
} );
}
return result;
}
function replaceImportedScript ( fileContent, scriptPath, scriptContent ) {
let sanitizedScriptPath = sanitizePathForRegEx( scriptPath );
let pattern = 'importScripts.*' + sanitizedScriptPath + '*.*\;';
let regex = new RegExp( pattern, 'mig' );
let res = fileContent.replace( regex, scriptContent );
return res;
}
function sanitizePathForRegEx ( path ) {
return path
.replace( /\//g, '\\/' )
.replace( /\-/g, '\\-' )
.replace( /\(/g, '\\(' )
.replace( /\"/g, '\\"' )
.replace( /\./g, '\\.' );
}
function getOutputFileName ( filePath ) {
let appendix = env;
if ( polyfill ) {
appendix += '-with-polyfills';
}
if ( ! es5Build && es6Extension && es6Extension.length ) {
appendix += '.' + es6Extension;
}
if ( bundleUMD && ! es5Build ) {
appendix += '.' + umdExtension;
}
if ( minifyBuild ) {
appendix += '.' + minifyExtension;
}
return fileName.replace( '{APPENDIX}', appendix ) + '.js';
}