penambahan web socket

This commit is contained in:
2025-09-18 19:01:22 +07:00
parent 1d053646a9
commit d7bb2eb5bb
15070 changed files with 2402916 additions and 0 deletions

View File

@@ -0,0 +1,60 @@
'use strict';
/**
* Dependencies
*/
const parseConfig = require('./parse-config');
/**
* Combine CLI script arguments with config options
*/
module.exports = function combineConfig(config, argv) {
//Extract options from config
let {
from, to, files, ignore, encoding, verbose,
allowEmptyPaths, disableGlobs, isRegex, dry, quiet,
} = config;
//Get from/to parameters from CLI args if not defined in options
if (typeof from === 'undefined') {
from = argv._.shift();
}
if (typeof to === 'undefined') {
to = argv._.shift();
}
//Get files and ignored files
if (typeof files === 'undefined') {
files = argv._;
}
if (typeof ignore === 'undefined' && typeof argv.ignore !== 'undefined') {
ignore = argv.ignore;
}
//Other parameters
if (typeof encoding === 'undefined') {
encoding = argv.encoding;
}
if (typeof disableGlobs === 'undefined') {
disableGlobs = !!argv.disableGlobs;
}
if (typeof isRegex === 'undefined') {
isRegex = !!argv.isRegex;
}
if (typeof verbose === 'undefined') {
verbose = !!argv.verbose;
}
if (typeof dry === 'undefined') {
dry = !!argv.dry;
}
if (typeof quiet === 'undefined') {
quiet = !!argv.quiet;
}
//Return through parser to validate
return parseConfig({
from, to, files, ignore, encoding, verbose,
allowEmptyPaths, disableGlobs, isRegex, dry, quiet,
});
};

View File

@@ -0,0 +1,9 @@
'use strict';
/**
* Error handler
*/
module.exports = function errorHandler(error, exitCode = 1) {
console.error(error);
process.exit(exitCode);
};

View File

@@ -0,0 +1,26 @@
'use strict';
/**
* Dependencies
*/
const globAsync = require('./glob-async');
/**
* Get paths asynchrously
*/
module.exports = function getPathsAsync(patterns, config) {
//Extract relevant config
const {ignore, disableGlobs, allowEmptyPaths, glob: cfg} = config;
//Not using globs?
if (disableGlobs) {
return Promise.resolve(patterns);
}
//Expand globs and flatten paths
return Promise
.all(patterns
.map(pattern => globAsync(pattern, ignore, allowEmptyPaths, cfg)))
.then(paths => [].concat.apply([], paths));
};

View File

@@ -0,0 +1,42 @@
'use strict';
/**
* Dependencies
*/
const glob = require('glob');
/**
* Get paths (sync)
*/
module.exports = function getPathsSync(patterns, config) {
//Extract relevant config
const {ignore, disableGlobs, glob: globConfig, cwd} = config;
//Not using globs?
if (disableGlobs) {
return patterns;
}
//Prepare glob config
const cfg = Object.assign({ignore}, globConfig, {nodir: true});
//Append CWD configuration if given (#56)
//istanbul ignore if
if (cwd) {
cfg.cwd = cwd;
}
//Get paths
const paths = patterns.map(pattern => glob.sync(pattern, cfg));
const flattened = [].concat.apply([], paths);
//Prefix each path with CWD if given (#56)
//istanbul ignore if
if (cwd) {
return flattened.map(path => `${cwd}${path}`);
}
//Return flattened
return flattened;
};

View File

@@ -0,0 +1,34 @@
'use strict';
/**
* Dependencies
*/
const glob = require('glob');
/**
* Async wrapper for glob
*/
module.exports = function globAsync(pattern, ignore, allowEmptyPaths, cfg) {
//Prepare glob config
cfg = Object.assign({ignore}, cfg, {nodir: true});
//Return promise
return new Promise((resolve, reject) => {
glob(pattern, cfg, (error, files) => {
//istanbul ignore if: hard to make glob error
if (error) {
return reject(error);
}
//Error if no files match, unless allowed
if (!allowEmptyPaths && files.length === 0) {
return reject(new Error('No files match the pattern: ' + pattern));
}
//Resolve
resolve(files);
});
});
};

View File

@@ -0,0 +1,23 @@
'use strict';
/**
* Dependencies
*/
const path = require('path');
/**
* Helper to load options from a config file
*/
module.exports = function loadConfig(file) {
//No config file provided?
if (!file) {
return {};
}
//Resolve path
file = path.resolve(file);
//Try to load
return require(file);
};

View File

@@ -0,0 +1,75 @@
'use strict';
/**
* Get replacement helper
*/
function getReplacement(replace, isArray, i) {
if (isArray && typeof replace[i] === 'undefined') {
return null;
}
if (isArray) {
return replace[i];
}
return replace;
}
/**
* Helper to make replacements
*/
module.exports = function makeReplacements(contents, from, to, file, count) {
//Turn into array
if (!Array.isArray(from)) {
from = [from];
}
//Check if replace value is an array and prepare result
const isArray = Array.isArray(to);
const result = {file};
//Counting? Initialize number of matches
if (count) {
result.numMatches = 0;
result.numReplacements = 0;
}
//Make replacements
const newContents = from.reduce((contents, item, i) => {
//Call function if given, passing in the filename
if (typeof item === 'function') {
item = item(file);
}
//Get replacement value
let replacement = getReplacement(to, isArray, i);
if (replacement === null) {
return contents;
}
//Call function if given, appending the filename
if (typeof replacement === 'function') {
const original = replacement;
replacement = (...args) => original(...args, file);
}
//Count matches
if (count) {
const matches = contents.match(item);
if (matches) {
const replacements = matches.filter(match => match !== replacement);
result.numMatches += matches.length;
result.numReplacements += replacements.length;
}
}
//Make replacement
return contents.replace(item, replacement);
}, contents);
//Check if changed
result.hasChanged = (newContents !== contents);
//Return result and new contents
return [result, newContents];
};

View File

@@ -0,0 +1,74 @@
'use strict';
/**
* Defaults
*/
const defaults = {
ignore: [],
encoding: 'utf-8',
disableGlobs: false,
allowEmptyPaths: false,
countMatches: false,
isRegex: false,
verbose: false,
quiet: false,
dry: false,
glob: {},
cwd: null,
};
/**
* Parse config
*/
module.exports = function parseConfig(config) {
//Validate config
if (typeof config !== 'object' || config === null) {
throw new Error('Must specify configuration object');
}
//Fix glob
config.glob = config.glob || {};
//Extract data
const {files, from, to, processor, ignore, encoding} = config;
if (typeof processor !== 'undefined') {
if (typeof processor !== 'function' && !Array.isArray(processor)) {
throw new Error('Processor should be either a function or an array of functions');
}
} else {
//Validate values
if (typeof files === 'undefined') {
throw new Error('Must specify file or files');
}
if (typeof from === 'undefined') {
throw new Error('Must specify string or regex to replace');
}
if (typeof to === 'undefined') {
throw new Error('Must specify a replacement (can be blank string)');
}
}
//Ensure arrays
if (!Array.isArray(files)) {
config.files = [files];
}
if (!Array.isArray(ignore)) {
if (typeof ignore === 'undefined') {
config.ignore = [];
}
else {
config.ignore = [ignore];
}
}
//Use default encoding if invalid
if (typeof encoding !== 'string' || encoding === '') {
config.encoding = 'utf-8';
}
//Merge config with defaults
return Object.assign({}, defaults, config);
};

View File

@@ -0,0 +1,45 @@
'use strict';
/**
* Dependencies
*/
const fs = require('fs');
const runProcessors = require('./run-processors');
/**
* Helper to process in a single file (async)
*/
module.exports = function processAsync(file, processor, config) {
//Extract relevant config
const {encoding, dry} = config;
//Wrap in promise
return new Promise((resolve, reject) => {
fs.readFile(file, encoding, (error, contents) => {
//istanbul ignore if
if (error) {
return reject(error);
}
//Make replacements
const [result, newContents] = runProcessors(
contents, processor, file,
);
//Not changed or dry run?
if (!result.hasChanged || dry) {
return resolve(result);
}
//Write to file
fs.writeFile(file, newContents, encoding, error => {
//istanbul ignore if
if (error) {
return reject(error);
}
resolve(result);
});
});
});
};

View File

@@ -0,0 +1,30 @@
'use strict';
/**
* Dependencies
*/
const fs = require('fs');
const runProcessors = require('./run-processors');
/**
* Helper to process in a single file (sync)
*/
module.exports = function processSync(file, processor, config) {
//Extract relevant config and read file contents
const {encoding, dry} = config;
const contents = fs.readFileSync(file, encoding);
//Process contents and check if anything changed
const [result, newContents] = runProcessors(
contents, processor, file,
);
//Contents changed and not a dry run? Write to file
if (result.hasChanged && !dry) {
fs.writeFileSync(file, newContents, encoding);
}
//Return result
return result;
};

View File

@@ -0,0 +1,45 @@
'use strict';
/**
* Dependencies
*/
const fs = require('fs');
const makeReplacements = require('./make-replacements');
/**
* Helper to replace in a single file (async)
*/
module.exports = function replaceAsync(file, from, to, config) {
//Extract relevant config
const {encoding, dry, countMatches} = config;
//Wrap in promise
return new Promise((resolve, reject) => {
fs.readFile(file, encoding, (error, contents) => {
//istanbul ignore if
if (error) {
return reject(error);
}
//Make replacements
const [result, newContents] = makeReplacements(
contents, from, to, file, countMatches
);
//Not changed or dry run?
if (!result.hasChanged || dry) {
return resolve(result);
}
//Write to file
fs.writeFile(file, newContents, encoding, error => {
//istanbul ignore if
if (error) {
return reject(error);
}
resolve(result);
});
});
});
};

View File

@@ -0,0 +1,30 @@
'use strict';
/**
* Dependencies
*/
const fs = require('fs');
const makeReplacements = require('./make-replacements');
/**
* Helper to replace in a single file (sync)
*/
module.exports = function replaceSync(file, from, to, config) {
//Extract relevant config and read file contents
const {encoding, dry, countMatches} = config;
const contents = fs.readFileSync(file, encoding);
//Replace contents and check if anything changed
const [result, newContents] = makeReplacements(
contents, from, to, file, countMatches
);
//Contents changed and not a dry run? Write to file
if (result.hasChanged && !dry) {
fs.writeFileSync(file, newContents, encoding);
}
//Return result
return result;
};

View File

@@ -0,0 +1,16 @@
/**
* Run processors
*/
module.exports = function runProcessors(contents, processor, file, count) {
const processors = Array.isArray(processor) ? processor : [processor];
const result = {file};
const newContents = processors.reduce((contents, processor, i) => {
return processor(contents);
}, contents);
result.hasChanged = (newContents !== contents);
return [result, newContents];
};

View File

@@ -0,0 +1,23 @@
'use strict';
/**
* Dependencies
*/
const chalk = require('chalk');
/**
* Success handler
*/
module.exports = function successHandler(results, verbose) {
const changed = results.filter(result => result.hasChanged);
const numChanges = changed.length;
if (numChanges > 0) {
console.log(chalk.green(`${numChanges} file(s) were changed`));
if (verbose) {
changed.forEach(result => console.log(chalk.grey('-', result.file)));
}
}
else {
console.log(chalk.yellow('No files were changed'));
}
};

View File

@@ -0,0 +1,83 @@
'use strict';
/**
* Dependencies
*/
const chalk = require('chalk');
const parseConfig = require('./helpers/parse-config');
const getPathsSync = require('./helpers/get-paths-sync');
const getPathsAsync = require('./helpers/get-paths-async');
const processSync = require('./helpers/process-sync');
const processAsync = require('./helpers/process-async');
function processFile(config, cb) {
try {
config = parseConfig(config);
}
catch (error) {
if (typeof cb === 'function') {
return cb(error, null);
}
return Promise.reject(error);
}
const {files, processor, dry, verbose} = config;
//Dry run?
//istanbul ignore if: No need to test console logs
if (dry && verbose) {
console.log(chalk.yellow('Dry run, not making actual changes'));
}
//Find paths
return getPathsAsync(files, config)
.then(paths => Promise.all(
paths.map(file => processAsync(file, processor, config))
))
.then(results => {
if (typeof cb === 'function') {
cb(null, results);
}
return results;
})
.catch(error => {
if (typeof cb === 'function') {
cb(error);
}
else {
throw error;
}
});
}
/**
* Sync API
*/
function processFileSync(config) {
//Parse config
config = parseConfig(config);
//Get config, paths, and initialize changed files
const {files, processor, dry, verbose} = config;
const paths = getPathsSync(files, config);
//Dry run?
//istanbul ignore if: No need to test console logs
if (dry && verbose) {
console.log(chalk.yellow('Dry run, not making actual changes'));
}
//Process synchronously and return results
return paths.map(path => processSync(path, processor, config));
}
// Self-reference to support named import
processFile.processFile = processFile;
processFile.processFileSync = processFileSync;
processFile.sync = processFileSync;
//Export
module.exports = processFile;

View File

@@ -0,0 +1,751 @@
'use strict';
/**
* Dependencies
*/
import transform, {sync, processFile, processFileSync} from './process-file';
const fs = require('fs');
const writeFile = Promise.promisify(fs.writeFile);
const deleteFile = Promise.promisify(fs.unlink);
/**
* Specifications
*/
describe('Process a file', () => {
//Test JSON
const testData = 'a re place c';
/**
* Prepare test files
*/
beforeEach(() => Promise.all([
writeFile('test1', testData, 'utf8'),
writeFile('test2', testData, 'utf8'),
writeFile('test3', 'nope', 'utf8'),
]));
/**
* Clean up test files
*/
afterEach(() => Promise.all([
deleteFile('test1'),
deleteFile('test2'),
deleteFile('test3'),
]));
function fromToToProcessor(config) {
const from = config.from;
const to = config.to;
delete config.from;
delete config.to;
config.processor = (content) => {
return content.replace(from, to);
};
return config;
}
/**
* Async with promises
*/
describe('Async with promises', () => {
it('should run processor', done => {
transform({
files: 'test1',
processor: (input) => {
return input.replace(/re\splace/g, 'b');
},
}).then(() => {
const test1 = fs.readFileSync('test1', 'utf8');
const test2 = fs.readFileSync('test2', 'utf8');
console.log(test1);
expect(test1).to.equal('a b c');
expect(test2).to.equal(testData);
done();
});
});
it('should throw an error when no config provided', () => {
return expect(transform()).to.eventually.be.rejectedWith(Error);
});
it('should throw an error when invalid config provided', () => {
return expect(transform(42)).to.eventually.be.rejectedWith(Error);
});
it('should throw an error when no `files` defined', () => {
return expect(transform(fromToToProcessor({
from: /re\splace/g,
to: 'b',
}))).to.eventually.be.rejectedWith(Error);
});
it('should replace contents in a single file with regex', done => {
transform(fromToToProcessor({
files: 'test1',
from: /re\splace/g,
to: 'b',
})).then(() => {
const test1 = fs.readFileSync('test1', 'utf8');
const test2 = fs.readFileSync('test2', 'utf8');
expect(test1).to.equal('a b c');
expect(test2).to.equal(testData);
done();
});
});
it('should replace contents with a string replacement', done => {
transform(fromToToProcessor({
files: 'test1',
from: 're place',
to: 'b',
})).then(() => {
const test1 = fs.readFileSync('test1', 'utf8');
expect(test1).to.equal('a b c');
done();
});
});
it('should replace contents in a an array of files', done => {
transform(fromToToProcessor({
files: ['test1', 'test2'],
from: /re\splace/g,
to: 'b',
})).then(() => {
const test1 = fs.readFileSync('test1', 'utf8');
const test2 = fs.readFileSync('test2', 'utf8');
expect(test1).to.equal('a b c');
expect(test2).to.equal('a b c');
done();
});
});
it('should expand globs', done => {
transform(fromToToProcessor({
files: 'test*',
from: /re\splace/g,
to: 'b',
})).then(() => {
const test1 = fs.readFileSync('test1', 'utf8');
const test2 = fs.readFileSync('test2', 'utf8');
expect(test1).to.equal('a b c');
expect(test2).to.equal('a b c');
done();
});
});
it('should expand globs while excluding ignored files', done => {
transform(fromToToProcessor({
files: 'test*',
ignore: 'test1',
from: /re\splace/g,
to: 'b',
})).then(() => {
const test1 = fs.readFileSync('test1', 'utf8');
const test2 = fs.readFileSync('test2', 'utf8');
expect(test1).to.equal('a re place c');
expect(test2).to.equal('a b c');
done();
});
});
it('should replace substrings', done => {
transform(fromToToProcessor({
files: 'test1',
from: /(re)\s(place)/g,
to: '$2 $1',
})).then(() => {
const test1 = fs.readFileSync('test1', 'utf8');
expect(test1).to.equal('a place re c');
done();
});
});
it('should fulfill the promise on success', () => {
return transform(fromToToProcessor({
files: 'test1',
from: /re\splace/g,
to: 'b',
})).should.be.fulfilled;
});
it('should reject the promise with an error on failure', () => {
return expect(transform(fromToToProcessor({
files: 'nope',
from: /re\splace/g,
to: 'b',
}))).to.eventually.be.rejectedWith(Error);
});
it('should not reject the promise if allowEmptyPaths is true', () => {
return transform(fromToToProcessor({
files: 'nope',
allowEmptyPaths: true,
from: /re\splace/g,
to: 'b',
})).should.be.fulfilled;
});
it('should return a results array', done => {
transform(fromToToProcessor({
files: 'test1',
from: /re\splace/g,
to: 'b',
})).then(results => {
expect(results).to.be.instanceof(Array);
expect(results).to.have.length(1);
expect(results[0].file).to.equal('test1');
done();
});
});
it('should mark if something was replaced', done => {
transform(fromToToProcessor({
files: 'test1',
from: /re\splace/g,
to: 'b',
})).then(results => {
expect(results[0].hasChanged).to.equal(true);
done();
});
});
it('should not mark if nothing was replaced', done => {
transform(fromToToProcessor({
files: 'test1',
from: 'nope',
to: 'b',
})).then(results => {
expect(results[0].hasChanged).to.equal(false);
done();
});
});
it('should return correct results for multiple files', done => {
transform(fromToToProcessor({
files: ['test1', 'test2', 'test3'],
from: /re\splace/g,
to: 'b',
})).then(results => {
expect(results).to.have.length(3);
expect(results[0].file).to.equal('test1');
expect(results[0].hasChanged).to.equal(true);
expect(results[1].file).to.equal('test2');
expect(results[1].hasChanged).to.equal(true);
expect(results[2].file).to.equal('test3');
expect(results[2].hasChanged).to.equal(false);
done();
});
});
it('should not replace in a dry run', done => {
transform(fromToToProcessor({
files: ['test1', 'test2'],
from: /re\splace/g,
to: 'b',
dry: true,
})).then(() => {
const test1 = fs.readFileSync('test1', 'utf8');
const test2 = fs.readFileSync('test2', 'utf8');
expect(test1).to.equal('a re place c');
expect(test2).to.equal('a re place c');
done();
});
});
it('should return changed files for a dry run', done => {
transform(fromToToProcessor({
files: ['test1', 'test2', 'test3'],
from: /re\splace/g,
to: 'b',
dry: true,
})).then(results => {
expect(results).to.have.length(3);
expect(results[0].file).to.equal('test1');
expect(results[0].hasChanged).to.equal(true);
expect(results[1].file).to.equal('test2');
expect(results[1].hasChanged).to.equal(true);
expect(results[2].file).to.equal('test3');
expect(results[2].hasChanged).to.equal(false);
done();
});
});
it('should accept glob configuration', done => {
transform(fromToToProcessor({
files: 'test1',
from: /re\splace/g,
to: 'b',
allowEmptyPaths: true,
glob: {
ignore: ['test1'],
},
})).then(() => {
const test1 = fs.readFileSync('test1', 'utf8');
expect(test1).to.equal('a re place c');
done();
});
});
it('should ignore empty glob configuration', done => {
transform(fromToToProcessor({
files: 'test1',
from: /re\splace/g,
to: 'b',
glob: null,
})).then(() => {
const test1 = fs.readFileSync('test1', 'utf8');
expect(test1).to.equal('a b c');
done();
});
});
});
/**
* Async with callback
*/
describe('Async with callback', () => {
it('should throw an error when no config provided', done => {
transform(null, (error) => {
expect(error).to.be.instanceof(Error);
done();
});
});
it('should throw an error when invalid config provided', done => {
transform(42, (error) => {
expect(error).to.be.instanceof(Error);
done();
});
});
it('should throw an error when no `files` defined', done => {
transform(fromToToProcessor({
from: /re\splace/g,
to: 'b',
}), (error) => {
expect(error).to.be.instanceof(Error);
done();
});
});
it('should replace contents in a single file with regex', done => {
transform(fromToToProcessor({
files: 'test1',
from: /re\splace/g,
to: 'b',
}), () => {
const test1 = fs.readFileSync('test1', 'utf8');
const test2 = fs.readFileSync('test2', 'utf8');
expect(test1).to.equal('a b c');
expect(test2).to.equal(testData);
done();
});
});
it('should replace contents with a string replacement', done => {
transform(fromToToProcessor({
files: 'test1',
from: 're place',
to: 'b',
}), () => {
const test1 = fs.readFileSync('test1', 'utf8');
expect(test1).to.equal('a b c');
done();
});
});
it('should replace contents in a an array of files', done => {
transform(fromToToProcessor({
files: ['test1', 'test2'],
from: /re\splace/g,
to: 'b',
}), () => {
const test1 = fs.readFileSync('test1', 'utf8');
const test2 = fs.readFileSync('test2', 'utf8');
expect(test1).to.equal('a b c');
expect(test2).to.equal('a b c');
done();
});
});
it('should expand globs', done => {
transform(fromToToProcessor({
files: 'test*',
from: /re\splace/g,
to: 'b',
}), () => {
const test1 = fs.readFileSync('test1', 'utf8');
const test2 = fs.readFileSync('test2', 'utf8');
expect(test1).to.equal('a b c');
expect(test2).to.equal('a b c');
done();
});
});
it('should expand globs while excluding ignored files', done => {
transform(fromToToProcessor({
files: 'test*',
ignore: 'test1',
from: /re\splace/g,
to: 'b',
}), () => {
const test1 = fs.readFileSync('test1', 'utf8');
const test2 = fs.readFileSync('test2', 'utf8');
expect(test1).to.equal('a re place c');
expect(test2).to.equal('a b c');
done();
});
});
it('should not return an error on success', done => {
transform(fromToToProcessor({
files: 'test1',
from: /re\splace/g,
to: 'b',
}), (error) => {
expect(error).to.equal(null);
done();
});
});
it('should return an error on failure', done => {
transform(fromToToProcessor({
files: 'nope',
from: /re\splace/g,
to: 'b',
}), (error) => {
expect(error).to.be.instanceof(Error);
done();
});
});
it('should not return an error if allowEmptyPaths is true', done => {
transform(fromToToProcessor({
files: 'nope',
allowEmptyPaths: true,
from: /re\splace/g,
to: 'b',
}), (error) => {
expect(error).to.equal(null);
done();
});
});
it('should return a results array', done => {
transform(fromToToProcessor({
files: 'test1',
from: /re\splace/g,
to: 'b',
}), (error, results) => {
expect(results).to.be.instanceof(Array);
expect(results).to.have.length(1);
expect(results[0].file).to.equal('test1');
done();
});
});
it('should mark if something was replaced', done => {
transform(fromToToProcessor({
files: 'test1',
from: /re\splace/g,
to: 'b',
}), (error, results) => {
expect(results[0].hasChanged).to.equal(true);
done();
});
});
it('should not mark if nothing was replaced', done => {
transform(fromToToProcessor({
files: 'test1',
from: 'nope',
to: 'b',
}), (error, results) => {
expect(results[0].hasChanged).to.equal(false);
done();
});
});
it('should return correct results for multiple files', done => {
transform(fromToToProcessor({
files: ['test1', 'test2', 'test3'],
from: /re\splace/g,
to: 'b',
}), (error, results) => {
expect(results).to.have.length(3);
expect(results[0].file).to.equal('test1');
expect(results[0].hasChanged).to.equal(true);
expect(results[1].file).to.equal('test2');
expect(results[1].hasChanged).to.equal(true);
expect(results[2].file).to.equal('test3');
expect(results[2].hasChanged).to.equal(false);
done();
});
});
it('should work without expanding globs if disabled', done => {
transform(fromToToProcessor({
files: ['test1', 'test2'],
from: /re\splace/g,
to: 'b',
disableGlobs: true,
}), () => {
const test1 = fs.readFileSync('test1', 'utf8');
const test2 = fs.readFileSync('test2', 'utf8');
expect(test1).to.equal('a b c');
expect(test2).to.equal('a b c');
done();
});
});
});
/**
* Sync
*/
describe('Sync', () => {
it('should throw an error when no config provided', () => {
expect(function() {
transform.sync();
}).to.throw(Error);
});
it('should throw an error when invalid config provided', () => {
expect(function() {
transform.sync(42);
}).to.throw(Error);
});
it('should throw an error when no `files` defined', () => {
expect(function() {
transform.sync(fromToToProcessor({
from: /re\splace/g,
to: 'b',
}));
}).to.throw(Error);
});
it('should support the encoding parameter', () => {
expect(function() {
transform.sync(fromToToProcessor({
files: 'test1',
from: /re\splace/g,
to: 'b',
encoding: 'utf-8',
}));
}).to.not.throw(Error);
});
it('should fall back to utf-8 encoding with invalid configuration', () => {
expect(function() {
transform.sync(fromToToProcessor({
files: 'test1',
from: /re\splace/g,
to: 'b',
encoding: '',
}));
}).to.not.throw(Error);
expect(function() {
transform.sync(fromToToProcessor({
files: 'test1',
from: /re\splace/g,
to: 'b',
encoding: null,
}));
}).to.not.throw(Error);
});
it('should replace contents in a single file with regex', function() {
transform.sync(fromToToProcessor({
files: 'test1',
from: /re\splace/g,
to: 'b',
}));
const test1 = fs.readFileSync('test1', 'utf8');
const test2 = fs.readFileSync('test2', 'utf8');
expect(test1).to.equal('a b c');
expect(test2).to.equal(testData);
});
it('should replace contents with a string replacement', function() {
transform.sync(fromToToProcessor({
files: 'test1',
from: 're place',
to: 'b',
}));
const test1 = fs.readFileSync('test1', 'utf8');
expect(test1).to.equal('a b c');
});
it('should replace contents in a an array of files', function() {
transform.sync(fromToToProcessor({
files: ['test1', 'test2'],
from: /re\splace/g,
to: 'b',
}));
const test1 = fs.readFileSync('test1', 'utf8');
const test2 = fs.readFileSync('test2', 'utf8');
expect(test1).to.equal('a b c');
expect(test2).to.equal('a b c');
});
it('should expand globs', function() {
transform.sync(fromToToProcessor({
files: 'test*',
from: /re\splace/g,
to: 'b',
}));
const test1 = fs.readFileSync('test1', 'utf8');
const test2 = fs.readFileSync('test2', 'utf8');
expect(test1).to.equal('a b c');
expect(test2).to.equal('a b c');
});
it('should return a results array', function() {
const results = transform.sync(fromToToProcessor({
files: 'test1',
from: /re\splace/g,
to: 'b',
}));
expect(results).to.be.instanceof(Array);
expect(results).to.have.length(1);
expect(results[0].file).to.equal('test1');
});
it('should mark if something was replaced', function() {
const results = transform.sync(fromToToProcessor({
files: 'test1',
from: /re\splace/g,
to: 'b',
}));
expect(results[0].hasChanged).to.equal(true);
});
it('should not mark if nothing was replaced', function() {
const results = transform.sync(fromToToProcessor({
files: 'test1',
from: 'nope',
to: 'b',
}));
expect(results[0].hasChanged).to.equal(false);
});
it('should return corret results for multiple files', function() {
const results = transform.sync(fromToToProcessor({
files: ['test1', 'test2', 'test3'],
from: /re\splace/g,
to: 'b',
}));
expect(results).to.have.length(3);
expect(results[0].file).to.equal('test1');
expect(results[0].hasChanged).to.equal(true);
expect(results[1].file).to.equal('test2');
expect(results[1].hasChanged).to.equal(true);
expect(results[2].file).to.equal('test3');
expect(results[2].hasChanged).to.equal(false);
});
it('should expand globs while excluding ignored files', () => {
transform.sync(fromToToProcessor({
files: 'test*',
ignore: 'test1',
from: /re\splace/g,
to: 'b',
}));
const test1 = fs.readFileSync('test1', 'utf8');
const test2 = fs.readFileSync('test2', 'utf8');
expect(test1).to.equal('a re place c');
expect(test2).to.equal('a b c');
});
it('should support an array of ignored files', () => {
transform.sync(fromToToProcessor({
files: 'test*',
ignore: ['test1', 'test3'],
from: /re\splace/g,
to: 'b',
}));
const test1 = fs.readFileSync('test1', 'utf8');
const test2 = fs.readFileSync('test2', 'utf8');
expect(test1).to.equal('a re place c');
expect(test2).to.equal('a b c');
});
it('should not fail when the ignore parameter is undefined', () => {
transform.sync(fromToToProcessor({
files: 'test*',
ignore: undefined,
from: /re\splace/g,
to: 'b',
}));
const test1 = fs.readFileSync('test1', 'utf8');
const test2 = fs.readFileSync('test2', 'utf8');
expect(test1).to.equal('a b c');
expect(test2).to.equal('a b c');
});
it('should work without expanding globs if disabled', () => {
transform.sync(fromToToProcessor({
files: ['test1', 'test2'],
from: /re\splace/g,
to: 'b',
disableGlobs: true,
}));
const test1 = fs.readFileSync('test1', 'utf8');
const test2 = fs.readFileSync('test2', 'utf8');
expect(test1).to.equal('a b c');
expect(test2).to.equal('a b c');
});
it('should not replace in a dry run', () => {
transform.sync(fromToToProcessor({
files: ['test1', 'test2'],
from: /re\splace/g,
to: 'b',
dry: true,
}));
const test1 = fs.readFileSync('test1', 'utf8');
const test2 = fs.readFileSync('test2', 'utf8');
expect(test1).to.equal('a re place c');
expect(test2).to.equal('a re place c');
});
it('should return changed files for a dry run', () => {
const results = transform.sync(fromToToProcessor({
files: ['test1', 'test2', 'test3'],
from: /re\splace/g,
to: 'b',
dry: true,
}));
expect(results).to.have.length(3);
expect(results[0].file).to.equal('test1');
expect(results[0].hasChanged).to.equal(true);
expect(results[1].file).to.equal('test2');
expect(results[1].hasChanged).to.equal(true);
expect(results[2].file).to.equal('test3');
expect(results[2].hasChanged).to.equal(false);
});
});
describe('module export', () => {
it('default module export refers to async replace implementation', () => {
expect(transform).to.be.a('function');
});
it(`exports named processFile, processFileSync and sync from module facade`, () => {
expect(processFile).to.be.a('function');
expect(processFileSync).to.be.a('function');
expect(sync).to.be.a('function');
});
it('exposes inner functions as own fields of transform', () => {
expect(transform.processFile).to.equal(transform);
expect(transform.sync).to.equal(processFileSync);
expect(transform.processFileSync).to.equal(processFileSync);
});
});
});

View File

@@ -0,0 +1,104 @@
'use strict';
/**
* Dependencies
*/
const chalk = require('chalk');
const parseConfig = require('./helpers/parse-config');
const getPathsSync = require('./helpers/get-paths-sync');
const getPathsAsync = require('./helpers/get-paths-async');
const replaceSync = require('./helpers/replace-sync');
const replaceAsync = require('./helpers/replace-async');
const processFile = require('./process-file');
/**
* Replace in file helper
*/
function replaceInFile(config, cb) {
// If custom processor is provided use it instead
if (config && config.processor) {
return processFile(config, cb);
}
//Parse config
try {
config = parseConfig(config);
}
catch (error) {
if (typeof cb === 'function') {
return cb(error, null);
}
return Promise.reject(error);
}
//Get config
const {files, from, to, dry, verbose} = config;
//Dry run?
//istanbul ignore if: No need to test console logs
if (dry && verbose) {
console.log(chalk.yellow('Dry run, not making actual changes'));
}
//Find paths
return getPathsAsync(files, config)
//Make replacements
.then(paths => Promise.all(
paths.map(file => replaceAsync(file, from, to, config))
))
//Success handler
.then(results => {
if (typeof cb === 'function') {
cb(null, results);
}
return results;
})
//Error handler
.catch(error => {
if (typeof cb === 'function') {
cb(error);
}
else {
throw error;
}
});
}
/**
* Sync API
*/
function replaceInFileSync(config) {
// If custom processor is provided use it instead
if (config && config.processor) {
return processFile.processFileSync(config);
}
//Parse config
config = parseConfig(config);
//Get config, paths, and initialize changed files
const {files, from, to, dry, verbose} = config;
const paths = getPathsSync(files, config);
//Dry run?
//istanbul ignore if: No need to test console logs
if (dry && verbose) {
console.log(chalk.yellow('Dry run, not making actual changes'));
}
//Process synchronously and return results
return paths.map(path => replaceSync(path, from, to, config));
}
// Self-reference to support named import
replaceInFile.replaceInFile = replaceInFile;
replaceInFile.replaceInFileSync = replaceInFileSync;
replaceInFile.sync = replaceInFileSync;
//Export
module.exports = replaceInFile;

View File

File diff suppressed because it is too large Load Diff