Skip to content

Instantly share code, notes, and snippets.

@ilyazub
Last active December 19, 2016 10:39
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ilyazub/4bf247fb31b74372796b4125ecdd7f81 to your computer and use it in GitHub Desktop.
Save ilyazub/4bf247fb31b74372796b4125ecdd7f81 to your computer and use it in GitHub Desktop.
Add missing require() calls and module.exports statements

Add missing require() calls and module.exports statements

npm run codemode:addMissingRequires

Find javascript files that are changed after creation of the new branch and run script placed in requireUndefined.js. It uses ESLint programmatic API.

codemode:addModuleExports

Find all javascripts inside app/components, filter by the lack of module.exports and apply jscodeshift transform placed in addModuleExports.js.

// Top-level variable declarations only
const isTopDeclaration = node => {
return node.parent.parent.parent.node.type === 'Program'
&& node.parent.node.type === 'VariableDeclarator'
&& node.parent.parent.node.type === 'VariableDeclaration';
};
// Construct statement like 'module.exports = SomeClass`
const moduleExportsStatement = node => {
j.expressionStatement(
j.assignmentExpression(
'=',
j.memberExpression(
j.identifier('module'),
j.identifier('exports'),
false
),
node
)
)
}
export default function transformer(file, api) {
const j = api.jscodeshift;
const root = j(file.source);
const topDeclarations = root
.find(j.Identifier)
.filter(isTopDeclaration);
return topDeclarations
.forEach(path => {
root.get().node.program.body.push(
moduleExportsStatement(path.node)
);
})
.toSource();
}
{
"private": true,
"license": "ISC",
"dependencies": {
"babel-cli": "^6.18.0",
"babel-core": "^6.18.2",
"babel-preset-es2015-node6": "^0.4.0",
"eslint": "^3.11.1",
"underscore": "^1.8.3"
},
"scripts": {
"codemode:addModuleExports": "find ./app/components -name '*.js' | xargs -r grep -L 'module.exports' | xargs jscodeshift -t ./addModuleExports.js",
"codemode:addMissingRequires": "git diff origin/master --name-only -- ./app/components/ | grep \"\\.js\" | xargs -r babel-node ./requireUndefined"
},
"engines": {
"node": ">=6",
"npm": ">=3"
},
"devDependencies": {
"glob": "^7.1.1",
"jscodeshift": "^0.3.30"
}
}
"use strict";
const CLIEngine = require("eslint").CLIEngine;
const _ = require('underscore');
const glob = require('glob');
const fs = require('fs');
const path = require('path');
const APP = path.join(__dirname, 'app');
const cli = new CLIEngine({
envs: ["browser"],
// Consider .eslintrc
useEslintrc: true,
// Find undefined variables only.
rules: {
'no-undef': 2
}
});
// Get filenames to process
const filesToCheck = process.argv.slice(2);
const report = cli.executeOnFiles(filesToCheck);
// RegExp to match undefined variables from the ESLint report.
// Sure, there should be a proper way to obtain variable names.
const REGEXP = /\'(.+)\' is not defined\./;
// Find all JavaScript files
const files = glob.sync('*/*/*/*.js', {
cwd: APP
});
function filterNoUndef(message) {
return message.ruleId === 'no-undef' && message.severity === 2 && REGEXP.test(message.message);
}
// Map variable name to "var <Class> = require('<path/to/Class>')" statement
// in case file with such name present in the file system.
// With an assumption that all "classes" are placed in files with the corresponding names.
function mapToRequireStatement(objName) {
var requirement = files.find(file => file.indexOf(`/${objName}.js`) > -1) || objName;
return `var ${objName} = require('${requirement}');`;
}
/**
* Map file name to object of { filePath: string, undefinedObjects: Array }
* Where filePath is path to the file and
* undefinedObjects is an array of "var <Class> = require('<path/to/Class>')" statements
*/
function getFilePathAndFilteredMessages(file) {
return {
filePath: file.filePath,
undefinedObjects: _.chain(file.messages)
.filter(filterNoUndef)
.map(message => message.message.match(REGEXP)[1])
.uniq()
.map(mapToRequireStatement)
.value()
};
}
// We've instructed ESLint to produce errors on undefined variables only
const results = CLIEngine.getErrorResults(report.results)
.map(getFilePathAndFilteredMessages)
// Drop results with no undefined variables
.filter(file => file.undefinedObjects.length > 0);
// Prepend require('') to files
results.forEach(result => {
var data = fs.readFileSync(result.filePath);
var fd = fs.openSync(result.filePath, 'w+');
// Construct a string of array of newly created require() statements
var buffer = new Buffer(result.undefinedObjects.join('\n') + '\n\n');
// Prepend require() statements to the file
fs.writeSync(fd, buffer, 0, buffer.length);
// Append the rest of file
fs.appendFileSync(fd, data);
fs.close(fd);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment