diff --git a/packages/xod-arduino-deploy/src/boardsParser.js b/packages/xod-arduino-deploy/src/boardsParser.js index feee32a51..8ff481604 100644 --- a/packages/xod-arduino-deploy/src/boardsParser.js +++ b/packages/xod-arduino-deploy/src/boardsParser.js @@ -56,35 +56,27 @@ import * as Utils from './utils'; // :: String -> Object const parseTxtConfig = R.compose( R.reduce( - (acc, [tokens, value]) => R.compose( - R.assocPath(R.__, value, acc), - R.when( - R.both( - R.propEq(1, 'menu'), - R.compose(R.equals(4), R.length) - ), - R.append('cpuName') - ) - )(tokens), + (acc, [tokens, value]) => + R.compose( + R.assocPath(R.__, value, acc), + R.when( + R.both(R.propEq(1, 'menu'), R.compose(R.equals(4), R.length)), + R.append('cpuName') + ) + )(tokens), {} ), - R.map(R.compose( - R.zipWith(R.call, [R.split('.'), R.identity]), - R.split('=') - )), + R.map(R.compose(R.zipWith(R.call, [R.split('.'), R.identity]), R.split('='))), R.reject(R.test(/^(#|$)/)), R.map(R.trim), - R.split(/$/mg) + R.split(/$/gm) ); /** Returns a parsed boards.txt from specified platform directory. */ // :: Path -> Promise (StrMap BoardPrefs) Error -export const loadBoards = R.curry( - platformDir => R.pipeP( - () => Promise.resolve(path.resolve( - platformDir, - 'boards.txt' - )), +export const loadBoards = R.curry(platformDir => + R.pipeP( + () => Promise.resolve(path.resolve(platformDir, 'boards.txt')), filePath => fse.readFile(filePath, 'utf8'), parseTxtConfig )() @@ -97,32 +89,26 @@ export const loadBoards = R.curry( // ============================================================================= // :: FQBN -> Map boardIdentifier BoardPrefs -> BoardPrefs -const getBoardPrefsByFqbn = R.curry( - (fqbn, boardPrefsMap) => { - const pabc = Utils.parseFQBN(fqbn); - const getCpuPrefs = R.pathOr({}, ['menu', 'cpu', pabc.cpu]); +const getBoardPrefsByFqbn = R.curry((fqbn, boardPrefsMap) => { + const pabc = Utils.parseFQBN(fqbn); + const getCpuPrefs = R.pathOr({}, ['menu', 'cpu', pabc.cpu]); - return R.compose( - R.omit('menu'), - boardPrefs => R.compose( - R.mergeDeepLeft(boardPrefs), - getCpuPrefs - )(boardPrefs), - R.prop(pabc.boardIdentifier) - )(boardPrefsMap); - } -); + return R.compose( + R.omit('menu'), + boardPrefs => + R.compose(R.mergeDeepLeft(boardPrefs), getCpuPrefs)(boardPrefs), + R.prop(pabc.boardIdentifier) + )(boardPrefsMap); +}); /** * Loads board preferences from boards.txt and returns only preferences for specified board. */ // :: FQBN -> Path -> Promise BoardPrefs Error -export const loadBoardPrefs = R.curry( - (fqbn, packagesDir) => { - const architectureDir = Utils.getArchitectureDirectory(fqbn, packagesDir); - return loadBoards(architectureDir).then(getBoardPrefsByFqbn(fqbn)); - } -); +export const loadBoardPrefs = R.curry((fqbn, packagesDir) => { + const architectureDir = Utils.getArchitectureDirectory(fqbn, packagesDir); + return loadBoards(architectureDir).then(getBoardPrefsByFqbn(fqbn)); +}); // BoardInfo :: { // name: String, @@ -132,22 +118,20 @@ export const loadBoardPrefs = R.curry( // } // :: FQBN -> Path -> [BoardInfo] -export const loadPABs = R.curry( - (fqbn, packagesDir) => { - const pab = Utils.parseFQBN(fqbn); - const architectureDir = Utils.getArchitectureDirectory(fqbn, packagesDir); - return loadBoards(architectureDir).then(R.compose( +export const loadPABs = R.curry((fqbn, packagesDir) => { + const pab = Utils.parseFQBN(fqbn); + const architectureDir = Utils.getArchitectureDirectory(fqbn, packagesDir); + return loadBoards(architectureDir).then( + R.compose( R.values, - R.mapObjIndexed( - (boardData, boardKey) => ({ - name: boardData.name, - package: pab.package, - architecture: pab.architecture, - boardIdentifier: boardKey, - }) - ) - )); - } -); + R.mapObjIndexed((boardData, boardKey) => ({ + name: boardData.name, + package: pab.package, + architecture: pab.architecture, + boardIdentifier: boardKey, + })) + ) + ); +}); export default loadBoardPrefs; diff --git a/packages/xod-arduino-deploy/src/builder.js b/packages/xod-arduino-deploy/src/builder.js index cde506a03..f3ba6f9d9 100644 --- a/packages/xod-arduino-deploy/src/builder.js +++ b/packages/xod-arduino-deploy/src/builder.js @@ -19,7 +19,9 @@ export const composeCommand = ( buildDir, builderToolDir ) => { - const builderExecFileName = (Utils.isWindows) ? 'arduino-builder.exe' : 'arduino-builder'; + const builderExecFileName = Utils.isWindows + ? 'arduino-builder.exe' + : 'arduino-builder'; const builderExec = path.join(builderToolDir, builderExecFileName); const builderHardware = path.join(builderToolDir, 'hardware'); @@ -40,7 +42,14 @@ export const composeCommand = ( // :: Path -> FQBN -> Path -> Path -> Path -> PortName -> Promise { exitCode, stdout, stderr } Error export const build = R.curry( - (sketchFilePath, fqbn, packagesDir, librariesDir, buildDir, builderToolDir) => { + ( + sketchFilePath, + fqbn, + packagesDir, + librariesDir, + buildDir, + builderToolDir + ) => { const cmd = composeCommand( sketchFilePath, fqbn, @@ -50,7 +59,8 @@ export const build = R.curry( builderToolDir ); - return fse.ensureDir(buildDir) + return fse + .ensureDir(buildDir) .then(() => cpp.exec(cmd)) .then(Utils.normalizeChildProcessResult); } diff --git a/packages/xod-arduino-deploy/src/packageManager.js b/packages/xod-arduino-deploy/src/packageManager.js index 50704dc3a..fe88dfdb3 100644 --- a/packages/xod-arduino-deploy/src/packageManager.js +++ b/packages/xod-arduino-deploy/src/packageManager.js @@ -15,7 +15,7 @@ import * as Utils from './utils'; // ============================================================================= // :: Path -> Boolean -const isDir = (dir) => { +const isDir = dir => { try { return fse.statSync(dir).isDirectory(); } catch (err) { @@ -25,12 +25,14 @@ const isDir = (dir) => { }; // :: Path -> Promise Boolean Error -const isFile = filePath => fse.stat(filePath) - .then(stat => stat.isFile()) - .catch((err) => { - if (err.code === 'ENOENT') return false; - return Promise.reject(err); - }); +const isFile = filePath => + fse + .stat(filePath) + .then(stat => stat.isFile()) + .catch(err => { + if (err.code === 'ENOENT') return false; + return Promise.reject(err); + }); // ============================================================================= // @@ -39,13 +41,14 @@ const isFile = filePath => fse.stat(filePath) // ============================================================================= // :: URL -> Path -> Promise Path Error -export const downloadFileFromUrl = R.curry( - (url, destinationPath) => fetch(url) +export const downloadFileFromUrl = R.curry((url, destinationPath) => + fetch(url) .then(tapP(() => fse.ensureDir(path.dirname(destinationPath)))) - .then((res) => { + .then(res => { const file = fse.createWriteStream(destinationPath); return new Promise((resolve, reject) => { - res.body.pipe(file) + res.body + .pipe(file) .on('error', reject) .on('finish', () => { file.close(() => resolve(destinationPath)); @@ -55,22 +58,25 @@ export const downloadFileFromUrl = R.curry( ); // :: Path -> Promise Path Error -export const extractBzip2 = archivePath => new Promise( - (resolve, reject) => { - const resultPath = archivePath.substr(0, archivePath.length - '.bz2'.length); +export const extractBzip2 = archivePath => + new Promise((resolve, reject) => { + const resultPath = archivePath.substr( + 0, + archivePath.length - '.bz2'.length + ); const source = fse.createReadStream(archivePath); const target = fse.createWriteStream(resultPath); - source - .on('error', reject); + source.on('error', reject); target .on('error', reject) - .on('finish', () => fse.remove(archivePath).then(() => resolve(resultPath))); + .on('finish', () => + fse.remove(archivePath).then(() => resolve(resultPath)) + ); source.pipe(bz2()).pipe(target); - } -); + }); /** * Unpacks tar[.gz] into the same directory. @@ -81,40 +87,42 @@ export const extractBzip2 = archivePath => new Promise( export const extractArchive = (archivePath, strip = 0) => { const dir = path.dirname(archivePath); - return tar.x({ file: archivePath, cwd: dir, strip }) + return tar + .x({ file: archivePath, cwd: dir, strip }) .then(() => fse.remove(archivePath)) .then(() => dir); }; // InstallResult :: { path: Path, installed: Boolean, downloaded: Boolean } // :: URL -> Path -> Path -> Number -> Promise InstallResult Error -export const downloadAndExtract = R.curry( - (url, unpackDest, strip) => { - const archiveName = path.basename(url); - const archivePath = path.join(unpackDest, archiveName); +export const downloadAndExtract = R.curry((url, unpackDest, strip) => { + const archiveName = path.basename(url); + const archivePath = path.join(unpackDest, archiveName); - let downloaded = false; + let downloaded = false; - return isFile(archivePath) - .then(isExist => ( + return isFile(archivePath) + .then( + isExist => !isExist ? downloadFileFromUrl(url, archivePath) : Promise.resolve() - )) - .then(() => { downloaded = true; }) - .then(() => { - // if its bzip — extract bzip first, and then extract as usual - if (path.extname(archivePath) === '.bz2') { - return extractBzip2(archivePath); - } - return archivePath; - }) - .then(tarPath => extractArchive(tarPath, strip)) - .then(() => ({ - path: unpackDest, - downloaded, - installed: true, - })); - } -); + ) + .then(() => { + downloaded = true; + }) + .then(() => { + // if its bzip — extract bzip first, and then extract as usual + if (path.extname(archivePath) === '.bz2') { + return extractBzip2(archivePath); + } + return archivePath; + }) + .then(tarPath => extractArchive(tarPath, strip)) + .then(() => ({ + path: unpackDest, + downloaded, + installed: true, + })); +}); // ============================================================================= // @@ -129,16 +137,17 @@ export const doesHardwareExist = R.compose( ); // :: Tool -> ToolsDirPath -> Boolean -export const doesToolExist = R.curry( - (toolsDir, tool) => R.compose( - isDir, - Utils.getToolVersionDirectory - )(tool.name, tool.version, toolsDir) +export const doesToolExist = R.curry((toolsDir, tool) => + R.compose(isDir, Utils.getToolVersionDirectory)( + tool.name, + tool.version, + toolsDir + ) ); // :: [Tool] -> ToolsDirPath -> Boolean -export const doesAllToolsExist = R.curry( - (toolsDir, tools) => R.all(doesToolExist(toolsDir), tools) +export const doesAllToolsExist = R.curry((toolsDir, tools) => + R.all(doesToolExist(toolsDir), tools) ); // :: FQBN -> PackageIndex -> Path -> Boolean @@ -162,51 +171,46 @@ export const doesArchitectureInstalled = R.curry( // ============================================================================= // :: FQBN -> PackagesDirPath -> PackageIndex -> Promise InstallResult Error -export const installHardware = R.curry( - (fqbn, packagesDir, packageIndex) => { - const architectureDir = Utils.getArchitectureDirectory(fqbn, packagesDir); - - if (doesHardwareExist(fqbn, packagesDir)) { - return Promise.resolve({ - path: architectureDir, - downloaded: false, - installed: true, - }); - } +export const installHardware = R.curry((fqbn, packagesDir, packageIndex) => { + const architectureDir = Utils.getArchitectureDirectory(fqbn, packagesDir); + + if (doesHardwareExist(fqbn, packagesDir)) { + return Promise.resolve({ + path: architectureDir, + downloaded: false, + installed: true, + }); + } - const architecture = Utils.getArchitectureByFqbn(fqbn, packageIndex); - const url = R.prop('url', architecture); + const architecture = Utils.getArchitectureByFqbn(fqbn, packageIndex); + const url = R.prop('url', architecture); - return downloadAndExtract(url, architectureDir, 1); - } -); + return downloadAndExtract(url, architectureDir, 1); +}); // :: FQBN -> PackagesDirPath -> PackageIndex -> Promise InstallResult Error -export const installTools = R.curry( - (fqbn, packagesDir, packageIndex) => { - const tools = Utils.getToolsByFqbn(fqbn, packageIndex); - const toolsDir = Utils.getToolsDirectory(fqbn, packagesDir); - - if (doesAllToolsExist(toolsDir, tools)) { - return Promise.resolve({ - path: toolsDir, - downloaded: false, - installed: true, - }); - } +export const installTools = R.curry((fqbn, packagesDir, packageIndex) => { + const tools = Utils.getToolsByFqbn(fqbn, packageIndex); + const toolsDir = Utils.getToolsDirectory(fqbn, packagesDir); + + if (doesAllToolsExist(toolsDir, tools)) { + return Promise.resolve({ + path: toolsDir, + downloaded: false, + installed: true, + }); + } - const url = Utils.getToolsUrl(fqbn, packageIndex); + const url = Utils.getToolsUrl(fqbn, packageIndex); - return downloadAndExtract(url, toolsDir, 0); - } -); + return downloadAndExtract(url, toolsDir, 0); +}); // :: FQBN -> PackagesDirPath -> PackageIndex -> // Promise { hardware: InstallResult, tools: InstallResult } Error -export const installArchitecture = R.curry( - (fqbn, packagesDir, packageIndex) => Promise.all([ +export const installArchitecture = R.curry((fqbn, packagesDir, packageIndex) => + Promise.all([ installHardware(fqbn, packagesDir, packageIndex), installTools(fqbn, packagesDir, packageIndex), - ]) - .then(([hardware, tools]) => ({ hardware, tools })) + ]).then(([hardware, tools]) => ({ hardware, tools })) ); diff --git a/packages/xod-arduino-deploy/src/serialport.js b/packages/xod-arduino-deploy/src/serialport.js index 0ac178f5f..2594141eb 100644 --- a/packages/xod-arduino-deploy/src/serialport.js +++ b/packages/xod-arduino-deploy/src/serialport.js @@ -43,27 +43,25 @@ const delay = ms => () => delayP(ms); // :: PortOptions -> Port -> Promise Port Error const setPortOptions = R.curry( - (options, port) => new Promise( - (resolve, reject) => { - port.set(options, (err) => { - if (err) { reject(err); } + (options, port) => + new Promise((resolve, reject) => { + port.set(options, err => { + if (err) { + reject(err); + } resolve(port); }); - } - ) + }) ); // :: Port -> Promise Port Error -const flushPort = port => new Promise( - (resolve, reject) => { - port.flush( - (err) => { - if (err) reject(err); - resolve(port); - } - ); - } -); +const flushPort = port => + new Promise((resolve, reject) => { + port.flush(err => { + if (err) reject(err); + resolve(port); + }); + }); // ============================================================================= // @@ -74,7 +72,7 @@ const flushPort = port => new Promise( /** Lists the available {@link Port}s. * @type {Function} * @return {Promise} */ - // :: () -> [PortInfo] +// :: () -> [PortInfo] export const listPorts = () => new Promise((resolve, reject) => { // serialport is a native module that can conflict in ABI versions @@ -113,14 +111,13 @@ export const openPort = (portName, opts = {}) => }); // :: Port -> Promise Port Error -export const closePort = port => new Promise( - (resolve, reject) => { - port.close((err) => { +export const closePort = port => + new Promise((resolve, reject) => { + port.close(err => { if (err) reject(err); resolve(port); }); - } -); + }); // :: PortName -> (String -> *) -> Promise Port Error export const openAndReadPort = (portName, onData, onClose) => { @@ -131,30 +128,32 @@ export const openAndReadPort = (portName, onData, onClose) => { return openPort(portName, { baudRate: 115200, parser: readline('\n'), - }).then(R.tap( - (port) => { + }).then( + R.tap(port => { port.on('data', onData); port.on('close', onClose); - } - )); + }) + ); }; // :: PortName -> Promise Port Error -export const flushSerialBuffer = portName => openPort(portName) - .then(flushPort) - .then(setPortOptions({ dtr: false, rts: false })) - .then(tapP(delay(100))) - .then(setPortOptions({ dtr: true, rts: true })) - .then(closePort); +export const flushSerialBuffer = portName => + openPort(portName) + .then(flushPort) + .then(setPortOptions({ dtr: false, rts: false })) + .then(tapP(delay(100))) + .then(setPortOptions({ dtr: true, rts: true })) + .then(closePort); // :: PortName -> Promise Port Error -export const touchPort1200 = portName => openPort(portName, { baudRate: 1200 }) - .then(setPortOptions({ dtr: false })) - .then(closePort) - .then(tapP(delay(400))); +export const touchPort1200 = portName => + openPort(portName, { baudRate: 1200 }) + .then(setPortOptions({ dtr: false })) + .then(closePort) + .then(tapP(delay(400))); // :: PortName -> Promise PortName Error -export const waitNewPort = async (initialPorts) => { +export const waitNewPort = async initialPorts => { let beforePorts = initialPorts; let elapsed = 0; const enumDelay = 50; @@ -162,7 +161,7 @@ export const waitNewPort = async (initialPorts) => { while (elapsed < 10000) { const afterPorts = await listPorts(); const diff = R.differenceWith( - (a, b) => (a.comName === b.comName), + (a, b) => a.comName === b.comName, afterPorts, beforePorts ); diff --git a/packages/xod-arduino-deploy/src/uploader.js b/packages/xod-arduino-deploy/src/uploader.js index 9d1d89e02..901bfd996 100644 --- a/packages/xod-arduino-deploy/src/uploader.js +++ b/packages/xod-arduino-deploy/src/uploader.js @@ -6,7 +6,12 @@ import packageIndex from './packageIndex.json'; import * as Utils from './utils'; import { loadBoardPrefs } from './boardsParser'; import { build } from './builder'; -import { flushSerialBuffer, touchPort1200, waitNewPort, listPorts } from './serialport'; +import { + flushSerialBuffer, + touchPort1200, + waitNewPort, + listPorts, +} from './serialport'; // ============================================================================= // @@ -14,37 +19,66 @@ import { flushSerialBuffer, touchPort1200, waitNewPort, listPorts } from './seri // // ============================================================================= -const bossacCommand = (opts) => { - const { toolDirPath, sketchFileName, buildDir, boardPrefs, portName, debug = false } = opts; - - const toolExecFile = (Utils.isWindows) ? 'bossac.exe' : 'bossac'; +const bossacCommand = opts => { + const { + toolDirPath, + sketchFileName, + buildDir, + boardPrefs, + portName, + debug = false, + } = opts; + + const toolExecFile = Utils.isWindows ? 'bossac.exe' : 'bossac'; const toolExecPath = path.join(toolDirPath, toolExecFile); const artifactPath = path.join(buildDir, `${sketchFileName}.bin`); - const verbose = (debug) ? ' -i -d' : ''; + const verbose = debug ? ' -i -d' : ''; - return `"${toolExecPath}"${verbose} --port=${portName} -U ${boardPrefs.upload.native_usb} -e -w -b "${artifactPath}" -R`; + return `"${toolExecPath}"${verbose} --port=${portName} -U ${ + boardPrefs.upload.native_usb + } -e -w -b "${artifactPath}" -R`; }; -const avrdudeCommand = (opts) => { - const { toolDirPath, sketchFileName, buildDir, boardPrefs, portName, debug = false } = opts; - - const toolExecFile = (Utils.isWindows) ? 'bin/avrdude.exe' : 'bin/avrdude'; +const avrdudeCommand = opts => { + const { + toolDirPath, + sketchFileName, + buildDir, + boardPrefs, + portName, + debug = false, + } = opts; + + const toolExecFile = Utils.isWindows ? 'bin/avrdude.exe' : 'bin/avrdude'; const toolExecPath = path.join(toolDirPath, toolExecFile); const configFile = path.join(toolDirPath, 'etc/avrdude.conf'); const artifactPath = path.join(buildDir, `${sketchFileName}.hex`); - const verbose = (debug) ? ' -v -v' : ''; + const verbose = debug ? ' -v -v' : ''; - const mcu = (boardPrefs.build.emu) ? boardPrefs.build.emu.mcu : boardPrefs.build.mcu; + const mcu = boardPrefs.build.emu + ? boardPrefs.build.emu.mcu + : boardPrefs.build.mcu; - return `"${toolExecPath}" -C "${configFile}"${verbose} -p ${mcu} -c ${boardPrefs.upload.protocol} -P ${portName} -b ${boardPrefs.upload.speed} -D "-Uflash:w:${artifactPath}:i"`; + return `"${toolExecPath}" -C "${configFile}"${verbose} -p ${mcu} -c ${ + boardPrefs.upload.protocol + } -P ${portName} -b ${ + boardPrefs.upload.speed + } -D "-Uflash:w:${artifactPath}:i"`; }; -const openocdCommand = (opts) => { - const { toolDirPath, sketchFileName, buildDir, boardPrefs, platformDir, debug = false } = opts; +const openocdCommand = opts => { + const { + toolDirPath, + sketchFileName, + buildDir, + boardPrefs, + platformDir, + debug = false, + } = opts; - const toolExecFile = (Utils.isWindows) ? 'bin/openocd.exe' : 'bin/openocd'; + const toolExecFile = Utils.isWindows ? 'bin/openocd.exe' : 'bin/openocd'; const toolExecPath = path.join(toolDirPath, toolExecFile); const scriptsDir = path.join(toolDirPath, 'share/openocd/scripts'); const buildVariant = path.join( @@ -57,7 +91,7 @@ const openocdCommand = (opts) => { const bootloaderSize = boardPrefs.bootloader.size || '0x2000'; - const verbose = (debug) ? ' -d2' : ''; + const verbose = debug ? ' -d2' : ''; return `"${toolExecPath}"${verbose} -s "${scriptsDir}" -f "${buildVariant}" -c "telnet_port disabled; program ${artifactPath} verify reset ${bootloaderSize}; shutdown"`; }; @@ -77,31 +111,56 @@ export const composeCommand = R.curry( const toolName = Utils.getBoardUploadTool(boardPrefs); const toolVersion = Utils.getToolVersion(fqbn, toolName, packageIndex); const toolsDir = Utils.getToolsDirectory(fqbn, packagesDir); - const toolDirPath = Utils.getToolVersionDirectory(toolName, toolVersion, toolsDir); + const toolDirPath = Utils.getToolVersionDirectory( + toolName, + toolVersion, + toolsDir + ); const sketchFileName = path.basename(sketchFilePath); switch (toolName) { case 'bossac': - return bossacCommand({ toolDirPath, sketchFileName, buildDir, boardPrefs, portName }); + return bossacCommand({ + toolDirPath, + sketchFileName, + buildDir, + boardPrefs, + portName, + }); case 'avrdude': - return avrdudeCommand({ toolDirPath, sketchFileName, buildDir, boardPrefs, portName }); + return avrdudeCommand({ + toolDirPath, + sketchFileName, + buildDir, + boardPrefs, + portName, + }); case 'openocd': case 'openocd-withbootsize': - return openocdCommand({ toolDirPath, sketchFileName, buildDir, boardPrefs, platformDir }); + return openocdCommand({ + toolDirPath, + sketchFileName, + buildDir, + boardPrefs, + platformDir, + }); default: { - throw new Error(`Architecture "${pab.architecture}" (${fqbn}) is not supported now. You can make a request to add it at https://forum.xod.io/`); + throw new Error( + `Architecture "${ + pab.architecture + }" (${fqbn}) is not supported now. You can make a request to add it at https://forum.xod.io/` + ); } } } ); // :: BoardPrefs -> Boolean -const getUploadBoolProp = R.curry( - (propName, boardPrefs) => R.compose( - R.equals('true'), - R.pathOr('false', ['upload', propName]) - )(boardPrefs) +const getUploadBoolProp = R.curry((propName, boardPrefs) => + R.compose(R.equals('true'), R.pathOr('false', ['upload', propName]))( + boardPrefs + ) ); // :: PortName -> BoardPrefs -> Promise PortName Error @@ -117,10 +176,11 @@ const prepareBoardUpload = async (portName, boardPrefs) => { } const protocol = R.path(['upload', 'protocol'], boardPrefs); - const uploadPortName = (getUploadBoolProp('wait_for_upload_port', boardPrefs)) ? - await waitNewPort(ports) : portName; + const uploadPortName = getUploadBoolProp('wait_for_upload_port', boardPrefs) + ? await waitNewPort(ports) + : portName; - return (protocol === 'sam-ba') ? path.basename(uploadPortName) : uploadPortName; + return protocol === 'sam-ba' ? path.basename(uploadPortName) : uploadPortName; }; // :: Path -> FQBN -> Path -> Path -> PortName -> Promise { exitCode, stdout, stderr } Error @@ -129,7 +189,12 @@ export const upload = R.curry( const boardPrefs = await loadBoardPrefs(fqbn, packagesDir); const newPortName = await prepareBoardUpload(portName, boardPrefs); const cmd = composeCommand( - sketchFilePath, fqbn, packagesDir, buildDir, boardPrefs, newPortName + sketchFilePath, + fqbn, + packagesDir, + buildDir, + boardPrefs, + newPortName ); const exec = await cpp.exec(cmd); return Utils.normalizeChildProcessResult(exec); @@ -138,24 +203,43 @@ export const upload = R.curry( // :: Path -> FQBN -> Path -> Path -> Path -> PortName -> Promise { exitCode, stdout, stderr } Error export const buildAndUpload = R.curry( - (sketchFilePath, fqbn, packagesDir, librariesDir, buildDir, portName, builderToolDir) => - build(sketchFilePath, fqbn, packagesDir, librariesDir, buildDir, builderToolDir) - .then((buildResult) => { - if (buildResult.exitCode !== 0) { - return Promise.reject(Object.assign(new Error(buildResult.stderr), buildResult)); - } + ( + sketchFilePath, + fqbn, + packagesDir, + librariesDir, + buildDir, + portName, + builderToolDir + ) => + build( + sketchFilePath, + fqbn, + packagesDir, + librariesDir, + buildDir, + builderToolDir + ).then(buildResult => { + if (buildResult.exitCode !== 0) { + return Promise.reject( + Object.assign(new Error(buildResult.stderr), buildResult) + ); + } - return upload(sketchFilePath, fqbn, packagesDir, buildDir, portName) - .then((uploadResult) => { - if (uploadResult.exitCode !== 0) { - return Promise.reject(Object.assign(new Error(uploadResult.stderr), uploadResult)); - } - - return { - exitCode: uploadResult.exitCode, - stdout: [buildResult.stdout, uploadResult.stdout].join('\n'), - stderr: [buildResult.stderr, uploadResult.stderr].join('\n'), - }; - }); - }) + return upload(sketchFilePath, fqbn, packagesDir, buildDir, portName).then( + uploadResult => { + if (uploadResult.exitCode !== 0) { + return Promise.reject( + Object.assign(new Error(uploadResult.stderr), uploadResult) + ); + } + + return { + exitCode: uploadResult.exitCode, + stdout: [buildResult.stdout, uploadResult.stdout].join('\n'), + stderr: [buildResult.stderr, uploadResult.stderr].join('\n'), + }; + } + ); + }) ); diff --git a/packages/xod-arduino-deploy/src/utils.js b/packages/xod-arduino-deploy/src/utils.js index dfcc217cf..db82f5ded 100644 --- a/packages/xod-arduino-deploy/src/utils.js +++ b/packages/xod-arduino-deploy/src/utils.js @@ -2,7 +2,7 @@ import os from 'os'; import * as R from 'ramda'; import path from 'path'; -export const isWindows = (os.platform() === 'win32'); +export const isWindows = os.platform() === 'win32'; // :: FQBN -> String const parseCpuParam = R.compose( @@ -24,62 +24,63 @@ export const parseFQBN = R.compose( ); // :: { package: String, architecture: String, boardIdentifier: String, cpu: String } -> FQBN -export const strigifyFQBN = ({ package: pkg, architecture, boardIdentifier, cpu = '' }) => ( - `${pkg}:${architecture}:${boardIdentifier}${(cpu && cpu.length > 0) ? `:cpu=${cpu}` : ''}` -); +export const strigifyFQBN = ({ + package: pkg, + architecture, + boardIdentifier, + cpu = '', +}) => + `${pkg}:${architecture}:${boardIdentifier}${ + cpu && cpu.length > 0 ? `:cpu=${cpu}` : '' + }`; // :: FQBN -> String -> PackageIndex -> String -export const getToolVersion = R.curry( - (fqbn, toolName, packageIndex) => R.compose( - pab => R.compose( - R.ifElse( - // ArduinoIDE has a preinstalled avr hardware - // And some boards (arduino:samd:tian) uses tools from this package. - // But PackageIndex doesn't has it in the `toolsDependencies` list. - // So for these cases we have to fallback to avr architecture - // to search for tools... - R.both(R.isNil, () => (pab.architecture !== 'avr')), - () => getToolVersion( - `${pab.package}:avr:${pab.boardIdentifier}`, - toolName, - packageIndex +export const getToolVersion = R.curry((fqbn, toolName, packageIndex) => + R.compose( + pab => + R.compose( + R.ifElse( + // ArduinoIDE has a preinstalled avr hardware + // And some boards (arduino:samd:tian) uses tools from this package. + // But PackageIndex doesn't has it in the `toolsDependencies` list. + // So for these cases we have to fallback to avr architecture + // to search for tools... + R.both(R.isNil, () => pab.architecture !== 'avr'), + () => + getToolVersion( + `${pab.package}:avr:${pab.boardIdentifier}`, + toolName, + packageIndex + ), + R.prop('version') ), - R.prop('version') - ), - R.find(R.propEq('name', toolName)), - R.prop('tools'), - R.find(R.propEq('architecture', pab.architecture)), - R.prop(pab.package) - )(packageIndex), + R.find(R.propEq('name', toolName)), + R.prop('tools'), + R.find(R.propEq('architecture', pab.architecture)), + R.prop(pab.package) + )(packageIndex), parseFQBN )(fqbn) ); // :: { package: String, architecture: String } -> Path -> Path -export const getArchitectureDirectory = R.curry( - (fqbn, packagesDir) => { - const pab = parseFQBN(fqbn); - return path.join(packagesDir, pab.package, 'hardware', pab.architecture); - } -); +export const getArchitectureDirectory = R.curry((fqbn, packagesDir) => { + const pab = parseFQBN(fqbn); + return path.join(packagesDir, pab.package, 'hardware', pab.architecture); +}); // :: FQBN -> PackageIndex -> Architecture -export const getArchitectureByFqbn = R.curry( - (fqbn, packageIndex) => { - const pab = parseFQBN(fqbn); +export const getArchitectureByFqbn = R.curry((fqbn, packageIndex) => { + const pab = parseFQBN(fqbn); - return R.compose( - R.find(R.propEq('architecture', pab.architecture)), - R.prop(pab.package) - )(packageIndex); - } -); + return R.compose( + R.find(R.propEq('architecture', pab.architecture)), + R.prop(pab.package) + )(packageIndex); +}); // :: FQBN -> PackageIndex -> [Tool] -export const getToolsByFqbn = R.compose( - R.prop('tools'), - getArchitectureByFqbn -); +export const getToolsByFqbn = R.compose(R.prop('tools'), getArchitectureByFqbn); // :: FQBN -> PackageIndex -> URL export const getToolsUrl = R.compose( @@ -89,26 +90,20 @@ export const getToolsUrl = R.compose( ); // :: FQBN -> PackageIndex -> Path -export const getToolsDirectory = R.curry( - (fqbn, packagesDir) => path.join( - packagesDir, - parseFQBN(fqbn).package, - 'tools' - ) +export const getToolsDirectory = R.curry((fqbn, packagesDir) => + path.join(packagesDir, parseFQBN(fqbn).package, 'tools') ); // :: String -> String -> Path -> Path export const getToolVersionDirectory = R.curry( - (toolName, toolVersion, toolsDir) => path.join( - toolsDir, - toolName, - toolVersion - ) + (toolName, toolVersion, toolsDir) => + path.join(toolsDir, toolName, toolVersion) ); // :: BoardPrefs -> String export const getBoardUploadTool = R.compose( - R.when( // Dirty hack to use `openocd` tool to upload with bootloader size + R.when( + // Dirty hack to use `openocd` tool to upload with bootloader size R.equals('openocd-withbootsize'), R.always('openocd') ), @@ -119,21 +114,22 @@ export const getBoardUploadTool = R.compose( export const listBoardsFromIndex = R.compose( R.flatten, R.values, - R.mapObjIndexed( - (pkg, pkgName) => R.map( - arch => R.compose( - R.map(board => ({ - name: board.name, - boardsTxtId: board.boardsTxtBoardId, - pio: board.platformioBoardId, - package: pkgName, - architecture: arch.architecture, - version: arch.version, - cpuName: R.defaultTo('', board.cpuName), - cpuId: R.defaultTo('', board.cpuId), - })), - R.prop('boards') - )(arch), + R.mapObjIndexed((pkg, pkgName) => + R.map( + arch => + R.compose( + R.map(board => ({ + name: board.name, + boardsTxtId: board.boardsTxtBoardId, + pio: board.platformioBoardId, + package: pkgName, + architecture: arch.architecture, + version: arch.version, + cpuName: R.defaultTo('', board.cpuName), + cpuId: R.defaultTo('', board.cpuId), + })), + R.prop('boards') + )(arch), pkg ) ) diff --git a/packages/xod-arduino-deploy/test-func/packageManager.spec.js b/packages/xod-arduino-deploy/test-func/packageManager.spec.js index 2daa8f96d..6dc705fed 100644 --- a/packages/xod-arduino-deploy/test-func/packageManager.spec.js +++ b/packages/xod-arduino-deploy/test-func/packageManager.spec.js @@ -16,29 +16,30 @@ describe('Package Manager', () => { // Installing it('installHardware() downloads and installs not existent hardware', () => PM.installHardware('arduino:avr:uno', packagesDir, packageIndex) - .then((res) => { + .then(res => { assert.isTrue(res.installed); assert.isTrue(res.downloaded); }) // Call it again to be sure that we won't download and install it again - .then(() => PM.installHardware('arduino:avr:uno', packagesDir, packageIndex)) - .then((res) => { + .then(() => + PM.installHardware('arduino:avr:uno', packagesDir, packageIndex) + ) + .then(res => { assert.isTrue(res.installed); assert.isFalse(res.downloaded); - }) - ); + })); it('installTools() downloads and installs all tools', () => - PM.installTools('arduino:avr:uno', packagesDir, packageIndex) - .then((res) => { - assert.isTrue(res.installed); - assert.isTrue(res.downloaded); - }) - ); + PM.installTools('arduino:avr:uno', packagesDir, packageIndex).then(res => { + assert.isTrue(res.installed); + assert.isTrue(res.downloaded); + })); it('installArchitecture() downloads and installs hardware and all tools', () => - PM.installArchitecture('arduino:sam:arduino_due_x', packagesDir, packageIndex) - .then((res) => { - assert.isTrue(res.hardware.installed); - assert.isTrue(res.tools.installed); - }) - ); + PM.installArchitecture( + 'arduino:sam:arduino_due_x', + packagesDir, + packageIndex + ).then(res => { + assert.isTrue(res.hardware.installed); + assert.isTrue(res.tools.installed); + })); }); diff --git a/packages/xod-arduino-deploy/test/boardsParser.spec.js b/packages/xod-arduino-deploy/test/boardsParser.spec.js index aa6f4e8c1..061632cd4 100644 --- a/packages/xod-arduino-deploy/test/boardsParser.spec.js +++ b/packages/xod-arduino-deploy/test/boardsParser.spec.js @@ -3,14 +3,19 @@ import { assert } from 'chai'; import * as BP from '../src/boardsParser'; - describe('BoardsParser', () => { const fixture = p => path.resolve(__dirname, 'fixtures', p); const packagesDir = fixture('packages'); it('loadBoardPrefs() returns correct BoardPrefs', async () => { - const mkr1000 = await BP.loadBoardPrefs('arduino:samd:mkr1000', packagesDir); - const nano168 = await BP.loadBoardPrefs('arduino:avr:nano:cpu=atmega168', packagesDir); + const mkr1000 = await BP.loadBoardPrefs( + 'arduino:samd:mkr1000', + packagesDir + ); + const nano168 = await BP.loadBoardPrefs( + 'arduino:avr:nano:cpu=atmega168', + packagesDir + ); assert.nestedPropertyVal(mkr1000, 'name', 'Arduino/Genuino MKR1000'); assert.nestedPropertyVal(mkr1000, 'build.mcu', 'cortex-m0plus'); diff --git a/packages/xod-arduino-deploy/test/builder.spec.js b/packages/xod-arduino-deploy/test/builder.spec.js index ba8cdd85d..82d9f5bc4 100644 --- a/packages/xod-arduino-deploy/test/builder.spec.js +++ b/packages/xod-arduino-deploy/test/builder.spec.js @@ -3,7 +3,7 @@ import { assert } from 'chai'; import { composeCommand } from '../src/builder'; import { isWindows } from '../src/utils'; -const winExt = (isWindows) ? '.exe' : ''; +const winExt = isWindows ? '.exe' : ''; describe('Builder', () => { it('composeCommand() returns correct command', () => { @@ -22,10 +22,15 @@ describe('Builder', () => { builderDir ); - const execCmd = path.normalize(`/xod/arduino-builder/arduino-builder${winExt}`); + const execCmd = path.normalize( + `/xod/arduino-builder/arduino-builder${winExt}` + ); const hardwareA = path.normalize('/xod/arduino-builder/hardware'); const toolsA = path.normalize('/xod/arduino-builder/tools'); - assert.strictEqual(cmd, `"${execCmd}" -hardware="${hardwareA}" -hardware="${packagesDir}" -libraries="${librariesDir}" -tools="${toolsA}" -tools="${packagesDir}" -fqbn="arduino:avr:uno" -build-path="${artifacts}" "${sketch}"`); + assert.strictEqual( + cmd, + `"${execCmd}" -hardware="${hardwareA}" -hardware="${packagesDir}" -libraries="${librariesDir}" -tools="${toolsA}" -tools="${packagesDir}" -fqbn="arduino:avr:uno" -build-path="${artifacts}" "${sketch}"` + ); }); }); diff --git a/packages/xod-arduino-deploy/test/upload.spec.js b/packages/xod-arduino-deploy/test/upload.spec.js index a6fd1a806..e963540e3 100644 --- a/packages/xod-arduino-deploy/test/upload.spec.js +++ b/packages/xod-arduino-deploy/test/upload.spec.js @@ -4,7 +4,7 @@ import { composeCommand } from '../src/uploader'; import { isWindows } from '../src/utils'; import { loadBoardPrefs } from '../src/boardsParser'; -const winExt = (isWindows) ? '.exe' : ''; +const winExt = isWindows ? '.exe' : ''; describe('Uploader', () => { const fixture = p => path.resolve(__dirname, 'fixtures', p); @@ -23,10 +23,15 @@ describe('Uploader', () => { '/dev/cu.usbmodem1411' ); - const execCmd = path.normalize(`${packagesDir}/arduino/tools/bossac/1.7.0/bossac${winExt}`); + const execCmd = path.normalize( + `${packagesDir}/arduino/tools/bossac/1.7.0/bossac${winExt}` + ); const binaryFile = path.join(artifactsDir, 'test.cpp.bin'); - assert.strictEqual(cmd, `"${execCmd}" --port=/dev/cu.usbmodem1411 -U true -e -w -b "${binaryFile}" -R`); + assert.strictEqual( + cmd, + `"${execCmd}" --port=/dev/cu.usbmodem1411 -U true -e -w -b "${binaryFile}" -R` + ); }); it('composeCommand() for `arduino:samd:mzero_pro_bl_dbg` (openocd-withbootsize)', async () => { const fqbn = 'arduino:samd:mzero_pro_bl_dbg'; @@ -40,12 +45,21 @@ describe('Uploader', () => { '/dev/cu.usbmodem1411' ); - const execCmd = path.normalize(`${packagesDir}/arduino/tools/openocd/0.9.0-arduino6-static/bin/openocd${winExt}`); - const scripts = path.normalize(`${packagesDir}/arduino/tools/openocd/0.9.0-arduino6-static/share/openocd/scripts`); - const config = path.normalize(`${packagesDir}/arduino/hardware/samd/variants/arduino_mzero/openocd_scripts/arduino_zero.cfg`); + const execCmd = path.normalize( + `${packagesDir}/arduino/tools/openocd/0.9.0-arduino6-static/bin/openocd${winExt}` + ); + const scripts = path.normalize( + `${packagesDir}/arduino/tools/openocd/0.9.0-arduino6-static/share/openocd/scripts` + ); + const config = path.normalize( + `${packagesDir}/arduino/hardware/samd/variants/arduino_mzero/openocd_scripts/arduino_zero.cfg` + ); const binaryFile = path.join(artifactsDir, 'test.cpp.bin'); - assert.strictEqual(cmd, `"${execCmd}" -s "${scripts}" -f "${config}" -c "telnet_port disabled; program ${binaryFile} verify reset 0x4000; shutdown"`); + assert.strictEqual( + cmd, + `"${execCmd}" -s "${scripts}" -f "${config}" -c "telnet_port disabled; program ${binaryFile} verify reset 0x4000; shutdown"` + ); }); it('composeCommand() for `arduino:samd:tian` (avrdude)', async () => { const fqbn = 'arduino:samd:tian'; @@ -59,10 +73,17 @@ describe('Uploader', () => { '/dev/cu.usbmodem1411' ); - const execCmd = path.normalize(`${packagesDir}/arduino/tools/avrdude/6.3.0-arduino9/bin/avrdude${winExt}`); - const config = path.normalize(`${packagesDir}/arduino/tools/avrdude/6.3.0-arduino9/etc/avrdude.conf`); + const execCmd = path.normalize( + `${packagesDir}/arduino/tools/avrdude/6.3.0-arduino9/bin/avrdude${winExt}` + ); + const config = path.normalize( + `${packagesDir}/arduino/tools/avrdude/6.3.0-arduino9/etc/avrdude.conf` + ); const binaryFile = path.join(artifactsDir, 'test.cpp.hex'); - assert.strictEqual(cmd, `"${execCmd}" -C "${config}" -p atmega2560 -c wiring -P /dev/cu.usbmodem1411 -b 57600 -D "-Uflash:w:${binaryFile}:i"`); + assert.strictEqual( + cmd, + `"${execCmd}" -C "${config}" -p atmega2560 -c wiring -P /dev/cu.usbmodem1411 -b 57600 -D "-Uflash:w:${binaryFile}:i"` + ); }); }); diff --git a/packages/xod-arduino-deploy/test/utils.spec.js b/packages/xod-arduino-deploy/test/utils.spec.js index e671a7f01..2a378cceb 100644 --- a/packages/xod-arduino-deploy/test/utils.spec.js +++ b/packages/xod-arduino-deploy/test/utils.spec.js @@ -6,31 +6,36 @@ import packageIndex from '../src/packageIndex.json'; describe('Utils', () => { it('parseFQBN() returns { package, architecture, boardIdentifier, cpu }', () => { - assert.deepEqual( - Utils.parseFQBN('arduino:avr:uno'), - { - package: 'arduino', - architecture: 'avr', - boardIdentifier: 'uno', - cpu: '', - } - ); - assert.deepEqual( - Utils.parseFQBN('arduino:avr:nano:cpu=atmega328'), - { - package: 'arduino', - architecture: 'avr', - boardIdentifier: 'nano', - cpu: 'atmega328', - } - ); + assert.deepEqual(Utils.parseFQBN('arduino:avr:uno'), { + package: 'arduino', + architecture: 'avr', + boardIdentifier: 'uno', + cpu: '', + }); + assert.deepEqual(Utils.parseFQBN('arduino:avr:nano:cpu=atmega328'), { + package: 'arduino', + architecture: 'avr', + boardIdentifier: 'nano', + cpu: 'atmega328', + }); }); it('getToolVersion() returns version', () => { - const version = Utils.getToolVersion('arduino:samd:arduino_zero_native', 'bossac', packageIndex); - assert.equal(version, '1.7.0', 'Should return proper version of a tool from packageIndex'); + const version = Utils.getToolVersion( + 'arduino:samd:arduino_zero_native', + 'bossac', + packageIndex + ); + assert.equal( + version, + '1.7.0', + 'Should return proper version of a tool from packageIndex' + ); }); it('getToolsByFqbn() returns a list of tools', () => { - const tools = Utils.getToolsByFqbn('arduino:samd:arduino_zero_native', packageIndex); + const tools = Utils.getToolsByFqbn( + 'arduino:samd:arduino_zero_native', + packageIndex + ); assert.lengthOf(tools, 6); }); @@ -43,8 +48,15 @@ describe('Utils', () => { assert.strictEqual(dir, path.normalize('/xod/arduino/tools')); }); it('getToolVersionDirectory() returns correct path', () => { - const dir = Utils.getToolVersionDirectory('avrdude', '1.0.0-arduino1', '/xod/arduino/tools/'); - assert.strictEqual(dir, path.normalize('/xod/arduino/tools/avrdude/1.0.0-arduino1')); + const dir = Utils.getToolVersionDirectory( + 'avrdude', + '1.0.0-arduino1', + '/xod/arduino/tools/' + ); + assert.strictEqual( + dir, + path.normalize('/xod/arduino/tools/avrdude/1.0.0-arduino1') + ); }); it('listBoardsFromIndex() returns flat list of board objects', () => { const boards = Utils.listBoardsFromIndex(packageIndex); diff --git a/packages/xod-arduino/src/directives.js b/packages/xod-arduino/src/directives.js index 33f28e676..846d19a5c 100644 --- a/packages/xod-arduino/src/directives.js +++ b/packages/xod-arduino/src/directives.js @@ -13,116 +13,137 @@ type Pragmas = Array; // [\s\S]* matches anything across lines // See: https://stackoverflow.com/questions/1068280/javascript-regex-multiline-flag-doesnt-work // [\s\S]*? is to reduce regex greedieness -const stripBlockComments: UnaryFn = - R.replace(/^(((?!\/\/).)*?)\s*\/\*[\s\S]*?\*\//gm, '$1'); +const stripBlockComments: UnaryFn = R.replace( + /^(((?!\/\/).)*?)\s*\/\*[\s\S]*?\*\//gm, + '$1' +); -const stripLineComments: UnaryFn = - R.replace(/\s*\/\/.*$/gm, ''); +const stripLineComments: UnaryFn = R.replace( + /\s*\/\/.*$/gm, + '' +); -const stripCppComments: UnaryFn = - R.compose( - stripLineComments, - stripBlockComments - ); +const stripCppComments: UnaryFn = R.compose( + stripLineComments, + stripBlockComments +); const pragmaHeadRegexp = /#\s*pragma\s+XOD\s+/; const pragmaLineRegexp = new RegExp(`${pragmaHeadRegexp.source}.*`, 'g'); -const tokenizePragma: UnaryFn = - R.compose( - R.map(R.replace(/^"|"$/g, '')), // strip enclosing quotes - R.match(/[\w._-]+|"[^"]+"/g), // match identifier or string - R.replace(pragmaHeadRegexp, ''), // remove leading #pragma XOD - ); - -const findXodPragmas: UnaryFn = +const tokenizePragma: UnaryFn = R.compose( + R.map(R.replace(/^"|"$/g, '')), // strip enclosing quotes + R.match(/[\w._-]+|"[^"]+"/g), // match identifier or string + R.replace(pragmaHeadRegexp, '') // remove leading #pragma XOD +); + +const findXodPragmas: UnaryFn = R.compose( + R.map(tokenizePragma), + R.match(pragmaLineRegexp), + stripCppComments +); + +const doesReferSymbol: CurriedFunction2 = R.curry( + (symbol, code: CppCode) => + R.compose(R.test(new RegExp(`\\b${symbol}\\b`)), stripCppComments)(code) +); + +const doesReferTemplateSymbol: CurriedFunction3< + string, + string, + CppCode, + boolean +> = R.curry((symbol, templateArg, code: CppCode) => R.compose( - R.map(tokenizePragma), - R.match(pragmaLineRegexp), - stripCppComments - ); - -const doesReferSymbol: CurriedFunction2 = - R.curry((symbol, code: CppCode) => R.compose( - R.test(new RegExp(`\\b${symbol}\\b`)), - stripCppComments - )(code)); - -const doesReferTemplateSymbol: CurriedFunction3 = - R.curry((symbol, templateArg, code: CppCode) => R.compose( R.test(new RegExp(`\\b${symbol}\\s*\\<\\s*${templateArg}\\s*\\>`)), stripCppComments - )(code)); - -const filterPragmasByFeature: CurriedFunction2 = - R.curry((feat, pragmas: Pragmas) => - pragmas.filter(R.compose( - R.equals(feat), - R.head - )) - ); + )(code) +); + +const filterPragmasByFeature: CurriedFunction2< + string, + Pragmas, + Pragmas +> = R.curry((feat, pragmas: Pragmas) => + pragmas.filter(R.compose(R.equals(feat), R.head)) +); // Returns whether a particular #pragma feature enabled, disabled, or set to auto. // Default is auto -const pragmaEndis: CurriedFunction2 = - R.curry((feature: string, code: CppCode) => R.compose( - state => ((state === 'enable' || state === 'disable') ? state : 'auto'), +const pragmaEndis: CurriedFunction2< + string, + CppCode, + 'enable' | 'disable' | 'auto' +> = R.curry((feature: string, code: CppCode) => + R.compose( + state => (state === 'enable' || state === 'disable' ? state : 'auto'), R.nth(1), R.defaultTo([]), R.last, filterPragmasByFeature(feature), - findXodPragmas, - )(code)); + findXodPragmas + )(code) +); const pragmaTimeouts = pragmaEndis('timeouts'); const pragmaNodeId = pragmaEndis('nodeid'); -const nthToken: CurriedFunction2 = - R.curry((n, pragma: PragmaTokens) => R.nth(n, pragma) || ''); +const nthToken: CurriedFunction2 = R.curry( + (n, pragma: PragmaTokens) => R.nth(n, pragma) || '' +); -const endisToBoolean: CurriedFunction2 = - R.curry((defaultR, endis: string) => { +const endisToBoolean: CurriedFunction2 = R.curry( + (defaultR, endis: string) => { switch (endis) { - case 'enable': return true; - case 'disable': return false; - default: return defaultR; + case 'enable': + return true; + case 'disable': + return false; + default: + return defaultR; } - }); + } +); -const isDirtienessRelatedTo: CurriedFunction2 = - R.curry((identifier, pragma: PragmaTokens) => R.compose( - XF.isAmong([identifier, '']), - nthToken(2), - )(pragma)); +const isDirtienessRelatedTo: CurriedFunction2< + string, + PragmaTokens, + boolean +> = R.curry((identifier, pragma: PragmaTokens) => + R.compose(XF.isAmong([identifier, '']), nthToken(2))(pragma) +); -const isOutput: UnaryFn = - R.test(/^output_/); +const isOutput: UnaryFn = R.test(/^output_/); -export const areTimeoutsEnabled: UnaryFn = - code => R.compose( +export const areTimeoutsEnabled: UnaryFn = code => + R.compose( endisToBoolean(doesReferSymbol('setTimeout', code)), pragmaTimeouts )(code); -export const isNodeIdEnabled: UnaryFn = - code => R.compose( - endisToBoolean(doesReferSymbol('getNodeId', code)), - pragmaNodeId - )(code); +export const isNodeIdEnabled: UnaryFn = code => + R.compose(endisToBoolean(doesReferSymbol('getNodeId', code)), pragmaNodeId)( + code + ); -export const isDirtienessEnabled: CurriedFunction2 = - R.curry((code, identifier: string) => R.compose( +export const isDirtienessEnabled: CurriedFunction2< + CppCode, + string, + boolean +> = R.curry((code, identifier: string) => + R.compose( R.reduce( (acc, pragma) => - (isDirtienessRelatedTo(identifier, pragma) + isDirtienessRelatedTo(identifier, pragma) ? endisToBoolean(acc, nthToken(1, pragma)) - : acc), - isOutput(identifier) || /* dirtieness enabled on outputs by default */ + : acc, + isOutput(identifier) /* dirtieness enabled on outputs by default */ || doesReferTemplateSymbol('isInputDirty', identifier, code) ), filterPragmasByFeature('dirtieness'), - findXodPragmas, - )(code)); + findXodPragmas + )(code) +); export const forUnitTests = { stripCppComments, diff --git a/packages/xod-arduino/src/templates.js b/packages/xod-arduino/src/templates.js index 773b4e8f7..79154a0b9 100644 --- a/packages/xod-arduino/src/templates.js +++ b/packages/xod-arduino/src/templates.js @@ -27,32 +27,24 @@ const omitLocalIncludes = R.replace(/#include ".*$/gm, ''); const indexByPinKey = R.indexBy(R.prop('pinKey')); -const getPatchPins = direction => R.compose( - indexByPinKey, - R.path(['patch', direction]) -); - -const omitNullValues = R.map(R.when( - R.propSatisfies(R.isNil, 'value'), - R.omit(['value']) -)); +const getPatchPins = direction => + R.compose(indexByPinKey, R.path(['patch', direction])); -const getNodePins = direction => R.compose( - indexByPinKey, - omitNullValues, - R.prop(direction) +const omitNullValues = R.map( + R.when(R.propSatisfies(R.isNil, 'value'), R.omit(['value'])) ); -const mergeAndListPins = (direction, node) => R.compose( - R.values, - R.converge( - R.mergeWith(R.merge), - [ +const getNodePins = direction => + R.compose(indexByPinKey, omitNullValues, R.prop(direction)); + +const mergeAndListPins = (direction, node) => + R.compose( + R.values, + R.converge(R.mergeWith(R.merge), [ getPatchPins(direction), getNodePins(direction), - ] - ) -)(node); + ]) + )(node); // Converts DataType value to a corresponding C++ storage type const cppType = def( @@ -68,11 +60,7 @@ const cppType = def( // Formats a plain JS string into C++ string object const cppStringLiteral = def( 'cppStringLiteral :: String -> String', - R.ifElse( - R.isEmpty, - R.always('XString()'), - str => `XStringCString("${str}")` - ) + R.ifElse(R.isEmpty, R.always('XString()'), str => `XStringCString("${str}")`) ); // ============================================================================= @@ -88,26 +76,28 @@ Handlebars.registerHelper('mergePins', function mergePins() { }); // Generate patch-level namespace name -Handlebars.registerHelper('ns', R.compose( - R.join('__'), - R.props(['owner', 'libName', 'patchName']) -)); +Handlebars.registerHelper( + 'ns', + R.compose(R.join('__'), R.props(['owner', 'libName', 'patchName'])) +); // Debug helper Handlebars.registerHelper('json', JSON.stringify); // Returns declaration type specifier for an initial value of an output -Handlebars.registerHelper('decltype', (type, value) => ( - (type === PIN_TYPE.STRING && value !== '') - ? 'static XStringCString' - : `constexpr ${cppType(type)}` -)); +Handlebars.registerHelper( + 'decltype', + (type, value) => + type === PIN_TYPE.STRING && value !== '' + ? 'static XStringCString' + : `constexpr ${cppType(type)}` +); // Check that variable is not undefined Handlebars.registerHelper('exists', function existsHelper(variable, options) { - return (typeof variable !== 'undefined') ? - options.fn(this) : - options.inverse(this); + return typeof variable !== 'undefined' + ? options.fn(this) + : options.inverse(this); }); // Temporary switch to global C++ namespace @@ -138,19 +128,29 @@ Handlebars.registerHelper('cppValue', (type, value) => // A helper to quickly introduce a new filtered {{each ...}} loop function registerHandlebarsFilterLoopHelper(name, predicate) { Handlebars.registerHelper(name, (list, block) => - R.compose( - R.join(''), - R.map(node => block.fn(node)), - R.filter(predicate) - )(list) + R.compose(R.join(''), R.map(node => block.fn(node)), R.filter(predicate))( + list + ) ); } -registerHandlebarsFilterLoopHelper('eachDeferNode', R.path(['patch', 'isDefer'])); -registerHandlebarsFilterLoopHelper('eachNonConstantNode', R.pathEq(['patch', 'isConstant'], false)); -registerHandlebarsFilterLoopHelper('eachNodeUsingTimeouts', R.path(['patch', 'usesTimeouts'])); +registerHandlebarsFilterLoopHelper( + 'eachDeferNode', + R.path(['patch', 'isDefer']) +); +registerHandlebarsFilterLoopHelper( + 'eachNonConstantNode', + R.pathEq(['patch', 'isConstant'], false) +); +registerHandlebarsFilterLoopHelper( + 'eachNodeUsingTimeouts', + R.path(['patch', 'usesTimeouts']) +); registerHandlebarsFilterLoopHelper('eachLinkedInput', R.has('fromNodeId')); -registerHandlebarsFilterLoopHelper('eachNonlinkedInput', R.complement(R.has('fromNodeId'))); +registerHandlebarsFilterLoopHelper( + 'eachNonlinkedInput', + R.complement(R.has('fromNodeId')) +); registerHandlebarsFilterLoopHelper('eachDirtyablePin', R.prop('isDirtyable')); // ============================================================================= @@ -184,38 +184,32 @@ export const renderPatchContext = def( 'renderPatchContext :: TPatch -> String', templates.patchContext ); -export const renderImpl = def( - 'renderImpl :: TPatch -> String', - (data) => { - const ctx = R.applySpec({ - owner: R.prop('owner'), - libName: R.prop('libName'), - patchName: R.prop('patchName'), - GENERATED_CODE: renderPatchContext, - })(data); - - const patchImpl = R.prop('impl', data); - return Handlebars.compile(patchImpl, renderingOptions)(ctx); - } -); +export const renderImpl = def('renderImpl :: TPatch -> String', data => { + const ctx = R.applySpec({ + owner: R.prop('owner'), + libName: R.prop('libName'), + patchName: R.prop('patchName'), + GENERATED_CODE: renderPatchContext, + })(data); + + const patchImpl = R.prop('impl', data); + return Handlebars.compile(patchImpl, renderingOptions)(ctx); +}); export const renderImplList = def( 'renderImplList :: [TPatch] -> String', R.compose( trimTrailingWhitespace, templates.implList, - R.map(patch => R.assoc('implementation', renderImpl(patch), patch)), + R.map(patch => R.assoc('implementation', renderImpl(patch), patch)) ) ); -export const renderProgram = def( - 'renderProgram :: [TNode] -> String', - nodes => trimTrailingWhitespace( - templates.program({ nodes }) - ) +export const renderProgram = def('renderProgram :: [TNode] -> String', nodes => + trimTrailingWhitespace(templates.program({ nodes })) ); export const renderProject = def( 'renderProject :: TProject -> String', - (originalProject) => { + originalProject => { // HACK: We have to clone TProject to prevent mutating // of original TProject by Handlebars templates. const project = R.clone(originalProject); diff --git a/packages/xod-arduino/src/transpiler.js b/packages/xod-arduino/src/transpiler.js index 888fd8529..5e441564c 100644 --- a/packages/xod-arduino/src/transpiler.js +++ b/packages/xod-arduino/src/transpiler.js @@ -49,49 +49,55 @@ const createPatchNames = def( const findPatchByPath = def( 'findPatchByPath :: PatchPath -> [TPatch] -> TPatch', - (path, patches) => R.compose( - R.find(R.__, patches), - R.allPass, - R.map(R.apply(R.propEq)), - R.toPairs, - createPatchNames - )(path) + (path, patches) => + R.compose( + R.find(R.__, patches), + R.allPass, + R.map(R.apply(R.propEq)), + R.toPairs, + createPatchNames + )(path) ); const getLinksInputNodeIds = def( 'getLinksInputNodeIds :: [Link] -> [TNodeId]', - R.compose( - R.uniq, - R.map(R.compose( - toInt, - Project.getLinkInputNodeId - )) - ) + R.compose(R.uniq, R.map(R.compose(toInt, Project.getLinkInputNodeId))) ); const getPatchByNodeId = def( 'getPatchByNodeId :: Project -> PatchPath -> [TPatch] -> NodeId -> TPatch', - (project, entryPath, patches, nodeId) => R.compose( - findPatchByPath(R.__, patches), - Project.getNodeType, - Project.getNodeByIdUnsafe(nodeId), - Project.getPatchByPathUnsafe - )(entryPath, project) + (project, entryPath, patches, nodeId) => + R.compose( + findPatchByPath(R.__, patches), + Project.getNodeType, + Project.getNodeByIdUnsafe(nodeId), + Project.getPatchByPathUnsafe + )(entryPath, project) ); const toposortProject = def( 'toposortProject :: PatchPath -> Project -> Either Error Object', - (path, project) => R.compose( - R.chain(nodeIdsMap => R.compose( - R.map(R.applySpec({ - project: R.identity, - nodeIdsMap: R.always(nodeIdsMap), - })), - () => Project.updatePatch(path, Project.applyNodeIdMap(R.__, nodeIdsMap), project) - )(nodeIdsMap)), - Project.getTopologyMap, - Project.getPatchByPathUnsafe - )(path, project) + (path, project) => + R.compose( + R.chain(nodeIdsMap => + R.compose( + R.map( + R.applySpec({ + project: R.identity, + nodeIdsMap: R.always(nodeIdsMap), + }) + ), + () => + Project.updatePatch( + path, + Project.applyNodeIdMap(R.__, nodeIdsMap), + project + ) + )(nodeIdsMap) + ), + Project.getTopologyMap, + Project.getPatchByPathUnsafe + )(path, project) ); //----------------------------------------------------------------------------- @@ -102,94 +108,92 @@ const toposortProject = def( const createTPatches = def( 'createTPatches :: PatchPath -> Project -> [TPatch]', - (entryPath, project) => R.compose( - R.values, - R.mapObjIndexed((patch, path) => { - const names = createPatchNames(path); - const impl = explodeMaybe( - `Implementation for ${path} not found`, - Project.getImpl(patch) - ); - - const isDirtyable = pin => - Project.getPinType(pin) === Project.PIN_TYPE.PULSE - || isDirtienessEnabled(impl, `${pin.direction}_${pin.label}`); - - const outputs = R.compose( - R.map(R.applySpec({ - type: Project.getPinType, - pinKey: Project.getPinLabel, - value: R.compose( - Project.defaultValueOfType, - Project.getPinType + (entryPath, project) => + R.compose( + R.values, + R.mapObjIndexed((patch, path) => { + const names = createPatchNames(path); + const impl = explodeMaybe( + `Implementation for ${path} not found`, + Project.getImpl(patch) + ); + + const isDirtyable = pin => + Project.getPinType(pin) === Project.PIN_TYPE.PULSE || + isDirtienessEnabled(impl, `${pin.direction}_${pin.label}`); + + const outputs = R.compose( + R.map( + R.applySpec({ + type: Project.getPinType, + pinKey: Project.getPinLabel, + value: R.compose(Project.defaultValueOfType, Project.getPinType), + isDirtyable, + isDirtyOnBoot: R.compose( + R.not, + R.equals(Project.PIN_TYPE.PULSE), + Project.getPinType + ), + }) ), - isDirtyable, - isDirtyOnBoot: R.compose( - R.not, - R.equals(Project.PIN_TYPE.PULSE), - Project.getPinType + Project.normalizePinLabels, + Project.listOutputPins + )(patch); + + const inputs = R.compose( + R.map( + R.applySpec({ + type: Project.getPinType, + pinKey: Project.getPinLabel, + isDirtyable, + }) ), - })), - Project.normalizePinLabels, - Project.listOutputPins - )(patch); - - const inputs = R.compose( - R.map(R.applySpec({ - type: Project.getPinType, - pinKey: Project.getPinLabel, - isDirtyable, - })), - Project.normalizePinLabels, - Project.listInputPins - )(patch); - - const isThisIsThat = { - isDefer: Project.isDeferNodeType(path), - isConstant: Project.isConstantNodeType(path), - usesTimeouts: areTimeoutsEnabled(impl), - usesNodeId: isNodeIdEnabled(impl), - }; - - return R.mergeAll([ - names, - isThisIsThat, - { - outputs, - inputs, - impl, - }, - ]); - }), - R.omit([entryPath]), - R.indexBy(Project.getPatchPath), - Project.listPatchesWithoutBuiltIns - )(project) + Project.normalizePinLabels, + Project.listInputPins + )(patch); + + const isThisIsThat = { + isDefer: Project.isDeferNodeType(path), + isConstant: Project.isConstantNodeType(path), + usesTimeouts: areTimeoutsEnabled(impl), + usesNodeId: isNodeIdEnabled(impl), + }; + + return R.mergeAll([ + names, + isThisIsThat, + { + outputs, + inputs, + impl, + }, + ]); + }), + R.omit([entryPath]), + R.indexBy(Project.getPatchPath), + Project.listPatchesWithoutBuiltIns + )(project) ); const getPinLabelsMap = def( 'getPinLabelsMap :: [Pin] -> Map PinKey PinLabel', - R.compose( - R.map(Project.getPinLabel), - R.indexBy(Project.getPinKey) - ) + R.compose(R.map(Project.getPinLabel), R.indexBy(Project.getPinKey)) ); const getNodePinsUnsafe = def( 'getNodePinsUnsafe :: Node -> Project -> [Pin]', - (node, project) => R.compose( - explodeMaybe(`Can’t get node pins of node ${node}. Referred type missing?`), - Project.getNodePins - )(node, project) + (node, project) => + R.compose( + explodeMaybe( + `Can’t get node pins of node ${node}. Referred type missing?` + ), + Project.getNodePins + )(node, project) ); const getNodePinLabels = def( 'getNodePinLabels :: Node -> Project -> Map PinKey PinLabel', - R.compose( - getPinLabelsMap, - Project.normalizePinLabels, - getNodePinsUnsafe - ) + R.compose(getPinLabelsMap, Project.normalizePinLabels, getNodePinsUnsafe) ); // TODO: Remove it when `Project.getBoundValue` will return default values @@ -199,15 +203,13 @@ const getNodePinLabels = def( */ const getDefaultPinValue = def( 'getDefaultPinValue :: PinKey -> Node -> Project -> DataValue', - (pinKey, node, project) => R.compose( - explodeMaybe(`Can’t find pin with key ${pinKey} for node ${node}"`), - R.map(R.compose( - Project.defaultValueOfType, - Project.getPinType - )), - R.chain(Project.getPinByKey(pinKey)), - Project.getPatchByNode(R.__, project) - )(node) + (pinKey, node, project) => + R.compose( + explodeMaybe(`Can’t find pin with key ${pinKey} for node ${node}"`), + R.map(R.compose(Project.defaultValueOfType, Project.getPinType)), + R.chain(Project.getPinByKey(pinKey)), + Project.getPatchByNode(R.__, project) + )(node) ); const getTNodeOutputs = def( @@ -221,8 +223,9 @@ const getTNodeOutputs = def( R.mapObjIndexed((links, pinKey) => ({ to: getLinksInputNodeIds(links), pinKey: nodePins[pinKey], - value: Project.getBoundValue(pinKey, node) - .getOrElse(getDefaultPinValue(pinKey, node, project)), + value: Project.getBoundValue(pinKey, node).getOrElse( + getDefaultPinValue(pinKey, node, project) + ), })), R.groupBy(Project.getLinkOutputPinKey), R.filter(Project.isLinkOutputNodeIdEquals(nodeId)), @@ -237,13 +240,12 @@ const getOutputPinLabelByLink = def( (project, patch, link) => { const pinKey = Project.getLinkOutputPinKey(link); return R.compose( - explodeMaybe(`Can’t find pin with key ${pinKey} for link ${link} on patch ${patch}`), - R.map(R.compose( - Project.getPinLabel, - R.head, - Project.normalizePinLabels, - R.of - )), + explodeMaybe( + `Can’t find pin with key ${pinKey} for link ${link} on patch ${patch}` + ), + R.map( + R.compose(Project.getPinLabel, R.head, Project.normalizePinLabels, R.of) + ), R.chain(Project.getPinByKey(pinKey)), R.chain(Project.getPatchByNode(R.__, project)), Project.getNodeById(R.__, patch), @@ -279,7 +281,10 @@ const getTNodeInputs = def( fromNodeId: R.compose(toInt, Project.getLinkOutputNodeId), fromPatch: getUpstreamNodePatch, fromPinKey: getUpstreamPinLabel, - fromOutput: R.converge(getTPatchOutputByLabel, [getUpstreamPinLabel, getUpstreamNodePatch]), + fromOutput: R.converge(getTPatchOutputByLabel, [ + getUpstreamPinLabel, + getUpstreamNodePatch, + ]), }); return R.compose( @@ -292,18 +297,24 @@ const getTNodeInputs = def( const createTNodes = def( 'createTNodes :: PatchPath -> [TPatch] -> Map NodeId String -> Project -> [TNode]', - (entryPath, patches, nodeIdsMap, project) => R.compose( - R.sortBy(R.prop('id')), - R.map(R.applySpec({ - id: R.compose(toInt, Project.getNodeId), - originalId: R.compose(reverseLookup(R.__, nodeIdsMap), Project.getNodeId), - patch: R.compose(findPatchByPath(R.__, patches), Project.getNodeType), - outputs: getTNodeOutputs(project, entryPath), - inputs: getTNodeInputs(project, entryPath, patches), - })), - Project.listNodes, - Project.getPatchByPathUnsafe - )(entryPath, project) + (entryPath, patches, nodeIdsMap, project) => + R.compose( + R.sortBy(R.prop('id')), + R.map( + R.applySpec({ + id: R.compose(toInt, Project.getNodeId), + originalId: R.compose( + reverseLookup(R.__, nodeIdsMap), + Project.getNodeId + ), + patch: R.compose(findPatchByPath(R.__, patches), Project.getNodeType), + outputs: getTNodeOutputs(project, entryPath), + inputs: getTNodeInputs(project, entryPath, patches), + }) + ), + Project.listNodes, + Project.getPatchByPathUnsafe + )(entryPath, project) ); /** @@ -313,45 +324,52 @@ const createTNodes = def( */ const transformProjectWithImpls = def( 'transformProjectWithImpls :: Project -> PatchPath -> TranspilationOptions -> Either Error TProject', - (project, path, opts) => R.compose( - Project.wrapDeadRefErrorMessage(path), - R.chain((tProject) => { - const nodeWithTooManyOutputs = R.find( - R.pipe(R.prop('outputs'), R.length, R.lt(7)), - tProject.patches - ); - - if (nodeWithTooManyOutputs) { - const { owner, libName, patchName } = nodeWithTooManyOutputs; - return Either.Left(new Error(`Native node ${owner}/${libName}/${patchName} has more than 7 outputs`)); - } - - return Either.of(tProject); - }), - R.map(({ project: proj, nodeIdsMap }) => { - const patches = createTPatches(path, proj); - - return R.merge( - { - config: { XOD_DEBUG: opts.debug }, - }, - R.applySpec({ - patches: R.always(patches), - nodes: createTNodes(path, patches, nodeIdsMap), - })(proj) - ); - }), - R.chain(R.compose( - toposortProject(path), - Project.extractBoundInputsToConstNodes(R.__, path, project), - )), - R.chain(Project.flatten(R.__, path)), - R.unless( - () => opts.debug, - R.chain(Project.updatePatch(path, Project.removeDebugNodes)) - ), - Project.validateProject - )(project) + (project, path, opts) => + R.compose( + Project.wrapDeadRefErrorMessage(path), + R.chain(tProject => { + const nodeWithTooManyOutputs = R.find( + R.pipe(R.prop('outputs'), R.length, R.lt(7)), + tProject.patches + ); + + if (nodeWithTooManyOutputs) { + const { owner, libName, patchName } = nodeWithTooManyOutputs; + return Either.Left( + new Error( + `Native node ${owner}/${libName}/${patchName} has more than 7 outputs` + ) + ); + } + + return Either.of(tProject); + }), + R.map(({ project: proj, nodeIdsMap }) => { + const patches = createTPatches(path, proj); + + return R.merge( + { + config: { XOD_DEBUG: opts.debug }, + }, + R.applySpec({ + patches: R.always(patches), + nodes: createTNodes(path, patches, nodeIdsMap), + })(proj) + ); + }), + R.chain( + R.compose( + toposortProject(path), + Project.extractBoundInputsToConstNodes(R.__, path, project) + ) + ), + R.chain(Project.flatten(R.__, path)), + R.unless( + () => opts.debug, + R.chain(Project.updatePatch(path, Project.removeDebugNodes)) + ), + Project.validateProject + )(project) ); export const getNodeIdsMap = def( diff --git a/packages/xod-arduino/test/directives.spec.js b/packages/xod-arduino/test/directives.spec.js index a4981fc27..759760ed2 100644 --- a/packages/xod-arduino/test/directives.spec.js +++ b/packages/xod-arduino/test/directives.spec.js @@ -1,4 +1,3 @@ - import { assert } from 'chai'; import { forUnitTests, @@ -124,10 +123,7 @@ describe('Search for #pragma XOD', () => { #pragma XOD digun liteta `; - assertPragmasFound(code, [ - ['foo', 'bar', 'baz'], - ['digun', 'liteta'], - ]); + assertPragmasFound(code, [['foo', 'bar', 'baz'], ['digun', 'liteta']]); }); it('finds tricky pragmas', () => { @@ -137,24 +133,19 @@ describe('Search for #pragma XOD', () => { //#pragma XOD commented out `; - assertPragmasFound(code, [ - ['foo', 'bar', 'baz'], - ['digun', 'liteta'], - ]); + assertPragmasFound(code, [['foo', 'bar', 'baz'], ['digun', 'liteta']]); }); it('considers enquoted arguments atomic', () => { - assertPragmasFound( - '#pragma XOD foo "bar baz" qux', - [['foo', 'bar baz', 'qux']] - ); + assertPragmasFound('#pragma XOD foo "bar baz" qux', [ + ['foo', 'bar baz', 'qux'], + ]); }); it('considers - _ . a non-breaking character', () => { - assertPragmasFound( - '#pragma XOD foo.bar baz-qux_kut', - [['foo.bar', 'baz-qux_kut']] - ); + assertPragmasFound('#pragma XOD foo.bar baz-qux_kut', [ + ['foo.bar', 'baz-qux_kut'], + ]); }); }); diff --git a/packages/xod-arduino/test/transpiler.spec.js b/packages/xod-arduino/test/transpiler.spec.js index bf1521e75..ef3b916d9 100644 --- a/packages/xod-arduino/test/transpiler.spec.js +++ b/packages/xod-arduino/test/transpiler.spec.js @@ -5,16 +5,22 @@ import { assert } from 'chai'; import { explode, foldEither, explodeEither } from 'xod-func-tools'; import { loadProject } from 'xod-fs'; -import { transpile, transformProject, getNodeIdsMap, forUnitTests } from '../src/transpiler'; +import { + transpile, + transformProject, + getNodeIdsMap, + forUnitTests, +} from '../src/transpiler'; const { createPatchNames } = forUnitTests; // Returns patch relative to repo’s `workspace` subdir -const wsPath = (...subpath) => path.resolve(__dirname, '../../../workspace', ...subpath); +const wsPath = (...subpath) => + path.resolve(__dirname, '../../../workspace', ...subpath); describe('xod-arduino transpiler', () => { describe('correctly transpiles workspace fixture', () => { - const testFixture = (projName) => { + const testFixture = projName => { const expectedCpp = fs.readFileSync( wsPath(projName, '__fixtures__/arduino.cpp'), 'utf-8' @@ -25,49 +31,50 @@ describe('xod-arduino transpiler', () => { .then(R.map(transpile)) .then(explode) .then(result => - assert.strictEqual(result, expectedCpp, 'expected and actual C++ don’t match') + assert.strictEqual( + result, + expectedCpp, + 'expected and actual C++ don’t match' + ) ); }; specify('blink', () => testFixture('blink')); specify('two-button-switch', () => testFixture('two-button-switch')); specify('lcd-time', () => testFixture('lcd-time')); - specify('count-with-feedback-loops', () => testFixture('count-with-feedback-loops')); + specify('count-with-feedback-loops', () => + testFixture('count-with-feedback-loops') + ); }); - it('returns error for non-existing-patch entry point', - () => - loadProject([wsPath()], wsPath('blink')) - .then(transformProject(R.__, '@/non-existing-patch')) - .then(result => assert.equal(result.isLeft, true)) - ); + it('returns error for non-existing-patch entry point', () => + loadProject([wsPath()], wsPath('blink')) + .then(transformProject(R.__, '@/non-existing-patch')) + .then(result => assert.equal(result.isLeft, true))); - it('returns error if some native node has more than 7 outputs', - () => - loadProject([wsPath()], wsPath('faulty')) - .then(transformProject(R.__, '@/too-many-outputs-main')) - .then(R.map(transpile)) - .then(foldEither( - (err) => { + it('returns error if some native node has more than 7 outputs', () => + loadProject([wsPath()], wsPath('faulty')) + .then(transformProject(R.__, '@/too-many-outputs-main')) + .then(R.map(transpile)) + .then( + foldEither( + err => { assert.include(err.message, 'too_many_outputs'); assert.include(err.message, 'has more than 7 outputs'); }, () => assert(false, 'expecting Either.Left') - )) - ); + ) + )); it('sorts nodes topologically', () => loadProject([wsPath()], wsPath('blink')) - .then(R.pipe( - transformProject(R.__, '@/main'), - explodeEither, - R.prop('nodes') - )) - .then((nodes) => { - const patchNames = R.compose( - R.pluck('patchName'), - R.pluck('patch') - )(nodes); + .then( + R.pipe(transformProject(R.__, '@/main'), explodeEither, R.prop('nodes')) + ) + .then(nodes => { + const patchNames = R.compose(R.pluck('patchName'), R.pluck('patch'))( + nodes + ); assert.deepEqual(patchNames, [ 'constant_number', // IVAL @@ -79,10 +86,12 @@ describe('xod-arduino transpiler', () => { ]); const ids = R.pluck('id', nodes); - assert.deepEqual(ids, [0, 1, 2, 3, 4, 5], - 'Node IDs were not arranged in topological order'); - }) - ); + assert.deepEqual( + ids, + [0, 1, 2, 3, 4, 5], + 'Node IDs were not arranged in topological order' + ); + })); }); describe('getNodeIdsMap', () => { @@ -97,10 +106,13 @@ describe('getNodeIdsMap', () => { .then(transformProject(R.__, '@/main')) .then(R.map(getNodeIdsMap)) .then(explode) - .then(result => R.mapObjIndexed( - (nodeId, origNodeId) => assert.propertyVal(result, origNodeId, nodeId), - expected - )); + .then(result => + R.mapObjIndexed( + (nodeId, origNodeId) => + assert.propertyVal(result, origNodeId, nodeId), + expected + ) + ); }); }); diff --git a/packages/xod-cli/src/lib-uri.js b/packages/xod-cli/src/lib-uri.js index 94caa48fb..1d7315ce6 100644 --- a/packages/xod-cli/src/lib-uri.js +++ b/packages/xod-cli/src/lib-uri.js @@ -8,10 +8,11 @@ export const createLibUri = def( export const parseLibUri = def( 'parseLibUri :: String -> Maybe LibUri', - string => Maybe - .toMaybe(string.match(/^([^@/]+?)\/([^@/]+?)(?:@([^@/]+?))?$/)) - .map(([, orgname, libname, tag]) => - createLibUri(orgname, libname, tag || 'latest')) + string => + Maybe.toMaybe(string.match(/^([^@/]+?)\/([^@/]+?)(?:@([^@/]+?))?$/)).map( + ([, orgname, libname, tag]) => + createLibUri(orgname, libname, tag || 'latest') + ) ); export const toStringWithoutTag = def( diff --git a/packages/xod-cli/src/messageUtils.js b/packages/xod-cli/src/messageUtils.js index 0ef936c69..f5855b895 100644 --- a/packages/xod-cli/src/messageUtils.js +++ b/packages/xod-cli/src/messageUtils.js @@ -22,6 +22,12 @@ export function error(msg) { } } -export function warn(msg) { write(clc.yellow(`! ${msg}`)); } -export function notice(msg) { write(clc.cyan(msg)); } -export function success(msg) { write(clc.green(`✓ ${msg}`)); } +export function warn(msg) { + write(clc.yellow(`! ${msg}`)); +} +export function notice(msg) { + write(clc.cyan(msg)); +} +export function success(msg) { + write(clc.green(`✓ ${msg}`)); +} diff --git a/packages/xod-cli/src/utils.js b/packages/xod-cli/src/utils.js index 95a3242eb..7d3ce1178 100644 --- a/packages/xod-cli/src/utils.js +++ b/packages/xod-cli/src/utils.js @@ -9,8 +9,8 @@ import { expandHomeDir, DEFAULT_WORKSPACE_PATH } from 'xod-fs'; * @param {Object.} programs */ export const runCommand = R.uncurryN(2, options => - R.mapObjIndexed( - (fn, command) => R.when( + R.mapObjIndexed((fn, command) => + R.when( R.compose(R.equals(true), R.prop(R.__, options)), R.tap(() => fn(options)) )(command) @@ -20,9 +20,7 @@ export const runCommand = R.uncurryN(2, options => // :: Nullable Path -> Path export const getWorkspacePath = R.compose( expandHomeDir, - R.defaultTo( - process.env.XOD_WORKSPACE || DEFAULT_WORKSPACE_PATH - ) + R.defaultTo(process.env.XOD_WORKSPACE || DEFAULT_WORKSPACE_PATH) ); export default { runCommand }; diff --git a/packages/xod-cli/src/xodc-install.js b/packages/xod-cli/src/xodc-install.js index f5b66d355..4e2ac8646 100644 --- a/packages/xod-cli/src/xodc-install.js +++ b/packages/xod-cli/src/xodc-install.js @@ -6,12 +6,15 @@ import * as MSG from './messages'; import { getWorkspacePath } from './utils'; // :: Path -> Promise Path Error -const ensureWorkspacePath = wsPath => new Promise((resolve, reject) => { - if (xodFs.isWorkspaceDir(wsPath)) return resolve(wsPath); - return reject( - new Error(`Directory "${wsPath}" is not a workspace directory. Workspace directory must contain ".xodworkspace" file.`) - ); -}); +const ensureWorkspacePath = wsPath => + new Promise((resolve, reject) => { + if (xodFs.isWorkspaceDir(wsPath)) return resolve(wsPath); + return reject( + new Error( + `Directory "${wsPath}" is not a workspace directory. Workspace directory must contain ".xodworkspace" file.` + ) + ); + }); /** * Installs the library version from package manager. @@ -23,8 +26,9 @@ export default function install(swaggerUrl, libQuery, workspace) { const wsPath = getWorkspacePath(workspace); return ensureWorkspacePath(wsPath) .then(() => fetchLibData(swaggerUrl, libQuery)) - .then( - libData => xodFs.scanWorkspaceForLibNames(wsPath) + .then(libData => + xodFs + .scanWorkspaceForLibNames(wsPath) .then(libNames => [libData, libNames]) ) .then(([libData, libNames]) => { @@ -34,14 +38,18 @@ export default function install(swaggerUrl, libQuery, workspace) { const libName = `${params.owner}/${params.name}`; return fetchLibsWithDependencies(swaggerUrl, libNames, [libName]) - .then(R.tap(R.forEachObjIndexed( - (proj, name) => messages.notice(MSG.dependencyResolved(name)) - ))) + .then( + R.tap( + R.forEachObjIndexed((proj, name) => + messages.notice(MSG.dependencyResolved(name)) + ) + ) + ) .then(xodFs.saveAllLibrariesEntirely(wsPath)) .then(() => MSG.allLibrariesInstalled(wsPath)); }) .then(messages.success) - .catch((err) => { + .catch(err => { messages.error(err.message); process.exit(1); }); diff --git a/packages/xod-cli/src/xodc-pack.js b/packages/xod-cli/src/xodc-pack.js index f3adbcac6..8611f52f3 100644 --- a/packages/xod-cli/src/xodc-pack.js +++ b/packages/xod-cli/src/xodc-pack.js @@ -7,7 +7,10 @@ import * as msg from './messageUtils'; export default (projectDir, output) => { const projectPath = path.resolve(projectDir); - const dirName = projectDir.split('/').filter(f => f !== '').pop(); + const dirName = projectDir + .split('/') + .filter(f => f !== '') + .pop(); msg.notice(`Packing ${dirName} into ${output} ...`); @@ -17,7 +20,7 @@ export default (projectDir, output) => { .then(() => { msg.success(`Packed project successfully written into ${output}`); }) - .catch((err) => { + .catch(err => { msg.error(err); process.exit(1); }); diff --git a/packages/xod-cli/src/xodc-publish.js b/packages/xod-cli/src/xodc-publish.js index 25558cf86..174e0752a 100644 --- a/packages/xod-cli/src/xodc-publish.js +++ b/packages/xod-cli/src/xodc-publish.js @@ -28,19 +28,23 @@ export default async function publish(swaggerUrl, orgname$, projectDir) { const { Auth, Library, Organization, User, Version } = swaggerClient.apis; const { hostname, protocol } = url.parse(swaggerUrl); const { XOD_PASSWORD, XOD_USERNAME } = process.env; - const user = XOD_PASSWORD && XOD_USERNAME - ? { password: XOD_PASSWORD, username: XOD_USERNAME } - : await inquirer.prompt([{ - message: `Username for '${protocol}//${hostname}':`, - name: 'username', - type: 'input', - }, { - message: `Password for '${protocol}//${hostname}':`, - name: 'password', - type: 'password', - }]); + const user = + XOD_PASSWORD && XOD_USERNAME + ? { password: XOD_PASSWORD, username: XOD_USERNAME } + : await inquirer.prompt([ + { + message: `Username for '${protocol}//${hostname}':`, + name: 'username', + type: 'input', + }, + { + message: `Password for '${protocol}//${hostname}':`, + name: 'password', + type: 'password', + }, + ]); const username = user.username; - const { obj: grant } = await Auth.getDirectGrant({ user }).catch((err) => { + const { obj: grant } = await Auth.getDirectGrant({ user }).catch(err => { if (err.status === 403) { throw new Error(`user "${username}" is not authenticated.`); } @@ -52,9 +56,9 @@ export default async function publish(swaggerUrl, orgname$, projectDir) { const orgname = orgname$ || username; const { libname, version } = await packLibVersion(projectDir); const libUri = createLibUri(orgname, libname, version.semver); - await Organization.getOrg({ orgname }).catch((err) => { + await Organization.getOrg({ orgname }).catch(err => { if (err.status !== 404) throw swagger.error(err); - return User.putUserOrg({ org: {}, orgname, username }).catch((err2) => { + return User.putUserOrg({ org: {}, orgname, username }).catch(err2 => { if (err2.status === 403) { throw new Error(`user "${username}" is not registered.`); } @@ -64,17 +68,18 @@ export default async function publish(swaggerUrl, orgname$, projectDir) { throw swagger.error(err2); }); }); - await Library.getOrgLib({ libname, orgname }).catch((err) => { + await Library.getOrgLib({ libname, orgname }).catch(err => { if (err.status !== 404) throw swagger.error(err); - return Library.putOrgLib({ lib: {}, libname, orgname }).catch((err2) => { + return Library.putOrgLib({ lib: {}, libname, orgname }).catch(err2 => { if (err2.status === 403) { - throw new Error(`user "${username}" can't access ${ - toStringWithoutTag(libUri)}.`); + throw new Error( + `user "${username}" can't access ${toStringWithoutTag(libUri)}.` + ); } throw swagger.error(err2); }); }); - await Version.postLibVersion({ libname, orgname, version }).catch((err) => { + await Version.postLibVersion({ libname, orgname, version }).catch(err => { if (err.status === 409) { throw new Error(`version "${toString(libUri)}" already exists.`); } diff --git a/packages/xod-cli/src/xodc-transpile.js b/packages/xod-cli/src/xodc-transpile.js index 9f36a5c4e..74caada11 100644 --- a/packages/xod-cli/src/xodc-transpile.js +++ b/packages/xod-cli/src/xodc-transpile.js @@ -10,7 +10,7 @@ import { getWorkspacePath } from './utils'; const bundledWorkspace = path.resolve(__dirname, '..'); -const showErrorAndExit = (err) => { +const showErrorAndExit = err => { msg.error(err); process.exit(1); }; @@ -24,20 +24,14 @@ export default (input, patchPath, program) => { loadProject(workspaces, input) .then(project => transformProject(project, patchPath)) .then(map(transpile)) - .then(eitherCode => - foldEither( - showErrorAndExit, - identity, - eitherCode - ) - ) - .then((code) => { + .then(eitherCode => foldEither(showErrorAndExit, identity, eitherCode)) + .then(code => { if (output) { return writeFile(output, code, 'utf8') .then(() => { msg.success(`Successfully transpiled to ${output}`); }) - .catch((err) => { + .catch(err => { msg.error(err); }); } diff --git a/packages/xod-cli/src/xodc-unpack.js b/packages/xod-cli/src/xodc-unpack.js index 47352da70..0ed0c7fd5 100644 --- a/packages/xod-cli/src/xodc-unpack.js +++ b/packages/xod-cli/src/xodc-unpack.js @@ -20,7 +20,7 @@ export default (xodball, dir) => { msg.success('Done!'); process.exit(0); }) - .catch((err) => { + .catch(err => { msg.error(err); process.exit(1); }); diff --git a/packages/xod-cli/src/xodc.js b/packages/xod-cli/src/xodc.js index 4a5c35f9d..9c644c585 100644 --- a/packages/xod-cli/src/xodc.js +++ b/packages/xod-cli/src/xodc.js @@ -43,22 +43,24 @@ Options: `; const programs = { - pack: o => pack(o[''], o[''], { - workspace: o['--workspace'], - }), + pack: o => + pack(o[''], o[''], { + workspace: o['--workspace'], + }), unpack: o => unpack(o[''], o['']), - transpile: o => transpile(o[''], o[''], { - output: o['--output'], - workspace: o['--workspace'], - }), - publish: o => publish( - o['--swagger'] || PM_SWAGGER_URL, - o['--orgname'], - o[''] || '.'), - install: o => install( - o['--swagger'] || PM_SWAGGER_URL, - o[''], - o['--workspace']), + transpile: o => + transpile(o[''], o[''], { + output: o['--output'], + workspace: o['--workspace'], + }), + publish: o => + publish( + o['--swagger'] || PM_SWAGGER_URL, + o['--orgname'], + o[''] || '.' + ), + install: o => + install(o['--swagger'] || PM_SWAGGER_URL, o[''], o['--workspace']), }; // Running command diff --git a/packages/xod-cli/test/transpile.spec.js b/packages/xod-cli/test/transpile.spec.js index c28190bf8..451f04ff6 100644 --- a/packages/xod-cli/test/transpile.spec.js +++ b/packages/xod-cli/test/transpile.spec.js @@ -7,21 +7,29 @@ import { rmrf } from 'xod-fs'; const tmpPath = subpath => path.resolve(__dirname, './tmp', subpath); const xodc = path.resolve(__dirname, '../bin/xodc'); -const wsPath = subpath => path.resolve(__dirname, '../../../workspace', subpath); +const wsPath = subpath => + path.resolve(__dirname, '../../../workspace', subpath); describe('xodc transpile', () => { afterEach(() => rmrf(tmpPath('./'))); it('should transpile Blink project for Arduino', () => - exec(`node ${xodc} transpile --output=${tmpPath('blink.cpp')} ${wsPath('blink')} @/main`) - .then(() => Promise.all([ - fs.readFile(tmpPath('blink.cpp'), 'utf-8'), - fs.readFile(wsPath('blink/__fixtures__/arduino.cpp'), 'utf-8'), - ])) - .then(([actual, expected]) => assert.strictEqual( - actual, - expected, - 'expected and actual C++ don’t match' - )) - ); + exec( + `node ${xodc} transpile --output=${tmpPath('blink.cpp')} ${wsPath( + 'blink' + )} @/main` + ) + .then(() => + Promise.all([ + fs.readFile(tmpPath('blink.cpp'), 'utf-8'), + fs.readFile(wsPath('blink/__fixtures__/arduino.cpp'), 'utf-8'), + ]) + ) + .then(([actual, expected]) => + assert.strictEqual( + actual, + expected, + 'expected and actual C++ don’t match' + ) + )); }); diff --git a/packages/xod-client-browser/src/components/PopupInstallApp.jsx b/packages/xod-client-browser/src/components/PopupInstallApp.jsx index e4c2e9040..64fd7d1bb 100644 --- a/packages/xod-client-browser/src/components/PopupInstallApp.jsx +++ b/packages/xod-client-browser/src/components/PopupInstallApp.jsx @@ -44,8 +44,8 @@ class PopupInstallApp extends React.PureComponent {
Uploading a program requires access to USB ports.
- Browsers do not provide it for security reasons,  - so you have to install more permissive IDE for the desktop. + Browsers do not provide it for security reasons,  so you have + to install more permissive IDE for the desktop.
Download -
diff --git a/packages/xod-client-browser/src/containers/App.jsx b/packages/xod-client-browser/src/containers/App.jsx index 16c1f5a1f..04d146b25 100644 --- a/packages/xod-client-browser/src/containers/App.jsx +++ b/packages/xod-client-browser/src/containers/App.jsx @@ -57,11 +57,13 @@ class App extends client.App { } onDocumentClick(e) { - if (R.allPass([ - notNil, - R.propEq('tagName', 'A'), - R.propEq('protocol', client.URL_ACTION_PROTOCOL), - ])(e.target)) { + if ( + R.allPass([ + notNil, + R.propEq('tagName', 'A'), + R.propEq('protocol', client.URL_ACTION_PROTOCOL), + ])(e.target) + ) { const url = urlParse(e.target.href, true); if (url.hostname !== client.URL_ACTION_PREFIX) return; @@ -75,7 +77,9 @@ class App extends client.App { if (action) { action(params); } else { - this.props.actions.addError(client.Messages.invalidUrlActionName(actionName)); + this.props.actions.addError( + client.Messages.invalidUrlActionName(actionName) + ); } } } @@ -104,8 +108,10 @@ class App extends client.App { const xodballJSON = XP.toXodball(project); const xodballName = XP.getProjectName(project); - const link = (document) ? document.createElement('a') : null; - const url = `data:application/xod;charset=utf8,${encodeURIComponent(xodballJSON)}`; + const link = document ? document.createElement('a') : null; + const url = `data:application/xod;charset=utf8,${encodeURIComponent( + xodballJSON + )}`; if (link && link.download !== undefined) { link.href = url; @@ -124,7 +130,7 @@ class App extends client.App { const file = event.target.files[0]; const reader = new window.FileReader(); - reader.onload = (e) => { + reader.onload = e => { this.onLoad(e.target.result); }; @@ -161,7 +167,8 @@ class App extends client.App { let message = true; if (this.props.hasUnsavedChanges) { - message = 'You have not saved changes in your project. Are you sure want to close app?'; + message = + 'You have not saved changes in your project. Are you sure want to close app?'; if (event) { event.returnValue = message; } // eslint-disable-line } @@ -169,45 +176,39 @@ class App extends client.App { } getMenuBarItems() { - const { - items, - onClick, - submenu, - } = client.menu; + const { items, onClick, submenu } = client.menu; const openProject = { key: items.openProject.key, - click: (event) => { + click: event => { if ( event.target === this.menuRefs.openProject || event.target.parentNode === this.menuRefs.openProject.parentNode - ) return; + ) { + return; + } event.stopPropagation(); this.menuRefs.openProject.click(); }, children: ( -
diff --git a/packages/xod-client/src/editor/components/Inspector.jsx b/packages/xod-client/src/editor/components/Inspector.jsx index e143267f0..1de8c510e 100644 --- a/packages/xod-client/src/editor/components/Inspector.jsx +++ b/packages/xod-client/src/editor/components/Inspector.jsx @@ -5,11 +5,7 @@ import React from 'react'; import { Patch } from 'xod-project'; import { $Maybe } from 'xod-func-tools'; -import { - SELECTION_ENTITY_TYPE, - PANEL_IDS, - SIDEBAR_IDS, -} from '../constants'; +import { SELECTION_ENTITY_TYPE, PANEL_IDS, SIDEBAR_IDS } from '../constants'; import SidebarPanel from '../components/SidebarPanel'; import NodeInspector from './NodeInspector'; @@ -20,7 +16,6 @@ import { noop, isMany, isOne } from '../../utils/ramda'; import { RenderableSelection } from '../../types'; import sanctuaryPropType from '../../utils/sanctuaryPropType'; - // ============================================================================= // // Sub-components @@ -29,9 +24,7 @@ import sanctuaryPropType from '../../utils/sanctuaryPropType'; const InspectorMessage = ({ text }) => (
- +
); @@ -45,30 +38,19 @@ InspectorMessage.propTypes = { // // ============================================================================= const renderSelectedManyElements = selection => ( - + ); const renderSelectedLink = () => ( - + ); const renderSelectedComment = () => ( - -); -const renderSelectedNode = R.curry( - (onPropUpdate, selection) => ( - - ) + ); +const renderSelectedNode = R.curry((onPropUpdate, selection) => ( + +)); const renderSelectedPatch = R.curry( (currentPatch, onPatchDescriptionUpdate) => ( ( - + ); // ============================================================================= @@ -90,14 +70,15 @@ const renderDefault = () => ( // ============================================================================= // :: [ RenderableSelection ] -> Boolean -const isEntity = entity => R.compose(R.equals(entity), R.prop('entityType'), R.head); +const isEntity = entity => + R.compose(R.equals(entity), R.prop('entityType'), R.head); const isSingleNode = R.both(isOne, isEntity(SELECTION_ENTITY_TYPE.NODE)); const isSingleLink = R.both(isOne, isEntity(SELECTION_ENTITY_TYPE.LINK)); const isSingleComment = R.both(isOne, isEntity(SELECTION_ENTITY_TYPE.COMMENT)); // :: [ RenderableSelection ] -> Patch -> Boolean -const isPatchSelected = R.curry((patch, selection) => ( - (R.isEmpty(selection) && patch.isJust) -)); +const isPatchSelected = R.curry( + (patch, selection) => R.isEmpty(selection) && patch.isJust +); // ============================================================================= // diff --git a/packages/xod-client/src/editor/components/LibSuggester.jsx b/packages/xod-client/src/editor/components/LibSuggester.jsx index 3b1adfa1d..c2c0d5405 100644 --- a/packages/xod-client/src/editor/components/LibSuggester.jsx +++ b/packages/xod-client/src/editor/components/LibSuggester.jsx @@ -19,15 +19,11 @@ import * as MSG from '../messages'; const getSuggestionValue = R.prop('requestParams'); const renderNothingFound = () => ( -
- {MSG.LIB_SUGGESTER_NOTHING_FOUND} -
+
{MSG.LIB_SUGGESTER_NOTHING_FOUND}
); const renderTypeToBegin = () => ( -
- {MSG.LIB_SUGGESTER_TYPE_TO_BEGIN} -
+
{MSG.LIB_SUGGESTER_TYPE_TO_BEGIN}
); class LibSuggester extends React.Component { @@ -45,8 +41,12 @@ class LibSuggester extends React.Component { this.renderItem = this.renderItem.bind(this); this.onChange = this.onChange.bind(this); - this.onSuggestionsFetchRequested = this.onSuggestionsFetchRequested.bind(this); - this.onSuggestionsClearRequested = this.onSuggestionsClearRequested.bind(this); + this.onSuggestionsFetchRequested = this.onSuggestionsFetchRequested.bind( + this + ); + this.onSuggestionsClearRequested = this.onSuggestionsClearRequested.bind( + this + ); this.onSuggestionSelected = this.onSuggestionSelected.bind(this); this.storeInputReference = this.storeInputReference.bind(this); this.renderContent = this.renderContent.bind(this); @@ -70,7 +70,7 @@ class LibSuggester extends React.Component { this.setState({ value: newValue, - notFound: (newValue === '') ? false : this.state.notFound, + notFound: newValue === '' ? false : this.state.notFound, }); } @@ -105,11 +105,11 @@ class LibSuggester extends React.Component { fetchLibData(getPmSwaggerUrl(), request) .then(R.of) // TODO: Once it will become an array .catch(R.always([])) - .then( - data => this.setState({ + .then(data => + this.setState({ suggestions: data, loading: false, - notFound: (data.length === 0), + notFound: data.length === 0, }) ); } @@ -141,9 +141,9 @@ class LibSuggester extends React.Component { 'is-highlighted': isHighlighted, }); - const license = (item.license) - ? {item.license} - : null; + const license = item.license ? ( + {item.license} + ) : null; const libName = `${item.owner}/${item.libname}`; @@ -187,7 +187,7 @@ class LibSuggester extends React.Component { placeholder: 'Search libraries', value, onChange: this.onChange, - onKeyDown: (event) => { + onKeyDown: event => { const code = event.keyCode || event.which; if (code === KEYCODE.ESCAPE && event.target.value === '') { this.props.onBlur(); @@ -197,12 +197,9 @@ class LibSuggester extends React.Component { type: 'search', }; - const loading = (this.state.loading) ? ( + const loading = this.state.loading ? (
- +
) : null; diff --git a/packages/xod-client/src/editor/components/NoPatch.jsx b/packages/xod-client/src/editor/components/NoPatch.jsx index c846dd879..82e88107d 100644 --- a/packages/xod-client/src/editor/components/NoPatch.jsx +++ b/packages/xod-client/src/editor/components/NoPatch.jsx @@ -1,13 +1,18 @@ import React from 'react'; const NoPatch = () => ( -
+
- + diff --git a/packages/xod-client/src/editor/components/NodeInspector.jsx b/packages/xod-client/src/editor/components/NodeInspector.jsx index d2de742dc..75fb65067 100644 --- a/packages/xod-client/src/editor/components/NodeInspector.jsx +++ b/packages/xod-client/src/editor/components/NodeInspector.jsx @@ -3,10 +3,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import * as XP from 'xod-project'; -import { - SELECTION_ENTITY_TYPE, - WIDGET_TYPE, -} from '../constants'; +import { SELECTION_ENTITY_TYPE, WIDGET_TYPE } from '../constants'; import { NODE_PROPERTY_KIND, NODE_PROPERTY_KEY } from '../../project/constants'; import WidgetsGroup from './WidgetsGroup'; @@ -19,13 +16,10 @@ import { getUtmSiteUrl } from '../../utils/urls'; import * as MESSAGES from '../messages'; // :: RenderablePin -> String -const getWidgetKey = R.converge( - (id, key) => `${id}_${key}`, - [ - R.prop('nodeId'), - R.prop('key'), - ] -); +const getWidgetKey = R.converge((id, key) => `${id}_${key}`, [ + R.prop('nodeId'), + R.prop('key'), +]); const getPinWidgetProps = R.applySpec({ entityId: R.prop('nodeId'), @@ -62,10 +56,7 @@ const createPinWidgetsConfig = R.compose( ), R.apply(R.concat), R.map(R.sort(R.ascend(XP.getPinOrder))), - R.juxt([ - R.filter(XP.isInputPin), - R.filter(XP.isOutputPin), - ]), + R.juxt([R.filter(XP.isInputPin), R.filter(XP.isOutputPin)]), R.values, R.prop('pins') ); @@ -82,9 +73,8 @@ const NodeDescriptionWidget = Widgets.composeWidget( const NodeInspector = ({ node, onPropUpdate }) => { const type = XP.getNodeType(node); - const nodeHelpIcon = (XP.isPathBuiltIn(type) || XP.isPathLocal(type)) - ? null - : ( + const nodeHelpIcon = + XP.isPathBuiltIn(type) || XP.isPathLocal(type) ? null : ( { const nodeId = XP.getNodeId(node); - const DeadNodeMessage = (node.dead) ? ( + const DeadNodeMessage = node.dead ? ( ) : null; diff --git a/packages/xod-client/src/editor/components/PanelContextMenu.jsx b/packages/xod-client/src/editor/components/PanelContextMenu.jsx index 4d7c5f942..d805df6f2 100644 --- a/packages/xod-client/src/editor/components/PanelContextMenu.jsx +++ b/packages/xod-client/src/editor/components/PanelContextMenu.jsx @@ -6,41 +6,43 @@ import { ContextMenu, MenuItem, connectMenu } from 'react-contextmenu'; import { PANEL_CONTEXT_MENU_ID, PANEL_IDS, SIDEBAR_IDS } from '../constants'; -const callCallbackWithPanelId = onClick => (event, data) => onClick(data.panelId); +const callCallbackWithPanelId = onClick => (event, data) => + onClick(data.panelId); -const PanelContextMenu = (props) => { - const trigger = (props.trigger) ? props.trigger : {}; +const PanelContextMenu = props => { + const trigger = props.trigger ? props.trigger : {}; const cls = cn('ContextMenu ContextMenu--Sidebar', { // It's a hack to prevent rendering contextmenu // after click something with wrong menu items 'ContextMenu--hide': !props.trigger, }); - const callSwitchSideClick = R.curry( - (sidebarId, panelId) => props.onSwitchSideClick(sidebarId, panelId) + const callSwitchSideClick = R.curry((sidebarId, panelId) => + props.onSwitchSideClick(sidebarId, panelId) ); return ( - + Minimize - {(trigger.sidebarId === SIDEBAR_IDS.LEFT) ? ( + {trigger.sidebarId === SIDEBAR_IDS.LEFT ? ( Dock to Right ) : ( Dock to Left diff --git a/packages/xod-client/src/editor/components/PatchDocs.jsx b/packages/xod-client/src/editor/components/PatchDocs.jsx index 75b10f247..8b96f5d2c 100644 --- a/packages/xod-client/src/editor/components/PatchDocs.jsx +++ b/packages/xod-client/src/editor/components/PatchDocs.jsx @@ -15,7 +15,7 @@ const NODE_POSITION_IN_PREVIEW = { y: 20, // compensate for labels outside the node }; -const MAX_NODE_WIDTH = 245 - (NODE_POSITION_IN_PREVIEW.x * 2); +const MAX_NODE_WIDTH = 245 - NODE_POSITION_IN_PREVIEW.x * 2; const NODE_PREVIEW_HEIGHT = 93; const PinInfo = ({ type, label, description, isVariadic }) => ( @@ -52,7 +52,9 @@ const InputPins = ({ pins, distanceBetweenPins }) => {
- {isLastPin ? null : } + {isLastPin ? null : ( + + )}
); }; @@ -102,25 +104,17 @@ OutputPins.defaultProps = { isFirst: true, }; - const PatchDocs = ({ patch, minimal }) => { const variadicPinKeys = R.compose( - foldEither( - R.always([]), - R.map(XP.getPinKey), - ), + foldEither(R.always([]), R.map(XP.getPinKey)), XP.listVariadicValuePins )(patch); const [inputPins, outputPins] = R.compose( R.partition(XP.isInputPin), XP.normalizePinLabels, - R.map( - pin => R.assoc( - 'isVariadic', - R.contains(XP.getPinKey(pin), variadicPinKeys), - pin - ) + R.map(pin => + R.assoc('isVariadic', R.contains(XP.getPinKey(pin), variadicPinKeys), pin) ), XP.listPins )(patch); @@ -130,19 +124,20 @@ const PatchDocs = ({ patch, minimal }) => { const nodeProps = patchToNodeProps(true, patch); - const scaleFactor = nodeProps.size.width < MAX_NODE_WIDTH - ? 1 - : MAX_NODE_WIDTH / nodeProps.size.width; + const scaleFactor = + nodeProps.size.width < MAX_NODE_WIDTH + ? 1 + : MAX_NODE_WIDTH / nodeProps.size.width; const fromNodeEdgeToPin = scaleFactor * (SLOT_SIZE.WIDTH / 2); - const scaledNodeWidth = (nodeProps.size.width * scaleFactor) + fromNodeEdgeToPin; - const distanceToFirstPin = minimal ? 0 : ( - fromNodeEdgeToPin + - (NODE_POSITION_IN_PREVIEW.x * scaleFactor) - ) - 1; + const scaledNodeWidth = + nodeProps.size.width * scaleFactor + fromNodeEdgeToPin; + const distanceToFirstPin = minimal + ? 0 + : fromNodeEdgeToPin + NODE_POSITION_IN_PREVIEW.x * scaleFactor - 1; const scaledNodePreviewHeight = NODE_PREVIEW_HEIGHT * scaleFactor; - const distanceBetweenPins = minimal ? 0 : (scaleFactor * SLOT_SIZE.WIDTH) - 1; + const distanceBetweenPins = minimal ? 0 : scaleFactor * SLOT_SIZE.WIDTH - 1; // because we never draw labels for terminal nodes const position = R.when( @@ -173,7 +168,9 @@ const PatchDocs = ({ patch, minimal }) => {
{inputPins.length > 0 && [ minimal && ( - Inputs: + + Inputs: + ), { />, ]} {!minimal && ( - + - + )}
{outputPins.length > 0 && ( -
- {minimal && ( - Outputs: - )} +
+ {minimal && Outputs:} (
-
- {title} -
+
{title}
{additionalButtons} 0), + 'Sidebar-title--hidden': isMinimized && maximizedPanels.length > 0, }); - const onToggleProjectBrowserPanel = () => onTogglePanel(PANEL_IDS.PROJECT_BROWSER); + const onToggleProjectBrowserPanel = () => + onTogglePanel(PANEL_IDS.PROJECT_BROWSER); const onToggleInspectorPanel = () => onTogglePanel(PANEL_IDS.INSPECTOR); const onToggleAccountPanel = () => onTogglePanel(PANEL_IDS.ACCOUNT); const ontoggleHelpPanel = () => onTogglePanel(PANEL_IDS.HELPBAR); return (
- {R.map(R.cond([ - sidebarPanelRenderer(PANEL_IDS.PROJECT_BROWSER, ({ maximized }) => ( -
); }; @@ -71,13 +76,15 @@ const SidebarSwitches = ({ SidebarSwitches.propTypes = { isMinimized: PropTypes.bool.isRequired, id: PropTypes.string.isRequired, - panels: PropTypes.objectOf(PropTypes.shape({ - /* eslint-disable react/no-unused-prop-types */ - maximized: PropTypes.bool.isRequired, - sidebar: PropTypes.oneOf(R.values(SIDEBAR_IDS)).isRequired, - autohide: PropTypes.bool.isRequired, - /* eslint-enable react/no-unused-prop-types */ - })), + panels: PropTypes.objectOf( + PropTypes.shape({ + /* eslint-disable react/no-unused-prop-types */ + maximized: PropTypes.bool.isRequired, + sidebar: PropTypes.oneOf(R.values(SIDEBAR_IDS)).isRequired, + autohide: PropTypes.bool.isRequired, + /* eslint-enable react/no-unused-prop-types */ + }) + ), onTogglePanel: PropTypes.func.isRequired, isLoggedIn: PropTypes.bool, }; diff --git a/packages/xod-client/src/editor/components/Suggester.jsx b/packages/xod-client/src/editor/components/Suggester.jsx index d41dc0796..363e13962 100644 --- a/packages/xod-client/src/editor/components/Suggester.jsx +++ b/packages/xod-client/src/editor/components/Suggester.jsx @@ -15,7 +15,9 @@ import SuggesterContainer from './SuggesterContainer'; const getSuggestionValue = ({ item }) => item.path; -const getSuggestionIndex = R.uncurryN(2, suggestion => R.findIndex(R.equals(suggestion))); +const getSuggestionIndex = R.uncurryN(2, suggestion => + R.findIndex(R.equals(suggestion)) +); class Suggester extends React.Component { constructor(props) { @@ -40,8 +42,12 @@ class Suggester extends React.Component { this.onChange = this.onChange.bind(this); this.onItemMouseOver = this.onItemMouseOver.bind(this); this.onContainerMouseMove = this.onContainerMouseMove.bind(this); - this.onSuggestionsFetchRequested = this.onSuggestionsFetchRequested.bind(this); - this.onSuggestionsClearRequested = this.onSuggestionsClearRequested.bind(this); + this.onSuggestionsFetchRequested = this.onSuggestionsFetchRequested.bind( + this + ); + this.onSuggestionsClearRequested = this.onSuggestionsClearRequested.bind( + this + ); this.onSuggestionSelected = this.onSuggestionSelected.bind(this); this.onSuggestionHighlighted = this.onSuggestionHighlighted.bind(this); this.storeRef = this.storeRef.bind(this); @@ -123,7 +129,9 @@ class Suggester extends React.Component { const { index } = this.props; const inputValue = value.trim().toLowerCase(); - if (inputValue.length === 0) { return []; } + if (inputValue.length === 0) { + return []; + } return index.search(regExpEscape(inputValue)); } @@ -169,7 +177,7 @@ class Suggester extends React.Component { placeholder: 'Search nodes', value, onChange: this.onChange, - onKeyDown: (event) => { + onKeyDown: event => { const code = event.keyCode || event.which; if (code === KEYCODE.ESCAPE && event.target.value === '') { this.props.onBlur(); diff --git a/packages/xod-client/src/editor/components/SuggesterContainer.jsx b/packages/xod-client/src/editor/components/SuggesterContainer.jsx index 26dbd7407..2e37b6739 100644 --- a/packages/xod-client/src/editor/components/SuggesterContainer.jsx +++ b/packages/xod-client/src/editor/components/SuggesterContainer.jsx @@ -34,18 +34,20 @@ class SuggesterContainer extends React.Component { if (this.scrollRef) { const contentWrapper = this.scrollRef.refs.contentWrapper; - const highlighted = contentWrapper.getElementsByClassName('is-highlighted'); + const highlighted = contentWrapper.getElementsByClassName( + 'is-highlighted' + ); if (highlighted.length > 0) { const top = highlighted[0].offsetTop; const height = highlighted[0].clientHeight; const containerHeight = contentWrapper.clientHeight; const scrollPos = this.scrollRef.state.scrollPos; - const isOutsideUp = (top < scrollPos); - const isOutsideDown = (top + height > (scrollPos + containerHeight)); + const isOutsideUp = top < scrollPos; + const isOutsideDown = top + height > scrollPos + containerHeight; if (isOutsideDown) { - return (top + height) - containerHeight; + return top + height - containerHeight; } else if (isOutsideUp) { return top; } diff --git a/packages/xod-client/src/editor/components/TabsContainer.jsx b/packages/xod-client/src/editor/components/TabsContainer.jsx index f27016b08..04939b9e8 100644 --- a/packages/xod-client/src/editor/components/TabsContainer.jsx +++ b/packages/xod-client/src/editor/components/TabsContainer.jsx @@ -2,9 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; const TabsContainer = ({ children }) => ( -
    - {children} -
+
    {children}
); TabsContainer.propTypes = { diff --git a/packages/xod-client/src/editor/components/TabsItem.jsx b/packages/xod-client/src/editor/components/TabsItem.jsx index 5efe88118..d7984f0d6 100644 --- a/packages/xod-client/src/editor/components/TabsItem.jsx +++ b/packages/xod-client/src/editor/components/TabsItem.jsx @@ -9,23 +9,15 @@ const TabsItem = ({ data, onClick, onClose }) => { }); const handleClick = () => onClick(data.id); - const handleClose = (event) => { + const handleClose = event => { event.stopPropagation(); onClose(data.id); }; return ( -
  • - - {data.label} - - +
  • + {data.label} + ×
  • diff --git a/packages/xod-client/src/editor/components/WidgetsGroup.jsx b/packages/xod-client/src/editor/components/WidgetsGroup.jsx index 1b7313ca5..9d140c8b7 100644 --- a/packages/xod-client/src/editor/components/WidgetsGroup.jsx +++ b/packages/xod-client/src/editor/components/WidgetsGroup.jsx @@ -13,30 +13,31 @@ class WidgetsGroup extends React.Component { } componentWillReceiveProps(nextProps) { - const shouldCreateComponents = !R.equals(this.props.entity, nextProps.entity); + const shouldCreateComponents = !R.equals( + this.props.entity, + nextProps.entity + ); const widgetsData = this.props.createWidgetsConfig(nextProps.entity); - const dataToUpdate = (shouldCreateComponents) ? widgetsData : R.pick(['props'], widgetsData); + const dataToUpdate = shouldCreateComponents + ? widgetsData + : R.pick(['props'], widgetsData); this.setState(dataToUpdate); } render() { const widgets = R.compose( R.values, - R.mapObjIndexed((Widget, key) => + R.mapObjIndexed((Widget, key) => (
  • - ) + )) )(this.state.components); - return ( -
      - {widgets} -
    - ); + return
      {widgets}
    ; } } diff --git a/packages/xod-client/src/editor/components/inspectorWidgets/DescriptionWidget.jsx b/packages/xod-client/src/editor/components/inspectorWidgets/DescriptionWidget.jsx index c28c1ea17..81d1f0bf8 100644 --- a/packages/xod-client/src/editor/components/inspectorWidgets/DescriptionWidget.jsx +++ b/packages/xod-client/src/editor/components/inspectorWidgets/DescriptionWidget.jsx @@ -1,15 +1,13 @@ import React from 'react'; import PropTypes from 'prop-types'; -const DescriptionWidget = (props) => { - const onChange = (event) => { +const DescriptionWidget = props => { + const onChange = event => { props.onChange(event.target.value); }; return (
    - +