mirror of
https://github.com/xodio/xod.git
synced 2026-03-10 10:46:52 +01:00
feat(xod-fs, xod-client-electron, xod-cli): load libs from a local workspace and extra lib directories (+4 squashed commits)
Squashed commits: [12a9428] refactor(xod-cli): bundle std libs with xodc and use them in `xodc transpile` command [66d1b9d] refactor(xod-fs, workspace): rename `lib` folder into `__lib__` and make it not necessary and don't copy stdlib into user workspace [f19c366] feat(xod-fs): load project with libs from local workspace and bundled workspace [ea9ac0e] feat(xod-func-tools): add `uniqLists` function
This commit is contained in:
1
packages/xod-cli/.gitignore
vendored
1
packages/xod-cli/.gitignore
vendored
@@ -1 +1,2 @@
|
||||
bin/
|
||||
__lib__/
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
"version": "0.14.0",
|
||||
"description": "XOD project: Command Line Interface",
|
||||
"scripts": {
|
||||
"build": "babel src/ -d bin/ --source-maps",
|
||||
"build:workspace": "cpx \"../../workspace/__lib__/**\" \"./__lib__\"",
|
||||
"build:src": "babel src/ -d bin/ --source-maps",
|
||||
"build": "yarn run build:src && yarn run build:workspace",
|
||||
"dev": "yarn run build --watch",
|
||||
"test": "mocha test/**/*.spec.js"
|
||||
},
|
||||
@@ -33,6 +35,7 @@
|
||||
"devDependencies": {
|
||||
"chai": "^4.0.0",
|
||||
"child-process-promise": "^2.2.1",
|
||||
"cpx": "^1.5.0",
|
||||
"fs-extra": "^3.0.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,8 +101,10 @@ export default function install(swaggerUrl, libUri, path2) {
|
||||
xodFs.findClosestWorkspaceDir(path2),
|
||||
])
|
||||
.then(([libUri2, swaggerClient, workspaceDir]) => {
|
||||
const orgDir = path.resolve(workspaceDir, 'lib',
|
||||
xodFs.fsSafeName(libUri2.orgname));
|
||||
const orgDir = path.resolve(
|
||||
xodFs.resolveLibPath(workspaceDir),
|
||||
xodFs.fsSafeName(libUri2.orgname)
|
||||
);
|
||||
const libDir = path.resolve(orgDir, xodFs.fsSafeName(libUri2.libname));
|
||||
return libDirDoesNotExist(libDir, libUri2)
|
||||
.then(() => getProject(swaggerClient, libUri2))
|
||||
|
||||
@@ -12,7 +12,7 @@ export default (projectDir, output) => {
|
||||
|
||||
msg.notice(`Packing ${dirName} into ${output} ...`);
|
||||
|
||||
loadProjectWithLibs(projectPath, workspace)
|
||||
loadProjectWithLibs([], projectPath, workspace)
|
||||
.then(({ project, libs }) => pack(project, libs))
|
||||
.then(packed => writeJSON(output, packed))
|
||||
.then(() => {
|
||||
|
||||
@@ -8,6 +8,8 @@ import { loadProject, readJSON, writeFile } from 'xod-fs';
|
||||
import { transformProject, transpile } from 'xod-arduino';
|
||||
import * as msg from './messages';
|
||||
|
||||
const bundledLibs = path.resolve(__dirname, '../__lib__');
|
||||
|
||||
const showErrorAndExit = (err) => {
|
||||
msg.error(err);
|
||||
process.exit(1);
|
||||
@@ -38,7 +40,7 @@ export default (input, patchPath, program) => {
|
||||
}
|
||||
|
||||
if (isDirectory) {
|
||||
loadProject(dir)
|
||||
loadProject(dir, [bundledLibs])
|
||||
.then(resolve)
|
||||
.catch(reject);
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
"name": "xod-client-electron",
|
||||
"version": "0.14.0",
|
||||
"scripts": {
|
||||
"copy-workspace": "cpx \"../../workspace/**/*\" \"src-babel/workspace\"",
|
||||
"build:workspace": "cpx \"../../workspace/**/*\" \"src-babel/workspace\"",
|
||||
"build:gui": "webpack --colors",
|
||||
"build:app": "babel src/app/ -d src-babel/app/ && babel src/shared/ -d src-babel/shared/",
|
||||
"build": "yarn build:gui && yarn build:app && yarn copy-workspace",
|
||||
"build": "yarn build:gui && yarn build:app && yarn build:workspace",
|
||||
"start": "electron .",
|
||||
"postinstall": "electron-builder install-app-deps",
|
||||
"test": "electron-mocha ./test",
|
||||
|
||||
@@ -4,8 +4,8 @@ import path from 'path';
|
||||
|
||||
import * as XP from 'xod-project';
|
||||
import {
|
||||
resolveLibPath,
|
||||
spawnWorkspaceFile,
|
||||
spawnStdLib,
|
||||
spawnDefaultProject,
|
||||
saveProject,
|
||||
getLocalProjects,
|
||||
@@ -14,7 +14,6 @@ import {
|
||||
getFilePath,
|
||||
filterDefaultProject,
|
||||
findProjectMetaByName,
|
||||
resolveLibPath,
|
||||
resolveDefaultProjectPath,
|
||||
ensureWorkspacePath,
|
||||
ERROR_CODES as FS_ERROR_CODES,
|
||||
@@ -32,6 +31,7 @@ import * as EVENTS from '../shared/events';
|
||||
|
||||
|
||||
export const getPathToBundledWorkspace = () => path.resolve(__dirname, '../workspace');
|
||||
export const getPathToBundledLibs = () => resolveLibPath(getPathToBundledWorkspace());
|
||||
|
||||
// =============================================================================
|
||||
//
|
||||
@@ -86,8 +86,6 @@ export const updateWorkspace = R.curry(
|
||||
//
|
||||
// =============================================================================
|
||||
|
||||
// :: () -> Path
|
||||
const getStdLibPath = () => resolveLibPath(getPathToBundledWorkspace());
|
||||
// :: () -> Path
|
||||
export const getDefaultProjectPath = () => resolveDefaultProjectPath(getPathToBundledWorkspace());
|
||||
|
||||
@@ -150,10 +148,6 @@ export const saveWorkspacePath = workspacePath => R.compose(
|
||||
settings.load
|
||||
)();
|
||||
|
||||
// :: Path -> Promise Path Error
|
||||
const spawnWorkspace = workspacePath => spawnWorkspaceFile(workspacePath)
|
||||
.then(spawnStdLib(getStdLibPath()));
|
||||
|
||||
// :: (String -> a -> ()) ->Path -> Promise ProjectMeta[] Error
|
||||
const spawnAndLoadDefaultProject = (send, workspacePath) =>
|
||||
spawnDefaultProject(getDefaultProjectPath(), workspacePath)
|
||||
@@ -204,7 +198,7 @@ export const onOpenProject = R.curry(
|
||||
// :: (String -> a -> ()) -> (() -> Path) -> Path -> Promise Project Error
|
||||
export const onSelectProject = R.curry(
|
||||
(send, pathGetter, projectMeta) => pathGetter()
|
||||
.then(() => loadProject(getFilePath(projectMeta)))
|
||||
.then(() => loadProject(getFilePath(projectMeta), [getPathToBundledLibs()]))
|
||||
.then(requestShowProject(send))
|
||||
.catch(R.ifElse(
|
||||
R.prop('errorCode'),
|
||||
@@ -217,7 +211,7 @@ export const onSelectProject = R.curry(
|
||||
// :: (String -> a -> ()) -> (Path -> Promise Path Error) -> Path -> Promise ProjectMeta[] Error
|
||||
export const onCreateWorkspace = R.curry(
|
||||
(send, pathSaver, workspacePath) => R.pipeP(
|
||||
spawnWorkspace,
|
||||
spawnWorkspaceFile,
|
||||
pathSaver,
|
||||
updateWorkspace(send, ''),
|
||||
loadProjectsOrSpawnDefault(send)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
export const WORKSPACE_FILENAME = '.xodworkspace';
|
||||
export const DEFAULT_WORKSPACE_PATH = '~/xod/';
|
||||
export const DEFAULT_PROJECT_NAME = 'welcome-to-xod';
|
||||
export const LIBS_FOLDERNAME = 'lib';
|
||||
export const LIBS_DIRNAME = '__lib__';
|
||||
|
||||
export const IMPL_FILENAMES = {
|
||||
cpp: 'any.cpp',
|
||||
|
||||
@@ -13,7 +13,7 @@ export {
|
||||
loadProjectWithLibs,
|
||||
loadProjectWithoutLibs,
|
||||
} from './load';
|
||||
export { loadAllLibs } from './loadLibs';
|
||||
export { loadLibsFromWorkspaceList } from './loadLibs';
|
||||
export * from './utils';
|
||||
export {
|
||||
findClosestProjectDir,
|
||||
|
||||
@@ -7,11 +7,12 @@ import * as XP from 'xod-project';
|
||||
|
||||
import pack from './pack';
|
||||
import { findClosestWorkspaceDir } from './find';
|
||||
import { loadAllLibs } from './loadLibs';
|
||||
import { loadLibs } from './loadLibs';
|
||||
import { readDir, readJSON } from './read';
|
||||
import * as ERROR_CODES from './errorCodes';
|
||||
import {
|
||||
resolvePath,
|
||||
resolveLibPath,
|
||||
resolveProjectFile,
|
||||
isLocalProjectDirectory,
|
||||
basenameEquals,
|
||||
@@ -122,35 +123,61 @@ export const loadProjectWithoutLibs = projectPath => R.composeP(
|
||||
readDir
|
||||
)(projectPath);
|
||||
|
||||
// :: Path -> Path -> Path -> Promise [File] Error
|
||||
export const loadProjectWithLibs = (projectPath, workspace, libWorkspace = workspace) => {
|
||||
const fullProjectPath = resolvePath(path.resolve(workspace, projectPath));
|
||||
const libPath = resolvePath(libWorkspace);
|
||||
// :: [Path] -> Path -> Path -> Promise [File] Error
|
||||
export const loadProjectWithLibs = R.curry(
|
||||
(extraLibDirs, projectPath, workspace) => {
|
||||
const fullProjectPath = resolvePath(path.resolve(workspace, projectPath));
|
||||
const userLibsPath = resolveLibPath(workspace);
|
||||
|
||||
return Promise.all([
|
||||
loadProjectWithoutLibs(fullProjectPath),
|
||||
loadAllLibs(libPath),
|
||||
]).then(([projectFiles, libs]) => ({ project: projectFiles, libs }))
|
||||
.catch(err => Promise.reject(
|
||||
Object.assign(err, {
|
||||
libPath,
|
||||
fullProjectPath,
|
||||
workspace,
|
||||
})
|
||||
));
|
||||
};
|
||||
const libDirPaths = R.compose(
|
||||
R.concat([userLibsPath]),
|
||||
R.map(resolvePath)
|
||||
)(extraLibDirs);
|
||||
|
||||
return Promise.all([
|
||||
loadProjectWithoutLibs(fullProjectPath),
|
||||
loadLibs(libDirPaths),
|
||||
]).then(([projectFiles, libs]) => ({ project: projectFiles, libs }))
|
||||
.catch(err => Promise.reject(
|
||||
Object.assign(err, {
|
||||
libPath: userLibsPath,
|
||||
fullProjectPath,
|
||||
workspace,
|
||||
})
|
||||
));
|
||||
}
|
||||
);
|
||||
|
||||
// :: Path -> Promise Project Error
|
||||
//
|
||||
// Loads a regular XOD project placed in a workspace. The workspace and project
|
||||
// name are determined by path provided. It is expected to be a path to the
|
||||
// project directory, e.g. `/path/to/workspace/my-proj`.
|
||||
//
|
||||
// Returns a Promise of complete `Project` (see `xod-project`).
|
||||
export const loadProject = projectPath =>
|
||||
/**
|
||||
* Loads a regular XOD project placed in a workspace. Workspace and project
|
||||
* names are determined by path provided. It is expected to be a path to the
|
||||
* project directory, e.g. `/path/to/workspace/my-proj`.
|
||||
*
|
||||
* Also it loads libraries from libs directory inside of the user workspace
|
||||
* and from `extraLibDirs` list, if it is not empty.
|
||||
* The lib directory in the user workspace has a highest priority,
|
||||
* all extra lib dirs has a smaller priority in accordance to its index
|
||||
* (more index is less priority).
|
||||
*
|
||||
* If lib directories contains libraries with the same names — only one will
|
||||
* be loaded, from lib directory with the highest priority.
|
||||
*
|
||||
* E.G.
|
||||
* - user workspace contains `xod/units`
|
||||
* - extraLibDir[0] contains `xod/core`, `xod/common-hardware`, `xod/units`
|
||||
* - extraLibDir[1] contains `xod/core`, `xod/awesome`
|
||||
* As a result:
|
||||
* - `xod/units` will be loaded from user workspace
|
||||
* - `xod/core` and `xod/common-hardware` will be loaded from extraLibDir[0]
|
||||
* - `xod/awesome` will be loaded from extraLibDir[1]
|
||||
*
|
||||
* Returns a Promise of complete `Project` (see `xod-project`).
|
||||
*/
|
||||
export const loadProject = (projectPath, extraLibDirs = []) =>
|
||||
findClosestWorkspaceDir(projectPath)
|
||||
.then(workspace => [path.relative(workspace, projectPath), workspace])
|
||||
.then(R.apply(loadProjectWithLibs))
|
||||
.then(R.apply(loadProjectWithLibs(extraLibDirs)))
|
||||
.then(({ project, libs }) => pack(project, libs))
|
||||
.then(XP.injectProjectTypeHints)
|
||||
.then(XP.resolveNodeTypesInProject);
|
||||
|
||||
@@ -2,10 +2,10 @@ import R from 'ramda';
|
||||
import path from 'path';
|
||||
|
||||
import * as XP from 'xod-project';
|
||||
import { uniqLists } from 'xod-func-tools';
|
||||
|
||||
import { readDir, readJSON } from './read';
|
||||
import {
|
||||
resolvePath,
|
||||
getPatchName,
|
||||
hasExt,
|
||||
rejectOnInvalidPatchFileContents,
|
||||
@@ -17,18 +17,19 @@ import {
|
||||
addMissingOptionsToPatchFileContents,
|
||||
} from './convertTypes';
|
||||
|
||||
const scanLibsFolder = (libs, libsDir) => Promise.all(
|
||||
libs.map(
|
||||
lib => readDir(path.resolve(libsDir, lib))
|
||||
.then(R.filter(hasExt('.xodp')))
|
||||
.catch((err) => {
|
||||
throw Object.assign(err, {
|
||||
path: path.resolve(libsDir, lib),
|
||||
libName: lib,
|
||||
});
|
||||
})
|
||||
))
|
||||
.then(R.zipObj(libs));
|
||||
const scanLibsFolder = (libs, libsDir) =>
|
||||
Promise.all(
|
||||
libs.map(
|
||||
lib => readDir(path.resolve(libsDir, lib))
|
||||
.then(R.filter(hasExt('.xodp')))
|
||||
.catch((err) => {
|
||||
throw Object.assign(err, {
|
||||
path: path.resolve(libsDir, lib),
|
||||
libName: lib,
|
||||
});
|
||||
})
|
||||
))
|
||||
.then(R.zipObj(libs));
|
||||
|
||||
const readLibFiles = (libfiles) => {
|
||||
const libNames = Object.keys(libfiles);
|
||||
@@ -55,12 +56,10 @@ const readLibFiles = (libfiles) => {
|
||||
return Promise.all(libPromises);
|
||||
};
|
||||
|
||||
export const loadLibs = (libs, workspace) => {
|
||||
const libsDir = path.resolve(resolvePath(workspace), 'lib');
|
||||
return scanLibsFolder(libs, libsDir)
|
||||
export const loadLibrary = (libs, libsDir) =>
|
||||
scanLibsFolder(libs, libsDir)
|
||||
.then(readLibFiles)
|
||||
.then(R.indexBy(XP.getPatchPath));
|
||||
};
|
||||
|
||||
// extract libNames from paths to xod-files
|
||||
// for example: '/Users/vasya/xod/lib/xod/core/and/patch.xodm' -> 'xod/core'
|
||||
@@ -77,16 +76,15 @@ const extractLibNamesFromFilenames = libsDir => R.compose(
|
||||
)
|
||||
);
|
||||
|
||||
export const loadAllLibs = (workspace) => {
|
||||
const libsDir = path.resolve(resolvePath(workspace), 'lib');
|
||||
// :: Path -> Promise [LibName]
|
||||
const scanForLibNames = (libDir) => {
|
||||
const folder = '.';
|
||||
|
||||
return scanLibsFolder([folder], libsDir)
|
||||
.then(R.compose(
|
||||
extractLibNamesFromFilenames(libsDir),
|
||||
R.prop(folder)
|
||||
))
|
||||
.then(libs => loadLibs(libs, workspace))
|
||||
return R.composeP(
|
||||
extractLibNamesFromFilenames(libDir),
|
||||
R.prop(folder),
|
||||
scanLibsFolder
|
||||
)([folder], libDir)
|
||||
.catch((err) => {
|
||||
// Catch error ENOENT in case if libsDir is not found.
|
||||
// E.G. User deleted it before select project.
|
||||
@@ -95,3 +93,20 @@ export const loadAllLibs = (workspace) => {
|
||||
return Promise.reject(err);
|
||||
});
|
||||
};
|
||||
|
||||
// :: [Path] -> Map PatchPath Patch
|
||||
export const loadLibs = libDirs => R.composeP(
|
||||
R.mergeAll,
|
||||
R.unnest,
|
||||
libPairs => Promise.all(
|
||||
R.map(
|
||||
([libDir, libs]) => loadLibrary(libs, libDir),
|
||||
libPairs
|
||||
)
|
||||
),
|
||||
R.toPairs,
|
||||
R.reject(R.isEmpty),
|
||||
R.zipObj(libDirs),
|
||||
uniqLists,
|
||||
libDir => Promise.all(R.map(scanForLibNames, libDir))
|
||||
)(libDirs);
|
||||
|
||||
@@ -4,7 +4,7 @@ import copy from 'recursive-copy';
|
||||
import { rejectWithCode } from 'xod-func-tools';
|
||||
|
||||
import { writeFile } from './write';
|
||||
import { resolvePath, resolveLibPath, resolveDefaultProjectPath } from './utils';
|
||||
import { resolvePath, resolveDefaultProjectPath } from './utils';
|
||||
import { WORKSPACE_FILENAME } from './constants';
|
||||
import * as ERROR_CODES from './errorCodes';
|
||||
|
||||
@@ -20,13 +20,6 @@ export const spawnWorkspaceFile = workspacePath =>
|
||||
.then(() => workspacePath)
|
||||
.catch(rejectWithCode(ERROR_CODES.CANT_CREATE_WORKSPACE_FILE));
|
||||
|
||||
// :: Path -> Promise Path Error
|
||||
export const spawnStdLib = curry((stdLibPath, workspacePath) =>
|
||||
copy(stdLibPath, resolveLibPath(workspacePath), copyOptions)
|
||||
.then(() => workspacePath)
|
||||
.catch(rejectWithCode(ERROR_CODES.CANT_COPY_STDLIB))
|
||||
);
|
||||
|
||||
// :: Path -> Promise Path Error
|
||||
export const spawnDefaultProject = curry((defaultProjectPath, workspacePath) =>
|
||||
copy(defaultProjectPath, resolveDefaultProjectPath(workspacePath), copyOptions)
|
||||
|
||||
@@ -16,7 +16,7 @@ import { PatchFileContents, Path, def } from './types';
|
||||
import {
|
||||
DEFAULT_WORKSPACE_PATH,
|
||||
DEFAULT_PROJECT_NAME,
|
||||
LIBS_FOLDERNAME,
|
||||
LIBS_DIRNAME,
|
||||
WORKSPACE_FILENAME,
|
||||
IMPL_FILENAMES,
|
||||
} from './constants';
|
||||
@@ -231,7 +231,7 @@ export const resolveWorkspacePath = R.compose(
|
||||
export const resolveLibPath = def(
|
||||
'resolveLibPath :: Path -> Path',
|
||||
workspacePath => path.resolve(
|
||||
workspacePath, LIBS_FOLDERNAME
|
||||
workspacePath, LIBS_DIRNAME
|
||||
)
|
||||
);
|
||||
export const resolveDefaultProjectPath = def(
|
||||
@@ -265,18 +265,11 @@ const isWorkspaceDirEmptyOrNotExist = def(
|
||||
R.T
|
||||
)
|
||||
);
|
||||
const doesWorkspaceHaveStdLib = def(
|
||||
'doesWorkspaceHaveStdLib :: Path -> Boolean',
|
||||
R.compose(
|
||||
doesDirectoryExist,
|
||||
resolveLibPath
|
||||
)
|
||||
);
|
||||
|
||||
// :: Path -> Promise Path Error
|
||||
export const isWorkspaceValid = R.cond([
|
||||
[
|
||||
R.both(doesWorkspaceFileExist, doesWorkspaceHaveStdLib),
|
||||
doesWorkspaceFileExist,
|
||||
Promise.resolve.bind(Promise),
|
||||
],
|
||||
[
|
||||
|
||||
2
packages/xod-fs/test/fixtures/notEmpty/awesome-project/empty-local-patch/patch.xodp
vendored
Normal file
2
packages/xod-fs/test/fixtures/notEmpty/awesome-project/empty-local-patch/patch.xodp
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
{
|
||||
}
|
||||
52
packages/xod-fs/test/fixtures/notEmpty/awesome-project/main/patch.xodp
vendored
Normal file
52
packages/xod-fs/test/fixtures/notEmpty/awesome-project/main/patch.xodp
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"links": [
|
||||
{
|
||||
"id": "1d78b081-ebfe-4a2d-b857-08a3b9ddd90f",
|
||||
"input": {
|
||||
"nodeId": "2c03e470-fefd-4f58-be6a-58d209d9158c",
|
||||
"pinKey": "value"
|
||||
},
|
||||
"output": {
|
||||
"nodeId": "4d716df5-81e7-4845-9b82-31a54c1634d7",
|
||||
"pinKey": "38395ee5-8634-40f9-b7fa-dfe9eee208e8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "3ed564cd-71e5-4ad3-9361-e7b9719bc82a",
|
||||
"input": {
|
||||
"nodeId": "2c03e470-fefd-4f58-be6a-58d209d9158c",
|
||||
"pinKey": "value"
|
||||
},
|
||||
"output": {
|
||||
"nodeId": "89c69d91-b484-478d-8990-027e5f0a2e3e",
|
||||
"pinKey": "b"
|
||||
}
|
||||
}
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"id": "2c03e470-fefd-4f58-be6a-58d209d9158c",
|
||||
"position": {
|
||||
"x": 292,
|
||||
"y": 182
|
||||
},
|
||||
"type": "xod/core/pot"
|
||||
},
|
||||
{
|
||||
"id": "4d716df5-81e7-4845-9b82-31a54c1634d7",
|
||||
"position": {
|
||||
"x": 363,
|
||||
"y": 322
|
||||
},
|
||||
"type": "@/qux"
|
||||
},
|
||||
{
|
||||
"id": "89c69d91-b484-478d-8990-027e5f0a2e3e",
|
||||
"position": {
|
||||
"x": 163,
|
||||
"y": 314
|
||||
},
|
||||
"type": "xod/core/and"
|
||||
}
|
||||
]
|
||||
}
|
||||
7
packages/xod-fs/test/fixtures/notEmpty/awesome-project/project.xod
vendored
Normal file
7
packages/xod-fs/test/fixtures/notEmpty/awesome-project/project.xod
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"authors": [
|
||||
"Amperka team"
|
||||
],
|
||||
"name": "awesome-project",
|
||||
"version": "0.0.42"
|
||||
}
|
||||
7
packages/xod-fs/test/fixtures/notEmpty/awesome-project/qux/README.md
vendored
Normal file
7
packages/xod-fs/test/fixtures/notEmpty/awesome-project/qux/README.md
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
# Hello world!
|
||||
|
||||
- It's a test file
|
||||
- If you see a correct content — it passed!
|
||||
- Also it could contain any UTF-8 characters, like this one 😎 or even кириллица!
|
||||
|
||||
Have a nice day!
|
||||
1
packages/xod-fs/test/fixtures/notEmpty/awesome-project/qux/any.cpp
vendored
Normal file
1
packages/xod-fs/test/fixtures/notEmpty/awesome-project/qux/any.cpp
vendored
Normal file
@@ -0,0 +1 @@
|
||||
// Impl loaded fine!
|
||||
BIN
packages/xod-fs/test/fixtures/notEmpty/awesome-project/qux/img/20x20.png
vendored
Normal file
BIN
packages/xod-fs/test/fixtures/notEmpty/awesome-project/qux/img/20x20.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 165 B |
33
packages/xod-fs/test/fixtures/notEmpty/awesome-project/qux/patch.xodp
vendored
Normal file
33
packages/xod-fs/test/fixtures/notEmpty/awesome-project/qux/patch.xodp
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"links": [
|
||||
{
|
||||
"id": "30dafe79-37e6-4fc0-a8b8-fa5e912ffef1",
|
||||
"input": {
|
||||
"nodeId": "38395ee5-8634-40f9-b7fa-dfe9eee208e8",
|
||||
"pinKey": "PIN"
|
||||
},
|
||||
"output": {
|
||||
"nodeId": "4da4c29d-f482-47c2-825e-a969084b1116",
|
||||
"pinKey": "brightness"
|
||||
}
|
||||
}
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"id": "38395ee5-8634-40f9-b7fa-dfe9eee208e8",
|
||||
"position": {
|
||||
"x": 276,
|
||||
"y": 155
|
||||
},
|
||||
"type": "xod/patch-nodes/input-number"
|
||||
},
|
||||
{
|
||||
"id": "4da4c29d-f482-47c2-825e-a969084b1116",
|
||||
"position": {
|
||||
"x": 310,
|
||||
"y": 307
|
||||
},
|
||||
"type": "xod/core/led"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -70,7 +70,7 @@ describe('Loader', () => {
|
||||
|
||||
describe('loadProjectWithLibs', () => {
|
||||
it('should return project with libs', () =>
|
||||
Loader.loadProjectWithLibs(projectPath, workspace)
|
||||
Loader.loadProjectWithLibs([], projectPath, workspace)
|
||||
.then(({ project, libs }) => {
|
||||
const quxPatch = R.find(R.pathEq(['content', 'path'], '@/qux'), project);
|
||||
assert.isDefined(quxPatch);
|
||||
@@ -90,7 +90,7 @@ describe('Loader', () => {
|
||||
const okProject = path.resolve(brokenWorkspace, './ok-project');
|
||||
|
||||
return expectRejectedWithCode(
|
||||
Loader.loadProjectWithLibs(okProject, brokenWorkspace),
|
||||
Loader.loadProjectWithLibs([], okProject, brokenWorkspace),
|
||||
ERROR_CODES.INVALID_FILE_CONTENTS
|
||||
);
|
||||
}
|
||||
@@ -100,7 +100,7 @@ describe('Loader', () => {
|
||||
|
||||
describe('loadProjectWithoutLibs', () => {
|
||||
it('should return project without libs', () => {
|
||||
const xodCore = path.resolve(workspace, './lib/xod/core');
|
||||
const xodCore = path.resolve(workspace, './__lib__/xod/core');
|
||||
const xodCoreOwner = path.resolve(xodCore, '..');
|
||||
|
||||
return Loader.loadProjectWithoutLibs(xodCore)
|
||||
|
||||
@@ -2,26 +2,28 @@ import R from 'ramda';
|
||||
import { assert } from 'chai';
|
||||
|
||||
import path from 'path';
|
||||
import { loadLibs, loadAllLibs } from '../src/loadLibs';
|
||||
import { loadLibrary, loadLibs } from '../src/loadLibs';
|
||||
import { resolveLibPath } from '../src/utils';
|
||||
|
||||
const workspaceDir = './fixtures/workspace';
|
||||
|
||||
describe('Library loader', () => {
|
||||
const workspace = path.resolve(__dirname, workspaceDir);
|
||||
const libDir = resolveLibPath(workspace);
|
||||
|
||||
it('should load xod/core libs from ./fixtures/workspace/lib', () =>
|
||||
loadLibs(['xod/core'], workspace).then((libs) => {
|
||||
it('should load xod/core libs from ./fixtures/workspace/lib', () => {
|
||||
loadLibrary(['xod/core'], libDir).then((libs) => {
|
||||
assert.sameMembers(R.keys(libs), [
|
||||
'xod/core/and',
|
||||
'xod/core/led',
|
||||
'xod/core/pot',
|
||||
'xod/core/test',
|
||||
]);
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('should load all libs from ./fixtures/workspace/lib', () =>
|
||||
loadAllLibs(workspace).then((libs) => {
|
||||
loadLibs([libDir]).then((libs) => {
|
||||
assert.sameMembers(R.keys(libs), [
|
||||
'user/utils/test',
|
||||
'user/with-omitted-optionals/empty-lib-patch',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { assert } from 'chai';
|
||||
import fs from 'fs-extra';
|
||||
|
||||
import { spawnWorkspaceFile, spawnStdLib, spawnDefaultProject } from '../src/spawn';
|
||||
import { spawnWorkspaceFile, spawnDefaultProject } from '../src/spawn';
|
||||
import { doesFileExist, doesDirectoryExist } from '../src/utils';
|
||||
import * as ERROR_CODES from '../src/errorCodes';
|
||||
|
||||
@@ -17,23 +17,6 @@ describe('Spawn', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('spawnStdLib resolves to workspace path on success', () => {
|
||||
after(() => fs.remove(fixture('./new-workspace')));
|
||||
return spawnStdLib(fixture('./workspace/lib'), fixture('./new-workspace'))
|
||||
.then(() => {
|
||||
assert.ok(doesDirectoryExist(fixture('./new-workspace/lib')));
|
||||
fs.readdir(fixture('./new-workspace/lib'), (err, files) => {
|
||||
assert.includeMembers(files, ['xod', 'user']);
|
||||
});
|
||||
});
|
||||
});
|
||||
it('spawnStdLib rejects CANT_COPY_STDLIB on failure', () =>
|
||||
expectRejectedWithCode(
|
||||
spawnStdLib(fixture('./no-dir/no-lib'), fixture('./new-workspace')),
|
||||
ERROR_CODES.CANT_COPY_STDLIB
|
||||
)
|
||||
);
|
||||
|
||||
it('spawnDefaultProject resolves to workspace path on success', () => {
|
||||
after(() => fs.remove(fixture('./new-workspace')));
|
||||
return spawnDefaultProject(fixture('./workspace/awesome-project'), fixture('./new-workspace'))
|
||||
|
||||
@@ -53,7 +53,7 @@ describe('Utils', () => {
|
||||
);
|
||||
it('isWorkspaceValid: if specified directory is not empty, rejects with error code WORKSPACE_DIR_NOT_EMPTY',
|
||||
() => expectRejectedWithCode(
|
||||
U.isWorkspaceValid(fixture('./emptyWorkspace')),
|
||||
U.isWorkspaceValid(fixture('./notEmpty')),
|
||||
ERROR_CODES.WORKSPACE_DIR_NOT_EMPTY
|
||||
)
|
||||
);
|
||||
|
||||
@@ -11,7 +11,7 @@ describe('welcome-to-xod', () => {
|
||||
const projectPath = 'welcome-to-xod';
|
||||
|
||||
it('should load as a valid project', () =>
|
||||
loadProjectWithLibs(projectPath, workspace)
|
||||
loadProjectWithLibs([], projectPath, workspace)
|
||||
.then(({ project, libs }) => {
|
||||
const packed = pack(project, libs);
|
||||
assert.isAtLeast(
|
||||
|
||||
@@ -332,6 +332,26 @@ export const invertMap = def(
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns the list of list of strings.
|
||||
* Removes all duplicates from the subsequent list
|
||||
* on the basis of already filtered values lists.
|
||||
*
|
||||
* E.G.
|
||||
* [['a', 'b', 'c'], ['b','c','d'], ['a','d','e']]
|
||||
* will become
|
||||
* [['a', 'b', 'c'], ['d'], ['e']]
|
||||
*/
|
||||
export const uniqLists = def(
|
||||
'uniqLists :: [[String]] -> [[String]]',
|
||||
R.reduce(
|
||||
(acc, nextList) => R.append(
|
||||
R.without(R.unnest(acc), nextList),
|
||||
acc
|
||||
),
|
||||
[]
|
||||
)
|
||||
);
|
||||
|
||||
export default Object.assign(
|
||||
{
|
||||
@@ -358,6 +378,7 @@ export default Object.assign(
|
||||
mapIndexed,
|
||||
swap,
|
||||
reverseLookup,
|
||||
uniqLists,
|
||||
},
|
||||
types
|
||||
);
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
reduceMaybe,
|
||||
reduceEither,
|
||||
omitRecursively,
|
||||
uniqLists,
|
||||
} from '../src/index';
|
||||
|
||||
describe('explode', () => {
|
||||
@@ -142,3 +143,24 @@ describe('omitRecursively', () => {
|
||||
assert.deepEqual(cleanObj, [[{ foo: 2 }]]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('uniqLists', () => {
|
||||
it('returns filtered list', () => {
|
||||
assert.deepEqual(
|
||||
uniqLists([['a', 'b', 'c'], ['b', 'c', 'd'], ['a', 'd', 'e', 'f']]),
|
||||
[['a', 'b', 'c'], ['d'], ['e', 'f']]
|
||||
);
|
||||
});
|
||||
it('returns filtered list with untouched empty lists', () => {
|
||||
assert.deepEqual(
|
||||
uniqLists([['a', 'b', 'c'], ['b', 'c', 'd'], [], []]),
|
||||
[['a', 'b', 'c'], ['d'], [], []]
|
||||
);
|
||||
});
|
||||
it('returns empty list for empty list', () => {
|
||||
assert.deepEqual(
|
||||
uniqLists([]),
|
||||
[]
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user