Initial work on new framework
This commit is contained in:
21
client/new-framework/add-loop-protect.js
Normal file
21
client/new-framework/add-loop-protect.js
Normal 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;
|
||||||
|
}));
|
||||||
|
}
|
13
client/new-framework/execute-challenge-saga.js
Normal file
13
client/new-framework/execute-challenge-saga.js
Normal 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;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
92
client/new-framework/polyvinyl.js
Normal file
92
client/new-framework/polyvinyl.js
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
0
client/new-framework/redux/types.js
Normal file
0
client/new-framework/redux/types.js
Normal file
89
client/new-framework/throw-unsafe-code.js
Normal file
89
client/new-framework/throw-unsafe-code.js
Normal 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);
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
}
|
@ -81,6 +81,7 @@
|
|||||||
"pmx": "~0.6.2",
|
"pmx": "~0.6.2",
|
||||||
"react": "^15.0.2",
|
"react": "^15.0.2",
|
||||||
"react-bootstrap": "~0.29.4",
|
"react-bootstrap": "~0.29.4",
|
||||||
|
"react-codemirror": "^0.2.6",
|
||||||
"react-css-transition-replace": "^1.2.0-beta",
|
"react-css-transition-replace": "^1.2.0-beta",
|
||||||
"react-dom": "^15.0.2",
|
"react-dom": "^15.0.2",
|
||||||
"react-fontawesome": "^0.3.3",
|
"react-fontawesome": "^0.3.3",
|
||||||
@ -97,6 +98,7 @@
|
|||||||
"redux-actions": "^0.9.1",
|
"redux-actions": "^0.9.1",
|
||||||
"redux-epic": "^0.1.1",
|
"redux-epic": "^0.1.1",
|
||||||
"redux-form": "^5.2.3",
|
"redux-form": "^5.2.3",
|
||||||
|
"replace-ext": "0.0.1",
|
||||||
"request": "^2.65.0",
|
"request": "^2.65.0",
|
||||||
"reselect": "^2.0.2",
|
"reselect": "^2.0.2",
|
||||||
"rx": "^4.0.0",
|
"rx": "^4.0.0",
|
||||||
|
@ -39,16 +39,16 @@ module.exports = {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
externals: {
|
externals: {
|
||||||
'codemirror': 'CodeMirror'
|
codemirror: 'CodeMirror'
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
new webpack.optimize.DedupePlugin(),
|
new webpack.optimize.DedupePlugin(),
|
||||||
new webpack.optimize.OccurenceOrderPlugin(true),
|
new webpack.optimize.OccurenceOrderPlugin(true),
|
||||||
new webpack.DefinePlugin({
|
new webpack.DefinePlugin({
|
||||||
'process.env': {
|
'process.env': {
|
||||||
'NODE_ENV': JSON.stringify(__DEV__ ? 'development' : 'production')
|
NODE_ENV: JSON.stringify(__DEV__ ? 'development' : 'production')
|
||||||
},
|
},
|
||||||
'__DEVTOOLS__': !__DEV__
|
__DEVTOOLS__: !__DEV__
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user