Initial work on new framework

This commit is contained in:
Berkeley Martinez
2016-05-06 13:20:18 -07:00
parent b6f9cfdf71
commit 861f89683b
7 changed files with 220 additions and 3 deletions

View File

@ -0,0 +1,21 @@
import loopProtect from 'loopProtect';
loopProtect.hit = function hit(line) {
var err = 'Error: 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.';
console.error(err);
};
// Observable[Observable[File]]::addLoopProtect() => Observable[String]
export default function addLoopProtect() {
const source = this;
return source.map(files$ => files$.map(file => {
if (file.extname === 'js') {
file.contents = loopProtect(file.contents);
}
return file;
}));
}

View File

@ -0,0 +1,13 @@
import createTypes from '../../common/app/utils/create-types';
const filterTypes = [
execute
];
export default function executeChallengeSaga(action$, getState) {
return action$
.filter(({ type }) => filterTypes.some(_type => _type === type))
.map(action => {
if (action.type === execute) {
const editors = getState().editors;
}
})
}

View File

@ -0,0 +1,92 @@
// originally base off of https://github.com/gulpjs/vinyl
import path from 'path';
import replaceExt from 'replace-ext';
export default class File {
constructor({
path,
history = [],
base,
contents = ''
} = {}) {
// Record path change
this.history = path ? [path] : history;
this.base = base || this.cwd;
this.contents = contents;
this._isPolyVinyl = true;
this.error = null;
}
static isPolyVinyl = function(file) {
return file && file._isPolyVinyl === true || false;
};
isEmpty() {
return !this._contents;
}
get contents() {
return this._contents;
}
set contents(val) {
if (typeof val !== 'string') {
throw new TypeError('File.contents can only a String');
}
this._contents = val;
}
get basename() {
if (!this.path) {
throw new Error('No path specified! Can not get basename.');
}
return path.basename(this.path);
}
set basename(basename) {
if (!this.path) {
throw new Error('No path specified! Can not set basename.');
}
this.path = path.join(path.dirname(this.path), basename);
}
get extname() {
if (!this.path) {
throw new Error('No path specified! Can not get extname.');
}
return path.extname(this.path);
}
set extname(extname) {
if (!this.path) {
throw new Error('No path specified! Can not set extname.');
}
this.path = replaceExt(this.path, extname);
}
get path() {
return this.history[this.history.length - 1];
}
set path(path) {
if (typeof path !== 'string') {
throw new TypeError('path should be string');
}
// Record history only when path changed
if (path && path !== this.path) {
this.history.push(path);
}
}
get error() {
return this._error;
}
set error(err) {
if (typeof err !== 'object') {
throw new TypeError('error must be an object or null');
}
this.error = err;
}
}

View File

View File

@ -0,0 +1,89 @@
import { helpers, Observable } from 'rx';
const throwForJsHtml = {
extname: /js|html/,
throwers: [
{
name: 'multiline-comment',
description: 'Detect if a JS multi-line comment is left open',
thrower: function checkForComments({ content }) {
const openingComments = content.match(/\/\*/gi);
const closingComments = content.match(/\*\//gi);
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,
thrower: function checkForNestedJquery({ content }) {
if (content.match(this.detectUnsafeJQ)) {
throw new Error('Unsafe $($)');
}
}
}, {
name: 'unfinished-function',
description: 'lonely function keywords breaks browsers',
detectFunctionCall: /function\s*?\(|function\s+\w+\s*?\(/gi,
thower: function checkForUnfinishedFunction({ content: code }) {
if (
code.match(/function/g) &&
!code.match(this.detectFunctionCall)
) {
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,
thrower: function checkForUnsafeConsole({ content }) {
if (content.match(this.detectUnsafeConsoleCall)) {
throw new Error('Invalid if (null) console.log(1); detected');
}
}
}
]
};
export default function pretester() {
const source = this;
return source.map(file$ => file$.flatMap(file => {
if (!throwForJsHtml.extname.test(file.extname)) {
return Observable.just(file);
}
return Observable.from(throwForJsHtml.throwers)
.flatMap(({ thrower }) => {
try {
let finalObs;
const maybeObservableOrPromise = thrower(file);
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);
});
}));
}

View File

@ -81,6 +81,7 @@
"pmx": "~0.6.2",
"react": "^15.0.2",
"react-bootstrap": "~0.29.4",
"react-codemirror": "^0.2.6",
"react-css-transition-replace": "^1.2.0-beta",
"react-dom": "^15.0.2",
"react-fontawesome": "^0.3.3",
@ -97,6 +98,7 @@
"redux-actions": "^0.9.1",
"redux-epic": "^0.1.1",
"redux-form": "^5.2.3",
"replace-ext": "0.0.1",
"request": "^2.65.0",
"reselect": "^2.0.2",
"rx": "^4.0.0",

View File

@ -39,16 +39,16 @@ module.exports = {
]
},
externals: {
'codemirror': 'CodeMirror'
codemirror: 'CodeMirror'
},
plugins: [
new webpack.optimize.DedupePlugin(),
new webpack.optimize.OccurenceOrderPlugin(true),
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': JSON.stringify(__DEV__ ? 'development' : 'production')
NODE_ENV: JSON.stringify(__DEV__ ? 'development' : 'production')
},
'__DEVTOOLS__': !__DEV__
__DEVTOOLS__: !__DEV__
})
]
};