diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..bbed0ee --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.idea +replacer.iml +node_modules diff --git a/package.json b/package.json new file mode 100644 index 0000000..da5ce51 --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "replacer", + "version": "1.0.0", + "description": "A command line utility to inject css and js files into an html file or to remove code based on conditions.", + "bin": "./replacer", + "scripts": { + "test": "tape tests/*.js | faucet" + }, + "author": "Danny Moerkerke ", + "license": "ISC", + "dependencies": { + "commander": "^2.9.0", + "faucet": "0.0.1", + "replacestream": "^4.0.0", + "tape": "^4.5.1" + } +} diff --git a/replacer b/replacer new file mode 100755 index 0000000..48ebfda --- /dev/null +++ b/replacer @@ -0,0 +1,103 @@ +#!/usr/bin/env node + +'use strict'; + +const process = require('process'); +const fs = require('fs'); +const path = require('path'); +const replaceStream = require('replacestream'); +const program = require('commander'); + +let cssFiles; +let jsFiles; +let removeCondition; + +const replaceFunc = (replacement) => { + return function() { + return replacement === undefined ? arguments[0] : replacement; + }; +}; + +const handleError = (message) => { + console.error(message); + process.exit(1); +}; + +program + .version('1.0.0') + .option('-i, --input ', 'Input file') + .option('-o, --output ', 'Output file') + .option('-c, --css ', 'css file(s) to inject (file or directory)') + .option('-j, --js ', 'js file(s) to inject (file or directory)') + .option('-r, --remove ', 'Remove condition') + .parse(process.argv); + +if(!program.input) { + handleError('Please specify an input file'); +} +else { + try { + if(fs.lstatSync(program.input).isDirectory()) { + handleError(`'${program.input}' is a directory, please specify an input file`); + } + } + catch(e) { + handleError(`File '${program.input}' not found`); + } +} + +if(!program.output) { + program.output = program.input; +} + +if(program.js) { + const js = program.js; + + try { + if(fs.lstatSync(js).isDirectory()) { + jsFiles = fs.readdirSync(js) + .filter(file => file.substr(-3) === '.js') + .map(file => ``) + .join('\n'); + } + else if(fs.lstatSync(js).isFile()) { + jsFiles = ``; + } + } + catch(e) { + handleError(`File or folder '${js}' not found`); + } +} + +if(program.css) { + const css = program.css; + + try { + if(fs.lstatSync(css).isDirectory()) { + cssFiles = fs.readdirSync(css) + .filter(file => file.substr(-4) === '.css') + .map(file => ``) + .join('\n'); + } + else if(fs.lstatSync(css).isFile()) { + cssFiles = ``; + } + } + catch(e) { + handleError(`File or folder '${css}' not found`); + } +} + +if(program.remove) { + removeCondition = program.remove.split(':').pop(); +} + +const replaceJs = replaceFunc(jsFiles); +const replaceCss = replaceFunc(cssFiles); +const regex = new RegExp(`()([\\s\\S]*?)()`, 'gm'); + +fs.createReadStream(path.join(__dirname, program.input)) + .pipe(replaceStream(/()([\s\S]*?)()/gm, replaceJs)) + .pipe(replaceStream(/()([\s\S]*?)()/gm, replaceCss)) + .pipe(replaceStream(regex, '')) + .pipe(fs.createWriteStream(path.join(__dirname, program.output))); \ No newline at end of file diff --git a/tests/test.js b/tests/test.js new file mode 100755 index 0000000..6db9c29 --- /dev/null +++ b/tests/test.js @@ -0,0 +1,162 @@ +"use strict"; + +const fs = require('fs'); +const path = require('path'); +const exec = require('child_process').exec; +const test = require('tape'); + +const before = test; +const after = test; +const tmpDir = 'tests/tmp'; +const inputFile = `${tmpDir}/index.html`; +const outputFile = `${tmpDir}/output.html`; +const cssFiles = ['styles1.css', 'styles2.css', 'styles3.css']; +const jsFiles = ['script1.js', 'script2.js', 'script3.js']; + +const setup = () => { + + try { + fs.lstatSync(tmpDir); + } + catch(e) { + fs.mkdirSync(tmpDir); + } + + fs.writeFileSync(`${inputFile}`, ` + + + + + Replacer + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + `); + + cssFiles.forEach((file) => { + fs.writeFileSync(`${tmpDir}/${file}`, ` + body { + padding: 0; + margin: 10px; + } + `); + }); + + jsFiles.forEach((file) => { + fs.writeFileSync(`${tmpDir}/${file}`, ` + console.log('${file}'); + `); + }); +}; + +before('test setup', (t) => { + setup(); + t.end(); +}); + +test('test injection of all stylesheets in directory', (t) => { + exec(`./replacer -i ${inputFile} -o ${outputFile} -c ${tmpDir}`, (err) => { + + fs.readFile(`${outputFile}`, (err, data) => { + cssFiles.forEach((file) => { + t.equal(true, data.indexOf(`${tmpDir}/${file}`) !== -1, `expect ${file} to be injected`); + }); + + t.end(); + }); + }); +}); + +test('test injection of all javascripts in directory', (t) => { + exec(`./replacer -i ${inputFile} -o ${outputFile} -j ${tmpDir}`, (err) => { + + fs.readFile(`${outputFile}`, (err, data) => { + jsFiles.forEach((file) => { + t.equal(true, data.indexOf(`${tmpDir}/${file}`) !== -1, `expect ${file} to be injected`); + }); + + t.end(); + }); + }); +}); + +test('test injection of single stylesheet', (t) => { + exec(`./replacer -i ${inputFile} -o ${outputFile} -c ${tmpDir}/${cssFiles[0]}`, (err) => { + + fs.readFile(`${outputFile}`, (err, data) => { + t.equal(true, data.indexOf(`${tmpDir}/${cssFiles[0]}`) !== -1, `expect ${cssFiles[0]} to be injected`); + + t.end(); + }); + }); +}); + +test('test injection of single javascript', (t) => { + exec(`./replacer -i ${inputFile} -o ${outputFile} -j ${tmpDir}/${jsFiles[0]}`, (err) => { + + fs.readFile(`${outputFile}`, (err, data) => { + t.equal(true, data.indexOf(`${tmpDir}/${jsFiles[0]}`) !== -1, `expect ${jsFiles[0]} to be injected`); + + t.end(); + }); + }); +}); + +test('test removal of development code', (t) => { + exec(`./replacer -i ${inputFile} -o ${outputFile} -r development`, (err) => { + const devRegex = new RegExp('()([\\s\\S]*?)()'); + const prodRegex = new RegExp('()([\\s\\S]*?)()'); + + fs.readFile(`${outputFile}`, (err, data) => { + t.equal(false, devRegex.test(data), `expect development code to be removed`); + t.equal(true, prodRegex.test(data), `expect production code to not be removed`); + + t.end(); + }); + }); +}); + +test('test removal of production code', (t) => { + exec(`./replacer -i ${inputFile} -o ${outputFile} -r production`, (err) => { + const prodRegex = new RegExp('()([\\s\\S]*?)()'); + const devRegex = new RegExp('()([\\s\\S]*?)()'); + + fs.readFile(`${outputFile}`, (err, data) => { + t.equal(false, prodRegex.test(data), `expect production code to be removed`); + t.equal(true, devRegex.test(data), `expect development code to not be removed`); + + t.end(); + }); + }); +}); + +after('test cleanup', (t) => { + exec(`rm -rf ${tmpDir}`, () => { + t.end(); + }); +}); \ No newline at end of file