feat(cli): scaffold out astro add command (#2849)

* feat(cli): scaffold out `astro add` command

* added first babel transforms

* Format output

* Added changes confirmation

* Error flow

* Add dependencies

* feat(cli): astro add cleanup pass

* feat: add support for tailwind

* chore: update lockfile

* fix: types

* chore: rever @proload/core bump

* chore: add changeset

* chore: rollback dep update

* Added spinners

* chore: remove extra deps

* Removed extra argument

* Use `execa` instead of `exec`

* Changed how lines are trimmed within diffLines

* refactor: move add to core

* refactor: remove old add entrypoint

* refactor: simplify wording

* feat: improve diff

* feat: improve diff and logging, add interactive prompt when no args passed

* Formatted files

* Added --yes

* feat: improve logging for install command

* Fixed execa

* Added help message to add

* refactor: extract consts to own file

* feat: remove implicit projectRoot behavior

* feat: improve error handling, existing integrations

* fix(tailwind): ensure existing tailwind config is not overwritten

* refactor: prefer cwd to projectRoot flag

* chore: add refactor notes

* refactor: throw createPrettyError > implicit bail

* refactor: cleanup language

* feat(cli): prompt user before generating tailwind config

* fix(cli): update config generation to use cwd

* fix: resolve root from cwd

* chore: update changelog

Co-authored-by: JuanM04 <me@juanm04.com>
This commit is contained in:
Nate Moore 2022-03-25 16:26:55 -05:00 committed by GitHub
parent 07a64be4d3
commit 72ef7ae64a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 792 additions and 84 deletions

View file

@ -0,0 +1,9 @@
---
'astro': minor
---
Introduce new `astro add` command to automatically configure integrations.
```shell
npx astro add
```

View file

@ -0,0 +1,7 @@
module.exports = {
content: [],
theme: {
extend: {},
},
plugins: [],
}

View file

@ -79,13 +79,17 @@
"@astrojs/prism": "0.4.1-next.0",
"@astrojs/webapi": "^0.11.0",
"@babel/core": "^7.17.8",
"@babel/generator": "^7.17.7",
"@babel/parser": "^7.17.8",
"@babel/traverse": "^7.17.3",
"@proload/core": "^0.2.2",
"@proload/plugin-tsm": "^0.1.1",
"@web/parse5-utils": "^1.3.0",
"boxen": "^6.2.1",
"ci-info": "^3.3.0",
"common-ancestor-path": "^1.0.1",
"debug": "^4.3.4",
"diff": "^5.0.0",
"eol": "^0.9.1",
"es-module-lexer": "^0.10.4",
"esbuild": "0.14.25",
@ -99,11 +103,14 @@
"magic-string": "^0.25.9",
"micromorph": "^0.1.2",
"mime": "^3.0.0",
"ora": "^6.1.0",
"parse5": "^6.0.1",
"path-to-regexp": "^6.2.0",
"postcss": "^8.4.12",
"postcss-load-config": "^3.1.3",
"preferred-pm": "^3.0.3",
"prismjs": "^1.27.0",
"prompts": "^2.4.2",
"rehype-slug": "^5.0.1",
"resolve": "^1.22.0",
"rollup": "^2.70.1",
@ -126,16 +133,19 @@
"devDependencies": {
"@babel/types": "^7.17.0",
"@types/babel__core": "^7.1.19",
"@types/babel__generator": "^7.6.4",
"@types/babel__traverse": "^7.14.2",
"@types/chai": "^4.3.0",
"@types/common-ancestor-path": "^1.0.0",
"@types/connect": "^3.4.35",
"@types/debug": "^4.1.7",
"@types/diff": "^5.0.2",
"@types/estree": "^0.0.51",
"@types/html-escaper": "^3.0.0",
"@types/mime": "^2.0.3",
"@types/mocha": "^9.1.0",
"@types/parse5": "^6.0.3",
"@types/prettier": "^2.4.4",
"@types/resolve": "^1.20.1",
"@types/rimraf": "^3.0.2",
"@types/send": "^0.17.1",

View file

@ -8,23 +8,23 @@ import yargs from 'yargs-parser';
import { z } from 'zod';
import { defaultLogDestination } from '../core/logger.js';
import build from '../core/build/index.js';
import add from '../core/add/index.js';
import devServer from '../core/dev/index.js';
import preview from '../core/preview/index.js';
import { check } from './check.js';
import { formatConfigError, loadConfig } from '../core/config.js';
import { pad } from '../core/dev/util.js';
import { printHelp } from '../core/messages.js';
type Arguments = yargs.Arguments;
type CLICommand = 'help' | 'version' | 'dev' | 'build' | 'preview' | 'reload' | 'check';
type CLICommand = 'help' | 'version' | 'add' | 'dev' | 'build' | 'preview' | 'reload' | 'check';
/** Display --help flag */
function printHelp() {
linebreak();
headline('astro', 'Futuristic web development tool.');
linebreak();
title('Commands');
table(
[
function printAstroHelp() {
printHelp({
commandName: 'astro',
headline: 'Futuristic web development tool.',
commands: [
['add', 'Add an integration to your configuration.'],
['dev', 'Run Astro in development mode.'],
['build', 'Build a pre-compiled production-ready site.'],
['preview', 'Preview your build locally before deploying.'],
@ -32,12 +32,7 @@ function printHelp() {
['--version', 'Show the version number and exit.'],
['--help', 'Show this help message.'],
],
{ padding: 28, prefix: ' astro ' }
);
linebreak();
title('Flags');
table(
[
flags: [
['--host [optional IP]', 'Expose server on network'],
['--config <path>', 'Specify the path to the Astro config file.'],
['--project-root <path>', 'Specify the path to the project root folder.'],
@ -48,39 +43,7 @@ function printHelp() {
['--verbose', 'Enable verbose logging'],
['--silent', 'Disable logging'],
],
{ padding: 28, prefix: ' ' }
);
// Logging utils
function linebreak() {
console.log();
}
function headline(name: string, tagline: string) {
console.log(` ${colors.bgGreen(colors.black(` ${name} `))} ${colors.green(`v${process.env.PACKAGE_VERSION ?? ''}`)} ${tagline}`);
}
function title(label: string) {
console.log(` ${colors.bgWhite(colors.black(` ${label} `))}`);
}
function table(rows: [string, string][], opts: { padding: number; prefix: string }) {
const split = rows.some((row) => {
const message = `${opts.prefix}${' '.repeat(opts.padding)}${row[1]}`;
return message.length > process.stdout.columns;
});
for (const row of rows) {
row.forEach((col, i) => {
if (i === 0) {
process.stdout.write(`${opts.prefix}${colors.bold(pad(`${col}`, opts.padding - opts.prefix.length))}`);
} else {
if (split) {
process.stdout.write('\n ');
}
process.stdout.write(colors.dim(col) + '\n');
}
});
}
return '';
}
});
}
/** Display --version flag */
@ -93,15 +56,15 @@ async function printVersion() {
/** Determine which command the user requested */
function resolveCommand(flags: Arguments): CLICommand {
if (flags.version) {
return 'version';
} else if (flags.help) {
return 'help';
}
const cmd = flags._[2] as string;
if (cmd === 'add') return 'add';
if (flags.version) return 'version';
else if (flags.help) return 'help';
const supportedCommands = new Set(['dev', 'build', 'preview', 'check']);
if (supportedCommands.has(cmd)) {
return cmd as 'dev' | 'build' | 'preview' | 'check';
return cmd as CLICommand;
}
return 'help';
}
@ -110,11 +73,11 @@ function resolveCommand(flags: Arguments): CLICommand {
export async function cli(args: string[]) {
const flags = yargs(args);
const cmd = resolveCommand(flags);
const projectRoot = flags.projectRoot || flags._[3];
const projectRoot = flags.projectRoot;
switch (cmd) {
case 'help':
printHelp();
printAstroHelp();
return process.exit(0);
case 'version':
await printVersion();
@ -135,6 +98,8 @@ export async function cli(args: string[]) {
let config: AstroConfig;
try {
// Note: ideally, `loadConfig` would return the config AND its filePath
// For now, `add` has to resolve the config again internally
config = await loadConfig({ cwd: projectRoot, flags });
} catch (err) {
throwAndExit(err);
@ -142,6 +107,16 @@ export async function cli(args: string[]) {
}
switch (cmd) {
case 'add': {
try {
const packages = flags._.slice(3) as string[];
await add(packages, { cwd: projectRoot, flags, logging });
process.exit(0);
} catch (err) {
throwAndExit(err);
}
return;
}
case 'dev': {
try {
await devServer(config, { logging });
@ -150,7 +125,6 @@ export async function cli(args: string[]) {
} catch (err) {
throwAndExit(err);
}
return;
}

View file

@ -0,0 +1,17 @@
import traverse from '@babel/traverse';
import generator from '@babel/generator';
import * as t from '@babel/types';
import parser from '@babel/parser';
// @ts-ignore @babel/traverse isn't ESM and needs this trick
export const visit = traverse.default as typeof traverse;
export { t };
export async function generate(ast: t.File) {
// @ts-ignore @babel/generator isn't ESM and needs this trick
const astToText = generator.default as typeof generator;
const { code } = astToText(ast);
return code;
}
export const parse = (code: string) => parser.parse(code, { sourceType: 'unambiguous', plugins: ['typescript'] });

View file

@ -0,0 +1,26 @@
export const FIRST_PARTY_FRAMEWORKS = [
{ value: 'react', title: 'React' },
{ value: 'preact', title: 'Preact' },
{ value: 'vue', title: 'Vue' },
{ value: 'svelte', title: 'Svelte' },
{ value: 'solid-js', title: 'Solid' },
{ value: 'lit', title: 'Lit' },
];
export const FIRST_PARTY_ADDONS = [
{ value: 'tailwind', title: 'Tailwind' },
{ value: 'turbolinks', title: 'Turbolinks' },
{ value: 'partytown', title: 'Partytown' },
{ value: 'sitemap', title: 'Sitemap' },
];
export const ALIASES = new Map([
['solid', 'solid-js'],
['tailwindcss', 'tailwind'],
]);
export const CONFIG_STUB = `import { defineConfig } from 'astro/config';\n\nexport default defineConfig({});`;
export const TAILWIND_CONFIG_STUB = `module.exports = {
content: [],
theme: {
extend: {},
},
plugins: [],
}\n`;

View file

@ -0,0 +1,35 @@
import { t, visit } from './babel.js';
export function ensureImport(root: t.File, importDeclaration: t.ImportDeclaration) {
let specifiersToFind = [...importDeclaration.specifiers];
visit(root, {
ImportDeclaration(path) {
if (path.node.source.value === importDeclaration.source.value) {
path.node.specifiers.forEach((specifier) =>
specifiersToFind.forEach((specifierToFind, i) => {
if (specifier.type !== specifierToFind.type) return;
if (specifier.local.name === specifierToFind.local.name) {
specifiersToFind.splice(i, 1);
}
})
);
}
},
});
if (specifiersToFind.length === 0) return;
visit(root, {
Program(path) {
const declaration = t.importDeclaration(specifiersToFind, importDeclaration.source);
const latestImport = path
.get('body')
.filter((statement) => statement.isImportDeclaration())
.pop();
if (latestImport) latestImport.insertAfter(declaration);
else path.unshiftContainer('body', declaration);
},
});
}

View file

@ -0,0 +1,459 @@
import type yargs from 'yargs-parser';
import path from 'path';
import { existsSync, promises as fs } from 'fs';
import { execa } from 'execa';
import { fileURLToPath, pathToFileURL } from 'url';
import { diffWords } from 'diff';
import boxen from 'boxen';
import prompts from 'prompts';
import preferredPM from 'preferred-pm';
import ora from 'ora';
import { resolveConfigURL } from '../config.js';
import { apply as applyPolyfill } from '../polyfill.js';
import { error, info, debug, LogOptions } from '../logger.js';
import { printHelp } from '../messages.js';
import * as msg from '../messages.js';
import * as CONSTS from './consts.js';
import { dim, red, cyan, green, magenta, bold } from 'kleur/colors';
import { parseNpmName } from '../util.js';
import { wrapDefaultExport } from './wrapper.js';
import { ensureImport } from './imports.js';
import { t, parse, visit, generate } from './babel.js';
export interface AddOptions {
logging: LogOptions;
flags: yargs.Arguments;
cwd?: string;
}
export interface IntegrationInfo {
id: string;
packageName: string;
dependencies: [name: string, version: string][];
}
export default async function add(names: string[], { cwd, flags, logging }: AddOptions) {
if (flags.help) {
printHelp({
commandName: 'astro add',
usage: '[FLAGS] [INTEGRATIONS...]',
flags: [
['--yes', 'Add the integration without user interaction.'],
['--help', 'Show this help message.'],
],
});
return;
}
let configURL: URL | undefined;
const root = pathToFileURL(cwd ? path.resolve(cwd) : process.cwd());
// TODO: improve error handling for invalid configs
configURL = await resolveConfigURL({ cwd, flags });
if (configURL?.pathname.endsWith('package.json')) {
throw new Error(`Unable to use astro add with package.json#astro configuration! Try migrating to \`astro.config.mjs\` and try again.`);
}
applyPolyfill();
if (names.length === 0) {
const response = await prompts([
{
type: 'multiselect',
name: 'frameworks',
message: 'What frameworks would you like to enable?',
instructions: '\n Space to select. Return to submit',
choices: CONSTS.FIRST_PARTY_FRAMEWORKS,
},
{
type: 'multiselect',
name: 'addons',
message: 'What additional integrations would you like to enable?',
instructions: '\n Space to select. Return to submit',
choices: CONSTS.FIRST_PARTY_ADDONS,
},
]);
if (!response.frameworks && !response.addons) {
info(logging, null, msg.cancelled(`Integrations skipped.`, `You can always run ${cyan('astro add')} later!`));
return;
}
const selected = [response.frameworks ?? [], response.addons ?? []].flat(1);
if (selected.length === 0) {
error(logging, null, `\n${red('No integrations specified!')}\n${dim('Try running')} astro add again.`);
return;
}
names = selected;
}
// Some packages might have a common alias! We normalize those here.
names = names.map((name) => (CONSTS.ALIASES.has(name) ? CONSTS.ALIASES.get(name)! : name));
if (configURL) {
debug('add', `Found config at ${configURL}`);
} else {
info(logging, 'add', `Unable to locate a config file, generating one for you.`);
configURL = new URL('./astro.config.mjs', root);
await fs.writeFile(fileURLToPath(configURL), CONSTS.CONFIG_STUB, { encoding: 'utf-8' });
}
const integrations = await validateIntegrations(names);
let ast: t.File | null = null;
try {
ast = await parseAstroConfig(configURL);
debug('add', 'Parsed astro config');
const defineConfig = t.identifier('defineConfig');
ensureImport(ast, t.importDeclaration([t.importSpecifier(defineConfig, defineConfig)], t.stringLiteral('astro/config')));
wrapDefaultExport(ast, defineConfig);
debug('add', 'Astro config ensured `defineConfig`');
for (const integration of integrations) {
await addIntegration(ast, integration);
debug('add', `Astro config added integration ${integration.id}`);
}
} catch (err) {
debug('add', 'Error parsing/modifying astro config: ', err);
throw createPrettyError(err as Error);
}
let configResult: UpdateResult | undefined;
let installResult: UpdateResult | undefined;
if (ast) {
try {
configResult = await updateAstroConfig({ configURL, ast, flags, logging });
} catch (err) {
debug('add', 'Error updating astro config', err);
throw createPrettyError(err as Error);
}
}
switch (configResult) {
case UpdateResult.cancelled: {
info(logging, null, msg.cancelled(`Your configuration has ${bold('NOT')} been updated.`));
return;
}
case UpdateResult.none: {
const pkgURL = new URL('./package.json', configURL);
if (existsSync(fileURLToPath(pkgURL))) {
const { dependencies = {}, devDependencies = {} } = await fs.readFile(fileURLToPath(pkgURL)).then(res => JSON.parse(res.toString()));
const deps = Object.keys(Object.assign(dependencies, devDependencies));
const missingDeps = integrations.filter(integration => !deps.includes(integration.packageName));
if (missingDeps.length === 0) {
info(logging, null, msg.success(`Configuration up-to-date.`));
return;
}
}
info(logging, null, msg.success(`Configuration up-to-date.`));
break;
}
}
installResult = await tryToInstallIntegrations({ integrations, cwd, flags, logging });
switch (installResult) {
case UpdateResult.updated: {
const len = integrations.length;
if (integrations.find((integration) => integration.id === 'tailwind')) {
const possibleConfigFiles = ['./tailwind.config.cjs', './tailwind.config.mjs', './tailwind.config.js'].map(p => fileURLToPath(new URL(p, configURL)));
let alreadyConfigured = false;
for (const possibleConfigPath of possibleConfigFiles) {
if (existsSync(possibleConfigPath)) {
alreadyConfigured = true;
break;
}
}
if (!alreadyConfigured) {
info(logging, null, `\n ${magenta(`Astro will generate a minimal ${bold('./tailwind.config.cjs')} file.`)}\n`);
if (await askToContinue({ flags })) {
await fs.writeFile(fileURLToPath(new URL('./tailwind.config.cjs', configURL)), CONSTS.TAILWIND_CONFIG_STUB, { encoding: 'utf-8' });
debug('add', `Generated default ./tailwind.config.cjs file`);
}
} else {
debug('add', `Using existing Tailwind configuration`);
}
}
const list = integrations.map(integration => ` - ${integration.packageName}`).join('\n')
info(logging, null, msg.success(`Added the following integration${len === 1 ? '' : 's'} to your project:\n${list}`));
return;
}
case UpdateResult.cancelled: {
info(logging, null, msg.cancelled(`Dependencies ${bold('NOT')} installed.`, `Be sure to install them manually before continuing!`));
return;
}
case UpdateResult.failure: {
throw createPrettyError(new Error(`Unable to install dependencies`));
}
}
}
async function parseAstroConfig(configURL: URL): Promise<t.File> {
const source = await fs.readFile(fileURLToPath(configURL), { encoding: 'utf-8' });
const result = parse(source);
if (!result) throw new Error('Unknown error parsing astro config');
if (result.errors.length > 0) throw new Error('Error parsing astro config: ' + JSON.stringify(result.errors));
return result;
}
const toIdent = (name: string) => {
if (name.includes('-')) {
return name.split('-')[0];
}
return name;
};
function createPrettyError(err: Error) {
err.message = `Astro could not update your astro.config.js file safely.
Reason: ${err.message}
You will need to add these integration(s) manually.
Documentation: https://next--astro-docs-2.netlify.app/en/guides/integrations-guide/`
return err;
}
async function addIntegration(ast: t.File, integration: IntegrationInfo) {
const integrationId = t.identifier(toIdent(integration.id));
ensureImport(ast, t.importDeclaration([t.importDefaultSpecifier(integrationId)], t.stringLiteral(integration.packageName)));
visit(ast, {
// eslint-disable-next-line @typescript-eslint/no-shadow
ExportDefaultDeclaration(path) {
if (!t.isCallExpression(path.node.declaration)) return;
const configObject = path.node.declaration.arguments[0];
if (!t.isObjectExpression(configObject)) return;
let integrationsProp = configObject.properties.find((prop) => {
if (prop.type !== 'ObjectProperty') return false;
if (prop.key.type === 'Identifier') {
if (prop.key.name === 'integrations') return true;
}
if (prop.key.type === 'StringLiteral') {
if (prop.key.value === 'integrations') return true;
}
return false;
}) as t.ObjectProperty | undefined;
const integrationCall = t.callExpression(integrationId, []);
if (!integrationsProp) {
configObject.properties.push(t.objectProperty(t.identifier('integrations'), t.arrayExpression([integrationCall])));
return;
}
if (integrationsProp.value.type !== 'ArrayExpression') throw new Error('Unable to parse integrations');
const existingIntegrationCall = integrationsProp.value.elements.find(
(expr) => t.isCallExpression(expr) && t.isIdentifier(expr.callee) && expr.callee.name === integrationId.name
);
if (existingIntegrationCall) return;
integrationsProp.value.elements.push(integrationCall);
},
});
}
const enum UpdateResult {
none,
updated,
cancelled,
failure,
}
async function updateAstroConfig({ configURL, ast, flags, logging }: { configURL: URL; ast: t.File; flags: yargs.Arguments; logging: LogOptions }): Promise<UpdateResult> {
const input = await fs.readFile(fileURLToPath(configURL), { encoding: 'utf-8' });
let output = await generate(ast);
const comment = '// https://astro.build/config';
const defaultExport = 'export default defineConfig';
output = output.replace(` ${comment}`, '');
output = output.replace(`${defaultExport}`, `\n${comment}\n${defaultExport}`);
if (input === output) {
return UpdateResult.none;
}
let changes = [];
for (const change of diffWords(input, output)) {
let lines = change.value
.trim()
.split('\n')
.slice(0, change.count)
if (lines.length === 0) continue;
if (change.added) {
if (!change.value.trim()) continue;
changes.push(change.value);
}
}
if (changes.length === 0) {
return UpdateResult.none;
}
let diffed = output;
for (let newContent of changes) {
const coloredOutput = newContent
.split('\n')
.map((ln) => (ln ? green(ln) : ''))
.join('\n');
diffed = diffed.replace(newContent, coloredOutput);
}
const message = `\n${boxen(diffed, { margin: 0.5, padding: 0.5, borderStyle: 'round', title: configURL.pathname.split('/').pop() })}\n`;
info(logging, null, `\n ${magenta('Astro will make the following changes to your config file:')}\n${message}`);
if (await askToContinue({ flags })) {
await fs.writeFile(fileURLToPath(configURL), output, { encoding: 'utf-8' });
debug('add', `Updated astro config`);
return UpdateResult.updated;
} else {
return UpdateResult.cancelled;
}
}
interface InstallCommand {
pm: string;
command: string;
flags: string[];
dependencies: string[];
}
async function getInstallIntegrationsCommand({ integrations, cwd = process.cwd() }: { integrations: IntegrationInfo[]; cwd?: string }): Promise<InstallCommand | null> {
const pm = await preferredPM(cwd);
debug('add', `package manager: ${JSON.stringify(pm)}`);
if (!pm) return null;
let dependencies = integrations
.map<[string, string | null][]>((i) => [[i.packageName, null], ...i.dependencies])
.flat(1)
.filter((dep, i, arr) => arr.findIndex((d) => d[0] === dep[0]) === i)
.map(([name, version]) => (version === null ? name : `${name}@${version}`))
.sort();
switch (pm.name) {
case 'npm':
return { pm: 'npm', command: 'install', flags: ['--save-dev'], dependencies };
case 'yarn':
return { pm: 'yarn', command: 'add', flags: ['--dev'], dependencies };
case 'pnpm':
return { pm: 'pnpm', command: 'install', flags: ['--save-dev'], dependencies };
default:
return null;
}
}
async function tryToInstallIntegrations({
integrations,
cwd,
flags,
logging,
}: {
integrations: IntegrationInfo[];
cwd?: string;
flags: yargs.Arguments;
logging: LogOptions;
}): Promise<UpdateResult> {
const installCommand = await getInstallIntegrationsCommand({ integrations, cwd });
if (installCommand === null) {
info(logging, null);
return UpdateResult.none;
} else {
const coloredOutput = `${bold(installCommand.pm)} ${installCommand.command} ${installCommand.flags.join(' ')} ${cyan(installCommand.dependencies.join(' '))}`;
const message = `\n${boxen(coloredOutput, { margin: 0.5, padding: 0.5, borderStyle: 'round' })}\n`;
info(
logging,
null,
`\n ${magenta('Astro will run the following command:')}\n ${dim('If you skip this step, you can always run it yourself later')}\n${message}`
);
if (await askToContinue({ flags })) {
const spinner = ora('Installing dependencies...').start();
try {
await execa(installCommand.pm, [installCommand.command, ...installCommand.flags, ...installCommand.dependencies], { cwd });
spinner.succeed();
return UpdateResult.updated;
} catch (err) {
debug('add', 'Error installing dependencies', err);
spinner.fail();
return UpdateResult.failure;
}
} else {
return UpdateResult.cancelled;
}
}
}
export async function validateIntegrations(integrations: string[]): Promise<IntegrationInfo[]> {
const spinner = ora('Resolving integrations...').start();
const integrationEntries = await Promise.all(
integrations.map(async (integration): Promise<IntegrationInfo> => {
const parsed = parseIntegrationName(integration);
if (!parsed) {
spinner.fail();
throw new Error(`${integration} does not appear to be a valid package name!`);
}
let { scope = '', name, tag } = parsed;
// Allow third-party integrations starting with `astro-` namespace
if (!name.startsWith('astro-')) {
scope = `astrojs`;
}
const packageName = `${scope ? `@${scope}/` : ''}${name}`;
const result = await fetch(`https://registry.npmjs.org/${packageName}/${tag}`).then((res) => {
if (res.status === 404) {
spinner.fail();
throw new Error(`Unable to fetch ${packageName}. Does this package exist?`);
}
return res.json();
});
let dependencies: IntegrationInfo['dependencies'] = [[result['name'], `^${result['version']}`]];
if (result['peerDependencies']) {
for (const peer in result['peerDependencies']) {
dependencies.push([peer, result['peerDependencies'][peer]]);
}
}
return { id: integration, packageName, dependencies };
})
);
spinner.succeed();
return integrationEntries;
}
function parseIntegrationName(spec: string) {
const result = parseNpmName(spec);
if (!result) return;
let { scope, name } = result;
let tag = 'latest';
if (scope) {
name = name.replace(scope + '/', '');
}
if (name.includes('@')) {
const tagged = name.split('@');
name = tagged[0];
tag = tagged[1];
}
return { scope, name, tag };
}
async function askToContinue({ flags }: { flags: yargs.Arguments }): Promise<boolean> {
if (flags.yes) return true;
const response = await prompts({
type: 'confirm',
name: 'askToContinue',
message: 'Continue?',
initial: true,
});
return Boolean(response.askToContinue);
}

View file

@ -0,0 +1,11 @@
import { t, visit } from './babel.js';
export function wrapDefaultExport(ast: t.File, functionIdentifier: t.Identifier) {
visit(ast, {
ExportDefaultDeclaration(path) {
if (!t.isExpression(path.node.declaration)) return;
if (t.isCallExpression(path.node.declaration) && t.isIdentifier(path.node.declaration.callee) && path.node.declaration.callee.name === functionIdentifier.name) return;
path.node.declaration = t.callExpression(functionIdentifier, [path.node.declaration]);
},
});
}

View file

@ -11,7 +11,6 @@ import load from '@proload/core';
import loadTypeScript from '@proload/plugin-tsm';
import postcssrc from 'postcss-load-config';
import { arraify, isObject } from './util.js';
import ssgAdapter from '../adapter-ssg/index.js';
load.use([loadTypeScript]);
@ -266,6 +265,28 @@ interface LoadConfigOptions {
flags?: Flags;
}
/**
* Resolve the file URL of the user's `astro.config.js|cjs|mjs|ts` file
* Note: currently the same as loadConfig but only returns the `filePath`
* instead of the resolved config
*/
export async function resolveConfigURL(configOptions: LoadConfigOptions): Promise<URL | undefined> {
const root = configOptions.cwd ? path.resolve(configOptions.cwd) : process.cwd();
const flags = resolveFlags(configOptions.flags || {});
let userConfigPath: string | undefined;
if (flags?.config) {
userConfigPath = /^\.*\//.test(flags.config) ? flags.config : `./${flags.config}`;
userConfigPath = fileURLToPath(new URL(userConfigPath, `file://${root}/`));
}
// Automatically load config file using Proload
// If `userConfigPath` is `undefined`, Proload will search for `astro.config.[cm]?[jt]s`
const config = await load('astro', { mustExist: false, cwd: root, filePath: userConfigPath });
if (config) {
return pathToFileURL(config.filePath);
}
}
/** Attempt to load an `astro.config.mjs` file */
export async function loadConfig(configOptions: LoadConfigOptions): Promise<AstroConfig> {
const root = configOptions.cwd ? path.resolve(configOptions.cwd) : process.cwd();

View file

@ -3,7 +3,7 @@
*/
import stripAnsi from 'strip-ansi';
import { bold, dim, red, green, underline, yellow, bgYellow, cyan, bgGreen, black } from 'kleur/colors';
import { bold, dim, red, green, underline, yellow, bgYellow, cyan, bgGreen, black, bgRed, bgWhite } from 'kleur/colors';
import { pad, emoji, getLocalAddress, getNetworkLogging } from './dev/util.js';
import os from 'os';
import type { AddressInfo } from 'net';
@ -87,6 +87,36 @@ export function prerelease({ currentVersion }: { currentVersion: string }) {
return [headline, warning, ''].map((msg) => ` ${msg}`).join('\n');
}
export function success(message: string, tip?: string) {
const badge = bgGreen(black(` success `));
const headline = green(message);
const footer = tip ? `\n ▶ ${tip}` : undefined;
return ['', badge, headline, footer]
.filter((v) => v !== undefined)
.map((msg) => ` ${msg}`)
.join('\n');
}
export function failure(message: string, tip?: string) {
const badge = bgRed(black(` error `));
const headline = red(message);
const footer = tip ? `\n ▶ ${tip}` : undefined;
return ['', badge, headline, footer]
.filter((v) => v !== undefined)
.map((msg) => ` ${msg}`)
.join('\n');
}
export function cancelled(message: string, tip?: string) {
const badge = bgYellow(black(` cancelled `));
const headline = yellow(message);
const footer = tip ? `\n ▶ ${tip}` : undefined;
return ['', badge, headline, footer]
.filter((v) => v !== undefined)
.map((msg) => ` ${msg}`)
.join('\n');
}
/** Display port in use */
export function portInUse({ port }: { port: number }): string {
return `Port ${port} in use. Trying a new one…`;
@ -102,3 +132,57 @@ export function err(error: Error): string {
stack = stack.slice(split).replace(/^\n+/, '');
return `${message}\n${dim(stack)}`;
}
export function printHelp({
commandName,
headline,
usage,
commands,
flags,
}: {
commandName: string;
headline?: string;
usage?: string;
commands?: [command: string, help: string][];
flags?: [flag: string, help: string][];
}) {
const linebreak = () => '';
const title = (label: string) => ` ${bgWhite(black(` ${label} `))}`;
const table = (rows: [string, string][], opts: { padding: number; prefix: string }) => {
const split = rows.some((row) => {
const message = `${opts.prefix}${' '.repeat(opts.padding)}${row[1]}`;
return message.length > process.stdout.columns;
});
let raw = '';
for (const row of rows) {
raw += `${opts.prefix}${bold(pad(`${row[0]}`, opts.padding - opts.prefix.length))}`;
if (split) raw += '\n ';
raw += dim(row[1]) + '\n';
}
return raw.slice(0, -1); // remove latest \n
};
let message = [];
if (headline) {
message.push(linebreak(), ` ${bgGreen(black(` ${commandName} `))} ${green(`v${process.env.PACKAGE_VERSION ?? ''}`)} ${headline}`);
}
if (usage) {
message.push(linebreak(), ` ${green(commandName)} ${bold(usage)}`);
}
if (commands) {
message.push(linebreak(), title('Commands'), table(commands, { padding: 28, prefix: ' astro ' }));
}
if (flags) {
message.push(linebreak(), title('Flags'), table(flags, { padding: 28, prefix: ' ' }));
}
// eslint-disable-next-line no-console
console.log(message.join('\n'));
}

View file

@ -448,32 +448,39 @@ importers:
'@astrojs/prism': 0.4.1-next.0
'@astrojs/webapi': ^0.11.0
'@babel/core': ^7.17.8
'@babel/generator': ^7.17.7
'@babel/parser': ^7.17.8
'@babel/traverse': ^7.17.3
'@babel/types': ^7.17.0
'@proload/core': ^0.2.2
'@proload/plugin-tsm': ^0.1.1
'@types/babel__core': ^7.1.19
'@types/babel__generator': ^7.6.4
'@types/babel__traverse': ^7.14.2
'@types/chai': ^4.3.0
'@types/common-ancestor-path': ^1.0.0
'@types/connect': ^3.4.35
'@types/debug': ^4.1.7
'@types/diff': ^5.0.2
'@types/estree': ^0.0.51
'@types/html-escaper': ^3.0.0
'@types/mime': ^2.0.3
'@types/mocha': ^9.1.0
'@types/parse5': ^6.0.3
'@types/prettier': ^2.4.4
'@types/resolve': ^1.20.1
'@types/rimraf': ^3.0.2
'@types/send': ^0.17.1
'@types/yargs-parser': ^21.0.0
'@web/parse5-utils': ^1.3.0
astro-scripts: workspace:*
boxen: ^6.2.1
chai: ^4.3.6
cheerio: ^1.0.0-rc.10
ci-info: ^3.3.0
common-ancestor-path: ^1.0.1
debug: ^4.3.4
diff: ^5.0.0
eol: ^0.9.1
es-module-lexer: ^0.10.4
esbuild: 0.14.25
@ -489,11 +496,14 @@ importers:
micromorph: ^0.1.2
mime: ^3.0.0
mocha: ^9.2.2
ora: ^6.1.0
parse5: ^6.0.1
path-to-regexp: ^6.2.0
postcss: ^8.4.12
postcss-load-config: ^3.1.3
preferred-pm: ^3.0.3
prismjs: ^1.27.0
prompts: ^2.4.2
rehype-slug: ^5.0.1
resolve: ^1.22.0
rollup: ^2.70.1
@ -520,13 +530,17 @@ importers:
'@astrojs/prism': link:../astro-prism
'@astrojs/webapi': link:../webapi
'@babel/core': 7.17.8
'@babel/generator': 7.17.7
'@babel/parser': 7.17.8
'@babel/traverse': 7.17.3
'@proload/core': 0.2.2
'@proload/plugin-tsm': 0.1.1_@proload+core@0.2.2
'@web/parse5-utils': 1.3.0
boxen: 6.2.1
ci-info: 3.3.0
common-ancestor-path: 1.0.1
debug: 4.3.4
diff: 5.0.0
eol: 0.9.1
es-module-lexer: 0.10.4
esbuild: 0.14.25
@ -540,11 +554,14 @@ importers:
magic-string: 0.25.9
micromorph: 0.1.2
mime: 3.0.0
ora: 6.1.0
parse5: 6.0.1
path-to-regexp: 6.2.0
postcss: 8.4.12
postcss-load-config: 3.1.3
preferred-pm: 3.0.3
prismjs: 1.27.0
prompts: 2.4.2
rehype-slug: 5.0.1
resolve: 1.22.0
rollup: 2.70.1
@ -566,16 +583,19 @@ importers:
devDependencies:
'@babel/types': 7.17.0
'@types/babel__core': 7.1.19
'@types/babel__generator': 7.6.4
'@types/babel__traverse': 7.14.2
'@types/chai': 4.3.0
'@types/common-ancestor-path': 1.0.0
'@types/connect': 3.4.35
'@types/debug': 4.1.7
'@types/diff': 5.0.2
'@types/estree': 0.0.51
'@types/html-escaper': 3.0.0
'@types/mime': 2.0.3
'@types/mocha': 9.1.0
'@types/parse5': 6.0.3
'@types/prettier': 2.4.4
'@types/resolve': 1.20.1
'@types/rimraf': 3.0.2
'@types/send': 0.17.1
@ -3821,6 +3841,10 @@ packages:
resolution: {integrity: sha512-CL7y71j2zaDmtPLD5Xq5S1Gv2dFoHl0/GBZm6s39Mj/ls28L3NzAOqf7H4H0/2TNVMgMjMVf9CAFYSjmXhi3bw==}
dev: false
/@types/diff/5.0.2:
resolution: {integrity: sha512-uw8eYMIReOwstQ0QKF0sICefSy8cNO/v7gOTiIy9SbwuHyEecJUm7qlgueOO5S1udZ5I/irVydHVwMchgzbKTg==}
dev: true
/@types/estree-jsx/0.0.1:
resolution: {integrity: sha512-gcLAYiMfQklDCPjQegGn0TBAn9it05ISEsEhlKQUddIk7o2XDokOcTN7HBO8tznM0D9dGezvHEfRZBfZf6me0A==}
dependencies:
@ -3936,6 +3960,10 @@ packages:
/@types/parse5/6.0.3:
resolution: {integrity: sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==}
/@types/prettier/2.4.4:
resolution: {integrity: sha512-ReVR2rLTV1kvtlWFyuot+d1pkpG2Fw/XKE3PDAdj57rbM97ttSp9JZ2UsP+2EHTylra9cUf6JA7tGwW1INzUrA==}
dev: true
/@types/prismjs/1.26.0:
resolution: {integrity: sha512-ZTaqn/qSqUuAq1YwvOFQfVW1AR/oQJlLSZVustdjwI+GZ8kr0MSHBj0tsXPW1EqHubx50gtBEjbPGsdZwQwCjQ==}
dev: true
@ -4460,6 +4488,12 @@ packages:
'@algolia/transporter': 4.13.0
dev: false
/ansi-align/3.0.1:
resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==}
dependencies:
string-width: 4.2.3
dev: false
/ansi-colors/4.1.1:
resolution: {integrity: sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==}
engines: {node: '>=6'}
@ -4473,7 +4507,6 @@ packages:
/ansi-regex/5.0.1:
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
engines: {node: '>=8'}
dev: true
/ansi-regex/6.0.1:
resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==}
@ -4492,6 +4525,11 @@ packages:
dependencies:
color-convert: 2.0.1
/ansi-styles/6.1.0:
resolution: {integrity: sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==}
engines: {node: '>=12'}
dev: false
/anymatch/3.1.2:
resolution: {integrity: sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==}
engines: {node: '>= 8'}
@ -4706,6 +4744,20 @@ packages:
/boolbase/1.0.0:
resolution: {integrity: sha1-aN/1++YMUes3cl6p4+0xDcwed24=}
/boxen/6.2.1:
resolution: {integrity: sha512-H4PEsJXfFI/Pt8sjDWbHlQPx4zL/bvSQjcilJmaulGt5mLDorHOHpmdXAJcBcmru7PhYSp/cDMWRko4ZUMFkSw==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
dependencies:
ansi-align: 3.0.1
camelcase: 6.3.0
chalk: 4.1.2
cli-boxes: 3.0.0
string-width: 5.1.2
type-fest: 2.12.1
widest-line: 4.0.1
wrap-ansi: 8.0.1
dev: false
/brace-expansion/1.1.11:
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
dependencies:
@ -4807,7 +4859,6 @@ packages:
/camelcase/6.3.0:
resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==}
engines: {node: '>=10'}
dev: true
/caniuse-lite/1.0.30001320:
resolution: {integrity: sha512-MWPzG54AGdo3nWx7zHZTefseM5Y1ccM7hlQKHRqJkPozUaw3hNbBTMmLn16GG2FUzjR13Cr3NPfhIieX5PzXDA==}
@ -4939,6 +4990,11 @@ packages:
engines: {node: '>=6'}
dev: true
/cli-boxes/3.0.0:
resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==}
engines: {node: '>=10'}
dev: false
/cli-cursor/4.0.0:
resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
@ -5423,7 +5479,6 @@ packages:
/emoji-regex/8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
dev: true
/emoji-regex/9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
@ -6244,7 +6299,6 @@ packages:
dependencies:
locate-path: 5.0.0
path-exists: 4.0.0
dev: true
/find-up/5.0.0:
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
@ -6252,14 +6306,12 @@ packages:
dependencies:
locate-path: 6.0.0
path-exists: 4.0.0
dev: true
/find-yarn-workspace-root2/1.2.16:
resolution: {integrity: sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA==}
dependencies:
micromatch: 4.0.5
pkg-dir: 4.2.0
dev: true
/flat-cache/3.0.4:
resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==}
@ -6938,7 +6990,6 @@ packages:
/is-fullwidth-code-point/3.0.0:
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
engines: {node: '>=8'}
dev: true
/is-generator-function/1.0.10:
resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==}
@ -7294,7 +7345,6 @@ packages:
js-yaml: 3.14.1
pify: 4.0.1
strip-bom: 3.0.0
dev: true
/local-pkg/0.4.1:
resolution: {integrity: sha512-lL87ytIGP2FU5PWwNDo0w3WhIo2gopIAxPg9RxDYF7m4rr5ahuZxP22xnJHIvaLTe4Z9P6uKKY2UHiwyB4pcrw==}
@ -7306,14 +7356,12 @@ packages:
engines: {node: '>=8'}
dependencies:
p-locate: 4.1.0
dev: true
/locate-path/6.0.0:
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
engines: {node: '>=10'}
dependencies:
p-locate: 5.0.0
dev: true
/lodash.debounce/4.0.8:
resolution: {integrity: sha1-gteb/zCmfEAF/9XiUVMArZyk168=}
@ -8260,28 +8308,24 @@ packages:
engines: {node: '>=6'}
dependencies:
p-try: 2.2.0
dev: true
/p-limit/3.1.0:
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
engines: {node: '>=10'}
dependencies:
yocto-queue: 0.1.0
dev: true
/p-locate/4.1.0:
resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
engines: {node: '>=8'}
dependencies:
p-limit: 2.3.0
dev: true
/p-locate/5.0.0:
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
engines: {node: '>=10'}
dependencies:
p-limit: 3.1.0
dev: true
/p-map/2.1.0:
resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==}
@ -8298,7 +8342,6 @@ packages:
/p-try/2.2.0:
resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
engines: {node: '>=6'}
dev: true
/pac-proxy-agent/5.0.0:
resolution: {integrity: sha512-CcFG3ZtnxO8McDigozwE3AqAw15zDvGH+OjXO4kzf7IkEKkQ4gxQ+3sdF50WmhQ4P/bVusXcqNE2S3XrNURwzQ==}
@ -8378,7 +8421,6 @@ packages:
/path-exists/4.0.0:
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
engines: {node: '>=8'}
dev: true
/path-is-absolute/1.0.1:
resolution: {integrity: sha1-F0uSaHNVNP+8es5r9TpanhtcX18=}
@ -8423,14 +8465,12 @@ packages:
/pify/4.0.1:
resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==}
engines: {node: '>=6'}
dev: true
/pkg-dir/4.2.0:
resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==}
engines: {node: '>=8'}
dependencies:
find-up: 4.1.0
dev: true
/postcss-js/4.0.0_postcss@8.4.12:
resolution: {integrity: sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==}
@ -8520,7 +8560,6 @@ packages:
find-yarn-workspace-root2: 1.2.16
path-exists: 4.0.0
which-pm: 2.0.0
dev: true
/prelude-ls/1.1.2:
resolution: {integrity: sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=}
@ -9391,7 +9430,6 @@ packages:
emoji-regex: 8.0.0
is-fullwidth-code-point: 3.0.0
strip-ansi: 6.0.1
dev: true
/string-width/5.1.2:
resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
@ -9470,7 +9508,6 @@ packages:
engines: {node: '>=8'}
dependencies:
ansi-regex: 5.0.1
dev: true
/strip-ansi/7.0.1:
resolution: {integrity: sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==}
@ -9487,7 +9524,6 @@ packages:
/strip-bom/3.0.0:
resolution: {integrity: sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=}
engines: {node: '>=4'}
dev: true
/strip-bom/4.0.0:
resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==}
@ -9871,7 +9907,7 @@ packages:
engines: {node: '>=12'}
hasBin: true
dependencies:
esbuild: 0.14.25
esbuild: 0.14.27
dev: false
/tsutils/3.21.0_typescript@4.6.3:
@ -10065,6 +10101,11 @@ packages:
engines: {node: '>=8'}
dev: true
/type-fest/2.12.1:
resolution: {integrity: sha512-AiknQSEqKVGDDjtZqeKrUoTlcj7FKhupmnVUgz6KoOKtvMwRGE6hUNJ/nVear+h7fnUPO1q/htSkYKb1pyntkQ==}
engines: {node: '>=12.20'}
dev: false
/typescript/4.6.3:
resolution: {integrity: sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==}
engines: {node: '>=4.2.0'}
@ -10562,7 +10603,6 @@ packages:
dependencies:
load-yaml-file: 0.2.0
path-exists: 4.0.0
dev: true
/which-typed-array/1.1.7:
resolution: {integrity: sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw==}
@ -10597,6 +10637,13 @@ packages:
string-width: 1.0.2
dev: true
/widest-line/4.0.1:
resolution: {integrity: sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==}
engines: {node: '>=12'}
dependencies:
string-width: 5.1.2
dev: false
/word-wrap/1.2.3:
resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==}
engines: {node: '>=0.10.0'}
@ -10770,6 +10817,15 @@ packages:
strip-ansi: 6.0.1
dev: true
/wrap-ansi/8.0.1:
resolution: {integrity: sha512-QFF+ufAqhoYHvoHdajT/Po7KoXVBPXS2bgjIam5isfWJPfIOnQZ50JtUiVvCv/sjgacf3yRrt2ZKUZ/V4itN4g==}
engines: {node: '>=12'}
dependencies:
ansi-styles: 6.1.0
string-width: 5.1.2
strip-ansi: 7.0.1
dev: false
/wrappy/1.0.2:
resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=}
@ -10866,7 +10922,6 @@ packages:
/yocto-queue/0.1.0:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
engines: {node: '>=10'}
dev: true
/zod/3.14.2:
resolution: {integrity: sha512-iF+wrtzz7fQfkmn60PG6XFxaWBhYYKzp2i+nv24WbLUWb2JjymdkHlzBwP0erpc78WotwP5g9AAu7Sk8GWVVNw==}