From d3d99fba269da9e812e748539a11dfed785ef8a4 Mon Sep 17 00:00:00 2001 From: Julien Cayzac Date: Wed, 14 Aug 2024 19:04:36 +0900 Subject: [PATCH 1/4] fix: code component was missing support for meta string (#11605) * fix: code component was missing support for meta string Fixed #11604 * Create odd-buttons-pay.md * : add reference link for meta prop * Apply suggestions from code review Co-authored-by: Sarah Rainsberger * Update .changeset/odd-buttons-pay.md * Update .changeset/odd-buttons-pay.md Co-authored-by: Sarah Rainsberger --------- Co-authored-by: Erika <3019731+Princesseuh@users.noreply.github.com> Co-authored-by: Sarah Rainsberger Co-authored-by: Emanuele Stoppa --- .changeset/odd-buttons-pay.md | 22 ++++++++++++++++++++++ packages/astro/components/Code.astro | 9 +++++++++ 2 files changed, 31 insertions(+) create mode 100644 .changeset/odd-buttons-pay.md diff --git a/.changeset/odd-buttons-pay.md b/.changeset/odd-buttons-pay.md new file mode 100644 index 000000000000..728068ef2a5f --- /dev/null +++ b/.changeset/odd-buttons-pay.md @@ -0,0 +1,22 @@ +--- +"astro": minor +--- + +Adds a new property `meta` to Astro's [built-in `` component](https://docs.astro.build/en/reference/api-reference/#code-). + +This allows you to provide a value for [Shiki's `meta` attribute](https://shiki.style/guide/transformers#meta) to pass options to transformers. + +The following example passes an option to highlight lines 1 and 3 to Shiki's `tranformerMetaHighlight`: + +```astro +--- +// src/components/Card.astro +import { Code } from "astro:components"; +import { transformerMetaHighlight } from '@shikijs/transformers'; +--- + +``` diff --git a/packages/astro/components/Code.astro b/packages/astro/components/Code.astro index 0cc639d7d577..d1c019c60d1b 100644 --- a/packages/astro/components/Code.astro +++ b/packages/astro/components/Code.astro @@ -23,6 +23,13 @@ interface Props extends Omit, 'lang'> { * @default "plaintext" */ lang?: BuiltinLanguage | SpecialLanguage | LanguageRegistration; + /** + * A metastring to pass to the highlighter. + * Allows passing information to transformers: https://shiki.style/guide/transformers#meta + * + * @default undefined + */ + meta?: string; /** * The styling theme. * Supports all themes listed here: https://shiki.style/themes @@ -72,6 +79,7 @@ interface Props extends Omit, 'lang'> { const { code, lang = 'plaintext', + meta, theme = 'github-dark', themes = {}, defaultColor = 'light', @@ -110,6 +118,7 @@ const highlighter = await getCachedHighlighter({ const html = await highlighter.highlight(code, typeof lang === 'string' ? lang : lang.name, { inline, + meta, attributes: rest as any, }); --- From a23c69d0d0bed229bee52a32e61f135f9ebf9122 Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Wed, 14 Aug 2024 18:05:19 +0800 Subject: [PATCH 2/4] Deprecates exporting prerender with dynamic values (#11657) * wip * done i think * Add changeset * Use hook instead * Reorder hooks [skip ci] * Update .changeset/eleven-pens-glow.md Co-authored-by: Sarah Rainsberger * Fix run * Fix link * Add link Co-authored-by: Sarah Rainsberger * More accurate migration [skip ci] --------- Co-authored-by: Sarah Rainsberger Co-authored-by: Sarah Rainsberger --- .changeset/eleven-pens-glow.md | 41 ++++++++++++ packages/astro/src/@types/astro.ts | 19 ++++++ packages/astro/src/core/create-vite.ts | 2 +- packages/astro/src/core/util.ts | 2 +- packages/astro/src/integrations/hooks.ts | 42 +++++++++++++ packages/astro/src/vite-plugin-env/index.ts | 17 ++++- .../astro/src/vite-plugin-scanner/index.ts | 36 +++++++++-- .../integrations/node/test/prerender.test.js | 62 +++++++++++++++++++ 8 files changed, 212 insertions(+), 9 deletions(-) create mode 100644 .changeset/eleven-pens-glow.md diff --git a/.changeset/eleven-pens-glow.md b/.changeset/eleven-pens-glow.md new file mode 100644 index 000000000000..d031fba49c70 --- /dev/null +++ b/.changeset/eleven-pens-glow.md @@ -0,0 +1,41 @@ +--- +'astro': minor +--- + +Deprecates the option for route-generating files to export a dynamic value for `prerender`. Only static values are now supported (e.g. `export const prerender = true` or `= false`). This allows for better treeshaking and bundling configuration in the future. + +Adds a new [`"astro:route:setup"` hook](https://docs.astro.build/en/reference/integrations-reference/#astroroutesetup) to the Integrations API to allow you to dynamically set options for a route at build or request time through an integration, such as enabling [on-demand server rendering](https://docs.astro.build/en/guides/server-side-rendering/#opting-in-to-pre-rendering-in-server-mode). + +To migrate from a dynamic export to the new hook, update or remove any dynamic `prerender` exports from individual routing files: + +```diff +// src/pages/blog/[slug].astro +- export const prerender = import.meta.env.PRERENDER +``` + +Instead, create an integration with the `"astro:route:setup"` hook and update the route's `prerender` option: + +```js +// astro.config.mjs +import { defineConfig } from 'astro/config'; +import { loadEnv } from 'vite'; + +export default defineConfig({ + integrations: [setPrerender()], +}); + +function setPrerender() { + const { PRERENDER } = loadEnv(process.env.NODE_ENV, process.cwd(), ''); + + return { + name: 'set-prerender', + hooks: { + 'astro:route:setup': ({ route }) => { + if (route.component.endsWith('/blog/[slug].astro')) { + route.prerender = PRERENDER; + } + }, + }, + }; +} +``` diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index e9c42954a10d..645b07900dd3 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -2223,6 +2223,21 @@ export interface ResolvedInjectedRoute extends InjectedRoute { resolvedEntryPoint?: URL; } +export interface RouteOptions { + /** + * The path to this route relative to the project root. The slash is normalized as forward slash + * across all OS. + * @example "src/pages/blog/[...slug].astro" + */ + readonly component: string; + /** + * Whether this route should be prerendered. If the route has an explicit `prerender` export, + * the value will be passed here. Otherwise, it's undefined and will fallback to a prerender + * default depending on the `output` option. + */ + prerender?: boolean; +} + /** * Resolved Astro Config * Config with user settings along with all defaults filled in. @@ -3128,6 +3143,10 @@ declare global { logger: AstroIntegrationLogger; cacheManifest: boolean; }) => void | Promise; + 'astro:route:setup': (options: { + route: RouteOptions; + logger: AstroIntegrationLogger; + }) => void | Promise; } } } diff --git a/packages/astro/src/core/create-vite.ts b/packages/astro/src/core/create-vite.ts index 0570d9d5d584..23a4068a8bda 100644 --- a/packages/astro/src/core/create-vite.ts +++ b/packages/astro/src/core/create-vite.ts @@ -132,7 +132,7 @@ export async function createVite( // The server plugin is for dev only and having it run during the build causes // the build to run very slow as the filewatcher is triggered often. mode !== 'build' && vitePluginAstroServer({ settings, logger, fs }), - envVitePlugin({ settings }), + envVitePlugin({ settings, logger }), astroEnv({ settings, mode, fs, sync }), markdownVitePlugin({ settings, logger }), htmlVitePlugin(), diff --git a/packages/astro/src/core/util.ts b/packages/astro/src/core/util.ts index 2dd4ddd3b411..654d1982994c 100644 --- a/packages/astro/src/core/util.ts +++ b/packages/astro/src/core/util.ts @@ -153,7 +153,7 @@ export function isPage(file: URL, settings: AstroSettings): boolean { export function isEndpoint(file: URL, settings: AstroSettings): boolean { if (!isInPagesDir(file, settings.config)) return false; if (!isPublicRoute(file, settings.config)) return false; - return !endsWithPageExt(file, settings); + return !endsWithPageExt(file, settings) && !file.toString().includes('?astro'); } export function isServerLikeOutput(config: AstroConfig) { diff --git a/packages/astro/src/integrations/hooks.ts b/packages/astro/src/integrations/hooks.ts index 9b2859e48b3c..e3de85ddd5f6 100644 --- a/packages/astro/src/integrations/hooks.ts +++ b/packages/astro/src/integrations/hooks.ts @@ -13,6 +13,7 @@ import type { DataEntryType, HookParameters, RouteData, + RouteOptions, } from '../@types/astro.js'; import type { SerializedSSRManifest } from '../core/app/types.js'; import type { PageBuildData } from '../core/build/types.js'; @@ -558,6 +559,47 @@ export async function runHookBuildDone({ } } +export async function runHookRouteSetup({ + route, + settings, + logger, +}: { + route: RouteOptions; + settings: AstroSettings; + logger: Logger; +}) { + const prerenderChangeLogs: { integrationName: string; value: boolean | undefined }[] = []; + + for (const integration of settings.config.integrations) { + if (integration?.hooks?.['astro:route:setup']) { + const originalRoute = { ...route }; + const integrationLogger = getLogger(integration, logger); + + await withTakingALongTimeMsg({ + name: integration.name, + hookName: 'astro:route:setup', + hookResult: integration.hooks['astro:route:setup']({ + route, + logger: integrationLogger, + }), + logger, + }); + + if (route.prerender !== originalRoute.prerender) { + prerenderChangeLogs.push({ integrationName: integration.name, value: route.prerender }); + } + } + } + + if (prerenderChangeLogs.length > 1) { + logger.debug( + 'router', + `The ${route.component} route's prerender option has been changed multiple times by integrations:\n` + + prerenderChangeLogs.map((log) => `- ${log.integrationName}: ${log.value}`).join('\n'), + ); + } +} + export function isFunctionPerRouteEnabled(adapter: AstroAdapter | undefined): boolean { if (adapter?.adapterFeatures?.functionPerRoute === true) { return true; diff --git a/packages/astro/src/vite-plugin-env/index.ts b/packages/astro/src/vite-plugin-env/index.ts index ea75e752f581..350a29b7ffe1 100644 --- a/packages/astro/src/vite-plugin-env/index.ts +++ b/packages/astro/src/vite-plugin-env/index.ts @@ -1,12 +1,15 @@ import { fileURLToPath } from 'node:url'; import { transform } from 'esbuild'; +import { bold } from 'kleur/colors'; import MagicString from 'magic-string'; import type * as vite from 'vite'; import { loadEnv } from 'vite'; import type { AstroConfig, AstroSettings } from '../@types/astro.js'; +import type { Logger } from '../core/logger/core.js'; interface EnvPluginOptions { settings: AstroSettings; + logger: Logger; } // Match `import.meta.env` directly without trailing property access @@ -116,7 +119,7 @@ async function replaceDefine( }; } -export default function envVitePlugin({ settings }: EnvPluginOptions): vite.Plugin { +export default function envVitePlugin({ settings, logger }: EnvPluginOptions): vite.Plugin { let privateEnv: Record; let defaultDefines: Record; let isDev: boolean; @@ -170,13 +173,25 @@ export default function envVitePlugin({ settings }: EnvPluginOptions): vite.Plug s.prepend(devImportMetaEnvPrepend); // EDGE CASE: We need to do a static replacement for `export const prerender` for `vite-plugin-scanner` + // TODO: Remove in Astro 5 + let exportConstPrerenderStr: string | undefined; s.replace(exportConstPrerenderRe, (m, key) => { + exportConstPrerenderStr = m; if (privateEnv[key] != null) { return `export const prerender = ${privateEnv[key]}`; } else { return m; } }); + if (exportConstPrerenderStr) { + logger.warn( + 'router', + `Exporting dynamic values from prerender is deprecated. Please use an integration with the "astro:route:setup" hook ` + + `to update the route's \`prerender\` option instead. This allows for better treeshaking and bundling configuration ` + + `in the future. See https://docs.astro.build/en/reference/integrations-reference/#astroroutesetup for a migration example.` + + `\nFound \`${bold(exportConstPrerenderStr)}\` in ${bold(id)}.`, + ); + } return { code: s.toString(), diff --git a/packages/astro/src/vite-plugin-scanner/index.ts b/packages/astro/src/vite-plugin-scanner/index.ts index 05be99a07422..842857777a87 100644 --- a/packages/astro/src/vite-plugin-scanner/index.ts +++ b/packages/astro/src/vite-plugin-scanner/index.ts @@ -2,11 +2,13 @@ import { extname } from 'node:path'; import { bold } from 'kleur/colors'; import type { Plugin as VitePlugin } from 'vite'; import { normalizePath } from 'vite'; -import type { AstroSettings } from '../@types/astro.js'; +import type { AstroSettings, RouteOptions } from '../@types/astro.js'; import { type Logger } from '../core/logger/core.js'; import { isEndpoint, isPage, isServerLikeOutput } from '../core/util.js'; import { rootRelativePath } from '../core/viteUtils.js'; +import { runHookRouteSetup } from '../integrations/hooks.js'; import { getPrerenderDefault } from '../prerender/utils.js'; +import type { PageOptions } from '../vite-plugin-astro/types.js'; import { scan } from './scan.js'; export interface AstroPluginScannerOptions { @@ -39,12 +41,8 @@ export default function astroScannerPlugin({ const fileIsPage = isPage(fileURL, settings); const fileIsEndpoint = isEndpoint(fileURL, settings); if (!(fileIsPage || fileIsEndpoint)) return; - const defaultPrerender = getPrerenderDefault(settings.config); - const pageOptions = await scan(code, id, settings); + const pageOptions = await getPageOptions(code, id, fileURL, settings, logger); - if (typeof pageOptions.prerender === 'undefined') { - pageOptions.prerender = defaultPrerender; - } // `getStaticPaths` warning is just a string check, should be good enough for most cases if ( !pageOptions.prerender && @@ -76,3 +74,29 @@ export default function astroScannerPlugin({ }, }; } + +async function getPageOptions( + code: string, + id: string, + fileURL: URL, + settings: AstroSettings, + logger: Logger, +): Promise { + // Run initial scan + const pageOptions = await scan(code, id, settings); + + // Run integration hooks to alter page options + const route: RouteOptions = { + component: rootRelativePath(settings.config.root, fileURL, false), + prerender: pageOptions.prerender, + }; + await runHookRouteSetup({ route, settings, logger }); + pageOptions.prerender = route.prerender; + + // Fallback if unset + if (typeof pageOptions.prerender === 'undefined') { + pageOptions.prerender = getPrerenderDefault(settings.config); + } + + return pageOptions; +} diff --git a/packages/integrations/node/test/prerender.test.js b/packages/integrations/node/test/prerender.test.js index d856d9d3e752..e699a1b3c01b 100644 --- a/packages/integrations/node/test/prerender.test.js +++ b/packages/integrations/node/test/prerender.test.js @@ -57,6 +57,7 @@ describe('Prerendering', () => { assert.equal(res.status, 200); assert.equal($('h1').text(), 'Two'); + assert.ok(fixture.pathExists('/client/two/index.html')); }); it('Can render prerendered route with redirect and query params', async () => { @@ -131,6 +132,7 @@ describe('Prerendering', () => { assert.equal(res.status, 200); assert.equal($('h1').text(), 'Two'); + assert.ok(fixture.pathExists('/client/two/index.html')); }); it('Can render prerendered route with redirect and query params', async () => { @@ -152,6 +154,64 @@ describe('Prerendering', () => { }); }); + describe('Via integration', () => { + before(async () => { + process.env.PRERENDER = false; + fixture = await loadFixture({ + root: './fixtures/prerender/', + output: 'server', + outDir: './dist/via-integration', + build: { + client: './dist/via-integration/client', + server: './dist/via-integration/server', + }, + adapter: nodejs({ mode: 'standalone' }), + integrations: [ + { + name: 'test', + hooks: { + 'astro:route:setup': ({ route }) => { + if (route.component.endsWith('two.astro')) { + route.prerender = true; + } + }, + }, + }, + ], + }); + await fixture.build(); + const { startServer } = await fixture.loadAdapterEntryModule(); + let res = startServer(); + server = res.server; + await waitServerListen(server.server); + }); + + after(async () => { + await server.stop(); + await fixture.clean(); + delete process.env.PRERENDER; + }); + + it('Can render SSR route', async () => { + const res = await fetch(`http://${server.host}:${server.port}/one`); + const html = await res.text(); + const $ = cheerio.load(html); + + assert.equal(res.status, 200); + assert.equal($('h1').text(), 'One'); + }); + + it('Can render prerendered route', async () => { + const res = await fetch(`http://${server.host}:${server.port}/two`); + const html = await res.text(); + const $ = cheerio.load(html); + + assert.equal(res.status, 200); + assert.equal($('h1').text(), 'Two'); + assert.ok(fixture.pathExists('/client/two/index.html')); + }); + }); + describe('Dev', () => { let devServer; @@ -243,6 +303,7 @@ describe('Hybrid rendering', () => { assert.equal(res.status, 200); assert.equal($('h1').text(), 'One'); + assert.ok(fixture.pathExists('/client/one/index.html')); }); it('Can render prerendered route with redirect and query params', async () => { @@ -316,6 +377,7 @@ describe('Hybrid rendering', () => { assert.equal(res.status, 200); assert.equal($('h1').text(), 'One'); + assert.ok(fixture.pathExists('/client/one/index.html')); }); it('Can render prerendered route with redirect and query params', async () => { From 849e4c6c23e61f7fa59f583419048b998bef2475 Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Wed, 14 Aug 2024 18:05:50 +0800 Subject: [PATCH 3/4] Use node parseArgs instead of yargs-parser and arg (#11645) * wip * done * Add changeset * Format * Update * Fix houston * Fix test * Fix test --- .changeset/fifty-stingrays-flow.md | 6 ++ .changeset/rude-queens-shop.md | 7 ++ packages/astro/package.json | 2 - .../astro/performance/content-benchmark.mjs | 4 +- packages/astro/performance/package.json | 4 +- packages/astro/src/cli/add/index.ts | 19 ++-- packages/astro/src/cli/build/index.ts | 7 +- packages/astro/src/cli/check/index.ts | 10 +- packages/astro/src/cli/db/index.ts | 25 +++-- packages/astro/src/cli/dev/index.ts | 5 +- packages/astro/src/cli/docs/index.ts | 4 +- packages/astro/src/cli/flags.ts | 7 +- packages/astro/src/cli/index.ts | 39 +++++--- packages/astro/src/cli/info/index.ts | 5 +- packages/astro/src/cli/preferences/index.ts | 7 +- packages/astro/src/cli/preview/index.ts | 5 +- packages/astro/src/cli/sync/index.ts | 5 +- packages/astro/src/cli/telemetry/index.ts | 5 +- packages/astro/src/core/config/config.ts | 21 +---- packages/astro/src/core/config/index.ts | 1 - packages/create-astro/src/actions/context.ts | 91 +++++++++++-------- packages/db/package.json | 2 - .../db/src/core/cli/commands/execute/index.ts | 4 +- .../db/src/core/cli/commands/login/index.ts | 4 +- .../db/src/core/cli/commands/push/index.ts | 4 +- .../db/src/core/cli/commands/shell/index.ts | 4 +- .../db/src/core/cli/commands/verify/index.ts | 4 +- packages/db/src/core/cli/index.ts | 4 +- packages/db/src/core/cli/types.ts | 7 ++ packages/db/src/core/integration/index.ts | 6 +- packages/upgrade/src/actions/context.ts | 28 +++--- packages/upgrade/test/context.test.js | 4 +- pnpm-lock.yaml | 23 ----- scripts/cmd/copy.js | 17 ++-- scripts/cmd/test.js | 50 +++++----- 35 files changed, 224 insertions(+), 216 deletions(-) create mode 100644 .changeset/fifty-stingrays-flow.md create mode 100644 .changeset/rude-queens-shop.md create mode 100644 packages/db/src/core/cli/types.ts diff --git a/.changeset/fifty-stingrays-flow.md b/.changeset/fifty-stingrays-flow.md new file mode 100644 index 000000000000..3f4b96b04969 --- /dev/null +++ b/.changeset/fifty-stingrays-flow.md @@ -0,0 +1,6 @@ +--- +'astro': patch +'@astrojs/db': patch +--- + +Refactors internally to use `node:util` `parseArgs` instead of `yargs-parser` diff --git a/.changeset/rude-queens-shop.md b/.changeset/rude-queens-shop.md new file mode 100644 index 000000000000..6610b16a5320 --- /dev/null +++ b/.changeset/rude-queens-shop.md @@ -0,0 +1,7 @@ +--- +'create-astro': patch +'@astrojs/upgrade': patch +--- + +Refactors internally to use `node:util` `parseArgs` instead of `arg` + diff --git a/packages/astro/package.json b/packages/astro/package.json index b131a0734e51..c16f39d7e766 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -181,7 +181,6 @@ "vite": "^5.4.0", "vitefu": "^0.2.5", "which-pm": "^3.0.0", - "yargs-parser": "^21.1.1", "zod": "^3.23.8", "zod-to-json-schema": "^3.23.2" }, @@ -206,7 +205,6 @@ "@types/js-yaml": "^4.0.9", "@types/prompts": "^2.4.9", "@types/semver": "^7.5.8", - "@types/yargs-parser": "^21.0.3", "astro-scripts": "workspace:*", "cheerio": "1.0.0", "eol": "^0.9.1", diff --git a/packages/astro/performance/content-benchmark.mjs b/packages/astro/performance/content-benchmark.mjs index a710bd762500..98ef5f0eac32 100644 --- a/packages/astro/performance/content-benchmark.mjs +++ b/packages/astro/performance/content-benchmark.mjs @@ -1,8 +1,8 @@ /* eslint-disable no-console */ import { fileURLToPath } from 'node:url'; +import { parseArgs } from 'node:util'; import { bold, cyan, dim } from 'kleur/colors'; -import yargs from 'yargs-parser'; import { loadFixture } from '../test/test-utils.js'; import { generatePosts } from './scripts/generate-posts.mjs'; @@ -40,7 +40,7 @@ async function benchmark({ fixtures, templates, numPosts }) { // Test the build performance for content collections across multiple file types (md, mdx, mdoc) (async function benchmarkAll() { try { - const flags = yargs(process.argv.slice(2)); + const { values: flags } = parseArgs({ strict: false }); const test = Array.isArray(flags.test) ? flags.test : typeof flags.test === 'string' diff --git a/packages/astro/performance/package.json b/packages/astro/performance/package.json index c0833b952288..36d62528176b 100644 --- a/packages/astro/performance/package.json +++ b/packages/astro/performance/package.json @@ -11,8 +11,6 @@ "author": "", "license": "ISC", "devDependencies": { - "@types/yargs-parser": "^21.0.3", - "kleur": "^4.1.5", - "yargs-parser": "^21.1.1" + "kleur": "^4.1.5" } } diff --git a/packages/astro/src/cli/add/index.ts b/packages/astro/src/cli/add/index.ts index 7d33fe33a5be..f710184d2cb9 100644 --- a/packages/astro/src/cli/add/index.ts +++ b/packages/astro/src/cli/add/index.ts @@ -9,7 +9,6 @@ import ora from 'ora'; import preferredPM from 'preferred-pm'; import prompts from 'prompts'; import maxSatisfying from 'semver/ranges/max-satisfying.js'; -import type yargs from 'yargs-parser'; import { loadTSConfig, resolveConfig, @@ -29,14 +28,14 @@ import { appendForwardSlash } from '../../core/path.js'; import { apply as applyPolyfill } from '../../core/polyfill.js'; import { ensureProcessNodeEnv, parseNpmName } from '../../core/util.js'; import { eventCliSession, telemetry } from '../../events/index.js'; -import { createLoggerFromFlags, flagsToAstroInlineConfig } from '../flags.js'; +import { type Flags, createLoggerFromFlags, flagsToAstroInlineConfig } from '../flags.js'; import { fetchPackageJson, fetchPackageVersions } from '../install-package.js'; import { generate, parse, t, visit } from './babel.js'; import { ensureImport } from './imports.js'; import { wrapDefaultExport } from './wrapper.js'; interface AddOptions { - flags: yargs.Arguments; + flags: Flags; } interface IntegrationInfo { @@ -143,7 +142,7 @@ export async function add(names: string[], { flags }: AddOptions) { } // Some packages might have a common alias! We normalize those here. - const cwd = flags.root; + const cwd = inlineConfig.root; const logger = createLoggerFromFlags(flags); const integrationNames = names.map((name) => (ALIASES.has(name) ? ALIASES.get(name)! : name)); const integrations = await validateIntegrations(integrationNames); @@ -249,7 +248,7 @@ export async function add(names: string[], { flags }: AddOptions) { const rawConfigPath = await resolveConfigPath({ root: rootPath, - configFile: flags.config, + configFile: inlineConfig.configFile, fs: fsMod, }); let configURL = rawConfigPath ? pathToFileURL(rawConfigPath) : undefined; @@ -580,7 +579,7 @@ async function updateAstroConfig({ }: { configURL: URL; ast: t.File; - flags: yargs.Arguments; + flags: Flags; logger: Logger; logAdapterInstructions: boolean; }): Promise { @@ -717,7 +716,7 @@ async function tryToInstallIntegrations({ }: { integrations: IntegrationInfo[]; cwd?: string; - flags: yargs.Arguments; + flags: Flags; logger: Logger; }): Promise { const installCommand = await getInstallIntegrationsCommand({ integrations, cwd, logger }); @@ -893,7 +892,7 @@ async function updateTSConfig( cwd = process.cwd(), logger: Logger, integrationsInfo: IntegrationInfo[], - flags: yargs.Arguments, + flags: Flags, ): Promise { const integrations = integrationsInfo.map( (integration) => integration.id as frameworkWithTSSettings, @@ -996,7 +995,7 @@ function parseIntegrationName(spec: string) { return { scope, name, tag }; } -async function askToContinue({ flags }: { flags: yargs.Arguments }): Promise { +async function askToContinue({ flags }: { flags: Flags }): Promise { if (flags.yes || flags.y) return true; const response = await prompts({ @@ -1038,7 +1037,7 @@ function getDiffContent(input: string, output: string): string | null { async function setupIntegrationConfig(opts: { root: URL; logger: Logger; - flags: yargs.Arguments; + flags: Flags; integrationName: string; possibleConfigFiles: string[]; defaultConfigFile: string; diff --git a/packages/astro/src/cli/build/index.ts b/packages/astro/src/cli/build/index.ts index 15ff584317d9..9c10aff1569e 100644 --- a/packages/astro/src/cli/build/index.ts +++ b/packages/astro/src/cli/build/index.ts @@ -1,10 +1,9 @@ -import type yargs from 'yargs-parser'; import _build from '../../core/build/index.js'; import { printHelp } from '../../core/messages.js'; -import { flagsToAstroInlineConfig } from '../flags.js'; +import { type Flags, flagsToAstroInlineConfig } from '../flags.js'; interface BuildOptions { - flags: yargs.Arguments; + flags: Flags; } export async function build({ flags }: BuildOptions) { @@ -25,5 +24,5 @@ export async function build({ flags }: BuildOptions) { const inlineConfig = flagsToAstroInlineConfig(flags); - await _build(inlineConfig, { force: flags.force ?? false }); + await _build(inlineConfig, { force: !!flags.force }); } diff --git a/packages/astro/src/cli/check/index.ts b/packages/astro/src/cli/check/index.ts index a95e1074a59a..490d23970b51 100644 --- a/packages/astro/src/cli/check/index.ts +++ b/packages/astro/src/cli/check/index.ts @@ -1,13 +1,15 @@ import path from 'node:path'; -import type { Arguments } from 'yargs-parser'; import { ensureProcessNodeEnv } from '../../core/util.js'; -import { createLoggerFromFlags, flagsToAstroInlineConfig } from '../flags.js'; +import { type Flags, createLoggerFromFlags, flagsToAstroInlineConfig } from '../flags.js'; import { getPackage } from '../install-package.js'; -export async function check(flags: Arguments) { +export async function check(flags: Flags) { ensureProcessNodeEnv('production'); const logger = createLoggerFromFlags(flags); - const getPackageOpts = { skipAsk: flags.yes || flags.y, cwd: flags.root }; + const getPackageOpts = { + skipAsk: !!flags.yes || !!flags.y, + cwd: typeof flags.root == 'string' ? flags.root : undefined, + }; const checkPackage = await getPackage( '@astrojs/check', logger, diff --git a/packages/astro/src/cli/db/index.ts b/packages/astro/src/cli/db/index.ts index dc6da36e1dff..c6be7411bb2a 100644 --- a/packages/astro/src/cli/db/index.ts +++ b/packages/astro/src/cli/db/index.ts @@ -1,18 +1,26 @@ -import type { Arguments } from 'yargs-parser'; import type { AstroConfig } from '../../@types/astro.js'; import { resolveConfig } from '../../core/config/config.js'; import { apply as applyPolyfill } from '../../core/polyfill.js'; -import { createLoggerFromFlags, flagsToAstroInlineConfig } from '../flags.js'; +import { type Flags, createLoggerFromFlags, flagsToAstroInlineConfig } from '../flags.js'; import { getPackage } from '../install-package.js'; +interface YargsArguments { + _: Array; + '--'?: Array; + [argName: string]: any; +} + type DBPackage = { - cli: (args: { flags: Arguments; config: AstroConfig }) => unknown; + cli: (args: { flags: YargsArguments; config: AstroConfig }) => unknown; }; -export async function db({ flags }: { flags: Arguments }) { +export async function db({ positionals, flags }: { positionals: string[]; flags: Flags }) { applyPolyfill(); const logger = createLoggerFromFlags(flags); - const getPackageOpts = { skipAsk: flags.yes || flags.y, cwd: flags.root }; + const getPackageOpts = { + skipAsk: !!flags.yes || !!flags.y, + cwd: typeof flags.root == 'string' ? flags.root : undefined, + }; const dbPackage = await getPackage('@astrojs/db', logger, getPackageOpts, []); if (!dbPackage) { @@ -27,5 +35,10 @@ export async function db({ flags }: { flags: Arguments }) { const inlineConfig = flagsToAstroInlineConfig(flags); const { astroConfig } = await resolveConfig(inlineConfig, 'build'); - await cli({ flags, config: astroConfig }); + const yargsArgs: YargsArguments = { + _: positionals, + ...flags, + }; + + await cli({ flags: yargsArgs, config: astroConfig }); } diff --git a/packages/astro/src/cli/dev/index.ts b/packages/astro/src/cli/dev/index.ts index 531cddde4ce9..2bd22b7c6aeb 100644 --- a/packages/astro/src/cli/dev/index.ts +++ b/packages/astro/src/cli/dev/index.ts @@ -1,11 +1,10 @@ import { cyan } from 'kleur/colors'; -import type yargs from 'yargs-parser'; import devServer from '../../core/dev/index.js'; import { printHelp } from '../../core/messages.js'; -import { flagsToAstroInlineConfig } from '../flags.js'; +import { type Flags, flagsToAstroInlineConfig } from '../flags.js'; interface DevOptions { - flags: yargs.Arguments; + flags: Flags; } export async function dev({ flags }: DevOptions) { diff --git a/packages/astro/src/cli/docs/index.ts b/packages/astro/src/cli/docs/index.ts index cd6325577b37..afb5a1c622cf 100644 --- a/packages/astro/src/cli/docs/index.ts +++ b/packages/astro/src/cli/docs/index.ts @@ -1,9 +1,9 @@ -import type yargs from 'yargs-parser'; import { printHelp } from '../../core/messages.js'; +import type { Flags } from '../flags.js'; import { openInBrowser } from './open.js'; interface DocsOptions { - flags: yargs.Arguments; + flags: Flags; } export async function docs({ flags }: DocsOptions) { diff --git a/packages/astro/src/cli/flags.ts b/packages/astro/src/cli/flags.ts index 0af16806df44..c8180708f7a3 100644 --- a/packages/astro/src/cli/flags.ts +++ b/packages/astro/src/cli/flags.ts @@ -1,8 +1,11 @@ -import type { Arguments as Flags } from 'yargs-parser'; +import type { parseArgs } from 'node:util'; import type { AstroInlineConfig } from '../@types/astro.js'; import { type LogOptions, Logger } from '../core/logger/core.js'; import { nodeLogDestination } from '../core/logger/node.js'; +export type ParsedArgsResult = ReturnType; +export type Flags = ParsedArgsResult['values']; + export function flagsToAstroInlineConfig(flags: Flags): AstroInlineConfig { return { // Inline-only configs @@ -16,7 +19,7 @@ export function flagsToAstroInlineConfig(flags: Flags): AstroInlineConfig { base: typeof flags.base === 'string' ? flags.base : undefined, outDir: typeof flags.outDir === 'string' ? flags.outDir : undefined, server: { - port: typeof flags.port === 'number' ? flags.port : undefined, + port: typeof flags.port === 'string' ? Number(flags.port) : undefined, host: typeof flags.host === 'string' || typeof flags.host === 'boolean' ? flags.host : undefined, open: diff --git a/packages/astro/src/cli/index.ts b/packages/astro/src/cli/index.ts index 2d37132ac3b3..b3a819e586eb 100644 --- a/packages/astro/src/cli/index.ts +++ b/packages/astro/src/cli/index.ts @@ -1,7 +1,8 @@ +import { parseArgs } from 'node:util'; /* eslint-disable no-console */ import * as colors from 'kleur/colors'; -import yargs from 'yargs-parser'; import { ASTRO_VERSION } from '../core/constants.js'; +import type { ParsedArgsResult } from './flags.js'; type CLICommand = | 'help' @@ -65,9 +66,9 @@ function printVersion() { } /** Determine which command the user requested */ -function resolveCommand(flags: yargs.Arguments): CLICommand { - const cmd = flags._[2] as string; - if (flags.version) return 'version'; +function resolveCommand(args: ParsedArgsResult): CLICommand { + const cmd = args.positionals[2] as string; + if (args.values.version) return 'version'; const supportedCommands = new Set([ 'add', @@ -97,7 +98,9 @@ function resolveCommand(flags: yargs.Arguments): CLICommand { * NOTE: This function provides no error handling, so be sure * to present user-friendly error output where the fn is called. **/ -async function runCommand(cmd: string, flags: yargs.Arguments) { +async function runCommand(cmd: string, args: ParsedArgsResult) { + const flags = args.values; + // These commands can run directly without parsing the user config. switch (cmd) { case 'help': @@ -120,7 +123,7 @@ async function runCommand(cmd: string, flags: yargs.Arguments) { // Do not track session start, since the user may be trying to enable, // disable, or modify telemetry settings. const { update } = await import('./telemetry/index.js'); - const subcommand = flags._[3]?.toString(); + const subcommand = args.positionals[3]; await update(subcommand, { flags }); return; } @@ -131,7 +134,7 @@ async function runCommand(cmd: string, flags: yargs.Arguments) { } case 'preferences': { const { preferences } = await import('./preferences/index.js'); - const [subcommand, key, value] = flags._.slice(3).map((v) => v.toString()); + const [subcommand, key, value] = args.positionals.slice(3); const exitCode = await preferences(subcommand, key, value, { flags }); return process.exit(exitCode); } @@ -151,7 +154,7 @@ async function runCommand(cmd: string, flags: yargs.Arguments) { switch (cmd) { case 'add': { const { add } = await import('./add/index.js'); - const packages = flags._.slice(3) as string[]; + const packages = args.positionals.slice(3); await add(packages, { flags }); return; } @@ -161,7 +164,7 @@ async function runCommand(cmd: string, flags: yargs.Arguments) { case 'link': case 'init': { const { db } = await import('./db/index.js'); - await db({ flags }); + await db({ positionals: args.positionals, flags }); return; } case 'dev': { @@ -201,11 +204,21 @@ async function runCommand(cmd: string, flags: yargs.Arguments) { } /** The primary CLI action */ -export async function cli(args: string[]) { - const flags = yargs(args, { boolean: ['global'], alias: { g: 'global' } }); - const cmd = resolveCommand(flags); +export async function cli(argv: string[]) { + const args = parseArgs({ + args: argv, + allowPositionals: true, + strict: false, + options: { + global: { type: 'boolean', short: 'g' }, + host: { type: 'string' }, // Can be boolean too, which is covered by `strict: false` + open: { type: 'string' }, // Can be boolean too, which is covered by `strict: false` + // TODO: Add more flags here + }, + }); + const cmd = resolveCommand(args); try { - await runCommand(cmd, flags); + await runCommand(cmd, args); } catch (err) { const { throwAndExit } = await import('./throw-and-exit.js'); await throwAndExit(cmd, err); diff --git a/packages/astro/src/cli/info/index.ts b/packages/astro/src/cli/info/index.ts index cb61e45bfc06..3fa91802f26e 100644 --- a/packages/astro/src/cli/info/index.ts +++ b/packages/astro/src/cli/info/index.ts @@ -3,15 +3,14 @@ import { arch, platform } from 'node:os'; /* eslint-disable no-console */ import * as colors from 'kleur/colors'; import prompts from 'prompts'; -import type yargs from 'yargs-parser'; import type { AstroConfig, AstroUserConfig } from '../../@types/astro.js'; import { resolveConfig } from '../../core/config/index.js'; import { ASTRO_VERSION } from '../../core/constants.js'; import { apply as applyPolyfill } from '../../core/polyfill.js'; -import { flagsToAstroInlineConfig } from '../flags.js'; +import { type Flags, flagsToAstroInlineConfig } from '../flags.js'; interface InfoOptions { - flags: yargs.Arguments; + flags: Flags; } export async function getInfoOutput({ diff --git a/packages/astro/src/cli/preferences/index.ts b/packages/astro/src/cli/preferences/index.ts index 5735a9b6c26e..3811e7f48708 100644 --- a/packages/astro/src/cli/preferences/index.ts +++ b/packages/astro/src/cli/preferences/index.ts @@ -1,5 +1,4 @@ /* eslint-disable no-console */ -import type yargs from 'yargs-parser'; import type { AstroSettings } from '../../@types/astro.js'; import { fileURLToPath } from 'node:url'; @@ -15,10 +14,10 @@ import * as msg from '../../core/messages.js'; import { apply as applyPolyfill } from '../../core/polyfill.js'; import { DEFAULT_PREFERENCES } from '../../preferences/defaults.js'; import { type PreferenceKey, coerce, isValidKey } from '../../preferences/index.js'; -import { createLoggerFromFlags, flagsToAstroInlineConfig } from '../flags.js'; +import { type Flags, createLoggerFromFlags, flagsToAstroInlineConfig } from '../flags.js'; interface PreferencesOptions { - flags: yargs.Arguments; + flags: Flags; } const PREFERENCES_SUBCOMMANDS = [ @@ -77,7 +76,7 @@ export async function preferences( const settings = await createSettings(astroConfig, fileURLToPath(astroConfig.root)); const opts: SubcommandOptions = { location: flags.global ? 'global' : undefined, - json: flags.json, + json: !!flags.json, }; if (subcommand === 'list') { diff --git a/packages/astro/src/cli/preview/index.ts b/packages/astro/src/cli/preview/index.ts index 387c1f241a49..468332ce3b97 100644 --- a/packages/astro/src/cli/preview/index.ts +++ b/packages/astro/src/cli/preview/index.ts @@ -1,11 +1,10 @@ import { cyan } from 'kleur/colors'; -import type yargs from 'yargs-parser'; import { printHelp } from '../../core/messages.js'; import previewServer from '../../core/preview/index.js'; -import { flagsToAstroInlineConfig } from '../flags.js'; +import { type Flags, flagsToAstroInlineConfig } from '../flags.js'; interface PreviewOptions { - flags: yargs.Arguments; + flags: Flags; } export async function preview({ flags }: PreviewOptions) { diff --git a/packages/astro/src/cli/sync/index.ts b/packages/astro/src/cli/sync/index.ts index 6849fee70844..89623e6a800b 100644 --- a/packages/astro/src/cli/sync/index.ts +++ b/packages/astro/src/cli/sync/index.ts @@ -1,10 +1,9 @@ -import type yargs from 'yargs-parser'; import { printHelp } from '../../core/messages.js'; import _sync from '../../core/sync/index.js'; -import { flagsToAstroInlineConfig } from '../flags.js'; +import { type Flags, flagsToAstroInlineConfig } from '../flags.js'; interface SyncOptions { - flags: yargs.Arguments; + flags: Flags; } export async function sync({ flags }: SyncOptions) { diff --git a/packages/astro/src/cli/telemetry/index.ts b/packages/astro/src/cli/telemetry/index.ts index 277b1cab6721..276f00ef1970 100644 --- a/packages/astro/src/cli/telemetry/index.ts +++ b/packages/astro/src/cli/telemetry/index.ts @@ -1,11 +1,10 @@ /* eslint-disable no-console */ -import type yargs from 'yargs-parser'; import * as msg from '../../core/messages.js'; import { telemetry } from '../../events/index.js'; -import { createLoggerFromFlags } from '../flags.js'; +import { type Flags, createLoggerFromFlags } from '../flags.js'; interface TelemetryOptions { - flags: yargs.Arguments; + flags: Flags; } export async function notify() { diff --git a/packages/astro/src/core/config/config.ts b/packages/astro/src/core/config/config.ts index 2e43661a437a..c10066ce3273 100644 --- a/packages/astro/src/core/config/config.ts +++ b/packages/astro/src/core/config/config.ts @@ -2,14 +2,12 @@ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import * as colors from 'kleur/colors'; -import type { Arguments as Flags } from 'yargs-parser'; import { ZodError } from 'zod'; import type { AstroConfig, AstroInlineConfig, AstroInlineOnlyConfig, AstroUserConfig, - CLIFlags, } from '../../@types/astro.js'; import { eventConfigError, telemetry } from '../../events/index.js'; import { trackAstroConfigZodError } from '../errors/errors.js'; @@ -19,23 +17,6 @@ import { mergeConfig } from './merge.js'; import { validateConfig } from './validate.js'; import { loadConfigWithVite } from './vite-load.js'; -/** Convert the generic "yargs" flag object into our own, custom TypeScript object. */ -// NOTE: This function will be removed in a later PR. Use `flagsToAstroInlineConfig` instead. -// All CLI related flow should be located in the `packages/astro/src/cli` directory. -export function resolveFlags(flags: Partial): CLIFlags { - return { - root: typeof flags.root === 'string' ? flags.root : undefined, - site: typeof flags.site === 'string' ? flags.site : undefined, - base: typeof flags.base === 'string' ? flags.base : undefined, - port: typeof flags.port === 'number' ? flags.port : undefined, - config: typeof flags.config === 'string' ? flags.config : undefined, - host: - typeof flags.host === 'string' || typeof flags.host === 'boolean' ? flags.host : undefined, - open: - typeof flags.open === 'string' || typeof flags.open === 'boolean' ? flags.open : undefined, - }; -} - export function resolveRoot(cwd?: string | URL): string { if (cwd instanceof URL) { cwd = fileURLToPath(cwd); @@ -66,7 +47,7 @@ async function search(fsMod: typeof fs, root: string) { interface ResolveConfigPathOptions { root: string; - configFile?: string; + configFile?: string | false; fs: typeof fs; } diff --git a/packages/astro/src/core/config/index.ts b/packages/astro/src/core/config/index.ts index 3beaa5663523..7ffc290141cc 100644 --- a/packages/astro/src/core/config/index.ts +++ b/packages/astro/src/core/config/index.ts @@ -2,7 +2,6 @@ export { configPaths, resolveConfig, resolveConfigPath, - resolveFlags, resolveRoot, } from './config.js'; export { createNodeLogger } from './logging.js'; diff --git a/packages/create-astro/src/actions/context.ts b/packages/create-astro/src/actions/context.ts index 83a13eda7c94..bf1b522bd3bd 100644 --- a/packages/create-astro/src/actions/context.ts +++ b/packages/create-astro/src/actions/context.ts @@ -1,7 +1,7 @@ import os from 'node:os'; +import { parseArgs } from 'node:util'; import { type Task, prompt } from '@astrojs/cli-kit'; import { random } from '@astrojs/cli-kit/utils'; -import arg from 'arg'; import getSeasonalData from '../data/seasonal.js'; import { getName, getVersion } from '../messages.js'; @@ -33,47 +33,44 @@ export interface Context { } export async function getContext(argv: string[]): Promise { - const flags = arg( - { - '--template': String, - '--ref': String, - '--yes': Boolean, - '--no': Boolean, - '--install': Boolean, - '--no-install': Boolean, - '--git': Boolean, - '--no-git': Boolean, - '--typescript': String, - '--skip-houston': Boolean, - '--dry-run': Boolean, - '--help': Boolean, - '--fancy': Boolean, - - '-y': '--yes', - '-n': '--no', - '-h': '--help', + const args = parseArgs({ + args: argv, + allowPositionals: true, + strict: false, + options: { + template: { type: 'string' }, + ref: { type: 'string' }, + yes: { type: 'boolean', short: 'y' }, + no: { type: 'boolean', short: 'n' }, + install: { type: 'boolean' }, + 'no-install': { type: 'boolean' }, + git: { type: 'boolean' }, + 'no-git': { type: 'boolean' }, + typescript: { type: 'string' }, + 'skip-houston': { type: 'boolean' }, + 'dry-run': { type: 'boolean' }, + help: { type: 'boolean', short: 'h' }, + fancy: { type: 'boolean' }, }, - { argv, permissive: true }, - ); + }); const packageManager = detectPackageManager() ?? 'npm'; - let cwd = flags['_'][0]; + const projectName = args.positionals[0]; let { - '--help': help = false, - '--template': template, - '--no': no, - '--yes': yes, - '--install': install, - '--no-install': noInstall, - '--git': git, - '--no-git': noGit, - '--typescript': typescript, - '--fancy': fancy, - '--skip-houston': skipHouston, - '--dry-run': dryRun, - '--ref': ref, - } = flags; - let projectName = cwd; + help, + template, + no, + yes, + install, + 'no-install': noInstall, + git, + 'no-git': noGit, + typescript, + fancy, + 'skip-houston': skipHouston, + 'dry-run': dryRun, + ref, + } = args.values; if (no) { yes = false; @@ -82,10 +79,26 @@ export async function getContext(argv: string[]): Promise { if (typescript == undefined) typescript = 'strict'; } + skipHouston = typeof skipHouston == 'boolean' ? skipHouston : undefined; skipHouston = ((os.platform() === 'win32' && !fancy) || skipHouston) ?? [yes, no, install, git, typescript].some((v) => v !== undefined); + // We use `strict: false` in `parseArgs` to allow unknown options, but Node also + // simply doesn't guarantee the types anymore, so we need to validate ourselves :( + help = !!help; + template = typeof template == 'string' ? template : undefined; + no = !!no; + yes = !!yes; + install = !!install; + noInstall = !!noInstall; + git = !!git; + noGit = !!noGit; + typescript = typeof typescript == 'string' ? typescript : undefined; + fancy = !!fancy; + dryRun = !!dryRun; + ref = typeof ref == 'string' ? ref : undefined; + const { messages, hats, ties } = getSeasonalData({ fancy }); const context: Context = { @@ -107,7 +120,7 @@ export async function getContext(argv: string[]): Promise { install: install ?? (noInstall ? false : undefined), git: git ?? (noGit ? false : undefined), typescript, - cwd, + cwd: projectName, exit(code) { process.exit(code); }, diff --git a/packages/db/package.json b/packages/db/package.json index 850c6c94775b..6dd5d58238f0 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -81,13 +81,11 @@ "ora": "^8.0.1", "prompts": "^2.4.2", "strip-ansi": "^7.1.0", - "yargs-parser": "^21.1.1", "zod": "^3.23.8" }, "devDependencies": { "@types/deep-diff": "^1.0.5", "@types/prompts": "^2.4.9", - "@types/yargs-parser": "^21.0.3", "astro": "workspace:*", "astro-scripts": "workspace:*", "cheerio": "1.0.0", diff --git a/packages/db/src/core/cli/commands/execute/index.ts b/packages/db/src/core/cli/commands/execute/index.ts index c6c11cdbb6b9..03b3d00adda2 100644 --- a/packages/db/src/core/cli/commands/execute/index.ts +++ b/packages/db/src/core/cli/commands/execute/index.ts @@ -3,7 +3,6 @@ import { getManagedAppTokenOrExit } from '@astrojs/studio'; import { LibsqlError } from '@libsql/client'; import type { AstroConfig } from 'astro'; import { green } from 'kleur/colors'; -import type { Arguments } from 'yargs-parser'; import { EXEC_DEFAULT_EXPORT_ERROR, EXEC_ERROR, @@ -16,6 +15,7 @@ import { } from '../../../integration/vite-plugin-db.js'; import { bundleFile, importBundledFile } from '../../../load-file.js'; import type { DBConfig } from '../../../types.js'; +import type { YargsArguments } from '../../types.js'; export async function cmd({ astroConfig, @@ -24,7 +24,7 @@ export async function cmd({ }: { astroConfig: AstroConfig; dbConfig: DBConfig; - flags: Arguments; + flags: YargsArguments; }) { const filePath = flags._[4]; if (typeof filePath !== 'string') { diff --git a/packages/db/src/core/cli/commands/login/index.ts b/packages/db/src/core/cli/commands/login/index.ts index 61f7e0275b15..251d61e05433 100644 --- a/packages/db/src/core/cli/commands/login/index.ts +++ b/packages/db/src/core/cli/commands/login/index.ts @@ -7,8 +7,8 @@ import { cyan } from 'kleur/colors'; import open from 'open'; import ora from 'ora'; import prompt from 'prompts'; -import type { Arguments } from 'yargs-parser'; import type { DBConfig } from '../../../types.js'; +import type { YargsArguments } from '../../types.js'; const isWebContainer = // Stackblitz heuristic @@ -21,7 +21,7 @@ export async function cmd({ }: { astroConfig: AstroConfig; dbConfig: DBConfig; - flags: Arguments; + flags: YargsArguments; }) { let session = flags.session; diff --git a/packages/db/src/core/cli/commands/push/index.ts b/packages/db/src/core/cli/commands/push/index.ts index ecd101ecea79..237f7f6ea29e 100644 --- a/packages/db/src/core/cli/commands/push/index.ts +++ b/packages/db/src/core/cli/commands/push/index.ts @@ -1,7 +1,6 @@ import { getManagedAppTokenOrExit } from '@astrojs/studio'; import type { AstroConfig } from 'astro'; import prompts from 'prompts'; -import type { Arguments } from 'yargs-parser'; import { safeFetch } from '../../../../runtime/utils.js'; import { MIGRATION_VERSION } from '../../../consts.js'; import { type DBConfig, type DBSnapshot } from '../../../types.js'; @@ -13,6 +12,7 @@ import { getMigrationQueries, getProductionCurrentSnapshot, } from '../../migration-queries.js'; +import type { YargsArguments } from '../../types.js'; export async function cmd({ dbConfig, @@ -20,7 +20,7 @@ export async function cmd({ }: { astroConfig: AstroConfig; dbConfig: DBConfig; - flags: Arguments; + flags: YargsArguments; }) { const isDryRun = flags.dryRun; const isForceReset = flags.forceReset; diff --git a/packages/db/src/core/cli/commands/shell/index.ts b/packages/db/src/core/cli/commands/shell/index.ts index e0a1a6086c4f..5a6bcaa53d1b 100644 --- a/packages/db/src/core/cli/commands/shell/index.ts +++ b/packages/db/src/core/cli/commands/shell/index.ts @@ -1,7 +1,6 @@ import { getManagedAppTokenOrExit } from '@astrojs/studio'; import type { AstroConfig } from 'astro'; import { sql } from 'drizzle-orm'; -import type { Arguments } from 'yargs-parser'; import { createLocalDatabaseClient, createRemoteDatabaseClient, @@ -11,6 +10,7 @@ import { DB_PATH } from '../../../consts.js'; import { SHELL_QUERY_MISSING_ERROR } from '../../../errors.js'; import type { DBConfigInput } from '../../../types.js'; import { getAstroEnv, getRemoteDatabaseUrl } from '../../../utils.js'; +import type { YargsArguments } from '../../types.js'; export async function cmd({ flags, @@ -18,7 +18,7 @@ export async function cmd({ }: { dbConfig: DBConfigInput; astroConfig: AstroConfig; - flags: Arguments; + flags: YargsArguments; }) { const query = flags.query; if (!query) { diff --git a/packages/db/src/core/cli/commands/verify/index.ts b/packages/db/src/core/cli/commands/verify/index.ts index 950fb661567c..081c8ae3f8ec 100644 --- a/packages/db/src/core/cli/commands/verify/index.ts +++ b/packages/db/src/core/cli/commands/verify/index.ts @@ -1,6 +1,5 @@ import { getManagedAppTokenOrExit } from '@astrojs/studio'; import type { AstroConfig } from 'astro'; -import type { Arguments } from 'yargs-parser'; import type { DBConfig } from '../../../types.js'; import { createCurrentSnapshot, @@ -9,6 +8,7 @@ import { getMigrationQueries, getProductionCurrentSnapshot, } from '../../migration-queries.js'; +import type { YargsArguments } from '../../types.js'; export async function cmd({ dbConfig, @@ -16,7 +16,7 @@ export async function cmd({ }: { astroConfig: AstroConfig; dbConfig: DBConfig; - flags: Arguments; + flags: YargsArguments; }) { const isJson = flags.json; const appToken = await getManagedAppTokenOrExit(flags.token); diff --git a/packages/db/src/core/cli/index.ts b/packages/db/src/core/cli/index.ts index 531b016a6b77..5d9aa10e97dd 100644 --- a/packages/db/src/core/cli/index.ts +++ b/packages/db/src/core/cli/index.ts @@ -1,13 +1,13 @@ import type { AstroConfig } from 'astro'; -import type { Arguments } from 'yargs-parser'; import { resolveDbConfig } from '../load-file.js'; import { printHelp } from './print-help.js'; +import type { YargsArguments } from './types.js'; export async function cli({ flags, config: astroConfig, }: { - flags: Arguments; + flags: YargsArguments; config: AstroConfig; }) { const args = flags._ as string[]; diff --git a/packages/db/src/core/cli/types.ts b/packages/db/src/core/cli/types.ts new file mode 100644 index 000000000000..9d8aad56b78f --- /dev/null +++ b/packages/db/src/core/cli/types.ts @@ -0,0 +1,7 @@ +// Copy of `yargs-parser` `Arguments` type. We don't use `yargs-parser` +// in runtime anymore, but our exposed API still relies on this shape. +export interface YargsArguments { + _: Array; + '--'?: Array; + [argName: string]: any; +} diff --git a/packages/db/src/core/integration/index.ts b/packages/db/src/core/integration/index.ts index 19c712c802aa..6573c838af13 100644 --- a/packages/db/src/core/integration/index.ts +++ b/packages/db/src/core/integration/index.ts @@ -1,4 +1,5 @@ import { existsSync } from 'fs'; +import { parseArgs } from 'node:util'; import { dirname } from 'path'; import { fileURLToPath } from 'url'; import { type ManagedAppToken, getManagedAppTokenOrExit } from '@astrojs/studio'; @@ -14,7 +15,6 @@ import { loadEnv, mergeConfig, } from 'vite'; -import parseArgs from 'yargs-parser'; import { AstroDbError } from '../../runtime/utils.js'; import { CONFIG_FILE_NAMES, DB_PATH } from '../consts.js'; import { EXEC_DEFAULT_EXPORT_ERROR, EXEC_ERROR } from '../errors.js'; @@ -72,8 +72,8 @@ function astroDBIntegration(): AstroIntegration { if (command === 'preview') return; let dbPlugin: VitePlugin | undefined = undefined; - const args = parseArgs(process.argv.slice(3)); - connectToStudio = process.env.ASTRO_INTERNAL_TEST_REMOTE || args['remote']; + const args = parseArgs({ strict: false }); + connectToStudio = !!process.env.ASTRO_INTERNAL_TEST_REMOTE || !!args.values.remote; if (connectToStudio) { appToken = await getManagedAppTokenOrExit(); diff --git a/packages/upgrade/src/actions/context.ts b/packages/upgrade/src/actions/context.ts index 2103a53277e2..cd9028e85a54 100644 --- a/packages/upgrade/src/actions/context.ts +++ b/packages/upgrade/src/actions/context.ts @@ -1,13 +1,13 @@ import { pathToFileURL } from 'node:url'; +import { parseArgs } from 'node:util'; import { prompt } from '@astrojs/cli-kit'; -import arg from 'arg'; import detectPackageManager from 'preferred-pm'; export interface Context { help: boolean; prompt: typeof prompt; version: string; - dryRun?: boolean; + dryRun: boolean; cwd: URL; stdin?: typeof process.stdin; stdout?: typeof process.stdout; @@ -28,22 +28,20 @@ export interface PackageInfo { } export async function getContext(argv: string[]): Promise { - const flags = arg( - { - '--dry-run': Boolean, - '--help': Boolean, - - '-h': '--help', + const args = parseArgs({ + args: argv, + allowPositionals: true, + strict: false, + options: { + 'dry-run': { type: 'boolean' }, + help: { type: 'boolean', short: 'h' }, }, - { argv, permissive: true }, - ); + }); const packageManager = (await detectPackageManager(process.cwd()))?.name ?? 'npm'; - const { - _: [version = 'latest'] = [], - '--help': help = false, - '--dry-run': dryRun, - } = flags; + const version = args.positionals[0] ?? 'latest'; + const help = !!args.values.help; + const dryRun = !!args.values['dry-run']; return { help, diff --git a/packages/upgrade/test/context.test.js b/packages/upgrade/test/context.test.js index bbc887c2ae3c..5ba484740f89 100644 --- a/packages/upgrade/test/context.test.js +++ b/packages/upgrade/test/context.test.js @@ -6,12 +6,12 @@ describe('context', () => { it('no arguments', async () => { const ctx = await getContext([]); assert.equal(ctx.version, 'latest'); - assert.equal(ctx.dryRun, undefined); + assert.equal(ctx.dryRun, false); }); it('tag', async () => { const ctx = await getContext(['beta']); assert.equal(ctx.version, 'beta'); - assert.equal(ctx.dryRun, undefined); + assert.equal(ctx.dryRun, false); }); it('dry run', async () => { const ctx = await getContext(['--dry-run']); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 95ef9f027cf1..58191b539a1d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -729,9 +729,6 @@ importers: which-pm: specifier: ^3.0.0 version: 3.0.0 - yargs-parser: - specifier: ^21.1.1 - version: 21.1.1 zod: specifier: ^3.23.8 version: 3.23.8 @@ -794,9 +791,6 @@ importers: '@types/semver': specifier: ^7.5.8 version: 7.5.8 - '@types/yargs-parser': - specifier: ^21.0.3 - version: 21.0.3 astro-scripts: specifier: workspace:* version: link:../../scripts @@ -1720,15 +1714,9 @@ importers: packages/astro/performance: devDependencies: - '@types/yargs-parser': - specifier: ^21.0.3 - version: 21.0.3 kleur: specifier: ^4.1.5 version: 4.1.5 - yargs-parser: - specifier: ^21.1.1 - version: 21.1.1 packages/astro/performance/fixtures/md: dependencies: @@ -4252,9 +4240,6 @@ importers: strip-ansi: specifier: ^7.1.0 version: 7.1.0 - yargs-parser: - specifier: ^21.1.1 - version: 21.1.1 zod: specifier: ^3.23.8 version: 3.23.8 @@ -4265,9 +4250,6 @@ importers: '@types/prompts': specifier: ^2.4.9 version: 2.4.9 - '@types/yargs-parser': - specifier: ^21.0.3 - version: 21.0.3 astro: specifier: workspace:* version: link:../astro @@ -7529,9 +7511,6 @@ packages: '@types/xml2js@0.4.14': resolution: {integrity: sha512-4YnrRemBShWRO2QjvUin8ESA41rH+9nQGLUGZV/1IDhi3SL9OhdpNC/MrulTWuptXKwhx/aDxE7toV0f/ypIXQ==} - '@types/yargs-parser@21.0.3': - resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} - '@typescript-eslint/eslint-plugin@8.0.1': resolution: {integrity: sha512-5g3Y7GDFsJAnY4Yhvk8sZtFfV6YNF2caLzjrRPUBzewjPCaj0yokePB4LJSobyCzGMzjZZYFbwuzbfDHlimXbQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -13410,8 +13389,6 @@ snapshots: dependencies: '@types/node': 18.19.31 - '@types/yargs-parser@21.0.3': {} - '@typescript-eslint/eslint-plugin@8.0.1(@typescript-eslint/parser@8.0.1(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4))(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4)': dependencies: '@eslint-community/regexpp': 4.11.0 diff --git a/scripts/cmd/copy.js b/scripts/cmd/copy.js index 943c998dfb4f..948ed114fe9b 100644 --- a/scripts/cmd/copy.js +++ b/scripts/cmd/copy.js @@ -1,19 +1,20 @@ -import arg from 'arg'; import { globby as glob } from 'globby'; import { promises as fs, readFileSync } from 'node:fs'; import { posix } from 'node:path'; +import { parseArgs } from 'node:util'; import * as tar from 'tar/create'; const { resolve, dirname, sep, join } = posix; -/** @type {import('arg').Spec} */ -const spec = { - '--tgz': Boolean, -}; - export default async function copy() { - let { _: patterns, ['--tgz']: isCompress } = arg(spec); - patterns = patterns.slice(1); + const args = parseArgs({ + allowPositionals: true, + options: { + tgz: { type: 'boolean' }, + }, + }); + const patterns = args.positionals.slice(1); + const isCompress = args.values.tgz; if (isCompress) { const files = await glob(patterns, { gitignore: true }); diff --git a/scripts/cmd/test.js b/scripts/cmd/test.js index 04f02f73afc0..17f6ecd04101 100644 --- a/scripts/cmd/test.js +++ b/scripts/cmd/test.js @@ -3,30 +3,32 @@ import { spec } from 'node:test/reporters'; import fs from 'node:fs/promises'; import path from 'node:path'; import { pathToFileURL } from 'node:url'; -import arg from 'arg'; +import { parseArgs } from 'node:util'; import glob from 'tiny-glob'; const isCI = !!process.env.CI; const defaultTimeout = isCI ? 1400000 : 600000; export default async function test() { - const args = arg({ - '--match': String, // aka --test-name-pattern: https://nodejs.org/api/test.html#filtering-tests-by-name - '--only': Boolean, // aka --test-only: https://nodejs.org/api/test.html#only-tests - '--parallel': Boolean, // aka --test-concurrency: https://nodejs.org/api/test.html#test-runner-execution-model - '--watch': Boolean, // experimental: https://nodejs.org/api/test.html#watch-mode - '--timeout': Number, // Test timeout in milliseconds (default: 30000ms) - '--setup': String, // Test setup file - // Aliases - '-m': '--match', - '-o': '--only', - '-p': '--parallel', - '-w': '--watch', - '-t': '--timeout', - '-s': '--setup', + const args = parseArgs({ + allowPositionals: true, + options: { + // aka --test-name-pattern: https://nodejs.org/api/test.html#filtering-tests-by-name + match: { type: 'string', alias: 'm' }, + // aka --test-only: https://nodejs.org/api/test.html#only-tests + only: { type: 'boolean', alias: 'o' }, + // aka --test-concurrency: https://nodejs.org/api/test.html#test-runner-execution-model + parallel: { type: 'boolean', alias: 'p' }, + // experimental: https://nodejs.org/api/test.html#watch-mode + watch: { type: 'boolean', alias: 'w' }, + // Test timeout in milliseconds (default: 30000ms) + timeout: { type: 'string', alias: 't' }, + // Test setup file + setup: { type: 'string', alias: 's' }, + }, }); - const pattern = args._[1]; + const pattern = args.positionals[1]; if (!pattern) throw new Error('Missing test glob pattern'); const files = await glob(pattern, { filesOnly: true, absolute: true }); @@ -34,12 +36,12 @@ export default async function test() { // For some reason, the `only` option does not work and we need to explicitly set the CLI flag instead. // Node.js requires opt-in to run .only tests :( // https://nodejs.org/api/test.html#only-tests - if (args['--only']) { + if (args.values.only) { process.env.NODE_OPTIONS ??= ''; process.env.NODE_OPTIONS += ' --test-only'; } - if (!args['--parallel']) { + if (!args.values.parallel) { // If not parallel, we create a temporary file that imports all the test files // so that it all runs in a single process. const tempTestFile = path.resolve('./node_modules/.astro/test.mjs'); @@ -56,12 +58,12 @@ export default async function test() { // https://nodejs.org/api/test.html#runoptions run({ files, - testNamePatterns: args['--match'], - concurrency: args['--parallel'], - only: args['--only'], - setup: args['--setup'], - watch: args['--watch'], - timeout: args['--timeout'] ?? defaultTimeout, // Node.js defaults to Infinity, so set better fallback + testNamePatterns: args.values.match, + concurrency: args.values.parallel, + only: args.values.only, + setup: args.values.setup, + watch: args.values.watch, + timeout: args.values.timeout ? Number(args.values.timeout) : defaultTimeout, // Node.js defaults to Infinity, so set better fallback }) .on('test:fail', () => { // For some reason, a test fail using the JS API does not set an exit code of 1, From 19a72592119ff4a47c8cc541990eadc1886cb0e4 Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Wed, 14 Aug 2024 10:08:44 +0000 Subject: [PATCH 4/4] [ci] format --- packages/astro/components/Code.astro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/astro/components/Code.astro b/packages/astro/components/Code.astro index d1c019c60d1b..8818b2ae0d27 100644 --- a/packages/astro/components/Code.astro +++ b/packages/astro/components/Code.astro @@ -26,7 +26,7 @@ interface Props extends Omit, 'lang'> { /** * A metastring to pass to the highlighter. * Allows passing information to transformers: https://shiki.style/guide/transformers#meta - * + * * @default undefined */ meta?: string;