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:
parent
07a64be4d3
commit
72ef7ae64a
12 changed files with 792 additions and 84 deletions
9
.changeset/brave-rings-jump.md
Normal file
9
.changeset/brave-rings-jump.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
'astro': minor
|
||||
---
|
||||
|
||||
Introduce new `astro add` command to automatically configure integrations.
|
||||
|
||||
```shell
|
||||
npx astro add
|
||||
```
|
7
examples/with-tailwindcss/tailwind.config.cjs
Normal file
7
examples/with-tailwindcss/tailwind.config.cjs
Normal file
|
@ -0,0 +1,7 @@
|
|||
module.exports = {
|
||||
content: [],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
17
packages/astro/src/core/add/babel.ts
Normal file
17
packages/astro/src/core/add/babel.ts
Normal 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'] });
|
26
packages/astro/src/core/add/consts.ts
Normal file
26
packages/astro/src/core/add/consts.ts
Normal 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`;
|
35
packages/astro/src/core/add/imports.ts
Normal file
35
packages/astro/src/core/add/imports.ts
Normal 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);
|
||||
},
|
||||
});
|
||||
}
|
459
packages/astro/src/core/add/index.ts
Normal file
459
packages/astro/src/core/add/index.ts
Normal 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);
|
||||
}
|
11
packages/astro/src/core/add/wrapper.ts
Normal file
11
packages/astro/src/core/add/wrapper.ts
Normal 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]);
|
||||
},
|
||||
});
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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'));
|
||||
}
|
||||
|
|
105
pnpm-lock.yaml
105
pnpm-lock.yaml
|
@ -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==}
|
||||
|
|
Loading…
Reference in a new issue