Skip to content

Commit

Permalink
core-create: large refactor
Browse files Browse the repository at this point in the history
* drop `yarn` support as it doesn't support peer dependency installation
* drop `ora` and add proper messaging
* change git branch to `main`
* add initial git commit
* merge `ProjectPackage` back into `sync`
  • Loading branch information
Paul committed Nov 11, 2021
1 parent d8383ed commit 122bdcf
Show file tree
Hide file tree
Showing 8 changed files with 231 additions and 404 deletions.
8 changes: 5 additions & 3 deletions core/create/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,21 @@
"kleur": "^4.1.4",
"lodash.merge": "^4.6.2",
"npm-package-arg": "^8.1.5",
"ora": "^6.0.1",
"sade": "^1.7.4",
"sort-package-json": "^1.52.0"
},
"devDependencies": {
"@types/cross-spawn": "^6.0.2",
"@types/dedent": "^0.7.0",
"@types/lodash.merge": "^4.6.6",
"@types/npm-package-arg": "^6.1.1",
"@types/sade": "^1.7.3"
"@types/sade": "^1.7.3",
"dedent": "^0.7.0"
},
"engines": {
"node": ">=14",
"npm": ">=7"
"npm": ">=7",
"yarn": "unsupported"
},
"publishConfig": {
"access": "public"
Expand Down
148 changes: 0 additions & 148 deletions core/create/src/ProjectPackage.ts

This file was deleted.

14 changes: 4 additions & 10 deletions core/create/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,11 @@ type ObjectEntries = <T extends object>(

const typedObjectEntries: ObjectEntries = Object.entries;

const SCOPE = '@pota';

const getPorterDependency = <T extends string>(name: T) => `${SCOPE}/${name}` as const;

const SKELETON_SHORTHANDS = {
[getPorterDependency('webpack-skeleton')]: ['webpack'] as const,
[getPorterDependency('react-skeleton')]: ['react'] as const,
[getPorterDependency('react-base-skeleton')]: ['react-base'] as const,
[getPorterDependency('vue-skeleton')]: ['vue'] as const,
['@pota/webpack-skeleton']: ['webpack'] as const,
['@pota/react-skeleton']: ['react'] as const,
['@pota/react-base-skeleton']: ['react-base'] as const,
['@pota/vue-skeleton']: ['vue'] as const,
};

const INVERTED_SKELETON_SHORTHANDS = Object.fromEntries(
Expand All @@ -33,7 +29,5 @@ export const isSkeletonShorthand = (shorthand: string) => shorthand in INVERTED_
export const getSkeletonFromShorthand = (shorthand: string) =>
INVERTED_SKELETON_SHORTHANDS[shorthand];

export type PackageManager = 'yarn' | 'npm';

export const POTA_CLI = '@pota/cli' as const;
export const POTA_CLI_BIN = 'pota' as const;
140 changes: 33 additions & 107 deletions core/create/src/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,157 +1,83 @@
import { exec, SpawnOptions } from 'child_process';
import { rm, mkdir } from 'fs/promises';

import npa from 'npm-package-arg';
import readline from 'readline';
import type { Result as NpaResult } from 'npm-package-arg';
import crossSpawn from 'cross-spawn';
import { readPackageJson } from '@pota/shared/fs';
import kleur from 'kleur';

import { SPINNER } from './spinner.js';
import { readPackageJson, isDirectoryAvailable } from '@pota/shared/fs';

const { green, cyan } = kleur;

export const newline = () => console.log();

export const log = (text: string) => console.log(text);

export function clear() {
if (process.stdout.isTTY) {
console.log('\n'.repeat(process.stdout.rows));
readline.cursorTo(process.stdout, 0, 0);
readline.clearScreenDown(process.stdout);
}
}
export const log = (text: string = '') => console.log(text);

const createSpawn = (options: SpawnOptions) =>
async function spawn(command: string, ...args: string[]): Promise<void> {
await new Promise<void>((resolve, reject) =>
function createSpawn(options: SpawnOptions) {
return (command: string, ...args: string[]): Promise<void> =>
new Promise<void>((resolve, reject) =>
crossSpawn(command, args, options)
.on('close', (code) =>
code !== 0 ? reject({ command: `${command} ${args.join(' ')}` }) : resolve(),
)
.on('error', reject),
);
};
}

export const spawn = createSpawn({ stdio: 'inherit' });
export const spawnSilent = createSpawn({ stdio: 'ignore' });

export const command = (command: string, quiet: boolean = true) =>
new Promise<string | undefined>((resolve, reject) =>
export function command(command: string, quiet: boolean = true) {
return new Promise<string | undefined>((resolve, reject) =>
exec(command, (error, stdout, stderr) => {
if (error) {
return reject(error);
}
if (error) return reject(error);
resolve(quiet ? undefined : stdout || stderr);
}),
);

export function isValidSkeleton(skeleton: string) {
try {
return Boolean(npa(skeleton));
} catch (error) {
return false;
}
}

export function isFileSkeleton(skeleton: string) {
return npa(skeleton).type === 'file';
}

type PackageManager = 'yarn' | 'npm';

function getPackageManager(): PackageManager {
const userAgent = process.env.npm_config_user_agent;

if (userAgent?.startsWith('npm')) {
return 'npm';
}

if (userAgent?.startsWith('yarn')) {
return 'yarn';
}

return 'npm';
}

interface InstallOptions {
cwd?: string;
dev?: boolean;
npm?: boolean;
yarn?: boolean;
}

export function createInstaller(options: InstallOptions = {}) {
const { cwd, dev, yarn, npm } = options;
let pre: Array<string> = [];
let post: Array<string> = [];

const pm = !yarn && !npm ? getPackageManager() : yarn ? 'yarn' : 'npm';

switch (pm) {
case 'npm': {
pre = ['install'];
if (cwd) post.push('--prefix', cwd);
if (dev) post.push('--save-dev');
// reduce terminal noise
post.push('--no-audit', '--no-fund');
break;
}
case 'yarn': {
pre = ['add'];
if (cwd) post.push('--cwd', cwd);
if (dev) post.push('--dev');
break;
}
}

return async (...packages: ReadonlyArray<string>) => {
// we must use `install` instead of `add` with `yarn`
// when installing `package.json` deps
switch (pm) {
case 'yarn':
if (packages.length === 0) pre[0] = 'install';
break;
export function createBailer(dir?: string) {
return async () => {
if (dir) {
try {
log();
console.error('Deleting created directory.');
await rm(dir, { recursive: true });
} catch {}
}

SPINNER.stopAndPersist();

try {
await spawn(pm, ...pre, ...packages, ...post);
} finally {
SPINNER.start();
}
process.exit(1);
};
}

export async function getSkeletonName(rawSkeletonName: string, packageJsonPath: string) {
const parsedName = npa(rawSkeletonName);
export async function createDir(path: string) {
if (await isDirectoryAvailable(path)) await mkdir(path, { recursive: true });
else throw new Error(`${green(path)} already exists, please specify a different directory`);
}

export async function getSkeletonName(skeletonPkgDetails: NpaResult, packageJsonPath: string) {
const { dependencies = {}, devDependencies = {} } = await readPackageJson(packageJsonPath);

const dependency = [dependencies, devDependencies]
.flatMap((d) => Object.entries(d))
.find(([name, version]) => {
switch (parsedName.type) {
switch (skeletonPkgDetails.type) {
case 'git': {
const { gitCommittish, hosted } = parsedName;
const { gitCommittish, hosted } = skeletonPkgDetails;
const { user, type, project } = hosted!;

// github:mediamonks/pota#feature
return (
version === `${type}:${user}/${project}#${gitCommittish}` ||
version === parsedName.rawSpec
version === skeletonPkgDetails.rawSpec
);
}
case 'file':
return version === parsedName.saveSpec;
return version === skeletonPkgDetails.saveSpec;
default:
return name === rawSkeletonName;
return name === skeletonPkgDetails.raw;
}
});

if (!dependency)
throw new Error(`Could not find ${cyan(rawSkeletonName)} in ${green(packageJsonPath)}`);
if (!dependency) {
throw new Error(`Could not find ${cyan(skeletonPkgDetails.raw)} in ${green(packageJsonPath)}`);
}

return dependency[0]; // the name of the dependency
}
Loading

0 comments on commit 122bdcf

Please sign in to comment.