Skip to content

Commit

Permalink
start
Browse files Browse the repository at this point in the history
  • Loading branch information
Danny Moerkerke committed Mar 29, 2016
0 parents commit 369a323
Show file tree
Hide file tree
Showing 4 changed files with 285 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.idea
replacer.iml
node_modules
17 changes: 17 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>",
"license": "ISC",
"dependencies": {
"commander": "^2.9.0",
"faucet": "0.0.1",
"replacestream": "^4.0.0",
"tape": "^4.5.1"
}
}
103 changes: 103 additions & 0 deletions replacer
Original file line number Diff line number Diff line change
@@ -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>', 'Input file')
.option('-o, --output <output>', 'Output file')
.option('-c, --css <css>', 'css file(s) to inject (file or directory)')
.option('-j, --js <js>', 'js file(s) to inject (file or directory)')
.option('-r, --remove <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 => `<script src="${js}/${file}"></script>`)
.join('\n');
}
else if(fs.lstatSync(js).isFile()) {
jsFiles = `<script src="${js}"></script>`;
}
}
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 => `<link rel="stylesheet" href="${css}/${file}">`)
.join('\n');
}
else if(fs.lstatSync(css).isFile()) {
cssFiles = `<link rel="stylesheet" href="${css}">`;
}
}
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(`(<!\\-\\- remove:${removeCondition} \\-\\->)([\\s\\S]*?)(<!\\-\\- endremove \\-\\->)`, 'gm');

fs.createReadStream(path.join(__dirname, program.input))
.pipe(replaceStream(/(<!\-\- inject:js \-\->)([\s\S]*?)(<!\-\- endinject \-\->)/gm, replaceJs))
.pipe(replaceStream(/(<!\-\- inject:css \-\->)([\s\S]*?)(<!\-\- endinject \-\->)/gm, replaceCss))
.pipe(replaceStream(regex, ''))
.pipe(fs.createWriteStream(path.join(__dirname, program.output)));
162 changes: 162 additions & 0 deletions tests/test.js
Original file line number Diff line number Diff line change
@@ -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}`, `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Replacer</title>
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- inject:css -->
<link rel="stylesheet" href="/client/css/styles.css">
<!-- endinject -->
</head>
<body>
<div></div>
<!-- remove:development -->
<script src="lib/profiler.js"></script>
<!-- endremove -->
<script src="/src/jquery.js"></script>
<!-- inject:js -->
<script src="/client/js/build.js"></script>
<!-- endinject -->
<!-- remove:production -->
<script src="http://localhost:35729/livereload.js?snipver=1"></script>
<!-- endremove -->
</body>
</html>
`);

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('(<!\\-\\- remove:development \\-\\->)([\\s\\S]*?)(<!\\-\\- endremove \\-\\->)');
const prodRegex = new RegExp('(<!\\-\\- remove:production \\-\\->)([\\s\\S]*?)(<!\\-\\- endremove \\-\\->)');

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('(<!\\-\\- remove:production \\-\\->)([\\s\\S]*?)(<!\\-\\- endremove \\-\\->)');
const devRegex = new RegExp('(<!\\-\\- remove:development \\-\\->)([\\s\\S]*?)(<!\\-\\- endremove \\-\\->)');

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();
});
});

0 comments on commit 369a323

Please sign in to comment.