@blitzjs/installer
Version:
Package installation for the Blitz CLI
1,581 lines (1,393 loc) • 56.1 kB
JavaScript
import { Box, Text, useInput, useStdin, useApp, render } from 'ink';
import { baseLogger } from 'next/dist/server/lib/logging';
import React, { createElement, Fragment, useState, useCallback, useEffect, useMemo } from 'react';
import { spawn } from 'cross-spawn';
import { existsSync, readFileSync, writeFileSync } from 'fs-extra';
import Spinner from 'ink-spinner';
import { resolve } from 'path';
import { createPatch } from 'diff';
import j from 'jscodeshift';
import getBabelOptions from 'recast/parsers/_babel_options';
import { parser } from 'recast/parsers/babel';
import { prompt } from 'enquirer';
import globby from 'globby';
import { Generator } from '@blitzjs/generator';
import { getSchema, printSchema } from '@mrleebo/prisma-ast';
function _extends() {
_extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
var Newline = function Newline(_ref) {
var _ref$count = _ref.count,
count = _ref$count === void 0 ? 1 : _ref$count;
return /*#__PURE__*/createElement(Box, {
paddingBottom: count
});
};
var EnterToContinue = function EnterToContinue(_ref) {
var _ref$message = _ref.message,
message = _ref$message === void 0 ? 'Press ENTER to continue' : _ref$message;
return /*#__PURE__*/createElement(Fragment, null, /*#__PURE__*/createElement(Newline, null), /*#__PURE__*/createElement(Text, {
bold: true
}, message));
};
function useEnterToContinue(cb, additionalCondition) {
if (additionalCondition === void 0) {
additionalCondition = true;
}
useInput(function (_input, key) {
if (additionalCondition && key["return"]) {
cb();
}
});
}
function useUserInput(cliFlags) {
var _useStdin = useStdin(),
isRawModeSupported = _useStdin.isRawModeSupported;
return isRawModeSupported && !cliFlags.yesToAll;
}
function isDynamicExecutorArgument(input) {
return typeof input === 'function';
}
function Frontmatter(_ref) {
var executor = _ref.executor;
var lineLength = executor.stepName.length + 6;
var verticalBorder = "+" + new Array(lineLength).fill('–').join('') + "+";
return /*#__PURE__*/createElement(Box, {
flexDirection: "column",
paddingBottom: 1
}, /*#__PURE__*/createElement(Newline, null), /*#__PURE__*/createElement(Box, {
flexDirection: "column"
}, /*#__PURE__*/createElement(Text, {
color: "#8a3df0",
bold: true
}, verticalBorder), /*#__PURE__*/createElement(Text, {
color: "#8a3df0",
bold: true
}, "\u23AA\xA0\xA0\xA0", executor.stepName, "\xA0\xA0\xA0\u23AA"), /*#__PURE__*/createElement(Text, {
color: "#8a3df0",
bold: true
}, verticalBorder)), /*#__PURE__*/createElement(Text, {
color: "gray",
italic: true
}, executor.explanation));
}
function getExecutorArgument(input, cliArgs) {
if (isDynamicExecutorArgument(input)) {
return input(cliArgs);
}
return input;
}
function isAddDependencyExecutor(executor) {
return executor.packages !== undefined;
}
var type$4 = 'add-dependency';
function Package(_ref) {
var pkg = _ref.pkg,
loading = _ref.loading;
return /*#__PURE__*/createElement(Text, null, " ", loading ? /*#__PURE__*/createElement(Spinner, null) : '📦', " " + pkg.name + "@" + pkg.version);
}
var DependencyList = function DependencyList(_ref2) {
var _ref2$lede = _ref2.lede,
lede = _ref2$lede === void 0 ? 'Hang tight! Installing dependencies...' : _ref2$lede,
_ref2$depsLoading = _ref2.depsLoading,
depsLoading = _ref2$depsLoading === void 0 ? false : _ref2$depsLoading,
_ref2$devDepsLoading = _ref2.devDepsLoading,
devDepsLoading = _ref2$devDepsLoading === void 0 ? false : _ref2$devDepsLoading,
packages = _ref2.packages;
var prodPackages = packages.filter(function (p) {
return !p.isDevDep;
});
var devPackages = packages.filter(function (p) {
return p.isDevDep;
});
return /*#__PURE__*/createElement(Box, {
flexDirection: "column"
}, /*#__PURE__*/createElement(Text, null, lede), /*#__PURE__*/createElement(Newline, null), prodPackages.length ? /*#__PURE__*/createElement(Text, null, "Dependencies to be installed:") : null, prodPackages.map(function (pkg) {
return /*#__PURE__*/createElement(Package, {
key: pkg.name,
pkg: pkg,
loading: depsLoading
});
}), /*#__PURE__*/createElement(Newline, null), devPackages.length ? /*#__PURE__*/createElement(Text, null, "Dev Dependencies to be installed:") : null, devPackages.map(function (pkg) {
return /*#__PURE__*/createElement(Package, {
key: pkg.name,
pkg: pkg,
loading: devDepsLoading
});
}));
};
/**
* Exported for unit testing purposes
*/
function getPackageManager() {
if (existsSync(resolve('yarn.lock'))) {
return 'yarn';
}
return 'npm';
}
/**
* Exported for unit testing purposes
*/
async function installPackages(packages, isDev) {
if (isDev === void 0) {
isDev = false;
}
var packageManager = getPackageManager();
var isNPM = packageManager === 'npm';
var pkgInstallArg = isNPM ? 'install' : 'add';
var args = [pkgInstallArg];
if (isDev) {
args.push(isNPM ? '--save-dev' : '-D');
}
packages.forEach(function (pkg) {
pkg.version ? args.push(pkg.name + "@" + pkg.version) : args.push(pkg.name);
});
await new Promise(function (resolve) {
var cp = spawn(packageManager, args, {
stdio: ['inherit', 'pipe', 'pipe']
});
cp.on('exit', resolve);
});
}
var Commit$4 = function Commit(_ref3) {
var cliArgs = _ref3.cliArgs,
cliFlags = _ref3.cliFlags,
step = _ref3.step,
onChangeCommitted = _ref3.onChangeCommitted;
var userInput = useUserInput(cliFlags);
var _React$useState = useState(false),
depsInstalled = _React$useState[0],
setDepsInstalled = _React$useState[1];
var _React$useState2 = useState(false),
devDepsInstalled = _React$useState2[0],
setDevDepsInstalled = _React$useState2[1];
var handleChangeCommitted = useCallback(function () {
var packages = step.packages;
var dependencies = packages.length === 1 ? 'dependency' : 'dependencies';
onChangeCommitted("Installed " + packages.length + " " + dependencies);
}, [onChangeCommitted, step]);
useEffect(function () {
async function installDeps() {
var packagesToInstall = getExecutorArgument(step.packages, cliArgs).filter(function (p) {
return !p.isDevDep;
});
await installPackages(packagesToInstall);
setDepsInstalled(true);
} // eslint-disable-next-line @typescript-eslint/no-floating-promises
installDeps();
}, [cliArgs, step]);
useEffect(function () {
if (!depsInstalled) return;
async function installDevDeps() {
var packagesToInstall = getExecutorArgument(step.packages, cliArgs).filter(function (p) {
return p.isDevDep;
});
await installPackages(packagesToInstall, true);
setDevDepsInstalled(true);
} // eslint-disable-next-line @typescript-eslint/no-floating-promises
installDevDeps();
}, [cliArgs, depsInstalled, step]);
useEffect(function () {
if (depsInstalled && devDepsInstalled) {
handleChangeCommitted();
}
}, [depsInstalled, devDepsInstalled, handleChangeCommitted]);
if (!isAddDependencyExecutor(step)) {
onChangeCommitted();
return null;
}
var childProps = {
depsInstalled: depsInstalled,
devDepsInstalled: devDepsInstalled,
handleChangeCommitted: handleChangeCommitted,
step: step,
cliArgs: cliArgs
};
if (userInput) return /*#__PURE__*/createElement(CommitWithInput$3, childProps);else return /*#__PURE__*/createElement(CommitWithoutInput$3, childProps);
};
var CommitWithInput$3 = function CommitWithInput(_ref4) {
var depsInstalled = _ref4.depsInstalled,
devDepsInstalled = _ref4.devDepsInstalled,
handleChangeCommitted = _ref4.handleChangeCommitted,
step = _ref4.step,
cliArgs = _ref4.cliArgs;
useEnterToContinue(handleChangeCommitted, depsInstalled && devDepsInstalled);
return /*#__PURE__*/createElement(DependencyList, {
depsLoading: !depsInstalled,
devDepsLoading: !devDepsInstalled,
packages: getExecutorArgument(step.packages, cliArgs)
});
};
var CommitWithoutInput$3 = function CommitWithoutInput(_ref5) {
var depsInstalled = _ref5.depsInstalled,
devDepsInstalled = _ref5.devDepsInstalled,
step = _ref5.step,
cliArgs = _ref5.cliArgs;
return /*#__PURE__*/createElement(DependencyList, {
depsLoading: !depsInstalled,
devDepsLoading: !devDepsInstalled,
packages: getExecutorArgument(step.packages, cliArgs)
});
};
var AddDependencyExecutor = /*#__PURE__*/Object.freeze({
__proto__: null,
isAddDependencyExecutor: isAddDependencyExecutor,
type: type$4,
getPackageManager: getPackageManager,
installPackages: installPackages,
Commit: Commit$4
});
function _arrayLikeToArray(arr, len) {
if (len == null || len > arr.length) len = arr.length;
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
return arr2;
}
function _unsupportedIterableToArray(o, minLen) {
if (!o) return;
if (typeof o === "string") return _arrayLikeToArray(o, minLen);
var n = Object.prototype.toString.call(o).slice(8, -1);
if (n === "Object" && o.constructor) n = o.constructor.name;
if (n === "Map" || n === "Set") return Array.from(o);
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
}
function _createForOfIteratorHelperLoose(o, allowArrayLike) {
var it;
if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) {
if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") {
if (it) o = it;
var i = 0;
return function () {
if (i >= o.length) return {
done: true
};
return {
done: false,
value: o[i++]
};
};
}
throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
it = o[Symbol.iterator]();
return it.next.bind(it);
}
var customTsParser = {
parse: function parse(source, options) {
var babelOptions = getBabelOptions(options);
babelOptions.plugins.push('typescript');
babelOptions.plugins.push('jsx');
return parser.parse(source, babelOptions);
}
};
var TransformStatus;
(function (TransformStatus) {
TransformStatus["Success"] = "success";
TransformStatus["Failure"] = "failure";
})(TransformStatus || (TransformStatus = {}));
function stringProcessFile(original, transformerFn) {
return transformerFn(original);
}
async function processFile(original, transformerFn) {
var program = j(original, {
parser: customTsParser
});
return (await transformerFn(program)).toSource();
}
async function transform(processFile, targetFilePaths) {
var results = [];
for (var _iterator = _createForOfIteratorHelperLoose(targetFilePaths), _step; !(_step = _iterator()).done;) {
var filePath = _step.value;
if (!existsSync(filePath)) {
results.push({
status: TransformStatus.Failure,
filename: filePath,
error: new Error("Error: " + filePath + " not found")
});
}
try {
var fileBuffer = readFileSync(filePath);
var fileSource = fileBuffer.toString('utf-8');
var transformedCode = await processFile(fileSource);
writeFileSync(filePath, transformedCode);
results.push({
status: TransformStatus.Success,
filename: filePath
});
} catch (err) {
results.push({
status: TransformStatus.Failure,
filename: filePath,
error: err
});
}
}
return results;
}
var SearchType;
(function (SearchType) {
SearchType[SearchType["file"] = 0] = "file";
SearchType[SearchType["directory"] = 1] = "directory";
})(SearchType || (SearchType = {}));
function getMatchingFiles(filter) {
if (filter === void 0) {
filter = '';
}
return globby(filter, {
expandDirectories: true
});
}
async function filePrompt(options) {
var choices = options.getChoices ? options.getChoices(options.context) : await getMatchingFiles(options.globFilter);
if (choices.length === 1) {
return choices[0];
}
var results = await prompt({
type: 'autocomplete',
name: 'file',
message: 'Select the target file',
// @ts-ignore
limit: 10,
choices: choices
});
return results.file;
}
function isFileTransformExecutor(executor) {
return executor.transform !== undefined || executor.transformPlain !== undefined;
}
var type$3 = 'file-transform';
var Propose = function Propose(_ref) {
var cliArgs = _ref.cliArgs,
cliFlags = _ref.cliFlags,
onProposalAccepted = _ref.onProposalAccepted,
step = _ref.step;
var userInput = useUserInput(cliFlags);
var _React$useState = useState(null),
diff = _React$useState[0],
setDiff = _React$useState[1];
var _React$useState2 = useState(null),
error = _React$useState2[0],
setError = _React$useState2[1];
var _React$useState3 = useState(''),
filePath = _React$useState3[0],
setFilePath = _React$useState3[1];
var _React$useState4 = useState(false),
proposalAccepted = _React$useState4[0],
setProposalAccepted = _React$useState4[1];
var acceptProposal = useCallback(function () {
setProposalAccepted(true);
onProposalAccepted(filePath);
}, [onProposalAccepted, filePath]);
useEffect(function () {
async function generateDiff() {
var fileToTransform = await filePrompt({
context: cliArgs,
globFilter: getExecutorArgument(step.singleFileSearch, cliArgs),
getChoices: step.selectTargetFiles
});
setFilePath(fileToTransform);
var originalFile = readFileSync(fileToTransform).toString('utf-8');
var newFile = await (step.transformPlain ? stringProcessFile(originalFile, step.transformPlain) : processFile(originalFile, step.transform));
return createPatch(fileToTransform, originalFile, newFile);
}
generateDiff().then(setDiff, setError);
}, [cliArgs, step]); // Let the renderer deal with errors from file transformers, otherwise the
// process would just hang.
if (error) throw error;
if (!diff) {
return /*#__PURE__*/createElement(Box, null, /*#__PURE__*/createElement(Text, null, /*#__PURE__*/createElement(Spinner, null), "Generating file diff..."));
}
var childProps = {
diff: diff,
filePath: filePath,
proposalAccepted: proposalAccepted,
acceptProposal: acceptProposal
};
if (userInput) return /*#__PURE__*/createElement(ProposeWithInput, childProps);else return /*#__PURE__*/createElement(ProposeWithoutInput, childProps);
};
var Diff = function Diff(_ref2) {
var diff = _ref2.diff;
return /*#__PURE__*/createElement(Fragment, null, diff.split('\n').slice(2).map(function (line, idx) {
var styleProps = {};
if (line.startsWith('-') && !line.startsWith('---')) {
styleProps.bold = true;
styleProps.color = 'red';
} else if (line.startsWith('+') && !line.startsWith('+++')) {
styleProps.bold = true;
styleProps.color = 'green';
}
return /*#__PURE__*/createElement(Text, _extends({}, styleProps, {
key: idx
}), line);
}));
};
var ProposeWithInput = function ProposeWithInput(_ref3) {
var diff = _ref3.diff,
filePath = _ref3.filePath,
proposalAccepted = _ref3.proposalAccepted,
acceptProposal = _ref3.acceptProposal;
useEnterToContinue(acceptProposal, filePath !== '' && !proposalAccepted);
return /*#__PURE__*/createElement(Box, {
flexDirection: "column"
}, /*#__PURE__*/createElement(Diff, {
diff: diff
}), /*#__PURE__*/createElement(EnterToContinue, {
message: "The above changes will be made. Press ENTER to continue"
}));
};
var ProposeWithoutInput = function ProposeWithoutInput(_ref4) {
var diff = _ref4.diff,
filePath = _ref4.filePath,
proposalAccepted = _ref4.proposalAccepted,
acceptProposal = _ref4.acceptProposal;
useEffect(function () {
if (filePath !== '' && !proposalAccepted) {
acceptProposal();
}
}, [acceptProposal, filePath, proposalAccepted]);
return /*#__PURE__*/createElement(Box, {
flexDirection: "column"
}, /*#__PURE__*/createElement(Diff, {
diff: diff
}));
};
var Commit$3 = function Commit(_ref5) {
var onChangeCommitted = _ref5.onChangeCommitted,
filePath = _ref5.proposalData,
step = _ref5.step;
useEffect(function () {
void async function () {
var results = await transform(async function (original) {
return await (step.transformPlain ? stringProcessFile(original, step.transformPlain) : processFile(original, step.transform));
}, [filePath]);
if (results.some(function (r) {
return r.status === TransformStatus.Failure;
})) {
console.error(results);
}
onChangeCommitted("Modified file: " + filePath);
}();
}, [filePath, onChangeCommitted, step]);
return /*#__PURE__*/createElement(Box, null, /*#__PURE__*/createElement(Spinner, null), /*#__PURE__*/createElement(Text, null, "Applying file changes"));
};
var FileTransformExecutor = /*#__PURE__*/Object.freeze({
__proto__: null,
isFileTransformExecutor: isFileTransformExecutor,
type: type$3,
Propose: Propose,
Commit: Commit$3
});
function _inheritsLoose(subClass, superClass) {
subClass.prototype = Object.create(superClass.prototype);
subClass.prototype.constructor = subClass;
subClass.__proto__ = superClass;
}
function isNewFileExecutor(executor) {
return executor.templatePath !== undefined;
}
var type$2 = 'new-file';
var TempGenerator = /*#__PURE__*/function (_Generator) {
_inheritsLoose(TempGenerator, _Generator);
function TempGenerator(options) {
var _this;
_this = _Generator.call(this, options) || this;
_this.sourceRoot = void 0;
_this.targetDirectory = void 0;
_this.templateValues = void 0;
_this.returnResults = true;
_this.sourceRoot = {
type: 'absolute',
path: options.templateRoot
};
_this.templateValues = options.templateValues;
_this.targetDirectory = options.targetDirectory || '.';
return _this;
}
var _proto = TempGenerator.prototype;
_proto.getTemplateValues = function getTemplateValues() {
return this.templateValues;
};
_proto.getTargetDirectory = function getTargetDirectory() {
return this.targetDirectory;
};
return TempGenerator;
}(Generator);
var Commit$2 = function Commit(_ref) {
var cliArgs = _ref.cliArgs,
cliFlags = _ref.cliFlags,
onChangeCommitted = _ref.onChangeCommitted,
step = _ref.step;
var userInput = useUserInput(cliFlags);
var generatorArgs = useMemo(function () {
return {
destinationRoot: '.',
targetDirectory: getExecutorArgument(step.targetDirectory, cliArgs),
templateRoot: getExecutorArgument(step.templatePath, cliArgs),
templateValues: getExecutorArgument(step.templateValues, cliArgs)
};
}, [cliArgs, step]);
var _useState = useState(''),
fileCreateOutput = _useState[0],
setFileCreateOutput = _useState[1];
var _useState2 = useState(false),
changeCommited = _useState2[0],
setChangeCommited = _useState2[1];
var fileCreateLines = fileCreateOutput.split('\n');
var handleChangeCommitted = useCallback(function () {
setChangeCommited(true);
onChangeCommitted("Successfully created " + fileCreateLines.map(function (l) {
return l.split(' ').slice(1).join('').trim();
}).join(', '));
}, [fileCreateLines, onChangeCommitted]);
useEffect(function () {
async function createNewFiles() {
if (!fileCreateOutput) {
var generator = new TempGenerator(generatorArgs);
var results = await generator.run();
setFileCreateOutput(results);
}
} // eslint-disable-next-line @typescript-eslint/no-floating-promises
createNewFiles();
}, [fileCreateOutput, generatorArgs]);
var childProps = {
changeCommited: changeCommited,
fileCreateOutput: fileCreateOutput,
handleChangeCommitted: handleChangeCommitted
};
if (userInput) return /*#__PURE__*/createElement(CommitWithInput$2, childProps);else return /*#__PURE__*/createElement(CommitWithoutInput$2, childProps);
};
var CommitWithInput$2 = function CommitWithInput(_ref2) {
var changeCommited = _ref2.changeCommited,
fileCreateOutput = _ref2.fileCreateOutput,
handleChangeCommitted = _ref2.handleChangeCommitted;
useEnterToContinue(handleChangeCommitted, !changeCommited && fileCreateOutput !== '');
return /*#__PURE__*/createElement(Box, {
flexDirection: "column"
}, fileCreateOutput !== '' && /*#__PURE__*/createElement(Fragment, null, /*#__PURE__*/createElement(Text, null, fileCreateOutput), /*#__PURE__*/createElement(EnterToContinue, null)));
};
var CommitWithoutInput$2 = function CommitWithoutInput(_ref3) {
var changeCommited = _ref3.changeCommited,
fileCreateOutput = _ref3.fileCreateOutput,
handleChangeCommitted = _ref3.handleChangeCommitted;
useEffect(function () {
if (!changeCommited && fileCreateOutput !== '') {
handleChangeCommitted();
}
}, [changeCommited, fileCreateOutput, handleChangeCommitted]);
return /*#__PURE__*/createElement(Box, {
flexDirection: "column"
}, fileCreateOutput !== '' && /*#__PURE__*/createElement(Text, null, fileCreateOutput));
};
var NewFileExecutor = /*#__PURE__*/Object.freeze({
__proto__: null,
isNewFileExecutor: isNewFileExecutor,
type: type$2,
Commit: Commit$2
});
var type$1 = 'print-message';
var Commit$1 = function Commit(_ref) {
var cliArgs = _ref.cliArgs,
cliFlags = _ref.cliFlags,
onChangeCommitted = _ref.onChangeCommitted,
step = _ref.step;
var userInput = useUserInput(cliFlags);
var generatorArgs = useMemo(function () {
return {
message: getExecutorArgument(step.message, cliArgs),
stepName: getExecutorArgument(step.stepName, cliArgs)
};
}, [cliArgs, step]);
var _React$useState = useState(false),
changeCommited = _React$useState[0],
setChangeCommited = _React$useState[1];
var handleChangeCommitted = useCallback(function () {
setChangeCommited(true);
onChangeCommitted(generatorArgs.stepName);
}, [onChangeCommitted, generatorArgs]);
var childProps = {
changeCommited: changeCommited,
generatorArgs: generatorArgs,
handleChangeCommitted: handleChangeCommitted
};
if (userInput) return /*#__PURE__*/createElement(CommitWithInput$1, childProps);else return /*#__PURE__*/createElement(CommitWithoutInput$1, childProps);
};
var CommitWithInput$1 = function CommitWithInput(_ref2) {
var changeCommited = _ref2.changeCommited,
generatorArgs = _ref2.generatorArgs,
handleChangeCommitted = _ref2.handleChangeCommitted;
useEnterToContinue(handleChangeCommitted, !changeCommited);
return /*#__PURE__*/createElement(Box, {
flexDirection: "column"
}, /*#__PURE__*/createElement(Text, null, generatorArgs.message), /*#__PURE__*/createElement(EnterToContinue, null));
};
var CommitWithoutInput$1 = function CommitWithoutInput(_ref3) {
var changeCommited = _ref3.changeCommited,
generatorArgs = _ref3.generatorArgs,
handleChangeCommitted = _ref3.handleChangeCommitted;
useEffect(function () {
if (!changeCommited) {
handleChangeCommitted();
}
}, [changeCommited, handleChangeCommitted]);
return /*#__PURE__*/createElement(Box, {
flexDirection: "column"
}, /*#__PURE__*/createElement(Text, null, generatorArgs.message));
};
var PrintMessageExecutor = /*#__PURE__*/Object.freeze({
__proto__: null,
type: type$1,
Commit: Commit$1
});
var type = 'run-command';
function Command(_ref) {
var command = _ref.command,
loading = _ref.loading;
return /*#__PURE__*/createElement(Text, null, " ", loading ? /*#__PURE__*/createElement(Spinner, null) : '✅', " " + (typeof command === 'string' ? command : command.join(' ')));
}
var CommandList = function CommandList(_ref2) {
var _ref2$lede = _ref2.lede,
lede = _ref2$lede === void 0 ? 'Hang tight! Running...' : _ref2$lede,
_ref2$commandLoading = _ref2.commandLoading,
commandLoading = _ref2$commandLoading === void 0 ? false : _ref2$commandLoading,
step = _ref2.step,
command = _ref2.command;
return /*#__PURE__*/createElement(Box, {
flexDirection: "column"
}, /*#__PURE__*/createElement(Text, null, lede), /*#__PURE__*/createElement(Newline, null), /*#__PURE__*/createElement(Command, {
key: step.stepId,
command: command,
loading: commandLoading
}));
};
/**
* INFO: Exported for unit testing purposes
*
* This function calls the defined command with their optional arguments if defined
*
* @param {CliCommand} input The Command and arguments
* @return Promise<void>
*
* @example await executeCommand("ls")
* @example await executeCommand(["ls"])
* @example await executeCommand(["ls", ...["-a", "-l"]])
*/
async function executeCommand(input) {
// from https://stackoverflow.com/a/43766456/9950655
var argsRegex = /("[^"\\]*(?:\\[\S\s][^"\\]*)*"|'[^'\\]*(?:\\[\S\s][^'\\]*)*'|\/[^/\\]*(?:\\[\S\s][^/\\]*)*\/[gimy]*(?=\s|$)|(?:\\\s|\S)+)/g;
var command = Array.isArray(input) ? input : input.match(argsRegex) || [];
if (command.length === 0) {
throw new Error("The command is too short: `" + JSON.stringify(input) + "`");
}
await new Promise(function (resolve) {
var cp = spawn(command[0], command.slice(1), {
stdio: ['inherit', 'pipe', 'pipe']
});
cp.on('exit', resolve);
});
}
var Commit = function Commit(_ref3) {
var cliArgs = _ref3.cliArgs,
cliFlags = _ref3.cliFlags,
step = _ref3.step,
onChangeCommitted = _ref3.onChangeCommitted;
var userInput = useUserInput(cliFlags);
var _React$useState = useState(false),
commandInstalled = _React$useState[0],
setCommandInstalled = _React$useState[1];
var executorCommand = getExecutorArgument(step.command, cliArgs);
var handleChangeCommitted = useCallback(function () {
onChangeCommitted("Executed command " + executorCommand);
}, [executorCommand, onChangeCommitted]);
useEffect(function () {
async function runCommand() {
await executeCommand(executorCommand);
setCommandInstalled(true);
} // eslint-disable-next-line @typescript-eslint/no-floating-promises
runCommand();
}, [cliArgs, step, executorCommand]);
useEffect(function () {
if (commandInstalled) {
handleChangeCommitted();
}
}, [commandInstalled, handleChangeCommitted]);
var childProps = {
commandInstalled: commandInstalled,
handleChangeCommitted: handleChangeCommitted,
command: executorCommand,
cliArgs: cliArgs,
step: step
};
if (userInput) return /*#__PURE__*/createElement(CommitWithInput, childProps);else return /*#__PURE__*/createElement(CommitWithoutInput, childProps);
};
var CommitWithInput = function CommitWithInput(_ref4) {
var commandInstalled = _ref4.commandInstalled,
handleChangeCommitted = _ref4.handleChangeCommitted,
command = _ref4.command,
step = _ref4.step;
useEnterToContinue(handleChangeCommitted, commandInstalled);
return /*#__PURE__*/createElement(CommandList, {
commandLoading: !commandInstalled,
step: step,
command: command
});
};
var CommitWithoutInput = function CommitWithoutInput(_ref5) {
var commandInstalled = _ref5.commandInstalled,
command = _ref5.command,
step = _ref5.step;
return /*#__PURE__*/createElement(CommandList, {
commandLoading: !commandInstalled,
step: step,
command: command
});
};
var RunCommandExecutor = /*#__PURE__*/Object.freeze({
__proto__: null,
type: type,
executeCommand: executeCommand,
Commit: Commit
});
var _ExecutorMap;
var Action;
(function (Action) {
Action[Action["SkipStep"] = 0] = "SkipStep";
Action[Action["ProposeChange"] = 1] = "ProposeChange";
Action[Action["ApplyChange"] = 2] = "ApplyChange";
Action[Action["CommitApproved"] = 3] = "CommitApproved";
Action[Action["CompleteChange"] = 4] = "CompleteChange";
})(Action || (Action = {}));
var Status;
(function (Status) {
Status[Status["Pending"] = 0] = "Pending";
Status[Status["Proposed"] = 1] = "Proposed";
Status[Status["ReadyToCommit"] = 2] = "ReadyToCommit";
Status[Status["Committing"] = 3] = "Committing";
Status[Status["Committed"] = 4] = "Committed";
})(Status || (Status = {}));
var ExecutorMap = (_ExecutorMap = {}, _ExecutorMap[type$4] = AddDependencyExecutor, _ExecutorMap[type$2] = NewFileExecutor, _ExecutorMap[type$1] = PrintMessageExecutor, _ExecutorMap[type$3] = FileTransformExecutor, _ExecutorMap[type] = RunCommandExecutor, _ExecutorMap);
function recipeReducer(state, action) {
var newState = _extends({}, state);
switch (action.type) {
case Action.ProposeChange:
newState.steps[newState.current].status = Status.Proposed;
break;
case Action.CommitApproved:
newState.steps[newState.current].status = Status.ReadyToCommit;
newState.steps[newState.current].proposalData = action.data;
break;
case Action.ApplyChange:
newState.steps[newState.current].status = Status.Committing;
break;
case Action.CompleteChange:
newState.steps[newState.current].status = Status.Committed;
newState.steps[newState.current].successMsg = action.data;
newState.current = Math.min(newState.current + 1, newState.steps.length - 1);
break;
case Action.SkipStep:
newState.current += 1;
break;
}
return newState;
}
var DispatchContext = /*#__PURE__*/React.createContext(function () {});
function WelcomeMessage(_ref) {
var recipeMeta = _ref.recipeMeta,
_ref$enterToContinue = _ref.enterToContinue,
enterToContinue = _ref$enterToContinue === void 0 ? true : _ref$enterToContinue;
return /*#__PURE__*/React.createElement(Box, {
flexDirection: "column"
}, /*#__PURE__*/React.createElement(Text, {
color: "#8a3df0",
bold: true
}, "Recipe: ", recipeMeta.name), /*#__PURE__*/React.createElement(Newline, null), /*#__PURE__*/React.createElement(Text, {
color: "gray"
}, /*#__PURE__*/React.createElement(Text, {
italic: true
}, recipeMeta.description)), /*#__PURE__*/React.createElement(Newline, null), /*#__PURE__*/React.createElement(Text, {
color: "gray"
}, "Repo: ", /*#__PURE__*/React.createElement(Text, {
italic: true
}, recipeMeta.repoLink)), /*#__PURE__*/React.createElement(Text, {
color: "gray"
}, "Author: ", /*#__PURE__*/React.createElement(Text, {
italic: true
}, recipeMeta.owner)), enterToContinue && /*#__PURE__*/React.createElement(EnterToContinue, null));
}
function StepMessages(_ref2) {
var state = _ref2.state;
var messages = state.steps.map(function (step) {
var _step$executor$succes;
return {
msg: step.successMsg,
icon: (_step$executor$succes = step.executor.successIcon) != null ? _step$executor$succes : '✅'
};
}).filter(function (s) {
return s.msg;
});
return /*#__PURE__*/React.createElement(React.Fragment, null, messages.map(function (_ref3, index) {
var msg = _ref3.msg,
icon = _ref3.icon;
return /*#__PURE__*/React.createElement(Text, {
key: msg + index,
color: "green"
}, msg === '\n' ? '' : icon, " ", msg);
}));
}
function StepExecutor(_ref4) {
var cliArgs = _ref4.cliArgs,
cliFlags = _ref4.cliFlags,
proposalData = _ref4.proposalData,
step = _ref4.step,
status = _ref4.status;
var _ExecutorMap$step$ste = ExecutorMap[step.stepType],
Propose = _ExecutorMap$step$ste.Propose,
Commit = _ExecutorMap$step$ste.Commit;
var dispatch = React.useContext(DispatchContext);
var handleProposalAccepted = React.useCallback(function (msg) {
dispatch({
type: Action.CommitApproved,
data: msg
});
}, [dispatch]);
var handleChangeCommitted = React.useCallback(function (msg) {
dispatch({
type: Action.CompleteChange,
data: msg
});
}, [dispatch]);
React.useEffect(function () {
if (status === Status.Pending) {
dispatch({
type: Action.ProposeChange
});
} else if (status === Status.ReadyToCommit) {
dispatch({
type: Action.ApplyChange
});
}
if (status === Status.Proposed && !Propose) {
dispatch({
type: Action.CommitApproved
});
}
}, [dispatch, status, Propose]);
return /*#__PURE__*/React.createElement(Box, {
flexDirection: "column"
}, status !== Status.Committed ? /*#__PURE__*/React.createElement(Frontmatter, {
executor: step
}) : null, [Status.Proposed].includes(status) && Propose ? /*#__PURE__*/React.createElement(Propose, {
cliArgs: cliArgs,
cliFlags: cliFlags,
step: step,
onProposalAccepted: handleProposalAccepted
}) : null, [Status.Committing].includes(status) ? /*#__PURE__*/React.createElement(Commit, {
cliArgs: cliArgs,
cliFlags: cliFlags,
proposalData: proposalData,
step: step,
onChangeCommitted: handleChangeCommitted
}) : null);
}
function RecipeRenderer(_ref5) {
var cliArgs = _ref5.cliArgs,
cliFlags = _ref5.cliFlags,
steps = _ref5.steps,
recipeMeta = _ref5.recipeMeta;
var userInput = useUserInput(cliFlags);
var _useApp = useApp(),
exit = _useApp.exit;
var _React$useReducer = React.useReducer(recipeReducer, {
current: userInput ? -1 : 0,
steps: steps.map(function (e) {
return {
executor: e,
status: Status.Pending,
successMsg: ''
};
})
}),
state = _React$useReducer[0],
dispatch = _React$useReducer[1];
if (steps.length === 0) {
exit(new Error('This recipe has no steps'));
}
React.useEffect(function () {
var _state$steps$state$cu;
if (state.current === state.steps.length - 1 && ((_state$steps$state$cu = state.steps[state.current]) == null ? void 0 : _state$steps$state$cu.status) === Status.Committed) {
exit();
}
});
return /*#__PURE__*/React.createElement(DispatchContext.Provider, {
value: dispatch
}, userInput ? /*#__PURE__*/React.createElement(RecipeRendererWithInput, {
cliArgs: cliArgs,
cliFlags: cliFlags,
state: state,
recipeMeta: recipeMeta
}) : /*#__PURE__*/React.createElement(RecipeRendererWithoutInput, {
cliArgs: cliArgs,
cliFlags: cliFlags,
state: state,
recipeMeta: recipeMeta
}));
}
function RecipeRendererWithInput(_ref6) {
var _state$steps$state$cu2, _state$steps$state$cu3, _state$steps$state$cu4;
var cliArgs = _ref6.cliArgs,
cliFlags = _ref6.cliFlags,
recipeMeta = _ref6.recipeMeta,
state = _ref6.state;
var _useApp2 = useApp(),
exit = _useApp2.exit;
var dispatch = React.useContext(DispatchContext);
useInput(function (input, key) {
if (input === 'c' && key.ctrl) {
exit(new Error('You aborted installation'));
return;
}
});
useEnterToContinue(function () {
return dispatch({
type: Action.SkipStep
});
}, state.current === -1);
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(StepMessages, {
state: state
}), state.current === -1 ? /*#__PURE__*/React.createElement(WelcomeMessage, {
recipeMeta: recipeMeta
}) : /*#__PURE__*/React.createElement(StepExecutor, {
cliArgs: cliArgs,
cliFlags: cliFlags,
proposalData: (_state$steps$state$cu2 = state.steps[state.current]) == null ? void 0 : _state$steps$state$cu2.proposalData,
step: (_state$steps$state$cu3 = state.steps[state.current]) == null ? void 0 : _state$steps$state$cu3.executor,
status: (_state$steps$state$cu4 = state.steps[state.current]) == null ? void 0 : _state$steps$state$cu4.status
}));
}
function RecipeRendererWithoutInput(_ref7) {
var _state$steps$state$cu5, _state$steps$state$cu6, _state$steps$state$cu7;
var cliArgs = _ref7.cliArgs,
cliFlags = _ref7.cliFlags,
recipeMeta = _ref7.recipeMeta,
state = _ref7.state;
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(WelcomeMessage, {
recipeMeta: recipeMeta,
enterToContinue: false
}), /*#__PURE__*/React.createElement(StepMessages, {
state: state
}), /*#__PURE__*/React.createElement(StepExecutor, {
cliArgs: cliArgs,
cliFlags: cliFlags,
proposalData: (_state$steps$state$cu5 = state.steps[state.current]) == null ? void 0 : _state$steps$state$cu5.proposalData,
step: (_state$steps$state$cu6 = state.steps[state.current]) == null ? void 0 : _state$steps$state$cu6.executor,
status: (_state$steps$state$cu7 = state.steps[state.current]) == null ? void 0 : _state$steps$state$cu7.status
}));
}
var RecipeExecutor = /*#__PURE__*/function () {
function RecipeExecutor(options, steps) {
this.steps = void 0;
this.options = void 0;
this.options = options;
this.steps = steps;
}
var _proto = RecipeExecutor.prototype;
_proto.run = async function run(cliArgs, cliFlags) {
if (cliArgs === void 0) {
cliArgs = {};
}
if (cliFlags === void 0) {
cliFlags = {
yesToAll: false
};
}
try {
var _render = render( /*#__PURE__*/React.createElement(RecipeRenderer, {
cliArgs: cliArgs,
cliFlags: cliFlags,
steps: this.steps,
recipeMeta: this.options
}), {
exitOnCtrlC: false
}),
waitUntilExit = _render.waitUntilExit;
await waitUntilExit();
baseLogger({
displayDateTime: false,
displayLogLevel: false
}).info("\n\uD83C\uDF89 The " + this.options.name + " recipe has been installed!\n");
} catch (e) {
baseLogger({
displayDateTime: false
}).error(e);
return;
}
};
return RecipeExecutor;
}();
function RecipeBuilder() {
var steps = [];
var meta = {};
return {
setName: function setName(name) {
meta.name = name;
return this;
},
setDescription: function setDescription(description) {
meta.description = description;
return this;
},
printMessage: function printMessage(step) {
steps.push(_extends({
stepType: type$1
}, step));
return this;
},
setOwner: function setOwner(owner) {
meta.owner = owner;
return this;
},
setRepoLink: function setRepoLink(repoLink) {
meta.repoLink = repoLink;
return this;
},
addAddDependenciesStep: function addAddDependenciesStep(step) {
steps.push(_extends({
stepType: type$4
}, step));
return this;
},
addNewFilesStep: function addNewFilesStep(step) {
steps.push(_extends({
stepType: type$2
}, step));
return this;
},
addTransformFilesStep: function addTransformFilesStep(step) {
steps.push(_extends({
stepType: type$3
}, step));
return this;
},
addRunCommandStep: function addRunCommandStep(step) {
steps.push(_extends({
stepType: type
}, step));
return this;
},
build: function build() {
return new RecipeExecutor(meta, steps);
}
};
}
function ext(jsx) {
if (jsx === void 0) {
jsx = false;
}
return existsSync(resolve('tsconfig.json')) ? jsx ? '.tsx' : '.ts' : '.js';
}
var paths = {
document: function document() {
return "app/pages/_document" + ext(true);
},
app: function app() {
return "app/pages/_app" + ext(true);
},
entry: function entry() {
return "app/pages/index" + ext(true);
},
babelConfig: function babelConfig() {
return 'babel.config.js';
},
blitzConfig: function blitzConfig() {
return "blitz.config" + ext();
},
packageJson: function packageJson() {
return 'package.json';
},
prismaSchema: function prismaSchema() {
return 'db/schema.prisma';
}
};
function addImport(program, importToAdd) {
var importStatementCount = program.find(j.ImportDeclaration).length;
if (importStatementCount === 0) {
program.find(j.Statement).at(0).insertBefore(importToAdd);
return program;
}
program.find(j.ImportDeclaration).forEach(function (stmt, idx) {
if (idx === importStatementCount - 1) {
stmt.replace(stmt.node, importToAdd);
}
});
return program;
}
var addBlitzMiddleware = function addBlitzMiddleware(program, middleware) {
return transformBlitzConfig(program, function (config) {
// Locate the middleware property
var middlewareProp = config.properties.find(function (value) {
return value.type === 'ObjectProperty' && value.key.type === 'Identifier' && value.key.name === 'middleware';
});
if (middlewareProp && middlewareProp.value.type === 'ArrayExpression') {
// We found it, pop on our middleware.
middlewareProp.value.elements.push(middleware);
} else {
// No middleware prop, add our own.
config.properties.push(j.property('init', j.identifier('middleware'), {
type: 'ArrayExpression',
elements: [middleware],
loc: null,
comments: null
}));
}
return config;
});
};
var findModuleExportsExpressions = function findModuleExportsExpressions(program) {
return program.find(j.AssignmentExpression).filter(function (path) {
var _path$value = path.value,
left = _path$value.left,
right = _path$value.right;
return left.type === 'MemberExpression' && left.object.type === 'Identifier' && left.property.type === 'Identifier' && left.property.name === 'exports' && right.type === 'ObjectExpression';
});
};
/**
* A file transformer that parses a schema.prisma string, offers you a callback
* of the parsed document object, then takes your changes to the document and
* writes out a new schema.prisma string with the changes applied.
*
* @param source - schema.prisma source file contents
* @param producer - a callback function that can mutate the parsed data model
* @returns The modified schema.prisma source
*/
async function produceSchema(source, producer) {
var schema = await getSchema(source);
producer(schema);
return printSchema(schema);
}
/**
* Adds an enum to your schema.prisma data model.
*
* @param source - schema.prisma source file contents
* @param enumProps - the enum to add
* @returns The modified schema.prisma source
* @example Usage
* ```
* addPrismaEnum(source, {
type: "enum",
name: "Role",
enumerators: [
{type: "enumerator", name: "USER"},
{type: "enumerator", name: "ADMIN"},
],
})
* ```
*/
function addPrismaEnum(source, enumProps) {
return produceSchema(source, function (schema) {
var existing = schema.list.find(function (x) {
return x.type === 'enum' && x.name === enumProps.name;
});
existing ? Object.assign(existing, enumProps) : schema.list.push(enumProps);
});
}
/**
* Adds a field to a model in your schema.prisma data model.
*
* @param source - schema.prisma source file contents
* @param modelName - name of the model to add a field to
* @param fieldProps - the field to add
* @returns The modified schema.prisma source
* @example Usage
* ```
* addPrismaField(source, "Project", {
type: "field",
name: "name",
fieldType: "String",
optional: false,
attributes: [{type: "attribute", kind: "field", name: "unique"}],
})
* ```
*/
function addPrismaField(source, modelName, fieldProps) {
return produceSchema(source, function (schema) {
var model = schema.list.find(function (x) {
return x.type === 'model' && x.name === modelName;
});
if (!model) return;
var existing = model.properties.find(function (x) {
return x.type === 'field' && x.name === fieldProps.name;
});
existing ? Object.assign(existing, fieldProps) : model.properties.push(fieldProps);
});
}
/**
* Adds a generator to your schema.prisma data model.
*
* @param source - schema.prisma source file contents
* @param generatorProps - the generator to add
* @returns The modified schema.prisma source
* @example Usage
* ```
* addPrismaGenerator(source, {
type: "generator",
name: "nexusPrisma",
assignments: [{type: "assignment", key: "provider", value: '"nexus-prisma"'}],
})
* ```
*/
function addPrismaGenerator(source, generatorProps) {
return produceSchema(source, function (schema) {
var existing = schema.list.find(function (x) {
return x.type === 'generator' && x.name === generatorProps.name;
});
existing ? Object.assign(existing, generatorProps) : schema.list.push(generatorProps);
});
}
/**
* Adds a field to a model in your schema.prisma data model.
*
* @remarks Not ready for actual use
* @param source - schema.prisma source file contents
* @param modelName - name of the model to add a field to
* @param attributeProps - the model attribute (such as an index) to add
* @returns The modified schema.prisma source
* @example Usage
* ```
* addPrismaModelAttribute(source, "Project", {
* type: "attribute",
* kind: "model",
* name: "index",
* args: [{ type: "attributeArgument", value: { type: "array", args: ["name"] } }]
* });
* ```
*/
function addPrismaModelAttribute(source, modelName, attributeProps) {
return produceSchema(source, function (schema) {
var model = schema.list.find(function (x) {
return x.type === 'model' && x.name === modelName;
});
if (!model) return;
var existing = model.properties.find(function (x) {
return x.type === 'attribute' && x.name === attributeProps.name;
});
existing ? Object.assign(existing, attributeProps) : model.properties.push(attributeProps);
});
}
/**
* Adds an enum to your schema.prisma data model.
*
* @param source - schema.prisma source file contents
* @param modelProps - the model to add
* @returns The modified schema.prisma source
* @example Usage
* ```
* addPrismaModel(source, {
type: "model",
name: "Project",
properties: [{type: "field", name: "id", fieldType: "String"}],
})
* ```
*/
function addPrismaModel(source, modelProps) {
return produceSchema(source, function (schema) {
var existing = schema.list.find(function (x) {
return x.type === 'model' && x.name === modelProps.name;
});
existing ? Object.assign(existing, modelProps) : schema.list.push(modelProps);
});
}
/**
* Modify the prisma datasource metadata to use the provider and url specified.
*
* @param source - schema.prisma source file contents
* @param datasourceProps - datasource object to assign to the schema
* @returns The modified schema.prisma source
* @example Usage
* ```
* setPrismaDataSource(source, {
type: "datasource",
name: "db",
assignments: [
{type: "assignment", key: "provider", value: '"postgresql"'},
{
type: "assignment",
key: "url",
value: {type: "function", name: "env", params: ['"DATABASE_URL"']},
},
],
})
* ```
*/
function setPrismaDataSource(source, datasourceProps) {
return produceSchema(source, function (schema) {
var existing = schema.list.find(function (x) {
return x.type === 'datasource';
});
existing ? Object.assign(existing, datasourceProps) : schema.list.push(datasourceProps);
});
}
function recursiveConfigSearch(program, obj) {
// Identifier being a variable name
if (obj.type === 'Identifier') {
var _j$get = j(obj).get(),
node = _j$get.node; // Get the definition of the variable
var identifier = program.find(j.VariableDeclarator, {
id: {
name: node.name
}
}).get(); // Return what is after the `=`
return identifier.value.init ? recursiveConfigSearch(program, identifier.value.init) : undefined;
} else if (obj.type === 'CallExpression') {
// If it's an function call (like `withBundleAnalyzer`), get the first argument
if (obj.arguments.length === 0) {
// If it has no arguments, create an empty object: `{}`
var _config = j.objectExpression([]);
obj.arguments.push(_config);
return _config;
} else {
var arg = obj.arguments[0];
if (arg.type === 'SpreadElement') return undefined;else return recursiveConfigSearch(program, arg);
}
} else if (obj.type === 'ObjectExpression') {
// If it's an object, return it
return obj;
} else {
return undefined;
}
}
function transformBlitzConfig(program, transform) {
var moduleExportsExpressions = program.find(j.AssignmentExpression, {
operator: '=',
left: {
object: {
name: 'module'
},
property: {
name: 'exports'
}
},
right: {}
}); // If there isn't any `module.exports = ...`, create one
if (moduleExportsExpressions.length === 0) {
var _config2 = j.objectExpression([]);
_config2 = transform(_config2);
var moduleExportExpression = j.expressionStatement(j.assignmentExpression('=', j.memberExpression(j.identifier('module'), j.identifier('exports')), _config2));
program.get().node.program.body.push(moduleExportExpression);
} else if (moduleExportsExpressions.length === 1) {
var moduleExportsExpression = moduleExportsExpressions.get();
var _config3 = recursiveConfigSearch(program, moduleExportsExpression.value.right);
if (_config3) {
_config3 = transform(_config3);
} else {
console.warn("The configuration couldn't be found, but there is a 'module.exports' inside `blitz.config.js`");
}
} else {