UNPKG

@blitzjs/installer

Version:

Package installation for the Blitz CLI

1,524 lines (1,343 loc) 59.6 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var ink = require('ink'); var logging = require('next/dist/server/lib/logging'); var React = require('react'); var crossSpawn = require('cross-spawn'); var fs = require('fs-extra'); var Spinner = require('ink-spinner'); var path = require('path'); var diff = require('diff'); var j = require('jscodeshift'); var getBabelOptions = require('recast/parsers/_babel_options'); var babel = require('recast/parsers/babel'); var enquirer = require('enquirer'); var globby = require('globby'); var generator = require('@blitzjs/generator'); var prismaAst = require('@mrleebo/prisma-ast'); function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; } var React__default = /*#__PURE__*/_interopDefault(React); var Spinner__default = /*#__PURE__*/_interopDefault(Spinner); var j__default = /*#__PURE__*/_interopDefault(j); var getBabelOptions__default = /*#__PURE__*/_interopDefault(getBabelOptions); var globby__default = /*#__PURE__*/_interopDefault(globby); 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__*/React.createElement(ink.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__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Newline, null), /*#__PURE__*/React.createElement(ink.Text, { bold: true }, message)); }; function useEnterToContinue(cb, additionalCondition) { if (additionalCondition === void 0) { additionalCondition = true; } ink.useInput(function (_input, key) { if (additionalCondition && key["return"]) { cb(); } }); } function useUserInput(cliFlags) { var _useStdin = ink.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__*/React.createElement(ink.Box, { flexDirection: "column", paddingBottom: 1 }, /*#__PURE__*/React.createElement(Newline, null), /*#__PURE__*/React.createElement(ink.Box, { flexDirection: "column" }, /*#__PURE__*/React.createElement(ink.Text, { color: "#8a3df0", bold: true }, verticalBorder), /*#__PURE__*/React.createElement(ink.Text, { color: "#8a3df0", bold: true }, "\u23AA\xA0\xA0\xA0", executor.stepName, "\xA0\xA0\xA0\u23AA"), /*#__PURE__*/React.createElement(ink.Text, { color: "#8a3df0", bold: true }, verticalBorder)), /*#__PURE__*/React.createElement(ink.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__*/React.createElement(ink.Text, null, " ", loading ? /*#__PURE__*/React.createElement(Spinner__default['default'], 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__*/React.createElement(ink.Box, { flexDirection: "column" }, /*#__PURE__*/React.createElement(ink.Text, null, lede), /*#__PURE__*/React.createElement(Newline, null), prodPackages.length ? /*#__PURE__*/React.createElement(ink.Text, null, "Dependencies to be installed:") : null, prodPackages.map(function (pkg) { return /*#__PURE__*/React.createElement(Package, { key: pkg.name, pkg: pkg, loading: depsLoading }); }), /*#__PURE__*/React.createElement(Newline, null), devPackages.length ? /*#__PURE__*/React.createElement(ink.Text, null, "Dev Dependencies to be installed:") : null, devPackages.map(function (pkg) { return /*#__PURE__*/React.createElement(Package, { key: pkg.name, pkg: pkg, loading: devDepsLoading }); })); }; /** * Exported for unit testing purposes */ function getPackageManager() { if (fs.existsSync(path.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 = crossSpawn.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 = React.useState(false), depsInstalled = _React$useState[0], setDepsInstalled = _React$useState[1]; var _React$useState2 = React.useState(false), devDepsInstalled = _React$useState2[0], setDevDepsInstalled = _React$useState2[1]; var handleChangeCommitted = React.useCallback(function () { var packages = step.packages; var dependencies = packages.length === 1 ? 'dependency' : 'dependencies'; onChangeCommitted("Installed " + packages.length + " " + dependencies); }, [onChangeCommitted, step]); React.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]); React.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]); React.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__*/React.createElement(CommitWithInput$3, childProps);else return /*#__PURE__*/React.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__*/React.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__*/React.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__default['default'](options); babelOptions.plugins.push('typescript'); babelOptions.plugins.push('jsx'); return babel.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__default['default'](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 (!fs.existsSync(filePath)) { results.push({ status: TransformStatus.Failure, filename: filePath, error: new Error("Error: " + filePath + " not found") }); } try { var fileBuffer = fs.readFileSync(filePath); var fileSource = fileBuffer.toString('utf-8'); var transformedCode = await processFile(fileSource); fs.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__default['default'](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 enquirer.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 = React.useState(null), diff$1 = _React$useState[0], setDiff = _React$useState[1]; var _React$useState2 = React.useState(null), error = _React$useState2[0], setError = _React$useState2[1]; var _React$useState3 = React.useState(''), filePath = _React$useState3[0], setFilePath = _React$useState3[1]; var _React$useState4 = React.useState(false), proposalAccepted = _React$useState4[0], setProposalAccepted = _React$useState4[1]; var acceptProposal = React.useCallback(function () { setProposalAccepted(true); onProposalAccepted(filePath); }, [onProposalAccepted, filePath]); React.useEffect(function () { async function generateDiff() { var fileToTransform = await filePrompt({ context: cliArgs, globFilter: getExecutorArgument(step.singleFileSearch, cliArgs), getChoices: step.selectTargetFiles }); setFilePath(fileToTransform); var originalFile = fs.readFileSync(fileToTransform).toString('utf-8'); var newFile = await (step.transformPlain ? stringProcessFile(originalFile, step.transformPlain) : processFile(originalFile, step.transform)); return diff.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$1) { return /*#__PURE__*/React.createElement(ink.Box, null, /*#__PURE__*/React.createElement(ink.Text, null, /*#__PURE__*/React.createElement(Spinner__default['default'], null), "Generating file diff...")); } var childProps = { diff: diff$1, filePath: filePath, proposalAccepted: proposalAccepted, acceptProposal: acceptProposal }; if (userInput) return /*#__PURE__*/React.createElement(ProposeWithInput, childProps);else return /*#__PURE__*/React.createElement(ProposeWithoutInput, childProps); }; var Diff = function Diff(_ref2) { var diff = _ref2.diff; return /*#__PURE__*/React.createElement(React.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__*/React.createElement(ink.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__*/React.createElement(ink.Box, { flexDirection: "column" }, /*#__PURE__*/React.createElement(Diff, { diff: diff }), /*#__PURE__*/React.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; React.useEffect(function () { if (filePath !== '' && !proposalAccepted) { acceptProposal(); } }, [acceptProposal, filePath, proposalAccepted]); return /*#__PURE__*/React.createElement(ink.Box, { flexDirection: "column" }, /*#__PURE__*/React.createElement(Diff, { diff: diff })); }; var Commit$3 = function Commit(_ref5) { var onChangeCommitted = _ref5.onChangeCommitted, filePath = _ref5.proposalData, step = _ref5.step; React.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__*/React.createElement(ink.Box, null, /*#__PURE__*/React.createElement(Spinner__default['default'], null), /*#__PURE__*/React.createElement(ink.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.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 = React.useMemo(function () { return { destinationRoot: '.', targetDirectory: getExecutorArgument(step.targetDirectory, cliArgs), templateRoot: getExecutorArgument(step.templatePath, cliArgs), templateValues: getExecutorArgument(step.templateValues, cliArgs) }; }, [cliArgs, step]); var _useState = React.useState(''), fileCreateOutput = _useState[0], setFileCreateOutput = _useState[1]; var _useState2 = React.useState(false), changeCommited = _useState2[0], setChangeCommited = _useState2[1]; var fileCreateLines = fileCreateOutput.split('\n'); var handleChangeCommitted = React.useCallback(function () { setChangeCommited(true); onChangeCommitted("Successfully created " + fileCreateLines.map(function (l) { return l.split(' ').slice(1).join('').trim(); }).join(', ')); }, [fileCreateLines, onChangeCommitted]); React.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__*/React.createElement(CommitWithInput$2, childProps);else return /*#__PURE__*/React.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__*/React.createElement(ink.Box, { flexDirection: "column" }, fileCreateOutput !== '' && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(ink.Text, null, fileCreateOutput), /*#__PURE__*/React.createElement(EnterToContinue, null))); }; var CommitWithoutInput$2 = function CommitWithoutInput(_ref3) { var changeCommited = _ref3.changeCommited, fileCreateOutput = _ref3.fileCreateOutput, handleChangeCommitted = _ref3.handleChangeCommitted; React.useEffect(function () { if (!changeCommited && fileCreateOutput !== '') { handleChangeCommitted(); } }, [changeCommited, fileCreateOutput, handleChangeCommitted]); return /*#__PURE__*/React.createElement(ink.Box, { flexDirection: "column" }, fileCreateOutput !== '' && /*#__PURE__*/React.createElement(ink.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 = React.useMemo(function () { return { message: getExecutorArgument(step.message, cliArgs), stepName: getExecutorArgument(step.stepName, cliArgs) }; }, [cliArgs, step]); var _React$useState = React.useState(false), changeCommited = _React$useState[0], setChangeCommited = _React$useState[1]; var handleChangeCommitted = React.useCallback(function () { setChangeCommited(true); onChangeCommitted(generatorArgs.stepName); }, [onChangeCommitted, generatorArgs]); var childProps = { changeCommited: changeCommited, generatorArgs: generatorArgs, handleChangeCommitted: handleChangeCommitted }; if (userInput) return /*#__PURE__*/React.createElement(CommitWithInput$1, childProps);else return /*#__PURE__*/React.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__*/React.createElement(ink.Box, { flexDirection: "column" }, /*#__PURE__*/React.createElement(ink.Text, null, generatorArgs.message), /*#__PURE__*/React.createElement(EnterToContinue, null)); }; var CommitWithoutInput$1 = function CommitWithoutInput(_ref3) { var changeCommited = _ref3.changeCommited, generatorArgs = _ref3.generatorArgs, handleChangeCommitted = _ref3.handleChangeCommitted; React.useEffect(function () { if (!changeCommited) { handleChangeCommitted(); } }, [changeCommited, handleChangeCommitted]); return /*#__PURE__*/React.createElement(ink.Box, { flexDirection: "column" }, /*#__PURE__*/React.createElement(ink.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__*/React.createElement(ink.Text, null, " ", loading ? /*#__PURE__*/React.createElement(Spinner__default['default'], 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__*/React.createElement(ink.Box, { flexDirection: "column" }, /*#__PURE__*/React.createElement(ink.Text, null, lede), /*#__PURE__*/React.createElement(Newline, null), /*#__PURE__*/React.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 = crossSpawn.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 = React.useState(false), commandInstalled = _React$useState[0], setCommandInstalled = _React$useState[1]; var executorCommand = getExecutorArgument(step.command, cliArgs); var handleChangeCommitted = React.useCallback(function () { onChangeCommitted("Executed command " + executorCommand); }, [executorCommand, onChangeCommitted]); React.useEffect(function () { async function runCommand() { await executeCommand(executorCommand); setCommandInstalled(true); } // eslint-disable-next-line @typescript-eslint/no-floating-promises runCommand(); }, [cliArgs, step, executorCommand]); React.useEffect(function () { if (commandInstalled) { handleChangeCommitted(); } }, [commandInstalled, handleChangeCommitted]); var childProps = { commandInstalled: commandInstalled, handleChangeCommitted: handleChangeCommitted, command: executorCommand, cliArgs: cliArgs, step: step }; if (userInput) return /*#__PURE__*/React.createElement(CommitWithInput, childProps);else return /*#__PURE__*/React.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__*/React.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__*/React.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__default['default'].createContext(function () {}); function WelcomeMessage(_ref) { var recipeMeta = _ref.recipeMeta, _ref$enterToContinue = _ref.enterToContinue, enterToContinue = _ref$enterToContinue === void 0 ? true : _ref$enterToContinue; return /*#__PURE__*/React__default['default'].createElement(ink.Box, { flexDirection: "column" }, /*#__PURE__*/React__default['default'].createElement(ink.Text, { color: "#8a3df0", bold: true }, "Recipe: ", recipeMeta.name), /*#__PURE__*/React__default['default'].createElement(Newline, null), /*#__PURE__*/React__default['default'].createElement(ink.Text, { color: "gray" }, /*#__PURE__*/React__default['default'].createElement(ink.Text, { italic: true }, recipeMeta.description)), /*#__PURE__*/React__default['default'].createElement(Newline, null), /*#__PURE__*/React__default['default'].createElement(ink.Text, { color: "gray" }, "Repo: ", /*#__PURE__*/React__default['default'].createElement(ink.Text, { italic: true }, recipeMeta.repoLink)), /*#__PURE__*/React__default['default'].createElement(ink.Text, { color: "gray" }, "Author: ", /*#__PURE__*/React__default['default'].createElement(ink.Text, { italic: true }, recipeMeta.owner)), enterToContinue && /*#__PURE__*/React__default['default'].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__default['default'].createElement(React__default['default'].Fragment, null, messages.map(function (_ref3, index) { var msg = _ref3.msg, icon = _ref3.icon; return /*#__PURE__*/React__default['default'].createElement(ink.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__default['default'].useContext(DispatchContext); var handleProposalAccepted = React__default['default'].useCallback(function (msg) { dispatch({ type: Action.CommitApproved, data: msg }); }, [dispatch]); var handleChangeCommitted = React__default['default'].useCallback(function (msg) { dispatch({ type: Action.CompleteChange, data: msg }); }, [dispatch]); React__default['default'].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__default['default'].createElement(ink.Box, { flexDirection: "column" }, status !== Status.Committed ? /*#__PURE__*/React__default['default'].createElement(Frontmatter, { executor: step }) : null, [Status.Proposed].includes(status) && Propose ? /*#__PURE__*/React__default['default'].createElement(Propose, { cliArgs: cliArgs, cliFlags: cliFlags, step: step, onProposalAccepted: handleProposalAccepted }) : null, [Status.Committing].includes(status) ? /*#__PURE__*/React__default['default'].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 = ink.useApp(), exit = _useApp.exit; var _React$useReducer = React__default['default'].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__default['default'].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__default['default'].createElement(DispatchContext.Provider, { value: dispatch }, userInput ? /*#__PURE__*/React__default['default'].createElement(RecipeRendererWithInput, { cliArgs: cliArgs, cliFlags: cliFlags, state: state, recipeMeta: recipeMeta }) : /*#__PURE__*/React__default['default'].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 = ink.useApp(), exit = _useApp2.exit; var dispatch = React__default['default'].useContext(DispatchContext); ink.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__default['default'].createElement(React__default['default'].Fragment, null, /*#__PURE__*/React__default['default'].createElement(StepMessages, { state: state }), state.current === -1 ? /*#__PURE__*/React__default['default'].createElement(WelcomeMessage, { recipeMeta: recipeMeta }) : /*#__PURE__*/React__default['default'].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__default['default'].createElement(React__default['default'].Fragment, null, /*#__PURE__*/React__default['default'].createElement(WelcomeMessage, { recipeMeta: recipeMeta, enterToContinue: false }), /*#__PURE__*/React__default['default'].createElement(StepMessages, { state: state }), /*#__PURE__*/React__default['default'].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 = ink.render( /*#__PURE__*/React__default['default'].createElement(RecipeRenderer, { cliArgs: cliArgs, cliFlags: cliFlags, steps: this.steps, recipeMeta: this.options }), { exitOnCtrlC: false }), waitUntilExit = _render.waitUntilExit; await waitUntilExit(); logging.baseLogger({ displayDateTime: false, displayLogLevel: false }).info("\n\uD83C\uDF89 The " + this.options.name + " recipe has been installed!\n"); } catch (e) { logging.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 fs.existsSync(path.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__default['default'].ImportDeclaration).length; if (importStatementCount === 0) { program.find(j__default['default'].Statement).at(0).insertBefore(importToAdd); return program; } program.find(j__default['default'].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__default['default'].property('init', j__default['default'].identifier('middleware'), { type: 'ArrayExpression', elements: [middleware], loc: null, comments: null })); } return config; }); }; var findModuleExportsExpressions = function findModuleExportsExpressions(program) { return program.find(j__default['default'].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 prismaAst.getSchema(source); producer(schema); return prismaAst.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