diff --git a/.babelrc b/.babelrc index ba557b0..156eaae 100644 --- a/.babelrc +++ b/.babelrc @@ -1,5 +1,9 @@ { - "presets": [ + "env": { + "test": { + "presets": [ ["preact-cli/babel", { "modules": "commonjs" }] - ] + ] + } + } } diff --git a/package.json b/package.json index ac496b6..bbf5bea 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "serve": "preact build && preact serve", "dev": "preact watch --template src/index.html --https --no-prerender", "lint": "eslint src", - "test": "jest ./tests", + "test": "jest", "precommit": "lint-staged" }, "eslintConfig": { @@ -53,8 +53,6 @@ }, "dependencies": { "@emmetio/codemirror-plugin": "^0.5.4", - "babel-preset-env": "^1.7.0", - "babel-preset-react": "^6.24.1", "code-blast-codemirror": "chinchang/code-blast-codemirror#web-maker", "codemirror": "^5.37.0", "copy-webpack-plugin": "^4.5.1", @@ -65,13 +63,19 @@ "preact-compat": "^3.17.0", "preact-portal": "^1.1.3", "preact-router": "^2.5.7", - "split.js": "1.3.4", - "prettier": "^1.10.2" + "prettier": "^1.10.2", + "react-inspector": "^2.3.0", + "split.js": "1.3.4" }, "jest": { "verbose": true, "setupFiles": [ - "/src/tests/__mocks__/browserMocks.js" + "/tests/__mocks__/browserMocks.js" + ], + "testRegex": "(/(__tests__|tests)/.*|(\\.|/)(test|spec))\\.jsx?$", + "testPathIgnorePatterns": [ + "/node_modules/", + "/tests/__mocks__/*" ], "testURL": "http://localhost:8080", "moduleFileExtensions": [ @@ -82,7 +86,7 @@ "node_modules" ], "moduleNameMapper": { - "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/src/tests/__mocks__/fileMock.js", + "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/tests/__mocks__/fileMock.js", "\\.(css|less|scss)$": "identity-obj-proxy", "^./style$": "identity-obj-proxy", "^preact$": "/node_modules/preact/dist/preact.min.js", diff --git a/src/components/Console.jsx b/src/components/Console.jsx index eda09bd..a044c52 100644 --- a/src/components/Console.jsx +++ b/src/components/Console.jsx @@ -1,70 +1,101 @@ -import { h } from 'preact'; -import CodeMirrorBox from './CodeMirrorBox'; +import { h, Component } from 'preact'; +import { PureComponent } from 'preact-compat'; -export function Console({ - isConsoleOpen, - onConsoleHeaderDblClick, - onClearConsoleBtnClick, - toggleConsole, - onEvalInputKeyup, - onReady -}) { - return ( - - ); +import { Inspector, chromeDark } from 'react-inspector'; + +class LogRow extends Component { + shouldComponentUpdate() { + return false; + } + render() { + const theme = { + ...chromeDark, + ...{ + OBJECT_VALUE_STRING_COLOR: 'green', + BASE_FONT_SIZE: '20px', + TREENODE_FONT_SIZE: '20px' + } + }; + + return ( + + ); + } +} + +export class Console extends Component { + componentWillUpdate(nextProps) { + if (nextProps.logs != this.props.logs) { + // Scroll down after new log dom is inserted + setTimeout(() => { + this.logContainerEl.scrollTop = this.logContainerEl.scrollHeight; + }, 1); + } + } + render() { + const { + logs, + isConsoleOpen, + onConsoleHeaderDblClick, + onClearConsoleBtnClick, + toggleConsole, + onEvalInputKeyup + } = this.props; + + return ( + + ); + } } diff --git a/src/components/ContentWrap.jsx b/src/components/ContentWrap.jsx index d984a5d..dda6c65 100644 --- a/src/components/ContentWrap.jsx +++ b/src/components/ContentWrap.jsx @@ -19,7 +19,8 @@ export default class ContentWrap extends Component { super(props); this.state = { isConsoleOpen: false, - isCssSettingsModalOpen: false + isCssSettingsModalOpen: false, + logs: [] }; this.updateTimer = null; this.updateDelay = 500; @@ -31,7 +32,6 @@ export default class ContentWrap extends Component { this.codeInPreview = { html: null, css: null, js: null }; this.cmCodes = { html: props.currentItem.html, css: '', js: '' }; this.cm = {}; - this.logCount = 0; window.onMessageFromConsole = this.onMessageFromConsole.bind(this); window.previewException = this.previewException.bind(this); @@ -42,6 +42,7 @@ export default class ContentWrap extends Component { return ( this.state.isConsoleOpen !== nextState.isConsoleOpen || this.state.isCssSettingsModalOpen !== nextState.isCssSettingsModalOpen || + this.state.logs !== nextState.logs || this.state.codeSplitSizes !== nextState.codeSplitSizes || this.state.mainSplitSizes !== nextState.mainSplitSizes || this.props.currentLayoutMode !== nextProps.currentLayoutMode || @@ -50,9 +51,6 @@ export default class ContentWrap extends Component { ); } componentDidUpdate() { - // HACK: becuase its a DOM manipulation - this.updateLogCount(); - // log('🚀', 'didupdate', this.props.currentItem); // if (this.isValidItem(this.props.currentItem)) { // this.refreshEditor(); @@ -288,11 +286,10 @@ export default class ContentWrap extends Component { ]).then(() => this.setPreviewContent(true)); } applyCodemirrorSettings(prefs) { - if (window.consoleEl) { - window.consoleEl.querySelector( - '.CodeMirror' - ).style.fontSize = `${parseInt(prefs.fontSize, 10)}px`; - } + document.documentElement.style.setProperty( + '--code-font-size', + `${parseInt(prefs.fontSize, 10)}px` + ); // Replace correct css file in LINK tags's href if (prefs.editorTheme) { @@ -556,46 +553,21 @@ export default class ContentWrap extends Component { }, 500); } - updateLogCount() { - if (window.logCountEl) { - logCountEl.textContent = this.logCount; - } - } - onMessageFromConsole() { - /* eslint-disable no-param-reassign */ - [...arguments].forEach(arg => { + const logs = [...arguments].map(arg => { if ( arg && arg.indexOf && arg.indexOf('filesystem:chrome-extension') !== -1 ) { - arg = arg.replace( + return arg.replace( /filesystem:chrome-extension.*\.js:(\d+):*(\d*)/g, 'script $1:$2' ); } - try { - this.consoleCm.replaceRange( - arg + - ' ' + - ((arg + '').match(/\[object \w+]/) ? JSON.stringify(arg) : '') + - '\n', - { - line: Infinity - } - ); - } catch (e) { - this.consoleCm.replaceRange('🌀\n', { - line: Infinity - }); - } - this.consoleCm.scrollTo(0, Infinity); - this.logCount++; + return arg; }); - this.updateLogCount(); - - /* eslint-enable no-param-reassign */ + this.setState({ logs: [...this.state.logs, ...logs] }); } previewException(error) { @@ -615,9 +587,7 @@ export default class ContentWrap extends Component { this.toggleConsole(); } clearConsole() { - this.consoleCm.setValue(''); - this.logCount = 0; - this.updateLogCount(); + this.setState({ logs: [] }); } clearConsoleBtnClickHandler() { this.clearConsole(); @@ -864,6 +834,7 @@ export default class ContentWrap extends Component { allowfullscreen /> (this.consoleCm = el)} /> { + const logs = [...arguments].map(arg => { if ( arg && arg.indexOf && arg.indexOf('filesystem:chrome-extension') !== -1 ) { - arg = arg.replace( + return arg.replace( /filesystem:chrome-extension.*\.js:(\d+):*(\d*)/g, 'script $1:$2' ); } - try { - this.consoleCm.replaceRange( - arg + - ' ' + - ((arg + '').match(/\[object \w+]/) ? JSON.stringify(arg) : '') + - '\n', - { - line: Infinity - } - ); - } catch (e) { - this.consoleCm.replaceRange('🌀\n', { - line: Infinity - }); - } - this.consoleCm.scrollTo(0, Infinity); - this.logCount++; + return arg; }); - this.updateLogCount(); - - /* eslint-enable no-param-reassign */ + this.setState({ logs: [...this.state.logs, ...logs] }); } previewException(error) { @@ -552,9 +527,7 @@ export default class ContentWrapFiles extends Component { this.toggleConsole(); } clearConsole() { - this.consoleCm.setValue(''); - this.logCount = 0; - this.updateLogCount(); + this.setState({ logs: [] }); } clearConsoleBtnClickHandler() { this.clearConsole(); @@ -654,6 +627,7 @@ export default class ContentWrapFiles extends Component { allowfullscreen /> (this.consoleCm = el)} /> diff --git a/src/components/UserCodeMirror.jsx b/src/components/UserCodeMirror.jsx index ed58f34..006907a 100644 --- a/src/components/UserCodeMirror.jsx +++ b/src/components/UserCodeMirror.jsx @@ -38,9 +38,6 @@ emmet(CodeMirror); export default class UserCodeMirror extends Component { componentDidMount() { this.initEditor(); - this.textarea.parentNode.querySelector( - '.CodeMirror' - ).style.fontSize = `${parseInt(this.props.prefs.fontSize, 10)}px`; } shouldComponentUpdate(nextProps) { if (nextProps.prefs !== this.props.prefs) { @@ -60,12 +57,6 @@ export default class UserCodeMirror extends Component { this.cm.setOption('keyMap', prefs.keymap); this.cm.setOption('lineWrapping', prefs.lineWrap); - if (this.textarea) { - this.textarea.parentNode.querySelector( - '.CodeMirror' - ).style.fontSize = `${parseInt(prefs.fontSize, 10)}px`; - } - this.cm.refresh(); } diff --git a/src/components/app.jsx b/src/components/app.jsx index 401d3c3..f414d1d 100644 --- a/src/components/app.jsx +++ b/src/components/app.jsx @@ -309,7 +309,11 @@ export default class App extends Component { item = { ...item, files: assignFilePaths([ - { name: 'index.html', content: '' }, + { + name: 'index.html', + content: + 'hello\n\n' + }, { name: 'styles', isFolder: true, diff --git a/src/style.css b/src/style.css index 54b5fea..6cb7d31 100644 --- a/src/style.css +++ b/src/style.css @@ -1418,12 +1418,6 @@ body > #demo-frame { transform: translateY(calc(100% - 29px)); } -.console .CodeMirror { - flex-grow: 1; - /* flex-basis of 0 to trigger overflow https://stackoverflow.com/a/52489012/891962 */ - flex-basis: 0; -} - .console__log { flex: 1; display: flex; @@ -1442,6 +1436,20 @@ body > #demo-frame { .console:not(.is-minimized) .code-wrap__header { cursor: ns-resize; } +.console__items { + padding: 0; + margin: 0; + overflow: auto; + flex-grow: 1; + /* flex-basis of 0 to trigger overflow https://stackoverflow.com/a/52489012/891962 */ + flex-basis: 0; +} +.console__items li { + font-size: var(--code-font-size) !important; + line-height: inherit !important; + padding: 5px 10px !important; + border-bottom: 1px solid rgba(255, 255, 255, 0.05); +} /* Detached mode */ diff --git a/src/tests/__mocks__/browserMocks.js b/tests/__mocks__/browserMocks.js similarity index 100% rename from src/tests/__mocks__/browserMocks.js rename to tests/__mocks__/browserMocks.js diff --git a/src/tests/__mocks__/fileMocks.js b/tests/__mocks__/fileMocks.js similarity index 72% rename from src/tests/__mocks__/fileMocks.js rename to tests/__mocks__/fileMocks.js index 82f0d91..d777aaa 100644 --- a/src/tests/__mocks__/fileMocks.js +++ b/tests/__mocks__/fileMocks.js @@ -1,3 +1,3 @@ // This fixed an error related to the CSS and loading gif breaking my Jest test -// See https://facebook.github.io/jest/docs/en/webpack.html#handling-static-assets -module.exports = 'test-file-stub'; \ No newline at end of file +// See https://facebook.github.io/jest/docs/en/webpack.html#handling-static-assets +module.exports = 'test-file-stub'; diff --git a/src/tests/fileUtils.test.js b/tests/fileUtils.test.js similarity index 99% rename from src/tests/fileUtils.test.js rename to tests/fileUtils.test.js index 439dd86..0285a3f 100644 --- a/src/tests/fileUtils.test.js +++ b/tests/fileUtils.test.js @@ -6,7 +6,7 @@ import { removeFileAtPath, getParentPath, getExtensionFromFileName -} from '../fileUtils'; +} from '../src/fileUtils'; function getNestedFiles() { return [ diff --git a/src/tests/footer.test.js b/tests/footer.test.js similarity index 90% rename from src/tests/footer.test.js rename to tests/footer.test.js index 55c2827..ec0edc9 100644 --- a/src/tests/footer.test.js +++ b/tests/footer.test.js @@ -1,5 +1,5 @@ import { h, Component } from 'preact'; -import Footer from '../components/Footer'; +import Footer from '../src/components/Footer'; import { Link } from 'preact-router/match'; // See: https://github.com/mzgoddard/preact-render-spy import { shallow, deep } from 'preact-render-spy'; diff --git a/yarn.lock b/yarn.lock index fc95c24..8d65c7f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5088,6 +5088,10 @@ is-directory@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" +is-dom@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/is-dom/-/is-dom-1.0.9.tgz#483832d52972073de12b9fe3f60320870da8370d" + is-dotfile@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" @@ -7973,6 +7977,13 @@ rc@^1.0.1, rc@^1.1.2, rc@^1.1.6, rc@^1.1.7: minimist "^1.2.0" strip-json-comments "~2.0.1" +react-inspector@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/react-inspector/-/react-inspector-2.3.0.tgz#fc9c1d38ab687fc0d190dcaf133ae40158968fc8" + dependencies: + babel-runtime "^6.26.0" + is-dom "^1.0.9" + read-all-stream@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/read-all-stream/-/read-all-stream-3.1.0.tgz#35c3e177f2078ef789ee4bfafa4373074eaef4fa"