Skip to content

Commit

Permalink
Use @clack/prompts instead of Inquirer
Browse files Browse the repository at this point in the history
  • Loading branch information
TooTallNate committed Aug 31, 2023
1 parent 04c95ce commit c946fe5
Show file tree
Hide file tree
Showing 4 changed files with 190 additions and 282 deletions.
5 changes: 5 additions & 0 deletions .changeset/afraid-beds-agree.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'create-nxjs-app': patch
---

Use `@clack/prompts` instead of Inquirer
2 changes: 1 addition & 1 deletion packages/create-nxjs-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"author": "Nathan Rajlich <n@n8.io>",
"license": "MIT",
"dependencies": {
"@inquirer/prompts": "^3.0.4",
"@clack/prompts": "^0.7.0",
"chalk": "^5.3.0",
"degit": "^2.8.4",
"terminal-link": "^3.0.0",
Expand Down
246 changes: 139 additions & 107 deletions packages/create-nxjs-app/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { promises as fs } from 'fs';
import { spawn } from 'child_process';
import terminalLink from 'terminal-link';
import { fileURLToPath, pathToFileURL } from 'url';
import { input, select, Separator } from '@inquirer/prompts';
import * as clack from '@clack/prompts';
import type { PackageJson } from 'types-package-json';

async function hasPackageManager(name: string) {
Expand All @@ -21,123 +21,155 @@ const packageManagersPromise = Promise.all([
hasPackageManager('npm'),
]);

// Ask which example app to clone
const distDir = new URL('../dist/', import.meta.url);
const { version } = JSON.parse(
await fs.readFile(new URL('../package.json', import.meta.url), 'utf8')
);
const choices: { name: string; value: string; description: string }[] =
JSON.parse(await fs.readFile(new URL('choices.json', distDir), 'utf8'));
for (const c of choices) {
c.value = c.name;
c.name = terminalLink(
c.name,
`https://github.com/TooTallNate/nx.js/tree/v${version}/apps/${c.name}`,
{ fallback: false }
);
}
const template = await select({
message: 'Select a nx.js template:',
choices,
});
clack.intro(chalk.bold('Create nx.js app'));

// Ask name of user's application
const appName = await input({
message: 'Enter the name of your application:',
default: template,
});
try {
// Ask which example app to clone
const distDir = new URL('../dist/', import.meta.url);
const { version } = JSON.parse(
await fs.readFile(new URL('../package.json', import.meta.url), 'utf8')
);
const choices: { name: string; value: string; description: string }[] =
JSON.parse(await fs.readFile(new URL('choices.json', distDir), 'utf8'));
const template = await clack.select({
message: 'Select a nx.js template:',
options: choices.map(({ name, description }) => {
return {
label: terminalLink(
name,
`https://github.com/TooTallNate/nx.js/tree/v${version}/apps/${name}`,
{ fallback: false }
),
value: name,
hint: description,
};
}),
});
if (clack.isCancel(template)) {
throw template;
}

// Begin cloning example to project directory
const appDir = new URL(`${appName}/`, pathToFileURL(process.cwd() + '/'));
await fs.mkdir(appDir, { recursive: true });
const emitter = degit(`TooTallNate/nx.js/apps/${template}#v${version}`);
emitter.on('info', (info) => {
if (info.code !== 'SUCCESS') {
console.log(`${info.code}: ${info.message}`);
// Ask name of user's application
const appName = await clack.text({
message: 'Enter the name of your application:',
// TODO: figure out why `template` is not inferred as "string"
defaultValue: template as string,
});
if (clack.isCancel(appName)) {
throw appName;
}
});
const clonePromise = emitter.clone(fileURLToPath(appDir)).catch((err) => err);

// Ask which package manager to use. This happens
// in parallel of cloning the example application.
const packageManagers = (await packageManagersPromise).filter(
(p) => p !== null
) as string[];
const packageManager = await select({
message: 'Install dependencies using which package manager?',
choices: [
...packageManagers.map((name) => {
return { name, value: name };
}),
new Separator(),
{
name: 'skip',
value: 'skip',
description: 'Dependencies will not be installed',
},
],
});
// Begin cloning example to project directory
const appDir = new URL(`${appName}/`, pathToFileURL(process.cwd() + '/'));
await fs.mkdir(appDir, { recursive: true });
const emitter = degit(`TooTallNate/nx.js/apps/${template}#v${version}`);
emitter.on('info', (info) => {
if (info.code !== 'SUCCESS') {
console.log(`${info.code}: ${info.message}`);
}
});
const clonePromise = emitter
.clone(fileURLToPath(appDir))
.catch((err) => err);

// Wait for cloning to complete
const cloneError = await clonePromise;
if (cloneError) {
console.error(`There was an error during the cloning process:`);
console.error(cloneError);
process.exit(1);
}
// Ask which package manager to use. This happens
// in parallel of cloning the example application.
const packageManagers = (await packageManagersPromise).filter(
(p) => p !== null
) as string[];
const packageManager = await clack.select({
message: 'Install dependencies using which package manager?',
options: [
...packageManagers.map((label) => {
return { label, value: label };
}),
{
label: 'skip',
value: 'skip',
hint: 'dependencies will not be installed',
},
],
});
if (clack.isCancel(packageManager)) {
throw packageManager;
}

// Wait for cloning to complete
const cloneError = await clonePromise;
if (cloneError) {
console.error(`There was an error during the cloning process:`);
console.error(cloneError);
process.exit(1);
}

function removeWorkspace(deps: Record<string, string> = {}) {
for (const [name, value] of Object.entries(deps)) {
const noWorkspace = value.replace(/^workspace:\*?/, '');
if (noWorkspace !== value) {
deps[name] = `${noWorkspace}${version}`;
function removeWorkspace(deps: Record<string, string> = {}) {
for (const [name, value] of Object.entries(deps)) {
const noWorkspace = value.replace(/^workspace:\*?/, '');
if (noWorkspace !== value) {
deps[name] = `${noWorkspace}${version}`;
}
}
}
}

// Patch the `package.json` file with the app name, and update any
// dependencies that use "workspace:" in the version to the actual version
const packageJsonUrl = new URL('package.json', appDir);
const packageJson: PackageJson = JSON.parse(
await fs.readFile(packageJsonUrl, 'utf8')
);
packageJson.name = appName;
packageJson.description = '';
removeWorkspace(packageJson.dependencies);
removeWorkspace(packageJson.devDependencies);
await fs.writeFile(packageJsonUrl, `${JSON.stringify(packageJson, null, 2)}\n`);
// Patch the `package.json` file with the app name, and update any
// dependencies that use "workspace:" in the version to the actual version
const packageJsonUrl = new URL('package.json', appDir);
const packageJson: PackageJson = JSON.parse(
await fs.readFile(packageJsonUrl, 'utf8')
);
packageJson.name = appName;
packageJson.description = '';
removeWorkspace(packageJson.dependencies);
removeWorkspace(packageJson.devDependencies);
await fs.writeFile(
packageJsonUrl,
`${JSON.stringify(packageJson, null, 2)}\n`
);

// Install dependencies
if (packageManager !== 'skip') {
const cp = spawn(packageManager, ['install'], {
stdio: 'inherit',
cwd: appDir,
});
const [exitCode] = await once(cp, 'exit');
if (exitCode !== 0) {
process.exit(exitCode);
// Install dependencies
if (packageManager !== 'skip') {
// TODO: figure out why `template` is not inferred as "string"
const spinner = clack.spinner();
spinner.start(`Installing via ${packageManager}`);
const cp = spawn(packageManager as string, ['install'], {
cwd: appDir,
});
const [exitCode] = await once(cp, 'exit');
if (exitCode !== 0) {
process.exit(exitCode);
}
spinner.stop(`Installed via ${packageManager}`);
}
}

// Ok, we're done
console.log(
chalk.green(
`\n🎉 nx.js app ${chalk.bold(`"${appName}"`)} initialized successfully!`
)
);
console.log();
console.log(chalk.bold('Next steps'));
console.log();
const cmd = chalk.yellow;
if (packageManager === 'skip') {
console.log(` • Run the following command: ${cmd(`cd ${appName}`)}`);
console.log(` • Install dependencies using your preferred package manager`);
console.log(` • Invoke the ${cmd('build')} script to bundle your app`);
console.log(` • Invoke the ${cmd('nro')} script to generate a NRO file`);
} else {
console.log(` • Run the following commands:`);
// Ok, we're done
clack.outro(
chalk.green(
`🎉 nx.js app ${chalk.bold(
`"${appName}"`
)} initialized successfully!`
)
);
console.log();
console.log(chalk.bold('Next steps'));
console.log();
console.log(` ${cmd(`cd ${appName}`)}`);
console.log(` ${cmd(`${packageManager} run build`)}`);
console.log(` ${cmd(`${packageManager} run nro`)}`);
const cmd = chalk.yellow;
if (packageManager === 'skip') {
console.log(` • Run the following command: ${cmd(`cd ${appName}`)}`);
console.log(
` • Install dependencies using your preferred package manager`
);
console.log(` • Invoke the ${cmd('build')} script to bundle your app`);
console.log(
` • Invoke the ${cmd('nro')} script to generate a NRO file`
);
} else {
console.log(` • Run the following commands:`);
console.log();
console.log(` ${cmd(`cd ${appName}`)}`);
console.log(` ${cmd(`${packageManager} run build`)}`);
console.log(` ${cmd(`${packageManager} run nro`)}`);
}
} catch (err: unknown) {
if (!clack.isCancel(err)) throw err;
clack.cancel('Operation cancelled.');
}

0 comments on commit c946fe5

Please sign in to comment.