/
index.ts
192 lines (176 loc) · 7.09 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
#!/usr/bin/env node
import {createCommand} from 'commander';
import {execSync} from 'child_process';
import Fuse from 'fuse.js';
import inquirer from 'inquirer';
import fs from 'fs';
import path from 'path';
import inquirerPrompt from 'inquirer-autocomplete-prompt';
import chalk from 'chalk';
import {version} from './version.js';
const COMMAND_HISTORY_FILE = fs.existsSync('.vscode')? `${process.cwd()}/.vscode/yw_helper_history.txt` : `${process.cwd()}/.idea/yw_helper_history.txt`;
inquirer.registerPrompt('autocomplete', inquirerPrompt);
const yarnWorkspaceRun = createCommand();
yarnWorkspaceRun.version(version, '-v, --version', 'output the current version');
yarnWorkspaceRun
.allowUnknownOption(true)
.argument('[workspaceName]', 'The name of the workspace')
.argument('[command...]', 'The command to run')
.option('-r, --re-run', 'Run again the last command')
.option('-c, --command-history', 'Show command history')
.option('-w, --reuse-workspace', 'Use the same workspace as last time')
.action(async (workspaceNameInputOrCommand: string, commandInput: string[]) => {
verifyYarn();
if(yarnWorkspaceRun.opts().commandHistory) {
const commandHistory = getCommandHistory();
const setOfLastCommands = Array.from(new Set(commandHistory.reverse()));
const { selectedCommand } = await inquirer.prompt([
{
type: 'list',
name: 'selectedCommand',
message: 'Select a command to run:',
choices: setOfLastCommands,
},
]);
console.log(chalk.green(`Running selected command: ${selectedCommand}`));
appendToCommandHistory(selectedCommand);
execSync(selectedCommand, { stdio: 'inherit' });
return;
}
if (yarnWorkspaceRun.opts().reRun) {
const lastCommand = getCommandHistory().pop();
if (lastCommand) {
console.log(chalk.green(`Running last command: ${lastCommand}`));
execSync(lastCommand, {stdio: 'inherit'});
return;
} else {
console.log(chalk.yellow('Could not find last command'));
}
}
const allWorkspaces: Record<string, string> =
listWorkspaces()
.split('\n')
.filter(line => line !== '')
.map((line) => JSON.parse(line))
.filter(({location}) => location !== '.')
.reduce((acc, {location, name}) => {
acc[name] = location;
return acc;
}, {})
const allWorkspacesNames = Object.keys(allWorkspaces);
const isSingleWorkspace = allWorkspacesNames.length === 1;
let workspaceName: string;
const lastCommand = getCommandHistory().pop();
if (yarnWorkspaceRun.opts().reuseWorkspace && lastCommand) {
const lastWorkspace = lastCommand.split(' ')[2];
if (lastWorkspace === undefined) {
console.log(chalk.red('Could not find last workspace'));
process.exit(1);
}
workspaceName = lastWorkspace;
workspaceNameInputOrCommand && commandInput.unshift(workspaceNameInputOrCommand);
console.log(chalk.green(`Using last workspace: ${workspaceName}`));
} else if (isSingleWorkspace) {
workspaceName = allWorkspacesNames[0];
// if there is only one workspace, we assume workspaceNameInputOrCommand is actually a command so we add it to the commandInput array
workspaceNameInputOrCommand && commandInput.unshift(workspaceNameInputOrCommand);
console.log(chalk.green(`Found workspace: ${workspaceName}`));
} else {
const workspaceNameInput = workspaceNameInputOrCommand;
workspaceName = await getWorkspaceName(workspaceNameInput, allWorkspacesNames);
}
let script: string;
if (commandInput.length > 0) {
script = commandInput.join(' ');
} else if (allWorkspacesNames.length === 1 && workspaceNameInputOrCommand) {
script = workspaceNameInputOrCommand.concat(...commandInput); //if there is only one workspace, we assume workspaceNameInputOrCommand is actually a command
} else {
script = await askForScriptToRun(allWorkspaces[workspaceName]);
}
const commandToRun = script !== 'run' ? script : await askForCustomCommandToRun();
const finalCommand = `yarn workspace ${workspaceName} ${commandToRun}`;
console.log(chalk.green(`Running: ${finalCommand}`));
appendToCommandHistory(finalCommand);
// execSync(`echo '${finalCommand}' | tee -a ~/.bash_history ~/.zsh_history >/dev/null && history -r ~/.bash_history 2> /dev/null || fc -R`)
execSync(finalCommand, {stdio: 'inherit'});
});
yarnWorkspaceRun.parse(process.argv);
function appendToCommandHistory(command: string){
execSync(`mkdir -p ${path.dirname(COMMAND_HISTORY_FILE)} && touch ${COMMAND_HISTORY_FILE}`);
fs.appendFileSync(COMMAND_HISTORY_FILE, command + '\n');
}
async function getWorkspaceName(workspaceNameInput: string | undefined, workspacesNames: string[]) {
const fuseWorkspaceNames = new Fuse(workspacesNames, {ignoreLocation: true});
if (workspaceNameInput) {
const foundName = fuseWorkspaceNames.search(workspaceNameInput)[0]?.item
if (foundName) {
console.log(chalk.green(`Found workspace: ${foundName}`));
return foundName;
} else {
console.log(chalk.red(`Could not find workspace with the name of ${workspaceNameInput}, please select workspace from the list below:`));
}
}
return (await inquirer
.prompt([
{
type: 'autocomplete',
name: 'workspaceName',
message: 'Workspace',
source: (_answersSoFar: any, input: string) => {
if (!input) {
return workspacesNames;
}
return fuseWorkspaceNames.search(input).map(({item}) => item);
},
},
])).workspaceName;
}
async function askForScriptToRun(workspaceLocation: string) {
const workspaceScripts = Object.keys(JSON.parse(fs.readFileSync(`${workspaceLocation}/package.json`, 'utf-8')).scripts || {});
workspaceScripts.unshift('run');
const fuseWorkspaceScripts = new Fuse(workspaceScripts, {ignoreLocation: true});
return (await inquirer
.prompt([
{
type: 'autocomplete',
name: 'script',
message: 'Script',
source: (_answersSoFar: any, input: string) => {
if (!input) {
return workspaceScripts;
}
return fuseWorkspaceScripts.search(input).map(({item}) => item);
},
},
])).script;
}
async function askForCustomCommandToRun() {
return (await inquirer.prompt({
message: 'Run:',
type: 'input',
name: 'commandToRun',
})).commandToRun;
}
function verifyYarn() {
try {
execSync('yarn --version', {stdio: 'pipe'});
} catch (e) {
console.log(chalk.red('yarn is not installed, please install yarn before using this tool'));
process.exit(1);
}
}
function getCommandHistory() {
if (fs.existsSync(COMMAND_HISTORY_FILE)) {
return fs.readFileSync(COMMAND_HISTORY_FILE, 'utf-8').trim().split('\n');
}
return [];
}
function listWorkspaces() {
try {
return execSync('yarn workspaces list --json', {stdio: 'pipe'})
.toString()
} catch (e) {
console.log(chalk.red('yarn workspaces list failed, please make sure you are running this command from the root of the monorepo'));
process.exit(1);
}
}