freeCodeCamp/client/rechallenge/throwers.js

100 lines
3.3 KiB
JavaScript
Raw Normal View History

2016-05-06 20:20:18 +00:00
import { helpers, Observable } from 'rx';
const throwForJsHtml = {
2016-05-20 19:42:26 +00:00
ext: /js|html/,
2016-05-06 20:20:18 +00:00
throwers: [
{
name: 'multiline-comment',
description: 'Detect if a JS multi-line comment is left open',
2016-05-20 19:42:26 +00:00
thrower: function checkForComments({ contents }) {
const openingComments = contents.match(/\/\*/gi);
const closingComments = contents.match(/\*\//gi);
2016-05-06 20:20:18 +00:00
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,
2016-05-20 19:42:26 +00:00
thrower: function checkForNestedJquery({ contents }) {
if (contents.match(this.detectUnsafeJQ)) {
2016-05-06 20:20:18 +00:00
throw new Error('Unsafe $($)');
}
}
}, {
name: 'unfinished-function',
description: 'lonely function keywords breaks browsers',
detectFunctionCall: /function\s*?\(|function\s+\w+\s*?\(/gi,
2016-05-20 19:42:26 +00:00
thrower: function checkForUnfinishedFunction({ contents }) {
2016-05-06 20:20:18 +00:00
if (
2016-05-20 19:42:26 +00:00
contents.match(/function/g) &&
!contents.match(this.detectFunctionCall)
2016-05-06 20:20:18 +00:00
) {
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,
2016-05-20 19:42:26 +00:00
thrower: function checkForUnsafeConsole({ contents }) {
if (contents.match(this.detectUnsafeConsoleCall)) {
2016-05-06 20:20:18 +00:00
throw new Error('Invalid if (null) console.log(1); detected');
}
}
}, {
2017-02-06 20:09:12 +00:00
name: 'gomix in code',
description: 'Code with the URL gomix.me ' +
'should not be allowed to run',
2017-02-06 20:09:12 +00:00
detectGomixInCode: /gomix\.me/gi,
thrower: function checkForGomix({ contents }) {
if (contents.match(this.detectGomixInCode)) {
throw new Error('Gomix.me should not be in the code');
}
}
2016-05-06 20:20:18 +00:00
}
]
};
2016-05-20 19:42:26 +00:00
export default function throwers() {
2016-05-06 20:20:18 +00:00
const source = this;
return source.map(file$ => file$.flatMap(file => {
2016-05-20 19:42:26 +00:00
if (!throwForJsHtml.ext.test(file.ext)) {
2016-05-06 20:20:18 +00:00
return Observable.just(file);
}
return Observable.from(throwForJsHtml.throwers)
2016-05-20 19:42:26 +00:00
.flatMap(context => {
2016-05-06 20:20:18 +00:00
try {
let finalObs;
2016-05-20 19:42:26 +00:00
const maybeObservableOrPromise = context.thrower(file);
2016-05-06 20:20:18 +00:00
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);
});
}));
}