How to run tslint only on modified files - javascript

I want to configure tslint to run only on git modified files.
In packages.json I have the 'lint' task defined as:
{
"scripts": {
"lint": "tslint \"./src/**/*.{ts,tsx}\"",
...
},
"dependencies": {
"tslint": "4.3.1",
...
},
...
}
So lint task works fine but it processes all files in project by hardcoded mask. Suppose I have a javascript listModified.js which produces list of git modified files. How do I pass the string produced by js as argument to tslint?
I can't use piping here since tslint accepts argument not a pipe. I failed to use eval to form argument.
How to do it?

try xargs
echo "file1.ts\nfile2.ts" | xargs tslint
Obviously you can also use git status or git log to produce list of files to check
If xargs is not available in your system then try to use regular javascript API
https://www.npmjs.com/package/tslint#library-1

I suggest you to use some build system because it should have plugins that allow to check if file was modified by computing hash or by checking last modified time.
For example, using gulp with gulp-tslint and gulp-changed-in-place plugins, and jsonfile package for reading and writing JSON files:
var changedInPlace = require('gulp-changed-in-place'),
gulpTsLint = require('gulp-tslint'),
jsonfile = require('jsonfile');
var tsHashFile = 'ts.hash.json';
gulp.task('tslint', function (cb) {
jsonfile.readFile(tsHashFile, function (err, obj) {
var firstPass = false;
if (err) {
firstPass = true;
obj = {};
}
return gulp.src(tsGlob)
.pipe(changedInPlace({
cache: obj,
firstPass: firstPass
}))
.pipe(gulpTsLint())
.pipe(gulpTsLint.report())
.on('end', function (event) {
jsonfile.writeFile(tsHashFile, obj, function () {
cb(event);
});
});
});
});
tsHashFile is used to persist computed hashes and when you will run gulp tslint again tslint will not be executed for files which hash is not changed.

Related

eslint on dynamically changing file list

I'd like to run eslint on modified files only. I've created a new run target in package.json to run eslint from command line (git diff --name-only --relative | grep -E '.*\\.(vue|js)$' | xargs eslint --ext .js,.vue). In theory, this should work fine, but there's a little transformation step happening in my project (a string replacement) when bundling the files with webpack that will throw off eslint (some non-standard markup will be expanded to JS).
What are my options and how would I go about implementing them? For instance, could I execute a particular webpack rule/loader and pipe the result to eslint? Another option I see is to include eslint into the webpack rule/loader process (instead of executing it from the command line), but how would I then filter on files that are currently modified (could this be handled by a temporary file that contains the git diff... result?)
I've got a somewhat working approach. I chose to modify webpack.base.conf.js instead of going for the command line solution to make use of the already existing string replacement loader.
The files are collected in the WebpackBeforeBuildPlugin callback function and instead of a regex based test variable, a function is used which checks against the previously collected files.
const exec = require('child_process').exec;
const WebpackBeforeBuildPlugin = require('before-build-webpack');
var modFilesList = new Set([]);
const srcPath = resolve('.');
...
rules: [{
test: function(filename) {
let relFilename = path.relative(srcPath, filename);
let lint = modFilesList.has(relFilename);
return lint
},
loader: 'eslint-loader',
include: resolve('src'),
exclude: /node_modules/,
options: {
formatter: require('eslint-friendly-formatter'),
cache: false
}
}, {
... other string replacement loader ...
}
plugins: [
...
new WebpackBeforeBuildPlugin(function(stats, callback) {
// Collect changed files before building.
let gitCmd = 'git diff --name-only --relative | grep -E ".*\\.(vue|js)$"';
const proc = exec(gitCmd, (error, stdout, stderr) => {
if (stdout) {
let files = stdout.split('\n');
modFilesList = new Set(files);
}
if (error !== null) {
console.log(`exec error: ${error}`);
}
});
callback();
})
]
The only problem at the moment is that when git file changes occur, they don't trigger a re-linting based on these file changes (i.e. new file is changed, or previously (before starting webpack-dev-server) changed file changes are discarded). I checked everything I could. The change is registered and stored in modFilesList, the test function is executed and returns true (for a new change in a previously unchanged file) or false in case the change was discarded. I also played with the cache option to no avail. It seems that at initial load, eslint-loader caches the files it will lint in future (don't know if that's a result of using a test function instead of a regex or also the case with the regex). Is anyone having an idea or has seen this before (eslint-loader not updating the file list)?
Update
This seems to be a problem with webpack (or one of the other loaders) as the eslint-loader isn't even executed when the file changed. The test function however is executed which is a bit weird. I don't fully understand how loaders work or how they play together, so there might be some other loader that is causing this...

How do you run eslint for only a specific rule or set of rules - command line only

I know you can define rules in an .eslintrc file, but what if I just want to run eslint and check for one specific rule?
E.g. $ eslint helpme.js --rule some-important-rule
I don't know if this is the best way, but I was able to get this working:
$ eslint helpme.js --no-eslintrc --env "es6" --env "node" --parser-options "{ecmaVersion: 2018}" --rule "{some-important-rule: error}"
Note: With this method (ignoring .eslintrc completeley) you still have to add some stuff from .eslintrc like your environment and parser options.
If you want to use your .eslintrc file to keep your configuration (parser, plugin settings, etc), you can use eslint-nibble with the --rule=some-important-rule flag. This will respect your normal configuration, but only show you errors from that rule. There are some other flags as well like --no-interactive if you want to run this in something like a CI environment.
Disclaimer: I'm the creator of eslint-nibble.
Expanding on #matrik answer, this doesn't require me to define all eslint config again and also shows the file name.
eslint helpme.js | egrep "react/jsx-no-literals" -B 1
Try ESLint custom formatter.
It can be used to filter part of rules, files you want to pay attention to.
And you don't need to :
Edit your ESLint config file.
Use complicate command.
DEMO for filter files contain error which rules id is prop-types:
// ./eslint-file-path-formatter.js
const fs = require('fs');
function containRules(result, targetRuleId) {
if (!result || !targetRuleId) {
return false;
}
return result.messages.some((cur) => {
// console.log(`cur?.ruleId = ${cur?.ruleId}`);
if (cur?.ruleId?.includes(targetRuleId)) {
return true;
}
});
}
module.exports = function (results, context) {
const summary = [];
results.forEach((cur) => {
if (containRules(cur, 'prop-types')) {
summary.push(`'${cur.filePath}',`);
}
});
// fs.writeFileSync('eslint-error-files.txt', summary.join('\n'));
// return 'Done Write';
return summary.join('\n');
};
Usage:
eslint . -f ./eslint-file-path-formatter.js
Then this formatter will print all files name to console.
You can also write result to local files, do whatever you want.
Simple way to see single rule output while still using .eslintrc is to use grep:
$ eslint helpme.js | egrep "(^/|some\-important\-rule$)"

Access option values webdriver.io

Right now with my ui tests using WebdriverIO, I have this in my configuration file:
var baseUrl = 'http://localhost:3000';
global.testParameters = {
baseUrl: baseUrl
};
This gives me access to my base url in the tests however it has to be fixed in the configuration file and I can't use the --baseUrl option when running wdio command. The reason for this is because from everything I have read, I don't see a way to have access to command line option values in my tests.
Is there a way to access the value of the command line options (specifically --baseUrl) in my actual test files?
You can use the yargs library. Do npm install yargs and in your config file add:
var argv = require('yargs').argv;
var baseUrl = argv.baseUrl;
You can then pass in the baseUrl with --baseUrl <your URL>
You can also make use of the WebdriverIO spec (wdio.conf.js) for configuration and create a separate conf.js file for each baseUrl you'd like to run your tests against
You can pass your base location through command line using -baseUrl= like below
wdio --baseUrl=http://[device IP]
You can pass any argument you need using the same way and can access it in wdio.config.js 'onprepare' event like below
wdio --type=XXX
Then in wdio config
onPrepare: function(config, capabilities) {
if (process.argv !== undefined && process.argv.length) {
process.argv.forEach(arg => {
if (arg.indexOf('--type=') !== -1) {
process.env.type = arg.replace('--type=', '');
}
});
}
},
before: function(capabilities, specs) {
global.type = process.env.type;
}
Now your type is available through-out your workspace as a global variable.

Disable Jasmine's fdescribe() and fit() based on environment

fdescribe() and fit() are great for reducing noise when you're working on a subset of tests. I sometimes forget to change them back to describe()/it() before merging my branch into master. (It's okay to have them in separate branch while working on code - i.e. a pre-commit check wouldn't work for me.)
My CI environment is Codeship. Is there a solution to this problem that would fail the tests in Codeship if it came across any focused methods?
Using something like no-focused-tests would be okay. Any idea how to enable this rule as an error in Codeship and disable it locally?
Edit 14.11.19:
To make things easier I created an installable package you can find at https://www.npmjs.com/package/tslint-jasmine
Original post:
If you're using TSLint and (like me) found that all the defocus and tslint-jasmine-noSkipOrFocus checkers are not working for you, I created a Gist for that: https://gist.github.com/djungowski/7d9126bb79970446b4ffeb5656c6bf1f
How to use:
Save Gist in a a folder called TSLint/Rules as noJasmineFocusRule.js
Add the Rules folder to your TSLint config: rulesDirectory: 'TSLint/Rules'
Enable option with "no-jasmine-focus": true
Using something like no-focused-tests would be okay. Any idea how to enable this rule as an error in Codeship and disable it locally?
You could use a combination of environment variables and redefining the fdescribe/fit global functions:
npm i --save cross-env
package.json:
"scripts": {
"test": "jasmine",
"test-safe": "cross-env FOCUSED_TESTS=off jasmine"
},
disableFocusedTestsIfNecessary.js (included after jasmine defines its globals):
if (process.env.FOCUSED_TESTS === "off") {
console.log("Focused tests must be off");
global.fdescribe = global.fit = function() {
throw new Error("fdescribe and fit are disabled in this environment");
};
}
else {
console.log("Focused tests enabled");
}
Tell codeship to run npm run test-safe instead of npm run test
For those interested, if you are using jasmine and eslint, you can use this plugin to ensure no focused tests: https://github.com/tlvince/eslint-plugin-jasmine.
First install eslint globally npm install -g eslint.
Then install the eslint-plugin-jasmine library npm install --save-dev eslint-plugin-jasmine.
Create a .eslintrc file which would look something like this:
{
"rules": {
"semi": 2
},
"plugins": ["jasmine"],
"env": {
"jasmine": true
},
"extends": "plugin:jasmine/recommended",
}
Then you are ready to run the linter eslint -c ./.eslintrc app.js
I'm late to the party.
I had a similar issue with my builds. We don't use ts / eslint so I just wrote a quick script to throw an error that would fail my dockerfile / build.
Here it is.
#!/bin/sh
files=$(find "./.." -type f -name '*.spec*')
errored=false
echo "Checking for focused tests"
for file in $files
do
if grep -E "fdescribe|fit" $file; [ $? -eq 0 ]; then
echo "-Focusing a test in the file $file"
errored=true
fi
done
if $errored; then
echo "Some tests were focused"
exit 1
else
echo "No tests were focused"
fi
This isn't the best solution. But it works for my needs.
To setup:
npm i lodash
npm i minimist
I call this from my gulp tasks:
node .\\build\\throwIfFocusedTest.js e2e/
node .\\build\\throwIfFocusedTest.js src/
throwIfFocusedTest.js:
const walkSync = require('./walkSync').default;
const _ = require('lodash');
const argv = require('minimist')(process.argv);
const fs = require('fs');
if (argv._.length !== 3) {
throw 'expecting 1 command line argument';
}
const directory = argv._[2];
const files = walkSync(directory);
const scriptFiles = _.filter(files, f => f.endsWith('.js') || f.endsWith('.ts'));
const invalidStrings = [
'fdescribe',
'fit',
];
_.each(scriptFiles, fileName => {
const contents = fs.readFileSync(fileName, 'utf8');
invalidStrings.forEach(is => {
if (contents.includes(is)) {
console.error(`throwIfFocusedTest: ${directory}: File contains ${is}: ${fileName}`);
process.exit(1);
}
});
});
console.log(`throwIfFocusedTest: ${directory}: No files contain: ${invalidStrings.join(', ')}`);
walkSync.js:
/**
* From: https://gist.github.com/kethinov/6658166
*/
exports.default = function walkSync(dir, filelist) {
var fs = fs || require('fs'),
files = fs.readdirSync(dir);
filelist = filelist || [];
files.forEach(function (file) {
var path = dir + file;
if (fs.statSync(dir + file).isDirectory()) {
filelist = walkSync(dir + file + '/', filelist);
}
else {
filelist.push(path);
}
});
return filelist;
};
If you're willing to fail on when tests are marked for focus or skip (fit + xit), there's a relatively new Karma feature that solves the problem with no plugins. Karma now supports a failOnSkippedTests config file / CLI option, which, per the docs, causes "failure on tests deliberately disabled, eg fit() or xit()".

remove asserts from javascript code for production version

What is the best way to remove asserts (console.assert) from JavaScript code for production version? Maybe there is some software that can be configured to sort of build JavaScript and remove asserts?
UPDATE __________________________
I've installed GRUNT and groundskeeper plugin. This is what my Gruntfile.js has:
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
groundskeeper: {
compile: {
files: {
'calculator/add.js': 'calculator/add.js'
},
options: {
console: false
}
}
}
});
// These plugins provide necessary tasks.
grunt.loadNpmTasks('grunt-groundskeeper');
The problem is with when I run grunt groundskeeper I get the following error:
Running "groundskeeper:compile" (groundskeeper) task
Warning: Line 2: Invalid left-hand side in assignment Use --force to continue.
I assume that the problem is with this line:
'calculator/add.js': 'calculator/add.js'
Since if I replace it with the following:
'path/to/result.js': 'path/to/source.js'
Everything works fine:
Running "groundskeeper:compile" (groundskeeper) task
>> File path/to/result.js created empty, because its counterpart was empty.
>> 1 files read, 0 cleaned.
What's wrong with the my original line?
make assert as an empty function in your production
console.assert = function(){}
if you can check it is a production version then put the code like,
if (production) {
console.assert = function(){}
}
for old IE shim
if (typeof console == "undefined" || typeof console.assert == "undefined"){
var console = { assert : function() {} };
}
I am using the grunt-groundkeeper plugin for this:
https://github.com/Couto/grunt-groundskeeper
If you're not using Grunt yet, I really recommend using it for JavaScript projects. Explaining how to use Grunt itself is out of scope of this question.
The following example config sets removing all logging statements in the www/scripts.min.js file as the default task.
'use strict';
module.exports = function(grunt) {
grunt.initConfig({
groundskeeper: {
dist: {
files: {
'www/scripts.min.js': 'www/scripts.min.js'
}
}
});
grunt.loadNpmTasks('grunt-groundskeeper');
grunt.registerTask('default', ['groundskeeper']);
};

Categories