Skip to content

Commit

Permalink
feat(create-analog): set start command based on package manager (#43)
Browse files Browse the repository at this point in the history
  • Loading branch information
markostanimirovic authored Aug 18, 2022
1 parent 0c2ee9f commit b9e5f28
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 107 deletions.
2 changes: 1 addition & 1 deletion packages/create-analog/__tests__/cli.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const createNonEmptyDir = () => {
writeFileSync(pkgJson, '{ "foo": "bar" }');
};

// Vue 3 starter template
// Angular 14 starter template
let templateFiles = readdirSync(join(CLI_PATH, 'template-angular-v14'));
templateFiles.push('.git');
// _gitignore is renamed to .gitignore
Expand Down
213 changes: 107 additions & 106 deletions packages/create-analog/index.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,18 @@
#!/usr/bin/env node

// @ts-check
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import minimist from 'minimist'
import prompts from 'prompts'
import {
blue,
cyan,
green,
lightRed,
magenta,
red,
reset,
yellow
} from 'kolorist'
import { execSync } from 'node:child_process'
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import minimist from 'minimist';
import prompts from 'prompts';
import { red, reset, yellow } from 'kolorist';
import { execSync } from 'node:child_process';

// Avoids autoconversion to number of the project name by defining that the args
// non associated with an option ( _ ) needs to be parsed as a string. See #4606
const argv = minimist(process.argv.slice(2), { string: ['_'] })
const cwd = process.cwd()
const argv = minimist(process.argv.slice(2), { string: ['_'] });
const cwd = process.cwd();

const APPS = [
{
Expand All @@ -31,29 +22,29 @@ const APPS = [
{
name: 'angular-v14',
display: 'TypeScript',
color: yellow
}
]
}
]
color: yellow,
},
],
},
];

const TEMPLATES = APPS.map(
(f) => (f.variants && f.variants.map((v) => v.name)) || [f.name]
).reduce((a, b) => a.concat(b), [])
).reduce((a, b) => a.concat(b), []);

const renameFiles = {
_gitignore: '.gitignore'
}
_gitignore: '.gitignore',
};

async function init() {
let targetDir = formatTargetDir(argv._[0])
let template = argv.template || argv.t
let targetDir = formatTargetDir(argv._[0]);
let template = argv.template || argv.t;

const defaultTargetDir = 'analog-project'
const defaultTargetDir = 'analog-project';
const getProjectName = () =>
targetDir === '.' ? path.basename(path.resolve()) : targetDir
targetDir === '.' ? path.basename(path.resolve()) : targetDir;

let result = {}
let result = {};

try {
result = await prompts(
Expand All @@ -64,8 +55,8 @@ async function init() {
message: reset('Project name:'),
initial: defaultTargetDir,
onState: (state) => {
targetDir = formatTargetDir(state.value) || defaultTargetDir
}
targetDir = formatTargetDir(state.value) || defaultTargetDir;
},
},
{
type: () =>
Expand All @@ -75,24 +66,24 @@ async function init() {
(targetDir === '.'
? 'Current directory'
: `Target directory "${targetDir}"`) +
` is not empty. Remove existing files and continue?`
` is not empty. Remove existing files and continue?`,
},
{
type: (_, { overwrite } = {}) => {
if (overwrite === false) {
throw new Error(red('✖') + ' Operation cancelled')
throw new Error(red('✖') + ' Operation cancelled');
}
return null
return null;
},
name: 'overwriteChecker'
name: 'overwriteChecker',
},
{
type: () => (isValidPackageName(getProjectName()) ? null : 'text'),
name: 'packageName',
message: reset('Package name:'),
initial: () => toValidPackageName(getProjectName()),
validate: (dir) =>
isValidPackageName(dir) || 'Invalid package.json name'
isValidPackageName(dir) || 'Invalid package.json name',
},
{
type: template && TEMPLATES.includes(template) ? null : 'select',
Expand All @@ -105,12 +96,12 @@ async function init() {
: reset('Select a template:'),
initial: 0,
choices: APPS.map((framework) => {
const frameworkColor = framework.color
const frameworkColor = framework.color;
return {
title: frameworkColor(framework.name),
value: framework
}
})
value: framework,
};
}),
},
{
type: (framework) =>
Expand All @@ -120,107 +111,101 @@ async function init() {
// @ts-ignore
choices: (framework) =>
framework.variants.map((variant) => {
const variantColor = variant.color
const variantColor = variant.color;
return {
title: variantColor(variant.name),
value: variant.name
}
})
}
value: variant.name,
};
}),
},
],
{
onCancel: () => {
throw new Error(red('✖') + ' Operation cancelled')
}
throw new Error(red('✖') + ' Operation cancelled');
},
}
)
);
} catch (cancelled) {
console.log(cancelled.message)
return
console.log(cancelled.message);
return;
}

// user choice associated with prompts
const { framework, overwrite, packageName, variant } = result
const { framework, overwrite, packageName, variant } = result;

const root = path.join(cwd, targetDir)
const root = path.join(cwd, targetDir);

if (overwrite) {
emptyDir(root)
emptyDir(root);
} else if (!fs.existsSync(root)) {
fs.mkdirSync(root, { recursive: true })
fs.mkdirSync(root, { recursive: true });
}

// determine template
template = variant || framework || template
template = variant || framework || template;

console.log(`\nScaffolding project in ${root}...`)
console.log(`\nScaffolding project in ${root}...`);

const templateDir = path.resolve(
fileURLToPath(import.meta.url),
'..',
`template-${template}`
)
);

const write = (file, content) => {
const targetPath = renameFiles[file]
? path.join(root, renameFiles[file])
: path.join(root, file)
: path.join(root, file);
if (content) {
fs.writeFileSync(targetPath, content)
fs.writeFileSync(targetPath, content);
} else {
copy(path.join(templateDir, file), targetPath)
copy(path.join(templateDir, file), targetPath);
}
}
};

const files = fs.readdirSync(templateDir)
const files = fs.readdirSync(templateDir);
for (const file of files.filter((f) => f !== 'package.json')) {
write(file)
write(file);
}

const pkgInfo = pkgFromUserAgent(process.env.npm_config_user_agent);
const pkgManager = pkgInfo ? pkgInfo.name : 'npm';
const pkg = JSON.parse(
fs.readFileSync(path.join(templateDir, `package.json`), 'utf-8')
)
);

pkg.name = packageName || getProjectName()
pkg.name = packageName || getProjectName();
pkg.scripts.start = getStartCommand(pkgManager);

write('package.json', JSON.stringify(pkg, null, 2))
write('package.json', JSON.stringify(pkg, null, 2));

console.log(`\nInitializing git repository:`);
execSync(`git init ${targetDir} && cd ${targetDir} && git add . && git commit -m "initial commit"`);
execSync(
`git init ${targetDir} && cd ${targetDir} && git add . && git commit -m "initial commit"`
);

const pkgInfo = pkgFromUserAgent(process.env.npm_config_user_agent)
const pkgManager = pkgInfo ? pkgInfo.name : 'npm'

console.log(`\nDone. Now run:\n`)
console.log(`\nDone. Now run:\n`);
if (root !== cwd) {
console.log(` cd ${path.relative(cwd, root)}`)
}
switch (pkgManager) {
case 'yarn':
console.log(' yarn')
console.log(' yarn dev')
break
default:
console.log(` ${pkgManager} install`)
console.log(` ${pkgManager} run dev`)
break
console.log(` cd ${path.relative(cwd, root)}`);
}
console.log()
console.log(` ${getInstallCommand(pkgManager)}`);
console.log(` ${getStartCommand(pkgManager)}`);
console.log();
}

/**
* @param {string | undefined} targetDir
*/
function formatTargetDir(targetDir) {
return targetDir?.trim().replace(/\/+$/g, '')
return targetDir?.trim().replace(/\/+$/g, '');
}

function copy(src, dest) {
const stat = fs.statSync(src)
const stat = fs.statSync(src);
if (stat.isDirectory()) {
copyDir(src, dest)
copyDir(src, dest);
} else {
fs.copyFileSync(src, dest)
fs.copyFileSync(src, dest);
}
}

Expand All @@ -230,7 +215,7 @@ function copy(src, dest) {
function isValidPackageName(projectName) {
return /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/.test(
projectName
)
);
}

/**
Expand All @@ -242,39 +227,39 @@ function toValidPackageName(projectName) {
.toLowerCase()
.replace(/\s+/g, '-')
.replace(/^[._]/, '')
.replace(/[^a-z0-9-~]+/g, '-')
.replace(/[^a-z0-9-~]+/g, '-');
}

/**
* @param {string} srcDir
* @param {string} destDir
*/
function copyDir(srcDir, destDir) {
fs.mkdirSync(destDir, { recursive: true })
fs.mkdirSync(destDir, { recursive: true });
for (const file of fs.readdirSync(srcDir)) {
const srcFile = path.resolve(srcDir, file)
const destFile = path.resolve(destDir, file)
copy(srcFile, destFile)
const srcFile = path.resolve(srcDir, file);
const destFile = path.resolve(destDir, file);
copy(srcFile, destFile);
}
}

/**
* @param {string} path
*/
function isEmpty(path) {
const files = fs.readdirSync(path)
return files.length === 0 || (files.length === 1 && files[0] === '.git')
const files = fs.readdirSync(path);
return files.length === 0 || (files.length === 1 && files[0] === '.git');
}

/**
* @param {string} dir
*/
function emptyDir(dir) {
if (!fs.existsSync(dir)) {
return
return;
}
for (const file of fs.readdirSync(dir)) {
fs.rmSync(path.resolve(dir, file), { recursive: true, force: true })
fs.rmSync(path.resolve(dir, file), { recursive: true, force: true });
}
}

Expand All @@ -283,15 +268,31 @@ function emptyDir(dir) {
* @returns object | undefined
*/
function pkgFromUserAgent(userAgent) {
if (!userAgent) return undefined
const pkgSpec = userAgent.split(' ')[0]
const pkgSpecArr = pkgSpec.split('/')
if (!userAgent) return undefined;
const pkgSpec = userAgent.split(' ')[0];
const pkgSpecArr = pkgSpec.split('/');
return {
name: pkgSpecArr[0],
version: pkgSpecArr[1]
}
version: pkgSpecArr[1],
};
}

/**
* @param {string} pkgManager
* @returns string
*/
function getInstallCommand(pkgManager) {
return pkgManager === 'yarn' ? 'yarn' : `${pkgManager} install`;
}

/**
* @param {string} pkgManager
* @returns string
*/
function getStartCommand(pkgManager) {
return pkgManager === 'yarn' ? 'yarn dev' : `${pkgManager} run dev`;
}

init().catch((e) => {
console.error(e)
})
console.error(e);
});

0 comments on commit b9e5f28

Please sign in to comment.