Fix: Babel errors in the browser console for user code (#99)

These changes will print out babel errors to the console and will help to not crash the app when bad/unfinished code is in the editor.
This commit is contained in:
Stuart Taylor
2018-05-26 21:32:40 +01:00
committed by Mrugesh Mohapatra
parent d004171593
commit a62e459c2a
5 changed files with 48 additions and 43 deletions

View File

@ -82,10 +82,12 @@ function tryTransform(wrap = identity) {
return function transformWrappedPoly(source) {
const result = attempt(wrap, source);
if (isError(result)) {
const friendlyError = `${result}`
.match(/[\w\W]+?\n/)[0]
.replace(' unknown:', '');
throw new Error(friendlyError);
console.error(result);
// note(Bouncey): Error thrown here to collapse the build pipeline
// At the minute, it will not bubble up
// We collapse the pipeline so the app doesn't fall over trying
// parse bad code (syntax/type errors etc...)
throw new Error();
}
return result;
};
@ -110,12 +112,7 @@ export const sassTransformer = cond([
[stubTrue, identity]
]);
export const _transformers = [
// addLoopProtectHtmlJsJsx,
replaceNBSP,
babelTransformer,
sassTransformer
];
export const _transformers = [replaceNBSP, babelTransformer, sassTransformer];
export function applyTransformers(file, transformers = _transformers) {
return transformers.reduce((obs, transformer) => {

View File

@ -126,7 +126,7 @@ export default function completionEpic(action$, { getState }) {
const { nextChallengePath, introPath, challengeType } = meta;
const next = of(push(introPath ? introPath : nextChallengePath));
const closeChallengeModal = of(closeModal('completion'));
let submitter = () => of({type: 'no-user-signed-in'});
let submitter = () => of({ type: 'no-user-signed-in' });
if (
!(challengeType in submitTypes) ||
!(submitTypes[challengeType] in submitters)
@ -140,7 +140,6 @@ export default function completionEpic(action$, { getState }) {
submitter = submitters[submitTypes[challengeType]];
}
return submitter(type, state).pipe(
concat(next),
concat(closeChallengeModal),

View File

@ -9,7 +9,12 @@ import {
map,
filter,
pluck,
concat
concat,
tap,
catchError,
ignoreElements,
startWith,
delay
} from 'rxjs/operators';
import { ofType, combineEpics } from 'redux-observable';
import { overEvery, isString } from 'lodash';
@ -22,7 +27,8 @@ import {
updateConsole,
checkChallenge,
updateTests,
disableJSOnError
disableJSOnError,
isJSEnabledSelector
} from './';
import { buildFromFiles, buildBackendChallenge } from '../utils/build';
import {
@ -49,10 +55,11 @@ function updateMainEpic(actions, { getState }, { document }) {
),
debounceTime(executeDebounceTimeout),
switchMap(() =>
buildFromFiles(getState(), true)
.map(frameMain)
.ignoreElements()
.catch(() => of({ type: 'NULL' }))
buildFromFiles(getState(), true).pipe(
map(frameMain),
ignoreElements(),
catchError(err => of(disableJSOnError(err)))
)
)
);
return merge(buildAndFrameMain, proxyLogger.map(updateConsole));
@ -76,40 +83,41 @@ function executeChallengeEpic(action$, { getState }, { document }) {
const postTests = of(
updateConsole('// tests completed'),
checkChallenge(checkChallengePayload)
).delay(250);
// run the tests within the test iframe
return runTestsInTestFrame(document, tests)
.flatMap(tests => {
).pipe(delay(250));
return runTestsInTestFrame(document, tests).pipe(
switchMap(tests => {
return from(tests).pipe(
map(({ message }) => message),
filter(overEvery(isString, Boolean)),
map(updateConsole),
concat(of(updateTests(tests)))
);
})
.concat(postTests);
}),
concat(postTests)
);
})
);
const buildAndFrameChallenge = action$.pipe(
ofType(types.executeChallenge),
debounceTime(executeDebounceTimeout),
// if isCodeLocked do not run challenges
// .filter(() => !codeLockedSelector(getState()))
filter(() => isJSEnabledSelector(getState())),
switchMap(() => {
const state = getState();
const { challengeType } = challengeMetaSelector(state);
if (challengeType === backend) {
return buildBackendChallenge(state)
.do(frameTests)
.ignoreElements()
.startWith(initConsole('// running test'))
.catch(err => disableJSOnError(err));
return buildBackendChallenge(state).pipe(
tap(frameTests),
ignoreElements(),
startWith(initConsole('// running test')),
catchError(err => of(disableJSOnError(err)))
);
}
return buildFromFiles(state, false)
.do(frameTests)
.ignoreElements()
.startWith(initConsole('// running test'))
.catch(err => disableJSOnError(err));
return buildFromFiles(state, false).pipe(
tap(frameTests),
ignoreElements(),
startWith(initConsole('// running test')),
catchError(err => of(disableJSOnError(err)))
);
})
);
return merge(buildAndFrameChallenge, challengeResults);

View File

@ -104,10 +104,13 @@ export const updateSuccessMessage = createAction(types.updateSuccessMessage);
export const lockCode = createAction(types.lockCode);
export const unlockCode = createAction(types.unlockCode);
export const disableJSOnError = createAction(types.disableJSOnError, err => {
console.error(err);
return {};
});
export const disableJSOnError = createAction(
types.disableJSOnError,
({ payload }) => {
console.error(JSON.stringify(payload));
return null;
}
);
export const storedCodeFound = createAction(types.storedCodeFound);
export const noStoredCodeFound = createAction(types.noStoredCodeFound);

View File

@ -8,7 +8,6 @@ import {
challengeFilesSelector,
isJSEnabledSelector,
challengeMetaSelector,
disableJSOnError,
backendFormValuesSelector
} from '../redux';
import {
@ -55,8 +54,7 @@ export function buildFromFiles(state, shouldProxyConsole) {
::pipe(shouldProxyConsole ? proxyLoggerTransformer : identity)
::pipe(jsToHtml)
::pipe(cssToHtml)
::concatHtml(finalRequires, template)
.catch(err => disableJSOnError(err));
::concatHtml(finalRequires, template);
}
export function buildBackendChallenge(state) {