* 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
125 lines
3.0 KiB
JavaScript
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);
|
|
});
|
|
}
|