UNPKG

8.64 kBJavaScriptView Raw
1"use strict";
2var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3 return new (P || (P = Promise))(function (resolve, reject) {
4 function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5 function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6 function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
7 step((generator = generator.apply(thisArg, _arguments || [])).next());
8 });
9};
10var __importDefault = (this && this.__importDefault) || function (mod) {
11 return (mod && mod.__esModule) ? mod : { "default": mod };
12};
13Object.defineProperty(exports, "__esModule", { value: true });
14const react_1 = __importDefault(require("react"));
15const ink_1 = require("ink");
16const algolia_1 = require("./algolia");
17const Footer_1 = require("./components/Footer");
18const Install_1 = __importDefault(require("./components/Install"));
19const Overview_1 = __importDefault(require("./components/Overview"));
20const Package_1 = __importDefault(require("./components/Package"));
21const Scroll_1 = __importDefault(require("./components/Scroll"));
22const Search_1 = __importDefault(require("./components/Search"));
23const installer_1 = require("./installer");
24const utils_1 = require("./utils");
25const SPACE = ' ';
26const ARROW_UP = '\u001B[A';
27const ARROW_DOWN = '\u001B[B';
28const ENTER = '\r';
29const CTRL_C = '\x03';
30class Emma extends react_1.default.Component {
31 constructor(props) {
32 super(props);
33 this.state = {
34 view: 'SEARCH',
35 query: '',
36 page: 0,
37 hits: [],
38 loading: false,
39 dependencies: {},
40 status: 'NOT_STARTED',
41 };
42 /**
43 * Creates a new dependency if newly selected or toggles the existing one.
44 */
45 this.toggleDependency = (pkg) => {
46 const { dependencies } = this.state;
47 const dependency = dependencies[pkg.name];
48 if (dependency === undefined) {
49 this.setState({
50 dependencies: Object.assign({}, dependencies, { [pkg.name]: { name: pkg.name, type: 'dependency' } }),
51 });
52 }
53 else {
54 const nextType = installer_1.getNextDependencyType(dependency.type);
55 if (nextType) {
56 this.setState({
57 dependencies: Object.assign({}, dependencies, { [pkg.name]: { name: pkg.name, type: nextType } }),
58 });
59 }
60 else {
61 this.setState({
62 dependencies: utils_1.removeKey(pkg.name, dependencies),
63 });
64 }
65 }
66 };
67 this.handleQueryChange = this.handleQueryChange.bind(this);
68 this.handleInput = this.handleInput.bind(this);
69 this.handleWillReachEnd = this.handleWillReachEnd.bind(this);
70 this.installDependencies = this.installDependencies.bind(this);
71 }
72 componentDidMount() {
73 const { stdin, setRawMode } = this.props;
74 if (setRawMode)
75 setRawMode(true);
76 stdin.on('data', this.handleInput);
77 }
78 componentWillUnmount() {
79 const { stdin, setRawMode } = this.props;
80 stdin.removeListener('data', this.handleInput);
81 if (setRawMode)
82 setRawMode(false);
83 }
84 /**
85 * Keyboard events manager split based on the active view.
86 */
87 handleInput(data) {
88 return __awaiter(this, void 0, void 0, function* () {
89 const s = String(data);
90 /**
91 * Create an exit listener.
92 */
93 if (s === CTRL_C) {
94 process.exit(0);
95 }
96 switch (this.state.view) {
97 case 'SEARCH': {
98 if (s === ARROW_DOWN || s === ENTER || SPACE) {
99 this.setState({ view: 'SCROLL' });
100 }
101 return;
102 }
103 case 'SCROLL': {
104 if (s === ENTER) {
105 this.setState({ view: 'OVERVIEW' });
106 }
107 return;
108 }
109 case 'OVERVIEW': {
110 if (s === ARROW_UP || ARROW_DOWN) {
111 this.setState({ view: 'SCROLL' });
112 }
113 if (s === ENTER) {
114 if (Object.values(this.state.dependencies).length > 0) {
115 this.setState({ view: 'INSTALL' });
116 try {
117 yield this.installDependencies();
118 process.exit(0);
119 }
120 catch (err) {
121 process.exit(1);
122 }
123 }
124 else {
125 process.exit(0);
126 }
127 }
128 return;
129 }
130 case 'INSTALL': {
131 return;
132 }
133 }
134 });
135 }
136 /**
137 * Whenever input changes, switch to the initial screen, change the value
138 * of the query accordingly, reset pagination and perform search.
139 */
140 handleQueryChange(value) {
141 return __awaiter(this, void 0, void 0, function* () {
142 this.setState({
143 query: value,
144 page: 0,
145 view: 'SEARCH',
146 loading: true,
147 });
148 const res = yield algolia_1.search(value);
149 if (res.query === this.state.query) {
150 this.setState({ hits: res.hits, loading: false });
151 }
152 });
153 }
154 /**
155 * Start querying new hits and update pagination. But limit pagniation to
156 * ten pages.
157 */
158 handleWillReachEnd() {
159 return __awaiter(this, void 0, void 0, function* () {
160 const { query, hits, page } = this.state;
161 if (page > 10)
162 return;
163 const res = yield algolia_1.search(query, page + 1);
164 if (res.query === this.state.query && res.page - 1 === this.state.page) {
165 this.setState({
166 page: res.page,
167 hits: [...hits, ...res.hits],
168 });
169 }
170 });
171 }
172 installDependencies() {
173 return __awaiter(this, void 0, void 0, function* () {
174 this.setState({ status: 'LOADING' });
175 try {
176 yield installer_1.install(Object.values(this.state.dependencies), 'dependency');
177 yield installer_1.install(Object.values(this.state.dependencies), 'devDependency');
178 this.setState({ status: 'INSTALLED' });
179 }
180 catch (err) {
181 this.setState({ status: 'ERROR' });
182 throw err;
183 }
184 });
185 }
186 render() {
187 const { view, query, loading, hits, dependencies, status } = this.state;
188 return (react_1.default.createElement(algolia_1.SearchContext.Provider, { value: hits },
189 react_1.default.createElement(ink_1.Box, { flexDirection: "column" },
190 react_1.default.createElement(Search_1.default, { value: query, onChange: this.handleQueryChange, loading: loading, active: true }),
191 react_1.default.createElement(Scroll_1.default, { values: this.state.hits, onWillReachEnd: this.handleWillReachEnd, active: view === 'SCROLL' }, pkg => (react_1.default.createElement(Package_1.default, { key: pkg.objectID, pkg: pkg, onClick: this.toggleDependency, active: pkg.active, type: (dependencies[pkg.name] || {}).type }))),
192 react_1.default.createElement(Overview_1.default, { dependencies: Object.values(dependencies), active: view === 'OVERVIEW' }),
193 react_1.default.createElement(Install_1.default, { dependencies: Object.values(dependencies), status: status, active: view === 'INSTALL' }),
194 react_1.default.createElement(Footer_1.Footer, null))));
195 }
196}
197class EmmaWithStdin extends react_1.default.Component {
198 render() {
199 return (react_1.default.createElement(ink_1.StdinContext.Consumer, null, ({ stdin, setRawMode }) => (react_1.default.createElement(Emma, Object.assign({}, this.props, { stdin: stdin, setRawMode: setRawMode })))));
200 }
201}
202exports.default = EmmaWithStdin;
203//# sourceMappingURL=index.js.map
\No newline at end of file