Files
freeCodeCamp/common/app/routes/Challenges/rechallenge/builders.js
Berkeley Martinez 2e410330f1 Feat(Challenges): no js preview (#16149)
* fix(files): Decouple files from challenges

* feat(server/react): Remove action logger

use redux remote devtools instead!

* feat(Challenges): Disable js on edit, enable on execute

* feat(Challenge/Preview): Show message when js is disabled

* refactor(frameEpic): Reduce code by using lodash

* feat(frameEpic): Disable js in preview by state

* feat(frameEpic): Colocate epic in Challenges/redux

* refactor(ExecuteChallengeEpic): CoLocated with Challenges

* refactor(executeChallengesEpic): Separate tests from main logic

* feat(Challenge/Preview): Update main on edit

* feat(frameEpuc): Replace frame on edit/execute

This allows for sandbox to work properly

* fix(Challenges/Utils): Require utisl

* revert(frameEpic): Hoist function to mount code in frame

* fix(frameEpic): Ensure new frame is given classname

* feat(executeChallenge): Update main on code unlocked

* fix(frameEpic): Filter out empty test message

* fix(Challenge/Preview): Remove unnessary quote in classname

* feat(codeStorageEpic): Separate localstorage from solutions loading

* fix(fetchUser): Merge user actions into one

prefer many effects from one action over one action to one effect

* fix(themes): Centralize theme utils and defs

* fix(entities.user): Fix user reducer namespacing

* feat(frame): Refactor frameEpic to util

* feat(Challenges.redux): Should not attempt to update main from storage

* fix(loadPreviousChallengeEpic): Refactor for RFR

* fix(Challenges.Modern): Show preview plane
2017-12-07 18:13:19 -06:00

100 lines
2.5 KiB
JavaScript

import { Observable } from 'rx';
import cond from 'lodash/cond';
import flow from 'lodash/flow';
import identity from 'lodash/identity';
import matchesProperty from 'lodash/matchesProperty';
import partial from 'lodash/partial';
import stubTrue from 'lodash/stubTrue';
import {
fetchScript,
fetchLink
} from '../utils/fetch-and-cache.js';
import {
compileHeadTail,
setExt,
transformContents
} from '../../../../utils/polyvinyl';
const htmlCatch = '\n<!--fcc-->\n';
const jsCatch = '\n;/*fcc*/\n';
const wrapInScript = partial(transformContents, (content) => (
`${htmlCatch}<script>${content}${jsCatch}</script>`
));
const wrapInStyle = partial(transformContents, (content) => (
`${htmlCatch}<style>${content}</style>`
));
const setExtToHTML = partial(setExt, 'html');
const padContentWithJsCatch = partial(compileHeadTail, jsCatch);
const padContentWithHTMLCatch = partial(compileHeadTail, htmlCatch);
export const jsToHtml = cond([
[
matchesProperty('ext', 'js'),
flow(padContentWithJsCatch, wrapInScript, setExtToHTML)
],
[ stubTrue, identity ]
]);
export const cssToHtml = cond([
[
matchesProperty('ext', 'css'),
flow(padContentWithHTMLCatch, wrapInStyle, setExtToHTML)
],
[ stubTrue, identity ]
]);
// FileStream::concactHtml(
// required: [ ...Object ]
// ) => Observable[{ build: String, sources: Dictionary }]
export function concactHtml(required) {
const source = this.shareReplay();
const sourceMap = source
.flatMap(files => files.reduce((sources, file) => {
sources[file.name] = file.source || file.contents;
return sources;
}, {}));
const head = Observable.from(required)
.flatMap(required => {
if (required.src) {
return fetchScript(required);
}
if (required.link) {
return fetchLink(required);
}
return Observable.just('');
})
.reduce((head, required) => head + required, '')
.map(head => `<head>${head}</head>`);
const body = source
.flatMap(file => file.reduce((body, file) => {
return body + file.contents + htmlCatch;
}, ''))
.map(source => `
<body style='margin:8px;'>
<!-- fcc-start-source -->
${source}
<!-- fcc-end-source -->
</body>
`);
return Observable
.combineLatest(
head,
body,
fetchScript({
src: '/js/frame-runner.js',
crossDomain: false,
cacheBreaker: true
}),
sourceMap,
(head, body, frameRunner, sources) => ({
build: head + body + frameRunner,
sources
})
);
}