* 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
		
			
				
	
	
		
			109 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			109 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| import cond from 'lodash/cond';
 | |
| import identity from 'lodash/identity';
 | |
| import matchesProperty from 'lodash/matchesProperty';
 | |
| import stubTrue from 'lodash/stubTrue';
 | |
| import conforms from 'lodash/conforms';
 | |
| 
 | |
| import * as babel from 'babel-core';
 | |
| import presetEs2015 from 'babel-preset-es2015';
 | |
| import presetReact from 'babel-preset-react';
 | |
| import { Observable } from 'rx';
 | |
| /* eslint-disable import/no-unresolved */
 | |
| import loopProtect from 'loop-protect';
 | |
| /* eslint-enable import/no-unresolved */
 | |
| 
 | |
| import {
 | |
|   transformHeadTailAndContents,
 | |
|   setContent
 | |
| } from '../../common/utils/polyvinyl.js';
 | |
| import castToObservable from '../../common/app/utils/cast-to-observable.js';
 | |
| 
 | |
| const babelOptions = { presets: [ presetEs2015, presetReact ] };
 | |
| loopProtect.hit = function hit(line) {
 | |
|   var err = 'Exiting potential infinite loop at line ' +
 | |
|     line +
 | |
|     '. To disable loop protection, write: \n\/\/ noprotect\nas the first ' +
 | |
|     'line. Beware that if you do have an infinite loop in your code, ' +
 | |
|     'this will crash your browser.';
 | |
|   throw new Error(err);
 | |
| };
 | |
| 
 | |
| // const sourceReg =
 | |
| //  /(<!-- fcc-start-source -->)([\s\S]*?)(?=<!-- fcc-end-source -->)/g;
 | |
| const HTML$JSReg = /html|js/;
 | |
| const console$logReg = /(?:\b)console(\.log\S+)/g;
 | |
| const NBSPReg = new RegExp(String.fromCharCode(160), 'g');
 | |
| 
 | |
| const testHTMLJS = conforms({ ext: (ext) => HTML$JSReg.test(ext) });
 | |
| const testJS = matchesProperty('ext', 'js');
 | |
| 
 | |
| // if shouldProxyConsole then we change instances of console log
 | |
| // to `window.__console.log`
 | |
| // this let's us tap into logging into the console.
 | |
| // currently we only do this to the main window and not the test window
 | |
| export function proxyLoggerTransformer(file) {
 | |
|   return transformHeadTailAndContents(
 | |
|     (source) => (
 | |
|       source.replace(console$logReg, (match, methodCall) => {
 | |
|       return 'window.__console' + methodCall;
 | |
|     })),
 | |
|     file
 | |
|   );
 | |
| }
 | |
| 
 | |
| export const addLoopProtect = cond([
 | |
|   [
 | |
|     testHTMLJS,
 | |
|     function(file) {
 | |
|       const _contents = file.contents.toLowerCase();
 | |
|       if (file.ext === 'html' && !_contents.indexOf('<script>') !== -1) {
 | |
|         // No JavaScript in user code, so no need for loopProtect
 | |
|         return file;
 | |
|       }
 | |
|       return setContent(loopProtect(file.contents), file);
 | |
|     }
 | |
|   ],
 | |
|   [ stubTrue, identity ]
 | |
| ]);
 | |
| export const replaceNBSP = cond([
 | |
|   [
 | |
|     testHTMLJS,
 | |
|     function(file) {
 | |
|       return setContent(
 | |
|       file.contents.replace(NBSPReg, ' '),
 | |
|         file
 | |
|     );
 | |
|     }
 | |
|   ],
 | |
|   [ stubTrue, identity ]
 | |
| ]);
 | |
| 
 | |
| export const babelTransformer = cond([
 | |
|   [
 | |
|     testJS,
 | |
|     function(file) {
 | |
|       const result = babel.transform(file.contents, babelOptions);
 | |
|       return setContent(
 | |
|       result.code,
 | |
|         file
 | |
|     );
 | |
|     }
 | |
|   ],
 | |
|   [ stubTrue, identity ]
 | |
| ]);
 | |
| 
 | |
| export const _transformers = [
 | |
|   addLoopProtect,
 | |
|   replaceNBSP,
 | |
|   babelTransformer
 | |
| ];
 | |
| 
 | |
| export function applyTransformers(file, transformers = _transformers) {
 | |
|   return transformers.reduce(
 | |
|     (obs, transformer) => {
 | |
|       return obs.flatMap(file => castToObservable(transformer(file)));
 | |
|     },
 | |
|     Observable.of(file)
 | |
|   );
 | |
| }
 |