├── specs ├── index.spec.js ├── specs-runner │ ├── fixtures │ │ └── invalid-tester.js │ └── worker.spec.js ├── .eslintrc ├── jsdoc │ └── fixtures │ │ ├── misc.js │ │ ├── variableDeclaration.js │ │ ├── flowTypeFile.js │ │ ├── functionDeclaration.js │ │ ├── properties.js │ │ ├── variousParamTypes.js │ │ ├── descriptionTag.js │ │ └── methodDeclaration.js ├── consumer │ └── component │ │ └── source │ │ ├── impl.spec.js │ │ └── specs.spec.js ├── environment │ └── environment.spec.js ├── search │ ├── indexer.spec.js │ ├── query-builder.spec.js │ └── serverless-index.spec.js ├── api │ └── consumer │ │ └── lib │ │ ├── export.spec.js │ │ ├── list-scope.spec.js │ │ └── get-scope-bit.spec.js └── version │ └── version-parser.spec.js ├── resources ├── specs.template.js ├── repos │ ├── bitsrc.repo │ └── deb-repo.sh ├── impl.template.js ├── bit.template.json └── win-chocolatey │ └── tools │ └── chocolateyinstall.ps1.in ├── src ├── scope │ ├── models │ │ ├── external-component.js │ │ ├── index.js │ │ ├── source.js │ │ ├── scopeMeta.js │ │ └── symlink.js │ ├── pack.js │ ├── network │ │ ├── ssh │ │ │ ├── index.js │ │ │ └── key-getter.js │ │ ├── fs │ │ │ └── index.js │ │ ├── exceptions │ │ │ ├── permission-denied.js │ │ │ ├── fs-scope-not-loaded.js │ │ │ ├── ssh-connection-error.js │ │ │ ├── protocol-not-supported.js │ │ │ ├── unexpected-network-error.js │ │ │ ├── network-error.js │ │ │ ├── remote-scope-not-found.js │ │ │ └── index.js │ │ ├── index.js │ │ ├── network-lib.js │ │ ├── network.js │ │ └── check-version-compatibility.js │ ├── ci-ops │ │ ├── index.js │ │ ├── ci-worker.js │ │ ├── ci-ops.js │ │ └── run-and-update-ci.js │ ├── exceptions │ │ ├── hash-not-found.js │ │ ├── bit-not-in-scope.js │ │ ├── scope-not-found.js │ │ ├── source-not-found.js │ │ ├── resolution-exception.js │ │ ├── scope-already-exists.js │ │ ├── version-not-found.js │ │ ├── merge-conflict.js │ │ ├── component-not-found.js │ │ ├── dependency-not-found.js │ │ └── index.js │ ├── dependencies │ │ ├── index.js │ │ ├── exceptions │ │ │ ├── invalid-dependency.js │ │ │ └── index.js │ │ ├── name-parser.js │ │ └── dependency.js │ ├── repositories │ │ ├── hooks.js │ │ ├── index.js │ │ ├── cache.js │ │ ├── sources-map.js │ │ └── external.js │ ├── dependency-maps │ │ ├── index.js │ │ ├── external-map.js │ │ └── sources-map.js │ ├── objects │ │ ├── index.js │ │ └── ref.js │ ├── index.js │ ├── object-registrar.js │ ├── component-dependencies.js │ ├── repository.js │ ├── scope-loader.js │ ├── flatten-dependencies.js │ └── component-objects.js ├── cli │ ├── loader │ │ ├── index.js │ │ ├── loader-messages.js │ │ └── loader.js │ ├── index.js │ ├── cli-utils.js │ ├── templates │ │ ├── bare-list-template.js │ │ ├── list-template.js │ │ └── component-template.js │ ├── commands │ │ ├── public-cmds │ │ │ ├── logout-cmd.js │ │ │ ├── login-cmd.js │ │ │ ├── watch-cmd.js │ │ │ ├── clear-cache-cmd.js │ │ │ ├── remove-cmd.js │ │ │ ├── reset-cmd.js │ │ │ ├── log-cmd.js │ │ │ ├── create-cmd.js │ │ │ ├── bind-cmd.js │ │ │ ├── export-cmd.js │ │ │ ├── test-cmd.js │ │ │ └── install-cmd.js │ │ └── private-cmds │ │ │ ├── cat-object-cmd.js │ │ │ ├── refresh-scope-cmd.js │ │ │ ├── _scope-cmd.js │ │ │ ├── _show-cmd.js │ │ │ ├── _list-cmd.js │ │ │ ├── _search-cmd.js │ │ │ ├── ci-update-cmd.js │ │ │ ├── cat-scope-cmd.js │ │ │ ├── _fetch-cmd.js │ │ │ └── _put-cmd.js │ └── command.js ├── version │ ├── index.js │ ├── exceptions │ │ ├── invalid-version.js │ │ ├── invalid-version-change.js │ │ └── index.js │ ├── version-parser.js │ └── version.js ├── bit-id │ ├── exceptions │ │ ├── invalid-bit-id.js │ │ ├── index.js │ │ └── invalid-id-chunk.js │ ├── index.js │ └── bit-ids.js ├── consumer │ ├── bit-map │ │ ├── index.js │ │ └── exceptions │ │ │ ├── invalid-bit-map.js │ │ │ ├── missing-bit-map-component.js │ │ │ ├── index.js │ │ │ └── missing-main-file.js │ ├── component │ │ ├── exceptions │ │ │ ├── invalid-bit.js │ │ │ ├── file-source-not-found.js │ │ │ ├── plugin-not-found.js │ │ │ ├── bit-already-exist-externaly.js │ │ │ └── component-not-found-in-path.js │ │ ├── index.js │ │ ├── environment │ │ │ ├── validate-plugin.js │ │ │ ├── index.js │ │ │ └── load-plugin.js │ │ ├── templates │ │ │ ├── impl.default-template.js │ │ │ └── specs.default-template.js │ │ └── sources │ │ │ ├── index.js │ │ │ ├── source.js │ │ │ ├── specs.js │ │ │ ├── impl.js │ │ │ ├── license.js │ │ │ ├── dist.js │ │ │ ├── source-file.js │ │ │ └── abstract-vinyl.js │ ├── exceptions │ │ ├── consumer-not-found.js │ │ ├── nothing-to-import.js │ │ ├── consumer-already-exists.js │ │ ├── component-specs-failed.js │ │ ├── missing-dependencies.js │ │ ├── driver-not-found.js │ │ └── index.js │ ├── bit-json │ │ ├── index.js │ │ └── exceptions │ │ │ ├── bit-json-not-found.js │ │ │ ├── bit-json-already-exists.js │ │ │ ├── invalid-bit-json.js │ │ │ └── index.js │ ├── specs-results │ │ └── index.js │ ├── index.js │ └── consumer-loader.js ├── remotes │ ├── exceptions │ │ ├── invalid-remote.js │ │ ├── primary-not-found.js │ │ ├── primary-overloaded.js │ │ ├── remote-not-found.js │ │ └── index.js │ ├── index.js │ ├── local-scope.js │ └── remote-resolver │ │ └── remote-resolver.js ├── npm-client │ └── index.js ├── environment │ └── index.js ├── specs-runner │ └── index.js ├── component-resolver │ ├── index.js │ └── component-resolver.js ├── utils │ ├── prepend-bang.js │ ├── array-remote-first.js │ ├── pack-command.js │ ├── fs-exists-sync.js │ ├── immutable-unshift.js │ ├── is-dir-empty-sync.js │ ├── fs-stats.js │ ├── is-bit-url.js │ ├── resolve-boolean.js │ ├── unpack-command.js │ ├── build-command-message.js │ ├── os-resolve-group-id.js │ ├── fs │ │ ├── dir-path-parser.js │ │ ├── is-dir-empty.js │ │ ├── current-dir-name.js │ │ ├── list-directories.js │ │ └── file-info.js │ ├── buffer │ │ └── to-read-stream.js │ ├── number │ │ ├── is-numeric.js │ │ └── is-number.js │ ├── is-valid-id-chunk.js │ ├── is-valid-scope-name.js │ ├── fs-read-file.js │ ├── fs-output-file.js │ ├── is-dir.js │ ├── os-resolve-home-path.js │ ├── zlib-deflate.js │ ├── zlib-inflate.js │ ├── fs-chown.js │ ├── promise-all-settled.js │ ├── filter-object.js │ ├── glob.js │ ├── promisify.js │ ├── mkdirp.js │ ├── object-to-stringified-tuple-array.js │ ├── string │ │ ├── is-string.js │ │ ├── from-base64.js │ │ ├── clean-bang.js │ │ ├── clean-char.js │ │ ├── contains.js │ │ └── to-base64.js │ ├── array │ │ ├── flatten.js │ │ ├── first.js │ │ ├── flat-map.js │ │ ├── diff.js │ │ └── split-by.js │ ├── promise-to-result-object.js │ ├── fs-rmdir.js │ ├── encryption │ │ └── sha1.js │ ├── object │ │ ├── values.js │ │ ├── has-own-property.js │ │ ├── foreach.js │ │ ├── to-tuple-array.js │ │ ├── empty.js │ │ └── filter.js │ ├── remove-from-require-cache.js │ ├── map │ │ └── to-object.js │ ├── object-clean.js │ ├── remove-containing-dir-if-empty.js │ ├── map-object.js │ ├── fs-write-file.js │ ├── fs-remove-file.js │ └── resolveLatestVersion.js ├── driver │ ├── index.js │ └── exceptions │ │ └── driver-not-found.js ├── jsdoc │ ├── index.js │ └── formater.js ├── global-config │ ├── index.js │ ├── config.js │ └── global-remotes.js ├── hooks │ ├── index.js │ ├── hooks.js │ ├── http-client │ │ └── client.js │ └── create-hook.js ├── api │ ├── scope │ │ ├── lib │ │ │ ├── scope-list.js │ │ │ ├── describe-scope.js │ │ │ ├── scope-init.js │ │ │ ├── refresh-scope.js │ │ │ ├── cat-object.js │ │ │ ├── cat-scope.js │ │ │ ├── fetch.js │ │ │ ├── scope-show.js │ │ │ ├── ci-update-action.js │ │ │ ├── put.js │ │ │ ├── modify-ci-props.js │ │ │ ├── resolver.js │ │ │ ├── scope-config.js │ │ │ └── build-in-scope.js │ │ └── index.js │ └── consumer │ │ ├── lib │ │ ├── get-driver.js │ │ ├── exceptions │ │ │ ├── path-not-exists.js │ │ │ └── invalid-id-on-commit.js │ │ ├── init.js │ │ ├── remove.js │ │ ├── get-consumer-component.js │ │ ├── get-component-logs.js │ │ ├── reset.js │ │ ├── status.js │ │ ├── test.js │ │ ├── create.js │ │ ├── list-scope.js │ │ ├── commit.js │ │ └── global-config.js │ │ └── index.js ├── search │ ├── index.js │ ├── stopwords.js │ ├── serverless-index.js │ └── query-builder.js ├── app.js └── api.js ├── docs ├── bitsource.md ├── extending-bit.md ├── bit-internals.md ├── scope.md ├── old │ └── advanced.md ├── INTRODUCTION.md └── why-code-component-manager.md ├── tests ├── windows.ps1 ├── e2e.sh └── bit.features ├── scripts ├── windows │ ├── bit.cmd │ ├── bit.ico │ ├── bit-banner.bmp │ └── bit-dialog.bmp ├── build-windows-installer.bat ├── linux │ ├── postRemove.sh │ └── postInstall.sh ├── generate-formula.sh ├── set-installation-method.js ├── bootstrap-env-ubuntu.sh ├── node-installer.sh ├── bit.rb ├── build-dist.ps1 ├── build-tar.sh ├── node-installer.ps1 ├── build-brew.sh ├── deploy-windows.ps1 └── establish-dev-link.js ├── bin └── bit ├── e2e ├── fixtures │ └── png_fixture.png ├── .eslintrc └── commands │ ├── create.e2e.js │ └── list.e2e.js ├── .eslintignore ├── tasks ├── clean.js ├── lint.js ├── build.js └── build-legacy.js ├── .editorconfig ├── bit.json ├── gulpfile.js ├── .babelrc ├── circle.yml ├── LICENSE ├── book.js ├── .eslintrc ├── .npmignore └── .gitignore /specs/index.spec.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/specs.template.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/scope/models/external-component.js: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/bitsource.md: -------------------------------------------------------------------------------- 1 | 2 | # BitSource 3 | 4 | // TODO -------------------------------------------------------------------------------- /tests/windows.ps1: -------------------------------------------------------------------------------- 1 | bit 2 | bit init 3 | bit create test 4 | -------------------------------------------------------------------------------- /scripts/windows/bit.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | "%~dp0\node.exe" "%~dp0\bit.js" %* 3 | -------------------------------------------------------------------------------- /bin/bit: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | /usr/local/bin/bitNode /usr/local/bin/bit.js "$@" 4 | -------------------------------------------------------------------------------- /src/scope/pack.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | 3 | export default class Pack { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /src/scope/network/ssh/index.js: -------------------------------------------------------------------------------- 1 | import SSH from './ssh'; 2 | 3 | export default SSH; 4 | -------------------------------------------------------------------------------- /src/cli/loader/index.js: -------------------------------------------------------------------------------- 1 | import loader from './loader'; 2 | 3 | export default loader; 4 | -------------------------------------------------------------------------------- /src/scope/ci-ops/index.js: -------------------------------------------------------------------------------- 1 | import ciOps from './ci-ops'; 2 | 3 | export default ciOps; 4 | -------------------------------------------------------------------------------- /src/scope/exceptions/hash-not-found.js: -------------------------------------------------------------------------------- 1 | export default class HashNotFound extends Error {} 2 | -------------------------------------------------------------------------------- /src/version/index.js: -------------------------------------------------------------------------------- 1 | import Version from './version'; 2 | 3 | export default Version; 4 | -------------------------------------------------------------------------------- /scripts/windows/bit.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstacruz/bit/master/scripts/windows/bit.ico -------------------------------------------------------------------------------- /src/bit-id/exceptions/invalid-bit-id.js: -------------------------------------------------------------------------------- 1 | export default class InvalidBitId extends Error {} 2 | -------------------------------------------------------------------------------- /src/consumer/bit-map/index.js: -------------------------------------------------------------------------------- 1 | import BitMap from './bit-map'; 2 | 3 | export default BitMap; 4 | -------------------------------------------------------------------------------- /src/remotes/exceptions/invalid-remote.js: -------------------------------------------------------------------------------- 1 | export default class InvalidRemote extends Error {} 2 | -------------------------------------------------------------------------------- /src/scope/exceptions/bit-not-in-scope.js: -------------------------------------------------------------------------------- 1 | export default class BitNotInScope extends Error {} 2 | -------------------------------------------------------------------------------- /src/scope/exceptions/scope-not-found.js: -------------------------------------------------------------------------------- 1 | export default class ScopeNotFound extends Error {} 2 | -------------------------------------------------------------------------------- /src/scope/exceptions/source-not-found.js: -------------------------------------------------------------------------------- 1 | export default class SourceNotFound extends Error {} 2 | -------------------------------------------------------------------------------- /src/version/exceptions/invalid-version.js: -------------------------------------------------------------------------------- 1 | export default class InvalidVersion extends Error {} 2 | -------------------------------------------------------------------------------- /src/consumer/component/exceptions/invalid-bit.js: -------------------------------------------------------------------------------- 1 | export default class InvalidBit extends Error {} 2 | -------------------------------------------------------------------------------- /src/npm-client/index.js: -------------------------------------------------------------------------------- 1 | import npmClient from './npm-client'; 2 | 3 | export default npmClient; 4 | -------------------------------------------------------------------------------- /src/remotes/exceptions/primary-not-found.js: -------------------------------------------------------------------------------- 1 | export default class PrimaryNotFound extends Error {} 2 | -------------------------------------------------------------------------------- /src/scope/network/fs/index.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import Fs from './fs'; 3 | 4 | export default Fs; 5 | -------------------------------------------------------------------------------- /e2e/fixtures/png_fixture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstacruz/bit/master/e2e/fixtures/png_fixture.png -------------------------------------------------------------------------------- /src/environment/index.js: -------------------------------------------------------------------------------- 1 | import Environment from './environment'; 2 | 3 | export default Environment; 4 | -------------------------------------------------------------------------------- /src/remotes/exceptions/primary-overloaded.js: -------------------------------------------------------------------------------- 1 | export default class PrimaryOverloaded extends Error {} 2 | -------------------------------------------------------------------------------- /src/scope/exceptions/resolution-exception.js: -------------------------------------------------------------------------------- 1 | export default class ResolutionException extends Error {} 2 | -------------------------------------------------------------------------------- /src/scope/exceptions/scope-already-exists.js: -------------------------------------------------------------------------------- 1 | export default class ScopeAlreadyExists extends Error {} 2 | -------------------------------------------------------------------------------- /src/scope/network/exceptions/permission-denied.js: -------------------------------------------------------------------------------- 1 | export default class PermissionDenied extends Error {} 2 | -------------------------------------------------------------------------------- /src/specs-runner/index.js: -------------------------------------------------------------------------------- 1 | import specsRunner from './specs-runner'; 2 | 3 | export default specsRunner; 4 | -------------------------------------------------------------------------------- /scripts/windows/bit-banner.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstacruz/bit/master/scripts/windows/bit-banner.bmp -------------------------------------------------------------------------------- /scripts/windows/bit-dialog.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstacruz/bit/master/scripts/windows/bit-dialog.bmp -------------------------------------------------------------------------------- /src/consumer/exceptions/consumer-not-found.js: -------------------------------------------------------------------------------- 1 | export default class ConsumerNotFoundError extends Error {} 2 | -------------------------------------------------------------------------------- /src/scope/dependencies/index.js: -------------------------------------------------------------------------------- 1 | import BitIds from '../../bit-id/bit-ids'; 2 | 3 | export default BitIds; 4 | -------------------------------------------------------------------------------- /src/scope/network/exceptions/fs-scope-not-loaded.js: -------------------------------------------------------------------------------- 1 | export default class FsScopeNotLoaded extends Error {} 2 | -------------------------------------------------------------------------------- /src/scope/network/exceptions/ssh-connection-error.js: -------------------------------------------------------------------------------- 1 | export default class SSHConnectionError extends Error {} 2 | -------------------------------------------------------------------------------- /src/version/exceptions/invalid-version-change.js: -------------------------------------------------------------------------------- 1 | export default class InvalidVersionChange extends Error {} 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | trunk 3 | tasks 4 | dist 5 | dist-legacy 6 | gulpfile.js 7 | specs/jsdoc/fixtures 8 | -------------------------------------------------------------------------------- /src/consumer/bit-json/index.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import BitJson from './bit-json'; 3 | 4 | export default BitJson; 5 | -------------------------------------------------------------------------------- /src/scope/dependencies/exceptions/invalid-dependency.js: -------------------------------------------------------------------------------- 1 | export default class InvalidDependency extends Error {} 2 | -------------------------------------------------------------------------------- /src/scope/network/exceptions/protocol-not-supported.js: -------------------------------------------------------------------------------- 1 | export default class ProtocolNotSupported extends Error {} 2 | -------------------------------------------------------------------------------- /src/scope/repositories/hooks.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | export default class Hooks { 3 | getPath() { 4 | 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/consumer/exceptions/nothing-to-import.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | export default class NothingToImport extends Error {} 3 | -------------------------------------------------------------------------------- /src/consumer/specs-results/index.js: -------------------------------------------------------------------------------- 1 | import specsResults from './specs-results'; 2 | 3 | export default specsResults; 4 | -------------------------------------------------------------------------------- /src/scope/network/exceptions/unexpected-network-error.js: -------------------------------------------------------------------------------- 1 | export default class UnexpectedNetworkError extends Error {} 2 | -------------------------------------------------------------------------------- /src/component-resolver/index.js: -------------------------------------------------------------------------------- 1 | import componentResolver from './component-resolver'; 2 | 3 | export default componentResolver; 4 | -------------------------------------------------------------------------------- /src/consumer/bit-json/exceptions/bit-json-not-found.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | export default class BitJsonNotFound extends Error {} 3 | -------------------------------------------------------------------------------- /src/consumer/component/index.js: -------------------------------------------------------------------------------- 1 | import ConsumerComponent from './consumer-component'; 2 | 3 | export default ConsumerComponent; 4 | -------------------------------------------------------------------------------- /src/consumer/exceptions/consumer-already-exists.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | export default class ConsumerAlreadyExists extends Error {} 3 | -------------------------------------------------------------------------------- /src/consumer/exceptions/component-specs-failed.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | export default class ComponentSpecsFailed extends Error { 3 | } 4 | -------------------------------------------------------------------------------- /src/utils/prepend-bang.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | export default function prependBang(str: string): string { 3 | return `!${str}`; 4 | } 5 | -------------------------------------------------------------------------------- /src/bit-id/index.js: -------------------------------------------------------------------------------- 1 | import BitId from './bit-id'; 2 | import BitIds from './bit-ids'; 3 | 4 | export { 5 | BitId, 6 | BitIds 7 | }; 8 | -------------------------------------------------------------------------------- /src/consumer/bit-json/exceptions/bit-json-already-exists.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | export default class BitJsonAlreadyExists extends Error {} 3 | -------------------------------------------------------------------------------- /scripts/build-windows-installer.bat: -------------------------------------------------------------------------------- 1 | "%ProgramFiles(x86)%\MSBuild\14.0\Bin\MSBuild.exe" ./scripts/windows/BitSetup.wixproj /p:Configuration=Release -------------------------------------------------------------------------------- /scripts/linux/postRemove.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | rm /usr/local/bin/bit 5 | rm /usr/local/bin/bit.js 6 | rm /usr/local/bin/bitNode 7 | -------------------------------------------------------------------------------- /specs/specs-runner/fixtures/invalid-tester.js: -------------------------------------------------------------------------------- 1 | function compile(src) { //eslint-disable-line 2 | 3 | } 4 | 5 | module.exports = { 6 | compile, 7 | }; 8 | -------------------------------------------------------------------------------- /src/consumer/component/environment/validate-plugin.js: -------------------------------------------------------------------------------- 1 | export default (pluginName) => { 2 | console.log('validating plugin...'); 3 | // @TODO 4 | }; 5 | -------------------------------------------------------------------------------- /src/driver/index.js: -------------------------------------------------------------------------------- 1 | import Driver from './driver'; 2 | import DriverNotFound from './exceptions/driver-not-found'; 3 | 4 | export { Driver, DriverNotFound }; 5 | -------------------------------------------------------------------------------- /src/remotes/index.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import Remotes from './remotes'; 3 | import Remote from './remote'; 4 | 5 | export { 6 | Remote, 7 | Remotes 8 | }; 9 | -------------------------------------------------------------------------------- /src/scope/network/index.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import SSH from './ssh'; 3 | import connect from './network-lib'; 4 | 5 | export { 6 | SSH, 7 | connect 8 | }; 9 | -------------------------------------------------------------------------------- /src/scope/repositories/index.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import Source from './source'; 3 | import Tmp from './tmp'; 4 | 5 | export { 6 | Source, 7 | Tmp, 8 | }; 9 | -------------------------------------------------------------------------------- /src/utils/array-remote-first.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | 3 | export default function remoteFirst(array: any[]): any[] { 4 | array.shift(); 5 | return array; 6 | } 7 | -------------------------------------------------------------------------------- /src/jsdoc/index.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import parser from './parser'; 3 | import formatter from './formater'; 4 | 5 | export { 6 | parser, 7 | formatter 8 | }; 9 | -------------------------------------------------------------------------------- /src/scope/dependencies/exceptions/index.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import InvalidDependency from './invalid-dependency'; 3 | 4 | export { 5 | InvalidDependency 6 | }; 7 | -------------------------------------------------------------------------------- /src/consumer/index.js: -------------------------------------------------------------------------------- 1 | import Consumer from './consumer'; 2 | import loadConsumer from './consumer-loader'; 3 | 4 | export { 5 | Consumer, 6 | loadConsumer, 7 | }; 8 | -------------------------------------------------------------------------------- /src/utils/pack-command.js: -------------------------------------------------------------------------------- 1 | import toBase64 from './string/to-base64'; 2 | 3 | module.exports = function packCmd(obj) { 4 | return toBase64(JSON.stringify(obj)); 5 | }; 6 | -------------------------------------------------------------------------------- /src/utils/fs-exists-sync.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import fs from 'fs'; 3 | 4 | export default function existsSync(path: string): Boolean { 5 | return fs.existsSync(path); 6 | } 7 | -------------------------------------------------------------------------------- /src/global-config/index.js: -------------------------------------------------------------------------------- 1 | import GlobalRemotes from './global-remotes'; 2 | import GlobalConfig from './config'; 3 | 4 | export { 5 | GlobalRemotes, 6 | GlobalConfig 7 | }; 8 | -------------------------------------------------------------------------------- /src/remotes/exceptions/remote-not-found.js: -------------------------------------------------------------------------------- 1 | export default class RemoteNotFound extends Error { 2 | constructor(name) { 3 | super(); 4 | this.name = name; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/utils/immutable-unshift.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | 3 | export default function immutableUnshift(arr: Array, newEntry: any): Array { 4 | return [].concat(newEntry, arr); 5 | } 6 | -------------------------------------------------------------------------------- /tasks/clean.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'); 2 | const clean = require('gulp-clean'); 3 | 4 | module.exports = () => gulp 5 | .src([ 6 | './dist' 7 | ]) 8 | .pipe(clean()) 9 | -------------------------------------------------------------------------------- /src/scope/exceptions/version-not-found.js: -------------------------------------------------------------------------------- 1 | export default class VersionNotFound extends Error { 2 | constructor(version: string) { 3 | super(); 4 | this.version = version; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/bit-id/exceptions/index.js: -------------------------------------------------------------------------------- 1 | import InvalidBitId from './invalid-bit-id'; 2 | import InvalidIdChunk from './invalid-id-chunk'; 3 | 4 | export { 5 | InvalidBitId, 6 | InvalidIdChunk, 7 | }; 8 | -------------------------------------------------------------------------------- /src/utils/is-dir-empty-sync.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import fs from 'fs'; 3 | 4 | export default function isDirEmptySync(dirPath: string): boolean { 5 | return !fs.readdirSync(dirPath).length; 6 | } 7 | -------------------------------------------------------------------------------- /src/consumer/component/environment/index.js: -------------------------------------------------------------------------------- 1 | import loadPlugin from './load-plugin'; 2 | import validatePlugin from './validate-plugin'; 3 | 4 | export default { 5 | loadPlugin, 6 | validatePlugin 7 | }; 8 | -------------------------------------------------------------------------------- /src/hooks/index.js: -------------------------------------------------------------------------------- 1 | import createHook from './create-hook'; 2 | 3 | export const postExportHook = createHook('post_export_hook', 'post'); 4 | export const postImportHook = createHook('post_import_hook', 'post'); 5 | -------------------------------------------------------------------------------- /e2e/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-unused-expressions": [0] 4 | }, 5 | "env": { 6 | "mocha": true 7 | }, 8 | "globals": { 9 | "describe": true, 10 | "it": true 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /specs/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-unused-expressions": [0] 4 | }, 5 | "env": { 6 | "mocha": true 7 | }, 8 | "globals": { 9 | "describe": true, 10 | "it": true 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/api/scope/lib/scope-list.js: -------------------------------------------------------------------------------- 1 | import { loadScope } from '../../../scope'; 2 | 3 | export default function list(path: string): Promise { 4 | return loadScope(path) 5 | .then(scope => scope.listStage()); 6 | } 7 | -------------------------------------------------------------------------------- /src/version/exceptions/index.js: -------------------------------------------------------------------------------- 1 | import InvalidVersionChange from './invalid-version-change'; 2 | import InvalidVersion from './invalid-version'; 3 | 4 | export { 5 | InvalidVersionChange, 6 | InvalidVersion 7 | }; 8 | -------------------------------------------------------------------------------- /scripts/linux/postInstall.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ln -sf /usr/share/bit/bin/bit /usr/local/bin/bit 4 | ln -sf /usr/share/bit/bin/bit.js /usr/local/bin/bit.js 5 | ln -sf /usr/share/bit/bin/node /usr/local/bin/bitNode 6 | -------------------------------------------------------------------------------- /src/scope/dependency-maps/index.js: -------------------------------------------------------------------------------- 1 | import ExternalDependencyMap from './external-map'; 2 | import SourcesDependencyMap from './sources-map'; 3 | 4 | export { 5 | ExternalDependencyMap, 6 | SourcesDependencyMap 7 | }; 8 | -------------------------------------------------------------------------------- /src/utils/fs-stats.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import { stat } from 'fs'; 3 | import promisify from './promisify'; 4 | 5 | export default function stats(path: string): Promise { 6 | return promisify(stat)(path); 7 | } 8 | -------------------------------------------------------------------------------- /src/api/consumer/lib/get-driver.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import { loadConsumer } from '../../../consumer'; 3 | 4 | export default function getDriver() { 5 | return loadConsumer() 6 | .then(consumer => consumer.driver); 7 | } 8 | -------------------------------------------------------------------------------- /src/bit-id/exceptions/invalid-id-chunk.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | export default class InvalidIdChunk extends Error { 3 | id: string; 4 | 5 | constructor(id: string) { 6 | super(); 7 | this.id = id; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/cli/index.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import buildRegistrar from './command-registrar-builder'; 3 | import CommandRegistrar from './command-registrar'; 4 | 5 | export default { 6 | buildRegistrar, 7 | CommandRegistrar 8 | }; 9 | -------------------------------------------------------------------------------- /src/utils/is-bit-url.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | export default function isBitUrl(url: string) { 3 | const regex = new RegExp('((bit|ssh|http(s)?)|(bit@[\w\.]+))(:(//)?)([\w\.@\:/\-~]+)(\.bit)(/)?'); 4 | return regex.test(url); 5 | } 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | indent_size = 2 9 | indent_style = space 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | -------------------------------------------------------------------------------- /src/scope/objects/index.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import BitObject from './object'; 3 | import Repository from './repository'; 4 | import Ref from './ref'; 5 | 6 | export { 7 | BitObject, 8 | Ref, 9 | Repository 10 | }; 11 | -------------------------------------------------------------------------------- /src/utils/resolve-boolean.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @bit 3 | */ 4 | export default function resolveBoolean(resolve, reject) { 5 | return (err) => { 6 | if (err) return reject(err); 7 | return resolve(true); 8 | }; 9 | } 10 | -------------------------------------------------------------------------------- /specs/jsdoc/fixtures/misc.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @name publicFunc 3 | * @public 4 | */ 5 | function publicFunc() { 6 | 7 | } 8 | 9 | /** 10 | * @name privateFunc 11 | * @private 12 | */ 13 | function privateFunc() { 14 | 15 | } 16 | -------------------------------------------------------------------------------- /resources/repos/bitsrc.repo: -------------------------------------------------------------------------------- 1 | [Bitsrc] 2 | name=Bitsrc 3 | baseurl=https://bitsrc.jfrog.io/bitsrc/bit-yum/stable 4 | enabled=1 5 | gpgcheck=0 6 | gpgkey=https://bitsrc.jfrog.io/bitsrc/bit-yum/stable/repodata/repomd.xml.key 7 | repo_gpgcheck=1 8 | -------------------------------------------------------------------------------- /src/api/consumer/lib/exceptions/path-not-exists.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | export default class PathNotExists extends Error { 3 | path: string; 4 | 5 | constructor(path: string) { 6 | super(); 7 | this.path = path; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/consumer/bit-map/exceptions/invalid-bit-map.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | export default class InvalidBitMap extends Error { 3 | path: string; 4 | 5 | constructor(path: string) { 6 | super(); 7 | this.path = path; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /resources/impl.template.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @param {type} name 4 | * @returns 5 | * @sig 6 | * @example 7 | * // example description 8 | * example.do(); //outputs nothing 9 | */ 10 | module.exports = function() { 11 | 12 | }; 13 | -------------------------------------------------------------------------------- /src/api/consumer/lib/exceptions/invalid-id-on-commit.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | export default class InvalidIdOnCommit extends Error { 3 | id: string; 4 | 5 | constructor(id: string) { 6 | super(); 7 | this.id = id; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/consumer/bit-json/exceptions/invalid-bit-json.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | export default class InvalidBitJson extends Error { 3 | path: string; 4 | 5 | constructor(path: string) { 6 | super(); 7 | this.path = path; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/search/index.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import indexer from './indexer'; 3 | import searcher from './searcher'; 4 | import searchAdapter from './search-adapter'; 5 | 6 | export { 7 | indexer, 8 | searcher, 9 | searchAdapter 10 | }; 11 | -------------------------------------------------------------------------------- /src/api/consumer/lib/init.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import { Consumer } from '../../../consumer'; 3 | 4 | export default function init(absPath: string): Promise { 5 | return Consumer.create(absPath) 6 | .then(consumer => consumer.write()); 7 | } 8 | -------------------------------------------------------------------------------- /src/scope/exceptions/merge-conflict.js: -------------------------------------------------------------------------------- 1 | export default class MergeConflict extends Error { 2 | id: string; 3 | code: number; 4 | 5 | constructor(id: string) { 6 | super(); 7 | this.code = 131; 8 | this.id = id; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/api/scope/lib/describe-scope.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import { loadScope } from '../../../scope'; 3 | 4 | export default function describeScope(path: string) { 5 | return loadScope(path).then((scope) => { 6 | return scope.describe(); 7 | }); 8 | } 9 | -------------------------------------------------------------------------------- /src/consumer/bit-map/exceptions/missing-bit-map-component.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | export default class MissingBitMapComponent extends Error { 3 | id: string; 4 | 5 | constructor(id: string) { 6 | super(); 7 | this.id = id; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/consumer/component/exceptions/file-source-not-found.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | export default class FileSourceNotFound extends Error { 3 | path: string; 4 | 5 | constructor(path: string) { 6 | super(); 7 | this.path = path; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/utils/unpack-command.js: -------------------------------------------------------------------------------- 1 | import fromBase64 from './string/from-base64'; 2 | 3 | module.exports = function unpackCmd(str) { 4 | try { 5 | return JSON.parse(fromBase64(str)); 6 | } catch (err) { 7 | throw new Error(str); 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /src/consumer/exceptions/missing-dependencies.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | export default class MissingDependencies extends Error { 3 | components: Object; 4 | 5 | constructor(components) { 6 | super(); 7 | this.components = components; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/scope/network/exceptions/network-error.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | 3 | export default class NetworkError extends Error { 4 | remoteErr: string; 5 | 6 | constructor(remoteErr: string) { 7 | super(); 8 | this.remoteErr = remoteErr; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/utils/build-command-message.js: -------------------------------------------------------------------------------- 1 | import { BIT_VERSION } from '../constants'; 2 | 3 | module.exports = function buildCommandMessage(payload) { 4 | return { 5 | payload, 6 | headers: { 7 | version: BIT_VERSION 8 | } 9 | }; 10 | }; 11 | -------------------------------------------------------------------------------- /src/utils/os-resolve-group-id.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import { getgrnam } from 'posix'; 3 | 4 | export default function resolveGroupId(groupName: string): ?number { 5 | const group = getgrnam(groupName); 6 | if (group) return group.gid; 7 | return null; 8 | } 9 | -------------------------------------------------------------------------------- /src/consumer/component/exceptions/plugin-not-found.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | export default class PluginNotFoundException extends Error { 3 | plugin: string; 4 | 5 | constructor(plugin : string) { 6 | super(); 7 | this.plugin = plugin; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /resources/repos/deb-repo.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | #add gpg key 4 | curl https://bitsrc.jfrog.io/bitsrc/api/gpg/key/public | sudo apt-key add - 5 | 6 | #add source 7 | sudo sh -c "echo 'deb http://bitsrc.jfrog.io/bitsrc/bit-deb all stable' >> /etc/apt/sources.list" 8 | 9 | -------------------------------------------------------------------------------- /src/scope/index.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import loadScope from './scope-loader'; 3 | import Scope from './scope'; 4 | import ComponentWithDependencies from './component-dependencies'; 5 | 6 | export { 7 | loadScope, 8 | Scope, 9 | ComponentWithDependencies 10 | }; 11 | -------------------------------------------------------------------------------- /src/scope/exceptions/component-not-found.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | export default class ComponentNotFound extends Error { 3 | id: string; 4 | code: number; 5 | 6 | constructor(id: string) { 7 | super(); 8 | this.code = 127; 9 | this.id = id; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/api/scope/lib/scope-init.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import { Scope } from '../../../scope'; 3 | 4 | export default function init(path: string, name: string, groupName: string): Promise { 5 | return Scope.ensure(path, name, groupName) 6 | .then(scope => scope.ensureDir()); 7 | } 8 | -------------------------------------------------------------------------------- /src/consumer/bit-map/exceptions/index.js: -------------------------------------------------------------------------------- 1 | import InvalidBitMap from './invalid-bit-map'; 2 | import MissingBitMapComponent from './missing-bit-map-component'; 3 | import MissingMainFile from './missing-main-file'; 4 | 5 | export { InvalidBitMap, MissingBitMapComponent, MissingMainFile }; 6 | -------------------------------------------------------------------------------- /src/consumer/component/exceptions/bit-already-exist-externaly.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | export default class BitAlreadyExistExternalyError extends Error { 3 | bitName: string; 4 | 5 | constructor(bitName : string) { 6 | super(); 7 | this.bitName = bitName; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/utils/fs/dir-path-parser.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import path from 'path'; 3 | 4 | /** 5 | * parse given dir path 6 | * @param {*} dirPath 7 | */ 8 | export default function parseDirPath(dirPath: string) { 9 | return path.parse(dirPath).dir.split(path.delimiter); 10 | } 11 | -------------------------------------------------------------------------------- /resources/bit.template.json: -------------------------------------------------------------------------------- 1 | { 2 | "remotes": { 3 | "wix": "ssh://bit@bit.wix.com:user" 4 | }, 5 | 6 | "dependencies": { 7 | "ranm8/fs/createDirectory": "*1", 8 | "@wix/user/getUser": "3", 9 | "ranm8/isString": "latest", 10 | "wix/foreach": "*2" 11 | } 12 | } -------------------------------------------------------------------------------- /src/scope/object-registrar.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import { Source, Component, Version, ScopeMeta, Symlink } from './models'; 3 | 4 | export default function types() { 5 | return [ 6 | Source, 7 | Component, 8 | Version, 9 | ScopeMeta, 10 | Symlink 11 | ]; 12 | } 13 | -------------------------------------------------------------------------------- /src/api/consumer/lib/remove.js: -------------------------------------------------------------------------------- 1 | 2 | // /** @flow */ 3 | // import { loadConsumer } from '../../consumer'; 4 | 5 | // export default function remove(id: string): Promise { 6 | // return loadConsumer().then(consumer => 7 | // consumer.removeFromInline(id) 8 | // ); 9 | // } 10 | -------------------------------------------------------------------------------- /src/scope/network/exceptions/remote-scope-not-found.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | export default class RemoteScopeNotFound extends Error { 3 | name: string; 4 | code: number; 5 | 6 | constructor(name: string) { 7 | super(); 8 | this.code = 129; 9 | this.name = name; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tasks/lint.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'); 2 | const eslint = require('gulp-eslint'); 3 | const flow = require('gulp-flowtype'); 4 | 5 | module.exports = () => gulp 6 | .src([ 7 | './src/**/*.js', 8 | './specs/**/*.js' 9 | ]) 10 | .pipe(eslint()) 11 | .pipe(eslint.format()); 12 | -------------------------------------------------------------------------------- /scripts/generate-formula.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ex 4 | 5 | 6 | rm -rf ./distribution/bit.rb 7 | mkdir -p ./distribution 8 | SHA=$( curl -s $1 |shasum -b -a 256| cut -d ' ' -f 1 ) 9 | sed 's#sha256 ""#sha256 "'${SHA}'"#' ./scripts/bit.rb | sed 's#url ""#url "'$1'"#' > ./distribution/bit.rb 10 | -------------------------------------------------------------------------------- /src/consumer/consumer-loader.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import * as path from 'path'; 3 | import Consumer from './consumer'; 4 | 5 | export default function loadConsumer(currentPath: ?string) { 6 | if (!currentPath) currentPath = process.cwd(); 7 | return Consumer.load(path.resolve(currentPath), false); 8 | } 9 | -------------------------------------------------------------------------------- /src/utils/buffer/to-read-stream.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import * as stream from 'stream'; 3 | 4 | /** 5 | * cast a buffer to a read stream 6 | */ 7 | export default function bufferToReadStream(buffer: Buffer) { 8 | const s = new stream.PassThrough(); 9 | s.end(buffer); 10 | return s; 11 | } 12 | -------------------------------------------------------------------------------- /src/utils/number/is-numeric.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | 3 | /** 4 | * determines whether `val` is a numeric value 5 | * @name isNumeric 6 | * @param {*} val 7 | * @return {boolean} 8 | */ 9 | export default function isNumeric(val: any) { 10 | return !isNaN(parseFloat(val)) && isFinite(val); 11 | } 12 | -------------------------------------------------------------------------------- /src/cli/cli-utils.js: -------------------------------------------------------------------------------- 1 | import { toBase64, fromBase64, isString } from '../utils'; 2 | 3 | export const pack = (x: Array|string): string => { 4 | return isString(x) ? toBase64(x) : toBase64(x.join('+++')); 5 | }; 6 | 7 | export const unpack = (str: string): Array => fromBase64(str).split('+++'); 8 | -------------------------------------------------------------------------------- /src/hooks/hooks.js: -------------------------------------------------------------------------------- 1 | import createHook from './create-hook'; 2 | import { CFG_POST_EXPORT_HOOK_KEY, CFG_POST_IMPORT_HOOK_KEY } from '../constants'; 3 | 4 | export const postExportHook = createHook(CFG_POST_EXPORT_HOOK_KEY, 'post'); 5 | export const postImportHook = createHook(CFG_POST_IMPORT_HOOK_KEY, 'post'); 6 | -------------------------------------------------------------------------------- /src/scope/exceptions/dependency-not-found.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | export default class DependencyNotFound extends Error { 3 | id: string; 4 | code: number; 5 | bitJsonPath: string; 6 | 7 | constructor(id: string) { 8 | super(); 9 | this.code = 127; 10 | this.id = id; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/utils/is-valid-id-chunk.js: -------------------------------------------------------------------------------- 1 | import isString from './string/is-string'; 2 | 3 | const validationRegExp = /^[$\-_!.a-z0-9]+$/; 4 | 5 | /** @flow */ 6 | export default function isValidIdChunk(val: any): boolean { 7 | if (!isString(val)) return false; 8 | return validationRegExp.test(val); 9 | } 10 | -------------------------------------------------------------------------------- /src/consumer/component/exceptions/component-not-found-in-path.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | export default class ComponentNotFoundInPath extends Error { 3 | path: string; 4 | code: number; 5 | 6 | constructor(path: string) { 7 | super(); 8 | this.code = 127; 9 | this.path = path; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/utils/is-valid-scope-name.js: -------------------------------------------------------------------------------- 1 | import isString from './string/is-string'; 2 | 3 | const validationRegExp = /^@?[$\-_!.a-z0-9]+$/; 4 | 5 | /** @flow */ 6 | export default function isValidScopeName(val: any): boolean { 7 | if (!isString(val)) return false; 8 | return validationRegExp.test(val); 9 | } 10 | -------------------------------------------------------------------------------- /src/scope/ci-ops/ci-worker.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import runAndUpdateCI from './run-and-update-ci'; 3 | 4 | const scopePath = process.env.__scope__; 5 | const id = process.env.__id__; 6 | 7 | // $FlowFixMe 8 | runAndUpdateCI({ id, scopePath }) 9 | .then(() => null) 10 | .catch(er => process.stderr.write(er)); 11 | -------------------------------------------------------------------------------- /src/utils/fs-read-file.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import fs from 'fs'; 3 | 4 | export default function readFile(path: string) { 5 | return new Promise((resolve, reject) => { 6 | fs.readFile(path, (err, res) => { 7 | if (err) return reject(err); 8 | return resolve(res); 9 | }); 10 | }); 11 | } 12 | -------------------------------------------------------------------------------- /src/utils/fs/is-dir-empty.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import fs from 'fs'; 3 | 4 | export default function isDirEmpty(dirPath: string, cb: (itDoes: boolean) => void) { 5 | fs.readdir(dirPath, (err, files) => { 6 | if (err) throw err; 7 | if (!files.length) return cb(true); 8 | return cb(false); 9 | }); 10 | } 11 | -------------------------------------------------------------------------------- /src/api/consumer/lib/get-consumer-component.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import { loadConsumer } from '../../../consumer'; 3 | import { BitId } from '../../../bit-id'; 4 | 5 | export default function getConsumerBit({ id }: { id: string }) { 6 | return loadConsumer() 7 | .then(consumer => consumer.loadComponent(BitId.parse(id))); 8 | } 9 | -------------------------------------------------------------------------------- /src/app.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import loudRejection from 'loud-rejection'; 3 | import buildRegistrar from './cli/command-registrar-builder'; 4 | 5 | loudRejection(); 6 | 7 | const registrar = buildRegistrar(); 8 | 9 | try { 10 | registrar.run(); 11 | } catch (err) { 12 | console.error('loud rejected:', err); 13 | } 14 | -------------------------------------------------------------------------------- /src/consumer/bit-map/exceptions/missing-main-file.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | export default class MissingMainFile extends Error { 3 | mainFile: string; 4 | files: string[]; 5 | 6 | constructor(mainFile: string, files: string[]) { 7 | super(); 8 | this.mainFile = mainFile; 9 | this.files = files; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/consumer/bit-json/exceptions/index.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import BitJsonAlreadyExists from './bit-json-already-exists'; 3 | import BitJsonNotFound from './bit-json-not-found'; 4 | import InvalidBitJson from './invalid-bit-json'; 5 | 6 | export { 7 | BitJsonNotFound, 8 | BitJsonAlreadyExists, 9 | InvalidBitJson 10 | }; 11 | -------------------------------------------------------------------------------- /src/utils/fs-output-file.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import fs from 'fs-extra'; 3 | 4 | export default function outputFile(file: string, data) { 5 | return new Promise((resolve, reject) => { 6 | fs.outputFile(file, data, err => { 7 | if (err) return reject(err); 8 | return resolve(file); 9 | }); 10 | }); 11 | } 12 | -------------------------------------------------------------------------------- /src/utils/is-dir.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import fs from 'fs'; 3 | 4 | export default function isDir(userPath: string): boolean { 5 | let stat; 6 | try { 7 | stat = fs.lstatSync(userPath); 8 | } catch (err) { 9 | throw new Error(`The path ${userPath} doesn't exist`); 10 | } 11 | return stat.isDirectory(); 12 | } 13 | -------------------------------------------------------------------------------- /src/utils/os-resolve-home-path.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | const userHome = require('user-home'); 3 | 4 | const HOME_SIGN = '~'; 5 | 6 | export default function resolveHomePath(relPath: string) { 7 | if (relPath.startsWith(HOME_SIGN)) { 8 | return relPath.replace(HOME_SIGN, userHome); 9 | } 10 | 11 | return relPath; 12 | } 13 | -------------------------------------------------------------------------------- /src/utils/zlib-deflate.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import zlib from 'zlib'; 3 | 4 | export default function deflate(buffer: Buffer): Promise { 5 | return new Promise((resolve, reject) => { 6 | zlib.deflate(buffer, (err, res) => { 7 | if (err) return reject(err); 8 | return resolve(res); 9 | }); 10 | }); 11 | } 12 | -------------------------------------------------------------------------------- /src/utils/zlib-inflate.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import zlib from 'zlib'; 3 | 4 | export default function inflate(buffer: Buffer): Promise { 5 | return new Promise((resolve, reject) => { 6 | zlib.inflate(buffer, (err, res) => { 7 | if (err) return reject(err); 8 | return resolve(res); 9 | }); 10 | }); 11 | } 12 | -------------------------------------------------------------------------------- /src/api/consumer/lib/get-component-logs.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import { loadConsumer } from '../../../consumer'; 3 | import { BitId } from '../../../bit-id'; 4 | 5 | export default function getComponentLogs(id: string) { 6 | return loadConsumer() 7 | .then(consumer => consumer.scope.loadComponentLogs(BitId.parse(id, consumer.scope.name))); 8 | } 9 | -------------------------------------------------------------------------------- /src/driver/exceptions/driver-not-found.js: -------------------------------------------------------------------------------- 1 | export default class DriverNotFoundError extends Error { 2 | driver: string; 3 | lang: string; 4 | name: string; 5 | 6 | constructor(driver: string, lang: string) { 7 | super(); 8 | this.name = 'DriverNotFound'; 9 | this.driver = driver; 10 | this.lang = lang; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/utils/fs-chown.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import fs from 'fs'; 3 | 4 | export default function chown(path: string, uid: number, gid: number): Promise { 5 | return new Promise((resolve, reject) => { 6 | fs.chown(path, uid, gid, (err) => { 7 | if (err) return reject(err); 8 | return resolve(); 9 | }); 10 | }); 11 | } 12 | -------------------------------------------------------------------------------- /src/utils/promise-all-settled.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import toResultObject from './promise-to-result-object'; 3 | import type { ResultObject } from './promise-to-result-object'; 4 | 5 | // $FlowFixMe 6 | export default function allSettled(promises: Promise[]): Promise { 7 | return Promise.all(promises.map(toResultObject())); 8 | } 9 | -------------------------------------------------------------------------------- /docs/extending-bit.md: -------------------------------------------------------------------------------- 1 | 2 | * [Extending Bit](extending-bit.md) 3 | * [Scope Resolvers](extending-bit.md#scope-resolvers) 4 | * [Hooks](extending-bit.md#hooks) 5 | * [API Reference](extending-bit.md#api-reference) 6 | 7 | # Scope Resolvers 8 | 9 | //TODO 10 | 11 | # Hooks 12 | 13 | //TODO 14 | 15 | # API Reference 16 | 17 | //TODO 18 | 19 | -------------------------------------------------------------------------------- /src/consumer/exceptions/driver-not-found.js: -------------------------------------------------------------------------------- 1 | export default class DriverNotFoundError extends Error { 2 | driver: string; 3 | lang: string; 4 | name: string; 5 | 6 | constructor(driver: string, lang: string) { 7 | super(); 8 | this.name = 'DriverNotFound'; 9 | this.driver = driver; 10 | this.lang = lang; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tests/e2e.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | 4 | rm -rf /tmp/scopy 5 | 6 | cd /tmp 7 | mkdir scopy && cd scopy 8 | bit init --bare 9 | bit remote add file:///tmp/scopy --global 10 | 11 | rm -rf /tmp/test 12 | cd /tmp 13 | mkdir test && cd test 14 | bit init 15 | bit create test 16 | bit commit test "test commit" 17 | bit export @this/test @scopy 18 | -------------------------------------------------------------------------------- /src/utils/number/is-number.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | 3 | /** 4 | * determiens whether `val` is a number. 5 | * @name isNumber 6 | * @param {*} val 7 | * @returns {boolean} 8 | * @example 9 | * ```js 10 | * isNumber('') // => false 11 | * ``` 12 | */ 13 | export default function isNumber(val: any) { 14 | return typeof val === 'number'; 15 | } 16 | -------------------------------------------------------------------------------- /src/consumer/component/templates/impl.default-template.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import camelcase from 'camelcase'; 3 | 4 | export default ({ name }: { name: string }): string => { 5 | return ` 6 | /** 7 | * {description} 8 | * @param {type} name 9 | * @returns 10 | * 11 | */ 12 | module.exports = function ${camelcase(name)}() { 13 | 14 | };`; 15 | }; 16 | -------------------------------------------------------------------------------- /src/api/scope/lib/refresh-scope.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import { loadScope } from '../../../scope'; 3 | 4 | export default function refreshScope(path: string): Promise { 5 | return loadScope(path) 6 | .then((scope) => { 7 | return scope.objects.list() 8 | .then(objects => Promise.all(objects.map(o => scope.objects.persistOne(o)))); 9 | }); 10 | } 11 | -------------------------------------------------------------------------------- /src/consumer/component/sources/index.js: -------------------------------------------------------------------------------- 1 | import Impl from './impl'; 2 | import Specs from './specs'; 3 | import Source from './source'; 4 | import Dist from './dist'; 5 | import License from './license'; 6 | import SourceFile from './source-file'; 7 | 8 | export { 9 | Impl, 10 | Specs, 11 | Source, 12 | Dist, 13 | License, 14 | SourceFile, 15 | }; 16 | -------------------------------------------------------------------------------- /src/remotes/exceptions/index.js: -------------------------------------------------------------------------------- 1 | import PrimaryNotFound from './primary-not-found'; 2 | import PrimaryOverloaded from './primary-overloaded'; 3 | import InvalidRemote from './invalid-remote'; 4 | import RemoteNotFound from './remote-not-found'; 5 | 6 | export { 7 | PrimaryNotFound, 8 | PrimaryOverloaded, 9 | InvalidRemote, 10 | RemoteNotFound 11 | }; 12 | -------------------------------------------------------------------------------- /src/utils/filter-object.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import forEach from './object/foreach'; 3 | 4 | /** 5 | * 6 | */ 7 | export default function filterObject(obj: {[any]: any}, fn: (val: any, key: any) => bool): Object { 8 | const newObj = {}; 9 | forEach(obj, (val, key) => { 10 | if (fn(val, key)) newObj[key] = val; 11 | }); 12 | return newObj; 13 | } 14 | -------------------------------------------------------------------------------- /src/utils/glob.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | const globlib = require('glob'); 3 | 4 | export default function glob(pattern: string, options: {}): Promise { 5 | return new Promise((resolve, reject) => { 6 | globlib(pattern, options, (err, matches) => { 7 | if (err) return reject(err); 8 | return resolve(matches); 9 | }); 10 | }); 11 | } 12 | -------------------------------------------------------------------------------- /src/utils/promisify.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | 3 | export default function promisify(fn: (...args: any[]) => any) { 4 | return (...args: any[]): Promise => { 5 | return new Promise((resolve, reject) => { 6 | fn(...args, (err, res) => { 7 | if (err) return reject(err); 8 | return resolve(res); 9 | }); 10 | }); 11 | }; 12 | } 13 | -------------------------------------------------------------------------------- /src/consumer/component/sources/source.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import Bit from '../../component'; 3 | 4 | export default class Source { 5 | src: string; 6 | 7 | constructor(src: string) { 8 | this.src = src; 9 | } 10 | 11 | create(bit: Bit): Source { // eslint-disable-line 12 | throw Error('every source must implement a create method'); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/utils/mkdirp.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | const mkdir = require('mkdirp'); 3 | 4 | export default function mkdirp(path: string, opts: {} = {}): Promise { 5 | return new Promise((resolve, reject) => { 6 | // $FlowFixMe 7 | mkdir(path, opts, (err) => { 8 | if (err) return reject(err); 9 | return resolve(true); 10 | }); 11 | }); 12 | } 13 | -------------------------------------------------------------------------------- /bit.json: -------------------------------------------------------------------------------- 1 | { 2 | "sources": { 3 | "impl": "impl.js", 4 | "spec": "spec.js" 5 | }, 6 | "env": { 7 | "compiler": "none", 8 | "tester": "none" 9 | }, 10 | "dependencies": { 11 | }, 12 | "structure": "components/{namespace}/{name}", 13 | "dist": { 14 | "target": "dist", 15 | "entry": "" 16 | } 17 | } -------------------------------------------------------------------------------- /src/scope/dependency-maps/external-map.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import path from 'path'; 3 | import { DependencyMap } from './dependency-map'; 4 | import { EXTERNAL_MAP } from '../../constants'; 5 | 6 | export default class ExternalDependencyMap extends DependencyMap { 7 | getPath(): string { 8 | return path.join(this.repository.getPath(), EXTERNAL_MAP); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/api/scope/lib/cat-object.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import { loadScope } from '../../../scope'; 3 | 4 | export default function catObject(hash: string) { 5 | return loadScope().then((scope) => { 6 | return scope.getObject(hash) 7 | .then((object) => { 8 | if (!object) return 'object not found'; 9 | return object.toBuffer(); 10 | }); 11 | }); 12 | } 13 | -------------------------------------------------------------------------------- /src/consumer/component/templates/specs.default-template.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import camelcase from 'camelcase'; 3 | 4 | export default ({ name }: { name: string }): string => { 5 | return `const expect = require('chai').expect; 6 | const ${camelcase(name)} = require('./impl.js'); 7 | 8 | describe('${camelcase(name)}', () => { 9 | it('', () => { 10 | 11 | }); 12 | });`; 13 | }; 14 | -------------------------------------------------------------------------------- /src/utils/object-to-stringified-tuple-array.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import forEach from './object/foreach'; 3 | 4 | export default function objectToStringifiedTupleArray(obj: {[string|number]: any}): [string|number][] { 5 | const arr = []; 6 | forEach(obj, (val, key) => { 7 | arr.push([key, (typeof val === 'object') ? JSON.stringify(val) : val ]); 8 | }); 9 | return arr; 10 | } 11 | -------------------------------------------------------------------------------- /src/scope/repositories/cache.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import * as path from 'path'; 3 | import Repository from '../repository'; 4 | import { BIT_CACHE_DIRNAME } from '../../constants'; 5 | 6 | export default class Cache extends Repository { 7 | getPath(): string { 8 | return path.join(super.getPath(), BIT_CACHE_DIRNAME); 9 | } 10 | 11 | set(name: string, buffer: Buffer) { 12 | 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tasks/build.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'); 2 | const newer = require('gulp-newer'); 3 | const babel = require('gulp-babel'); 4 | const fs = require('fs'); 5 | const path = require('path'); 6 | const sourceMaps = require('gulp-sourcemaps') 7 | 8 | module.exports = () => gulp 9 | .src([ 10 | './src/**/*.js' 11 | ]) 12 | .pipe(newer('dist')) 13 | .pipe(babel()) 14 | .pipe(gulp.dest('dist')); 15 | -------------------------------------------------------------------------------- /src/scope/models/index.js: -------------------------------------------------------------------------------- 1 | import Component from './component'; 2 | import ExternalComponent from './external-component'; 3 | import ScopeMeta from './scopeMeta'; 4 | import Source from './source'; 5 | import Version from './version'; 6 | import Symlink from './symlink'; 7 | 8 | module.exports = { 9 | Component, 10 | ExternalComponent, 11 | ScopeMeta, 12 | Source, 13 | Version, 14 | Symlink, 15 | }; 16 | -------------------------------------------------------------------------------- /src/utils/string/is-string.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | 3 | /** 4 | * determines whether `val` is of type string. 5 | * @name isString 6 | * @param {*} val value to test. 7 | * @returns {boolean} 8 | * @example 9 | * ```js 10 | * isString('') // => true 11 | * isString(4) // => false 12 | * ``` 13 | */ 14 | export default function isString(val: any): boolean { 15 | return typeof val === 'string'; 16 | } 17 | -------------------------------------------------------------------------------- /src/scope/component-dependencies.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import Component from '../consumer/component'; 3 | 4 | export default class ComponentWithDependencies { 5 | component: Component; 6 | dependencies: Component[]; 7 | 8 | constructor(props: { component: Component, dependencies: Component[] }) { 9 | this.component = props.component; 10 | this.dependencies = props.dependencies || []; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/scope/repository.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import Scope from './scope'; 3 | import { mkdirp } from '../utils'; 4 | 5 | export default class Repository { 6 | scope: Scope; 7 | path: string; 8 | 9 | constructor(scope: Scope) { 10 | this.scope = scope; 11 | } 12 | 13 | getPath() { 14 | return this.scope.getPath(); 15 | } 16 | 17 | ensureDir() { 18 | return mkdirp(this.getPath()); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/utils/array/flatten.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | 3 | /** 4 | * flatten arrays to a single dimension. 5 | * @name flatten 6 | * @param {[[*]]} arrays 7 | * @returns [*] a flatten array 8 | * @example 9 | * ```js 10 | * flatten([[1], [2], [3]]) // => [1, 2, 3] 11 | * ``` 12 | */ 13 | export default function flatten(arrays: Array): Array { 14 | const concat = [].concat; 15 | return concat.apply([], arrays); 16 | } 17 | -------------------------------------------------------------------------------- /src/utils/promise-to-result-object.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | export type ResultObject = { 3 | success: boolean, 4 | val: ?T, 5 | error: Error 6 | }; 7 | 8 | export default function toResultObject() { 9 | return (promise: Promise): Promise> => { 10 | return promise 11 | .then(val => ({ success: true, val })) 12 | .catch(error => ({ success: false, error, val: null })); 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /src/utils/array/first.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | 3 | /** 4 | * returns the first element of an array. 5 | * @name first 6 | * @param {[]} array 7 | * @returns {[]} the first element of given array 8 | * @example 9 | * ```js 10 | * first([1,2,3]) // => 1 11 | * first([]) // => null 12 | * ``` 13 | */ 14 | export default function first(array: any[]): ?any { 15 | if (array && array[0]) return array[0]; 16 | return null; 17 | } 18 | -------------------------------------------------------------------------------- /src/utils/fs/current-dir-name.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import path from 'path'; 3 | 4 | /** 5 | * get the current working dir name. 6 | * @name currentDirName 7 | * @returns {string} current working dir name 8 | * @example 9 | * ```js 10 | * currentDirName() // => 'bit' 11 | * ``` 12 | */ 13 | export default function currentDirName(): string { 14 | const currentDir = process.cwd(); 15 | return path.basename(currentDir); 16 | } 17 | -------------------------------------------------------------------------------- /src/utils/string/from-base64.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | 3 | /** 4 | * decode a base64 string 5 | * @name fromBase64 6 | * @param {string} base64 base64 string to decode 7 | * @returns {string} decoded string 8 | * @example 9 | * ```js 10 | * fromBase64('aGVsbG8gd29ybGQ=') // => 'hello world' 11 | * ``` 12 | */ 13 | export default function fromBase64(base64: string) { 14 | return new Buffer(base64, 'base64').toString(); 15 | } 16 | -------------------------------------------------------------------------------- /src/scope/scope-loader.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import * as path from 'path'; 3 | import Scope from './scope'; 4 | import { resolveHomePath } from '../utils'; 5 | 6 | export default function loadScope(currentPath: ?string): Promise { 7 | if (!currentPath) currentPath = process.cwd(); 8 | try { 9 | return Scope.load(path.resolve(resolveHomePath(currentPath))); 10 | } catch (err) { 11 | return Promise.reject(err); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tests/bit.features: -------------------------------------------------------------------------------- 1 | Feature: Run commands 2 | 3 | There are several steps to run commands with `aruba`. 4 | 5 | Scenario: Run command bit 6 | When I run `bit` 7 | Then the features should all pass 8 | Scenario: Run command bit init 9 | When I run `bit init` 10 | Then the features should all pass 11 | Scenario: Run command bit create test 12 | When I run `bit init && bit create test` 13 | Then the features should all pass 14 | -------------------------------------------------------------------------------- /src/api/scope/lib/cat-scope.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import { loadScope } from '../../../scope'; 3 | import Component from '../../../scope/models/component'; 4 | import BitObject from '../../../scope/objects/object'; 5 | 6 | export default function catScope(path: string, full: bool): Promise { 7 | return loadScope(path) 8 | .then((scope) => { 9 | return full ? scope.objects.list() : scope.objects.listComponents(); 10 | }); 11 | } 12 | -------------------------------------------------------------------------------- /src/utils/string/clean-bang.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import cleanChar from './clean-char'; 3 | 4 | /** 5 | * remove first bang (!) from `str` 6 | * @name cleanBang 7 | * @param {string} str string to manipulate 8 | * @returns {string} string without first found bang 9 | * @example 10 | * ```js 11 | * cleanBang('!bang') // => 'bang' 12 | * ``` 13 | */ 14 | export default function cleanBang(str: string): string { 15 | return cleanChar(str, '!'); 16 | } 17 | -------------------------------------------------------------------------------- /specs/jsdoc/fixtures/variableDeclaration.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Adds two numbers. 3 | * 4 | * @name add 5 | * @category Math 6 | * @param {number} a The first number in an addition. 7 | * @param {number} b The second number in an addition. 8 | * @returns {number} Returns the total. 9 | * @example 10 | * 11 | * _.add(6, 4); 12 | * // => 10 13 | */ 14 | var add = function(a, b) { 15 | return a+b; 16 | }; 17 | 18 | module.exports = { 19 | add, 20 | }; 21 | 22 | -------------------------------------------------------------------------------- /specs/jsdoc/fixtures/flowTypeFile.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | 3 | /** 4 | * returns the first element of an array reference. 5 | * @name first 6 | * @param {[]} array 7 | * @returns {*|null} first element of given array 8 | * @example 9 | * ```js 10 | * first([1, 2, 3]) // => 1 11 | * first(['a', 'b', 'c']) // => 'a' 12 | * ``` 13 | */ 14 | module.exports = function first(array: any[]): ?any { 15 | if (array && array[0]) return array[0]; 16 | return null; 17 | }; -------------------------------------------------------------------------------- /src/api/scope/lib/fetch.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import { loadScope } from '../../../scope'; 3 | import { BitIds } from '../../../bit-id'; 4 | 5 | export default function fetch(path: string, ids: string[], noDependencies: bool = false) { 6 | return loadScope(path) 7 | .then((scope) => { 8 | const bitIds = BitIds.deserialize(ids); 9 | if (noDependencies) return scope.manyOneObjects(bitIds); 10 | return scope.getObjects(bitIds); 11 | }); 12 | } 13 | -------------------------------------------------------------------------------- /specs/jsdoc/fixtures/functionDeclaration.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Adds two numbers. 3 | * 4 | * @name add 5 | * @static 6 | * @public 7 | * @param {number} a The first number in an addition. 8 | * @param {number} b The second number in an addition. 9 | * @returns {number} Returns the total. 10 | * @example 11 | * //- 12 | * // Adds two numbers 13 | * //- 14 | * add(2, 3); 15 | * //=> 16 | * // 5 17 | * //=> 18 | */ 19 | function add(a, b) { 20 | return a+b; 21 | } 22 | -------------------------------------------------------------------------------- /src/utils/string/clean-char.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | 3 | /** 4 | * clean the first occurrence of (`char`) from a string (`str`) 5 | * @name cleanChar 6 | * @param {string} str string to mainpulate. 7 | * @param {string} char char to clean. 8 | * @returns {string} cleaned string. 9 | * ```js 10 | * cleanChar('foo', 'f') // => 'oo' 11 | * ``` 12 | */ 13 | export default function cleanChar(str: string, char: string): string { 14 | return str.replace(char, ''); 15 | } 16 | -------------------------------------------------------------------------------- /scripts/set-installation-method.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /** 3 | * Sets the installationMethod field in package.json. Useful for setting it in 4 | * shell scripts. 5 | */ 6 | 7 | const fs = require('fs'); 8 | 9 | const packageManifestFilename = process.argv[2]; 10 | const packageManifest = require(packageManifestFilename); 11 | packageManifest.installationMethod = process.argv[3]; 12 | fs.writeFileSync(packageManifestFilename, JSON.stringify(packageManifest, null, 2) + "\n"); -------------------------------------------------------------------------------- /src/cli/templates/bare-list-template.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import ConsumerComponent from '../../consumer/component/consumer-component'; 3 | import {VERSION_DELIMITER} from '../../constants'; 4 | 5 | export default (components: ConsumerComponent[]) => { 6 | function paintBareComponent(component) { 7 | return `${component.scope}/${component.box}/${component.name}${VERSION_DELIMITER}${component.version}`; 8 | } 9 | 10 | return components.map(paintBareComponent).join('\n'); 11 | }; 12 | -------------------------------------------------------------------------------- /src/utils/string/contains.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | 3 | /** 4 | * determines whether string `str` ref contains substring `searchRef`. 5 | * @param {string} str 6 | * @param {string} searchStr 7 | * @returns {boolean} 8 | * @example 9 | * ```js 10 | * contains('foo bar', 'bar') // => true 11 | * contains('foo', 'bar') // => false 12 | * ``` 13 | */ 14 | export default function contains(str: string, searchStr: string): boolean { 15 | return str.indexOf(searchStr) !== -1; 16 | } 17 | -------------------------------------------------------------------------------- /src/api/scope/lib/scope-show.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import { loadScope } from '../../../scope'; 3 | import { BitId } from '../../../bit-id'; 4 | import ConsumerComponent from '../../../consumer/component'; 5 | 6 | export default function list(path: string, id: string): Promise { 7 | return loadScope(path) 8 | .then((scope) => { 9 | const bitId = BitId.parse(id); 10 | return scope.loadComponent(bitId) 11 | .then((c: ConsumerComponent) => c.toString()); 12 | }); 13 | } 14 | -------------------------------------------------------------------------------- /src/utils/fs-rmdir.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import fs from 'fs'; 3 | import * as pathlib from 'path'; 4 | 5 | export default function rmDir(path: string) { 6 | if (fs.existsSync(path)) { 7 | fs.readdirSync(path).forEach((file) => { 8 | const curPath = pathlib.join(path, file); 9 | if (fs.lstatSync(curPath).isDirectory()) { 10 | rmDir(curPath); 11 | } else { 12 | fs.unlinkSync(curPath); 13 | } 14 | }); 15 | fs.rmdirSync(path); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'); 2 | const fs = require('fs'); 3 | const path = require('path'); 4 | const runSequence = require('run-sequence'); 5 | const gulpsync = require('gulp-sync')(gulp); 6 | 7 | const tasksDir = './tasks'; 8 | 9 | fs.readdirSync(tasksDir).forEach(filename => gulp.task( 10 | filename.replace(/(\.js)/, ''), 11 | require(path.join(__dirname, tasksDir, filename)) 12 | )); 13 | 14 | gulp.task('default', done => runSequence('lint', 'test', 'clean', 'build', done)); 15 | -------------------------------------------------------------------------------- /src/api/scope/lib/ci-update-action.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import { loadScope } from '../../../scope'; 3 | import { BitId } from '../../../bit-id'; 4 | import runAndUpdateCi from '../../../scope/ci-ops/run-and-update-ci'; 5 | 6 | export default function CiUpdateAction(id: string, path: string) { 7 | return loadScope(path) 8 | .then((scope) => { 9 | const realId = BitId.parse(id, scope.name).toString(); 10 | return runAndUpdateCi({ id: realId, scopePath: path }); 11 | }); 12 | } 13 | -------------------------------------------------------------------------------- /scripts/bootstrap-env-ubuntu.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | apt-get install ruby-dev -y 3 | apt-get install -y rubygems 4 | #apt-get install rubygems-integration -y 5 | #apt-get install -y git-core curl zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev libcurl4-openssl-dev python-software-properties libffi-dev 6 | apt-get update -qq 7 | apt-get install -y rpm lintian 8 | gem install fpm 9 | apt-get install fakeroot -y 10 | npm install -g bit-bin 11 | -------------------------------------------------------------------------------- /src/api.js: -------------------------------------------------------------------------------- 1 | import { getScopeComponent } from './api/consumer/index'; 2 | import { scopeList } from './api/scope/index'; 3 | 4 | module.exports = { 5 | show: (scopePath, id, opts) => getScopeComponent({ scopePath, id, allVersions: opts && opts.versions }) 6 | .then((c) => { 7 | if (Array.isArray(c)) { return c.map(v => v.toObject()); } 8 | return c.toObject(); 9 | }), 10 | list: scopePath => scopeList(scopePath) 11 | .then(components => components.map(c => c.id.toString())), 12 | }; 13 | -------------------------------------------------------------------------------- /src/consumer/component/environment/load-plugin.js: -------------------------------------------------------------------------------- 1 | // /** @Flow */ 2 | // import path from 'path'; 3 | // import { PLUGINS_DIR } from '../../constants'; 4 | // import PluginNotFoundException from '../exceptions/plugin-not-found'; 5 | 6 | // export default (moduleName) => { 7 | // const moduleFullPath = path.join(PLUGINS_DIR, moduleName); 8 | // try { 9 | // return require(moduleFullPath); 10 | // } catch (e) { 11 | // throw new PluginNotFoundException(moduleName); 12 | // } 13 | // }; 14 | -------------------------------------------------------------------------------- /src/utils/encryption/sha1.js: -------------------------------------------------------------------------------- 1 | import crypto from 'crypto'; 2 | 3 | /** 4 | * encrypt `data` buffer or string into a sha1 hash 5 | * @param {string|Buffer} 6 | * @param {string} encoding 7 | * @returns {string} sha1 hash 8 | * @example 9 | * ```js 10 | * sha1('foo bar') // => '3773dea65156909838fa6c22825cafe090ff8030' 11 | * ``` 12 | */ 13 | export default function sha1(data, encoding) { 14 | return crypto.createHash('sha1') 15 | .update(data) 16 | .digest(encoding || 'hex'); 17 | } 18 | -------------------------------------------------------------------------------- /src/cli/commands/public-cmds/logout-cmd.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import Command from '../../command'; 3 | 4 | export default class Logout extends Command { 5 | name = 'logout'; 6 | description = 'logout from bit'; 7 | alias = ''; 8 | opts = []; 9 | 10 | action(): Promise { 11 | const m = this.alias; 12 | console.log('logged out from bit...'); 13 | return new Promise(resolve => resolve(m)); 14 | } 15 | 16 | report(data: {string: any}): string { 17 | return ''; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/utils/object/values.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import forEach from './foreach'; 3 | 4 | /** 5 | * get all object values. 6 | * @name values 7 | * @param {object} object 8 | * @returns {[]} object's values 9 | * @example 10 | * ```js 11 | * values({ a: 1, b: 2, c: 3 }) // => [1, 2, 3] 12 | * ``` 13 | */ 14 | export default function values(object: {[any]: any}): any[] { 15 | const objValues = []; 16 | // $FlowFixMe 17 | forEach(object, val => objValues.push(val)); 18 | return objValues; 19 | } 20 | -------------------------------------------------------------------------------- /docs/bit-internals.md: -------------------------------------------------------------------------------- 1 | 2 | # Bit internals 3 | 4 | ## Bit Objects 5 | 6 | //TODO 7 | 8 | ## JSDoc parsing 9 | 10 | Parsing the Docs yield useful information, such as the description of the component, its arguments, return type and usage examples. 11 | Other parts of the system, the Search, in particular, use that information for a better understanding what a component does. 12 | 13 | The JSDoc get discovered by a Regex pattern, and parsing the docs is done by [Doctrine](https://github.com/eslint/doctrine). 14 | -------------------------------------------------------------------------------- /src/utils/remove-from-require-cache.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | // $FlowFixMe 3 | import Module from 'module'; 4 | import filterObject from './filter-object'; 5 | 6 | // remove any cached module path for a module name (Module._pathCache) 7 | export default function removeFromRequireCache(currentRequestName: string) { 8 | Module._pathCache = filterObject(Module._pathCache, (val, key) => { 9 | const cachedRequestName = JSON.parse(key).request; 10 | return currentRequestName !== cachedRequestName; 11 | }); 12 | } 13 | -------------------------------------------------------------------------------- /tasks/build-legacy.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'); 2 | const newer = require('gulp-newer'); 3 | const babel = require('gulp-babel'); 4 | const fs = require('fs'); 5 | const path = require('path'); 6 | const sourceMaps = require('gulp-sourcemaps') 7 | 8 | const babelRc = JSON.parse(fs.readFileSync(path.resolve('.babelrc')), 'utf8'); 9 | 10 | module.exports = () => gulp 11 | .src([ 12 | './src/**/*.js' 13 | ]) 14 | .pipe(newer('dist-legacy')) 15 | .pipe(babel(babelRc.env.node4)) 16 | .pipe(gulp.dest('dist-legacy')); 17 | -------------------------------------------------------------------------------- /scripts/node-installer.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -ex 3 | 4 | OS=$1 5 | 6 | if [ "$OS" == "linux" ]; then 7 | url="https://nodejs.org/dist/v6.10.0/node-v6.10.0-linux-x64.tar.xz" 8 | elif [ "$OS" == "mac" ]; then 9 | url="https://nodejs.org/dist/v6.10.0/node-v6.10.0-darwin-x64.tar.gz" 10 | fi 11 | 12 | 13 | rm -rf ./nodeBin 14 | mkdir -p ./nodeBin 15 | pushd . 16 | cd nodeBin 17 | wget $url 18 | tar --strip-components=1 -xf node* 19 | popd 20 | pwd 21 | cp nodeBin/bin/node ./bin 22 | chmod +x ./bin/node 23 | rm -rf ./nodeBin 24 | -------------------------------------------------------------------------------- /src/cli/commands/public-cmds/login-cmd.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import Command from '../../command'; 3 | 4 | export default class Login extends Command { 5 | name = 'login'; 6 | description = 'login to bit'; 7 | alias = ''; 8 | opts = []; 9 | 10 | action([name, password]: [string, string]): Promise { 11 | const m = this.alias; 12 | console.log('logging in to bit.'); 13 | return new Promise(resolve => resolve(m)); 14 | } 15 | 16 | report(data: {string: any}): string { 17 | return ''; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /specs/consumer/component/source/impl.spec.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import { expect } from 'chai'; 3 | import sinon from 'sinon'; 4 | import Impl from '../../../../src/consumer/component/sources/impl'; 5 | 6 | describe('Impl', () => { 7 | describe('create', () => { 8 | it('should use a default template when failed to get a custom one', () => { 9 | const impl = Impl.create('my-component'); 10 | expect(impl).to.be.an.instanceof(Impl); 11 | expect(impl.src).to.contain('myComponent'); 12 | }); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /src/utils/array/flat-map.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | 3 | /** 4 | * Builds a new collection by applying a function to all elements of 5 | * this array and using the elements of the resulting collections. 6 | * @name flatMap 7 | * @param {[*]} array 8 | * @param {Function} cb 9 | * @returns {[*]} 10 | * @example 11 | * ```js 12 | * flatMap([[1, 2, 3], [4, 5, 6]], val => val) // => [1, 2, 3, 4, 5, 6] 13 | * ``` 14 | */ 15 | export default function flatMap(cb: Function) { 16 | return Array.prototype.concat.apply([], this.map(cb)); 17 | } 18 | -------------------------------------------------------------------------------- /specs/consumer/component/source/specs.spec.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import { expect } from 'chai'; 3 | import sinon from 'sinon'; 4 | import Specs from '../../../../src/consumer/component/sources/specs'; 5 | 6 | describe('Specs', () => { 7 | describe('create', () => { 8 | it('should use a default template when failed to get a custom one', () => { 9 | const specs = Specs.create('my-component'); 10 | expect(specs).to.be.an.instanceof(Specs); 11 | expect(specs.src).to.contain('myComponent'); 12 | }); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /src/api/scope/lib/put.js: -------------------------------------------------------------------------------- 1 | import { loadScope } from '../../../scope'; 2 | import ComponentObjects from '../../../scope/component-objects'; 3 | 4 | export type ComponentObjectsInput = { 5 | path: string; 6 | componentObjects: ComponentObjects 7 | } 8 | 9 | export default function put({ path, componentObjects }: ComponentObjectsInput): Promise { 10 | return loadScope(path).then((scope) => { 11 | const allComponents = ComponentObjects.manyFromString(componentObjects); 12 | return scope.exportManyBareScope(allComponents); 13 | }); 14 | } 15 | -------------------------------------------------------------------------------- /src/api/scope/lib/modify-ci-props.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import { loadScope } from '../../../scope'; 3 | import { BitId } from '../../../bit-id'; 4 | import ConsumerComponent from '../../../consumer/component'; 5 | 6 | export default function modifyCIProps(path: string, id: string, ciProps: Object): Promise { 7 | return loadScope(path) 8 | .then((scope) => { 9 | const bitId = BitId.parse(id); 10 | return scope.loadComponent(bitId) 11 | .then((c: ConsumerComponent) => 12 | scope.sources.modifyCIProps({ source: c, ciProps })); 13 | }); 14 | } 15 | -------------------------------------------------------------------------------- /src/consumer/exceptions/index.js: -------------------------------------------------------------------------------- 1 | import ConsumerNotFound from './consumer-not-found'; 2 | import ConsumerAlreadyExists from './consumer-already-exists'; 3 | import NothingToImport from './nothing-to-import'; 4 | import ComponentSpecsFailed from './component-specs-failed'; 5 | import DriverNotFound from './driver-not-found'; 6 | import MissingDependencies from './missing-dependencies'; 7 | 8 | export { 9 | ConsumerNotFound, 10 | ConsumerAlreadyExists, 11 | NothingToImport, 12 | ComponentSpecsFailed, 13 | DriverNotFound, 14 | MissingDependencies 15 | }; 16 | -------------------------------------------------------------------------------- /src/scope/dependencies/name-parser.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import { DEPENDENCY_DELIMITER } from '../../constants'; 3 | 4 | export type DepNameProps = { 5 | name: string, 6 | remote: string, 7 | box: ?string 8 | }; 9 | 10 | export default function parseDepName(depName: string): DepNameProps { 11 | const [remote, box, name] = depName.split(DEPENDENCY_DELIMITER); 12 | if (!name) { 13 | return { 14 | remote, 15 | name: box, 16 | box: null 17 | }; 18 | } 19 | 20 | return { 21 | name, 22 | remote, 23 | box 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /src/utils/object/has-own-property.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | 3 | /** 4 | * Determines whether the object has the specified property. 5 | * @name hasOwnProperty 6 | * @param {object} obj 7 | * @param {string|number} prop property to test 8 | * @returns {boolean} 9 | * @example 10 | * ```js 11 | * hasOwnProperty({foo: 'bar'}, 'foo') // => true 12 | * hasOwnProperty({foo: 'bar'}, 'bar') // => false 13 | * ``` 14 | */ 15 | export default function hasOwnProperty(obj: Object, prop: string|number) { 16 | return Object.prototype.hasOwnProperty.call(obj, prop); 17 | } 18 | -------------------------------------------------------------------------------- /src/utils/string/to-base64.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | 3 | /** 4 | * encode a string or a buffer to base64 5 | * @name toBase64 6 | * @param {string|Buffer} val string or buffer to encode 7 | * @returns {string} base64 encoded string 8 | * @example 9 | * ```js 10 | * toBase64('foo bar') // => Zm9vIGJhcg== 11 | * toBase64(new Buffer('foo bar')) // => Zm9vIGJhcg== 12 | * ``` 13 | */ 14 | export default function toBase64(val: string|Buffer) { 15 | if (val instanceof Buffer) return val.toString('base64'); 16 | return new Buffer(val).toString('base64'); 17 | } 18 | -------------------------------------------------------------------------------- /src/utils/object/foreach.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | 3 | /** 4 | * Invoke a function for every key within given object or array. 5 | * @name forEach 6 | * @param {object} obj object or array to iterate 7 | * @param {function} cb callback function to invoke 8 | * @example 9 | * ```js 10 | * forEach({ a: 1, b: 2, c: 3 }, (val, key) => console.log(key, val)); 11 | * // => a 1 b 2 c 3 12 | * ``` 13 | */ 14 | export default function forEach(obj: Object, cb: (val: any, key: any) => void) { 15 | const keys = Object.keys(obj); 16 | keys.forEach(key => cb(obj[key], key)); 17 | } 18 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "only": "*.js", 3 | "presets": [ 4 | ["env", { 5 | "targets": { 6 | "node": 4 7 | } 8 | }] 9 | ], 10 | "plugins": [ 11 | ["transform-flow-strip-types"], 12 | ["babel-plugin-transform-builtin-extend", { 13 | "globals": ["Error", "Array", "Map"] 14 | }], 15 | ["transform-runtime", { 16 | "helpers": false, 17 | "polyfill": false, 18 | "regenerator": true 19 | }], 20 | ["syntax-async-functions"], 21 | ["transform-class-properties"], 22 | ["transform-inline-imports-commonjs"] 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /src/cli/commands/private-cmds/cat-object-cmd.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import Command from '../../command'; 3 | import { catObject } from '../../../api/scope'; 4 | 5 | export default class CatObject extends Command { 6 | name = 'cat-object [hash]'; 7 | description = 'cat a bit object by hash'; 8 | private = true; 9 | alias = ''; 10 | opts = []; 11 | 12 | action([hash, ]: [string, ]): Promise { 13 | // @TODO - import should support multiple bits 14 | return catObject(hash); 15 | } 16 | 17 | report(file: any): string { 18 | return file.toString(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /scripts/bit.rb: -------------------------------------------------------------------------------- 1 | require "language/node" 2 | class Bit < Formula 3 | desc "Distributed Code Component Manager" 4 | homepage "https://www.bitsrc.io" 5 | url "" 6 | sha256 "" 7 | 8 | def install 9 | libexec.install Dir["*"] 10 | bin.install_symlink Dir["#{libexec}/bin/bit"] 11 | bin.install_symlink Dir["#{libexec}/bin/bit.js"] 12 | bin.install_symlink "#{libexec}/bin/node" => "bitNode" 13 | end 14 | 15 | test do 16 | assert_equal "successfully initialized an empty bit scope.\n", 17 | shell_output("#{bin}/bit init --skip-update") 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /src/utils/array/diff.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | 3 | /** 4 | * compute the difference between two array references. 5 | * @name diff 6 | * @param {[]} firstArray 7 | * @param {[]} secondArray 8 | * @returns {[]} returns an array representing the difference between the two arrays 9 | * @example 10 | * ```js 11 | * diff([1,2,3], [1,2,3,4,5]) // => [4,5] 12 | * ``` 13 | */ 14 | export default function diff(firstArray: any[], secondArray: any[]): any[] { 15 | return firstArray.concat(secondArray).filter((val) => { 16 | return !(firstArray.includes(val) && secondArray.includes(val)); 17 | }); 18 | } 19 | -------------------------------------------------------------------------------- /docs/scope.md: -------------------------------------------------------------------------------- 1 | Bit's Scope is one of the fundamental entities of Bit. A Scope is a dynamic codebase responsible for end-to-end management of code components. 2 | Scopes are where components are stored, tested, built and integrate with each other. 3 | 4 | Each component belongs in a scope (or multiple scopes). therefore, the scope's name appears in the component's ID path (which consists of owner/scope/box/component). 5 | 6 | The Scope's distributed nature is meant to enable teams to create scopes for every different level of abstraction inside the organization. 7 | For example, a team in an organization may want 8 | 9 | -------------------------------------------------------------------------------- /scripts/build-dist.ps1: -------------------------------------------------------------------------------- 1 | npm pack 2 | if (Test-Path distribution/windows) { 3 | rm distribution/windows -Recurse 4 | } 5 | $VERSION= $(node -p -e "require('./package.json').version") 6 | mkdir distribution 7 | mkdir distribution/windows 8 | mv bit-bin-$VERSION.tgz distribution/windows/ 9 | 10 | cd distribution/windows 11 | 12 | tar -xzf bit-bin-$VERSION.tgz --strip 1 13 | rm bit-bin-$VERSION.tgz 14 | npm install -g bit-bin --no-optional --unsafe 15 | npm install --no-optional 16 | bit import 17 | npm run build 18 | rm -r node_modules 19 | npm install --production --no-optional 20 | mv scripts/windows/bit.cmd bin/ 21 | -------------------------------------------------------------------------------- /docs/old/advanced.md: -------------------------------------------------------------------------------- 1 | # js-docs parsing 2 | 3 | Parsing the Docs yield useful information, such as the description of the component, its arguments, return type and usage examples. 4 | Other parts of the system, the Search, in particular, use that information for a better understanding what a component does. 5 | 6 | The JS Docs get discovered by a Regex pattern, and parsing the docs is done by [Doctrine](https://github.com/eslint/doctrine). 7 | 8 | # advanced testing 9 | 10 | working on it ... 11 | 12 | # resolution algorithm 13 | 14 | working on it ... 15 | 16 | # component debugging 17 | 18 | working on it ... 19 | -------------------------------------------------------------------------------- /src/utils/fs/list-directories.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import path from 'path'; 3 | import fs from 'fs'; 4 | 5 | /** 6 | * synchronous component for listing directory contents. 7 | * @param {string} dirPath target dir 8 | * @returns {string[]} array representing directory contents 9 | * @example 10 | * ```js 11 | * listDirectories('/usr/local/foo/bar') //=> ['foo.html', 'bar.css'] 12 | * ``` 13 | */ 14 | export default function listDirectories(dirPath: string): string[] { 15 | return fs.readdirSync(dirPath).filter((file) => { 16 | return fs.statSync(path.join(dirPath, file)).isDirectory(); 17 | }); 18 | } 19 | -------------------------------------------------------------------------------- /src/utils/object/to-tuple-array.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import forEach from '../object/foreach'; 3 | 4 | /** 5 | * cast an object to tupple array. 6 | * @name objectToTupleArray 7 | * @param {*} obj 8 | * @returns [[string, *]] tuple array representing given object 9 | * @example 10 | * ```js 11 | * objectToTupleArray({foo: 'bar', bar: 'foo'}) // => [['foo', 'bar'], ['bar', 'foo']] 12 | * ``` 13 | */ 14 | export default function objectToTupleArray(obj: {[string]: any}): [string, any][] { 15 | const arr = []; 16 | forEach(obj, (val, key) => { 17 | arr.push([key, val]); 18 | }); 19 | return arr; 20 | } 21 | -------------------------------------------------------------------------------- /src/utils/map/to-object.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | 3 | /** 4 | * Cast a `Map` to a plain object. 5 | * Keys are being casted by invoking `toString` on each key. 6 | * @name mapToObject 7 | * @param {Map} map to cast 8 | * @returns {*} plain object 9 | * @example 10 | * ```js 11 | * mapToObject(new Map([['key', 'val'], ['foo', 'bar']])); 12 | * // => { key: 'val', foo: 'bar' } 13 | * ``` 14 | */ 15 | export default function mapToObject(map: Map): {[string|number]: any} { 16 | const object = {}; 17 | map.forEach((val, key) => { 18 | object[key.toString()] = val; 19 | }); 20 | return object; 21 | } 22 | -------------------------------------------------------------------------------- /specs/jsdoc/fixtures/properties.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @namespace 3 | * @property {object} defaults - The default values for parties. 4 | * @property {number} defaults.players - The default number of players. 5 | * @property {string} defaults.level - The default level for the party. 6 | * @property {object} defaults.treasure - The default treasure. 7 | * @property {number} defaults.treasure.gold - How much gold the party starts with. 8 | */ 9 | var config = { 10 | defaults: { 11 | players: 1, 12 | level: 'beginner', 13 | treasure: { 14 | gold: 0 15 | } 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /src/scope/network/network-lib.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import SSH from './ssh'; 3 | import Fs from './fs'; 4 | import type { Network } from './network'; 5 | import { ProtocolNotSupported } from './exceptions'; 6 | import { isBitUrl, parseSSHUrl } from '../../utils'; 7 | 8 | export default function connect(host: string): Promise { 9 | if (host.startsWith('ssh://') || host.startsWith('bit://')) { 10 | return new SSH(parseSSHUrl(host)).connect(); 11 | } 12 | 13 | if (host.startsWith('file://')) { 14 | return new Fs(host.replace('file://', '')).connect(); 15 | } 16 | 17 | throw new ProtocolNotSupported(); 18 | } 19 | -------------------------------------------------------------------------------- /specs/jsdoc/fixtures/variousParamTypes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Testing various params options 3 | * 4 | * @param {*} anyType 5 | * @param {[]} arrayType 6 | * @param {number|[]} unionType 7 | * @param {CustomType} myCustomType 8 | * @param {Object} objectType 9 | * @param {Function} functionType The function to attempt. 10 | * @param {string[]} arrayOfType 11 | * @param {Array} arrayOfUnion 12 | * 13 | */ 14 | function foo(anyType, arrayType, unionType, myCustomType, objectType, functionType, arrayOfType, arrayOfUnion) { 15 | // ... 16 | } 17 | 18 | class CustomType {} 19 | 20 | module.exports = { 21 | add, 22 | }; 23 | 24 | -------------------------------------------------------------------------------- /src/cli/templates/list-template.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import c from 'chalk'; 3 | import Table from 'cli-table2'; 4 | import ConsumerComponent from '../../consumer/component/consumer-component'; 5 | 6 | export default (components: ConsumerComponent[]) => { 7 | const table = new Table({ 8 | head: [c.cyan('ID'), c.cyan('Version')], 9 | colWidths: [46, 9], 10 | wordWrap: true, 11 | }); 12 | 13 | function tablizeComponent(component) { 14 | return [`${component.box}/${component.name}`, component.version]; // Add date, author 15 | } 16 | 17 | table.push(...components.map(tablizeComponent)); 18 | return table.toString(); 19 | }; 20 | -------------------------------------------------------------------------------- /src/utils/object-clean.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import forEach from './object/foreach'; 3 | 4 | /** 5 | * Cleans all object's properties that contains a falsy value 6 | * and returns a new object without them. 7 | * @name clean 8 | * @param {object} obj object to clean 9 | * @returns {object} 10 | * @example new cleaned object 11 | * ```js 12 | * clean({ foo: null, bar: 'foo' }) // => { bar: 'foo' } 13 | * ``` 14 | */ 15 | export default function clean(obj: {[any]: any}) { 16 | const newObj = {}; 17 | 18 | forEach(obj, (val, key) => { 19 | if (!val) return; 20 | newObj[key] = val; 21 | }); 22 | 23 | return newObj; 24 | } 25 | -------------------------------------------------------------------------------- /src/scope/models/source.js: -------------------------------------------------------------------------------- 1 | import { BitObject } from '../objects'; 2 | 3 | export default class Source extends BitObject { 4 | contents: Buffer; 5 | 6 | constructor(contents: Buffer) { 7 | super(); 8 | this.contents = contents; 9 | } 10 | 11 | id() { 12 | return this.contents; 13 | } 14 | 15 | toBuffer() { 16 | return this.contents; 17 | } 18 | 19 | toString() { 20 | return this.contents.toString(); 21 | } 22 | 23 | static parse(contents: Buffer): Source { 24 | return new Source(contents); 25 | } 26 | 27 | static from(buffer: Buffer): Source { 28 | return new Source(buffer); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/search/stopwords.js: -------------------------------------------------------------------------------- 1 | const words = ['about', 'all', 'also', 'am', 'an', 'another', 'are', 'at', 'be', 'because', 2 | 'been', 'before', 'being', 'both', 'but', 'came', 'come', 'could', 'did', 'do', 'each', 'for', 3 | 'from', 'had', 'he', 'her', 'here', 'him', 'himself', 'his', 'how', 'if', 'it', 'make', 'me', 4 | 'might', 'much', 'must', 'my', 'never', 'now', 'of', 'other', 'our', 'over', 'said', 'see', 5 | 'some', 'such', 'than', 'that', 'the', 'their', 'them', 'there', 'these', 'they', 'those', 6 | 'through', 'too', 'very', 'way', 'we', 'well', 'were', 'what', 'who', 'would', 'you', 'your', 7 | 'a', 'i']; 8 | 9 | export default words; 10 | -------------------------------------------------------------------------------- /src/utils/object/empty.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import hasOwnProperty from './has-own-property'; 3 | 4 | /** 5 | * determines whether `obj` reference is empty (empty array, empty object and/or falsy values) 6 | * @name empty 7 | * @param {*} name 8 | * @returns {boolean} 9 | * @example 10 | * ```js 11 | * empty([]) // => true 12 | * empty({}) // => true 13 | * empty(1) // => false 14 | * empty('') // => false 15 | * empty('foo') // => true 16 | * ``` 17 | */ 18 | export default function empty(obj: any): boolean { 19 | for (const n in obj) if (hasOwnProperty(obj, n) && obj[n]) return false; // eslint-disable-line 20 | return true; 21 | } 22 | -------------------------------------------------------------------------------- /src/scope/network/exceptions/index.js: -------------------------------------------------------------------------------- 1 | import ProtocolNotSupported from './protocol-not-supported'; 2 | import FsScopeNotLoaded from './fs-scope-not-loaded'; 3 | import RemoteScopeNotFound from './remote-scope-not-found'; 4 | import SSHConnectionError from './ssh-connection-error'; 5 | import PermissionDenied from './permission-denied'; 6 | import NetworkError from './network-error'; 7 | import UnexpectedNetworkError from './unexpected-network-error'; 8 | 9 | export { 10 | ProtocolNotSupported, 11 | NetworkError, 12 | UnexpectedNetworkError, 13 | PermissionDenied, 14 | FsScopeNotLoaded, 15 | RemoteScopeNotFound, 16 | SSHConnectionError 17 | }; 18 | -------------------------------------------------------------------------------- /src/cli/commands/private-cmds/refresh-scope-cmd.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import Command from '../../command'; 3 | import { refreshScope } from '../../../api/scope'; 4 | 5 | export default class RefershScope extends Command { 6 | name = 'refresh-scope [scopePath]'; 7 | description = 'load all the object in the scope and write them again (for possible model changes between versions)'; 8 | alias = ''; 9 | opts = []; 10 | private = true; 11 | 12 | action([scopePath, ]: [string, ?string]): Promise { 13 | return refreshScope(scopePath || process.cwd()); 14 | } 15 | 16 | report(): string { 17 | return 'scope updated successfully'; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/cli/commands/public-cmds/watch-cmd.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import Command from '../../command'; 3 | import { watch, watchAll } from '../../../api/consumer'; 4 | 5 | export default class Create extends Command { 6 | name = 'watch'; 7 | description = 'watch components and perform `build` on changes'; 8 | alias = 'w'; 9 | opts = [ 10 | ['v', 'verbose', 'showing npm verbose output for inspection'], 11 | ]; 12 | loader = true; 13 | 14 | action(args: string[], { verbose } : { 15 | verbose: ?bool, 16 | }): Promise<*> { 17 | return watchAll(verbose); 18 | } 19 | 20 | report(): string { 21 | return 'watcher terminated'; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/hooks/http-client/client.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | const requestify = require('requestify'); 3 | 4 | const client = { 5 | POST: (url: string, data: Object|string): Promise => { 6 | return requestify.post(url, data); 7 | }, 8 | GET: (url: string): Promise => { 9 | return requestify.get(url); 10 | }, 11 | DEL: (url: string): Promise => { 12 | return requestify.delete(url); 13 | }, 14 | PUT: (url: string, data: Object|string): Promise => { 15 | return requestify.put(url, data); 16 | }, 17 | HEAD: (url: string): Promise => { 18 | return requestify.head(url); 19 | } 20 | }; 21 | 22 | export default client; 23 | -------------------------------------------------------------------------------- /src/utils/fs/file-info.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import path from 'path'; 3 | 4 | /** 5 | * get the current working dir name of file and file name. 6 | * @name fileInfo 7 | * @param file relative path 8 | * @returns {object} 9 | * @example 10 | * ```js 11 | * currentDirName() // => 'bit' 12 | * ``` 13 | */ 14 | export default function calculateFileInfo(relativePath: string) { 15 | const fileInfo = path.parse(relativePath); 16 | const fullPath = path.dirname(relativePath); 17 | const rootDir = path.dirname(fullPath); 18 | const parentDir = path.relative(rootDir, fullPath); 19 | return { PARENT_FOLDER: parentDir, FILE_NAME: fileInfo.name }; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/scope/network/network.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import { BitIds, BitId } from '../../bit-id'; 3 | import ComponentObjects from '../../scope/component-objects'; 4 | import type { ScopeDescriptor } from '../scope'; 5 | 6 | export interface Network { 7 | connect(host: string): Network; 8 | close(): void; 9 | get(commandName: string): Promise; 10 | describeScope(): Promise; 11 | fetch(bitIds: BitIds): Promise; 12 | list(): Promise; 13 | search(query: string, reindex: boolean): Promise; 14 | show(bitId: BitId): Promise<>; 15 | fetchOnes(bitIds: BitIds): Promise; 16 | } 17 | -------------------------------------------------------------------------------- /src/utils/object/filter.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import forEach from './foreach'; 3 | 4 | /** 5 | * create a new array with all elements that pass the test implemented by the provided function. 6 | * @name filter 7 | * @param {object} obj object or array to iterate 8 | * @param {function} cb callback function to invoke 9 | * @example 10 | * ```js 11 | * filter({ a: 1, b: 2, c: 3 }, (val, key) => val === 1) // => { a: 1 } 12 | * ``` 13 | */ 14 | export default function filter(obj: Object, cb: (val: any, key: any) => boolean) { 15 | const newObj = {}; 16 | 17 | forEach(obj, (val, key) => { 18 | if (cb(val, key)) newObj[key] = val; 19 | }); 20 | 21 | return obj; 22 | } 23 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | machine: 2 | node: 3 | version: 6 4 | 5 | dependencies: 6 | pre: 7 | - "echo \"-----BEGIN RSA PRIVATE KEY-----\" >> /home/ubuntu/.ssh/id_rsa" 8 | - echo ${bithubKey} >> /home/ubuntu/.ssh/id_rsa 9 | - "echo \"-----END RSA PRIVATE KEY-----\" >> /home/ubuntu/.ssh/id_rsa" 10 | 11 | override: 12 | - npm install 13 | 14 | test: 15 | override: 16 | - npm run build 17 | - npm run test-circle: 18 | environment: 19 | MOCHA_FILE: $CIRCLE_TEST_REPORTS/junit/unit-test-results.xml 20 | - npm i -g 21 | - npm run e2e-test-circle: 22 | environment: 23 | MOCHA_FILE: $CIRCLE_TEST_REPORTS/junit/e2e-test-results.xml 24 | 25 | -------------------------------------------------------------------------------- /scripts/build-tar.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -ex 3 | 4 | ./scripts/node-installer.sh $1 5 | rm -rf ./*.tar.gz 6 | rm -rf ./distribution 7 | ver=$(cat ./package.json | grep version | head -1 | awk -F: '{ print $2 }' | sed 's/[",]//g' | xargs echo -n) 8 | tarName="bit-${ver}.tar.gz" 9 | 10 | npm install 11 | npm run build 12 | npm prune --production 13 | 14 | umask 0022 15 | #set package json with corret packeing type 16 | packageDest=$(cd $(dirname "$1") && pwd -P)/$(basename "package.json") 17 | node ./scripts/set-installation-method.js $packageDest tar 18 | tar --exclude='./Jenkinsfile' --exclude='./distribution/' --exclude='./scripts/' -zcvf ${tarName} * 19 | shasum -a 256 -b $tarName 20 | -------------------------------------------------------------------------------- /specs/jsdoc/fixtures/descriptionTag.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @name invalidDescription 3 | * Adds two numbers. 4 | * @public 5 | */ 6 | function invalidDescription() { 7 | // the description is not the first and it doesn't have the description tag. 8 | } 9 | 10 | 11 | /** 12 | * @name descriptionTag 13 | * @description Adds two numbers. 14 | * @public 15 | */ 16 | function descriptionTag() { 17 | // the description is not the first and it doesn't have the description tag. 18 | } 19 | 20 | 21 | /** 22 | * @name descTag 23 | * @desc Adds two numbers. 24 | * @public 25 | */ 26 | function descTag() { 27 | // the description is not the first and it doesn't have the description tag. 28 | } 29 | -------------------------------------------------------------------------------- /src/consumer/component/sources/specs.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import Source from './source'; 3 | import BitId from '../../../bit-id'; 4 | import createTemplate from '../templates/specs.default-template'; 5 | import { Scope } from '../../../scope'; 6 | 7 | export default class Specs extends Source { 8 | 9 | static create(name: string, testerId: BitId, scope: Scope): Specs { 10 | function getTemplate() { 11 | try { 12 | const testerModule = scope.loadEnvironment(testerId); 13 | return testerModule.getTemplate(name); 14 | } catch (e) { 15 | return createTemplate({ name }); 16 | } 17 | } 18 | 19 | return new Specs(getTemplate()); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /scripts/node-installer.ps1: -------------------------------------------------------------------------------- 1 | $url = "https://nodejs.org/dist/v6.10.0/node-v6.10.0-win-x64.zip" 2 | $zipName = "node-v6.10.0-win-x64.zip" 3 | 4 | if (Test-Path distribution/windowsNode) { 5 | rm distribution/windowsNode -Recurse 6 | } 7 | Invoke-WebRequest -Uri $url -OutFile $zipName 8 | 9 | Get-ChildItem -Force . 10 | mkdir distribution 11 | mkdir distribution/windowsNode 12 | mv $PSScriptRoot/"../node-v6.10.0-win-x64.zip" $PSScriptRoot/"../distribution/windowsNode/" 13 | cd $PSScriptRoot"/../distribution/windowsNode" 14 | Get-ChildItem -Force . 15 | unzip node-v6.10.0-win-x64.zip 16 | Get-ChildItem -Force . 17 | mv node-v6.10.0-win-x64/node.exe ../../bin/ 18 | cd ../../ 19 | rm -r distribution 20 | -------------------------------------------------------------------------------- /src/cli/commands/private-cmds/_scope-cmd.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import Command from '../../command'; 3 | import { describeScope } from '../../../api/scope'; 4 | import { fromBase64, empty, buildCommandMessage, packCommand } from '../../../utils'; 5 | 6 | export default class Prepare extends Command { 7 | name = '_scope '; 8 | description = 'describe a scope'; 9 | private = true; 10 | alias = ''; 11 | opts = []; 12 | 13 | action([path, ]: [string, string]): Promise<*> { 14 | return describeScope(fromBase64(path)); 15 | } 16 | 17 | report(scopeObj: any): string { 18 | if (empty(scopeObj)) return ''; 19 | return packCommand(buildCommandMessage(scopeObj)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/cli/commands/public-cmds/clear-cache-cmd.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import chalk from 'chalk'; 3 | import fs from 'fs-extra'; 4 | import Command from '../../command'; 5 | 6 | const { MODULES_CACHE_DIR } = require('../../../constants'); 7 | 8 | export default class ClearCache extends Command { 9 | name = 'clear-cache'; 10 | description = 'clears Bit\'s cache from current working machine'; 11 | alias = 'cc'; 12 | opts = []; 13 | loader = false; 14 | 15 | action(): Promise { 16 | fs.removeSync(MODULES_CACHE_DIR); 17 | fs.mkdirsSync(MODULES_CACHE_DIR); 18 | return Promise.resolve(); 19 | } 20 | 21 | report(): string { 22 | return chalk.green('Cache was cleared'); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/scope/objects/ref.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import Repository from './repository'; 3 | import BitObject from './object'; 4 | 5 | export default class Ref { 6 | hash: string; 7 | 8 | constructor(hash: string) { 9 | this.hash = hash; 10 | } 11 | 12 | toString() { 13 | return this.hash; 14 | } 15 | 16 | load(repository: Repository): Promise { 17 | return repository.findOne(this); 18 | } 19 | 20 | loadSync(repo: Repository): BitObject { 21 | return repo.loadSync(this); 22 | } 23 | 24 | loadRaw(repo: Repository): Promise { 25 | return repo.loadRaw(this); 26 | } 27 | 28 | static from(hash: string): Ref { 29 | return new Ref(hash); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Bit - Distributed code component manager. 2 | 3 | Copyright (C) 2014-2017 Cocycles LTD. 4 | 5 | Can be contacted at: team@bitsrc.io 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | -------------------------------------------------------------------------------- /src/utils/remove-containing-dir-if-empty.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import path from 'path'; 3 | import fs from 'fs-extra'; 4 | import glob from 'glob'; 5 | import R from 'ramda'; 6 | 7 | module.exports = function removeContainingDirIfEmpty(componentDir: string): Promise { 8 | return new Promise((resolve, reject) => { 9 | const containingDir = path.dirname(componentDir); 10 | 11 | return glob(path.join(containingDir, '*'), (err, matches) => { 12 | if (err) return reject(err); 13 | if (R.isEmpty(matches)) { 14 | return fs.remove(containingDir, (e) => { 15 | if (e) return reject(e); 16 | return resolve(); 17 | }); 18 | } 19 | 20 | return resolve(); 21 | }); 22 | }); 23 | }; 24 | -------------------------------------------------------------------------------- /src/scope/network/ssh/key-getter.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import * as fs from 'fs'; 3 | import { getSync } from '../../../api/consumer/lib/global-config'; 4 | import { CFG_SSH_KEY_FILE_KEY, DEFAULT_SSH_KEY_FILE } from '../../../constants'; 5 | 6 | function getPathToIdentityFile() { 7 | const identityFile = getSync(CFG_SSH_KEY_FILE_KEY); 8 | return identityFile || DEFAULT_SSH_KEY_FILE; 9 | } 10 | 11 | function readKey(keyPath: ?string) { 12 | if (!keyPath) return ''; 13 | 14 | try { 15 | return fs.readFileSync(keyPath); 16 | } catch (e) { 17 | return ''; 18 | } 19 | } 20 | 21 | export default function keyGetter(keyPath: ?string) { 22 | if (keyPath) return readKey(keyPath); 23 | return readKey(getPathToIdentityFile()); 24 | } 25 | -------------------------------------------------------------------------------- /src/consumer/component/sources/impl.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import Source from './source'; 3 | import createTemplate from '../templates/impl.default-template'; 4 | import BitId from '../../../bit-id'; 5 | import { Scope } from '../../../scope'; 6 | 7 | export default class Impl extends Source { 8 | 9 | static create(name: string, compilerId: BitId, scope: Scope): Impl { 10 | function getTemplate() { 11 | try { 12 | if (!compilerId) return createTemplate({ name }); 13 | const testerModule = scope.loadEnvironment(compilerId); 14 | return testerModule.getTemplate(name); 15 | } catch (e) { 16 | return createTemplate({ name }); 17 | } 18 | } 19 | 20 | return new Impl(getTemplate()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/remotes/local-scope.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import { Scope } from '../scope'; 3 | import { BitId } from '../bit-id'; 4 | import { LOCAL_SCOPE_NOTATION } from '../constants'; 5 | import Bit from '../consumer/component'; 6 | 7 | export default class LocalScope { 8 | scope: Scope; 9 | host: string; 10 | 11 | constructor(scope: Scope) { 12 | this.scope = scope; 13 | this.host = LOCAL_SCOPE_NOTATION; 14 | } 15 | 16 | fetch(bitIds: BitId[]): Promise { 17 | const promises = bitIds.map(bitId => this.scope.get(bitId)); 18 | return Promise.all(promises); 19 | } 20 | 21 | getHost() { 22 | return this.host; 23 | } 24 | 25 | toPlainObject() { 26 | return { 27 | alias: LOCAL_SCOPE_NOTATION 28 | }; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/cli/command.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | 3 | export default class Cmd { 4 | name: string; 5 | description: string; 6 | alias: string; 7 | opts: [string, string, string][]; 8 | commands: Cmd[] = []; 9 | private: ?bool; 10 | loader: ?bool; 11 | 12 | action(params: any, opts: {[string]: any}): Promise<{[string]: any}> { // eslint-disable-line 13 | console.log('"action" method not implemented on this command'); 14 | return new Promise(resolve => resolve({})); 15 | } 16 | 17 | report(data: any): string { // eslint-disable-line 18 | return '"report" method not implemented on this command'; 19 | } 20 | 21 | handleError(): ?string { 22 | return null; 23 | } 24 | 25 | splitList(val) { 26 | return val.split(','); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/cli/commands/private-cmds/_show-cmd.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import Command from '../../command'; 3 | import { fromBase64, unpackCommand, packCommand, buildCommandMessage } from '../../../utils'; 4 | import { scopeShow } from '../../../api/scope'; 5 | 6 | export default class _Show extends Command { 7 | name = '_show '; 8 | private = true; 9 | description = 'show a specific component on scope'; 10 | alias = ''; 11 | opts = []; 12 | 13 | action([path, args]: [string, string]): Promise { 14 | const { payload, headers } = unpackCommand(args); 15 | // validateVersion(headers) 16 | return scopeShow(fromBase64(path), payload); 17 | } 18 | 19 | report(str: string): string { 20 | return packCommand(buildCommandMessage(str)); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/utils/map-object.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | export type Iteratee = (val: any, key: any) => any; 3 | 4 | /** 5 | * Returns the results of applying the iteratee to each element of the object. 6 | * @param {object} object 7 | * @param {function(val, key)} iteratee 8 | * @returns {object} 9 | * @example 10 | * ```js 11 | * const newObj = mapObject({ start: 5, end: 12 }, function(val, key) { 12 | * return val + 5; 13 | * }); 14 | * console.log(newObj) // { start: 10, end: 17 } 15 | * ``` 16 | */ 17 | export default function mapObject(obj: Object, iteratee: Iteratee) { 18 | const keys = Object.keys(obj); 19 | const mappedObject = {}; 20 | 21 | keys.forEach((key) => { 22 | mappedObject[key] = iteratee(obj[key], key); 23 | }); 24 | 25 | return mappedObject; 26 | } 27 | -------------------------------------------------------------------------------- /src/api/scope/lib/resolver.js: -------------------------------------------------------------------------------- 1 | import { loadScope } from '../../../scope'; 2 | 3 | export const setResolver = (currentPath: string, resolverPath: string): Promise => { 4 | return loadScope(currentPath) 5 | .then((scope) => { 6 | scope.scopeJson.resolverPath = resolverPath; 7 | return scope.scopeJson.write(scope.getPath()); 8 | }); 9 | }; 10 | 11 | export const getResolver = (currentPath: string): Promise => { 12 | return loadScope(currentPath) 13 | .then(scope => scope.scopeJson.resolverPath); 14 | }; 15 | 16 | export const resetResolver = (currentPath: string): Promise => { 17 | return loadScope(currentPath) 18 | .then((scope) => { 19 | scope.scopeJson.resolverPath = null; 20 | return scope.scopeJson.write(scope.getPath()); 21 | }); 22 | }; 23 | -------------------------------------------------------------------------------- /src/utils/fs-write-file.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import { userInfo } from 'os'; 3 | import { outputFile } from 'fs-extra'; 4 | import chown from './fs-chown'; 5 | import promisify from './promisify'; 6 | 7 | export type Options = { 8 | uid?: ?number, 9 | gid?: ?number 10 | }; 11 | 12 | export default function writeFile( 13 | path: string, 14 | contents: string|Buffer, 15 | options?: Options = {}): Promise { 16 | return promisify(outputFile)(path, contents, options) 17 | .then(() => { 18 | if (options.gid || options.uid) { 19 | const user = userInfo(); 20 | return chown( 21 | path, 22 | options.uid || user.uid, 23 | options.gid || user.gid 24 | ); 25 | } 26 | 27 | return Promise.resolve(); 28 | }); 29 | } 30 | -------------------------------------------------------------------------------- /src/cli/commands/private-cmds/_list-cmd.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import Command from '../../command'; 3 | import { fromBase64, unpackCommand, packCommand, buildCommandMessage } from '../../../utils'; 4 | import { scopeList } from '../../../api/scope'; 5 | 6 | export default class List extends Command { 7 | name = '_list '; 8 | private = true; 9 | description = 'list scope components'; 10 | alias = ''; 11 | opts = []; 12 | 13 | action([path, args]: [string, string]): Promise { 14 | const { headers } = unpackCommand(args); 15 | // validateVersion(headers) 16 | return scopeList(fromBase64(path)) 17 | .then(components => components.map(c => c.toString())); 18 | } 19 | 20 | report(str: string): string { 21 | return packCommand(buildCommandMessage(str)); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/utils/fs-remove-file.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import fs from 'fs-extra'; 3 | import pathlib from 'path'; 4 | 5 | export default function removeFile(path: string, propogateDirs: boolean = false): Promise { 6 | return new Promise((resolve, reject) => { 7 | fs.unlink(path, (err, res) => { 8 | if (err) return reject(err); 9 | if (propogateDirs) { 10 | const { dir } = pathlib.parse(path); 11 | fs.readdir(dir, (e, files) => { 12 | if (e) return reject(err); 13 | if (files.length !== 0) return resolve(res); 14 | return fs.remove(dir, (error) => { 15 | if (err) return reject(error); 16 | resolve(res); 17 | }); 18 | }); 19 | } else { 20 | return resolve(res); 21 | } 22 | }); 23 | }); 24 | } 25 | -------------------------------------------------------------------------------- /src/utils/array/split-by.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | 3 | /** 4 | * splits an array to two chunks using a conditional predicate function. 5 | * @name splitBy 6 | * @param {[*]} array array to split. 7 | * @param {() => boolean} fn predicate function to test each element of the array. 8 | * @returns {[[], []]} two new arrays with the elements that failed the test from the left. 9 | * @example 10 | * ```js 11 | * splitBy([1, 2, 3, 4, 5], isEven) // => [[1, 3, 5], [2, 4]] 12 | * ``` 13 | */ 14 | function splitBy(array: [], fn: (elm: any) => boolean): [Array, Array] { 15 | const truthy = []; 16 | const falsy = []; 17 | 18 | array.forEach((elm) => { 19 | if (fn(elm)) truthy.push(elm); 20 | else falsy.push(elm); 21 | }); 22 | 23 | return [falsy, truthy]; 24 | } 25 | 26 | module.exports = splitBy; 27 | -------------------------------------------------------------------------------- /specs/environment/environment.spec.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import sinon from 'sinon'; 3 | import Environment from '../../src/environment'; 4 | import { Scope } from '../../src/scope'; 5 | 6 | describe('Environment', () => { 7 | let sandbox; 8 | beforeEach(() => { 9 | sandbox = sinon.sandbox.create(); 10 | }); 11 | afterEach(() => { 12 | sandbox.restore(); 13 | }); 14 | 15 | describe('constructor', () => { 16 | it.skip('should generate a unique path for every instance', () => { 17 | sandbox.stub(Scope, 'load').returns({ getPath: () => '' }); 18 | const scope = Scope.load(); 19 | const environment1 = new Environment(scope); 20 | const environment2 = new Environment(scope); 21 | expect(environment1.path).not.to.be.equal(environment2.path); 22 | }); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/cli/commands/public-cmds/remove-cmd.js: -------------------------------------------------------------------------------- 1 | // /** @flow */ 2 | // import chalk from 'chalk'; 3 | // import Command from '../command'; 4 | // import { remove } from '../../api'; 5 | 6 | // export default class Remove extends Command { 7 | // name = 'rm '; 8 | // description = 'remove a bit'; 9 | // alias = ''; 10 | // opts = [ 11 | // ['i', 'inline', 'remove inline bit'] 12 | // ]; 13 | 14 | // action([name]: [string], opts: any): Promise { 15 | // return remove(name, opts) 16 | // .then(() => ({ 17 | // name, 18 | // inline: opts.inline 19 | // })); 20 | // } 21 | 22 | // report({ name, inline }: any): string { 23 | // const pathTo: string = inline ? 'inline' : 'external'; 24 | // return chalk.green(`removed the bit "${name}" from the ${pathTo} directory`); 25 | // } 26 | // } 27 | -------------------------------------------------------------------------------- /src/consumer/component/sources/license.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import * as fs from 'fs'; 3 | import * as path from 'path'; 4 | import Source from './source'; 5 | import { LICENSE_FILENAME } from '../../../constants'; 6 | 7 | export default class License extends Source { 8 | 9 | write(bitPath: string, force?: boolean = true): Promise { 10 | const filePath = path.join(bitPath, LICENSE_FILENAME); 11 | if (!force && fs.existsSync(filePath)) return Promise.resolve(); 12 | return new Promise((resolve, reject) => 13 | fs.writeFile(filePath, this.src, (err, res) => { 14 | if (err) return reject(err); 15 | return resolve(res); 16 | }) 17 | ); 18 | } 19 | 20 | serialize() { 21 | return this.src; 22 | } 23 | 24 | static deserialize(str: string) { 25 | return new License(str); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/scope/exceptions/index.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import ScopeNotFound from './scope-not-found'; 3 | import ScopeAlreadyExists from './scope-already-exists'; 4 | import SourceNotFound from './source-not-found'; 5 | import BitNotInScope from './bit-not-in-scope'; 6 | import MergeConflict from './merge-conflict'; 7 | import ComponentNotFound from './component-not-found'; 8 | import VersionNotFound from './version-not-found'; 9 | import HashNotFound from './hash-not-found'; 10 | import ResolutionException from './resolution-exception'; 11 | import DependencyNotFound from './dependency-not-found'; 12 | 13 | export { 14 | ScopeNotFound, 15 | ComponentNotFound, 16 | SourceNotFound, 17 | HashNotFound, 18 | MergeConflict, 19 | VersionNotFound, 20 | ScopeAlreadyExists, 21 | BitNotInScope, 22 | ResolutionException, 23 | DependencyNotFound, 24 | }; 25 | -------------------------------------------------------------------------------- /book.js: -------------------------------------------------------------------------------- 1 | const pkg = require('./package.json'); 2 | 3 | module.exports = { 4 | // Documentation for GitBook is stored under "docs" 5 | root: './docs', 6 | title: 'Bit - distributed component manager', 7 | structure: { 8 | readme: 'INTRODUCTION.md', 9 | summary: 'SUMMARY.md' 10 | }, 11 | author: 'teambit', 12 | language: 'en', 13 | gitbook: '>= 3.2.0', 14 | plugins: ['sitemap', 'prism', '-highlight'], 15 | variables: { 16 | version: pkg.version 17 | }, 18 | pluginsConfig: { 19 | sitemap: { 20 | hostname: 'https://teambit.github.io/bit/' 21 | }, 22 | sharing: { 23 | facebook: true, 24 | twitter: true, 25 | google: false, 26 | weibo: false, 27 | instapaper: false, 28 | vk: false, 29 | all: [ 'facebook', 'google', 'twitter', 'weibo', 'instapaper' ] 30 | } 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /src/api/scope/lib/scope-config.js: -------------------------------------------------------------------------------- 1 | import { loadScope } from '../../../scope'; 2 | 3 | function set(key: string, value: string): Promise { 4 | return loadScope() 5 | .then((scope) => { 6 | scope.scopeJson.set(key, value); 7 | return scope.scopeJson.write(process.cwd()).then(() => ({ key, value })); 8 | }); 9 | } 10 | 11 | function get(key: string): Promise { 12 | return loadScope() 13 | .then(scope => scope.scopeJson.get(key)); 14 | } 15 | 16 | function del(key: string): Promise { 17 | return loadScope() 18 | .then((scope) => { 19 | scope.scopeJson.del(key); 20 | return scope.scopeJson.write(process.cwd()); 21 | }); 22 | } 23 | 24 | function list(): Promise { 25 | return loadScope().then(scope => scope.scopeJson.toPlainObject()); 26 | } 27 | 28 | 29 | module.exports = { set, get, del, list }; 30 | -------------------------------------------------------------------------------- /src/cli/commands/private-cmds/_search-cmd.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import Command from '../../command'; 3 | import { fromBase64, unpackCommand, buildCommandMessage, packCommand } from '../../../utils'; 4 | import { searchAdapter } from '../../../search'; 5 | import { Doc } from '../../../search/indexer'; 6 | 7 | export default class Search extends Command { 8 | name = '_search '; 9 | private = true; 10 | description = 'search for components on a remote scope'; 11 | alias = ''; 12 | opts = []; 13 | 14 | action([path, args]: [string, string]): Promise { 15 | const { payload } = unpackCommand(args); 16 | return searchAdapter.scopeSearch(fromBase64(path), payload.query, payload.reindex === 'true'); 17 | } 18 | 19 | report(searchResults: Array): string { 20 | return packCommand(buildCommandMessage(searchResults)); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/cli/commands/public-cmds/reset-cmd.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import chalk from 'chalk'; 3 | import Command from '../../command'; 4 | import { reset } from '../../../api/consumer'; 5 | 6 | export default class Reset extends Command { 7 | name = 'reset '; 8 | description = 'revert a component version to previous one.'; 9 | alias = ''; 10 | opts = []; 11 | loader = true; 12 | 13 | action([id, ]: [string, ]): Promise { 14 | return reset({ id }) 15 | .then((component) => { 16 | return { 17 | name: component.name, 18 | box: component.box 19 | }; 20 | }); 21 | } 22 | 23 | report({ name, box }: { name: string, box: string, path: string }): string { 24 | return chalk.white('put back ') + 25 | chalk.magenta(`"${box}/${name}"`) + 26 | chalk.white(' in inline_components directory'); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/cli/loader/loader-messages.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | 3 | export const BEFORE_REMOTE_SHOW = 'fetching remote component'; 4 | export const BEFORE_IMPORT_ENVIRONMENT = 'importing environment dependencies...'; 5 | export const BEFORE_REMOTE_LIST = 'listing remote components'; 6 | export const BEFORE_RESET_ACTION = 'resetting component'; 7 | export const BEFORE_IMPORT_ACTION = 'importing components'; 8 | export const BEFORE_REMOTE_SEARCH = ({ scope, queryStr }: { scope: string, queryStr: string }) => 9 | `searching remote scope <${scope}> for '${queryStr}'`; 10 | export const BEFORE_RUNNING_SPECS = 'running specs'; 11 | export const BEFORE_IMPORT_PUT_ON_SCOPE = 'importing components'; 12 | export const BEFORE_PERSISTING_PUT_ON_SCOPE = 'persisting...'; 13 | export const BEFORE_INSTALL_NPM_DEPENDENCIES = 'ensuring npm dependencies'; 14 | export const BEFORE_EXPORT = 'exporting component'; 15 | -------------------------------------------------------------------------------- /src/api/consumer/lib/reset.js: -------------------------------------------------------------------------------- 1 | import { loadConsumer } from '../../../consumer'; 2 | import { BitId } from '../../../bit-id'; 3 | import loader from '../../../cli/loader'; 4 | import { ComponentWithDependencies } from '../../../scope'; 5 | import { BEFORE_RESET_ACTION } from '../../../cli/loader/loader-messages'; 6 | 7 | export default function reset({ id }: { id: string }) { 8 | return loadConsumer() 9 | .then((consumer) => { 10 | const bitId = BitId.parse(id, consumer.scope.name); 11 | loader.start(BEFORE_RESET_ACTION); 12 | return consumer.scope.reset({ bitId, consumer }) 13 | .then((c: ComponentDependencies) => { 14 | // const inlineId = new InlineId({ box: bitId.box, name: bitId.name }); 15 | // const inlineBitPath = inlineId.composeBitPath(consumer.getPath()); 16 | // return c.component.write(inlineBitPath, true); 17 | }); 18 | }); 19 | } 20 | -------------------------------------------------------------------------------- /specs/jsdoc/fixtures/methodDeclaration.js: -------------------------------------------------------------------------------- 1 | /* Class representing a point. */ 2 | class Point { 3 | /** 4 | * Create a point. 5 | * @name constructor 6 | * @param {number} x - The x value. 7 | * @param {number} y - The y value. 8 | */ 9 | constructor(x, y) { 10 | // ... 11 | } 12 | 13 | /** 14 | * Get the x value. 15 | * @name getX 16 | * @return {number} The x value. 17 | */ 18 | getX() { 19 | // ... 20 | } 21 | 22 | /** 23 | * Get the y value. 24 | * @name getY 25 | * @return {number} The y value. 26 | */ 27 | getY() { 28 | // ... 29 | } 30 | 31 | /** 32 | * Convert a string containing two comma-separated numbers into a point. 33 | * @name fromString 34 | * @param {string} str - The string containing two comma-separated numbers. 35 | * @return {Point} A Point object. 36 | */ 37 | static fromString(str) { 38 | // ... 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /specs/search/indexer.spec.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import indexer from '../../src/search/indexer'; 3 | 4 | describe('Indexer', () => { 5 | describe('tokenizeStr', () => { 6 | it('should split by camelCase', () => { 7 | expect(indexer.tokenizeStr('camelCase')).to.equal('camel case'); 8 | }); 9 | it('should split by hyphen', () => { 10 | expect(indexer.tokenizeStr('foo-bar')).to.equal('foo bar'); 11 | }); 12 | it('should split by underscore', () => { 13 | expect(indexer.tokenizeStr('foo_bar')).to.equal('foo bar'); 14 | }); 15 | it('should lowercase', () => { 16 | expect(indexer.tokenizeStr('Foo')).to.equal('foo'); 17 | }); 18 | it('should tokenize various combinations', () => { 19 | expect(indexer.tokenizeStr('CamelCase-with-hyphen_and_underscore')) 20 | .to.equal('camel case with hyphen and underscore'); 21 | }); 22 | }); 23 | }); 24 | 25 | -------------------------------------------------------------------------------- /src/consumer/component/sources/dist.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import * as fs from 'fs-extra'; 3 | import * as path from 'path'; 4 | import AbstractVinyl from './abstract-vinyl'; 5 | import { DEFAULT_DIST_DIRNAME } from '../../../constants'; 6 | const DEFAULT_SOURCEMAP_VERSION = 3; // TODO - move to constant ! 7 | 8 | export default class Dist extends AbstractVinyl { 9 | // TODO: remove this distFilePath? 10 | distFilePath:string; 11 | 12 | static getFilePath(bitPath: string, fileName: string) { 13 | return path.join(bitPath, DEFAULT_DIST_DIRNAME, fileName); 14 | } 15 | 16 | static loadFromParsedString(parsedString: Object) { 17 | if (!parsedString) return; 18 | const opts = super.loadFromParsedString(parsedString); 19 | return new Dist(opts); 20 | } 21 | 22 | static loadFromParsedStringArray(arr: Object[]) { 23 | if (!arr) return; 24 | return arr.map(this.loadFromParsedString); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/cli/commands/public-cmds/log-cmd.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import R from 'ramda'; 3 | import Command from '../../command'; 4 | import { getComponentLogs } from '../../../api/consumer'; 5 | import { paintLog } from '../../chalk-box'; 6 | 7 | export default class Show extends Command { 8 | name = 'log '; 9 | description = 'show components(s) commit history.'; 10 | alias = ''; 11 | opts = []; 12 | 13 | action([id, ]: [string]): Promise<*> { 14 | return getComponentLogs(id) 15 | .then(logs => 16 | R.reverse(R.values(logs)) 17 | .map(R.evolve( 18 | { 19 | date: n => new Date(parseInt(n)).toLocaleString() 20 | } 21 | )) 22 | ); 23 | } 24 | 25 | report(logs: Array<{ 26 | message: string, 27 | hash: string, 28 | date: string, 29 | username: ?string, 30 | email: ?string 31 | }>): string { 32 | return logs.map(paintLog).join('\n'); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /e2e/commands/create.e2e.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import path from 'path'; 3 | import fs from 'fs-extra'; 4 | import Helper from '../e2e-helper'; 5 | 6 | describe('bit create', function () { 7 | this.timeout(0); 8 | const helper = new Helper(); 9 | after(() => { 10 | helper.destroyEnv(); 11 | }); 12 | 13 | describe.skip('create simple component', () => { 14 | before(() => { 15 | helper.reInitLocalScope(); 16 | helper.reInitRemoteScope(); 17 | helper.addRemoteScope(); 18 | }); 19 | it('Should write the correct files to fs', () => { 20 | helper.runCmd('bit create simple'); 21 | const dirpath = path.join(helper.localScopePath, 'components', 'global', 'simple'); 22 | const files = fs.readdirSync(dirpath); 23 | expect(files).to.include('impl.js'); 24 | // Make sure there is no other files/dirs created 25 | expect(files.length).to.equal(1); 26 | }); 27 | }); 28 | }); -------------------------------------------------------------------------------- /src/scope/dependency-maps/sources-map.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import path from 'path'; 3 | import { BitIds, BitId } from '../../bit-id'; 4 | import Bit from '../../consumer/component'; 5 | import { DependencyMap } from './dependency-map'; 6 | import { SOURCES_MAP } from '../../constants'; 7 | 8 | export default class SourcesDependencyMap extends DependencyMap { 9 | getPath(): string { 10 | return path.join(this.repository.getPath(), SOURCES_MAP); 11 | } 12 | 13 | get(bitId: BitId): BitId[] { 14 | return super.get(bitId.toStringWithoutScope()); 15 | } 16 | 17 | setBit(id: BitId, bits: Bit[]) { 18 | super.set(id.toStringWithoutScope(), new BitIds(...bits.map(bit => bit.getId()))); 19 | return this; 20 | } 21 | 22 | toObject() { 23 | const obj = {}; 24 | this.forEach((bitIds, bitId) => { 25 | obj[bitId.toStringWithoutScope()] = bitIds.map(dependency => dependency.toString()); 26 | }); 27 | return obj; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/api/scope/index.js: -------------------------------------------------------------------------------- 1 | import initScope from './lib/scope-init'; 2 | import put from './lib/put'; 3 | import fetch from './lib/fetch'; 4 | import describeScope from './lib/describe-scope'; 5 | import catObject from './lib/cat-object'; 6 | import scopeList from './lib/scope-list'; 7 | import scopeShow from './lib/scope-show'; 8 | import buildInScope from './lib/build-in-scope'; 9 | import testInScope from './lib/test-in-scope'; 10 | import modifyCIProps from './lib/modify-ci-props'; 11 | import ciUpdateAction from './lib/ci-update-action'; 12 | import scopeConfig from './lib/scope-config' ; 13 | import catScope from './lib/cat-scope'; 14 | import refreshScope from './lib/refresh-scope'; 15 | 16 | export { 17 | catObject, 18 | describeScope, 19 | initScope, 20 | testInScope, 21 | buildInScope, 22 | put, 23 | scopeList, 24 | scopeShow, 25 | fetch, 26 | modifyCIProps, 27 | ciUpdateAction, 28 | scopeConfig, 29 | catScope, 30 | refreshScope, 31 | }; 32 | -------------------------------------------------------------------------------- /specs/specs-runner/worker.spec.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import { fork } from 'child_process'; 3 | import path from 'path'; 4 | 5 | describe('worker', () => { 6 | it('should throw an error for a mismatch tester interface', (done) => { 7 | const child = fork(path.join(__dirname, '..', '..', 'src', 'specs-runner', 'worker.js'), { 8 | silent: false, 9 | env: { 10 | __impl__: '', 11 | __specs__: '', 12 | __tester__: path.join(__dirname, 'fixtures', 'invalid-tester.js'), 13 | __testerId__: 'myScope/box/my-component' 14 | } 15 | }); 16 | 17 | process.on('exit', () => { 18 | child.kill('SIGKILL'); 19 | }); 20 | 21 | child.on('message', ({ type, payload }: { type: string, payload: Object }) => { 22 | expect(type).to.equal('error'); 23 | expect(payload).to.equal('"myScope/box/my-component" doesn\'t have a valid tester interface'); 24 | done(); 25 | }); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /src/cli/commands/public-cmds/create-cmd.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import Command from '../../command'; 3 | import { create } from '../../../api/consumer'; 4 | import Component from '../../../consumer/component'; 5 | 6 | const chalk = require('chalk'); 7 | 8 | export default class Create extends Command { 9 | name = 'create '; 10 | description = 'create a new component'; 11 | alias = 'cr'; 12 | opts = [ 13 | ['s', 'specs', 'create specs file automatically'], 14 | ['j', 'json', 'create bit.json file automatically'], 15 | ['f', 'force', 'override an existing component'] 16 | ]; 17 | 18 | private = true; 19 | 20 | action([id, ]: [string], { specs, json, force }: any): Promise<*> { 21 | return create(id, specs, json, force); 22 | } 23 | 24 | report(component: Component): string { 25 | const name = component.name; 26 | const box = component.box; 27 | 28 | return chalk.green(`created component "${name}" in box "${box}"`); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /specs/api/consumer/lib/export.spec.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import sinon from 'sinon'; 3 | import exportAction from '../../../../src/api/consumer/lib/export'; 4 | import { ComponentNotFound } from '../../../../src/scope/exceptions'; 5 | import * as consumer from '../../../../src/consumer'; 6 | 7 | describe('export', () => { 8 | let sandbox; 9 | beforeEach(() => { 10 | sandbox = sinon.sandbox.create(); 11 | }); 12 | afterEach(() => { 13 | sandbox.restore(); 14 | }); 15 | xit('should throw a ComponentNotFound error if the component-id does include "@this" annotation', () => { 16 | sandbox.stub(consumer, 'loadConsumer').returns(Promise.resolve({ exportAction: () => Promise.reject(new ComponentNotFound()) })); 17 | return exportAction('@this/box/name', 'my.remote') 18 | .then(() => expect.fail('should not be here')) 19 | .catch((err) => { 20 | expect(err).to.be.an.instanceof(ComponentNotFound); 21 | }); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /src/cli/commands/private-cmds/ci-update-cmd.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import Command from '../../command'; 3 | import { ciUpdateAction } from '../../../api/scope'; 4 | import SpecsResults from '../../../consumer/specs-results/specs-results'; 5 | import { paintSpecsResults } from '../../chalk-box'; 6 | 7 | export default class CiUpdate extends Command { 8 | name = 'ci-update [scopePath]'; 9 | description = 'run an update for build and test of a certain bit-component'; 10 | alias = ''; 11 | opts = []; 12 | private = true; 13 | 14 | action([id, scopePath, ]: [string, ?string]): Promise { 15 | return ciUpdateAction(id, scopePath || process.cwd()); 16 | } 17 | 18 | report(maybeSpecsResults: SpecsResults|Error): string { 19 | if (!maybeSpecsResults) { return 'no results found'; } 20 | 21 | if (maybeSpecsResults instanceof Error) { 22 | return maybeSpecsResults.message; 23 | } 24 | 25 | return paintSpecsResults(maybeSpecsResults); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["airbnb", "airbnb-flow"], 3 | "rules": { 4 | "arrow-body-style": [0, "as-needed"], 5 | "camelcase": [0], 6 | "no-trailing-spaces": [0], 7 | "import/no-unresolved": [0], 8 | "radix": [0], 9 | "no-underscore-dangle": [0], 10 | "no-param-reassign": [0], 11 | "array-bracket-spacing": [0], 12 | "class-methods-use-this": [0], 13 | "import/no-dynamic-require": [0], 14 | "global-require": [0], 15 | "prefer-arrow-callback": [0], 16 | "import/no-extraneous-dependencies": [0], 17 | "comma-dangle": [0, "never"], 18 | "no-use-before-define": [0], 19 | "max-len": [2, 120, 2, { 20 | "ignoreUrls": true, 21 | "ignoreComments": true, 22 | "ignoreRegExpLiterals": true, 23 | "ignoreStrings": true, 24 | "ignoreTemplateLiterals": true 25 | }], 26 | "func-names": [0] 27 | }, 28 | "env": { 29 | "node": true 30 | }, 31 | "globals": { 32 | "__impl__": true 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/cli/commands/private-cmds/cat-scope-cmd.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import Command from '../../command'; 3 | import { catScope } from '../../../api/scope'; 4 | import componentObject from '../../../scope/models/component'; 5 | 6 | export default class CatScope extends Command { 7 | name = 'cat-scope [scopePath]'; 8 | description = 'cat a scope and show all the contents'; 9 | private = true; 10 | alias = ''; 11 | opts = [ 12 | ['f', 'full', 'show all of the objects in the scope'] 13 | ]; 14 | 15 | action([scopePath, ]: [string, ], { full }: { full: ?bool }): Promise { 16 | return catScope(scopePath || process.cwd(), full).then(payload => ({ payload, full })); 17 | } 18 | 19 | report({ payload, full }: {payload: componentObject[], full: ?bool }): string { 20 | if (!full) { 21 | return payload.map(co => `${co.id()} -> ${co.hash().toString()}`).join('\n'); 22 | } 23 | 24 | return payload.map(co => `> ${co.hash().toString()}\n\n${co.id()}\n`).join('\n'); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/jsdoc/formater.js: -------------------------------------------------------------------------------- 1 | /** @Flow */ 2 | import { Doclet } from './parser'; 3 | 4 | export default function format(doc: Doclet): string { 5 | let args; 6 | let returns = ''; 7 | let formattedDoc = `\nname: ${doc.name} \n`; 8 | 9 | if (doc.description) { 10 | formattedDoc += `description: ${doc.description}\n`; 11 | } 12 | 13 | if (doc.args && doc.args.length) { 14 | args = doc.args.map((arg) => { 15 | let formattedParam = `${arg.name}`; 16 | if (arg.type) { 17 | formattedParam += ` (${arg.type})`; 18 | } 19 | return formattedParam; 20 | }).join(', '); 21 | formattedDoc += `args: ${args}\n`; 22 | } 23 | if (doc.returns) { 24 | if (doc.returns.description) { 25 | returns = `${doc.returns.description} `; 26 | } 27 | 28 | if (doc.returns.type) { 29 | returns += `(${doc.returns.type})`; 30 | } 31 | 32 | if (returns) { 33 | formattedDoc += `returns: ${returns}\n`; 34 | } 35 | } 36 | 37 | return formattedDoc; 38 | } 39 | -------------------------------------------------------------------------------- /src/cli/commands/private-cmds/_fetch-cmd.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import Command from '../../command'; 3 | import { fromBase64, unpackCommand, packCommand, buildCommandMessage } from '../../../utils'; 4 | import { fetch } from '../../../api/scope'; 5 | import ComponentObjects from '../../../scope/component-objects'; 6 | 7 | export default class Fetch extends Command { 8 | name = '_fetch '; 9 | private = true; 10 | description = 'fetch components(s) from a scope'; 11 | alias = ''; 12 | opts = [ 13 | ['n', 'no_dependencies', 'do not include component dependencies'] 14 | ]; 15 | 16 | action([path, args]: [string, string], { no_dependencies }: any): Promise { 17 | const { payload } = unpackCommand(args); 18 | return fetch(fromBase64(path), payload, no_dependencies); 19 | } 20 | 21 | report(componentObjects: ComponentObjects[]): string { 22 | const components = componentObjects.map(obj => obj.toString()); 23 | return packCommand(buildCommandMessage(components)); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /specs/search/query-builder.spec.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import buildQuery from '../../src/search/query-builder'; 3 | 4 | describe('query-builder', () => { 5 | describe('buildQuery', () => { 6 | const queryToObject = query => query.reduce((acc, val) => Object.assign(acc, val.AND), {}); 7 | 8 | it('should produce a clause for the name field as is', () => { 9 | const query = queryToObject(buildQuery('is-string')); 10 | expect(query.name).to.eql(['is-string']); 11 | }); 12 | it('should produce a clause for the name field tokenized', () => { 13 | const query = queryToObject(buildQuery('is-string')); 14 | expect(query.tokenizedName).to.eql(['is', 'string']); 15 | }); 16 | it('should produce clauses for the stem version of the search query', () => { 17 | const query = queryToObject(buildQuery('casting int')); 18 | expect(query.stemmedName).to.eql(['cast', 'int']); 19 | expect(query.stemmedMinDescription).to.eql(['cast', 'int']); 20 | }); 21 | }); 22 | }); 23 | 24 | -------------------------------------------------------------------------------- /scripts/build-brew.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ex 4 | 5 | PACKAGE_TMPDIR=./distribution/brew_pkg 6 | VERSION=$(cat ./package.json | grep version | head -1 | awk -F: '{ print $2 }' | sed 's/[",]//g' | xargs echo -n) 7 | TARBALL_NAME=./bit-$VERSION.tar.gz 8 | tarName="bit-${VERSION}-brew.tar.gz" 9 | 10 | if [ ! -e $TARBALL_NAME ]; then 11 | echo "Hey! Listen! You need to run build-dist.sh first." 12 | exit 1 13 | fi; 14 | 15 | 16 | # Extract to a temporary directory 17 | rm -rf $PACKAGE_TMPDIR 18 | mkdir -p $PACKAGE_TMPDIR/bit 19 | umask 0022 # Ensure permissions are correct (0755 for dirs, 0644 for files) 20 | tar zxf $TARBALL_NAME -C $PACKAGE_TMPDIR/bit 21 | PACKAGE_TMPDIR_ABSOLUTE=$(cd $(dirname ".") && pwd -P)/$PACKAGE_TMPDIR/bit/ 22 | 23 | 24 | node ./scripts/set-installation-method.js $PACKAGE_TMPDIR_ABSOLUTE/package.json brew 25 | cd $PACKAGE_TMPDIR_ABSOLUTE 26 | 27 | eval tar --exclude='./Jenkinsfile' --exclude='./scripts/' -zcvf ${tarName} * 28 | shasum -a 256 ${tarName} 29 | mv ${tarName} ../../ 30 | rm -rf ../../brew_pkg 31 | 32 | -------------------------------------------------------------------------------- /src/scope/dependencies/dependency.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import Version from '../../version'; 3 | // import { DEPENDENCY_DELIMITER } from '../constants'; 4 | import { InvalidDependency } from './exceptions'; 5 | import parseDepName from './name-parser'; 6 | // import Box from '../box'; 7 | 8 | export default class Dependency { 9 | name: string; 10 | remote: string; 11 | box: ?string; 12 | version: string; 13 | 14 | constructor(name: string, box: ?string, remote: string, version: string) { 15 | this.name = name; 16 | this.box = box; 17 | this.version = version; 18 | this.remote = remote; 19 | } 20 | 21 | import() { 22 | // this.remote 23 | // .connect() 24 | // .get(this.name, this.version) 25 | // .persist(); 26 | } 27 | 28 | flatten() { 29 | 30 | } 31 | 32 | static load(depName: string, version: string) { 33 | if (!depName) throw new InvalidDependency(); 34 | const { name, box, remote } = parseDepName(depName); 35 | return new Dependency(name, box, remote, Version.parse(version)); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /docs/INTRODUCTION.md: -------------------------------------------------------------------------------- 1 | 2 | # Bit 3 | 4 | Bit is a distributed virtual component repository designed for easy use, maintenance and discovery of code components. 5 | 6 | With Bit, you can virtually create and model components on a [distributed Scope](https://teambit.github.io/bit/bit-scope.html), then discover and use these components to dynamically compose a virtual API with the components you actually use in your application. 7 | 8 | It helps reusing code components in different contexts (repositories, micro-services, serverless functions, etc.) without the overhead of maintaining many small packages with different boilerplates or pulling redundant code. 9 | 10 | Components are stored and managed in [Scopes](Glossary#scopes). 11 | 12 | Scopes takes care of your components entire lifecycle from storing and version management, through build and test execution all the way to faster dependency management. It also makes your components easy to find with a built-in semantic search and a simple scoping mechanism. 13 | 14 | ## Documentation 15 | 16 | {% include "./SUMMARY.md" %} 17 | -------------------------------------------------------------------------------- /src/api/consumer/lib/status.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import R from 'ramda'; 3 | import { loadConsumer } from '../../../consumer'; 4 | import ComponentsList from '../../../consumer/component/components-list'; 5 | 6 | export default async function status(): Promise { 7 | const consumer = await loadConsumer(); 8 | const componentsList = new ComponentsList(consumer); 9 | const newComponents = await componentsList.listNewComponents(true); 10 | const modifiedComponent = await componentsList.listModifiedComponents(true); 11 | const stagedComponents = await componentsList.listExportPendingComponents(); 12 | 13 | // Run over the components to check if there is missing dependencies 14 | // If there is at least one we won't commit anything 15 | const newAndModified = newComponents.concat(modifiedComponent); 16 | const componentsWithMissingDeps = newAndModified.filter((component) => { 17 | return (component.missingDependencies && !R.isEmpty(component.missingDependencies)); 18 | }); 19 | 20 | return { newComponents, modifiedComponent, stagedComponents, componentsWithMissingDeps }; 21 | } 22 | -------------------------------------------------------------------------------- /src/remotes/remote-resolver/remote-resolver.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | import R from 'ramda'; 3 | import Scope from '../../scope/scope'; 4 | import { getSync } from '../../api/consumer/lib/global-config'; 5 | import { DEFAULT_HUB_DOMAIN, CFG_HUB_DOMAIN_KEY } from '../../constants'; 6 | 7 | const hubDomain = getSync(CFG_HUB_DOMAIN_KEY) || DEFAULT_HUB_DOMAIN; 8 | 9 | const hubResolver = (scopeName) => { 10 | const hubPrefix = `ssh://bit@${hubDomain}:`; 11 | return Promise.resolve(hubPrefix + scopeName); 12 | }; 13 | 14 | const remoteResolver = (scopeName: string, thisScope?: Scope): Promise => { 15 | const resolverPath = R.path(['scopeJson', 'resolverPath'], thisScope); 16 | let resolverFunction; 17 | if (!resolverPath) resolverFunction = hubResolver; // use the default resolver 18 | // $FlowFixMe 19 | else resolverFunction = require(resolverPath); // use the resolver described in the scopeJson 20 | 21 | return resolverFunction( 22 | scopeName, 23 | thisScope ? thisScope.name : undefined 24 | ); // should return promise 25 | }; 26 | 27 | export default remoteResolver; 28 | -------------------------------------------------------------------------------- /src/cli/commands/public-cmds/bind-cmd.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import chalk from 'chalk'; 3 | import Command from '../../command'; 4 | import { getDriver } from '../../../api/consumer'; 5 | 6 | export default class Create extends Command { 7 | name = 'bind'; 8 | description = 'Call the driver bind action'; 9 | alias = 'b'; 10 | opts = [ 11 | ['v', 'verbose', 'showing the driver path'], 12 | ]; 13 | private = true; 14 | loader = true; 15 | 16 | action(args: string[], { verbose }: { verbose: ?bool }): Promise<*> { 17 | return getDriver().then((driverObj) => { 18 | return driverObj.getDriver(false).bind({}); 19 | }); 20 | } 21 | 22 | report(result: {[string]: string}): string { 23 | const reportComponents = components => Object.keys(components) 24 | .map(component => chalk.cyan(`\t${component} => ${components[component]}`)).join('\n'); 25 | 26 | const reportTitle = components => 27 | chalk.underline(`Bound ${chalk.bold(Object.keys(components).length.toString())} components\n`); 28 | 29 | return reportTitle(result) + reportComponents(result); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /resources/win-chocolatey/tools/chocolateyinstall.ps1.in: -------------------------------------------------------------------------------- 1 | $ErrorActionPreference = 'Stop'; # stop on all errors 2 | $packageName = 'bit' 3 | $packageArgs = @{ 4 | packageName = $packageName 5 | softwareName = 'Bit*' 6 | fileType = 'msi' 7 | silentArgs = "/qn /norestart /l*v `"$env:TEMP\chocolatey\$($packageName)\$($packageName).MsiInstall.log`"" 8 | validExitCodes = @(0, 3010, 1641) 9 | checksumType = 'sha256' 10 | 11 | url = 'https://bitsrc.jfrog.io/bitsrc/bit-msi/{ENVIRONMENT}/bit/{VERSION}/bit-{VERSION}-unsigned.msi' 12 | checksum = '{CHECKSUM}' 13 | } 14 | 15 | Install-ChocolateyPackage @packageArgs 16 | 17 | # Update Bit package.json file so it can tell that it was installed via Chocolatey. 18 | if (Test-Path "${env:ProgramFiles(x86)}\Bit\package.json") { 19 | $path = "${env:ProgramFiles(x86)}\Bit\package.json" 20 | } else { 21 | $path = "$env:ProgramFiles\Bit\package.json" 22 | } 23 | $script = @" 24 | (Get-Content -Path '$path') `` 25 | -replace 'installationMethod":.+', 'installationMethod": "choco"' `` 26 | | Set-Content '$path' 27 | "@ 28 | Start-ChocolateyProcessAsAdmin -Statements $script -------------------------------------------------------------------------------- /src/cli/commands/private-cmds/_put-cmd.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import Command from '../../command'; 3 | import ComponentObjects from '../../../scope/component-objects'; 4 | import { fromBase64, buildCommandMessage, packCommand } from '../../../utils'; 5 | import { put } from '../../../api/scope'; 6 | 7 | export default class Put extends Command { 8 | name = '_put '; 9 | private = true; 10 | description = 'upload a component to a scope'; 11 | alias = ''; 12 | opts = []; 13 | 14 | action([path, ]: [string, string]): Promise { 15 | let data = ''; 16 | return new Promise((resolve, reject) => { 17 | process.stdin 18 | .on('data', (chunk) => { 19 | data += chunk.toString(); 20 | }) 21 | .on('end', () => { 22 | return put({ componentObjects: fromBase64(data.toString()), path: fromBase64(path) }) 23 | .then(resolve).catch(reject); 24 | }); 25 | }); 26 | } 27 | 28 | report(componentObjects: ComponentObjects): string { 29 | return packCommand(buildCommandMessage(ComponentObjects.manyToString(componentObjects))); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/cli/commands/public-cmds/export-cmd.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import Command from '../../command'; 3 | import { exportAction } from '../../../api/consumer'; 4 | import Component from '../../../consumer/component/consumer-component'; 5 | 6 | const chalk = require('chalk'); 7 | 8 | export default class Export extends Command { 9 | name = 'export [id...]'; 10 | description = 'export components to a remote scope.'; 11 | alias = 'e'; 12 | opts = [ 13 | ['f', 'forget', 'do not save to bit.json after export'] 14 | ]; 15 | loader = true; 16 | 17 | action([remote, ids]: [string, string[]], { forget }: any): Promise<*> { 18 | return exportAction(ids, remote, !forget).then(component => ({ component, remote })); 19 | } 20 | 21 | report({ component, remote }: { component: Component|Component[], remote: string }): string { 22 | if (Array.isArray(component)) { 23 | return chalk.green(`exported ${component.length} components to scope ${chalk.bold(remote)}`); 24 | } 25 | 26 | return chalk.green(`exported component ${chalk.bold(component.id.toString())} to scope ${chalk.bold(remote)}`); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/search/serverless-index.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import path from 'path'; 3 | import fs from 'fs-extra'; 4 | import searchIndex from 'search-index'; 5 | 6 | const indexName = 'search_index'; 7 | const logLevel = 'error'; 8 | let index: Promise; 9 | 10 | function getIndexPath(scopePath: string) { 11 | return path.join(scopePath, indexName); 12 | } 13 | 14 | function deleteDb(scopePath: string) { 15 | const indexPath = getIndexPath(scopePath); 16 | if (!scopePath || !indexPath) return; 17 | fs.removeSync(indexPath); 18 | } 19 | 20 | function initializeIndex(scopePath: string): Promise { 21 | if (!index) { // static var to make sure the index is not instantiated twice 22 | const indexOptions = { 23 | indexPath: getIndexPath(scopePath), 24 | logLevel, 25 | stopwords: [] 26 | }; 27 | 28 | index = new Promise((resolve, reject) => { 29 | searchIndex(indexOptions, (err, si) => { 30 | if (err) reject(err); 31 | resolve(si); 32 | }); 33 | }); 34 | } 35 | 36 | return index; 37 | } 38 | 39 | module.exports = { 40 | deleteDb, 41 | getIndexPath, 42 | initializeIndex, 43 | }; 44 | -------------------------------------------------------------------------------- /src/scope/network/check-version-compatibility.js: -------------------------------------------------------------------------------- 1 | import * as semver from 'semver'; 2 | import chalk from 'chalk'; 3 | import { BIT_VERSION } from '../../constants'; 4 | import loader from '../../cli/loader/loader'; 5 | 6 | const createMajorMessage = (remoteVersion, currentVersion) => chalk.red( 7 | `Fatal: There is a mismatch between the remote scope version - "${remoteVersion}" and your bit version - "${currentVersion}", please update\n` 8 | ); 9 | 10 | const createMinorMessage = (remoteVersion, currentVersion) => chalk.yellow( 11 | `Warning: There is a mismatch between the remote scope version - "${remoteVersion}" and your bit version - "${currentVersion}", please update\n` 12 | ); 13 | 14 | export default function checkVersionCompatibility(remoteVersion: string) { 15 | if (semver.major(remoteVersion) > semver.major(BIT_VERSION)) { 16 | loader.off(); 17 | console.log(createMajorMessage(remoteVersion, BIT_VERSION)); // eslint-disable-line 18 | return; 19 | } 20 | 21 | if (semver.minor(remoteVersion) > semver.minor(BIT_VERSION)) { 22 | loader.off(); 23 | console.log(createMinorMessage(remoteVersion, BIT_VERSION)); // eslint-disable-line 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/scope/models/scopeMeta.js: -------------------------------------------------------------------------------- 1 | import { BitObject, Ref } from '../objects'; 2 | import { bufferFrom } from '../../utils'; 3 | 4 | type ScopeMetaProps = { 5 | name: string; 6 | license: string; 7 | }; 8 | 9 | export default class ScopeMeta extends BitObject { 10 | license: ?string; 11 | name: string; 12 | 13 | constructor(props: ScopeMetaProps) { 14 | super(); 15 | this.license = props.license; 16 | this.name = props.name; 17 | } 18 | 19 | toObject(): Object { 20 | return { 21 | license: this.license, 22 | name: this.name, 23 | }; 24 | } 25 | 26 | toString(): string { 27 | return JSON.stringify(this.toObject()); 28 | } 29 | 30 | id(): Object { 31 | return this.name; 32 | } 33 | 34 | toBuffer(): Buffer { 35 | return bufferFrom(this.toString()); 36 | } 37 | 38 | static fromScopeName(name: string): Ref { 39 | return ScopeMeta.fromObject({ name }).hash(); 40 | } 41 | 42 | static parse(propsStr: string|Buffer): ScopeMeta { 43 | return this.fromObject(JSON.parse(propsStr)); 44 | } 45 | 46 | static fromObject(props: ScopeMetaProps): ScopeMeta { 47 | return new ScopeMeta(props); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/api/consumer/lib/test.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import { loadConsumer, Consumer } from '../../../consumer'; 3 | import { BitId } from '../../../bit-id'; 4 | import ComponentsList from '../../../consumer/component/components-list'; 5 | import Bit from '../../../consumer/component'; 6 | 7 | export async function test(id: string, verbose: boolean = true): Promise { 8 | const consumer: Consumer = await loadConsumer(); 9 | const idParsed = BitId.parse(id); 10 | const component = await consumer.loadComponent(idParsed); 11 | const result = await consumer.runComponentSpecs(idParsed, verbose); 12 | return { specs: result, component }; 13 | } 14 | 15 | export async function testAll(verbose: boolean = true): Promise { 16 | const consumer = await loadConsumer(); 17 | const componentsList = new ComponentsList(consumer); 18 | const newAndModifiedComponents = await componentsList.newAndModifiedComponents(); 19 | const specsResults = newAndModifiedComponents.map(async (component) => { 20 | const result = await component.runSpecs({ scope: consumer.scope, consumer, verbose }); 21 | return { specs: result, component }; 22 | }); 23 | 24 | return Promise.all(specsResults); 25 | } 26 | -------------------------------------------------------------------------------- /specs/version/version-parser.spec.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import versionParser from '../../src/version/version-parser'; 3 | 4 | describe('versionParser()', () => { 5 | it('should return latest version representation', () => { 6 | const version = versionParser('latest'); 7 | expect(version.latest).to.equal(true); 8 | expect(version.versionNum).to.equal(null); 9 | }); 10 | 11 | it('should throw invalid version', () => { 12 | expect(() => { 13 | versionParser('$1'); 14 | }).to.throw(); 15 | }); 16 | 17 | it('should return a concrete version', () => { 18 | const version = versionParser('1'); 19 | expect(version.latest).to.equal(false); 20 | expect(version.versionNum).to.equal(1); 21 | }); 22 | 23 | it('should return a latest tested version', () => { 24 | const version = versionParser('*1'); 25 | expect(version.latest).to.equal(true); 26 | expect(version.versionNum).to.equal(1); 27 | }); 28 | 29 | it('should return a latest tested version with double digits', () => { 30 | const version = versionParser('*11'); 31 | expect(version.latest).to.equal(true); 32 | expect(version.versionNum).to.equal(11); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /specs/search/serverless-index.spec.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import sinon from 'sinon'; 3 | import searchIndex from 'search-index'; 4 | import serverlessIndex from '../../src/search/serverless-index'; 5 | 6 | describe('SeverLessIndex', () => { 7 | describe('initializeIndex', () => { 8 | it('should initial the index using search-index lib with some default settings', () => { 9 | // this hack was taken from: https://github.com/sinonjs/sinon/issues/562#issuecomment-164522794 10 | // it is needed because search-index library returns a function, not object. 11 | const myStubbedModule = (moduleName) => { 12 | const stub = sinon.stub(); 13 | require.cache[require.resolve(moduleName)] = { 14 | default: stub, 15 | exports: stub 16 | }; 17 | return stub; 18 | }; 19 | myStubbedModule('search-index'); 20 | const result = serverlessIndex.initializeIndex('test_path'); 21 | 22 | sinon.assert.calledWith(searchIndex, { 23 | indexPath: 'test_path/search_index', 24 | logLevel: 'error', 25 | stopwords: [] 26 | }); 27 | expect(result).to.be.a('Promise'); 28 | }); 29 | }); 30 | }); 31 | 32 | -------------------------------------------------------------------------------- /src/scope/ci-ops/ci-ops.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import { spawn } from 'child_process'; 3 | import path from 'path'; 4 | import ConsumerComponent from '../../consumer/component'; 5 | import * as globalConfig from '../../api/consumer/lib/global-config'; 6 | import { CFG_CI_FUNCTION_PATH_KEY, CFG_CI_ENABLE_KEY } from '../../constants'; 7 | 8 | function defaultCIFunc(id: string, scopePath: string) { 9 | const child = spawn(process.argv[0], [path.join(__dirname, 'ci-worker.js')], { 10 | detached: true, 11 | cwd: scopePath, 12 | env: { 13 | __id__: id, 14 | __scope__: scopePath, 15 | }, 16 | stdio: [ 'ignore', 'ignore', 'ignore' ] 17 | }); 18 | 19 | child.unref(); 20 | } 21 | 22 | export default (component: ConsumerComponent, scopePath: string) => { 23 | const enableCI = globalConfig.getSync(CFG_CI_ENABLE_KEY); 24 | const ciFuncPath = globalConfig.getSync(CFG_CI_FUNCTION_PATH_KEY); 25 | 26 | if (enableCI !== 'true' && !ciFuncPath) return component; 27 | 28 | const id = component.id.toString(); 29 | let ciFunc; 30 | 31 | if (!ciFuncPath) ciFunc = defaultCIFunc; 32 | // $FlowFixMe 33 | else ciFunc = require(ciFuncPath); 34 | 35 | ciFunc(id, scopePath); 36 | return component; 37 | }; 38 | -------------------------------------------------------------------------------- /src/cli/loader/loader.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import ora from 'ora'; 3 | import { SPINNER_TYPE } from '../../constants'; 4 | 5 | let _loader; 6 | 7 | type Loader = { 8 | on: () => Loader, 9 | off: () => ?Loader, 10 | start: (text: ?string) => ?Loader, 11 | stop: () => ?Loader, 12 | setText: (string) => ?Loader, 13 | get: () => ?Loader 14 | } 15 | 16 | const start = (text: ?string): Loader => { 17 | if (_loader) { 18 | if (text) _loader.text = text; 19 | _loader.start(); 20 | } 21 | return loader; 22 | }; 23 | 24 | const setText = (text: string): Loader => { 25 | if (_loader) _loader.text = text; 26 | return loader; 27 | }; 28 | 29 | const get = (): ?Loader => _loader; 30 | 31 | const stop = (): Loader => { 32 | if (_loader) _loader.stop(); 33 | return loader; 34 | }; 35 | 36 | const on = (): Loader => { 37 | if (!_loader) _loader = ora({ spinner: SPINNER_TYPE, text: '' }); 38 | return loader; 39 | }; 40 | 41 | const off = (): Loader => { 42 | stop(); 43 | _loader = null; 44 | return loader; 45 | }; 46 | 47 | const loader: Loader = { 48 | on: on, 49 | off: off, 50 | stop: stop, 51 | start: start, 52 | setText: setText, 53 | get: get, 54 | }; 55 | 56 | export default loader; 57 | 58 | -------------------------------------------------------------------------------- /src/hooks/create-hook.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | import R from 'ramda'; 3 | import { get } from '../api/consumer/lib/global-config'; 4 | import client from './http-client/client'; 5 | import logger from '../logger/logger'; 6 | 7 | const createHook = (hookNameKey: string, methodName: string): () => Promise => { 8 | logger.debug(`Planning to run a hook ${hookNameKey} with a method ${methodName}`); 9 | methodName = R.toUpper(methodName); 10 | return (data: ?Object|string): Promise => { 11 | return new Promise((resolve) => { 12 | return get(hookNameKey) 13 | .then((destUrl) => { 14 | if (!destUrl) { 15 | logger.warn(`Failed running the ${hookNameKey} hook as destUrl is not set in the config file`); 16 | return resolve(); 17 | } 18 | logger.debug(`Running the ${hookNameKey} hook with destUrl: ${destUrl}, and data: ${data}`); 19 | return client[methodName](destUrl, data) 20 | .then(() => resolve()) 21 | .catch((err) => { 22 | logger.warn(`Failed running the hook ${hookNameKey}. Error: ${err}`); 23 | return resolve(); 24 | }); 25 | }); 26 | }); 27 | }; 28 | }; 29 | 30 | export default createHook; 31 | -------------------------------------------------------------------------------- /src/scope/flatten-dependencies.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import { flatten, values } from '../utils'; 3 | import VersionDependencies from './version-dependencies'; 4 | import Repository from './objects/repository'; 5 | import { BitId } from '../bit-id'; 6 | 7 | export function flattenDependencies(dependencies: VersionDependencies[]) { 8 | return values(flatten(dependencies 9 | .map(dep => dep.dependencies.concat(dep.component))) 10 | .reduce((components, component) => { 11 | components[component.id.toString()] = component; 12 | return components; 13 | }, {})); 14 | } 15 | 16 | export function flattenDependencyIds(dependencies: VersionDependencies[], repo: Repository): Promise { // eslint-disable-line 17 | return Promise.all(dependencies.map((dep) => { 18 | const depCompId = dep.component.id; 19 | depCompId.scope = dep.sourceScope; 20 | return dep.component.flattenedDependencies(repo) 21 | .then(flattnedDeps => flattnedDeps.concat(depCompId)); 22 | })) 23 | .then((idMatrix) => { 24 | const ids = flatten(idMatrix); 25 | return values(ids.reduce((components, id) => { 26 | components[id.toString()] = id; 27 | return components; 28 | }, {})); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | *.pid.lock 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # nyc test coverage 19 | .nyc_output 20 | 21 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 22 | .grunt 23 | 24 | # node-waf configuration 25 | .lock-wscript 26 | 27 | # Compiled binary addons (http://nodejs.org/api/addons.html) 28 | build/Release 29 | 30 | # Dependency directories 31 | node_modules 32 | jspm_packages 33 | 34 | # Optional npm cache directory 35 | .npm 36 | 37 | # Optional eslint cache 38 | .eslintcache 39 | 40 | # Optional REPL history 41 | .node_repl_history 42 | 43 | # Output of 'npm pack' 44 | *.tgz 45 | 46 | # Yarn Integrity file 47 | .yarn-Integrity 48 | 49 | # vscode 50 | .vscode 51 | 52 | # idea 53 | .idea/ 54 | 55 | #flow configuration 56 | .flowconfig 57 | 58 | #output 59 | flow-typed/ 60 | 61 | 62 | #distrubution 63 | distribution/ 64 | 65 | artifacts/ 66 | bit-*.tar.gz 67 | tmp/ 68 | 69 | # bit directories 70 | .bit 71 | inline_components 72 | 73 | #vscode history 74 | .history -------------------------------------------------------------------------------- /src/cli/commands/public-cmds/test-cmd.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import Command from '../../command'; 3 | import { test, testAll } from '../../../api/consumer'; 4 | import { paintAllSpecsResults } from '../../chalk-box'; 5 | 6 | export default class Test extends Command { 7 | name = 'test [id]'; 8 | description = 'test any set of components with configured tester (component tester or as defined in bit.json)'; 9 | alias = 't'; 10 | opts = [ 11 | ['e', 'environment', 'also pre install the required environment bit before running the build'], 12 | ['s', 'save', 'for running build and save the results in the model'], 13 | ['v', 'verbose', 'showing npm verbose output for inspection'], 14 | ]; 15 | loader = true; 16 | 17 | action([id, ]: [string, ], { save, environment, verbose }: { 18 | save: ?bool, 19 | environment: ?bool, 20 | verbose: ?bool, 21 | }): Promise { 22 | if (!id) return testAll(verbose); 23 | return test(id, verbose); 24 | } 25 | 26 | report(results: any): string { 27 | if (results && Array.isArray(results)) return paintAllSpecsResults(results); 28 | if (results && typeof (results) === 'object') return paintAllSpecsResults([results]); 29 | return "couldn't get test results..."; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /scripts/deploy-windows.ps1: -------------------------------------------------------------------------------- 1 | param( 2 | [switch] $Publish = $false, 3 | [string] $Repo = "", 4 | [string] $File = "", 5 | [string] $Source = "", 6 | [string] $ENVIRONMENT = "", 7 | [string] $ReleaseServer = "", 8 | [string] $Method = "" 9 | ) 10 | 11 | $VERSION= $(node -p -e "require('./package.json').version") 12 | $repoUrl = "https://bitsrc.jfrog.io/bitsrc" 13 | 14 | 15 | $AF_USER = $env:repoUser 16 | $AF_PWD = ConvertTo-SecureString "$env:repoPassword" -AsPlainText -Force 17 | $CREDS = New-Object System.Management.Automation.PSCredential ($AF_USER, $AF_PWD) 18 | 19 | $URI = New-Object System.Uri("${repoUrl}/$Repo/$ENVIRONMENT/bit/${VERSION}/$File") 20 | $SOURCE = "$Source" 21 | Invoke-WebRequest -Uri $URI -InFile $SOURCE -Method Put -Credential $CREDS 22 | 23 | 24 | $body = @{ 25 | version="$VERSION" 26 | method="$Method" 27 | file="$repoUrl/$Repo/$ENVIRONMENT/bit/${VERSION}/$File" 28 | } 29 | $AF_USER = "$env:releaseUser" 30 | $AF_PWD = ConvertTo-SecureString "$env:releasePassword" -AsPlainText -Force 31 | $CREDS = New-Object System.Management.Automation.PSCredential ($AF_USER, $AF_PWD) 32 | 33 | $json = $body | ConvertTo-Json 34 | Invoke-RestMethod "$ReleaseServer" -Method Post -Body $json -ContentType 'application/json' -Credential $CREDS -------------------------------------------------------------------------------- /scripts/establish-dev-link.js: -------------------------------------------------------------------------------- 1 | const { exec } = require('child_process'); 2 | const path = require('path'); 3 | 4 | const binDir = '/usr/local/bin'; 5 | 6 | const osPaths = ( 7 | process.env.PATH || process.env.Path || process.env.path 8 | ).split(path.delimiter); 9 | 10 | if (osPaths.indexOf(binDir) === -1) { 11 | throw new Error(`the directory ${binDir} is not a bin directory on the machine, please update establish-dev-link script`); 12 | } 13 | 14 | const userLinkName = process.argv[2]; 15 | const linkName = userLinkName || 'bit-dev'; 16 | const pkg = require('../package.json'); 17 | 18 | const source = path.join(__dirname, '..', pkg.bin.bit); 19 | const dest = path.join(binDir, linkName); 20 | 21 | exec(`ln -sf ${source} ${dest}`, (error, stdout, stderr) => { 22 | if (error) { 23 | console.error(`exec error: ${error}`); 24 | return; 25 | } 26 | 27 | if (stdout) console.log(stdout); 28 | if (stderr) console.error(stderr); 29 | 30 | exec(`chmod u+x ${source}`, (e, sout, serr) => { 31 | if (error) { 32 | console.error(`exec error: ${error}`); 33 | return; 34 | } 35 | 36 | if (sout) console.log(sout); 37 | if (serr) console.error(serr); 38 | console.log(`now you can use the "${linkName}" command to run you dev app`); 39 | }); 40 | }); 41 | 42 | -------------------------------------------------------------------------------- /src/scope/repositories/sources-map.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import path from 'path'; 3 | import { writeFile, forEach } from '../../utils'; 4 | import { SOURCES_JSON } from '../../constants'; 5 | import { Source } from './index'; 6 | import { BitIds, BitId } from '../../bit-id'; 7 | 8 | export default class SourcesJson extends Map { 9 | sources: Source; 10 | 11 | constructor(sourcesTuples: [string, BitIds][], sources: Source) { 12 | super(sourcesTuples); 13 | this.sources = sources; 14 | } 15 | 16 | getPath(): string { 17 | return path.join(this.sources.getPath(), SOURCES_JSON); 18 | } 19 | 20 | toPlainObject() { 21 | const obj = {}; 22 | 23 | this.forEach((bitIds, bitId) => { 24 | obj[bitId.toString()] = bitIds.serialize(); 25 | }); 26 | 27 | return obj; 28 | } 29 | 30 | toJson() { 31 | return JSON.stringify(this.toPlainObject()); 32 | } 33 | 34 | write() { 35 | return writeFile(this.getPath(), this.toJson()); 36 | } 37 | 38 | static load(json: {[string]: string[]}, sources: Source) { 39 | const tuples = []; 40 | 41 | forEach(json, (bitIds, bitId) => { 42 | tuples.push([BitId.parse(bitId), BitIds.deserialize(bitIds)]); 43 | }); 44 | 45 | return new SourcesJson(tuples, sources); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/bit-id/bit-ids.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import { mergeAll } from 'ramda'; 3 | import { BitId } from '../bit-id'; 4 | import { forEach, getLatestVersionNumber } from '../utils'; 5 | 6 | export default class BitIds extends Array { 7 | serialize(): string[] { 8 | return this.map(bitId => bitId.toString()); 9 | } 10 | 11 | toObject(): Object { 12 | return mergeAll( 13 | this.map(bitId => bitId.toObject()) 14 | ); 15 | } 16 | 17 | /** 18 | * Resolve an id with latest to specific version 19 | * This used to get the real version from the flatten deps by the deps ids 20 | * 21 | * @param {BitId} idWithLatest - A bit id object with latest version 22 | * @returns {BitId} - The bit id found in the array (with actual version) 23 | * @memberof BitIds 24 | */ 25 | resolveVersion(idWithLatest) { 26 | return getLatestVersionNumber(this, idWithLatest); 27 | } 28 | 29 | static deserialize(array: string[] = []) { 30 | return new BitIds( 31 | ...array.map(id => BitId.parse(id)) 32 | ); 33 | } 34 | 35 | static fromObject(dependencies: {[string]: string}) { 36 | const array = []; 37 | 38 | forEach(dependencies, (version, id) => { 39 | array.push(BitId.parse(id, null, version)); 40 | }); 41 | 42 | return new BitIds(...array); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/scope/models/symlink.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import { BitObject } from '../objects'; 3 | import { DEFAULT_BOX_NAME } from '../../constants'; 4 | 5 | export type SymlinkProp = { 6 | scope: string; 7 | box?: string; 8 | name: string; 9 | realScope: string; 10 | }; 11 | 12 | export default class Symlink extends BitObject { 13 | scope: string; 14 | name: string; 15 | box: string; 16 | realScope: string; 17 | 18 | constructor(props: SymlinkProp) { 19 | super(); 20 | this.scope = props.scope; 21 | this.name = props.name; 22 | this.box = props.box || DEFAULT_BOX_NAME; 23 | this.realScope = props.realScope; 24 | } 25 | 26 | id(): string { 27 | return [this.box, this.name].join('/'); 28 | } 29 | 30 | getRealComponentId() { 31 | return [this.realScope, this.box, this.name].join('/'); 32 | } 33 | 34 | static parse(contents: string): Symlink { 35 | const rawContent = JSON.parse(contents); 36 | return Symlink.from(rawContent); 37 | } 38 | 39 | toObject() { 40 | return { 41 | scope: this.scope, 42 | box: this.box, 43 | name: this.name, 44 | realScope: this.realScope 45 | }; 46 | } 47 | 48 | toBuffer() { 49 | return new Buffer(JSON.stringify(this.toObject())); 50 | } 51 | 52 | static from(props: SymlinkProp) { 53 | return new Symlink(props); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/cli/templates/component-template.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import R from 'ramda'; 3 | import c from 'chalk'; 4 | import Table from 'cli-table2'; 5 | import ConsumerComponent from '../../consumer/component/consumer-component'; 6 | import paintDocumentation from './docs-template'; 7 | 8 | export default (component: ConsumerComponent) => { 9 | const table = new Table({ 10 | colWidths: [20, 100], 11 | wordWrap: true, 12 | }); 13 | 14 | const { name, box, lang, compilerId, testerId, dependencies, packageDependencies, docs, files, mainFile } = component; 15 | 16 | const rows = [ 17 | { [c.cyan('ID')]: `${box}/${name}` }, 18 | compilerId ? { [c.cyan('Compiler')]: compilerId.toString() }: null, 19 | lang ? { [c.cyan('Language')]: lang }: null, 20 | testerId ? { [c.cyan('Tester')]: testerId.toString() }: null, 21 | mainFile ? { [c.cyan('Main file')]: mainFile }: null, 22 | !R.isEmpty(dependencies) ? { [c.cyan('Dependencies')]: dependencies.map(dependency => dependency.id.toString()).join(',\n') } : null, 23 | !R.isEmpty(packageDependencies) ? { [c.cyan('Packages')]: Object.keys(packageDependencies).join(',\n') } : null, 24 | !R.isEmpty(files) ? { [c.cyan('Files')]: files.map(file => file.relative).join(',\n') } : null, 25 | ].filter(x => x); 26 | 27 | table.push(...rows); 28 | 29 | return table.toString() + paintDocumentation(docs); 30 | }; 31 | -------------------------------------------------------------------------------- /src/api/consumer/lib/create.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import { loadConsumer } from '../../../consumer'; 3 | import Component from '../../../consumer/component'; 4 | import { BitId } from '../../../bit-id'; 5 | import BitMap from '../../../consumer/bit-map'; 6 | import { COMPONENT_ORIGINS } from '../../../constants'; 7 | 8 | /** 9 | * Creates a new component, writes it to the file system and adds to bit.map 10 | */ 11 | export default async function create( 12 | idRaw: string, 13 | withSpecs: boolean = false, 14 | withBitJson: boolean = false, 15 | force: boolean = false 16 | ): Promise { 17 | const consumer = await loadConsumer(); 18 | const id = BitId.parse(idRaw); 19 | const bitPath = consumer.composeRelativeBitPath(id); 20 | const defaultImpl = consumer.bitJson.getImplBasename(); 21 | const files = { [defaultImpl]: defaultImpl }; 22 | const component = Component.create({ 23 | name: id.name, 24 | box: id.box, 25 | withSpecs, 26 | files, 27 | consumerBitJson: consumer.bitJson, 28 | bitPath, 29 | consumerPath: consumer.getPath() 30 | }, consumer.scope); 31 | const bitMap = await BitMap.load(consumer.getPath()); 32 | await component.write({ withBitJson, force, bitMap, origin: COMPONENT_ORIGINS.AUTHORED }); 33 | await bitMap.write(); 34 | // await consumer.driver.runHook('onCreate', component); 35 | return component; 36 | } 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | *.pid.lock 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # nyc test coverage 19 | .nyc_output 20 | 21 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 22 | .grunt 23 | 24 | # node-waf configuration 25 | .lock-wscript 26 | 27 | # Compiled binary addons (http://nodejs.org/api/addons.html) 28 | build/Release 29 | 30 | # Dependency directories 31 | node_modules 32 | jspm_packages 33 | 34 | # Optional npm cache directory 35 | .npm 36 | 37 | # Optional eslint cache 38 | .eslintcache 39 | 40 | # Optional REPL history 41 | .node_repl_history 42 | 43 | # Output of 'npm pack' 44 | *.tgz 45 | 46 | # Yarn Integrity file 47 | .yarn-Integrity 48 | 49 | # vscode 50 | .vscode 51 | 52 | # idea 53 | .idea/ 54 | 55 | #flow configuration 56 | .flowconfig 57 | 58 | #output 59 | dist/ 60 | dist-legacy/ 61 | flow-typed/ 62 | 63 | 64 | #distrubution 65 | distribution/ 66 | 67 | artifacts/ 68 | bit-*.tar.gz 69 | tmp/ 70 | bin/node 71 | nodeBin 72 | 73 | # bit directories 74 | .bit 75 | components 76 | inline_components 77 | 78 | # DS_Store 79 | .DS_Store 80 | 81 | #vscode history 82 | .history 83 | 84 | #gitbook 85 | _book 86 | -------------------------------------------------------------------------------- /src/api/consumer/index.js: -------------------------------------------------------------------------------- 1 | import init from './lib/init'; 2 | import create from './lib/create'; 3 | import remove from './lib/remove'; 4 | import listScope from './lib/list-scope'; 5 | import { commitAction, commitAllAction } from './lib/commit'; 6 | import status from './lib/status'; 7 | import { build, buildAll } from './lib/build'; 8 | import reset from './lib/reset'; 9 | import importAction from './lib/import'; 10 | import exportAction from './lib/export'; 11 | import getConsumerComponent from './lib/get-consumer-component'; 12 | import getScopeComponent from './lib/get-scope-component'; 13 | import { test, testAll } from './lib/test'; 14 | import getComponentLogs from './lib/get-component-logs'; 15 | import { add as remoteAdd, list as remoteList, remove as remoteRm } from './lib/remote'; 16 | import config from './lib/global-config'; 17 | import getDriver from './lib/get-driver'; 18 | import { watchAll } from './lib/watch'; 19 | import add from './lib/add'; 20 | 21 | export { 22 | init, 23 | config, 24 | exportAction, 25 | create, 26 | remove, 27 | buildAll, 28 | listScope, 29 | commitAction, 30 | commitAllAction, 31 | status, 32 | build, 33 | reset, 34 | importAction, 35 | getConsumerComponent, 36 | getScopeComponent, 37 | getComponentLogs, 38 | test, 39 | testAll, 40 | remoteAdd, 41 | remoteList, 42 | remoteRm, 43 | getDriver, 44 | watchAll, 45 | add, 46 | }; 47 | -------------------------------------------------------------------------------- /src/api/consumer/lib/list-scope.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import { loadConsumer } from '../../../consumer'; 3 | import { loadScope } from '../../../scope'; 4 | import { ConsumerNotFound } from '../../../consumer/exceptions'; 5 | import loader from '../../../cli/loader'; 6 | import { BEFORE_REMOTE_LIST } from '../../../cli/loader/loader-messages'; 7 | import Remotes from '../../../remotes/remotes'; 8 | 9 | export default function list({ scopeName, cache }: 10 | { scopeName?: string, cache?: bool }): Promise { 11 | const remoteList = (remote) => { 12 | loader.start(BEFORE_REMOTE_LIST); 13 | return remote.list(); 14 | }; 15 | const scopeList = (scope) => { return cache ? scope.list() : scope.listStage(); }; 16 | 17 | return loadConsumer() 18 | .then((consumer) => { 19 | const scope = consumer.scope; 20 | 21 | if (scopeName) { 22 | return scope.remotes() 23 | .then(remotes => 24 | remotes.resolve(scopeName, scope.name) 25 | .then(remoteList) 26 | ); 27 | } 28 | 29 | return scopeList(scope); 30 | }) 31 | .catch((err) => { 32 | if (!(err instanceof ConsumerNotFound)) throw err; 33 | 34 | if (scopeName) { 35 | return Remotes.getScopeRemote(scopeName).then(remoteList); 36 | } 37 | 38 | return loadScope(process.cwd()) 39 | .catch(() => Promise.reject(err)) 40 | .then(scopeList) 41 | .catch(e => Promise.reject(e)); 42 | }); 43 | } 44 | -------------------------------------------------------------------------------- /specs/api/consumer/lib/list-scope.spec.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import sinon from 'sinon'; 3 | import listScope from '../../../../src/api/consumer/lib/list-scope'; 4 | import { ConsumerNotFound } from '../../../../src/consumer/exceptions'; 5 | import * as consumer from '../../../../src/consumer'; 6 | import { GlobalRemotes } from '../../../../src/global-config'; 7 | import Remotes from '../../../../src/remotes/remotes'; 8 | 9 | describe('ListScope', () => { 10 | let sandbox; 11 | before(() => { 12 | sandbox = sinon.sandbox.create(); 13 | }); 14 | after(() => { 15 | sandbox.restore(); 16 | }); 17 | describe('list', () => { 18 | it('should list components outside a scope if scopeName is given', () => { 19 | sandbox.stub(consumer, 'loadConsumer').returns(Promise.reject(new ConsumerNotFound())); 20 | sandbox.stub(GlobalRemotes, 'load').returns(Promise.resolve({ toPlainObject: () => {} })); 21 | const listSpy = sandbox.spy(); 22 | const resolveStub = sandbox.stub(Remotes.prototype, 'resolve').returns(Promise.resolve({ list: listSpy })); 23 | 24 | const result = listScope({ scopeName: 'non-exists-scope' }); 25 | expect(result).to.be.a('Promise'); 26 | 27 | return result 28 | .then(() => { 29 | expect(resolveStub.getCall(0).args[0]).to.equal('non-exists-scope'); 30 | expect(listSpy.called).to.be.true; 31 | }); 32 | }).timeout(5000); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /src/search/query-builder.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import { tokenizeStr, stem } from './indexer'; 3 | import stopwords from './stopwords'; 4 | 5 | const boost = { 6 | box: 3, 7 | tokenizedBox: 2, 8 | name: 5, 9 | tokenizedName: 4, 10 | stemmedName: 3, 11 | functionNames: 2, 12 | tokenizedFunctionNames: 2, 13 | minDescription: 1, 14 | stemmedMinDescription: 0.5, 15 | }; 16 | 17 | function queryItem(field, queryStr): Object { 18 | return { 19 | AND: { [field]: queryStr.toLowerCase().split(' ') }, 20 | BOOST: boost[field], 21 | }; 22 | } 23 | 24 | function buildQuery(queryStr: string): Array { 25 | const queryStrWithoutStopwords = queryStr 26 | .split(' ') 27 | .filter(word => !stopwords.includes(word)) 28 | .join(' '); 29 | const tokenizedQuery = tokenizeStr(queryStr); 30 | const query = []; 31 | query.push(queryItem('box', queryStr)); 32 | query.push(queryItem('tokenizedBox', queryStr)); 33 | query.push(queryItem('name', queryStr)); 34 | query.push(queryItem('tokenizedName', tokenizedQuery)); 35 | query.push(queryItem('stemmedName', stem(tokenizedQuery))); 36 | query.push(queryItem('functionNames', queryStr)); 37 | query.push(queryItem('tokenizedFunctionNames', tokenizedQuery)); 38 | query.push(queryItem('minDescription', queryStrWithoutStopwords)); 39 | query.push(queryItem('stemmedMinDescription', stem(queryStrWithoutStopwords))); 40 | return query; 41 | } 42 | 43 | export default buildQuery; 44 | -------------------------------------------------------------------------------- /src/api/scope/lib/build-in-scope.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import { loadConsumer } from '../../../consumer'; 3 | import { BitId } from '../../../bit-id'; 4 | import { loadScope, Scope } from '../../../scope'; 5 | import { ConsumerNotFound } from '../../../consumer/exceptions'; 6 | import logger from '../../../logger/logger'; 7 | 8 | export default function buildInScope({ id, environment, save, verbose, scopePath }: 9 | { id: string, environment: ?bool, save: ?bool, verbose: ?bool, scopePath: string }) { 10 | logger.debug(`buildInScope, id: ${id}, scopePath: ${scopePath}`); 11 | function loadFromScope(initialError: ?Error) { 12 | return loadScope(scopePath || process.cwd()) 13 | .catch(newErr => Promise.reject(initialError || newErr)) 14 | .then((scope: Scope) => { 15 | const bitId = BitId.parse(id, scope.name); 16 | return scope.build({ bitId, environment, save, verbose }); 17 | }) 18 | .catch(e => Promise.reject(e)); 19 | } 20 | 21 | function loadFromConsumer() { 22 | return loadConsumer() 23 | .then((consumer) => { 24 | const bitId = BitId.parse(id, consumer.scope.name); 25 | return consumer.scope.build({ bitId, environment, save, consumer, verbose }); 26 | }); 27 | } 28 | 29 | if (scopePath) return loadFromScope(); 30 | 31 | return loadFromConsumer() 32 | .catch((err) => { 33 | if (!(err instanceof ConsumerNotFound)) throw err; 34 | return loadFromScope(err); 35 | }); 36 | } 37 | -------------------------------------------------------------------------------- /src/scope/repositories/external.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import path from 'path'; 3 | import { BIT_EXTERNAL_DIRNAME } from '../../constants'; 4 | import { BitId } from '../../bit-id'; 5 | import Repository from '../repository'; 6 | import Scope from '../scope'; 7 | import Bit from '../../consumer/component'; 8 | import { ExternalDependencyMap } from '../dependency-maps'; 9 | 10 | export default class External extends Repository { 11 | externalMap: ExternalDependencyMap; 12 | 13 | constructor(scope: Scope, externalMap: ExternalDependencyMap) { 14 | super(scope); 15 | this.externalMap = externalMap; 16 | } 17 | 18 | getPath(): string { 19 | return path.join(super.getPath(), BIT_EXTERNAL_DIRNAME); 20 | } 21 | 22 | composePath(bitId: BitId) { 23 | return path.join(bitId.scope, bitId.box, bitId.name, bitId.version); 24 | } 25 | 26 | store(bit: Bit) { 27 | return bit 28 | .cd(this.composePath(bit.getId())) 29 | .write(true) 30 | .then(() => this.externalMap.setBit(bit)) 31 | .then(() => bit); 32 | } 33 | 34 | storeMany(bits: Bit[]) { 35 | return Promise.all(bits.map(bit => bit.store())); 36 | } 37 | 38 | get(id: BitId): Promise { 39 | return Bit.load(this.composePath(id), id.name) 40 | .then((bit) => { 41 | return { bit, success: true }; 42 | }) 43 | .catch(() => { 44 | return { id, success: false }; 45 | }); 46 | } 47 | 48 | write() { 49 | return this.externalMap.write(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/global-config/config.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import path from 'path'; 3 | import fs from 'fs'; 4 | import { GLOBAL_CONFIG, GLOBAL_CONFIG_FILE } from '../constants'; 5 | import { mapToObject, objectToTupleArray, writeFile, readFile } from '../utils'; 6 | 7 | function getPath() { 8 | return path.join(GLOBAL_CONFIG, GLOBAL_CONFIG_FILE); 9 | } 10 | 11 | export default class Config extends Map { 12 | toPlainObject() { 13 | return mapToObject(this); 14 | } 15 | 16 | toJson() { 17 | return JSON.stringify(this.toPlainObject()); 18 | } 19 | 20 | write() { 21 | return writeFile(getPath(), this.toJson()); 22 | } 23 | 24 | writeSync() { 25 | return fs.writeFileSync(getPath(), this.toJson()); 26 | } 27 | 28 | static loadSync(): Config { 29 | try { 30 | const contents = fs.readFileSync(getPath()); 31 | return new Config(objectToTupleArray(JSON.parse(contents.toString()))); 32 | } catch (err) { 33 | if (err.code !== 'ENOENT') return err; 34 | const config = new Config([]); 35 | config.writeSync(); 36 | return config; 37 | } 38 | } 39 | 40 | static load(): Promise { 41 | return readFile(getPath()) 42 | .then(contents => new Config(objectToTupleArray(JSON.parse(contents.toString())))) 43 | .catch((err) => { 44 | if (err.code !== 'ENOENT') return err; 45 | const config = new Config([]); 46 | return config.write() 47 | .then(() => config); 48 | }); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/global-config/global-remotes.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import path from 'path'; 3 | import { GLOBAL_CONFIG, GLOBAL_REMOTES } from '../constants'; 4 | import { writeFile, readFile } from '../utils'; 5 | import { Remotes, Remote } from '../remotes'; 6 | 7 | export default class GlobalRemotes { 8 | remotes: {[string]: string}; 9 | 10 | constructor(remotes: {[string]: string}) { 11 | this.remotes = remotes; 12 | } 13 | 14 | addRemote(remote: Remote) { 15 | this.remotes[remote.name] = remote.host; 16 | return this; 17 | } 18 | 19 | rmRemote(name: string) { 20 | delete this.remotes[name]; 21 | return this; 22 | } 23 | 24 | toJson(readable: boolean = true) { 25 | if (!readable) return JSON.stringify(this.toPlainObject()); 26 | return JSON.stringify(this.toPlainObject(), null, 4); 27 | } 28 | 29 | asRemotes() { 30 | return Remotes.load(this.remotes); 31 | } 32 | 33 | toPlainObject() { 34 | return this.remotes; 35 | } 36 | 37 | write() { 38 | return writeFile(path.join(GLOBAL_CONFIG, GLOBAL_REMOTES), this.toJson()); 39 | } 40 | 41 | static load() { 42 | return readFile(path.join(GLOBAL_CONFIG, GLOBAL_REMOTES)) 43 | .then(contents => new GlobalRemotes(JSON.parse(contents.toString('utf8')))) 44 | .catch((err) => { 45 | if (err.code !== 'ENOENT') return err; 46 | const globalRemotes = new GlobalRemotes({}); 47 | return globalRemotes.write() 48 | .then(() => globalRemotes); 49 | }); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/scope/ci-ops/run-and-update-ci.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import serializeError from 'serialize-error'; 3 | import { buildInScope, testInScope, modifyCIProps } from '../../api/scope'; 4 | 5 | function runAndUpdateCI({ id, scopePath }: { id: string, scopePath: string }): Promise { 6 | function addCIAttrsInTheModel({ error, startTime }: { error?: any, startTime: string }) { 7 | const endTime = Date.now().toString(); 8 | const ciProps = { startTime, endTime, error: undefined }; 9 | 10 | if (error) { 11 | const serializedError = serializeError(error); 12 | ciProps.error = serializedError; 13 | return modifyCIProps(scopePath, id, ciProps); 14 | } 15 | 16 | return modifyCIProps(scopePath, id, ciProps); 17 | } 18 | 19 | const startTime = Date.now().toString(); 20 | 21 | try { 22 | // define options 23 | const environment = true; 24 | const save = true; 25 | const verbose = true; 26 | 27 | return buildInScope({ id, scopePath, environment, save, verbose }) 28 | .then(() => testInScope({ id, scopePath, environment, save, verbose })) 29 | .then((specsResults) => { 30 | return addCIAttrsInTheModel({ startTime }).then(() => specsResults); 31 | }) 32 | .catch((e) => { 33 | return addCIAttrsInTheModel({ error: e, startTime }).then(() => { throw e; }); 34 | }); 35 | } catch (e) { 36 | return addCIAttrsInTheModel({ error: e, startTime }).then(() => { throw e; }); 37 | } 38 | } 39 | 40 | module.exports = runAndUpdateCI; 41 | -------------------------------------------------------------------------------- /src/api/consumer/lib/commit.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import R from 'ramda'; 3 | import { loadConsumer } from '../../../consumer'; 4 | import InvalidIdOnCommit from './exceptions/invalid-id-on-commit'; 5 | import ComponentsList from '../../../consumer/component/components-list'; 6 | import { MissingDependencies } from '../../../consumer/exceptions'; 7 | import logger from '../../../logger/logger'; 8 | 9 | export async function commitAction({ id, message, force, verbose }: 10 | { id: string, message: string, force: ?bool, verbose?: bool }) { 11 | try { 12 | const consumer = await loadConsumer(); 13 | const components = await consumer.commit([id], message, force, verbose); 14 | return R.head(components); 15 | } catch (err) { 16 | if (err instanceof MissingDependencies) return Promise.reject(err); 17 | logger.info('real error during commit:', err.message); 18 | // TODO: should be changes, we don't want this to be always the error, it should propagate from the below layer 19 | return Promise.reject(new InvalidIdOnCommit(id)); 20 | } 21 | } 22 | 23 | export async function commitAllAction({ message, force, verbose }: 24 | { message: string, force: ?bool, verbose?: bool }) { 25 | const consumer = await loadConsumer(); 26 | const componentsList = new ComponentsList(consumer); 27 | const commitPendingComponents = await componentsList.listCommitPendingComponents(); 28 | if (R.isEmpty(commitPendingComponents)) return null; 29 | return consumer.commit(commitPendingComponents, message, force, verbose); 30 | } 31 | -------------------------------------------------------------------------------- /e2e/commands/list.e2e.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import Helper from '../e2e-helper'; 3 | 4 | describe('bit list command', function () { 5 | this.timeout(0); 6 | const helper = new Helper(); 7 | after(() => { 8 | helper.destroyEnv(); 9 | }); 10 | describe('when no components created', () => { 11 | before(() => { 12 | helper.cleanEnv(); 13 | helper.runCmd('bit init'); 14 | }); 15 | it('should display "Total 0 components"', () => { 16 | const output = helper.runCmd('bit list'); 17 | expect(output.includes('Total 0 components')).to.be.true; 18 | }); 19 | }); 20 | describe('when a component is created but not committed', () => { 21 | before(() => { 22 | helper.cleanEnv(); 23 | helper.runCmd('bit init'); 24 | helper.createComponentBarFoo(); 25 | }); 26 | it('should display "Total 0 components"', () => { 27 | const output = helper.runCmd('bit list'); 28 | expect(output.includes('Total 0 components')).to.be.true; 29 | }); 30 | }); 31 | describe('when a component is created and committed', () => { 32 | before(() => { 33 | helper.cleanEnv(); 34 | helper.runCmd('bit init'); 35 | helper.createComponentBarFoo(); 36 | helper.addComponentBarFoo(); 37 | helper.commitComponentBarFoo(); 38 | }); 39 | it('should display "Total 1 components"', () => { 40 | const output = helper.runCmd('bit list'); 41 | expect(output.includes('Total 1 components')).to.be.true; 42 | }); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /src/consumer/component/sources/source-file.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs-extra'; 2 | import path from 'path'; 3 | import AbstractVinyl from './abstract-vinyl'; 4 | import vinylFile from 'vinyl-file'; 5 | import FileSourceNotFound from '../exceptions/file-source-not-found'; 6 | import logger from '../../../logger/logger'; 7 | 8 | export default class SourceFile extends AbstractVinyl { 9 | // TODO: remove this distFilePath? 10 | distFilePath: ?string; 11 | 12 | static load(filePath: string, distTarget: string, base: string = consumerPath, consumerPath: string, extendedProps: Object): SourceFile|null { 13 | try { 14 | const file = new SourceFile(vinylFile.readSync(filePath, { base, cwd: consumerPath })); 15 | // TODO: remove this distFilePath? 16 | file.distFilePath = path.join(consumerPath, distTarget, file.relative); 17 | for (const k in extendedProps) file[k] = extendedProps[k]; 18 | return file; 19 | } catch (err) { 20 | logger.error(`failed loading file ${filePath}. Error: ${err}`); 21 | if (err.code === 'ENOENT' && err.path) { 22 | throw new FileSourceNotFound(err.path); 23 | } 24 | return null; 25 | } 26 | } 27 | 28 | static loadFromParsedString(parsedString: Object) { 29 | if (!parsedString) return; 30 | const opts = super.loadFromParsedString(parsedString); 31 | return new SourceFile(opts); 32 | } 33 | 34 | static loadFromParsedStringArray(arr: Object[]) { 35 | if (!arr) return; 36 | return arr.map(this.loadFromParsedString); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /specs/api/consumer/lib/get-scope-bit.spec.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import sinon from 'sinon'; 3 | import getScopeBit from '../../../../src/api/consumer/lib/get-scope-component'; 4 | import { ConsumerNotFound } from '../../../../src/consumer/exceptions'; 5 | import { ScopeNotFound } from '../../../../src/scope/exceptions'; 6 | import * as consumer from '../../../../src/consumer'; 7 | import { GlobalRemotes } from '../../../../src/global-config'; 8 | import Remotes from '../../../../src/remotes/remotes'; 9 | import * as scope from '../../../../src/scope'; 10 | 11 | describe('getScopeBit', () => { 12 | let sandbox; 13 | before(() => { 14 | sandbox = sinon.sandbox.create(); 15 | }); 16 | after(() => { 17 | sandbox.restore(); 18 | }); 19 | // todo: it fails on Circle-CI for no reason. Fix it there, then, enable it here 20 | xit('should show a component when is running outside a scope', async () => { 21 | sandbox.stub(consumer, 'loadConsumer').returns(Promise.reject(new ConsumerNotFound())); 22 | sandbox.stub(scope, 'loadScope').returns(Promise.reject(new ScopeNotFound())); 23 | sandbox.stub(GlobalRemotes, 'load').returns(Promise.resolve({ toPlainObject: () => {} })); 24 | const showSpy = sandbox.spy(); 25 | const resolveStub = sandbox.stub(Remotes.prototype, 'resolve').returns(Promise.resolve({ show: showSpy })); 26 | 27 | await getScopeBit({ id: 'my-scope/box/name' }); 28 | expect(resolveStub.getCall(0).args[0]).to.equal('my-scope'); 29 | expect(showSpy.called).to.be.true; 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /src/version/version-parser.js: -------------------------------------------------------------------------------- 1 | import Version from './version'; 2 | import { LATEST, LATEST_TESTED_MARK } from '../constants'; 3 | import { contains } from '../utils'; 4 | import { InvalidVersion } from './exceptions'; 5 | 6 | function isLatest(versionStr: string): boolean { 7 | return versionStr === LATEST; 8 | } 9 | 10 | function isLatestTested(versionStr: string) { 11 | if (!contains(versionStr, LATEST_TESTED_MARK)) return false; 12 | const splited = versionStr.split(LATEST_TESTED_MARK); 13 | if (splited.length !== 2) return false; 14 | const [, numberStr] = splited; 15 | const version = parseInt(numberStr); 16 | if (!version) return false; 17 | return true; 18 | } 19 | 20 | function isRegular(versionStr: string) { 21 | return !!parseInt(versionStr); 22 | } 23 | 24 | function returnRegular(versionStr: string) { 25 | return new Version(parseInt(versionStr), false); 26 | } 27 | 28 | function returnLatestTestedVersion(versionStr: string): Version { 29 | const [, numberStr] = versionStr.split(LATEST_TESTED_MARK); 30 | return new Version(parseInt(numberStr), true); 31 | } 32 | 33 | function returnLatest(): Version { 34 | return new Version(null, true); 35 | } 36 | 37 | export default function versionParser(versionStr: string): Version { 38 | if (!versionStr) return returnLatest(); 39 | if (isLatest(versionStr)) return returnLatest(); 40 | if (isLatestTested(versionStr)) return returnLatestTestedVersion(versionStr); 41 | if (isRegular(versionStr)) return returnRegular(versionStr); 42 | throw new InvalidVersion(); 43 | } 44 | -------------------------------------------------------------------------------- /src/version/version.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import { InvalidVersionChange, InvalidVersion } from './exceptions'; 3 | import versionParser from './version-parser'; 4 | 5 | export default class Version { 6 | versionNum: ?number; 7 | latest: boolean; 8 | 9 | constructor(versionNum: number, latest: boolean) { 10 | this.versionNum = versionNum; 11 | this.latest = latest; 12 | } 13 | 14 | increase(): Version { 15 | if (!this.versionNum) throw new InvalidVersionChange(); 16 | this.versionNum = this.versionNum + 1; 17 | return this; 18 | } 19 | 20 | decrease() { 21 | if (!this.versionNum || this.versionNum <= 1) throw new InvalidVersionChange(); 22 | this.versionNum = this.versionNum - 1; 23 | return this; 24 | } 25 | 26 | resolve(availableVersion: number[]) { 27 | const getLatest = () => Math.max(...availableVersion); 28 | 29 | if (this.latest) return getLatest(); 30 | return this.versionNum; 31 | } 32 | 33 | toString() { 34 | if (!this.versionNum && this.latest) return 'latest'; 35 | if (this.versionNum && this.latest) return `*${this.versionNum}`; 36 | if (this.versionNum && !this.latest) return this.versionNum.toString(); 37 | throw new InvalidVersion(); 38 | } 39 | 40 | static parse(versionStr: string): Version { 41 | return versionParser(versionStr); 42 | } 43 | 44 | static validate(versionStr: string): boolean { 45 | try { 46 | versionParser(versionStr); 47 | return true; 48 | } catch (err) { 49 | return false; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/component-resolver/component-resolver.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import path from 'path'; 3 | import glob from 'glob'; 4 | import { BitId } from '../bit-id'; 5 | import { LATEST_BIT_VERSION, 6 | BITS_DIRNAME, 7 | } from '../constants'; 8 | import { ComponentNotFound } from '../scope/exceptions'; 9 | import logger from '../logger/logger'; 10 | 11 | function getLatestVersion(bitId: BitId, componentsDir: string): number { 12 | if (bitId.version !== LATEST_BIT_VERSION) return bitId.version; 13 | const regexRemoveLatestVersion = new RegExp(`${LATEST_BIT_VERSION}$`); 14 | const relativePathWithoutVersion = bitId.toFullPath().replace(regexRemoveLatestVersion, ''); 15 | const pathWithoutVersion = path.join(componentsDir, relativePathWithoutVersion); 16 | const versionsDirs = glob.sync('*', { cwd: pathWithoutVersion }); 17 | if (!versionsDirs || !versionsDirs.length) { 18 | throw new ComponentNotFound(bitId.toString()); 19 | } 20 | return Math.max(versionsDirs); 21 | } 22 | 23 | function componentResolver(componentId: string, mainFilePath: string, projectRoot: string = process.cwd()): string { 24 | const bitId = BitId.parse(componentId); 25 | const componentsDir = path.join(projectRoot, BITS_DIRNAME); 26 | const version = getLatestVersion(bitId, componentsDir); 27 | bitId.version = version.toString(); 28 | const componentPath = path.join(componentsDir, bitId.toFullPath()); 29 | logger.debug(`resolving component, path: ${componentPath}`); 30 | return path.join(componentPath, mainFilePath); 31 | } 32 | 33 | export default componentResolver; 34 | -------------------------------------------------------------------------------- /src/consumer/component/sources/abstract-vinyl.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import path from 'path'; 3 | import fs from 'fs-extra'; 4 | import Vinyl from 'vinyl'; 5 | 6 | export default class AbstractVinyl extends Vinyl { 7 | // Update the base path and keep the relative value to be the same 8 | updatePaths({newBase, newRelative, newCwd}: {newBase: string, newRelative?: string, newCwd?: string}){ 9 | const relative = newRelative || this.relative; 10 | if (newCwd) this.cwd = newCwd 11 | this.base = newBase; 12 | this.path = path.join(this.base, relative); 13 | } 14 | 15 | write(path?: string, force?: boolean = true): Promise { 16 | const filePath = path || this.path; 17 | if (!force && fs.existsSync(filePath)) return Promise.resolve(); 18 | return new Promise((resolve, reject) => { 19 | fs.outputFile(filePath, this.contents, (err, res) => { 20 | if (err) return reject(err); 21 | return resolve(filePath); 22 | }); 23 | }); 24 | } 25 | 26 | static loadFromParsedString(parsedString: Object) { 27 | if (!parsedString) return; 28 | const contents = Buffer.isBuffer(parsedString._contents) ? parsedString._contents : new Buffer(parsedString._contents); 29 | return { 30 | cwd: parsedString._cwd, 31 | path: parsedString.history[parsedString.history.length - 1], 32 | base: parsedString._base, 33 | contents, 34 | }; 35 | } 36 | 37 | static loadFromParsedStringArray(arr: Object[]) { 38 | if (!arr) return; 39 | return arr.map(this.loadFromParsedString); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/utils/resolveLatestVersion.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | /** 3 | * Retrive bitId with the highest version from a list according to provided id 4 | * @description will return the provided id if it's not with latest, if the list contains id without version / with the latest version it will return it 5 | * @param {BitId[] | string[]} bitIds 6 | * @param { string | BitId } bitId 7 | * @returns {string} 8 | */ 9 | import maxBy from 'lodash.maxby'; 10 | import { BitId } from '../bit-id'; 11 | 12 | export default function getLatestVersionNumber(bitIds: BitId[] | string[], bitId: string | BitId) { 13 | const getParsed = (id) => (typeof id === 'string') ? BitId.parse(id) : id; 14 | const getString = (id, ignoreScope = false, ignoreVersion = true) => (typeof id === 'string') ? BitId.parse(id).toString(ignoreScope, ignoreVersion) : id.toString(ignoreScope, ignoreVersion); 15 | 16 | const componentId = getParsed(bitId); 17 | if (!componentId.getVersion().latest) return bitId; 18 | const maxByFunc = searchId => (id) => { 19 | if (getString(searchId) === getString(id)) { 20 | const version = getParsed(id).getVersion(); 21 | if (version.latest) return 10000000; 22 | return version.versionNum; 23 | } 24 | return -1; 25 | }; 26 | let result = maxBy(bitIds, maxByFunc(bitId)); 27 | // A case when the bitId provided doesn't exists in the array it will just return one of them 28 | // So we want to make sure it won't return this wrong result 29 | if (getString(result, true) !== getString(bitId, true)) result = bitId; 30 | return result; 31 | } 32 | -------------------------------------------------------------------------------- /src/cli/commands/public-cmds/install-cmd.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import Command from '../../command'; 3 | import { importAction } from '../../../api/consumer'; 4 | import Component from '../../../consumer/component'; 5 | import { ComponentWithDependencies } from '../../../scope'; 6 | import Import from './import-cmd'; 7 | 8 | export default class Install extends Command { 9 | name = 'install'; 10 | description = 'install bit components from bit.json file'; 11 | alias = ''; 12 | opts = [ 13 | ['e', 'environment', 'install development environment dependencies (compiler | tester)'], 14 | ['v', 'verbose', 'show a more verbose output when possible'], 15 | ['p', 'prefix', 'install components into a specific directory'], 16 | ]; 17 | loader = true; 18 | private = true; 19 | 20 | action(args: string[], { verbose, prefix, environment }: 21 | { 22 | verbose?: bool, 23 | prefix?: bool, 24 | environment?: bool, 25 | }): Promise { 26 | if (prefix) { return Promise.reject(new Error('prefix option currently not supported')); } 27 | 28 | return importAction({ ids: [], tester: false, compiler: false, verbose, prefix, environment }); 29 | } 30 | 31 | report({ dependencies, envDependencies, warnings }: { 32 | dependencies?: ComponentWithDependencies[], 33 | envDependencies?: Component[], 34 | warnings?: { 35 | notInPackageJson: [], 36 | notInNodeModules: [], 37 | notInBoth:[], 38 | } 39 | }): string { 40 | const importCmd = new Import(); 41 | return importCmd.report({ dependencies, envDependencies, warnings }); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/scope/component-objects.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import BitObject from './objects/object'; 3 | import Repository from './objects/repository'; 4 | 5 | export default class ComponentObjects { 6 | component: Buffer; 7 | objects: Buffer[]; 8 | 9 | constructor(component: Buffer, objects: Buffer[]) { 10 | this.component = component; 11 | this.objects = objects; 12 | } 13 | 14 | // @TODO optimize ASAP. 15 | toString(): string { 16 | return JSON.stringify({ 17 | component: this.component, 18 | objects: this.objects 19 | }); 20 | } 21 | 22 | // @TODO optimize ASAP. 23 | static fromString(str: string): ComponentObjects { 24 | return ComponentObjects.fromObject(JSON.parse(str)); 25 | } 26 | 27 | static manyToString(componentsAndObjects: Array<{ component: Buffer, objects: Buffer[] }>) { 28 | return JSON.stringify(componentsAndObjects); 29 | } 30 | 31 | static manyFromString(str: string): ComponentObjects { 32 | return JSON.parse(str).map(componentObject => ComponentObjects.fromObject(componentObject)); 33 | } 34 | 35 | static fromObject(object: Object): ComponentObjects { 36 | const { component, objects } = object; 37 | 38 | return new ComponentObjects( 39 | new Buffer(component), 40 | objects.map(obj => new Buffer(obj)) 41 | ); 42 | } 43 | 44 | toObjects(repo: Repository): { component: BitObject, objects: BitObject[] } { 45 | return { 46 | component: BitObject.parseSync(this.component, repo.types), 47 | objects: this.objects.map(obj => BitObject.parseSync(obj, repo.types)) 48 | }; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/api/consumer/lib/global-config.js: -------------------------------------------------------------------------------- 1 | /** @flow */ 2 | import { GlobalConfig } from '../../../global-config'; 3 | import Config from '../../../global-config/config'; 4 | 5 | export function set(key: string, val: string): Promise { 6 | return GlobalConfig.load() 7 | .then((config) => { 8 | config.set(key, val); 9 | return config.write() 10 | .then(() => config); 11 | }); 12 | } 13 | 14 | export function setSync(key: string, val: string): Config { 15 | const config = GlobalConfig.loadSync(); 16 | config.set(key, val); 17 | config.writeSync(); 18 | return config; 19 | } 20 | 21 | export function del(key: string): Promise { 22 | return GlobalConfig.load() 23 | .then((config) => { 24 | config.delete(key); 25 | return config.write() 26 | .then(() => config); 27 | }); 28 | } 29 | 30 | export function delSync(key: string): Config { 31 | const config = GlobalConfig.loadSync(); 32 | config.delete(key); 33 | config.writeSync(); 34 | return config; 35 | } 36 | 37 | export function get(key: string): Promise { 38 | return GlobalConfig.load() 39 | .then((config) => { 40 | return config.get(key); 41 | }); 42 | } 43 | 44 | export function getSync(key: string): ?string { 45 | const config = GlobalConfig.loadSync(); 46 | return config.get(key); 47 | } 48 | 49 | export function list(): Promise { 50 | return GlobalConfig.load() 51 | .then(config => config.toPlainObject()); 52 | } 53 | 54 | export function listSync(): any { 55 | const config = GlobalConfig.loadSync(); 56 | return config.toPlainObject(); 57 | } 58 | -------------------------------------------------------------------------------- /docs/why-code-component-manager.md: -------------------------------------------------------------------------------- 1 | 2 | # Why Code Component Management? 3 | 4 | As the world moves to a multi-repository architecture we increasingly need to use the same code components in multiple places again and again. People are re-inventing and duplicating code components already written before on daily basis. 5 | 6 | This wastes precious time and effort which can be better invested in building new things. Components can be improved and evolved without having to start from scratch every time. Duplicating components across repositories isn't any better, as maintaining or changing a single function becomes an odyssey, and the larger the code base the worse it gets. 7 | 8 | Up until Bit, the only way to make components reusable was to spend too much time on boilerplating and build configuration just to end up having to maintain a git repository, a package and CI for every small component. This isn’t practical and is very hard to scale. 9 | 10 | To understand the solution we turned back to the basics of writing code, as we were taught from day one. We should create independent and isolated components. We should also manage them to make this a practical practice. 11 | 12 | Bit is a distributed and virtual component repository designed to be language agnostic. It allows you to make components reusable with zero initial configuration and use these components across repositories. It also helps to store, organize and manage your components. It allows you to group your components by context, while also handling versioning, dependency management, build and test execution and more. Bit also makes components easy to find and collaborate on. 13 | 14 | --------------------------------------------------------------------------------