Skip to content

Commit

Permalink
feat: Use Redux for state management
Browse files Browse the repository at this point in the history
  • Loading branch information
andreidmt committed Sep 12, 2020
1 parent d183184 commit 64be8c5
Show file tree
Hide file tree
Showing 10 changed files with 539 additions and 331 deletions.
238 changes: 149 additions & 89 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,151 +1,211 @@
const blessed = require("neo-blessed")
const { reduce } = require("m.xyz")
const { converge, reduce, read, pipe, map, count, max } = require("m.xyz")
const { fork } = require("child_process")

const pkg = require("../package.json")
const projectPkg = require(`${process.cwd()}/package.json`)
const { filesUI } = require("./ui.files/files")
const { resultUI } = require("./ui.result/result")
const { commandUI } = require("./ui.cli/cli")

const fileList = require("./widgets/file.list")
const cliInput = require("./widgets/cli.input")
const resultTextbox = require("./widgets/result.textbox")
const { FileList } = require("./ui.files/files.list")
const { store } = require("./index.state")

const executor = fork(`${__dirname}/lib/executor.js`)
//
module.exports = ({ requireModules, fileGlob }) => {
const executorRunArgs = reduce(
(acc, item) => [...acc, "-r", item],
[],
requireModules
)

// Separate node process to offload test file execution
const executor = fork(`${__dirname}/lib/executor.js`, executorRunArgs)

executor.on("message", ({ path, stdout, stderr, code }) => {
FileList.update(path, {
isRunning: false,
stdout,
stderr,
code,
})
})

const screen = blessed.screen({
title: `${pkg.name} v${pkg.version}`,
/**
*
*/

// The width of tabs within an element's content
tabSize: 2,
const screen = blessed.screen({
title: `${projectPkg.name} v${projectPkg.version}`,

// Automatically position child elements with border and padding in mind
autoPadding: true,
// The width of tabs within an element's content
tabSize: 2,

// Whether the focused element grabs all keypresses
grabKeys: true,
// Automatically position child elements with border and padding in mind
autoPadding: true,

// Prevent keypresses from being received by any element
lockKeys: true,
// Whether the focused element grabs all keypresses
grabKeys: true,

// Automatically "dock" borders with other elements instead of overlapping,
// depending on position
dockBorders: true,
// Prevent keypresses from being received by any element
// lockKeys: true,

// Allow for rendering of East Asian double-width characters, utf-16
// surrogate pairs, and unicode combining characters.
fullUnicode: true,
// Automatically "dock" borders with other elements instead of overlapping,
// depending on position
dockBorders: true,

debug: true,
})
// Allow for rendering of East Asian double-width characters, utf-16
// surrogate pairs, and unicode combining characters.
fullUnicode: true,

debug: true,
})

module.exports = ({ requireModules, fileGlob }) => {
/**
* List with test file names
*/

const list = fileList({
const [filesRef, renderFilesUI] = filesUI({
parent: screen,
fileGlob,
onChange: path => {
store.dispatch({
type: "USE-STATE.SET",
payload: { id: "fileSelectId", value: path },
})
},
onRun: path => {
store.dispatch({
type: "USE-STATE.SET",
payload: { id: "fileSelectId", value: path },
})

executor.send({
path,
runArgs: executorRunArgs,
})

FileList.update(path, {
isRunning: true,
})
},
})

filesRef.focus()

/**
* Text box with test results
*/

const box = resultTextbox({
const [resultRef, renderResultUI] = resultUI({
parent: screen,
label: `${projectPkg.name} v${projectPkg.version}`,
width: `100%-${list.width}`,
})

list.on("run", (file, path) => {
// box.setLabel(` ${name} `)
box.setContent(JSON.stringify({ file, path }, 2, 2))

executor.on("message", ({ stdout, stderr }) => {
box.setContent(`${box.getContent()}\n${stdout}${stderr}`)

screen.render()
})

executor.send({
path,
runArgs: reduce((acc, item) => [...acc, "-r", item], [], requireModules),
})

screen.render()
})

list.focus()

/**
* Command Line Interface input for:
*
* - searching & highlighting through files
*/

const input = cliInput({
const handleCLIHide = () => {
store.dispatch({
type: "USE-STATE.SET",
payload: { id: "isCLIVisible", value: false },
})
}

const [, renderCommandUI] = commandUI({
parent: screen,
width: "100%",
height: "1px",
top: "100%-1",
left: "0",
onChange: source => {
store.dispatch({
type: "USE-STATE.SET",
payload: { id: "cliQuery", value: source },
})
},
onSubmit: source => {
filesRef.selectFirstWith(source)

handleCLIHide()
},
onCancel: handleCLIHide,
onBlur: handleCLIHide,
})

input.on("change", value => {
list.setHighlight(value)
screen.on("keypress", (code, key) => {
if (key.full === "right") {
resultRef.focus()
}

screen.render()
if (key.full === "left") {
filesRef.focus()
}
})

input.on("cancel", () => {
list.setHighlight("")
input.hide()

screen.render()
})

input.on("submit", () => {
input.hide()

screen.render()
})

input.hide()

/**
* Global shortcuts
*/

screen.key("/", () => {
input.setValue("")
input.show()
input.focus()
input.setFront()

screen.render()
})

screen.key("C-l", () => {
list.setHighlight("")
input.hide()

screen.render()
store.dispatch({
type: "USE-STATE.SET",
payload: { id: "isCLIVisible", value: true },
})
})

screen.key("S-tab", () => {
screen.focusPrevious()
screen.render()
})

screen.key("tab", () => {
screen.focusNext()
screen.render()
})

/* eslint-disable unicorn/no-process-exit */
screen.key(["C-c"], () => {
return process.exit(0)
})

screen.render()
// Load all files matching glob
FileList.read(fileGlob)

/*
* When any part of state changes, re-render all UI wigets
*/
store.subscribe(() => {
const currentState = store.getState()
const [cliQuery, fileSelectId, isCLIVisible] = converge(
(...params) => params,
[
read(["USE-STATE", "cliQuery"], ""),
read(["USE-STATE", "fileSelectId"], null),
read(["USE-STATE", "isCLIVisible"], false),
]
)(currentState)

//
const { items, byId } = FileList.selector(currentState)
const files = items()
const listWidth = pipe(map([read("name"), count]), max)(files) + 6

renderFilesUI({
items: files,
highlight: cliQuery,
width: listWidth,
})

//
const { name, stdout } = byId(fileSelectId, {})

renderResultUI({
width: `100%-${listWidth}`,
label: name,
content: stdout,
})

//
renderCommandUI({
value: cliQuery,
isVisible: isCLIVisible,
})

screen.render()
})
}
26 changes: 26 additions & 0 deletions src/index.state.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const { createStore, combineReducers } = require("redux")

const { FileList } = require("./ui.files/files.list")

// Global state store
const store = createStore(
combineReducers({
"USE-STATE": (state = {}, { type, payload: { id, value } = {} }) => {
switch (type) {
case "USE-STATE.SET":
return {
...state,
[id]: value,
}
default:
return state
}
},
[FileList.name]: FileList.reducer,
})
)

// Link list to app store
FileList.set({ dispatch: store.dispatch })

module.exports = { store }
9 changes: 9 additions & 0 deletions src/lib/depends-on.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// const espree = require("espree")
// const fs = require("fs")
// const path = require("path")

// fs.readFile(path.resolve(`${__dirname}/all/all.js`), "utf8", (err, data) => {
// console.log(data)
// const ast = espree.parse(data, { ecmaVersion: 8, sourceType: "module" })
// console.log(ast.body[0])
// })
Loading

0 comments on commit 64be8c5

Please sign in to comment.