Skip to content

Commit 718b190

Browse files
lucas-labsuetchy
andauthored
feat: guess pm, skip install/git (#42) by @lucas-labs
* fix: variables in template filenames * feat: guess pm, prompt for PM and license option + adds pm guessing feature + adds option promptForNodePM (if setted to true it will ask for a pm and it will not use the new pm guessing feature to define which pm to use) + adds promptForLicense option (defaults to true) if setted to false, it will use UNLICENSED and not ask the user for a license + adds skipGitInit option, if setted to true it will skip git initialization step (can also be setted by --skip-git cli arg) + adds skipNpmInstall option, if setted to true it will skip npm install step (can also be setted by --skip-install cli arg) * fix: tests * chore: delete utils.ts * Update src/npm.ts Co-authored-by: uetchy <y@uechi.io>
1 parent 101e03c commit 718b190

File tree

3 files changed

+108
-15
lines changed

3 files changed

+108
-15
lines changed

src/index.ts

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import path from 'path';
77
import yargsInteractive, { OptionData } from 'yargs-interactive';
88
import { exists, isOccupied } from './fs';
99
import { getGitUser, initGit } from './git';
10-
import { addDeps, installDeps } from './npm';
10+
import { addDeps, installDeps, whichPm } from './npm';
1111
import { copy, getAvailableTemplates } from './template';
1212

1313
export interface Option {
@@ -17,6 +17,10 @@ export interface Option {
1717
export interface Options {
1818
templateRoot: string;
1919
promptForTemplate?: boolean;
20+
promptForLicense?: boolean;
21+
promptForNodePM?: boolean;
22+
skipGitInit?: boolean;
23+
skipNpmInstall?: boolean;
2024
modifyName?: (name: string) => string | Promise<string>;
2125
extra?: Option;
2226
caveat?:
@@ -73,6 +77,8 @@ function getContact(author: string, email?: string) {
7377
async function getYargsOptions(
7478
templateRoot: string,
7579
promptForTemplate: boolean,
80+
promptForLicense: boolean,
81+
promptForNodePM: boolean,
7682
extraOptions: Option = {}
7783
) {
7884
const gitUser = await getGitUser();
@@ -110,16 +116,16 @@ async function getYargsOptions(
110116
type: 'list',
111117
describe: 'license',
112118
choices: [...availableLicenses(), 'UNLICENSED'],
113-
default: 'MIT',
114-
prompt: 'if-no-arg',
119+
default: promptForLicense ? 'MIT' : 'UNLICENSED',
120+
prompt: promptForLicense ? 'if-no-arg' : 'never',
115121
},
116122
'node-pm': {
117123
type: 'list',
118124
describe:
119125
'select package manager to use for installing packages from npm',
120126
choices: ['npm', 'yarn', 'pnpm'],
121-
default: 'npm',
122-
prompt: 'never',
127+
default: undefined, // undefined by default, we'll try to guess pm manager later
128+
prompt: promptForNodePM ? 'if-no-arg' : 'never',
123129
},
124130
...extraOptions,
125131
};
@@ -149,13 +155,21 @@ export async function create(appName: string, options: Options) {
149155
throw new CLIError(`${packageDir} is not empty directory.`);
150156
}
151157

152-
const { templateRoot, promptForTemplate = false } = options;
158+
const {
159+
templateRoot,
160+
promptForTemplate = false,
161+
promptForLicense = true,
162+
promptForNodePM = false,
163+
} = options;
153164

154165
const yargsOption = await getYargsOptions(
155166
templateRoot,
156167
promptForTemplate,
168+
promptForLicense,
169+
promptForNodePM,
157170
options.extra
158171
);
172+
159173
const args = await yargsInteractive()
160174
.usage('$0 <name> [args]')
161175
.interactive(yargsOption as any);
@@ -212,12 +226,17 @@ export async function create(appName: string, options: Options) {
212226
}
213227

214228
// init git
215-
try {
216-
console.log('\nInitializing a git repository');
217-
await initGit(packageDir);
218-
} catch (err: any) {
219-
if (err?.exitCode == 127) return; // no git available
220-
throw err;
229+
const skipGit = args['skip-git'];
230+
231+
// init git if option skipGitInit or arg --skip-git are not set
232+
if (!(options.skipGitInit || skipGit)) {
233+
try {
234+
console.log('\nInitializing a git repository');
235+
await initGit(packageDir);
236+
} catch (err: any) {
237+
if (err?.exitCode == 127) return; // no git available
238+
throw err;
239+
}
221240
}
222241

223242
const run = (command: string, options: CommonOptions<string> = {}) => {
@@ -233,10 +252,16 @@ export async function create(appName: string, options: Options) {
233252
let installNpmPackage = async (packageName: string): Promise<void> => {};
234253

235254
if (exists('package.json', packageDir)) {
236-
const packageManager = args['node-pm'];
255+
const nodePMArg = args['node-pm'];
256+
const skipInstallArg = args['skip-install'];
257+
258+
// guess which package manager to use
259+
const packageManager = whichPm(nodePMArg);
237260

238-
console.log(`Installing dependencies using ${packageManager}`);
239-
await installDeps(packageDir, packageManager);
261+
// install deps only if skipNpmInstall is not falsy
262+
if (!(options.skipNpmInstall || skipInstallArg)) {
263+
await installDeps(packageDir, packageManager);
264+
}
240265

241266
installNpmPackage = async (
242267
pkg: string | string[],

src/npm.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,49 @@
11
import { NodePM, printCommand, CLIError } from '.';
22
import { spawnPromise } from './fs';
33

4+
function getPm(name: string) {
5+
switch (name) {
6+
case 'pnpm':
7+
return NodePM.Pnpm;
8+
9+
case 'yarn':
10+
return NodePM.Yarn;
11+
12+
case 'npm':
13+
return NodePM.Npm;
14+
15+
default:
16+
return NodePM.Npm;
17+
}
18+
}
19+
20+
// License for `whichPm`
21+
// The MIT License (MIT)
22+
// Copyright (c) 2017-2022 Zoltan Kochan <z@kochan.io>
23+
// https://github.com/zkochan/packages/tree/main/which-pm-runs
24+
export function whichPm(defaultPm?: string) {
25+
// if there's a default pm (passed by argv), use it
26+
if(defaultPm) {
27+
return getPm(defaultPm);
28+
}
29+
30+
if (!process.env.npm_config_user_agent) {
31+
return NodePM.Npm;
32+
}
33+
34+
const pmSpec = process.env.npm_config_user_agent.split(' ')[0]
35+
const separatorPos = pmSpec.lastIndexOf('/')
36+
const name = pmSpec.substring(0, separatorPos)
37+
38+
return getPm(name);
39+
}
40+
441
export async function installDeps(rootDir: string, pm: NodePM) {
542
let command: string;
643
let args: string[];
744

45+
console.log(`Installing dependencies using ${pm}`);
46+
847
switch (pm) {
948
case NodePM.Npm: {
1049
command = 'npm';

tests/index.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,3 +219,32 @@ test('create unlicensed app', async () => {
219219
const existed = existsSync(`${baseDir}/create-greet/LICENSE`);
220220
expect(existed).toBeFalsy();
221221
}, 300000);
222+
223+
it('should create project without npm install and without git', async () => {
224+
const baseDir = mkdtempSync(TEST_PREFIX);
225+
226+
const opts = [
227+
'create-greet',
228+
'--description',
229+
'desc.',
230+
'--author',
231+
'"Awesome Doe"',
232+
'--email',
233+
'awesome@example.com',
234+
'--template',
235+
'default',
236+
'--license',
237+
'UNLICENSED',
238+
'--skip-install',
239+
'--skip-git'
240+
];
241+
const { stdout } = await execa(SCRIPT_PATH, opts, {
242+
cwd: baseDir,
243+
});
244+
245+
// cant check node_modules doesn't exist, because
246+
// it will exist anyways after AfterHookOptions executes!
247+
expect(stdout).not.toContain('Installing dependencies using');
248+
expect(stdout).not.toContain('Initializing a git repository');
249+
expect(existsSync(`${baseDir}/create-greet/.git`)).toBeFalsy();
250+
}, 300000);

0 commit comments

Comments
 (0)