* feat(rechallenge): Retool challenge framework * fix(code-storage): should use setContent not updateContent * fix(rechallenge): fix context issue and temporal zone of death * fix(rechallenge): Fix frame sources for user code * fix(polyvinyl): Set should ignore source and transform should keep track of source * fix(rechallenge): Missing return statement causing issues
		
			
				
	
	
		
			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 '../../common/app/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);
 | 
						|
    });
 | 
						|
}
 |