Skip to content

Commit

Permalink
Replace brace with ace-build and react-ace
Browse files Browse the repository at this point in the history
To enable removal of the CSP directive `style-src unsafe-inline`, the
ace editor version needs to be upgraded to at least 1.4.13 which
includes the global option `useStrictCSP`. In order to do this
brace has had to be replaced since it hasn't had an update in 4 years.
react-ace is chosen as a replacement as it uses ace-builds and wraps
ace in a react component. The option `useStrictCSP` is enabled,
with the required style sheets imported, where possible properties
that can be handled by react-ace are passed directly to `AceEditor`
as part of the `render()` call, otherwise customizations are left
to be handled by the codeeditor component.
  • Loading branch information
rhyshort committed Jan 19, 2022
1 parent 215192e commit 508a944
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 47 deletions.
65 changes: 38 additions & 27 deletions app/addons/components/components/codeeditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,20 @@
// the License.
import React from "react";
import FauxtonAPI from "../../../core/api";
import ace from "brace";
import "brace/ext/searchbox";
import AceEditor from "react-ace";
import ace from 'ace-builds';
import "ace-builds/src-min-noconflict/ext-searchbox";
import {StringEditModal} from './stringeditmodal';
import 'ace-builds/css/theme/idle_fingers.css';
import 'ace-builds/css/ace.css';
// importing the webpack resolver enables dynamically loading modes, which is required for syntax checking
import 'ace-builds/webpack-resolver';

require('brace/mode/javascript');
require('brace/mode/json');
require('brace/theme/idle_fingers');
require('ace-builds/src-noconflict/mode-javascript');
require('ace-builds/src-noconflict/mode-json');
require('ace-builds/src-noconflict/theme-idle_fingers');

ace.config.set("useStrictCSP", true);

export class CodeEditor extends React.Component {
static defaultProps = {
Expand Down Expand Up @@ -88,17 +95,12 @@ export class CodeEditor extends React.Component {
}
);

// suppresses an Ace editor error
this.editor.$blockScrolling = Infinity;

if (shouldUpdateCode) {
this.setValue(props.defaultCode);
}

this.editor.setShowPrintMargin(props.showPrintMargin);
this.editor.autoScrollEditorIntoView = props.autoScrollEditorIntoView;

this.editor.setOption('highlightActiveLine', this.props.highlightActiveLine);

if (this.props.setHeightToLineCount) {
this.setHeightToLineCount();
Expand All @@ -109,16 +111,6 @@ export class CodeEditor extends React.Component {
}

this.addCommands();
this.editor.getSession().setMode('ace/mode/' + props.mode);
this.editor.setTheme('ace/theme/' + props.theme);
this.editor.setFontSize(props.fontSize);
this.editor.getSession().setTabSize(2);
this.editor.getSession().setUseSoftTabs(true);

if (this.props.autoFocus) {
this.editor.focus();
}
this.editor.setReadOnly(props.disabled);
};

addCommands = () => {
Expand All @@ -128,8 +120,6 @@ export class CodeEditor extends React.Component {
};

setupEvents = () => {
this.editor.on('blur', _.bind(this.onBlur, this));
this.editor.on('change', _.bind(this.onContentChange, this));

if (this.props.stringEditModalEnabled) {
this.editor.on('changeSelection', _.bind(this.showHideEditStringGutterIcon, this));
Expand Down Expand Up @@ -179,10 +169,6 @@ export class CodeEditor extends React.Component {
componentDidMount() {
this.setupAce(this.props, true);
this.setupEvents();

if (this.props.autoFocus) {
this.editor.focus();
}
}

componentWillUnmount() {
Expand Down Expand Up @@ -357,10 +343,35 @@ export class CodeEditor extends React.Component {
this.editor.getSelection().moveCursorUp();
};

onAceLoad = (ace) => {
this.ace = ace;
};

render() {
return (
<div>
<div ref={node => this.ace = node} className="js-editor" id={this.props.id}></div>
<AceEditor
name={this.props.id}
className="js-editor"
mode={this.props.mode}
theme={this.props.theme}
onLoad={_.bind(this.onAceLoad, this)}
onBlur={_.bind(this.onBlur, this)}
onChange={_.bind(this.onContentChange, this)}
editorProps={{
$blockScrolling: Infinity,
useSoftTabs: true
}}
readOnly={this.disabled}
showPrintMargin={this.props.showPrintMargin}
highlightActiveLine={this.props.highlightActiveLine}
width="100%"
height="100%"
tabSize={2}
fontSize={this.props.fontSize}
focus={this.props.autoFocus}
setOptions={{
}}/>
<button ref={node => this.stringEditIcon = node}
className="btn string-edit"
title="Edit string"
Expand Down
8 changes: 4 additions & 4 deletions app/addons/components/components/stringeditmodal.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ import PropTypes from 'prop-types';
import React from "react";
import ReactDOM from "react-dom";
import {Modal} from "react-bootstrap";
import ace from "brace";
import ace from "ace-builds";
import Helpers from "../../documents/helpers";
require('brace/mode/javascript');
require('brace/mode/json');
require('brace/theme/idle_fingers');
require('ace-builds/src-min-noconflict/mode-javascript');
require('ace-builds/src-min-noconflict/mode-json');
require('ace-builds/src-noconflict/theme-idle_fingers');

// this appears when the cursor is over a string. It shows an icon in the gutter that opens the modal.
export class StringEditModal extends React.Component {
Expand Down
2 changes: 1 addition & 1 deletion app/addons/components/components/zenmodeoverlay.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import app from "../../../app";
import {CodeEditor} from './codeeditor';
import {Tooltip, OverlayTrigger} from 'react-bootstrap';

require('brace/theme/dawn');
require('ace-builds/src-min-noconflict/theme-dawn');

const themes = {
dark: 'idle_fingers',
Expand Down
11 changes: 4 additions & 7 deletions app/addons/documents/rev-browser/components/splitscreenarea.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,13 @@

import React from 'react';
import ReactDOM from "react-dom";
import ace from "brace";

require('brace/ext/static_highlight');
const highlight = ace.acequire('ace/ext/static_highlight');
const highlight = require('ace-builds/src-min-noconflict/ext-static_highlight');

require('brace/mode/json');
const JavaScriptMode = ace.acequire('ace/mode/json').Mode;
require('ace-builds/src-min-noconflict/mode-json');
const JavaScriptMode = require('ace-builds/src-min-noconflict/mode-javascript').Mode;

require('brace/theme/idle_fingers');
const theme = ace.acequire('ace/theme/idle_fingers');
const theme = require('ace-builds/src-noconflict/theme-idle_fingers');

export default class SplitScreenArea extends React.Component {

Expand Down
1 change: 1 addition & 0 deletions jest-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

"moduleNameMapper": {
"underscore": "lodash",
"ace-builds": "<rootDir>/node_modules/ace-builds",

"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|swf|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js",
"\\.(css|less)$": "<rootDir>/__mocks__/styleMock.js"
Expand Down
30 changes: 23 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,13 @@
},
"dependencies": {
"@webcomponents/url": "^0.7.8",
"ace-builds": "^1.4.13",
"acorn": "^6.4.2",
"ajv": "^6.12.6",
"async": "~0.2.6",
"backbone": "^1.4.0",
"base-64": "^1.0.0",
"bluebird": "^3.7.2",
"brace": "^0.11.0",
"classnames": "^2.2.6",
"clean-css": "^4.2.4",
"clipboard": "^2.0.8",
Expand All @@ -88,6 +88,7 @@
"prop-types": "^15.8.1",
"rc-slider": "^9.7.5",
"react": "^16.14.0",
"react-ace": "^9.5.0",
"react-bootstrap": "^0.31.3",
"react-dom": "^16.14.0",
"react-motion": "^0.5.0",
Expand Down

0 comments on commit 508a944

Please sign in to comment.