diff --git a/ivette/.eslintrc.js b/ivette/.eslintrc.js index 99d874dc84fbd8cd595d7b37c558c2af268cb6f6..7d1798cfb90ad18e1d1702b6662b11d21b9a2b1d 100644 --- a/ivette/.eslintrc.js +++ b/ivette/.eslintrc.js @@ -5,14 +5,18 @@ module.exports = { '@typescript-eslint', ], extends: [ + 'airbnb-typescript', 'eslint:recommended', 'plugin:react/recommended', 'plugin:react-hooks/recommended', 'plugin:@typescript-eslint/eslint-recommended', 'plugin:@typescript-eslint/recommended', ], + parserOptions: { + project: './tsconfig.json', + }, rules: { - "react/display-name": 'off', + "react/display-name": "off", // Allow type any, even if it should be avoided "@typescript-eslint/no-explicit-any": "off", // Allow functions without return type, even if exported function should have one @@ -31,5 +35,34 @@ module.exports = { "error", { "destructuring": "all", "ignoreReadBeforeAssign": false } ], + // Allow declarations with char '_' as prefix/suffix + "no-underscore-dangle": "off", + // Allow return statements even if not strictly needed + "no-useless-return": "off", + // Just warn about shadowing concerning variables + "no-shadow": "warn", + "lines-between-class-members": [ + "error", "always", { "exceptAfterSingleLine": true } + ], + // Allow ++/-- operators only in for-loops + "no-plusplus": ["error", { "allowForLoopAfterthoughts": true }], + // Just warn about simple promise rejections + "prefer-promise-reject-errors": "warn", + // Force code to 80 columns, but for trailing comments + "max-len": ["error", { "code": 80, "ignoreTrailingComments": true, }], + // Allow more than one class per file, even if not a good practice + "max-classes-per-file": "off", + // Allow assignment in return statements only with outer parenthesis + "no-return-assign": ["error", "except-parens" ], + // Allow single line expressions in react + "react/jsx-one-expression-per-line": "off", + // Allow all sorts of linebreaking for operators + "operator-linebreak": "off", + // Force curly brackets on newline if some item is + "object-curly-newline": ["error", { "multiline": true }], + // Allow non-destructed assignments + "react/destructuring-assignment": "off", + // Allow console errors and warnings + "no-console": ["error", { allow: ["warn", "error"] }], } }; \ No newline at end of file diff --git a/ivette/package.json b/ivette/package.json index 4f674a7ee55d28fc5d0f1bf59ffbd578886decc7..6e6fdbb307b69d351ef934e596df1bb51d33dc79 100644 --- a/ivette/package.json +++ b/ivette/package.json @@ -35,6 +35,10 @@ "electron-devtools-installer": "^2.2.4", "electron-webpack": "^2.8.2", "eslint": "^6.8.0", + "eslint-config-airbnb": "^18.1.0", + "eslint-config-airbnb-typescript": "^7.2.1", + "eslint-plugin-import": "^2.20.2", + "eslint-plugin-jsx-a11y": "^6.2.3", "eslint-plugin-react": "^7.19.0", "eslint-plugin-react-hooks": "^3.0.0", "html-loader": "1.0.0-alpha.0", diff --git a/ivette/src/dome/src/renderer/text/buffers.js b/ivette/src/dome/src/renderer/text/buffers.js index 5b67d43eceaa172baffb9c121546b4236940ec71..4711a6a063b6e31bc7372f842fea2525a189381d 100644 --- a/ivette/src/dome/src/renderer/text/buffers.js +++ b/ivette/src/dome/src/renderer/text/buffers.js @@ -68,7 +68,7 @@ class Proxy { */ -export class Buffer extends Emitter { +export class RichTextBuffer extends Emitter { /** @param {object} [props] - Constructor properties (see below) @@ -453,6 +453,6 @@ is blocked. // -------------------------------------------------------------------------- -export default { Buffer }; +export default { RichTextBuffer }; // -------------------------------------------------------------------------- diff --git a/ivette/src/frama-c/LabViews.tsx b/ivette/src/frama-c/LabViews.tsx index 660f46cd929c64e6cd6b49038dd27628c31e552a..db23aa4b00565938526d992d9cfecb16918d93a6 100644 --- a/ivette/src/frama-c/LabViews.tsx +++ b/ivette/src/frama-c/LabViews.tsx @@ -18,7 +18,7 @@ import { Label } from 'dome/controls/labels'; import { Icon } from 'dome/controls/icons'; import { Item as ItemToRender, - Render as RenderItem + Render as RenderItem, } from 'dome/layout/dispatch'; import './labviews.css'; @@ -41,14 +41,19 @@ class Library { commit() { if (!_.isEqual(this.collection, this.virtual)) { - this.collection = Object.assign({}, this.virtual); + this.collection = { ...this.virtual }; this.items = _.sortBy(this.collection, ['order', 'id']); this.modified = false; Dome.emit('labview.library'); } } - useItem(id: string, gcontext: any, path: any[], props: { rank: undefined; group: any }) { + useItem( + id: string, + gcontext: any, + path: any[], + props: { rank: undefined; group: any }, + ) { if (!this.modified) { this.modified = true; setImmediate(() => this.commit()); @@ -59,19 +64,23 @@ class Library { : path.slice(0, -1).concat([props.rank]); const group = props.group || gcontext; const collection: any = this.virtual; - collection[id] = Object.assign({ id, order, group }, props); + collection[id] = { id, order, group, ...props }; return (): boolean => delete collection[id]; } - } // -------------------------------------------------------------------------- // --- Library Components // -------------------------------------------------------------------------- -const isItemId = (fd: string, id: string) => id.startsWith(fd) && id[fd.length] === '.'; -const getItemId = (fd: string, id: string) => isItemId(fd, id) ? id.substring(fd.length + 1) : undefined; -const getItems = (items: any[], fd: string) => items.filter((item) => isItemId(fd, item.id)); +const isItemId = + (fd: string, id: string) => id.startsWith(fd) && id[fd.length] === '.'; +const getItemId = + (fd: string, id: string) => ( + isItemId(fd, id) ? id.substring(fd.length + 1) : undefined + ); +const getItems = + (items: any[], fd: string) => items.filter((item) => isItemId(fd, item.id)); const LibraryManager = React.createContext(undefined); @@ -80,31 +89,32 @@ const useLibraryItem = (fd: string, { id, ...props }: any) => { React.useEffect(() => { if (context) { const { group, order, library }: any = context; - const itemId = fd + '.' + id; + const itemId = `${fd}.${id}`; return library.useItem(itemId, group, order, props); - } else - return undefined; + } + return undefined; }); return context; }; -const Rankify = ({ library, group, order, children }: any) => { - let rank = 0; - const rankify = (elt: any) => { - const context: any = { group, order, library }; - context.order.concat([rank++]) +const Rankify = + ({ library, group, order, children }: any) => { + let rank = 0; + const rankify = (elt: any) => { + rank += 1; + const context: any = { group, order: [...order, rank], library }; + return ( + <LibraryManager.Provider value={context}> + {elt} + </LibraryManager.Provider> + ); + }; return ( - <LibraryManager.Provider value={context}> - {elt} - </LibraryManager.Provider> + <> + {React.Children.map(children, rankify)} + </> ); }; - return ( - <React.Fragment> - {React.Children.map(children, rankify)} - </React.Fragment> - ); -}; const HIDDEN = { display: 'none' }; @@ -120,22 +130,22 @@ const UseLibrary = ({ library, children }: any) => ( @summary Ordered collection of LabView Components. @description Renderers its children in the specified order. - Otherwized, elements are ordered by `rank` and `id`. + Otherwise, elements are ordered by `rank` and `id`. */ export const Fragment = ({ group, children }: any) => { const context: any = React.useContext(LibraryManager); - if (!context) - return null; - else { - return ( - <Rankify - group={group || context.group} - order={context.order} - library={context.library} > - {children} - </Rankify> - ); - } + + if (!context) return null; + + return ( + <Rankify + group={group || context.group} + order={context.order} + library={context.library} + > + {children} + </Rankify> + ); }; /** @@ -157,7 +167,8 @@ export const Group = ({ children, ...props }: any) => { <Rankify group={props.id} order={context.order} - library={context.library} > + library={context.library} + > {children} </Rankify> ); @@ -181,7 +192,8 @@ export const Group = ({ children, ...props }: any) => { - `<GridVbox>…</GridVbox>` a vertical grid of `GridContent` elements; - `<GridItem id=…>` a single component. - These grid content components must be imported from the `dome/layout/grids` module: + These grid content components must be imported from the `dome/layout/grids` + module: ``` import { GridItem, GridHbox, GridVbox } from 'dome/layout/grids'; ``` @@ -224,16 +236,21 @@ const TitleContext: any = React.createContext(undefined); @property {React.Children} children - additional components to render @description Defines an alternative component title bar. - If specified, the icon, label and title properties are rendered in an `<Label/>` component. - By default, the component title bar is labelled according to the component properties. + If specified, the icon, label and title properties are rendered in an + `<Label/>` component. + By default, the component title bar is labelled according to the component + properties. */ export const TitleBar = ({ icon, label, title, children }: any) => { const context: any = React.useContext(TitleContext); return ( - <ItemToRender id={'labview.title.' + context.id}> - <Label className='labview-handle' icon={icon} + <ItemToRender id={`labview.title.${context.id}`}> + <Label + className="labview-handle" + icon={icon} label={label || context.label} - title={title || context.title} /> + title={title || context.title} + /> {children} </ItemToRender> ); @@ -252,7 +269,7 @@ const GRIDITEM = { minWidth: 40, minHeight: 40, width: 120, - height: 120 + height: 120, }; const GRIDITEM_PLAIN = { fill: 'both' }; @@ -262,41 +279,55 @@ const GRIDITEM_VPANE = { fill: 'vertical' }; const makeGridItem = (customize: any, onClose: any) => (comp: any) => { const { id: libId, label, title, layout = 'PLAIN', children } = comp; const id = getItemId('components', libId); - const properties: any = Object.assign({}, GRIDITEM); + let properties: any = { ...GRIDITEM }; switch (layout) { case 'PLAIN': - Object.assign(properties, GRIDITEM_PLAIN); + properties = { ...properties, ...GRIDITEM_PLAIN }; break; case 'HPANE': - Object.assign(properties, GRIDITEM_HPANE); + properties = { ...properties, GRIDITEM_HPANE }; break; case 'VPANE': - Object.assign(properties, GRIDITEM_VPANE); + properties = { ...properties, GRIDITEM_VPANE }; break; default: console.warn(`[labviews] unexpected layout for ${id} component`, layout); break; } - for (const fd in properties) { - const prop = comp[fd]; - if (prop) properties[fd] = prop; - } + Object.keys(properties).forEach((key) => { + const prop = comp[key]; + if (prop) properties[key] = prop; + }); let CLOSING; - if (customize) CLOSING = ( - <IconButton - className='labview-close' - offset={-1} icon='CROSS' - onClick={() => onClose(id)} /> - ); + if (customize) { + CLOSING = ( + <IconButton + className="labview-close" + offset={-1} + icon="CROSS" + onClick={() => onClose(id)} + /> + ); + } return ( - <GridItem id={id} {...properties}> - <Vfill className='labview-content'> - <Hbox className='labview-titlebar'> + <GridItem + id={id} + className={properties.className} + handle={properties.handle} + resize={properties.resize} + fill={properties.fill} + shrink={properties.shrink} + minWidth={properties.minWidth} + minHeight={properties.minHeight} + width={properties.width} + height={properties.height} + > + <Vfill className="labview-content"> + <Hbox className="labview-titlebar"> <Hfill> <Catch title={id}> - <RenderItem id={'labview.title.' + id}> - <Label className='labview-handle' - label={label} title={title} /> + <RenderItem id={`labview.title.${id}`}> + <Label className="labview-handle" label={label} title={title} /> </RenderItem> </Catch> </Hfill> @@ -324,15 +355,23 @@ function CustomViews({ settings, shape, setShape, views: libViews }: any) { const theViews: any = {}; _.forEach(libViews, (view) => { - const { id: origin, order, label = '(Stock View)', title, defaultView } = view; - const id = 'builtin.' + origin; - theViews[id] = { id, order, label, title, builtin: true, defaultView, origin }; + const { + id: origin, + order, + label = '(Stock View)', + title, defaultView, + } = view; + const id = `builtin.${origin}`; + + theViews[id] = + { id, order, label, title, builtin: true, defaultView, origin }; }); _.forEach(customs, (view) => { const { id, order, label = '(Custom View)', title, origin } = view; - if (id && !theViews[id]) + if (id && !theViews[id]) { theViews[id] = { id, order, label, title, builtin: false, origin }; + } }); const getStock = (origin: any) => ( @@ -370,21 +409,25 @@ function CustomViews({ settings, shape, setShape, views: libViews }: any) { const RENAME = () => setEdited(id); const DUPLICATE = () => { - const base = 'custom.' + view.origin; + const base = `custom.${view.origin}`; const stock = getStock(view.origin); - let k = 1, newId = base; - while (theViews[newId]) newId = base + '~' + (++k); + let k = 1; + let newId = base; + while (theViews[newId]) { + k += 1; + newId = `${base}~${k}`; + } let newOrder = view.order; if (newOrder && newOrder.concat) newOrder = newOrder.concat([k]); - let newLabel = 'Custom ' + stock.label; - if (k > 1) newLabel += '~' + k; + let newLabel = `Custom ${stock.label}`; + if (k > 1) newLabel += `~${k}`; customs[newId] = { id: newId, label: newLabel, order: newOrder, - title: 'Derived from ' + stock.label, + title: `Derived from ${stock.label}`, origin: view.origin, - builtin: false + builtin: false, }; setCustoms(customs); if (current) shapes[current] = shape; @@ -401,11 +444,11 @@ function CustomViews({ settings, shape, setShape, views: libViews }: any) { }; Dome.popupMenu([ - { label: "Rename View", display: (!edited && isCustom), onClick: RENAME }, - { label: "Restore Default", display: isCurrent, onClick: DEFAULT }, - { label: "Duplicate View", onClick: DUPLICATE }, + { label: 'Rename View', display: (!edited && isCustom), onClick: RENAME }, + { label: 'Restore Default', display: isCurrent, onClick: DEFAULT }, + { label: 'Duplicate View', onClick: DUPLICATE }, 'separator', - { label: "Remove View", display: isCustom, onClick: REMOVE } + { label: 'Remove View', display: isCustom, onClick: REMOVE }, ]); }; @@ -420,45 +463,52 @@ function CustomViews({ settings, shape, setShape, views: libViews }: any) { setEdited(undefined); }; const FIELD = ( - <Field className='labview-field' - placeholder='View Name' - autoFocus value={label} - onEnter={RENAMED} /> + <Field + className="labview-field" + placeholder="View Name" + autoFocus + value={label} + onEnter={RENAMED} + /> ); return ( - <Item key={id} id={id} icon='DISPLAY' title={title} label={FIELD} /> - ); - } else { - const FLAGS = []; - if (builtin) FLAGS.push('LOCK'); - return ( - <Item - key={id} id={id} - icon='DISPLAY' - label={label} - title={title} - selected={id && current === id} - onSelection={SELECT} - onContextMenu={POPUP} - > - {FLAGS.map((icn) => ( - <Icon key={icn} className='labview-icon' size={9} offset={1} id={icn} /> - ))} - </Item> + <Item key={id} id={id} icon="DISPLAY" title={title} label={FIELD} /> ); } + const FLAGS = []; + if (builtin) FLAGS.push('LOCK'); + return ( + <Item + key={id} + id={id} + icon="DISPLAY" + label={label} + title={title} + selected={id && current === id} + onSelection={SELECT} + onContextMenu={POPUP} + > + {FLAGS.map((icn) => ( + <Icon + key={icn} + className="labview-icon" + size={9} + offset={1} + id={icn} + /> + ))} + </Item> + ); }; if (!current && !triggerDefault.current) { const theDefault = _.find(theViews, (item) => item.defaultView); triggerDefault.current = theDefault; - if (theDefault) setTimeout(() => { - SELECT(theDefault.id); - }); + if (theDefault) setTimeout(() => { SELECT(theDefault.id); }); } return ( - <Section label='Views'> + <Section label="Views"> {_.sortBy(theViews, ['order', 'id']).map(makeViewItem)} </Section> ); @@ -470,17 +520,26 @@ function CustomViews({ settings, shape, setShape, views: libViews }: any) { const DRAGOVERLAY = { className: 'labview-stock' }; -function CustomGroup({ dnd, shape, setDragging, id, title, label, components }: any) { - +function CustomGroup( + { dnd, shape, setDragging, id, title, label, components }: any, +) { const makeComponent = ({ id, label, title }: any) => { const itemId = getItemId('components', id); const disabled = Grids.getShapeItem(shape, itemId) !== undefined; return ( - <DragSource key={id} dnd={dnd} + <DragSource + key={id} + dnd={dnd} disabled={disabled} overlay={disabled ? undefined : DRAGOVERLAY} - onStart={() => setDragging(itemId)} > - <Item icon='COMPONENT' disabled={disabled} label={label} title={title} /> + onStart={() => setDragging(itemId)} + > + <Item + icon="COMPONENT" + disabled={disabled} + label={label} + title={title} + /> </DragSource> ); }; @@ -496,22 +555,23 @@ function CustomGroup({ dnd, shape, setDragging, id, title, label, components }: // --- Customization Panel // -------------------------------------------------------------------------- -function CustomizePanel({ dnd, settings, library, shape, setShape, setDragging }: any) { - +function CustomizePanel( + { dnd, settings, library, shape, setShape, setDragging }: any, +) { Dome.useUpdate('labview.library'); - const items = library.items; + const { items } = library; const views = getItems(items, 'views'); const groups = getItems(items, 'groups'); const components = getItems(items, 'components'); - const settingFolds = settings && settings + '.folds'; - const settingViews = settings && settings + '.views'; + const settingFolds = settings && `${settings}.folds`; + const settingViews = settings && `${settings}.views`; const contents: any = {}; groups.unshift({ id: 'nogroup', label: 'Components' }); - groups.forEach((g) => contents[g.id] = []); + groups.forEach((g) => (contents[g.id] = [])); components.forEach((c) => { - const gid = c.group ? 'groups.' + c.group : 'nogroup'; + const gid = c.group ? `groups.${c.group}` : 'nogroup'; let content = contents[gid]; if (content === undefined) content = contents.nogroup; content.push(c); @@ -520,17 +580,23 @@ function CustomizePanel({ dnd, settings, library, shape, setShape, setDragging } return ( <SideBar settings={settingFolds}> <CustomViews - key='views' + key="views" settings={settingViews} shape={shape} setShape={setShape} - views={views} /> + views={views} + /> {groups.map((g) => ( <CustomGroup - key={g.id} id={g.id} - label={g.label} title={g.title} - dnd={dnd} setDragging={setDragging} shape={shape} - components={contents[g.id]} /> + key={g.id} + id={g.id} + label={g.label} + title={g.title} + dnd={dnd} + setDragging={setDragging} + shape={shape} + components={contents[g.id]} + /> ))} </SideBar> ); @@ -541,7 +607,6 @@ function CustomizePanel({ dnd, settings, library, shape, setShape, setDragging } // -------------------------------------------------------------------------- /** - @class @summary Reconfigurable Container (React Component) @property {boolean} [customize] - show components panel (false by default) @property {string} [settings] - window settings to make views persistent @@ -549,17 +614,17 @@ function CustomizePanel({ dnd, settings, library, shape, setShape, setDragging } @description This container displays its content into a reconfigurable view. - The entire content is rendered, but elements must be packed into `<Component/>` - containers, otherwise, they would remain invisible. - Content may also contains `<View/>` and `<Group/>` definitions, and the content - can be defined through any kind of React components. + The entire content is rendered, but elements must be packed into + `<Component/>` containers, otherwise, they would remain invisible. + Content may also contains `<View/>` and `<Group/>` definitions, and the + content can be defined through any kind of React components. */ export function LabView(props: any) { // Parameters const { settings, customize = false, children } = props; - const settingSplit = settings && settings + '.split'; - const settingShape = settings && settings + '.shape'; - const settingPanel = settings && settings + '.panel'; + const settingSplit = settings && `${settings}.split`; + const settingShape = settings && `${settings}.shape`; + const settingPanel = settings && `${settings}.panel`; // Hooks & State Dome.useUpdate('labview.library', 'dome.defaults'); const dnd = React.useMemo(() => new DnD(), []); @@ -567,36 +632,41 @@ export function LabView(props: any) { const [shape, setShape] = Dome.useState(settingShape); const [dragging, setDragging] = React.useState(); // Preparation - const onClose = (id: string) => setShape(Grids.removeShapeItem(shape, id)); - const components = _.filter(lib.collection, (item: any) => isItemId('components', item.id)); - const gridItems = components.map(makeGridItem(customize, onClose)); - const holding = dragging ? gridItems.find((elt) => elt.props.id === dragging) : undefined; + const onClose = + (id: string) => setShape(Grids.removeShapeItem(shape, id)); + const components = + _.filter(lib.collection, (item: any) => isItemId('components', item.id)); + const gridItems = + components.map(makeGridItem(customize, onClose)); + const holding = + dragging ? gridItems.find((elt) => elt.props.id === dragging) : undefined; // Make view return ( - <React.Fragment> + <> <UseLibrary library={lib}> {children} </UseLibrary> - <Splitter settings={settingSplit} unfold={customize} dir='RIGHT'> + <Splitter settings={settingSplit} unfold={customize} dir="RIGHT"> <GridLayout dnd={dnd} padding={2} - className='labview-container' + className="labview-container" items={gridItems} shape={shape} onReshape={setShape} - holding={holding} /> + holding={holding} + /> <CustomizePanel dnd={dnd} settings={settingPanel} shape={shape} setShape={setShape} setDragging={setDragging} - library={lib} /> + library={lib} + /> </Splitter> - </React.Fragment> + </> ); - } // -------------------------------------------------------------------------- diff --git a/ivette/src/frama-c/server.ts b/ivette/src/frama-c/server.ts index cfb83941ed95b28ed0843b65e77dd66bccd8c253..84bf3f2d20d74dd6c5e413b5ef17b6d6ac81711c 100644 --- a/ivette/src/frama-c/server.ts +++ b/ivette/src/frama-c/server.ts @@ -7,12 +7,12 @@ * @description Manage the current Frama-C server/client interface */ -import _ from 'lodash' -import React from 'react' -import Dome from 'dome' -import System from 'dome/system' -import { Buffer } from 'dome/text/buffers' -import { Request } from 'zeromq' +import _ from 'lodash'; +import React from 'react'; +import Dome from 'dome'; +import System from 'dome/system'; +import { RichTextBuffer } from 'dome/text/buffers'; +import { Request } from 'zeromq'; // -------------------------------------------------------------------------- // --- Events @@ -102,25 +102,24 @@ export enum StatusCode { // -------------------------------------------------------------------------- let status = StatusCode.OFF; -let error: string | undefined; // process error -let rqid: number; // Request ID -let pending: any; // Pending promise callbacks -let queueCmd: any; // Queue of server commands to be sent -let queueIds: any; // Waiting request ids to be sent -let polling: any; // Timeout Polling timer -let flushing: any; // Immediate Flushing timer +let error: string | undefined; // process error +let rqid: number; // Request ID +let pending: any; // Pending promise callbacks +let queueCmd: any; // Queue of server commands to be sent +let queueIds: any; // Waiting request ids to be sent +let polling: any; // Timeout Polling timer +let flushing: any; // Immediate Flushing timer let config: ServerConfiguration; -let process: any; // Server process -let socket: any; // ZMQ (REQ) socket -let busy: boolean; // ZMQ socket is busy -let killing: any; // killing timeout +let process: any; // Server process +let socket: any; // ZMQ (REQ) socket +let busy: boolean; // ZMQ socket is busy +let killing: any; // killing timeout // -------------------------------------------------------------------------- // --- Server Console // -------------------------------------------------------------------------- -export const buffer = new Buffer({ maxlines: 200 }); -export const feedback = ''; +const buffer = new RichTextBuffer({ maxlines: 200 }); // -------------------------------------------------------------------------- // --- Server Status @@ -132,7 +131,7 @@ export const feedback = ''; * @description * See [STATUS](module-frama-c_server.html#~STATUS) code definitions. */ -export function getStatus(): StatusCode { return status } +function getStatus(): StatusCode { return status; } /** * @summary Hook on current server (Custom React Hook). @@ -140,47 +139,47 @@ export function getStatus(): StatusCode { return status } * @description * See [STATUS](module-frama-c_server.html#~STATUS) code definitions. */ -export function useStatus(): StatusCode { +function useStatus(): StatusCode { Dome.useUpdate(STATUS); return status; } /** Return `FAILED` status message. */ -export function getError() { return error; } +function getError() { return error; } /** * @summary Frama-C Server is running and ready to handle requests. * @return {boolean} status is `RUNNING`. */ -export function isRunning(): boolean { return status === StatusCode.RUNNING } +function isRunning(): boolean { return status === StatusCode.RUNNING; } /** * @summary Number of requests still pending. * @return {number} pending requests */ -export function getPending(): number { - return _.reduce(pending, (_, n) => n + 1, 0) +function getPending(): number { + return _.reduce(pending, (_, n) => n + 1, 0); } /** * @summary Register callback on READY event. * @param {function} callback - invoked when the server enters RUNNING status */ -export function onReady(callback: any) { Dome.on(READY, callback) } +function onReady(callback: any) { Dome.on(READY, callback); } /** * @summary Register callback on SHUTDOWN event. * @param {function} callback - invoked when the server enters SHUTDOWN status */ -export function onShutdown(callback: any) { Dome.on(SHUTDOWN, callback) } +function onShutdown(callback: any) { Dome.on(SHUTDOWN, callback); } /** * @summary Register callback on Signal ACTIVITY event. * @*param {string} id - the signal event to listen to * @*param {function} callback - invoked with `callback(signal,active)` */ -export function onActivity(signal: string, callback: any) { - Dome.on(ACTIVITY + signal, callback) +function onActivity(signal: string, callback: any) { + Dome.on(ACTIVITY + signal, callback); } // -------------------------------------------------------------------------- @@ -188,7 +187,9 @@ export function onActivity(signal: string, callback: any) { // -------------------------------------------------------------------------- function _status(newStatus: StatusCode, err?: string) { - if (Dome.DEVEL && err) console.error('[Server]', err); + if (Dome.DEVEL && err) { + console.error('[Server]', err); + } if (newStatus !== status || err) { const oldStatus = status; status = newStatus; @@ -210,7 +211,7 @@ function _status(newStatus: StatusCode, err?: string) { * If the server is being shutdown, it will reboot. * Otherwise, the Frama-C Server is spawned. */ -export function start() { +function start() { switch (status) { case StatusCode.OFF: case StatusCode.FAILED: @@ -242,7 +243,7 @@ export function start() { * When the server is shutting down, restart is canceled. * Otherwise, this is a no-op. */ -export function stop() { +function stop() { switch (status) { case StatusCode.STARTED: _kill(); @@ -274,9 +275,10 @@ export function stop() { * it is hard killed and restart is canceled. * Otherwize, this is no-op. * - * This function is automatically called when the `module` emits the `KILL` signal. + * This function is automatically called when the `module` emits the `KILL` + * signal. */ -export function kill() { +function kill() { switch (status) { case StatusCode.STARTED: case StatusCode.RUNNING: @@ -303,7 +305,7 @@ export function kill() { * When running, try to gracefully shutdown the Server, * and finally schedule a reboot on exit. */ -export function restart() { +function restart() { switch (status) { case StatusCode.OFF: case StatusCode.FAILED: @@ -333,7 +335,7 @@ export function restart() { * When not running, clear the console and reset any error flag. * Otherwised, do nothing. */ -export function clear() { +function clear() { switch (status) { case StatusCode.FAILED: _status(StatusCode.OFF); @@ -354,22 +356,22 @@ export function clear() { // -------------------------------------------------------------------------- export interface ServerConfiguration { - env?: any; // Process environment variables (default: `undefined`) - cwd?: string; // Working directory (default: current) - command?: string; // Server command (default: `frama-c`) - params: string[]; // Additional server arguments (default: empty) - sockaddr?: string; // Server socket (default: `ipc:///.frama-c.<pid>.io`) - timeout?: number; // Shutdown timeout before server is hard killed, in milliseconds (default: 300ms) - polling?: number; // Server polling period, in milliseconds (default: 50ms) - logout?: string; // Process stdout log file (default: `undefined`) - logerr?: string; // Process stderr log file (default: `undefined`) + env?: any; // Process environment variables (default: `undefined`) + cwd?: string; // Working directory (default: current) + command?: string; // Server command (default: `frama-c`) + params: string[]; // Additional server arguments (default: empty) + sockaddr?: string; // Server socket (default: `ipc:///.frama-c.<pid>.io`) + timeout?: number; // Shutdown timeout before server is hard killed, in milliseconds (default: 300ms) + polling?: number; // Server polling period, in milliseconds (default: 50ms) + logout?: string; // Process stdout log file (default: `undefined`) + logerr?: string; // Process stderr log file (default: `undefined`) } /** * @summary Configure the Server. * @param {ServerConfiguration} sc - Server Configuration */ -export function configure(sc: ServerConfiguration) { +function configure(sc: ServerConfiguration) { config = sc || {}; } @@ -379,7 +381,7 @@ export function configure(sc: ServerConfiguration) { * @description * See `configure()` method. */ -export function getConfig(): ServerConfiguration { +function getConfig(): ServerConfiguration { return config; } @@ -389,28 +391,41 @@ export function getConfig(): ServerConfiguration { async function _launch() { _reset(); - if (!config) throw ('Frama-C Server not configured'); - let { env, cwd, command = 'frama-c', params = [], sockaddr, logout, logerr } = config; + if (!config) { + throw new Error('Frama-C Server not configured'); + } + let { + env, + cwd, + command = 'frama-c', + params = [], + sockaddr, + logout, + logerr, + } = config; buffer.clear(); buffer.append('$', command); const size = params.reduce((n: any, p: any) => n + p.length, 0); - if (size < 40) + if (size < 40) { buffer.append('', ...params); - else + } else { params.forEach((argv: string) => { - if (argv.startsWith('-') || argv.endsWith('.c') || argv.endsWith('.i') || argv.endsWith('.h')) + if (argv.startsWith('-') || argv.endsWith('.c') + || argv.endsWith('.i') || argv.endsWith('.h')) { buffer.append('\n '); + } buffer.append(' '); buffer.append(argv); }); + } buffer.append('\n'); if (!cwd) cwd = System.getWorkingDir(); if (!sockaddr) { - const socketfile = System.join(cwd, '.frama-c.' + System.getPID() + '.io'); + const socketfile = System.join(cwd, `.frama-c.${System.getPID()}.io`); System.atExit(() => System.remove(socketfile)); - sockaddr = 'ipc://' + socketfile; + sockaddr = `ipc://${socketfile}`; } logout = logout && System.join(cwd, logout); logerr = logerr && System.join(cwd, logerr); @@ -419,14 +434,15 @@ async function _launch() { cwd, stdout: { path: logout, pipe: true }, stderr: { path: logerr, pipe: true }, - env + env, }; // Launch Process const process = await System.spawn(command, params, options); const logger = (text: string | string[]) => { buffer.append(text); - if (0 <= text.indexOf('\n')) + if (text.indexOf('\n') >= 0) { buffer.scroll(undefined, undefined); + } }; process.stdout.on('data', logger); process.stderr.on('data', logger); @@ -435,8 +451,8 @@ async function _launch() { _close(err); }); process.on('exit', (status: StatusCode, signal: string) => { - signal && buffer.log('Signal:', signal); - status && buffer.log('Exit:', status); + if (signal) buffer.log('Signal:', signal); + if (status) buffer.log('Exit:', status); _close(signal || status); }); // Connect to Server @@ -561,28 +577,31 @@ class Signal { } } } - } -//--- Memo +// --- Memo const signals: any[] = []; function _signal(id: any) { let s = signals[id]; - if (!s) s = signals[id] = new Signal(id); + if (!s) { + signals[id] = new Signal(id); + s = signals[id]; + } return s; } -//--- External API +// --- External API /** * @summary Register a Signal callback. * @param {string} id - the signal event to listen to * @param {function} callback - the callback to call on received signal * @description - * If the server is not yet listening to this signal, a `SIGON` command is sent. + * If the server is not yet listening to this signal, a `SIGON` command is + * sent. */ -export function onSignal(id: string, callback: any) { +function onSignal(id: string, callback: any) { _signal(id).on(callback); } @@ -594,7 +613,7 @@ export function onSignal(id: string, callback: any) { * When no more callbacks are listening to this signal for a while, * the server will be notified with a `SIGOFF` command. */ -export function offSignal(id: string, callback: any) { +function offSignal(id: string, callback: any) { _signal(id).off(callback); } @@ -603,14 +622,14 @@ export function offSignal(id: string, callback: any) { * @param {string} id - the signal event to listen to * @param {function} callback - the callback to be called on signal */ -export function useSignal(id: string, callback: any) { +function useSignal(id: string, callback: any) { React.useEffect(() => { onSignal(id, callback); return () => { offSignal(id, callback); }; - }) + }); } -//--- Server Synchro +// --- Server Synchro Dome.on(READY, () => { _.forEach(signals, (s) => s.sigon()); @@ -628,14 +647,14 @@ Dome.on(SHUTDOWN, () => { * @typedef RqKind * @summary Request kind. * @description - * - `GET` Used to read data from the server + * - `GET` Used to read data from the server * - `SET` Used to write data into the server * - `EXEC` Used to make the server execute a task */ export enum RqKind { - GET = "GET", - SET = "SET", - EXEC = "EXEC" + GET = 'GET', + SET = 'SET', + EXEC = 'EXEC' } /** @@ -648,10 +667,11 @@ export enum RqKind { * You may _kill_ the request before its normal termination by * invoking `kill()` on the returned promised. */ -export function send(kind: RqKind, rq: string, params: any) { +function send(kind: RqKind, rq: string, params: any) { if (!isRunning()) return Promise.reject('Server not running'); if (!rq) return Promise.reject('Undefined request'); - const rid = 'RQ.' + rqid++; + const rid = `RQ.${rqid}`; + rqid += 1; const data = JSON.stringify(params); const promise: any = new Promise((resolve, reject) => { pending[rid] = { resolve, reject }; @@ -731,7 +751,7 @@ function _send() { .catch(() => _cancel(ids)) .finally(() => { busy = false; Dome.emit(STATUS); }); } else { - _cancel(ids) + _cancel(ids); } } else { // No pending command nor pending response @@ -743,9 +763,13 @@ function _send() { function _receive(resp: any) { try { - let rid, data, err, cmd; + let rid; + let data; + let err; + let cmd; const shift = () => resp.shift().toString(); - while (resp.length) { + let unknownResponse = false; + while (resp.length && !unknownResponse) { cmd = shift(); switch (cmd) { case 'NONE': @@ -778,15 +802,13 @@ function _receive(resp: any) { break; default: console.error('[Frama-C Server] Unknown Response:', cmd); - resp.length = 0; + unknownResponse = true; break; } } } finally { - if (queueCmd.length) - _flush(); - else - _poll(); + if (queueCmd.length) _flush(); + else _poll(); } } @@ -795,14 +817,30 @@ function _receive(resp: any) { // -------------------------------------------------------------------------- export default { - configure, getConfig, - getStatus, useStatus, buffer, - getError, getPending, isRunning, - start, stop, kill, restart, clear, + configure, + getConfig, + getStatus, + useStatus, + buffer, + getError, + getPending, + isRunning, + start, + stop, + kill, + restart, + clear, send, - onReady, onShutdown, onActivity, - onSignal, offSignal, useSignal, - STATUS, READY, SHUTDOWN, StatusCode + onReady, + onShutdown, + onActivity, + onSignal, + offSignal, + useSignal, + STATUS, + READY, + SHUTDOWN, + StatusCode, }; // -------------------------------------------------------------------------- diff --git a/ivette/src/frama-c/states.ts b/ivette/src/frama-c/states.ts index 8ae36c2358bff6e91804a92eee4a29b46a0ebe6d..9e63255e1b4f38a36da1c7323e98823b1763df15 100644 --- a/ivette/src/frama-c/states.ts +++ b/ivette/src/frama-c/states.ts @@ -143,10 +143,12 @@ export function useState(id: string) { * The request is send asynchronously and cached until any change in * `rq`, `params`, current project or server activity. * - * Default values for various situations can be defined in the options parameter, - * which is `undefined` unless specified, or `null` to keep the current value. - * For instance `{ pending: null }` will return `undefined` when off-line and in case of errors, - * but will keep the last received value until a new one is actually received. + * Default values for various situations can be defined in the options + * parameter, which is `undefined` unless specified, or `null` to keep the + * current value. + * For instance `{ pending: null }` will return `undefined` when off-line and + * in case of errors, but will keep the last received value until a new one is + * actually received. */ export function useRequest(rq: string, params: any, options: any = {}) { const project = useProject(); @@ -159,7 +161,7 @@ export function useRequest(rq: string, params: any, options: any = {}) { .then(setValue) .catch((err: string) => { if (Dome.DEVEL) console.warn(`[Server] use request '${rq}':`, err); - const error = options.error; + const { error } = options; if (error !== null) setValue(error); }); } else { @@ -179,26 +181,38 @@ export function useRequest(rq: string, params: any, options: any = {}) { * @param {string} rq - GET request name * @param {any} [params] - GET request parameter (default `'null'`) * @param {object} [options] - Dictionary options - * @param {boolean} [options.key] - The property to index an item (default `'name'`) - * @param {boolean} [options.offline] - Keep the dictionary when offline (default `true`) - * @param {boolean} [options.pending] - Keep the dictionary when pending (default `true`) - * @param {boolean} [options.error] - Keep the dictionary on error (default `false`) - * @param {function} [options.filter] - Only index items satisfying the filter (default `undefined`) + * @param {boolean} [options.key] - The property to index an item + * (default `'name'`) + * @param {boolean} [options.offline] - Keep the dictionary when offline + * (default `true`) + * @param {boolean} [options.pending] - Keep the dictionary when pending + * (default `true`) + * @param {boolean} [options.error] - Keep the dictionary on error + * (default `false`) + * @param {function} [options.filter] - Only index items satisfying the filter + * (default `undefined`) * @return {object} [result] GET request response indexed by key * @description - * Sends the specified GET request and returns its returned collection indexed by the provided key. + * Sends the specified GET request and returns its returned collection indexed + * by the provided key. * Items in the collection that do have the key are not indexed. */ -export function useDictionary(rq: string, params: any = null, options: any = {}) { - const { offline = true, pending = true, error = false, key = 'name', filter } = options; +function useDictionary(rq: string, params: any = null, options: any = {}) { + const { + offline = true, + pending = true, + error = false, + key = 'name', + filter, + } = options; const tags = useRequest(rq, params, { offline: offline ? null : undefined, pending: pending ? null : undefined, - error: error ? null : undefined + error: error ? null : undefined, }); const dict = React.useMemo(() => { const d: any = {}; - _.forEach(tags, tg => { + _.forEach(tags, (tg) => { const k: any = tg[key]; if (k && (!filter || filter(tg))) d[k] = tg; }); @@ -225,9 +239,9 @@ class SyncState { constructor(id: any) { this.id = id; this.UPDATE = STATE + id; - this.signal = id + '.sig'; - this.getRq = id + '.get'; - this.setRq = id + '.set'; + this.signal = `${id}.sig`; + this.getRq = `${id}.get`; + this.setRq = `${id}.set`; this.insync = false; this.value = undefined; this.update = this.update.bind(this); @@ -237,8 +251,9 @@ class SyncState { } getValue() { - if (!this.insync && Server.isRunning()) + if (!this.insync && Server.isRunning()) { this.update(); + } return this.value; } @@ -256,7 +271,6 @@ class SyncState { Dome.emit(this.UPDATE); }); } - } // -------------------------------------------------------------------------- @@ -267,11 +281,14 @@ let syncStates: any = {}; function getSyncState(id: any) { let s: any = syncStates[id]; - if (!s) s = syncStates[id] = new SyncState(id); + if (!s) { + syncStates[id] = new SyncState(id); + s = syncStates[id]; + } return s; } -Server.onShutdown(() => syncStates = {}); +Server.onShutdown(() => (syncStates = {})); // -------------------------------------------------------------------------- // --- Synchronized State Hooks @@ -287,7 +304,7 @@ Server.onShutdown(() => syncStates = {}); * - sends a `<id>.set` request to update the value of the state; * - listens to `<id>.sig` signal to stay in sync with server updates. */ -export function useSyncState(id: string) { +function useSyncState(id: string) { const s = getSyncState(id); Dome.useUpdate(PROJECT, s.UPDATE); Server.useSignal(s.signal, s.update); @@ -303,7 +320,7 @@ export function useSyncState(id: string) { * - sends a `<id>.get` request to obtain the current value of the state; * - listens to `<id>.sig` signal to stay in sync with server updates. */ -export function useSyncValue(id: string) { +function useSyncValue(id: string) { const s = getSyncState(id); Dome.useUpdate(s.update); Server.useSignal(s.signal, s.update); @@ -325,9 +342,9 @@ class SyncArray { constructor(id: string) { this.UPDATE = STATE + id; - this.signal = id + '.sig'; - this.fetchRq = id + '.fetch'; - this.reloadRq = id + '.reload'; + this.signal = `${id}.sig`; + this.fetchRq = `${id}.fetch`; + this.reloadRq = `${id}.reload`; this.index = {}; this.insync = false; this.fetch = this.fetch.bind(this); @@ -346,9 +363,7 @@ class SyncArray { fetch() { this.insync = true; Server.send(RqKind.GET, this.fetchRq, 50) - .then(({ - reload = false, removed = [], updated = [], pending = 0 - }) => { + .then(({ reload = false, removed = [], updated = [], pending = 0 }) => { let reloaded = false; if (reload) { reloaded = this.isEmpty(); @@ -361,7 +376,7 @@ class SyncArray { this.index[item.key] = item; }); if (reloaded || removed.length || updated.length) { - this.index = Object.assign({}, this.index); + this.index = { ...this.index }; Dome.emit(this.UPDATE); } if (pending > 0) { @@ -376,7 +391,6 @@ class SyncArray { this.insync = false; Dome.emit(this.UPDATE); } - } // -------------------------------------------------------------------------- @@ -395,7 +409,7 @@ function getSyncArray(id: string) { return a; } -Server.onShutdown(() => syncArrays = {}); +Server.onShutdown(() => (syncArrays = {})); // -------------------------------------------------------------------------- // --- Synchronized Array Hooks @@ -440,12 +454,12 @@ setStateDefault(SELECTION, {}); * @return {array} `[selection,update]` for the current selection * @description * The selection is an object with many independant fields. - * You update it by providing only some fields, the other ones being kept unchanged, - * like the `setState()` behaviour of React components. + * You update it by providing only some fields, the other ones being kept + * unchanged, like the `setState()` behaviour of React components. */ export function useSelection() { const [state, setState] = useState(SELECTION); - return [state, (upd: any) => setState(Object.assign({}, state, upd))]; + return [state, (upd: any) => setState({ ...state, ...upd })]; } // -------------------------------------------------------------------------- @@ -462,7 +476,8 @@ export default { useRequest, useDictionary, useSelection, - PROJECT, STATE + PROJECT, + STATE, }; // -------------------------------------------------------------------------- diff --git a/ivette/src/renderer/ASTview.tsx b/ivette/src/renderer/ASTview.tsx index a66f4d0d82e06eb37568a31e7cc7b97bd6998b3c..1fd9d1befb4abe282da2f0684d4d61fcbf8bb33e 100644 --- a/ivette/src/renderer/ASTview.tsx +++ b/ivette/src/renderer/ASTview.tsx @@ -7,11 +7,11 @@ import Server, { RqKind } from 'frama-c/server'; import States from 'frama-c/states'; import { Vfill } from 'dome/layout/boxes'; -import { Buffer } from 'dome/text/buffers'; +import { RichTextBuffer } from 'dome/text/buffers'; import { Text } from 'dome/text/editors'; import { Component } from 'frama-c/LabViews'; -import 'codemirror/mode/clike/clike.js'; +import 'codemirror/mode/clike/clike'; import 'codemirror/theme/ambiance.css'; // -------------------------------------------------------------------------- @@ -21,13 +21,16 @@ import 'codemirror/theme/ambiance.css'; const print = (buffer: any, text: string) => { if (Array.isArray(text)) { const tag = text.shift(); - if (tag !== '') + if (tag !== '') { buffer.openTextMarker({ id: tag }); - text.forEach(txt => print(buffer, txt)); - if (tag !== '') + } + text.forEach((txt) => print(buffer, txt)); + if (tag !== '') { buffer.closeTextMarker(); - } else if (typeof (text) === 'string') + } + } else if (typeof (text) === 'string') { buffer.append(text); + } }; // -------------------------------------------------------------------------- @@ -35,9 +38,8 @@ const print = (buffer: any, text: string) => { // -------------------------------------------------------------------------- const ASTview = () => { - // Hooks - const buffer = React.useMemo(() => new Buffer(), []); + const buffer = React.useMemo(() => new RichTextBuffer(), []); const [select, setSelect] = States.useSelection(); const theFunction = select && select.function; const theMarker = select && select.marker; @@ -47,11 +49,12 @@ const ASTview = () => { buffer.clear(); if (theFunction) { buffer.log('// Loading', theFunction, '…'); - Server.send(RqKind.GET, "kernel.ast.printFunction", theFunction) + Server.send(RqKind.GET, 'kernel.ast.printFunction', theFunction) .then((data: string) => { buffer.clear(); - if (!data) + if (!data) { buffer.log('// No code for function ', theFunction); + } print(buffer, data); if (theMarker) buffer.scroll(theMarker, undefined); }); @@ -69,12 +72,14 @@ const ASTview = () => { // Component return ( <Vfill> - <Text buffer={buffer} - mode='text/x-csrc' - theme='ambiance' + <Text + buffer={buffer} + mode="text/x-csrc" + theme="ambiance" selection={theMarker} onSelection={onSelection} - readOnly /> + readOnly + /> </Vfill> ); }; @@ -84,9 +89,10 @@ const ASTview = () => { // -------------------------------------------------------------------------- export default () => ( - <Component id='frama-c.astview' - label='AST' - title='Normalized source code representation.' + <Component + id="frama-c.astview" + label="AST" + title="Normalized source code representation." > <ASTview /> </Component> diff --git a/ivette/src/renderer/Application.tsx b/ivette/src/renderer/Application.tsx index e5528cd1c5b824d6b49c07cc9cd4288b9b6f1545..e93e3d07bf67ce5b2af512769f26031922e6deb0 100644 --- a/ivette/src/renderer/Application.tsx +++ b/ivette/src/renderer/Application.tsx @@ -21,39 +21,46 @@ import ASTview from './ASTview'; // --- Main View // -------------------------------------------------------------------------- -export default (function () { - - const [sidebar, flipSidebar] = Dome.useSwitch('frama-c.sidebar.unfold', false); - const [viewbar, flipViewbar] = Dome.useSwitch('frama-c.viewbar.unfold', false); +export default (() => { + const [sidebar, flipSidebar] = Dome.useSwitch( + 'frama-c.sidebar.unfold', + false, + ); + const [viewbar, flipViewbar] = Dome.useSwitch( + 'frama-c.viewbar.unfold', + false, + ); return ( <Vfill> <Toolbar.ToolBar> <Toolbar.Button - icon='SIDEBAR' title='Show/Hide side bar' + icon="SIDEBAR" + title="Show/Hide side bar" selected={sidebar} onClick={flipSidebar} /> <Controller.Control /> <Toolbar.Filler /> <Toolbar.Button - icon='ITEMS.GRID' - title='Customize Main View' + icon="ITEMS.GRID" + title="Customize Main View" selected={viewbar} - onClick={flipViewbar} /> + onClick={flipViewbar} + /> </Toolbar.ToolBar> - <Splitter dir='LEFT' settings='frame-c.sidebar.position' unfold={sidebar}> + <Splitter dir="LEFT" settings="frame-c.sidebar.position" unfold={sidebar}> <Sidebar.SideBar> <div>(Empty)</div> </Sidebar.SideBar> <LabView customize={viewbar} - settings='frama-c.labview' + settings="frama-c.labview" > - <View id='dashboard' label='Dashboard' defaultView> - <GridItem id='frama-c.console' /> + <View id="dashboard" label="Dashboard" defaultView> + <GridItem id="frama-c.console" /> </View> - <Group id='frama-c' label='Frama-C' title='Frama-C Kernel Components'> + <Group id="frama-c" label="Frama-C" title="Frama-C Kernel Components"> <Controller.Console /> <Properties /> <ASTview /> @@ -67,7 +74,6 @@ export default (function () { </Toolbar.ToolBar> </Vfill> ); - }); // -------------------------------------------------------------------------- diff --git a/ivette/src/renderer/Controller.tsx b/ivette/src/renderer/Controller.tsx index d0ca7436fd7c6915e9fc50c1feb272632c1d4f0d..0e46c90baef4e127529e2a1a3758ced1d6722583 100644 --- a/ivette/src/renderer/Controller.tsx +++ b/ivette/src/renderer/Controller.tsx @@ -8,7 +8,7 @@ import Dome from 'dome'; import { Button as ToolButton, ButtonGroup, Space } from 'dome/layout/toolbars'; import { LED, IconButton } from 'dome/controls/buttons'; import { Label, Code } from 'dome/controls/labels'; -import { Buffer } from 'dome/text/buffers'; +import { RichTextBuffer } from 'dome/text/buffers'; import { Text } from 'dome/text/editors'; import Server, { StatusCode, ServerConfiguration } from 'frama-c/server'; @@ -21,7 +21,7 @@ import 'codemirror/theme/ambiance.css'; // -------------------------------------------------------------------------- let cmdConfig: ServerConfiguration; -const cmdLine = new Buffer(); +const cmdLine = new RichTextBuffer(); function dumpCmdLine(sc: ServerConfiguration): void { const { cwd, command, sockaddr, params } = sc; @@ -29,16 +29,19 @@ function dumpCmdLine(sc: ServerConfiguration): void { if (cwd) cmdLine.log('--cwd', cwd); if (command) cmdLine.log('--command', command); if (sockaddr) cmdLine.log('--socket', sockaddr); - if (params) params.forEach((v: string, i: number) => { - if (i > 0) { - if (v.startsWith('-') || v.endsWith('.c') - || v.endsWith('.h') || v.endsWith('.i')) - cmdLine.append('\n'); - else - cmdLine.append(' '); - } - cmdLine.append(v); - }); + if (params) { + params.forEach((v: string, i: number) => { + if (i > 0) { + if (v.startsWith('-') || v.endsWith('.c') + || v.endsWith('.h') || v.endsWith('.i')) { + cmdLine.append('\n'); + } else { + cmdLine.append(' '); + } + } + cmdLine.append(v); + }); + } cmdLine.append('\n'); } @@ -51,19 +54,27 @@ function buildServerConfiguration(argv: string[], cwd?: string) { const v = argv[k]; switch (v) { case '--cwd': - cwdir = argv[++k]; + k += 1; + cwdir = argv[k]; break; case '--command': - command = argv[++k]; + k += 1; + command = argv[k]; break; case '--socket': - sockaddr = argv[++k]; + k += 1; + sockaddr = argv[k]; break; default: params.push(v); } } - return { cwd: cwdir, command, sockaddr, params }; + return { + cwd: cwdir, + command, + sockaddr, + params, + }; } Dome.onCommand((argv: string[], cwd: string) => { @@ -79,9 +90,14 @@ Dome.onCommand((argv: string[], cwd: string) => { export const Control = () => { const status = Server.useStatus(); - let play: { enabled: boolean; onClick: any } = { enabled: false, onClick: null }; - let stop: { enabled: boolean; onClick: any } = { enabled: false, onClick: null }; - let reload: { enabled: boolean; onClick: any } = { enabled: false, onClick: null }; + + let play: { enabled: boolean; onClick: any } = + { enabled: false, onClick: null }; + let stop: { enabled: boolean; onClick: any } = + { enabled: false, onClick: null }; + let reload: { enabled: boolean; onClick: any } = + { enabled: false, onClick: null }; + switch (status) { case StatusCode.OFF: case StatusCode.FAILED: @@ -91,15 +107,29 @@ export const Control = () => { stop = { enabled: true, onClick: Server.stop }; reload = { enabled: true, onClick: Server.restart }; break; + default: + break; } return ( <ButtonGroup> - <ToolButton icon='MEDIA.PLAY' {...play} - title='Start the server' /> - <ToolButton icon='RELOAD' {...reload} - title='Re-start the server' /> - <ToolButton icon='MEDIA.STOP' {...stop} - title='Shut down the server' /> + <ToolButton + icon="MEDIA.PLAY" + enabled={play.enabled} + onClick={play.onClick} + title="Start the server" + /> + <ToolButton + icon="RELOAD" + enabled={reload.enabled} + onClick={reload.onClick} + title="Re-start the server" + /> + <ToolButton + icon="MEDIA.STOP" + enabled={stop.enabled} + onClick={stop.onClick} + title="Shut down the server" + /> </ButtonGroup> ); }; @@ -121,10 +151,12 @@ function execCmdLine(cmd: string) { const RenderConsole = () => { const [cmd, switchCmd] = Dome.useSwitch(); - const { current, next, prev, index, length, update, insert, clear }: any = Dome.useHistory('frama-c.command.history'); + const { current, next, prev, index, length, update, insert, clear }: any = + Dome.useHistory('frama-c.command.history'); + const doExec = () => { const cmd = getCmdLine(); - if (cmd != current) insert(cmd); + if (cmd !== current) insert(cmd); execCmdLine(cmd); switchCmd(); }; @@ -136,34 +168,77 @@ const RenderConsole = () => { cmdLine.getDoc().setValue(update(undefined) || ''); }; return ( - <React.Fragment> + <> <TitleBar label={cmd ? 'Command Line' : 'Console'}> - <Label className='dimmed' display={cmd && length > 0}> + <Label className="dimmed" display={cmd && length > 0}> {1 + index}/{length} </Label> <Space /> - <IconButton icon='TRASH' display={cmd && clear} disabled={!clear} onClick={clear} title='Clear History' /> - <IconButton icon='CROSS' display={cmd && clear} disabled={!current} onClick={doDrop} title='Remove Command' /> + <IconButton + icon="TRASH" + display={cmd && clear} + disabled={!clear} + onClick={clear} + title="Clear History" + /> + <IconButton + icon="CROSS" + display={cmd && clear} + disabled={!current} + onClick={doDrop} + title="Remove Command" + /> <Space /> - <IconButton icon='MEDIA.PREV' display={cmd} disabled={!prev} onClick={doPrev} title='Previous Command' /> - <IconButton icon='RELOAD' display={cmd} onClick={doReload} title='Reset Command Line' /> - <IconButton icon='MEDIA.NEXT' display={cmd} disabled={!next} onClick={doNext} title='Previous Command' /> + <IconButton + icon="MEDIA.PREV" + display={cmd} + disabled={!prev} + onClick={doPrev} + title="Previous Command" + /> + <IconButton + icon="RELOAD" + display={cmd} + onClick={doReload} + title="Reset Command Line" + /> + <IconButton + icon="MEDIA.NEXT" + display={cmd} + disabled={!next} + onClick={doNext} + title="Previous Command" + /> <Space /> - <IconButton icon='MEDIA.PLAY' display={cmd} onClick={doExec} title='Execute Command Line' /> - <IconButton icon='EDIT' selected={cmd} onClick={switchCmd} title='Edit Command Line' /> + <IconButton + icon="MEDIA.PLAY" + display={cmd} + onClick={doExec} + title="Execute Command Line" + /> + <IconButton + icon="EDIT" + selected={cmd} + onClick={switchCmd} + title="Edit Command Line" + /> </TitleBar> - <Text buffer={cmd ? cmdLine : Server.buffer} - mode='text' + <Text + buffer={cmd ? cmdLine : Server.buffer} + mode="text" readOnly={!cmd} - theme='ambiance' /> - </React.Fragment> + theme="ambiance" + /> + </> ); }; export const Console = () => ( - <Component id='frama-c.console' - label='Console' - title='Frama-C Server Output & Command Line'> + <Component + id="frama-c.console" + label="Console" + title="Frama-C Server Output & Command Line" + > <RenderConsole /> </Component> ); @@ -176,7 +251,9 @@ export const Status = () => { Dome.useUpdate(Server.STATUS); const s = Server.getStatus(); const n = Server.getPending(); - let led, blink, error; + let led; + let blink; + let error; switch (s) { case StatusCode.OFF: led = 'inactive'; @@ -201,13 +278,15 @@ export const Status = () => { blink = false; error = Server.getError(); break; + default: + break; } return ( - <React.Fragment> + <> <LED status={led} blink={blink} /> <Code label={s} /> - {error && <Label icon='WARNING' label={error} />} - </React.Fragment> + {error && <Label icon="WARNING" label={error} />} + </> ); }; @@ -225,6 +304,11 @@ export const Stats = () => { // --- Controller Exports // -------------------------------------------------------------------------- -export default { Control, Console, Status, Stats }; +export default { + Control, + Console, + Status, + Stats, +}; // -------------------------------------------------------------------------- diff --git a/ivette/src/renderer/Properties.tsx b/ivette/src/renderer/Properties.tsx index 06aa58ca1966635f35df53ab6eace464da027a05..f8a7b51e758616366c239bbbcdf2a066f32aea30 100644 --- a/ivette/src/renderer/Properties.tsx +++ b/ivette/src/renderer/Properties.tsx @@ -19,8 +19,9 @@ const ColumnCode: any = const ColumnTag: any = DefineColumn({ - renderValue: (l: { label: string; descr: string }) => - (<Label label={l.label} title={l.descr} />) + renderValue: (l: { label: string; descr: string }) => ( + <Label label={l.label} title={l.descr} /> + ), }); // -------------------------------------------------------------------------- @@ -28,7 +29,6 @@ const ColumnTag: any = // ------------------------------------------------------------------------- const RenderTable = () => { - // Hooks const model = React.useMemo(() => new ArrayModel(), []); const items = States.useSyncArray('kernel.properties'); @@ -43,24 +43,30 @@ const RenderTable = () => { const selection = select ? items[select.marker] : undefined; const onSelection = (item: any) => item && setSelect({ marker: item.key, - function: item.function + function: item.function, }); // Rendering return ( - <React.Fragment> - <Table model={model} + <> + <Table + model={model} selection={selection} onSelection={onSelection} scrollToItem={selection} > - <ColumnCode id='function' label='Function' width={120} /> - <ColumnCode id='descr' label='Description' fill /> - <ColumnTag id='status' label='Status' - fixed width={80} align='center' - getValue={getStatus} /> + <ColumnCode id="function" label="Function" width={120} /> + <ColumnCode id="descr" label="Description" fill /> + <ColumnTag + id="status" + label="Status" + fixed + width={80} + align="center" + getValue={getStatus} + /> </Table> - </React.Fragment> + </> ); }; @@ -69,9 +75,11 @@ const RenderTable = () => { // ------------------------------------------------------------------------- export default () => ( - <Component id='frama-c.properties' - label='Properties' - title='Registered ACSL properties status' > + <Component + id="frama-c.properties" + label="Properties" + title="Registered ACSL properties status" + > <RenderTable /> </Component> ); diff --git a/ivette/yarn.lock b/ivette/yarn.lock index 5193f6c861badfe00ed74e63092fef8450f68a8a..92759d49f5d57ca41a516fde994c7b0d52d7b4ca 100644 --- a/ivette/yarn.lock +++ b/ivette/yarn.lock @@ -876,7 +876,7 @@ core-js-pure "^3.0.0" regenerator-runtime "^0.13.4" -"@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.4.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": version "7.9.2" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.2.tgz#d90df0583a3a252f09aaa619665367bae518db06" integrity sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q== @@ -1171,6 +1171,26 @@ eslint-scope "^5.0.0" eslint-utils "^2.0.0" +"@typescript-eslint/experimental-utils@2.29.0": + version "2.29.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.29.0.tgz#3cb8060de9265ba131625a96bbfec31ba6d4a0fe" + integrity sha512-H/6VJr6eWYstyqjWXBP2Nn1hQJyvJoFdDtsHxGiD+lEP7piGnGpb/ZQd+z1ZSB1F7dN+WsxUDh8+S4LwI+f3jw== + dependencies: + "@types/json-schema" "^7.0.3" + "@typescript-eslint/typescript-estree" "2.29.0" + eslint-scope "^5.0.0" + eslint-utils "^2.0.0" + +"@typescript-eslint/parser@^2.24.0": + version "2.29.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.29.0.tgz#6e3c4e21ed6393dc05b9d8b47f0b7e731ef21c9c" + integrity sha512-H78M+jcu5Tf6m/5N8iiFblUUv+HJDguMSdFfzwa6vSg9lKR8Mk9BsgeSjO8l2EshKnJKcbv0e8IDDOvSNjl0EA== + dependencies: + "@types/eslint-visitor-keys" "^1.0.0" + "@typescript-eslint/experimental-utils" "2.29.0" + "@typescript-eslint/typescript-estree" "2.29.0" + eslint-visitor-keys "^1.1.0" + "@typescript-eslint/parser@^2.28.0": version "2.28.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.28.0.tgz#bb761286efd2b0714761cab9d0ee5847cf080385" @@ -1194,6 +1214,19 @@ semver "^6.3.0" tsutils "^3.17.1" +"@typescript-eslint/typescript-estree@2.29.0": + version "2.29.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.29.0.tgz#1be6612bb02fc37ac9f466521c1459a4744e8d3a" + integrity sha512-3YGbtnWy4az16Egy5Fj5CckkVlpIh0MADtAQza+jiMADRSKkjdpzZp/5WuvwK/Qib3Z0HtzrDFeWanS99dNhnA== + dependencies: + debug "^4.1.1" + eslint-visitor-keys "^1.1.0" + glob "^7.1.6" + is-glob "^4.0.1" + lodash "^4.17.15" + semver "^6.3.0" + tsutils "^3.17.1" + "@webassemblyjs/ast@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964" @@ -1522,6 +1555,14 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +aria-query@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-3.0.0.tgz#65b3fcc1ca1155a8c9ae64d6eee297f15d5133cc" + integrity sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w= + dependencies: + ast-types-flow "0.0.7" + commander "^2.11.0" + arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" @@ -1573,6 +1614,14 @@ array-unique@^0.3.2: resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= +array.prototype.flat@^1.2.1: + version "1.2.3" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz#0de82b426b0318dbfdb940089e38b043d37f6c7b" + integrity sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + asn1.js@^4.0.0: version "4.10.1" resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" @@ -1595,6 +1644,11 @@ assign-symbols@^1.0.0: resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= +ast-types-flow@0.0.7, ast-types-flow@^0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad" + integrity sha1-9wtzXGvKGlycItmCw+Oef+ujva0= + astral-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" @@ -1644,6 +1698,11 @@ autoprefixer@^6.3.1: postcss "^5.2.16" postcss-value-parser "^3.2.3" +axobject-query@^2.0.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.1.2.tgz#2bdffc0371e643e5f03ba99065d5179b9ca79799" + integrity sha512-ICt34ZmrVt8UQnvPl6TVyDTkmhXmAyAT4Jh5ugfGUX4MOrZ+U/ZY6/sdylRw3qGNr9Ub5AJsaHeDMzNLehRdOQ== + babel-loader@^8.0.6, babel-loader@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.1.0.tgz#c611d5112bd5209abe8b9fa84c3e4da25275f1c3" @@ -2365,7 +2424,7 @@ colors@~1.1.2: resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" integrity sha1-FopHAXVran9RoSzgyXv6KMCE7WM= -commander@^2.19.0, commander@^2.20.0: +commander@^2.11.0, commander@^2.19.0, commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -2440,6 +2499,11 @@ configstore@^5.0.1: write-file-atomic "^3.0.0" xdg-basedir "^4.0.0" +confusing-browser-globals@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.9.tgz#72bc13b483c0276801681871d4898516f8f54fdd" + integrity sha512-KbS1Y0jMtyPgIxjO7ZzMAuUpAKMt1SzCL9fsrKsX6b0zJPTaT0SiSPmewwVZg9UAO83HVIlEhZF84LIjZ0lmAw== + connect-history-api-fallback@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc" @@ -2455,6 +2519,11 @@ constants-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= +contains-path@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" + integrity sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo= + content-disposition@0.5.3: version "0.5.3" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" @@ -2756,6 +2825,11 @@ cyclist@^1.0.1: resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= +damerau-levenshtein@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz#143c1641cb3d85c60c32329e26899adea8701791" + integrity sha512-JVrozIeElnj3QzfUIt8tB8YMluBJom4Vw9qTPpjGYQ9fYlB3D/rb6OordUxf3xeFB35LKWs0xqcO5U6ySvBtug== + debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -2946,6 +3020,14 @@ dns-txt@^2.0.2: dependencies: buffer-indexof "^1.0.0" +doctrine@1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" + integrity sha1-N53Ocw9hZvds76TmcHoVmwLFpvo= + dependencies: + esutils "^2.0.2" + isarray "^1.0.0" + doctrine@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" @@ -3209,7 +3291,7 @@ elliptic@^6.0.0: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.0" -emoji-regex@^7.0.1: +emoji-regex@^7.0.1, emoji-regex@^7.0.2: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== @@ -3281,6 +3363,13 @@ errno@^0.1.3, errno@~0.1.7: dependencies: prr "~1.0.1" +error-ex@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + es-abstract@^1.17.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.5: version "1.17.5" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.5.tgz#d8c9d1d66c8981fb9200e2251d799eee92774ae9" @@ -3332,6 +3421,82 @@ escape-string-regexp@^2.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== +eslint-config-airbnb-base@^14.1.0: + version "14.1.0" + resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.1.0.tgz#2ba4592dd6843258221d9bff2b6831bd77c874e4" + integrity sha512-+XCcfGyCnbzOnktDVhwsCAx+9DmrzEmuwxyHUJpw+kqBVT744OUBrB09khgFKlK1lshVww6qXGsYPZpavoNjJw== + dependencies: + confusing-browser-globals "^1.0.9" + object.assign "^4.1.0" + object.entries "^1.1.1" + +eslint-config-airbnb-typescript@^7.2.1: + version "7.2.1" + resolved "https://registry.yarnpkg.com/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-7.2.1.tgz#bce3f02fa894d1ec2f31ac527992e03761a9b7d4" + integrity sha512-D3elVKUbdsCfkOVstSyWuiu+KGCVTrYxJPoenPIqZtL6Li/R4xBeVTXjZIui8B8D17bDN3Pz5dSr7jRLY5HqIg== + dependencies: + "@typescript-eslint/parser" "^2.24.0" + eslint-config-airbnb "^18.1.0" + eslint-config-airbnb-base "^14.1.0" + +eslint-config-airbnb@^18.1.0: + version "18.1.0" + resolved "https://registry.yarnpkg.com/eslint-config-airbnb/-/eslint-config-airbnb-18.1.0.tgz#724d7e93dadd2169492ff5363c5aaa779e01257d" + integrity sha512-kZFuQC/MPnH7KJp6v95xsLBf63G/w7YqdPfQ0MUanxQ7zcKUNG8j+sSY860g3NwCBOa62apw16J6pRN+AOgXzw== + dependencies: + eslint-config-airbnb-base "^14.1.0" + object.assign "^4.1.0" + object.entries "^1.1.1" + +eslint-import-resolver-node@^0.3.2: + version "0.3.3" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.3.tgz#dbaa52b6b2816b50bc6711af75422de808e98404" + integrity sha512-b8crLDo0M5RSe5YG8Pu2DYBj71tSB6OvXkfzwbJU2w7y8P4/yo0MyF8jU26IEuEuHF2K5/gcAJE3LhQGqBBbVg== + dependencies: + debug "^2.6.9" + resolve "^1.13.1" + +eslint-module-utils@^2.4.1: + version "2.6.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz#579ebd094f56af7797d19c9866c9c9486629bfa6" + integrity sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA== + dependencies: + debug "^2.6.9" + pkg-dir "^2.0.0" + +eslint-plugin-import@^2.20.2: + version "2.20.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.20.2.tgz#91fc3807ce08be4837141272c8b99073906e588d" + integrity sha512-FObidqpXrR8OnCh4iNsxy+WACztJLXAHBO5hK79T1Hc77PgQZkyDGA5Ag9xAvRpglvLNxhH/zSmZ70/pZ31dHg== + dependencies: + array-includes "^3.0.3" + array.prototype.flat "^1.2.1" + contains-path "^0.1.0" + debug "^2.6.9" + doctrine "1.5.0" + eslint-import-resolver-node "^0.3.2" + eslint-module-utils "^2.4.1" + has "^1.0.3" + minimatch "^3.0.4" + object.values "^1.1.0" + read-pkg-up "^2.0.0" + resolve "^1.12.0" + +eslint-plugin-jsx-a11y@^6.2.3: + version "6.2.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.2.3.tgz#b872a09d5de51af70a97db1eea7dc933043708aa" + integrity sha512-CawzfGt9w83tyuVekn0GDPU9ytYtxyxyFZ3aSWROmnRRFQFT2BiPJd7jvRdzNDi6oLWaS2asMeYSNMjWTV4eNg== + dependencies: + "@babel/runtime" "^7.4.5" + aria-query "^3.0.0" + array-includes "^3.0.3" + ast-types-flow "^0.0.7" + axobject-query "^2.0.2" + damerau-levenshtein "^1.0.4" + emoji-regex "^7.0.2" + has "^1.0.3" + jsx-ast-utils "^2.2.1" + eslint-plugin-react-hooks@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-3.0.0.tgz#9e80c71846eb68dd29c3b21d832728aa66e5bd35" @@ -3737,7 +3902,7 @@ find-cache-dir@^3.2.0: make-dir "^3.0.2" pkg-dir "^4.1.0" -find-up@^2.1.0: +find-up@^2.0.0, find-up@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= @@ -4597,6 +4762,11 @@ is-arguments@^1.0.4: resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3" integrity sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA== +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + is-binary-path@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" @@ -4981,7 +5151,7 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" -jsx-ast-utils@^2.2.3: +jsx-ast-utils@^2.2.1, jsx-ast-utils@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.2.3.tgz#8a9364e402448a3ce7f14d357738310d9248054f" integrity sha512-EdIHFMm+1BPynpKOpdPqiOsvnIrInRGJD7bzPZdPkjitQEqpdpUuFpq4T0npZFKTiB3RhWFdGN+oqOJIdhDhQA== @@ -5078,6 +5248,16 @@ linkify-it@^2.0.0: dependencies: uc.micro "^1.0.1" +load-json-file@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" + integrity sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg= + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + strip-bom "^3.0.0" + loader-runner@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" @@ -5621,7 +5801,7 @@ node-releases@^1.1.53: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.53.tgz#2d821bfa499ed7c5dffc5e2f28c88e78a08ee3f4" integrity sha512-wp8zyQVwef2hpZ/dJH7SfSrIPD6YoJz6BDQDpGEkcA0s3LpAQoxBIYmfIq6QAhC1DhwsyCgTaTTcONwX8qzCuQ== -normalize-package-data@^2.5.0: +normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== @@ -5997,6 +6177,13 @@ parse-asn1@^5.0.0: pbkdf2 "^3.0.3" safe-buffer "^5.1.1" +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= + dependencies: + error-ex "^1.2.0" + parse-passwd@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" @@ -6070,6 +6257,13 @@ path-to-regexp@0.1.7: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= +path-type@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" + integrity sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM= + dependencies: + pify "^2.0.0" + pbkdf2@^3.0.3: version "3.0.17" resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6" @@ -6113,6 +6307,13 @@ pinkie@^2.0.0: resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= +pkg-dir@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" + integrity sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s= + dependencies: + find-up "^2.1.0" + pkg-dir@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" @@ -6759,6 +6960,23 @@ read-config-file@~4.0.1: json5 "^2.1.0" lazy-val "^1.0.4" +read-pkg-up@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" + integrity sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4= + dependencies: + find-up "^2.0.0" + read-pkg "^2.0.0" + +read-pkg@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" + integrity sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg= + dependencies: + load-json-file "^2.0.0" + normalize-package-data "^2.3.2" + path-type "^2.0.0" + "readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" @@ -6995,6 +7213,13 @@ resolve@^1.10.0, resolve@^1.15.1, resolve@^1.3.2, resolve@^1.8.1: dependencies: path-parse "^1.0.6" +resolve@^1.12.0, resolve@^1.13.1: + version "1.17.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" + integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== + dependencies: + path-parse "^1.0.6" + responselike@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" @@ -7687,6 +7912,11 @@ strip-ansi@^6.0.0: dependencies: ansi-regex "^5.0.0" +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= + strip-eof@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"