0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2024-12-30 22:03:56 -05:00

[ci] format

This commit is contained in:
Nate Moore 2023-11-27 23:02:46 +00:00 committed by astrobot-houston
parent 5a38750188
commit e7ce779ff8
10 changed files with 171 additions and 112 deletions

View file

@ -36,7 +36,7 @@ export async function getContext(argv: string[]): Promise<Context> {
'-h': '--help',
},
{ argv, permissive: true }
)
);
const packageManager = detectPackageManager()?.name ?? 'npm';
const { _: [version = 'latest'] = [], '--help': help = false, '--dry-run': dryRun } = flags;
@ -52,5 +52,5 @@ export async function getContext(argv: string[]): Promise<Context> {
exit(code) {
process.exit(code);
},
} satisfies Context
} satisfies Context;
}

View file

@ -8,7 +8,7 @@ export function help() {
tables: {
Flags: [
['--help (-h)', 'See all available flags.'],
['--dry-run', 'Walk through steps without executing.']
['--dry-run', 'Walk through steps without executing.'],
],
},
});

View file

@ -1,37 +1,56 @@
import type { Context, PackageInfo } from './context.js';
import { color, say } from '@astrojs/cli-kit';
import { random, sleep } from '@astrojs/cli-kit/utils';
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { color, say } from '@astrojs/cli-kit';
import { pluralize, celebrations, done, error, info, log, spinner, success, upgrade, banner, title, changelog, warn, bye, newline } from '../messages.js';
import {
banner,
bye,
celebrations,
changelog,
done,
error,
info,
newline,
pluralize,
spinner,
success,
title,
upgrade,
warn,
} from '../messages.js';
import { shell } from '../shell.js';
import { random, sleep } from '@astrojs/cli-kit/utils';
import { satisfies } from 'semver';
export async function install(
ctx: Pick<Context, 'version' | 'packages' | 'packageManager' | 'prompt' | 'dryRun' | 'exit' | 'cwd'>
ctx: Pick<
Context,
'version' | 'packages' | 'packageManager' | 'prompt' | 'dryRun' | 'exit' | 'cwd'
>
) {
await banner();
newline();
const { current, dependencies, devDependencies } = filterPackages(ctx);
const toInstall = [...dependencies, ...devDependencies].sort(sortPackages);
for (const packageInfo of current.sort(sortPackages)) {
const tag = /^\d/.test(packageInfo.targetVersion) ? packageInfo.targetVersion : packageInfo.targetVersion.slice(1)
await info(`${packageInfo.name}`, `is up to date on`, `v${tag}`)
const tag = /^\d/.test(packageInfo.targetVersion)
? packageInfo.targetVersion
: packageInfo.targetVersion.slice(1);
await info(`${packageInfo.name}`, `is up to date on`, `v${tag}`);
await sleep(random(50, 150));
}
if (toInstall.length === 0 && !ctx.dryRun) {
newline()
newline();
await success(random(celebrations), random(done));
return;
}
const majors: PackageInfo[] = []
const majors: PackageInfo[] = [];
for (const packageInfo of toInstall) {
const word = ctx.dryRun ? 'can' : 'will';
await upgrade(packageInfo, `${word} be updated to`)
await upgrade(packageInfo, `${word} be updated to`);
if (packageInfo.isMajor) {
majors.push(packageInfo)
majors.push(packageInfo);
}
}
if (majors.length > 0) {
@ -39,22 +58,25 @@ export async function install(
name: 'proceed',
type: 'confirm',
label: title('wait'),
message: `${pluralize(['One package has', 'Some packages have'], majors.length)} breaking changes. Continue?`,
message: `${pluralize(
['One package has', 'Some packages have'],
majors.length
)} breaking changes. Continue?`,
initial: true,
});
if (!proceed) {
return ctx.exit(0);
}
newline();
await warn('check', `Be sure to follow the ${pluralize('CHANGELOG', majors.length)}.`);
for (const pkg of majors.sort(sortPackages)) {
await changelog(pkg.name, pkg.changelogTitle!, pkg.changelogURL!);
}
}
newline()
newline();
if (ctx.dryRun) {
await info('--dry-run', `Skipping dependency installation`);
} else {
@ -76,13 +98,13 @@ function filterPackages(ctx: Pick<Context, 'packages'>) {
arr.push(packageInfo);
}
}
return { current, dependencies, devDependencies }
return { current, dependencies, devDependencies };
}
/**
* An `Array#sort` comparator function to normalize how packages are displayed.
* This only changes how the packages are displayed in the CLI, it is not persisted to `package.json`.
*/
* An `Array#sort` comparator function to normalize how packages are displayed.
* This only changes how the packages are displayed in the CLI, it is not persisted to `package.json`.
*/
function sortPackages(a: PackageInfo, b: PackageInfo): number {
if (a.isMajor && !b.isMajor) return 1;
if (b.isMajor && !a.isMajor) return -1;
@ -93,7 +115,11 @@ function sortPackages(a: PackageInfo, b: PackageInfo): number {
return a.name.localeCompare(b.name);
}
async function runInstallCommand(ctx: Pick<Context, 'cwd' | 'packageManager' | 'exit'>, dependencies: PackageInfo[], devDependencies: PackageInfo[]) {
async function runInstallCommand(
ctx: Pick<Context, 'cwd' | 'packageManager' | 'exit'>,
dependencies: PackageInfo[],
devDependencies: PackageInfo[]
) {
const cwd = fileURLToPath(ctx.cwd);
if (ctx.packageManager === 'yarn') await ensureYarnLock({ cwd });
@ -103,17 +129,40 @@ async function runInstallCommand(ctx: Pick<Context, 'cwd' | 'packageManager' | '
while: async () => {
try {
if (dependencies.length > 0) {
await shell(ctx.packageManager, ['install', ...dependencies.map(({ name, targetVersion }) => `${name}@${(targetVersion).replace(/^\^/, '')}`)], { cwd, timeout: 90_000, stdio: 'ignore' })
await shell(
ctx.packageManager,
[
'install',
...dependencies.map(
({ name, targetVersion }) => `${name}@${targetVersion.replace(/^\^/, '')}`
),
],
{ cwd, timeout: 90_000, stdio: 'ignore' }
);
}
if (devDependencies.length > 0) {
await shell(ctx.packageManager, ['install', '--save-dev', ...devDependencies.map(({ name, targetVersion }) => `${name}@${(targetVersion).replace(/^\^/, '')}`)], { cwd, timeout: 90_000, stdio: 'ignore' })
await shell(
ctx.packageManager,
[
'install',
'--save-dev',
...devDependencies.map(
({ name, targetVersion }) => `${name}@${targetVersion.replace(/^\^/, '')}`
),
],
{ cwd, timeout: 90_000, stdio: 'ignore' }
);
}
} catch {
const packages = [...dependencies, ...devDependencies].map(({ name, targetVersion }) => `${name}@${targetVersion}`).join(' ')
const packages = [...dependencies, ...devDependencies]
.map(({ name, targetVersion }) => `${name}@${targetVersion}`)
.join(' ');
newline();
error(
'error',
`Dependencies failed to install, please run the following command manually:\n${color.bold(`${ctx.packageManager} install ${packages}`)}`
`Dependencies failed to install, please run the following command manually:\n${color.bold(
`${ctx.packageManager} install ${packages}`
)}`
);
return ctx.exit(1);
}

View file

@ -1,14 +1,13 @@
import type { Context, PackageInfo } from './context.js';
import { color } from '@astrojs/cli-kit';
import dns from 'node:dns/promises';
import { existsSync } from 'node:fs';
import { readFile } from 'node:fs/promises';
import { color } from '@astrojs/cli-kit';
import { bannerAbort, error, getRegistry, info, log, newline } from '../messages.js';
import semverDiff from 'semver/functions/diff.js';
import semverCoerce from 'semver/functions/coerce.js';
import semverDiff from 'semver/functions/diff.js';
import semverParse from 'semver/functions/parse.js';
import { bannerAbort, error, getRegistry, info, newline } from '../messages.js';
export async function verify(
ctx: Pick<Context, 'version' | 'packages' | 'cwd' | 'dryRun' | 'exit'>
@ -49,21 +48,21 @@ function safeJSONParse(value: string) {
try {
return JSON.parse(value);
} catch {}
return {}
return {};
}
async function verifyAstroProject(ctx: Pick<Context, 'cwd' | 'version' | 'packages'>) {
const packageJson = new URL('./package.json', ctx.cwd);
if (!existsSync(packageJson)) return false;
if (!existsSync(packageJson)) return false;
const contents = await readFile(packageJson, { encoding: 'utf-8' });
if (!contents.includes('astro')) return false;
const { dependencies = {}, devDependencies = {} } = safeJSONParse(contents)
const { dependencies = {}, devDependencies = {} } = safeJSONParse(contents);
if (dependencies['astro'] === undefined && devDependencies['astro'] === undefined) return false;
// Side-effect! Persist dependency info to the shared context
collectPackageInfo(ctx, dependencies, devDependencies);
return true;
}
@ -71,14 +70,18 @@ function isAstroPackage(name: string) {
return name === 'astro' || name.startsWith('@astrojs/');
}
function collectPackageInfo(ctx: Pick<Context, 'version' | 'packages'>, dependencies: Record<string, string>, devDependencies: Record<string, string>) {
function collectPackageInfo(
ctx: Pick<Context, 'version' | 'packages'>,
dependencies: Record<string, string>,
devDependencies: Record<string, string>
) {
for (const [name, currentVersion] of Object.entries(dependencies)) {
if (!isAstroPackage(name)) continue;
ctx.packages.push({
name,
currentVersion,
targetVersion: ctx.version,
})
});
}
for (const [name, currentVersion] of Object.entries(devDependencies)) {
if (!isAstroPackage(name)) continue;
@ -86,12 +89,15 @@ function collectPackageInfo(ctx: Pick<Context, 'version' | 'packages'>, dependen
name,
currentVersion,
targetVersion: ctx.version,
isDevDependency: true
})
isDevDependency: true,
});
}
}
async function verifyVersions(ctx: Pick<Context, 'version' | 'packages' | 'exit'>, registry: string) {
async function verifyVersions(
ctx: Pick<Context, 'version' | 'packages' | 'exit'>,
registry: string
) {
const tasks: Promise<void>[] = [];
for (const packageInfo of ctx.packages) {
tasks.push(resolveTargetVersion(packageInfo, registry));
@ -110,11 +116,13 @@ async function verifyVersions(ctx: Pick<Context, 'version' | 'packages' | 'exit'
}
async function resolveTargetVersion(packageInfo: PackageInfo, registry: string): Promise<void> {
const packageMetadata = await fetch(`${registry}/${packageInfo.name}`, { headers: { accept: 'application/vnd.npm.install-v1+json' }});
const packageMetadata = await fetch(`${registry}/${packageInfo.name}`, {
headers: { accept: 'application/vnd.npm.install-v1+json' },
});
if (packageMetadata.status >= 400) {
throw new Error(`Unable to resolve "${packageInfo.name}"`);
}
const { "dist-tags": distTags } = await packageMetadata.json();
const { 'dist-tags': distTags } = await packageMetadata.json();
let version = distTags[packageInfo.targetVersion];
if (version) {
packageInfo.tag = packageInfo.targetVersion;
@ -159,7 +167,16 @@ async function resolveTargetVersion(packageInfo: PackageInfo, registry: string):
}
}
function extractChangelogURLFromRepository(repository: Record<string, string>, version: string, branch = 'main') {
return repository.url.replace('git+', '').replace('.git', '') + `/blob/${branch}/` + repository.directory + '/CHANGELOG.md#' + version.replace(/\./g, '')
function extractChangelogURLFromRepository(
repository: Record<string, string>,
version: string,
branch = 'main'
) {
return (
repository.url.replace('git+', '').replace('.git', '') +
`/blob/${branch}/` +
repository.directory +
'/CHANGELOG.md#' +
version.replace(/\./g, '')
);
}

View file

@ -1,7 +1,7 @@
import { getContext } from './actions/context.js';
import { install } from './actions/install.js';
import { help } from './actions/help.js';
import { install } from './actions/install.js';
import { verify } from './actions/verify.js';
import { setStdout } from './messages.js';
@ -21,10 +21,7 @@ export async function main() {
return;
}
const steps = [
verify,
install,
];
const steps = [verify, install];
for (const step of steps) {
await step(ctx);
@ -32,9 +29,4 @@ export async function main() {
process.exit(0);
}
export {
install,
getContext,
setStdout,
verify,
};
export { getContext, install, setStdout, verify };

View file

@ -1,11 +1,11 @@
/* eslint no-console: 'off' */
import type { PackageInfo } from './actions/context.js';
import { color, label, spinner as load } from '@astrojs/cli-kit';
import { align } from '@astrojs/cli-kit/utils';
import detectPackageManager from 'which-pm-runs';
import { shell } from './shell.js';
import semverParse from 'semver/functions/parse.js';
import terminalLink from 'terminal-link';
import detectPackageManager from 'which-pm-runs';
import type { PackageInfo } from './actions/context.js';
import { shell } from './shell.js';
// Users might lack access to the global npm registry, this function
// checks the user's project type and will return the proper npm registry
@ -51,28 +51,28 @@ export const celebrations = [
'Nice.',
'Wonderful.',
'Lovely!',
'Lookin\' good.',
'Awesome.'
]
"Lookin' good.",
'Awesome.',
];
export const done = [
'You\'re on the latest and greatest.',
"You're on the latest and greatest.",
'Your integrations are up-to-date.',
'Everything is current.',
'Everything is up to date.',
'Integrations are all up to date.',
'Everything is on the latest and greatest.',
'Integrations are up to date.',
]
];
export const bye = [
'Thanks for using Astro!',
'Have fun building!',
'Take it easy, astronaut!',
'Can\'t wait to see what you build.',
"Can't wait to see what you build.",
'Good luck out there.',
'See you around, astronaut.',
]
];
export const log = (message: string) => stdout.write(message + '\n');
@ -80,7 +80,9 @@ export const newline = () => stdout.write('\n');
export const banner = async () =>
log(
`\n${label('astro', color.bgGreen, color.black)} ${color.bold('Integration upgrade in progress.')}`
`\n${label('astro', color.bgGreen, color.black)} ${color.bold(
'Integration upgrade in progress.'
)}`
);
export const bannerAbort = () =>
@ -88,7 +90,7 @@ export const bannerAbort = () =>
export const warn = async (prefix: string, text: string) => {
log(`${label(prefix, color.bgCyan, color.black)} ${text}`);
}
};
export const info = async (prefix: string, text: string, version = '') => {
const length = 11 + prefix.length + text.length + version?.length;
@ -97,12 +99,14 @@ export const info = async (prefix: string, text: string, version = '') => {
log(`${' '.repeat(5)} ${color.cyan(symbol)} ${prefix}`);
log(`${' '.repeat(9)}${color.dim(text)} ${color.reset(version)}`);
} else {
log(`${' '.repeat(5)} ${color.cyan(symbol)} ${prefix} ${color.dim(text)} ${color.reset(version)}`);
log(
`${' '.repeat(5)} ${color.cyan(symbol)} ${prefix} ${color.dim(text)} ${color.reset(version)}`
);
}
}
};
export const upgrade = async (packageInfo: PackageInfo, text: string) => {
const { name, isMajor = false, targetVersion } = packageInfo;
const bg = isMajor ? (v: string) => color.bgYellow(color.black(` ${v} `)) : color.green;
const style = isMajor ? color.yellow : color.green;
const symbol = isMajor ? '▲' : '●';
@ -116,17 +120,18 @@ export const upgrade = async (packageInfo: PackageInfo, text: string) => {
} else {
log(`${' '.repeat(5)} ${style(symbol)} ${name} ${color.dim(text)} ${bg(version)}`);
}
}
};
export const title = (text: string) => align(label(text, color.bgYellow, color.black), 'end', 7) + ' ';
export const title = (text: string) =>
align(label(text, color.bgYellow, color.black), 'end', 7) + ' ';
export const success = async (prefix: string, text: string) => {
const length = 10 + prefix.length + text.length;
if (length > stdout.columns) {
log(`${' '.repeat(5)} ${color.green("✔")} ${prefix}`);
log(`${' '.repeat(5)} ${color.green('✔')} ${prefix}`);
log(`${' '.repeat(9)}${color.dim(text)}`);
} else {
log(`${' '.repeat(5)} ${color.green("✔")} ${prefix} ${color.dim(text)}`);
log(`${' '.repeat(5)} ${color.green('✔')} ${prefix} ${color.dim(text)}`);
}
};

View file

@ -41,7 +41,7 @@ export async function shell(
shell: true,
stdio: opts.stdio,
timeout: opts.timeout,
signal
signal,
});
const done = new Promise((resolve) => child.on('close', resolve));
[stdout, stderr] = await Promise.all([text(child.stdout), text(child.stderr)]);

View file

@ -9,7 +9,7 @@ describe('install', () => {
version: 'latest',
packageManager: 'npm',
dryRun: true,
}
};
it('up to date', async () => {
const context = {
@ -19,8 +19,8 @@ describe('install', () => {
name: 'astro',
currentVersion: '1.0.0',
targetVersion: '1.0.0',
}
]
},
],
};
await install(context);
expect(fixture.hasMessage('◼ astro is up to date on v1.0.0')).to.be.true;
@ -34,8 +34,8 @@ describe('install', () => {
name: 'astro',
currentVersion: '1.0.0',
targetVersion: '1.0.1',
}
]
},
],
};
await install(context);
expect(fixture.hasMessage('● astro can be updated to v1.0.1')).to.be.true;
@ -49,8 +49,8 @@ describe('install', () => {
name: 'astro',
currentVersion: '1.0.0',
targetVersion: '1.2.0',
}
]
},
],
};
await install(context);
expect(fixture.hasMessage('● astro can be updated to v1.2.0')).to.be.true;
@ -61,9 +61,9 @@ describe('install', () => {
let exitCode;
const context = {
...ctx,
prompt: () => {
prompt: () => {
prompted = true;
return { proceed: false }
return { proceed: false };
},
exit: (code) => {
exitCode = code;
@ -75,9 +75,9 @@ describe('install', () => {
targetVersion: '2.0.0',
isMajor: true,
changelogTitle: 'CHANGELOG',
changelogURL: 'https://example.com'
}
]
changelogURL: 'https://example.com',
},
],
};
await install(context);
expect(fixture.hasMessage('▲ astro can be updated to v2.0.0')).to.be.true;
@ -91,9 +91,9 @@ describe('install', () => {
let exitCode;
const context = {
...ctx,
prompt: () => {
prompt: () => {
prompted = true;
return { proceed: true }
return { proceed: true };
},
exit: (code) => {
exitCode = code;
@ -105,9 +105,9 @@ describe('install', () => {
targetVersion: '2.0.0',
isMajor: true,
changelogTitle: 'CHANGELOG',
changelogURL: 'https://example.com'
}
]
changelogURL: 'https://example.com',
},
],
};
await install(context);
expect(fixture.hasMessage('▲ astro can be updated to v2.0.0')).to.be.true;
@ -121,9 +121,9 @@ describe('install', () => {
let exitCode;
const context = {
...ctx,
prompt: () => {
prompt: () => {
prompted = true;
return { proceed: true }
return { proceed: true };
},
exit: (code) => {
exitCode = code;
@ -135,7 +135,7 @@ describe('install', () => {
targetVersion: '2.0.0',
isMajor: true,
changelogTitle: 'CHANGELOG',
changelogURL: 'https://example.com'
changelogURL: 'https://example.com',
},
{
name: 'b',
@ -143,9 +143,9 @@ describe('install', () => {
targetVersion: '7.0.0',
isMajor: true,
changelogTitle: 'CHANGELOG',
changelogURL: 'https://example.com'
}
]
changelogURL: 'https://example.com',
},
],
};
await install(context);
expect(fixture.hasMessage('▲ a can be updated to v2.0.0')).to.be.true;
@ -163,9 +163,9 @@ describe('install', () => {
let exitCode;
const context = {
...ctx,
prompt: () => {
prompt: () => {
prompted = true;
return { proceed: true }
return { proceed: true };
},
exit: (code) => {
exitCode = code;
@ -192,9 +192,9 @@ describe('install', () => {
targetVersion: '3.0.0',
isMajor: true,
changelogTitle: 'CHANGELOG',
changelogURL: 'https://example.com'
}
]
changelogURL: 'https://example.com',
},
],
};
await install(context);
expect(fixture.hasMessage('◼ current is up to date on v1.0.0')).to.be.true;

View file

@ -38,7 +38,7 @@ const resetBasicFixture = async () => {
);
const overriddenPackageJson = Object.assign(packageJsonData, {
dependencies: {
astro: '1.0.0'
astro: '1.0.0',
},
});
@ -49,5 +49,4 @@ const resetBasicFixture = async () => {
]);
};
export const resetFixtures = () =>
Promise.allSettled([resetBasicFixture()]);
export const resetFixtures = () => Promise.allSettled([resetBasicFixture()]);

View file

@ -1,9 +1,6 @@
{
"extends": "../../tsconfig.base.json",
"include": [
"src",
"index.d.ts"
],
"include": ["src", "index.d.ts"],
"compilerOptions": {
"allowJs": true,
"emitDeclarationOnly": false,