[SCRIPTS] ESLint (finally) (#853)

As discussed in #816, here's the linting subsystem rebuilt:

- Added `eslint` to the `devDependencies`.
- Configured `.eslintrc.json` to enforce our coding style.
- Swapped out `semistandard`, as `eslint` does the same job that it did.
- Updated `lint.js` to use `eslint` (some rules are overriden there in order to deal with all the problematic data that comes from generated files).
- Made `eslint` `--fix` errors on its own in the `linter` process and give us a report (that does not seem to auto-commit from CI builds, that's probably for the best).
- Run the new `linter` on pretty much everything, some rules are now better applied across the board.

**Only thing remaining** is to configure Codacy to use this ruleset from now on and we are golden.

## PR Type
- [ ] Snippets, Tests & Tags (new snippets, updated snippets, re-tagging of snippets, added/updated tests)
- [x] Scripts & Website & Meta (anything related to files in the [scripts folder](https://github.com/30-seconds/30-seconds-of-code/tree/master/scripts), how the repository's automated procedures work and the website)
- [ ] Glossary & Secondary Features (anything related to the glossary, such as new or updated terms or other secondary features)
- [ ] General, Typos, Misc. & Meta (everything else, typos, general stuff and meta files in the repository - e.g. the issue template)

## Guidelines
- [x] I have read the guidelines in the [CONTRIBUTING](https://github.com/30-seconds/30-seconds-of-code/blob/master/CONTRIBUTING.md) document.
- [x] My PR doesn't include any `testlog` changes.
This commit is contained in:
Felix Wu
2018-10-19 08:04:37 +02:00
committed by GitHub
31 changed files with 2287 additions and 1845 deletions

173
.eslintrc.json Normal file
View File

@ -0,0 +1,173 @@
{
"env": {
"browser": true,
"es6": true,
"node": true
},
"extends": "eslint:recommended",
"parserOptions": {
"ecmaVersion": 2018,
"sourceType": "module"
},
"rules": {
"indent": [
"error",
2
],
"quotes": [
"error",
"single",
{
"avoidEscape": true,
"allowTemplateLiterals": true
}
],
"semi": [
"error",
"always"
],
"semi-spacing": [
"error",
{
"before": false,
"after": true
}
],
"no-duplicate-imports": "error",
"no-useless-computed-key": "error",
"rest-spread-spacing": [
"error",
"never"
],
"no-console": "off",
"eqeqeq": [
"error",
"smart"
],
"brace-style": [
"error",
"1tbs",
{
"allowSingleLine": true
}
],
"curly": [
"error",
"multi-or-nest"
],
"guard-for-in": "warn",
"object-shorthand": [
"warn",
"always"
],
"key-spacing": [
"error",
{
"beforeColon": false,
"afterColon": true,
"mode": "strict"
}
],
"camelcase": [
"error",
{
"properties": "always"
}
],
"dot-location": [
"error",
"property"
],
"generator-star-spacing": [
"error",
{
"before": true,
"after": false
}
],
"block-spacing": [
"error",
"always"
],
"comma-style": [
"error",
"last"
],
"comma-spacing": [
"error",
{
"before": false,
"after": true
}
],
"no-extend-native": "error",
"no-loop-func": "error",
"no-implied-eval": "error",
"no-iterator": "error",
"no-label-var": "error",
"no-multi-str": "error",
"no-script-url": "error",
"no-shadow-restricted-names": "error",
"no-spaced-func": "error",
"no-sparse-arrays": "warn",
"no-fallthrough": "warn",
"no-caller": "error",
"no-eval": "error",
"no-multiple-empty-lines": [
"error",
{
"max": 2,
"maxEOF": 1
}
],
"no-multi-spaces":[
"error",
{
"ignoreEOLComments": true
}
],
"no-negated-in-lhs": "error",
"no-new": "error",
"no-new-require": "error",
"block-scoped-var": "error",
"no-use-before-define": "warn",
"no-trailing-spaces": "error",
"no-proto": "error",
"complexity": [
"warn",
5
],
"wrap-iife": [
"error",
"outside"
],
"new-parens": "error",
"space-infix-ops": "error",
"eol-last": [
"error",
"always"
],
"space-unary-ops": "error",
"arrow-parens": [
"error",
"as-needed"
],
"arrow-spacing": "error",
"space-before-blocks": [
"error",
"always"
],
"yoda": [
"error",
"never"
],
"space-before-function-paren": [
"error",
"never"
],
"spaced-comment": [
"error",
"always"
]
}
}

3761
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -3,6 +3,7 @@
"@babel/core": "^7.1.2",
"@babel/preset-env": "^7.1.0",
"chalk": "^2.4.1",
"eslint": "^5.7.0",
"fs-extra": "^6.0.0",
"html-minifier": "^3.5.20",
"jest": "^23.6.0",
@ -16,8 +17,7 @@
"prismjs": "^1.15.0",
"rollup": "^0.58.2",
"rollup-plugin-babel": "^4.0.3",
"rollup-plugin-babel-minify": "^4.0.0",
"semistandard": "^12.0.1"
"rollup-plugin-babel-minify": "^4.0.0"
},
"name": "30-seconds-of-code",
"description": "A collection of useful JavaScript snippets.",

View File

@ -45,10 +45,10 @@ if (
.readdirSync(SNIPPETS_ARCHIVE_PATH)
.sort((a, b) => a.toLowerCase() - b.toLowerCase());
// Store the data read from each snippet in the appropriate object
for (const name of snippetFilenames.filter(s => s !== 'README.md')) {
for (const name of snippetFilenames.filter(s => s !== 'README.md'))
snippets[name] = fs.readFileSync(path.join(SNIPPETS_ARCHIVE_PATH, name), 'utf8');
}
} catch (err) {
}
catch (err) {
console.log(`${chalk.red('ERROR!')} During snippet loading: ${err}`);
process.exit(1);
}
@ -65,13 +65,13 @@ if (
);
output += misc.hr();
for (const snippet of Object.entries(snippets)) {
for (const snippet of Object.entries(snippets))
output += makeExamples(snippet[1]);
}
// Write to the README file of the archive
fs.writeFileSync(path.join(SNIPPETS_ARCHIVE_PATH, 'README.md'), output);
} catch (err) {
}
catch (err) {
console.log(`${chalk.red('ERROR!')} During README generation for snippets archive: ${err}`);
process.exit(1);
}
@ -110,7 +110,8 @@ snippets = util.readSnippets(SNIPPETS_PATH);
try {
startPart = fs.readFileSync(path.join(STATIC_PARTS_PATH, 'README-start.md'), 'utf8');
endPart = fs.readFileSync(path.join(STATIC_PARTS_PATH, 'README-end.md'), 'utf8');
} catch (err) {
}
catch (err) {
console.log(`${chalk.red('ERROR!')} During static part loading: ${err}`);
process.exit(1);
}
@ -170,7 +171,8 @@ try {
output += `\n${endPart}\n`;
// Write to the README file
fs.writeFileSync('README.md', output);
} catch (err) {
}
catch (err) {
console.log(`${chalk.red('ERROR!')} During README generation: ${err}`);
process.exit(1);
}

View File

@ -28,9 +28,9 @@ try {
// turn it into an object so we can add data to it to be used in a different scope
.map(name => ({ name }));
if (!fs.existsSync(TEMP_PATH)) {
if (!fs.existsSync(TEMP_PATH))
fs.mkdirSync(TEMP_PATH);
}
for (const snippet of snippets) {
snippet.data = fs.readFileSync(path.join(SNIPPETS_PATH, snippet.name), 'utf8');
@ -52,10 +52,10 @@ try {
}
const cmd =
`semistandard "${TEMP_PATH}" --fix & ` +
`prettier "${TEMP_PATH}/*.js" --single-quote --print-width=100 --write`;
`prettier "${TEMP_PATH}/*.js" --single-quote --print-width=100 --write & ` +
`eslint "${TEMP_PATH}/*.js" --quiet --fix --rule "no-undef: 0, no-unused-vars: 0, no-multiple-empty-lines: 0" -o eslint_errors.log -f table`;
cp.exec(cmd, {}, (err, stdout, stderr) => {
cp.exec(cmd, {}, () => {
// Loop through each snippet now that semistandard and prettier did their job
for (const snippet of snippets) {
// an array to store each linted code block (definition + example)
@ -77,7 +77,8 @@ try {
console.log(`${chalk.green('SUCCESS!')} Snippet files linted!`);
console.timeEnd('Linter');
});
} catch (err) {
}
catch (err) {
console.log(`${chalk.red('ERROR!')} During linting: ${err}`);
process.exit(1);
}

View File

@ -30,22 +30,25 @@ tagDbStats = Object.entries(tagDbData).reduce((acc, val) => {
}, {});
// Update the listing of snippets in tag_database and log the statistics, along with missing scripts
try {
for (let snippet of Object.entries(snippets))
for (let snippet of Object.entries(snippets)) {
if (
tagDbData.hasOwnProperty(snippet[0].slice(0, -3)) &&
tagDbData[snippet[0].slice(0, -3)].join(',').trim()
)
) {
output += `${snippet[0].slice(0, -3)}:${tagDbData[snippet[0].slice(0, -3)]
.join(',')
.trim()}\n`;
}
else {
output += `${snippet[0].slice(0, -3)}:uncategorized\n`;
missingTags++;
console.log(`${chalk.yellow('Tagged uncategorized:')} ${snippet[0].slice(0, -3)}`);
}
}
// Write to tag_database
fs.writeFileSync('tag_database', output);
} catch (err) {
}
catch (err) {
// Handle errors (hopefully not!)
console.log(`${chalk.red('ERROR!')} During tag_database generation: ${err}`);
process.exit(1);

View File

@ -31,7 +31,8 @@ const getFilesInDir = (directoryPath, withPath, exclude = null) => {
}, []);
}
return directoryFilenames;
} catch (err) {
}
catch (err) {
console.log(`${chalk.red('ERROR!')} During snippet loading: ${err}`);
process.exit(1);
}
@ -45,7 +46,8 @@ const readSnippets = snippetsPath => {
try {
for (let snippet of snippetFilenames)
snippets[snippet] = fs.readFileSync(path.join(snippetsPath, snippet), 'utf8');
} catch (err) {
}
catch (err) {
console.log(`${chalk.red('ERROR!')} During snippet loading: ${err}`);
process.exit(1);
}
@ -68,7 +70,8 @@ const readTags = () => {
return data;
})
);
} catch (err) {
}
catch (err) {
// Handle errors (hopefully not!)
console.log(`${chalk.red('ERROR!')} During tag database loading: ${err}`);
process.exit(1);
@ -82,9 +85,9 @@ const optimizeNodes = (data, regexp, replacer) => {
do {
output = output.replace(regexp, replacer);
count = 0;
while (regexp.exec(output) !== null) {
while (regexp.exec(output) !== null)
++count;
}
} while (count > 0);
return output;
};
@ -116,9 +119,9 @@ const getCodeBlocks = str => {
const results = [];
let m = null;
while ((m = regex.exec(str)) !== null) {
if (m.index === regex.lastIndex) {
if (m.index === regex.lastIndex)
regex.lastIndex += 1;
}
m.forEach((match, groupIndex) => {
results.push(match);
});
@ -131,9 +134,9 @@ const getTextualContent = str => {
const results = [];
let m = null;
while ((m = regex.exec(str)) !== null) {
if (m.index === regex.lastIndex) {
if (m.index === regex.lastIndex)
regex.lastIndex += 1;
}
m.forEach((match, groupIndex) => {
results.push(match);
});

View File

@ -47,12 +47,12 @@ const generateSnippetCard = (
${
addCornerTag
? `<div class="corner ${
snippetKey[1].includes('advanced')
? 'advanced'
: snippetKey[1].includes('beginner')
? 'beginner'
: 'intermediate'
}"></div>`
snippetKey[1].includes('advanced')
? 'advanced'
: snippetKey[1].includes('beginner')
? 'beginner'
: 'intermediate'
}"></div>`
: ''
}
${md
@ -109,9 +109,10 @@ sass.render(
if (!err2) console.log(`${chalk.green('SUCCESS!')} style.css file generated!`);
else console.log(`${chalk.red('ERROR!')} During style.css file generation: ${err}`);
});
} else {
console.log(`${chalk.red('ERROR!')} During style.css file generation: ${err}`);
}
else
console.log(`${chalk.red('ERROR!')} During style.css file generation: ${err}`);
}
);
// Set variables for paths
@ -147,7 +148,8 @@ try {
'static-page-start.html',
'static-page-end.html'
].map(filename => fs.readFileSync(path.join(staticPartsPath, filename), 'utf8'));
} catch (err) {
}
catch (err) {
// Handle errors (hopefully not!)
console.log(`${chalk.red('ERROR!')} During static part loading: ${err}`);
process.exit(1);
@ -169,7 +171,7 @@ try {
.replace(/<p>/g, '')
.replace(/<\/p>/g, '') +
'</h4><ul>';
for (let taggedSnippet of Object.entries(tagDbData).filter(v => v[1][0] === tag))
for (let taggedSnippet of Object.entries(tagDbData).filter(v => v[1][0] === tag)) {
output += md
.render(
`[${taggedSnippet[0]}](./${
@ -179,6 +181,7 @@ try {
.replace(/<p>/g, '')
.replace(/<\/p>/g, '</li>')
.replace(/<a/g, `<li><a tags="${taggedSnippet[1].join(',')}"`);
}
output += '</ul>\n';
}
output += `<h4 class="static-link"><a href="./archive">Archive</a></h4>
@ -218,7 +221,7 @@ try {
/<span class="token keyword">([^\0<]*?)<\/span>([\n\r\s]*)<span class="token keyword">([^\0]*?)<\/span>/gm,
(match, p1, p2, p3) => `<span class="token keyword">${p1}${p2}${p3}</span>`
);
pagesOutput.push({ tag: tag, content: localOutput });
pagesOutput.push({ tag, content: localOutput });
}
// Minify output
pagesOutput.forEach(page => {
@ -231,7 +234,8 @@ try {
`${chalk.green('SUCCESS!')} ${page.tag === 'array' ? 'index' : page.tag}.html file generated!`
);
});
} catch (err) {
}
catch (err) {
// Handle errors (hopefully not!)
console.log(`${chalk.red('ERROR!')} During category page generation: ${err}`);
process.exit(1);
@ -251,7 +255,7 @@ const staticPageStartGenerator = (staticPart, heading, description) => {
.replace(/<p>/g, '')
.replace(/<\/p>/g, '') +
'</h4><ul>';
for (let taggedSnippet of Object.entries(tagDbData).filter(v => v[1][0] === tag))
for (let taggedSnippet of Object.entries(tagDbData).filter(v => v[1][0] === tag)) {
htmlCode += md
.render(
`[${taggedSnippet[0]}](./${
@ -261,6 +265,7 @@ const staticPageStartGenerator = (staticPart, heading, description) => {
.replace(/<p>/g, '')
.replace(/<\/p>/g, '</li>')
.replace(/<a/g, `<li><a tags="${taggedSnippet[1].join(',')}"`);
}
htmlCode += '</ul>\n';
}
htmlCode += `<h4 class="static-link"><a href="./archive">Archive</a></h4>
@ -271,13 +276,13 @@ const staticPageStartGenerator = (staticPart, heading, description) => {
</nav><main class="col-centered"><span id="top"><br/><br/></span><h2 class="category-name">${heading}</h2>
<p style="text-align: justify">${description}</p><br />`;
return htmlCode.replace(/\$page_name/g, util.capitalize(heading));
}
};
// Create the output for the archive.html file
try {
// Add the static part
let heading = "Snippets Archive";
let heading = 'Snippets Archive';
let description = "These snippets, while useful and interesting, didn't quite make it into the repository due to either having very specific use-cases or being outdated. However we felt like they might still be useful to some readers, so here they are.";
let htmlCode = staticPageStartGenerator(staticPageStartPart, heading, description);
@ -316,7 +321,8 @@ try {
fs.writeFileSync(path.join(docsPath, 'archive.html'), minifiedArchivedOutput);
console.log(`${chalk.green('SUCCESS!')} archive.html file generated!`);
} catch (err) {
}
catch (err) {
console.log(`${chalk.red('ERROR!')} During archive.html generation: ${err}`);
process.exit(1);
}
@ -324,8 +330,8 @@ try {
// Create the output for the glossary.html file
try {
// Add the static part
let heading = "Glossary";
let description = "Developers use a lot of terminology daily. Every once in a while, you might find a term you do not know. We know how frustrating that can get, so we provide you with a handy glossary of frequently used web development terms.";
let heading = 'Glossary';
let description = 'Developers use a lot of terminology daily. Every once in a while, you might find a term you do not know. We know how frustrating that can get, so we provide you with a handy glossary of frequently used web development terms.';
let htmlCode = staticPageStartGenerator(staticPageStartPart, heading, description);
glossaryOutput += htmlCode;
@ -333,7 +339,7 @@ try {
const filteredGlossarySnippets = filterSnippets(glossarySnippets, ['README.md']);
// Generate glossary snippets from md files
for (let snippet of Object.entries(filteredGlossarySnippets))
for (let snippet of Object.entries(filteredGlossarySnippets)) {
glossaryOutput +=
'<div class="card code-card"><div class="section card-content">' +
md
@ -341,6 +347,7 @@ try {
.replace(/<h3/g, `<h4 id="${snippet[0].toLowerCase()}"`)
.replace(/<\/h3>/g, '</h4>') +
'</div></div>';
}
glossaryOutput += `${staticPageEndPart}`;
@ -348,7 +355,8 @@ try {
const minifiedGlossaryOutput = minifyHTML(glossaryOutput);
fs.writeFileSync(path.join(docsPath, 'glossary.html'), minifiedGlossaryOutput);
console.log(`${chalk.green('SUCCESS!')} glossary.html file generated!`);
} catch (err) {
}
catch (err) {
console.log(`${chalk.red('ERROR!')} During glossary.html generation: ${err}`);
process.exit(1);
}
@ -358,7 +366,8 @@ staticFiles.forEach(f => {
try {
fs.copyFileSync(path.join(staticPartsPath, f), path.join(docsPath, f));
console.log(`${chalk.green('SUCCESS!')} ${f} file copied!`);
} catch (err) {
}
catch (err) {
console.log(`${chalk.red('ERROR!')} During ${f} copying: ${err}`);
process.exit(1);
}

View File

@ -11,9 +11,9 @@ const call = (key, ...args) => context => context[key](...args);
```js
Promise.resolve([1, 2, 3])
.then(call('map', x => 2 * x))
.then(console.log); //[ 2, 4, 6 ]
.then(console.log); // [ 2, 4, 6 ]
const map = call.bind(null, 'map');
Promise.resolve([1, 2, 3])
.then(map(x => 2 * x))
.then(console.log); //[ 2, 4, 6 ]
.then(console.log); // [ 2, 4, 6 ]
```

View File

@ -8,7 +8,7 @@ Calls `Object.freeze(obj)` recursively on all unfrozen properties of passed obje
const deepFreeze = obj =>
Object.keys(obj).forEach(
prop =>
!obj[prop] instanceof Object || Object.isFrozen(obj[prop]) ? null : deepFreeze(obj[prop])
!(obj[prop] instanceof Object) || Object.isFrozen(obj[prop]) ? null : deepFreeze(obj[prop])
) || Object.freeze(obj);
```

View File

@ -14,6 +14,6 @@ defer(console.log, 'a'), console.log('b'); // logs 'b' then 'a'
// Example B:
document.querySelector('#someElement').innerHTML = 'Hello';
longRunningFunction(); //Browser will not update the HTML until this has finished
longRunningFunction(); // Browser will not update the HTML until this has finished
defer(longRunningFunction); // Browser will update the HTML then run the function
```

View File

@ -10,9 +10,9 @@ const dig = (obj, target) =>
target in obj
? obj[target]
: Object.values(obj).reduce((acc, val) => {
if (acc !== undefined) return acc;
if (typeof val === 'object') return dig(val, target);
}, undefined);
if (acc !== undefined) return acc;
if (typeof val === 'object') return dig(val, target);
}, undefined);
```
```js

View File

@ -15,9 +15,9 @@ const elo = ([...ratings], kFactor = 32, selfRating) => {
const expectedScore = (self, opponent) => 1 / (1 + 10 ** ((opponent - self) / 400));
const newRating = (rating, i) =>
(selfRating || rating) + kFactor * (i - expectedScore(i ? a : b, i ? b : a));
if (ratings.length === 2) {
if (ratings.length === 2)
return [newRating(a, 1), newRating(b, 0)];
}
for (let i = 0, len = ratings.length; i < len; i++) {
let j = i;
while (j < len - 1) {

View File

@ -11,8 +11,8 @@ Throws an exception if `n` is a negative number.
const factorial = n =>
n < 0
? (() => {
throw new TypeError('Negative numbers are not allowed!');
})()
throw new TypeError('Negative numbers are not allowed!');
})()
: n <= 1
? 1
: n * factorial(n - 1);

View File

@ -21,7 +21,7 @@ const httpGet = (url, callback, err = console.error) => {
httpGet(
'https://jsonplaceholder.typicode.com/posts/1',
console.log
); /*
); /*
Logs: {
"userId": 1,
"id": 1,

View File

@ -42,7 +42,7 @@ Logs: {
*/
httpPost(
'https://jsonplaceholder.typicode.com/posts',
null, //does not send a body
null, // does not send a body
console.log
); /*
Logs: {

View File

@ -17,7 +17,7 @@ const sum = pipeAsyncFunctions(
x => x + 3,
async x => (await x) + 4
);
(async () => {
(async() => {
console.log(await sum(5)); // 15 (after one second)
})();
```

View File

@ -9,9 +9,9 @@ The `func` is invoked with three arguments (`value, index, array`).
const remove = (arr, func) =>
Array.isArray(arr)
? arr.filter(func).reduce((acc, val) => {
arr.splice(arr.indexOf(val), 1);
return acc.concat(val);
}, [])
arr.splice(arr.indexOf(val), 1);
return acc.concat(val);
}, [])
: [];
```

View File

@ -29,9 +29,9 @@ const longRunningFunction = () => {
let result = 0;
for (let i = 0; i < 1000; i++) {
for (let j = 0; j < 700; j++) {
for (let k = 0; k < 300; k++) {
for (let k = 0; k < 300; k++)
result = result + i + j + k;
}
}
}
return result;

View File

@ -16,6 +16,6 @@ const sumPower = (end, power = 2, start = 1) =>
```js
sumPower(10); // 385
sumPower(10, 3); //3025
sumPower(10, 3, 5); //2925
sumPower(10, 3); // 3025
sumPower(10, 3, 5); // 2925
```

View File

@ -16,6 +16,6 @@ const unzip = arr =>
```
```js
unzip([['a', 1, true], ['b', 2, false]]); //[['a', 'b'], [1, 2], [true, false]]
unzip([['a', 1, true], ['b', 2]]); //[['a', 'b'], [1, 2], [true]]
unzip([['a', 1, true], ['b', 2, false]]); // [['a', 'b'], [1, 2], [true, false]]
unzip([['a', 1, true], ['b', 2]]); // [['a', 'b'], [1, 2], [true]]
```

View File

@ -1,6 +1,6 @@
const deepFreeze = obj =>
Object.keys(obj).forEach(
prop =>
!obj[prop] instanceof Object || Object.isFrozen(obj[prop]) ? null : deepFreeze(obj[prop])
!(obj[prop] instanceof Object) || Object.isFrozen(obj[prop]) ? null : deepFreeze(obj[prop])
) || Object.freeze(obj);
module.exports = deepFreeze;

View File

@ -2,7 +2,7 @@ const dig = (obj, target) =>
target in obj
? obj[target]
: Object.values(obj).reduce((acc, val) => {
if (acc !== undefined) return acc;
if (typeof val === 'object') return dig(val, target);
}, undefined);
if (acc !== undefined) return acc;
if (typeof val === 'object') return dig(val, target);
}, undefined);
module.exports = dig;

View File

@ -3,9 +3,9 @@ const elo = ([...ratings], kFactor = 32, selfRating) => {
const expectedScore = (self, opponent) => 1 / (1 + 10 ** ((opponent - self) / 400));
const newRating = (rating, i) =>
(selfRating || rating) + kFactor * (i - expectedScore(i ? a : b, i ? b : a));
if (ratings.length === 2) {
if (ratings.length === 2)
return [newRating(a, 1), newRating(b, 0)];
}
for (let i = 0, len = ratings.length; i < len; i++) {
let j = i;
while (j < len - 1) {

View File

@ -1,8 +1,8 @@
const factorial = n =>
n < 0
? (() => {
throw new TypeError('Negative numbers are not allowed!');
})()
throw new TypeError('Negative numbers are not allowed!');
})()
: n <= 1
? 1
: n * factorial(n - 1);

View File

@ -15,4 +15,4 @@ test("getImages returns an Array", () => {
test("getImages removes duplicates from images Array", () => {
expect(getImages(TEST_HTML, false).length).toBeLessThanOrEqual(getImages(TEST_HTML, true).length);
expect(getImages(TEST_HTML, true)).toEqual(expect.arrayContaining(getImages(TEST_HTML, false)));
});\n
});

View File

@ -1,2 +1,2 @@
const isPrimitive = val => !['object', 'function'].includes(typeof val) || val === null;
const isPrimitive = val => Object(val) !== val;
module.exports = isPrimitive;

View File

@ -1,8 +1,8 @@
const remove = (arr, func) =>
Array.isArray(arr)
? arr.filter(func).reduce((acc, val) => {
arr.splice(arr.indexOf(val), 1);
return acc.concat(val);
}, [])
arr.splice(arr.indexOf(val), 1);
return acc.concat(val);
}, [])
: [];
module.exports = remove;

View File

@ -0,0 +1,2 @@
const squareSum = (...args) => args.reduce((squareSum, number) => squareSum + Math.pow(number, 2), 0);
module.exports = squareSum;

View File

@ -0,0 +1,6 @@
const expect = require('expect');
const squareSum = require('./squareSum.js');
test('squareSum is a Function', () => {
expect(squareSum).toBeInstanceOf(Function);
});

View File

@ -14,7 +14,7 @@ const throttle = (fn, wait) => {
fn.apply(context, args);
lastTime = Date.now();
}
}, wait - (Date.now() - lastTime));
}, Math.max(wait - (Date.now() - lastTime), 0));
}
};
};