From 122bdcf3220929f60c19a615fdc688abd25ca896 Mon Sep 17 00:00:00 2001
From: Paul
Date: Thu, 11 Nov 2021 20:55:05 +0100
Subject: [PATCH] core-create: large refactor * 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`
---
core/create/package.json | 8 +-
core/create/src/ProjectPackage.ts | 148 ----------------------
core/create/src/config.ts | 14 +--
core/create/src/helpers.ts | 140 +++++----------------
core/create/src/index.ts | 200 +++++++++++++-----------------
core/create/src/spinner.ts | 3 -
core/create/src/sync.ts | 99 +++++++++++++--
package-lock.json | 23 +++-
8 files changed, 231 insertions(+), 404 deletions(-)
delete mode 100644 core/create/src/ProjectPackage.ts
delete mode 100644 core/create/src/spinner.ts
diff --git a/core/create/package.json b/core/create/package.json
index fa7b8fc2..c636296b 100755
--- a/core/create/package.json
+++ b/core/create/package.json
@@ -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"
diff --git a/core/create/src/ProjectPackage.ts b/core/create/src/ProjectPackage.ts
deleted file mode 100644
index dcf55336..00000000
--- a/core/create/src/ProjectPackage.ts
+++ /dev/null
@@ -1,148 +0,0 @@
-import { basename, extname } from 'path';
-
-// @ts-ignore TypeScript is being weird
-import merge from 'lodash.merge';
-import { sortPackageJson } from 'sort-package-json';
-import { readPackageJson, writePackageJson } from '@pota/shared/fs';
-import { PotaConfig } from '@pota/shared/config';
-import type { PackageJsonShape } from '@pota/shared/config';
-
-import { POTA_CLI, POTA_CLI_BIN } from './config.js';
-
-function filterObject>(o: T, fields: ReadonlyArray) {
- type Result = Record;
-
- let r: Result | undefined = undefined;
-
- for (const field of Object.keys(o) as ReadonlyArray) {
- if (fields.includes(field)) {
- r ??= {} as Result;
- r[field] = o[field];
- }
- }
-
- return r;
-}
-
-export default class ProjectPackage {
- private static ignoredFields: ReadonlyArray = [
- 'name',
- 'version',
- 'peerDependencies',
- 'dependencies',
- 'devDependencies',
- 'files',
- 'publishConfig',
- 'repository',
- 'bugs',
- 'author',
- ];
-
- private pkg: PackageJsonShape | null = null;
-
- constructor(private path: string) {}
-
- /*
- * Loads the package from disk via the provided `path`
- */
- async load() {
- this.pkg = await readPackageJson(this.path);
-
- return this;
- }
-
- /*
- * Adds the passed file basename as a `@pota/cli command to the `scripts`
- */
- applyFileAsScript(file: string) {
- if (!this.pkg) throw new Error('`setProjectPackage` must be called before `addFileAsScript`');
-
- const command = basename(file, extname(file));
-
- this.pkg.scripts ??= {};
- this.pkg.scripts[command] = `${POTA_CLI_BIN} ${command}`;
- }
-
- /*
- * Merges the fields of the package from `path` into the project package.
- */
- async merge(path: string, config: PotaConfig) {
- if (!this.pkg) {
- throw new Error('`setProjectPackage` must be called before `applySkeletonPackage`');
- }
-
- const skeletonPkg = await readPackageJson(path);
-
- const { name: skeleton, version, peerDependencies = {} } = skeletonPkg;
-
- for (const field of ProjectPackage.ignoredFields) {
- delete skeletonPkg[field];
- }
-
- if ('scripts' in skeletonPkg && 'scripts' in config) {
- skeletonPkg.scripts = filterObject(skeletonPkg.scripts!, config.scripts!);
- }
-
- merge(this.pkg, skeletonPkg);
-
- // place the skeleton package in `devDependencies`
- this.pkg.devDependencies ??= {};
- // TODO: this semver chevron ain't great, chief
- this.pkg.devDependencies[skeleton!] = `^${version!}`;
-
- for (const [dep, version] of Object.entries(peerDependencies)) {
- // place the `@pota/cli` (if it exists) package in `devDependencies`
- if (dep === POTA_CLI) this.pkg.devDependencies[POTA_CLI] = peerDependencies[POTA_CLI];
- // this `else if` condition makes sure that the dependency isn't defined in `devDependencies`
- // which might me the case for extended skeletons defined in `peerDependencies`
- else if (!(dep in this.pkg.devDependencies)) {
- this.pkg.dependencies ??= {};
- this.pkg.dependencies[dep] = version;
- }
- }
- }
-
- /*
- * Sorts package fields using `sort-package-json`
- */
- sort() {
- if (!this.pkg) throw new Error('`setProjectPackage` must be called before `sort`');
-
- this.pkg = sortPackageJson(this.pkg);
-
- return this;
- }
-
- /*
- * Sets the `pota` field.
- */
- setDefaultSkeleton(skeleton: string) {
- if (!this.pkg) {
- throw new Error('`setProjectPackage` must be called before `setDefaultSkeleton`');
- }
-
- this.pkg.pota = skeleton;
-
- return this;
- }
-
- /*
- * Sets the `name` field.
- */
- setName(name: string) {
- if (!this.pkg) throw new Error('`setProjectPackage` must be called before `setName`');
-
- this.pkg.name = name;
-
- return this;
- }
-
- /*
- * Writes the package back to the `path`
- */
- async write() {
- if (!this.pkg) throw new Error('`setProjectPackage` must be called before `write`');
-
- await writePackageJson(this.pkg, this.path);
- }
-}
diff --git a/core/create/src/config.ts b/core/create/src/config.ts
index 2f5bad4b..f1c21bc3 100755
--- a/core/create/src/config.ts
+++ b/core/create/src/config.ts
@@ -11,15 +11,11 @@ type ObjectEntries = (
const typedObjectEntries: ObjectEntries = Object.entries;
-const SCOPE = '@pota';
-
-const getPorterDependency = (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(
@@ -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;
diff --git a/core/create/src/helpers.ts b/core/create/src/helpers.ts
index f50891f2..29fe28ca 100755
--- a/core/create/src/helpers.ts
+++ b/core/create/src/helpers.ts
@@ -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 {
- await new Promise((resolve, reject) =>
+function createSpawn(options: SpawnOptions) {
+ return (command: string, ...args: string[]): Promise =>
+ new Promise((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((resolve, reject) =>
+export function command(command: string, quiet: boolean = true) {
+ return new Promise((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 = [];
- let post: Array = [];
-
- 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) => {
- // 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
}
diff --git a/core/create/src/index.ts b/core/create/src/index.ts
index 53d0e5e1..c7f8f59c 100755
--- a/core/create/src/index.ts
+++ b/core/create/src/index.ts
@@ -1,166 +1,134 @@
-import { rm, mkdir } from 'fs/promises';
import { relative, basename } from 'path';
+import npa from 'npm-package-arg';
import sade from 'sade';
import kleur from 'kleur';
-import * as fs from '@pota/shared/fs';
+// @ts-ignore TypeScript is being weird
+import dedent from 'dedent';
+import { resolveUser } from '@pota/shared/fs';
-import { isSkeletonShorthand, getSkeletonFromShorthand, POTA_CLI } from './config.js';
+import { isSkeletonShorthand, getSkeletonFromShorthand } from './config.js';
import * as helpers from './helpers.js';
import sync from './sync.js';
-import { SPINNER } from './spinner.js';
-const { clear, log } = helpers;
-const { red, green, cyan } = kleur;
+const { log } = helpers;
+const { green, cyan, bold } = kleur;
type SadeSkeleton = string;
type SadeDirectory = string;
interface SadeOptions {
'fail-cleanup': boolean;
- 'use-npm': boolean;
- 'use-yarn': boolean;
}
sade('@pota/create ', true)
.describe('Create Pota project')
.option('--fail-cleanup', 'Cleanup after failing initialization', true)
- .option('--use-yarn', 'Force Pota to use yarn', false)
- .option('--use-npm', 'Force Pota to use npm', false)
.example('npx @pota/create webpack ./project-directory')
.action(async (skeleton: SadeSkeleton, dir: SadeDirectory, options: SadeOptions) => {
- SPINNER.start(`Creating new Pota App in ${green(dir)}`);
-
- /**
- * Validation
- */
- if (!(await fs.isDirectoryAvailable(dir))) {
- console.error(`${green(dir)} already exists, please specify a different directory`);
-
- process.exit(1);
- }
-
- if (!helpers.isValidSkeleton(skeleton)) {
- console.error(`${green(skeleton)} is not a valid skeleton package`);
-
- process.exit(1);
- }
-
- /**
- * Post-validation, initialization of utilities
- */
- let isFileSkeleton = false;
-
const pkgName = basename(dir);
- const cwd = fs.resolveUser(dir);
-
- if (helpers.isFileSkeleton(skeleton)) {
- isFileSkeleton = true;
- skeleton = relative(cwd, skeleton);
- } else if (isSkeletonShorthand(skeleton)) skeleton = getSkeletonFromShorthand(skeleton);
-
- const installOptions = { cwd, npm: options['use-npm'], yarn: options['use-yarn'] };
- const install = helpers.createInstaller(installOptions);
- const installDev = helpers.createInstaller({ ...installOptions, dev: true });
-
- async function bail() {
- if (options['fail-cleanup']) {
- try {
- console.log();
- console.error('Deleting created directory.');
- await rm(cwd, { recursive: true });
- } catch {}
- }
+ const cwd = resolveUser(dir);
- process.exit(1);
- }
-
- /**
- * Project creation
- */
-
- clear();
-
- try {
- await mkdir(cwd, { recursive: true });
- } catch (error) {
- SPINNER.fail();
- console.error(error);
-
- await bail();
- }
+ log(`Creating a new Pota App ${cyan(pkgName)} in ${green(cwd)}.`);
- SPINNER.succeed();
- // change directory into current working directory (the project directory)
- process.chdir(cwd);
+ // resolve shorthand to full package name
+ if (isSkeletonShorthand(skeleton)) skeleton = getSkeletonFromShorthand(skeleton);
- SPINNER.start('Initializing git');
+ // parse the skeleton package
+ let skeletonPkgDetails: npa.Result;
try {
- await helpers.command(`git init`);
+ skeletonPkgDetails = npa(skeleton);
} catch (error) {
- SPINNER.fail();
- console.error(error);
+ console.error(`${green(skeleton)} is not a valid skeleton package`);
- await bail();
+ process.exit(1);
}
- SPINNER.succeed();
+ // if the skeleton package is a file path, then resolve it to a relative path
+ if (skeletonPkgDetails.type === 'file') skeleton = relative(cwd, skeleton);
- const visualName = isFileSkeleton ? basename(skeleton) : skeleton;
-
- SPINNER.start(`Installing ${cyan(visualName)}, this might take a while...`);
+ const bail = helpers.createBailer(options['fail-cleanup'] ? cwd : undefined);
try {
- await installDev(skeleton);
+ // create project directory
+ await helpers.createDir(cwd);
+
+ // change directory into current working directory (the project directory)
+ process.chdir(cwd);
+
+ const visualName = skeletonPkgDetails.type === 'file' ? basename(skeleton) : skeleton;
+
+ log(`Installing ${cyan(visualName)}, this might take a while...`);
+ log();
+
+ await helpers.spawn(
+ 'npm',
+ 'install',
+ skeleton,
+ `--prefix ${cwd}`,
+ '--include=dev',
+ '--no-audit',
+ '--no-fund',
+ );
+
+ log();
+ console.log(`Setting up project structure...`);
+
+ await sync(cwd, await helpers.getSkeletonName(skeletonPkgDetails, cwd), pkgName);
+
+ console.log(`Installing remaining peer dependencies...`);
+
+ await helpers.spawn(
+ 'npm',
+ 'install',
+ `--prefix ${cwd}`,
+ '--no-audit',
+ '--no-fund',
+ '--prefer-offline',
+ );
+
+ // create branch under the name `main` and create initial commit
+ try {
+ await helpers.command('git init -b main');
+ await helpers.command('git add .');
+ await helpers.command('git commit -m "Initial commit from @pota/create"');
+ } catch (error) {
+ // if `-b main` isn't supported fallback to renaming the branch
+ if ((error as { code: number }).code === 129) {
+ await helpers.command('git init');
+ await helpers.command('git add .');
+ await helpers.command('git commit -m "Initial commit from @pota/create"');
+ await helpers.command('git branch master -m main');
+ } else throw error;
+ }
} catch (error) {
- SPINNER.fail();
console.error(error);
await bail();
}
- try {
- skeleton = await helpers.getSkeletonName(skeleton, cwd);
- } catch (error) {
- SPINNER.fail(`An Error occured reading the project ${green('package.json')}`);
- console.error(error);
+ log();
+ log(dedent`
+ Initialized a git repository.
- await bail();
- }
+ 🚀🚀🚀 ${green('SUCCESS')} 🚀🚀🚀
+ Created ${cyan(pkgName)} at ${green(cwd)}
- SPINNER.succeed();
+ Inside that directory, you can run several commands:
- SPINNER.start(`Syncing...`);
+ ${cyan(`npm run dev`)}
+ Starts the development server.
- try {
- await sync(cwd, skeleton, pkgName);
- } catch (error) {
- SPINNER.fail();
- console.error(error);
-
- await bail();
- }
-
- SPINNER.succeed();
-
- let failed = false;
+ ${cyan(`npm run build`)}
+ Builds the app for production.
- SPINNER.start(`Syncing peer dependencies...`);
+ We suggest that you begin by typing:
- try {
- await install();
- SPINNER.succeed();
- } catch (error) {
- SPINNER.fail();
- console.error(error);
-
- failed = true;
- }
+ ${cyan('cd')} my-app
+ ${cyan(`npm run dev`)}
- SPINNER.stop();
- if (failed) log(red(`🥊 Done, but with errors 😥`));
- else log(`🥊 Done`);
+ `);
process.exit(0);
})
diff --git a/core/create/src/spinner.ts b/core/create/src/spinner.ts
deleted file mode 100644
index 6548759f..00000000
--- a/core/create/src/spinner.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-import ora from 'ora';
-
-export const SPINNER = ora();
diff --git a/core/create/src/sync.ts b/core/create/src/sync.ts
index 06134d55..6546a271 100644
--- a/core/create/src/sync.ts
+++ b/core/create/src/sync.ts
@@ -1,11 +1,85 @@
-import { join, dirname } from 'path';
+import { join, dirname, basename, extname } from 'path';
+import { copyFile, mkdir } from 'fs/promises';
-import { PACKAGE_JSON_FILE, POTA_COMMANDS_DIR, POTA_DIR } from '@pota/shared/config';
+// @ts-ignore TypeScript is being weird
+import merge from 'lodash.merge';
+import { sortPackageJson } from 'sort-package-json';
+import type { PackageJsonShape } from '@pota/shared/config';
+import { PotaConfig, PACKAGE_JSON_FILE, POTA_COMMANDS_DIR, POTA_DIR } from '@pota/shared/config';
import { getNestedSkeletons } from '@pota/shared/skeleton';
+import { exists, readPackageJson, writePackageJson } from '@pota/shared/fs';
-import ProjectPackage from './ProjectPackage.js';
-import { copyFile, mkdir } from 'fs/promises';
-import { exists } from '@pota/shared/fs';
+import { POTA_CLI, POTA_CLI_BIN } from './config.js';
+
+function addFileAsScript(pkg: PackageJsonShape, file: string) {
+ const command = basename(file, extname(file));
+
+ pkg.scripts ??= {};
+ pkg.scripts[command] = `${POTA_CLI_BIN} ${command}`;
+}
+
+const IGNORED_PACKAGE_FIELDS: ReadonlyArray = [
+ 'name',
+ 'version',
+ 'peerDependencies',
+ 'dependencies',
+ 'devDependencies',
+ 'files',
+ 'publishConfig',
+ 'repository',
+ 'bugs',
+ 'author',
+];
+
+function filterObject>(o: T, fields: ReadonlyArray) {
+ type Result = Record;
+
+ let r: Result | undefined = undefined;
+
+ for (const field of Object.keys(o) as ReadonlyArray) {
+ if (fields.includes(field)) {
+ r ??= {} as Result;
+ r[field] = o[field];
+ }
+ }
+
+ return r;
+}
+
+/*
+ * Merges the fields of the package from `path` into the project package.
+ */
+async function mergeSkeleton(pkg: PackageJsonShape, path: string, config: PotaConfig) {
+ const skeletonPkg = await readPackageJson(path);
+
+ const { name: skeleton, version, peerDependencies = {} } = skeletonPkg;
+
+ for (const field of IGNORED_PACKAGE_FIELDS) {
+ delete skeletonPkg[field];
+ }
+
+ if ('scripts' in skeletonPkg && 'scripts' in config) {
+ skeletonPkg.scripts = filterObject(skeletonPkg.scripts!, config.scripts!);
+ }
+
+ merge(pkg, skeletonPkg);
+
+ // place the skeleton package in `devDependencies`
+ pkg.devDependencies ??= {};
+ // TODO: this semver chevron ain't great, chief
+ pkg.devDependencies[skeleton!] = `^${version!}`;
+
+ for (const [dep, version] of Object.entries(peerDependencies)) {
+ // place the `@pota/cli` (if it exists) package in `devDependencies`
+ if (dep === POTA_CLI) pkg.devDependencies[POTA_CLI] = peerDependencies[POTA_CLI];
+ // this `else if` condition makes sure that the dependency isn't defined in `devDependencies`
+ // which might me the case for extended skeletons defined in `peerDependencies`
+ else if (!(dep in pkg.devDependencies)) {
+ pkg.dependencies ??= {};
+ pkg.dependencies[dep] = version;
+ }
+ }
+}
async function copy(src: string, dst: string) {
// create destination directories if they don't exist
@@ -18,9 +92,9 @@ async function copy(src: string, dst: string) {
export default async function sync(targetPath: string, skeleton: string, pkgName: string) {
const nestedSkeletons = await getNestedSkeletons(targetPath, skeleton);
- const projectPkg = (await new ProjectPackage(targetPath).load())
- .setName(pkgName)
- .setDefaultSkeleton(skeleton);
+ const pkg = await readPackageJson(targetPath);
+ pkg.name = pkgName;
+ pkg.pota = skeleton;
const commandsDir = join(POTA_DIR, POTA_COMMANDS_DIR);
@@ -30,10 +104,9 @@ export default async function sync(targetPath: string, skeleton: string, pkgName
for (const { path, config, files } of nestedSkeletons) {
if (config.omit) omits.push(...config.omit);
for (const file of files) {
- if (file === PACKAGE_JSON_FILE) {
- await projectPkg.merge(path, config);
- } else if (file.startsWith(POTA_DIR)) {
- if (file.startsWith(commandsDir)) projectPkg.applyFileAsScript(file);
+ if (file === PACKAGE_JSON_FILE) await mergeSkeleton(pkg, path, config);
+ else if (file.startsWith(POTA_DIR)) {
+ if (file.startsWith(commandsDir)) addFileAsScript(pkg, file);
} else {
const renamed = config.rename?.[file] ?? file;
fileMap.set(renamed, { src: join(path, file), dst: join(targetPath, renamed) });
@@ -45,5 +118,5 @@ export default async function sync(targetPath: string, skeleton: string, pkgName
if (!omits.includes(file)) await copy(src, dst);
}
- await projectPkg.sort().write();
+ await writePackageJson(sortPackageJson(pkg), targetPath);
}
diff --git a/package-lock.json b/package-lock.json
index 13e9fb1c..b83f289c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -50,7 +50,6 @@
"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"
},
@@ -59,13 +58,16 @@
},
"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"
}
},
"core/create/node_modules/kleur": {
@@ -2254,6 +2256,12 @@
"@types/node": "*"
}
},
+ "node_modules/@types/dedent": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/@types/dedent/-/dedent-0.7.0.tgz",
+ "integrity": "sha512-EGlKlgMhnLt/cM4DbUSafFdrkeJoC9Mvnj0PUCU7tFmTjMjNRT957kXCx0wYm3JuEq4o4ZsS5vG+NlkM2DMd2A==",
+ "dev": true
+ },
"node_modules/@types/eslint": {
"version": "7.28.2",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.28.2.tgz",
@@ -13802,14 +13810,15 @@
"requires": {
"@pota/shared": "^1.4.0",
"@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",
"cross-spawn": "^7.0.3",
+ "dedent": "^0.7.0",
"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"
},
@@ -14288,6 +14297,12 @@
"@types/node": "*"
}
},
+ "@types/dedent": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/@types/dedent/-/dedent-0.7.0.tgz",
+ "integrity": "sha512-EGlKlgMhnLt/cM4DbUSafFdrkeJoC9Mvnj0PUCU7tFmTjMjNRT957kXCx0wYm3JuEq4o4ZsS5vG+NlkM2DMd2A==",
+ "dev": true
+ },
"@types/eslint": {
"version": "7.28.2",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.28.2.tgz",