From 69a87e3b0282d79d88b4d69c2811ee1c5aa82371 Mon Sep 17 00:00:00 2001 From: Yasuaki Uechi Date: Wed, 29 Jun 2022 13:53:33 +0900 Subject: [PATCH] fix: make `installNpmPackage` work without package.json --- README.md | 90 +++++----- src/cli.ts | 9 +- src/index.ts | 247 +++++++++++++++++++++------ src/npm.ts | 40 +++++ src/template.ts | 19 ++- tests/fixtures/create-test/LICENSE | 19 --- tests/fixtures/create-test/README.md | 36 ---- tests/fixtures/minimal/LICENSE | 19 --- tests/fixtures/minimal/README.md | 36 ---- 9 files changed, 296 insertions(+), 219 deletions(-) delete mode 100644 tests/fixtures/create-test/LICENSE delete mode 100644 tests/fixtures/create-test/README.md delete mode 100644 tests/fixtures/minimal/LICENSE delete mode 100644 tests/fixtures/minimal/README.md diff --git a/README.md b/README.md index 11303a4..51567d3 100644 --- a/README.md +++ b/README.md @@ -19,47 +19,51 @@ -- [Quick Start](#quick-start) - - [1. Bootstrap your project](#1-bootstrap-your-project) - - [2. Add and edit template files](#2-add-and-edit-template-files) - - [3. Build the app (TypeScript only)](#3-build-the-app-typescript-only) - - [4. Publish package to npm](#4-publish-package-to-npm) - - [5. PROFIT](#5-profit) -- [Template](#template) - - [Advanced: Multiple templates](#advanced-multiple-templates) - - [Helper functions](#helper-functions) - - [`upper`](#upper) - - [`lower`](#lower) - - [`capital`](#capital) - - [`camel`](#camel) - - [`snake`](#snake) - - [`kebab`](#kebab) - - [`space`](#space) - - [`uuid`](#uuid) -- [Config](#config) - - [templateRoot (required)](#templateroot-required) - - [modifyName (default: `undefined`)](#modifyname-default-undefined) - - [extra (default: `undefined`)](#extra-default-undefined) - - [defaultDescription (default: `description`)](#defaultdescription-default-description) - - [defaultAuthor (default: `user.name` in `~/.gitconfig` otherwise `Your name`)](#defaultauthor-default-username-in-gitconfig-otherwise-your-name) - - [defaultEmail (default: `user.email` in `~/.gitconfig` otherwise `Your email`)](#defaultemail-default-useremail-in-gitconfig-otherwise-your-email) - - [defaultTemplate (default: `default`)](#defaulttemplate-default-default) - - [defaultLicense (default: `MIT`)](#defaultlicense-default-mit) - - [defaultPackageManager (default: `undefined`)](#defaultpackagemanager-default-undefined) - - [promptForDescription (default: `true`)](#promptfordescription-default-true) - - [promptForAuthor (default: `true`)](#promptforauthor-default-true) - - [promptForEmail (default: `true`)](#promptforemail-default-true) - - [promptForTemplate (default: `false`)](#promptfortemplate-default-false) - - [promptForLicense (default: `true`)](#promptforlicense-default-true) - - [promptForPackageManager (default: `false`)](#promptforpackagemanager-default-false) - - [skipGitInit (default: `false`)](#skipgitinit-default-false) - - [skipNpmInstall (default: `false`)](#skipnpminstall-default-false) - - [after (default: `undefined`)](#after-default-undefined) - - [caveat (default: `undefined`)](#caveat-default-undefined) - - [`AfterHookOptions`](#afterhookoptions) -- [Showcase](#showcase) -- [Contribution](#contribution) - - [Contributors ✨](#contributors-) + +- [✨ Create Create App](#-create-create-app) + - [Why?](#why) + - [Table of contents](#table-of-contents) + - [Quick Start](#quick-start) + - [1. Bootstrap your project](#1-bootstrap-your-project) + - [2. Add and edit template files](#2-add-and-edit-template-files) + - [3. Build the app (TypeScript only)](#3-build-the-app-typescript-only) + - [4. Publish package to npm](#4-publish-package-to-npm) + - [5. PROFIT](#5-profit) + - [Template](#template) + - [Advanced: Multiple templates](#advanced-multiple-templates) + - [Helper functions](#helper-functions) + - [`upper`](#upper) + - [`lower`](#lower) + - [`capital`](#capital) + - [`camel`](#camel) + - [`snake`](#snake) + - [`kebab`](#kebab) + - [`space`](#space) + - [`uuid`](#uuid) + - [Config](#config) + - [templateRoot (required)](#templateroot-required) + - [modifyName (default: `undefined`)](#modifyname-default-undefined) + - [extra (default: `undefined`)](#extra-default-undefined) + - [defaultDescription (default: `description`)](#defaultdescription-default-description) + - [defaultAuthor (default: `user.name` in `~/.gitconfig` otherwise `Your name`)](#defaultauthor-default-username-in-gitconfig-otherwise-your-name) + - [defaultEmail (default: `user.email` in `~/.gitconfig` otherwise `Your email`)](#defaultemail-default-useremail-in-gitconfig-otherwise-your-email) + - [defaultTemplate (default: `default`)](#defaulttemplate-default-default) + - [defaultLicense (default: `MIT`)](#defaultlicense-default-mit) + - [defaultPackageManager (default: `undefined`)](#defaultpackagemanager-default-undefined) + - [promptForDescription (default: `true`)](#promptfordescription-default-true) + - [promptForAuthor (default: `true`)](#promptforauthor-default-true) + - [promptForEmail (default: `true`)](#promptforemail-default-true) + - [promptForTemplate (default: `false`)](#promptfortemplate-default-false) + - [promptForLicense (default: `true`)](#promptforlicense-default-true) + - [promptForPackageManager (default: `false`)](#promptforpackagemanager-default-false) + - [skipGitInit (default: `false`)](#skipgitinit-default-false) + - [skipNpmInstall (default: `false`)](#skipnpminstall-default-false) + - [after (default: `undefined`)](#after-default-undefined) + - [caveat (default: `undefined`)](#caveat-default-undefined) + - [`AfterHookOptions`](#afterhookoptions) + - [Showcase](#showcase) + - [Contribution](#contribution) + - [Contributors ✨](#contributors-) @@ -120,6 +124,8 @@ Built-in variables are: - `{{contact}}` author name formatted with `{{name}} <{{email}}>`. If email is missing, simply `{{name}}` - `{{license}}` package license (e.g. `MIT`) - `{{year}}` current year (e.g. `2021`) +- `{{template}} selected template name (e.g. `typescript`) +- `{{packageManager}} package manager (e.g. `yarn`) ### Advanced: Multiple templates @@ -346,6 +352,8 @@ create('create-greet', { ```typescript { // variables + name: string; // e.g. "create-greet" + template: string; // e.g. "default" packageDir: string; // e.g. "/path/to/ohayo" templateDir: string; // e.g. "/path/to/create-greet/templates/default" year: number; // e.g. 2020 diff --git a/src/cli.ts b/src/cli.ts index dc7b271..a7f8ab8 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -6,7 +6,7 @@ import { AfterHookOptions, create } from '.'; const templateRoot = resolve(__dirname, '..', 'templates'); -const caveat = ({ name, template }: AfterHookOptions) => { +const caveat = ({ answers: { name, template } }: AfterHookOptions) => { let text = ` cd ${chalk.bold.green(name)} `; @@ -40,14 +40,15 @@ ${chalk.yellow( create('create-create-app', { templateRoot, - modifyName: (name) => (name.startsWith('create-') ? name : `create-${name}`), - promptForTemplate: true, skipNpmInstall: true, + modifyName: (name) => (name.startsWith('create-') ? name : `create-${name}`), + after: async ({ installNpmPackage }: AfterHookOptions) => { - console.log('Installing the latest version of create-create-app'); + console.log('\nInstalling the latest version of create-create-app'); await installNpmPackage('create-create-app'); }, + caveat, }); diff --git a/src/index.ts b/src/index.ts index 48d3656..741c36a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,72 +1,203 @@ import chalk from 'chalk'; import { epicfail } from 'epicfail'; import execa, { CommonOptions, ExecaChildProcess } from 'execa'; -import fs from 'fs'; +import { existsSync, writeFileSync } from 'fs'; import { availableLicenses as getAvailableLicenses, makeLicenseSync, } from 'license.js'; -import path from 'path'; +import path, { join, resolve } from 'path'; import yargsInteractive, { OptionData } from 'yargs-interactive'; import { exists, isOccupied } from './fs'; import { getGitUser, initGit } from './git'; -import { addDeps, installDeps, PackageManager, whichPm } from './npm'; +import { + addDeps, + initPackage, + installDeps, + PackageManager, + whichPm, +} from './npm'; import { copy, getAvailableTemplates } from './template'; export interface Option { [key: string]: OptionData | { default: boolean }; } +/** Options for `create` function */ export interface Options { + /** Path to templates folder. + * + * In default, `templateRoot` is set to `path.resolve(__dirname, '../templates')`. You can change this to any location you like. */ templateRoot: string; + /** Modify package name. + * + * ```js + * { + * modifyName: (name) => (name.startsWith('create-') ? name : `create-${name}`); + * } + * ``` + */ modifyName?: (name: string) => string | Promise; + + /** Additional questions can be defined. + * + * These options will be available as CLI flags, interactive questions, and template strings. In the example above, `--language` flag and the `{{language}}` template string will be enabled in the app. + */ extra?: Option; + /** Default value for a package description (default: `description`) */ defaultDescription?: string; + + /** Default value for a package author (default: `user.name` in `~/.gitconfig` otherwise `Your name`) */ defaultAuthor?: string; + + /** Default value for a package author email (default: `user.email` in `~/.gitconfig` otherwise `Your email`) */ defaultEmail?: string; + + /** Default value for a template (default: `default`) */ defaultTemplate?: string; + + /** Default value for license (default: `MIT`) */ defaultLicense?: string; + + /** Default value for package manager (default: `undefined`) + * + * `npm`, `yarn` and `pnpm` are available. `undefined` to auto detect package manager. */ defaultPackageManager?: PackageManager; + /** Interactively asks users for a description */ promptForDescription?: boolean; + + /** Interactively asks users for a package author */ promptForAuthor?: boolean; + + /** Interactively asks users for a package author email */ promptForEmail?: boolean; + + /** Interactively asks users for a template + * + * If there are no multiple templates in the `templates` directory, it won't display a prompt anyways. + * + * Even if `promptForTemplate` is set to `false`, users can still specify a template via a command line flag `--template