@coat/cli
Version:
TODO: See #3
196 lines (183 loc) • 8.82 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.create = create;
var _fsExtra = _interopRequireDefault(require("fs-extra"));
var _path = _interopRequireDefault(require("path"));
var _execa = _interopRequireDefault(require("execa"));
var _ora = _interopRequireDefault(require("ora"));
var _chalk = _interopRequireDefault(require("chalk"));
var _getProjectName = require("./get-project-name");
var _getTemplateInfo = require("./get-template-info");
var _constants = require("../constants");
var _json = require("../file-types/json");
var _addInitialCommit = require("./add-initial-commit");
var _printCreateCustomizationHelp = require("./print-create-customization-help");
var _getCoatHeader = require("../bin/get-coat-header");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Creates and generates a new coat project.
*
* @param options.template The template that should be used for the new project
* @param options.directory The optional path to the directory that should be used
* @param options.projectName The optional project name that has been provided
*/
async function create({
template,
directory,
projectName
}) {
// Print the coat logo and header
console.log((0, _getCoatHeader.getCoatHeader)());
const directoryInputSanitized = directory === null || directory === void 0 ? void 0 : directory.trim();
const projectNameInputSanitized = projectName === null || projectName === void 0 ? void 0 : projectName.trim();
// Resolve the target directory and project name for the new coat project
// from the cli arguments if they are available
let targetCwd;
let usableProjectName;
if (!directoryInputSanitized) {
// No input for the target directory has been provided,
// therefore the user should be prompted for a project name that
// will be used as the target directory.
//
// The projectNameInput argument will be ignored, since it could have
// only been passed via an edge case, e.g. when running
// `coat create template "" my-project` which should not be supported.
usableProjectName = await (0, _getProjectName.getProjectName)();
// The project name should be used as the target directory in the current
// working directory
targetCwd = _path.default.join(process.cwd(), usableProjectName);
} else {
if (_path.default.isAbsolute(directoryInputSanitized)) {
// If the directory input is a valid absolute path, it should be used directly
targetCwd = directoryInputSanitized;
} else {
// Otherwise, the directory input should be treated as a relative path
// to the current working directory
targetCwd = _path.default.join(process.cwd(), directoryInputSanitized);
}
if (projectNameInputSanitized) {
usableProjectName = projectNameInputSanitized;
} else {
// Retrieve the trailing folder name of the target directory to use it
// as the project name
const suggestedProjectName = _path.default.basename(targetCwd);
if (suggestedProjectName === directoryInputSanitized) {
// If the suggested project name matches the directory input argument
// it should be used directly without prompting the user.
//
// This should be the most common scenario where create is run like
// `coat create template my-project`
// where both the directoryInput and suggested project name are "my-project"
usableProjectName = suggestedProjectName;
} else {
// If the suggested project name does not directly match the directory input,
// the user should be prompted for a project name and with the trailing folder
// name as the suggestion.
//
// This will likely be a scenario where the user calls coat create like:
// (process.cwd = /usr/source/some-project)
// `coat create template .`
// -> suggested project name will be some-project
usableProjectName = await (0, _getProjectName.getProjectName)(suggestedProjectName);
}
}
}
// Check if a coat manifest file already exists in the project
let coatManifestExists = false;
try {
await _fsExtra.default.readFile(_path.default.join(targetCwd, _constants.COAT_MANIFEST_FILENAME));
// If this line is reached the coat manifest file already exists
coatManifestExists = true;
} catch (error) {
if (error.code !== "ENOENT") {
// If the error is not due to a missing file, but e.g. due
// to missing permissions it should be thrown to the user
throw error;
}
// The coat manifest file does not exist in the target directory
}
if (coatManifestExists) {
console.error(`A coat manifest file already exists in the target directory.\n\nPlease install the template manually via npm and add the name of the template to the existing coat manifest file.`);
throw new Error("coat manifest file already exists");
}
// Check if a package.json file already exists
// in the target directory, otherwise create it
let previousPackageJson;
try {
const previousPackageJsonRaw = await _fsExtra.default.readFile(_path.default.join(targetCwd, _constants.PACKAGE_JSON_FILENAME), "utf-8");
previousPackageJson = JSON.parse(previousPackageJsonRaw);
} catch (error) {
if (error.code !== "ENOENT") {
// If the error is not due to a missing file, but e.g. due
// to missing permissions it should be thrown to the user
throw error;
}
// package.json does not exit in the target directory
}
if (!previousPackageJson) {
const packageJson = {
name: usableProjectName,
version: "1.0.0"
};
// Create the package.json file inside the
// target directory.
await _fsExtra.default.outputFile(_path.default.join(targetCwd, _constants.PACKAGE_JSON_FILENAME),
// package.json does not need to be styled, since npm
// will alter and format it while installing dependencies
JSON.stringify(packageJson));
previousPackageJson = packageJson;
}
console.log((0, _chalk.default)`\nCreating a new {cyan coat} project in {green ${targetCwd}}\n`);
// Print getting started guidance for customization
(0, _printCreateCustomizationHelp.printCreateCustomizationHelp)();
console.log((0, _chalk.default)`{cyan coat} will install the project template and its dependencies into the project directory.\nThis might take a couple of minutes.\n`);
const installSpinner = (0, _ora.default)("Installing template into project directory\n").start();
try {
// Run npm install to install the template and its dependencies
await (0, _execa.default)("npm", ["install", "--save-exact", "--save-dev", template], {
cwd: targetCwd
});
// We need to retrieve the correct template name,
// since the template string that has been passed
// to coat create could also be a local file path,
// GitHub URL or npm tag
const templateInfo = await (0, _getTemplateInfo.getTemplateInfo)(targetCwd, previousPackageJson, template, installSpinner);
installSpinner.succeed();
// Write the coat manifest file
const coatManifest = {
name: usableProjectName,
extends: templateInfo.name
};
await _fsExtra.default.writeFile(_path.default.join(targetCwd, _constants.COAT_MANIFEST_FILENAME), (0, _json.polish)(coatManifest, _constants.COAT_MANIFEST_FILENAME));
} catch (error) {
installSpinner.fail();
// Re-throw error to provide feedback to the user
throw error;
}
// Run setup and sync commands in the project directory
//
// The current coat process is respawned to use a local
// @coat/cli in case it is available after installing
// the template, since templates should have a peerDependency
// on the cli.
//
// The setup step will be triggered automatically by running sync
await (0, _execa.default)(process.argv[0], [process.argv[1], "sync"], {
cwd: targetCwd,
stdio: "inherit"
});
// Initialize a git repository and add an initial commit
// if the project was not created in an existing git repository
await (0, _addInitialCommit.addInitialCommit)(targetCwd);
console.log((0, _chalk.default)`🎊 Your new {cyan coat} project has been successfully created! 🎊\n`);
const relativePath = _path.default.relative(process.cwd(), targetCwd);
// Only log if relativePath exists, i.e. the user is in a different
// directory than the project target directory
if (relativePath) {
console.log((0, _chalk.default)`Get started by changing into your project folder: {cyan cd ${relativePath}}`);
}
console.log((0, _chalk.default)`You can ensure your generated files are up to date by running: {cyan coat sync}\n`);
console.log("⚡️ Happy hacking! ⚡️\n");
}