freeCodeCamp/client/rechallenge/throwers.js

100 lines
3.3 KiB
JavaScript

import { helpers, Observable } from 'rx';
const throwForJsHtml = {
ext: /js|html/,
throwers: [
{
name: 'multiline-comment',
description: 'Detect if a JS multi-line comment is left open',
thrower: function checkForComments({ contents }) {
const openingComments = contents.match(/\/\*/gi);
const closingComments = contents.match(/\*\//gi);
if (
openingComments &&
(!closingComments || openingComments.length > closingComments.length)
) {
throw new Error('SyntaxError: Unfinished multi-line comment');
}
}
}, {
name: 'nested-jQuery',
description: 'Nested dollar sign calls breaks browsers',
detectUnsafeJQ: /\$\s*?\(\s*?\$\s*?\)/gi,
thrower: function checkForNestedJquery({ contents }) {
if (contents.match(this.detectUnsafeJQ)) {
throw new Error('Unsafe $($)');
}
}
}, {
name: 'unfinished-function',
description: 'lonely function keywords breaks browsers',
detectFunctionCall: /function\s*?\(|function\s+\w+\s*?\(/gi,
thrower: function checkForUnfinishedFunction({ contents }) {
if (
contents.match(/function/g) &&
!contents.match(this.detectFunctionCall)
) {
throw new Error(
'SyntaxError: Unsafe or unfinished function declaration'
);
}
}
}, {
name: 'unsafe console call',
description: 'console call stops tests scripts from running',
detectUnsafeConsoleCall: /if\s\(null\)\sconsole\.log\(1\);/gi,
thrower: function checkForUnsafeConsole({ contents }) {
if (contents.match(this.detectUnsafeConsoleCall)) {
throw new Error('Invalid if (null) console.log(1); detected');
}
}
}, {
name: 'hyperdev in code',
description: 'Code with the URL hyperdev.com ' +
'should not be allowed to run',
detectHyperdevInCode: /hyperdev\.com/gi,
thrower: function checkForHyperdev({ contents }) {
if (contents.match(this.detectHyperdevInCode)) {
throw new Error('Hyperdev.com should not be in the code');
}
}
}
]
};
export default function throwers() {
const source = this;
return source.map(file$ => file$.flatMap(file => {
if (!throwForJsHtml.ext.test(file.ext)) {
return Observable.just(file);
}
return Observable.from(throwForJsHtml.throwers)
.flatMap(context => {
try {
let finalObs;
const maybeObservableOrPromise = context.thrower(file);
if (helpers.isPromise(maybeObservableOrPromise)) {
finalObs = Observable.fromPromise(maybeObservableOrPromise);
} else if (Observable.isObservable(maybeObservableOrPromise)) {
finalObs = maybeObservableOrPromise;
} else {
finalObs = Observable.just(maybeObservableOrPromise);
}
return finalObs;
} catch (err) {
return Observable.throw(err);
}
})
// if none of the throwers throw, wait for last one
.last({ defaultValue: null })
// then map to the original file
.map(file)
// if err add it to the file
// and return file
.catch(err => {
file.error = err;
return Observable.just(file);
});
}));
}