Files
freeCodeCamp/common/app/routes/Challenges/rechallenge/throwers.js
Berkeley Martinez 2e410330f1 Feat(Challenges): no js preview (#16149)
* fix(files): Decouple files from challenges

* feat(server/react): Remove action logger

use redux remote devtools instead!

* feat(Challenges): Disable js on edit, enable on execute

* feat(Challenge/Preview): Show message when js is disabled

* refactor(frameEpic): Reduce code by using lodash

* feat(frameEpic): Disable js in preview by state

* feat(frameEpic): Colocate epic in Challenges/redux

* refactor(ExecuteChallengeEpic): CoLocated with Challenges

* refactor(executeChallengesEpic): Separate tests from main logic

* feat(Challenge/Preview): Update main on edit

* feat(frameEpuc): Replace frame on edit/execute

This allows for sandbox to work properly

* fix(Challenges/Utils): Require utisl

* revert(frameEpic): Hoist function to mount code in frame

* fix(frameEpic): Ensure new frame is given classname

* feat(executeChallenge): Update main on code unlocked

* fix(frameEpic): Filter out empty test message

* fix(Challenge/Preview): Remove unnessary quote in classname

* feat(codeStorageEpic): Separate localstorage from solutions loading

* fix(fetchUser): Merge user actions into one

prefer many effects from one action over one action to one effect

* fix(themes): Centralize theme utils and defs

* fix(entities.user): Fix user reducer namespacing

* feat(frame): Refactor frameEpic to util

* feat(Challenges.redux): Should not attempt to update main from storage

* fix(loadPreviousChallengeEpic): Refactor for RFR

* fix(Challenges.Modern): Show preview plane
2017-12-07 18:13:19 -06:00

125 lines
3.0 KiB
JavaScript

import { Observable } from 'rx';
import cond from 'lodash/cond';
import identity from 'lodash/identity';
import stubTrue from 'lodash/stubTrue';
import conforms from 'lodash/conforms';
import castToObservable from '../../../utils/cast-to-observable.js';
const HTML$JSReg = /html|js/;
const testHTMLJS = conforms({ ext: (ext) => HTML$JSReg.test(ext) });
// const testJS = matchesProperty('ext', 'js');
const passToNext = [ stubTrue, identity ];
// Detect if a JS multi-line comment is left open
const throwIfOpenComments = cond([
[
testHTMLJS,
function _checkForComments({ contents }) {
const openingComments = contents.match(/\/\*/gi);
const closingComments = contents.match(/\*\//gi);
if (
openingComments &&
(!closingComments || openingComments.length > closingComments.length)
) {
throw new SyntaxError('Unfinished multi-line comment');
}
}
],
passToNext
]);
// Nested dollar sign calls breaks browsers
const nestedJQCallReg = /\$\s*?\(\s*?\$\s*?\)/gi;
const throwIfNestedJquery = cond([
[
testHTMLJS,
function({ contents }) {
if (nestedJQCallReg.test(contents)) {
throw new SyntaxError('Nested jQuery calls breaks browsers');
}
}
],
passToNext
]);
const functionReg = /function/g;
const functionCallReg = /function\s*?\(|function\s+\w+\s*?\(/gi;
// lonely function keywords breaks browsers
const ThrowIfUnfinishedFunction = cond([
[
testHTMLJS,
function({ contents }) {
if (
functionReg.test(contents) &&
!functionCallReg.test(contents)
) {
throw new SyntaxError(
'Unsafe or unfinished function declaration'
);
}
}
],
passToNext
]);
// console call stops tests scripts from running
const unsafeConsoleCallReg = /if\s\(null\)\sconsole\.log\(1\);/gi;
const throwIfUnsafeConsoleCall = cond([
[
testHTMLJS,
function({ contents }) {
if (unsafeConsoleCallReg.test(contents)) {
throw new SyntaxError(
'`if (null) console.log(1)` detected. This will break tests'
);
}
}
],
passToNext
]);
// Code with the URL hyperdev.com should not be allowed to run,
const goMixReg = /glitch\.(com|me)/gi;
const throwIfGomixDetected = cond([
[
testHTMLJS,
function({ contents }) {
if (goMixReg.test(contents)) {
throw new Error('Glitch.com or Glitch.me should not be in the code');
}
}
],
passToNext
]);
const validators = [
throwIfOpenComments,
throwIfGomixDetected,
throwIfNestedJquery,
ThrowIfUnfinishedFunction,
throwIfUnsafeConsoleCall
];
export default function validate(file) {
return validators.reduce((obs, validator) => obs.flatMap(file => {
try {
return castToObservable(validator(file));
} catch (err) {
return Observable.throw(err);
}
}), Observable.of(file))
// if no error has occured 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);
});
}