-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Use Redux for state management
- Loading branch information
Showing
10 changed files
with
539 additions
and
331 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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]) | ||
// }) |
Oops, something went wrong.