From 75921b3cd916d439f6392c487c21532fde35ed13 Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Tue, 7 Mar 2023 06:55:41 +0000 Subject: [PATCH] feat(cli): add `--watch` to `astro check` command (#6356) * feat(cli): add `--watch` to `astro check` command * chore: refactor in a leaner way, logic not changed * chore: lint * chore: revert changes in sync command * chore: tweak server settings * test: add one test case * chore: increase timeout * test: predictable testing * chore: add changeset * chore: code suggestions * code suggestions * chore: use directly `chokidar` * chore: tweak code * fix: open documents first * chore: disable test * chore: code suggestions * chore: code suggestions * Apply suggestions from code review Co-authored-by: Erika <3019731+Princesseuh@users.noreply.github.com> * code suggestions * chore: rebase --------- Co-authored-by: Erika <3019731+Princesseuh@users.noreply.github.com> --- .changeset/six-moons-taste.md | 5 + packages/astro/package.json | 1 + packages/astro/src/cli/check/index.ts | 361 +++++++++++++++--- packages/astro/src/cli/index.ts | 19 +- packages/astro/src/core/sync/index.ts | 24 +- packages/astro/test/cli.test.js | 59 ++- .../astro-check-watch/astro.config.mjs | 6 + .../fixtures/astro-check-watch/package.json | 8 + .../astro-check-watch/src/pages/index.astro | 1 + .../fixtures/astro-check-watch/tsconfig.json | 2 + packages/astro/test/test-utils.js | 40 +- packages/webapi/mod.d.ts | 2 +- pnpm-lock.yaml | 8 + 13 files changed, 467 insertions(+), 69 deletions(-) create mode 100644 .changeset/six-moons-taste.md create mode 100644 packages/astro/test/fixtures/astro-check-watch/astro.config.mjs create mode 100644 packages/astro/test/fixtures/astro-check-watch/package.json create mode 100644 packages/astro/test/fixtures/astro-check-watch/src/pages/index.astro create mode 100644 packages/astro/test/fixtures/astro-check-watch/tsconfig.json diff --git a/.changeset/six-moons-taste.md b/.changeset/six-moons-taste.md new file mode 100644 index 000000000000..badeae9b67fc --- /dev/null +++ b/.changeset/six-moons-taste.md @@ -0,0 +1,5 @@ +--- +'astro': minor +--- + +Added a new `--watch` flag to the command `astro check` diff --git a/packages/astro/package.json b/packages/astro/package.json index 861fcb245d93..f91fa52e33dc 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -113,6 +113,7 @@ "@types/yargs-parser": "^21.0.0", "acorn": "^8.8.1", "boxen": "^6.2.1", + "chokidar": "^3.5.3", "ci-info": "^3.3.1", "common-ancestor-path": "^1.0.1", "cookie": "^0.5.0", diff --git a/packages/astro/src/cli/check/index.ts b/packages/astro/src/cli/check/index.ts index 1634718c4257..531d4fb8bef4 100644 --- a/packages/astro/src/cli/check/index.ts +++ b/packages/astro/src/cli/check/index.ts @@ -1,5 +1,5 @@ /* eslint-disable no-console */ -import { AstroCheck, DiagnosticSeverity } from '@astrojs/language-server'; +import { AstroCheck, DiagnosticSeverity, GetDiagnosticsResult } from '@astrojs/language-server'; import glob from 'fast-glob'; import * as fs from 'fs'; import { bold, dim, red, yellow } from 'kleur/colors'; @@ -11,18 +11,77 @@ import type { AstroSettings } from '../../@types/astro'; import type { LogOptions } from '../../core/logger/core.js'; import { printHelp } from '../../core/messages.js'; import { printDiagnostic } from './print.js'; +import type { Arguments as Flags } from 'yargs-parser'; +import { debug, info } from '../../core/logger/core.js'; +import type { ProcessExit, SyncOptions } from '../../core/sync'; +import fsMod from 'fs'; +import type { FSWatcher } from 'chokidar'; +import { join } from 'node:path'; -interface Result { +type DiagnosticResult = { errors: number; - // The language server cannot actually return any warnings at the moment, but we'll keep this here for future use warnings: number; hints: number; +}; + +export type CheckPayload = { + /** + * Flags passed via CLI + */ + flags: Flags; + + /** + * Logging options + */ + logging: LogOptions; +}; + +type CheckFlags = { + /** + * Whether the `check` command should watch for `.astro` and report errors + * @default {false} + */ + watch: boolean; +}; + +/** + * + * Types of response emitted by the checker + */ +export enum CheckResult { + /** + * Operation finished without errors + */ + ExitWithSuccess, + /** + * Operation finished with errors + */ + ExitWithError, + /** + * The consumer should not terminate the operation + */ + Listen, } +const ASTRO_GLOB_PATTERN = '**/*.astro'; + +/** + * Checks `.astro` files for possible errors. + * + * If the `--watch` flag is provided, the command runs indefinitely and provides diagnostics + * when `.astro` files are modified. + * + * Every time an astro files is modified, content collections are also generated. + * + * @param {AstroSettings} settings + * @param {CheckPayload} options Options passed {@link AstroChecker} + * @param {Flags} options.flags Flags coming from the CLI + * @param {LogOptions} options.logging Logging options + */ export async function check( settings: AstroSettings, - { logging, flags }: { logging: LogOptions; flags: Arguments } -) { + { logging, flags }: CheckPayload +): Promise { if (flags.help || flags.h) { printHelp({ commandName: 'astro check', @@ -34,66 +93,256 @@ export async function check( }); return; } - console.log(bold('astro check')); + const checkFlags = parseFlags(flags); + if (checkFlags.watch) { + info(logging, 'check', 'Checking files in watch mode'); + } else { + info(logging, 'check', 'Checking files'); + } const { syncCli } = await import('../../core/sync/index.js'); - const syncRet = await syncCli(settings, { logging, fs, flags }); - // early exit on sync failure - if (syncRet === 1) return syncRet; - const root = settings.config.root; - - const spinner = ora(` Getting diagnostics for Astro files in ${fileURLToPath(root)}…`).start(); - const require = createRequire(import.meta.url); - let checker = new AstroCheck( + const diagnosticChecker = new AstroCheck( root.toString(), - require.resolve('typescript/lib/tsserverlibrary.js', { paths: [root.toString()] }) + require.resolve('typescript/lib/tsserverlibrary.js', { + paths: [root.toString()], + }) ); - const filesCount = await openAllDocuments(root, [], checker); - let diagnostics = await checker.getDiagnostics(); + return new AstroChecker({ + syncCli, + settings, + fileSystem: fs, + logging, + diagnosticChecker, + isWatchMode: checkFlags.watch, + }); +} - spinner.succeed(); +type CheckerConstructor = { + diagnosticChecker: AstroCheck; - let result: Result = { - errors: 0, - warnings: 0, - hints: 0, - }; + isWatchMode: boolean; - diagnostics.forEach((diag) => { - diag.diagnostics.forEach((d) => { - console.log(printDiagnostic(diag.fileUri, diag.text, d)); + syncCli: (settings: AstroSettings, options: SyncOptions) => Promise; - switch (d.severity) { - case DiagnosticSeverity.Error: { - result.errors++; - break; - } - case DiagnosticSeverity.Warning: { - result.warnings++; - break; - } - case DiagnosticSeverity.Hint: { - result.hints++; - break; - } + settings: Readonly; + + logging: Readonly; + + fileSystem: typeof fsMod; +}; + +/** + * Responsible to check files - classic or watch mode - and report diagnostics. + * + * When in watch mode, the class does a whole check pass, and then starts watching files. + * When a change occurs to an `.astro` file, the checker builds content collections again and lint all the `.astro` files. + */ +export class AstroChecker { + readonly #diagnosticsChecker: AstroCheck; + readonly #shouldWatch: boolean; + readonly #syncCli: (settings: AstroSettings, opts: SyncOptions) => Promise; + + readonly #settings: AstroSettings; + + readonly #logging: LogOptions; + readonly #fs: typeof fsMod; + #watcher?: FSWatcher; + + #filesCount: number; + #updateDiagnostics: NodeJS.Timeout | undefined; + + constructor({ + diagnosticChecker, + isWatchMode, + syncCli, + settings, + fileSystem, + logging, + }: CheckerConstructor) { + this.#diagnosticsChecker = diagnosticChecker; + this.#shouldWatch = isWatchMode; + this.#syncCli = syncCli; + this.#logging = logging; + this.#settings = settings; + this.#fs = fileSystem; + this.#filesCount = 0; + } + + /** + * Check all `.astro` files once and then finishes the operation. + */ + public async check(): Promise { + return await this.#checkAllFiles(true); + } + + /** + * Check all `.astro` files and then start watching for changes. + */ + public async watch(): Promise { + await this.#checkAllFiles(true); + await this.#watch(); + return CheckResult.Listen; + } + + /** + * Stops the watch. It terminates the inner server. + */ + public async stop() { + await this.#watcher?.close(); + } + + /** + * Whether the checker should run in watch mode + */ + public get isWatchMode(): boolean { + return this.#shouldWatch; + } + + async #openDocuments() { + this.#filesCount = await openAllDocuments( + this.#settings.config.root, + [], + this.#diagnosticsChecker + ); + } + + /** + * Lint all `.astro` files, and report the result in console. Operations executed, in order: + * 1. Compile content collections. + * 2. Optionally, traverse the file system for `.astro` files and saves their paths. + * 3. Get diagnostics for said files and print the result in console. + * + * @param openDocuments Whether the operation should open all `.astro` files + */ + async #checkAllFiles(openDocuments: boolean): Promise { + const processExit = await this.#syncCli(this.#settings, { + logging: this.#logging, + fs: this.#fs, + }); + // early exit on sync failure + if (processExit === 1) return processExit; + + let spinner = ora( + ` Getting diagnostics for Astro files in ${fileURLToPath(this.#settings.config.root)}…` + ).start(); + + if (openDocuments) { + await this.#openDocuments(); + } + + let diagnostics = await this.#diagnosticsChecker.getDiagnostics(); + + spinner.succeed(); + + let brokenDownDiagnostics = this.#breakDownDiagnostics(diagnostics); + this.#logDiagnosticsSeverity(brokenDownDiagnostics); + return brokenDownDiagnostics.errors > 0 + ? CheckResult.ExitWithError + : CheckResult.ExitWithSuccess; + } + + #checkForDiagnostics() { + clearTimeout(this.#updateDiagnostics); + // @ematipico: I am not sure of `setTimeout`. I would rather use a debounce but let's see if this works. + // Inspiration from `svelte-check`. + this.#updateDiagnostics = setTimeout(async () => await this.#checkAllFiles(false), 500); + } + + /** + * This function is responsible to attach events to the server watcher + */ + async #watch() { + const { default: chokidar } = await import('chokidar'); + this.#watcher = chokidar.watch( + join(fileURLToPath(this.#settings.config.root), ASTRO_GLOB_PATTERN), + { + ignored: ['**/node_modules/**'], + ignoreInitial: true, } + ); + + this.#watcher.on('add', (file) => { + this.#addDocument(file); + this.#filesCount += 1; + this.#checkForDiagnostics(); }); - }); + this.#watcher.on('change', (file) => { + this.#addDocument(file); + this.#checkForDiagnostics(); + }); + this.#watcher.on('unlink', (file) => { + this.#diagnosticsChecker.removeDocument(file); + this.#filesCount -= 1; + this.#checkForDiagnostics(); + }); + } - console.log( - [ - bold(`Result (${filesCount} file${filesCount === 1 ? '' : 's'}): `), - bold(red(`${result.errors} ${result.errors === 1 ? 'error' : 'errors'}`)), - bold(yellow(`${result.warnings} ${result.warnings === 1 ? 'warning' : 'warnings'}`)), - dim(`${result.hints} ${result.hints === 1 ? 'hint' : 'hints'}\n`), - ].join(`\n${dim('-')} `) - ); + /** + * Add a document to the diagnostics checker + * @param filePath Path to the file + */ + #addDocument(filePath: string) { + const text = fs.readFileSync(filePath, 'utf-8'); + this.#diagnosticsChecker.upsertDocument({ + uri: pathToFileURL(filePath).toString(), + text, + }); + } + + /** + * Logs the result of the various diagnostics + * + * @param result Result emitted by AstroChecker.#breakDownDiagnostics + */ + #logDiagnosticsSeverity(result: Readonly) { + info( + this.#logging, + 'diagnostics', + [ + bold(`Result (${this.#filesCount} file${this.#filesCount === 1 ? '' : 's'}): `), + bold(red(`${result.errors} ${result.errors === 1 ? 'error' : 'errors'}`)), + bold(yellow(`${result.warnings} ${result.warnings === 1 ? 'warning' : 'warnings'}`)), + dim(`${result.hints} ${result.hints === 1 ? 'hint' : 'hints'}\n`), + ].join(`\n${dim('-')} `) + ); + } + + /** + * It loops through all diagnostics and break down diagnostics that are errors, warnings or hints. + */ + #breakDownDiagnostics(diagnostics: Readonly): DiagnosticResult { + let result: DiagnosticResult = { + errors: 0, + warnings: 0, + hints: 0, + }; + + diagnostics.forEach((diag) => { + diag.diagnostics.forEach((d) => { + info(this.#logging, 'diagnostics', `\n ${printDiagnostic(diag.fileUri, diag.text, d)}`); - const exitCode = result.errors ? 1 : 0; - return exitCode; + switch (d.severity) { + case DiagnosticSeverity.Error: { + result.errors++; + break; + } + case DiagnosticSeverity.Warning: { + result.warnings++; + break; + } + case DiagnosticSeverity.Hint: { + result.hints++; + break; + } + } + }); + }); + + return result; + } } /** @@ -104,13 +353,14 @@ async function openAllDocuments( filePathsToIgnore: string[], checker: AstroCheck ): Promise { - const files = await glob('**/*.astro', { + const files = await glob(ASTRO_GLOB_PATTERN, { cwd: fileURLToPath(workspaceUri), ignore: ['node_modules/**'].concat(filePathsToIgnore.map((ignore) => `${ignore}/**`)), absolute: true, }); for (const file of files) { + debug('check', `Adding file ${file} to the list of files to check.`); const text = fs.readFileSync(file, 'utf-8'); checker.upsertDocument({ uri: pathToFileURL(file).toString(), @@ -120,3 +370,12 @@ async function openAllDocuments( return files.length; } + +/** + * Parse flags and sets defaults + */ +function parseFlags(flags: Flags): CheckFlags { + return { + watch: flags.watch ?? false, + }; +} diff --git a/packages/astro/src/cli/index.ts b/packages/astro/src/cli/index.ts index fdab81aab5f1..806393b47fc6 100644 --- a/packages/astro/src/cli/index.ts +++ b/packages/astro/src/cli/index.ts @@ -18,7 +18,7 @@ import { enableVerboseLogging, nodeLogDestination } from '../core/logger/node.js import { formatConfigErrorMessage, formatErrorMessage, printHelp } from '../core/messages.js'; import * as event from '../events/index.js'; import { eventConfigError, eventError, telemetry } from '../events/index.js'; -import { check } from './check/index.js'; +import { check, CheckResult } from './check/index.js'; import { openInBrowser } from './open.js'; type Arguments = yargs.Arguments; @@ -222,15 +222,24 @@ async function runCommand(cmd: string, flags: yargs.Arguments) { } case 'check': { - const ret = await check(settings, { logging, flags }); - return process.exit(ret); + // We create a server to start doing our operations + const checkServer = await check(settings, { flags, logging }); + if (checkServer) { + if (checkServer.isWatchMode) { + await checkServer.watch(); + return await new Promise(() => {}); // lives forever + } else { + let checkResult = await checkServer.check(); + return process.exit(checkResult); + } + } } case 'sync': { const { syncCli } = await import('../core/sync/index.js'); - const ret = await syncCli(settings, { logging, fs, flags }); - return process.exit(ret); + const result = await syncCli(settings, { logging, fs, flags }); + return process.exit(result); } case 'preview': { diff --git a/packages/astro/src/core/sync/index.ts b/packages/astro/src/core/sync/index.ts index 8806f236d60d..b4e0b3265581 100644 --- a/packages/astro/src/core/sync/index.ts +++ b/packages/astro/src/core/sync/index.ts @@ -2,19 +2,24 @@ import { dim } from 'kleur/colors'; import type fsMod from 'node:fs'; import { performance } from 'node:perf_hooks'; import { createServer } from 'vite'; -import { Arguments } from 'yargs-parser'; import type { AstroSettings } from '../../@types/astro'; import { createContentTypesGenerator } from '../../content/index.js'; import { globalContentConfigObserver } from '../../content/utils.js'; import { runHookConfigSetup } from '../../integrations/index.js'; import { setUpEnvTs } from '../../vite-plugin-inject-env-ts/index.js'; import { getTimeStat } from '../build/util.js'; -import { createVite } from '../create-vite.js'; import { AstroError, AstroErrorData } from '../errors/index.js'; import { info, LogOptions } from '../logger/core.js'; import { printHelp } from '../messages.js'; +import type { Arguments } from 'yargs-parser'; +import { createVite } from '../create-vite.js'; + +export type ProcessExit = 0 | 1; -type ProcessExit = 0 | 1; +export type SyncOptions = { + logging: LogOptions; + fs: typeof fsMod; +}; export async function syncCli( settings: AstroSettings, @@ -40,9 +45,20 @@ export async function syncCli( return sync(resolvedSettings, { logging, fs }); } +/** + * Generate content collection types, and then returns the process exit signal. + * + * A non-zero process signal is emitted in case there's an error while generating content collection types. + * + * @param {SyncOptions} options + * @param {AstroSettings} settings Astro settings + * @param {typeof fsMod} options.fs The file system + * @param {LogOptions} options.logging Logging options + * @return {Promise} + */ export async function sync( settings: AstroSettings, - { logging, fs }: { logging: LogOptions; fs: typeof fsMod } + { logging, fs }: SyncOptions ): Promise { const timerStart = performance.now(); // Needed to load content config diff --git a/packages/astro/test/cli.test.js b/packages/astro/test/cli.test.js index 2c1dd0dead9d..09918a451646 100644 --- a/packages/astro/test/cli.test.js +++ b/packages/astro/test/cli.test.js @@ -1,8 +1,11 @@ import { expect } from 'chai'; -import { cli, parseCliDevStart, cliServerLogSetup } from './test-utils.js'; -import { promises as fs } from 'fs'; -import { fileURLToPath } from 'url'; -import { isIPv4 } from 'net'; +import { cli, parseCliDevStart, cliServerLogSetup, loadFixture } from './test-utils.js'; +import stripAnsi from 'strip-ansi'; +import { promises as fs, readFileSync } from 'node:fs'; +import { fileURLToPath } from 'node:url'; +import { isIPv4 } from 'node:net'; +import { join } from 'node:path'; +import { Writable } from 'node:stream'; describe('astro cli', () => { const cliServerLogSetupWithFixture = (flags, cmd) => { @@ -15,6 +18,50 @@ describe('astro cli', () => { expect(proc.exitCode).to.equal(0); }); + // Flaky test, in CI it exceeds the timeout most of the times + it.skip('astro check --watch reports errors on modified files', async () => { + let messageResolve; + const messagePromise = new Promise((resolve) => { + messageResolve = resolve; + }); + const oneErrorContent = 'foobar'; + + /** @type {import('./test-utils').Fixture} */ + const fixture = await loadFixture({ + root: './fixtures/astro-check-watch/', + }); + const logs = []; + + const checkServer = await fixture.check({ + flags: { watch: true }, + logging: { + level: 'info', + dest: new Writable({ + objectMode: true, + write(event, _, callback) { + logs.push({ ...event, message: stripAnsi(event.message) }); + if (event.message.includes('1 error')) { + messageResolve(logs); + } + callback(); + }, + }), + }, + }); + await checkServer.watch(); + const pagePath = join(fileURLToPath(fixture.config.root), 'src/pages/index.astro'); + const pageContent = readFileSync(pagePath, 'utf-8'); + await fs.writeFile(pagePath, oneErrorContent); + const messages = await messagePromise; + await fs.writeFile(pagePath, pageContent); + await checkServer.stop(); + const diagnostics = messages.filter( + (m) => m.type === 'diagnostics' && m.message.includes('Result') + ); + expect(diagnostics[0].message).to.include('0 errors'); + expect(diagnostics[1].message).to.include('1 error'); + }).timeout(35000); + it('astro --version', async () => { const pkgURL = new URL('../package.json', import.meta.url); const pkgVersion = await fs.readFile(pkgURL, 'utf8').then((data) => JSON.parse(data).version); @@ -144,7 +191,9 @@ describe('astro cli i18n', () => { const projectRootURL = new URL('./fixtures/astro-basic/', import.meta.url); let error = null; try { - const proc = cli('dev', '--root', fileURLToPath(projectRootURL), { env: { LANG: locale } }); + const proc = cli('dev', '--root', fileURLToPath(projectRootURL), { + env: { LANG: locale }, + }); await parseCliDevStart(proc); } catch (e) { console.log(e); diff --git a/packages/astro/test/fixtures/astro-check-watch/astro.config.mjs b/packages/astro/test/fixtures/astro-check-watch/astro.config.mjs new file mode 100644 index 000000000000..db9a6db87e38 --- /dev/null +++ b/packages/astro/test/fixtures/astro-check-watch/astro.config.mjs @@ -0,0 +1,6 @@ +import { defineConfig } from 'astro/config'; + +// https://astro.build/config +export default defineConfig({ + integrations: [], +}); diff --git a/packages/astro/test/fixtures/astro-check-watch/package.json b/packages/astro/test/fixtures/astro-check-watch/package.json new file mode 100644 index 000000000000..a1ffe7f47fbb --- /dev/null +++ b/packages/astro/test/fixtures/astro-check-watch/package.json @@ -0,0 +1,8 @@ +{ + "name": "@test/astro-check-watch", + "version": "0.0.0", + "private": true, + "dependencies": { + "astro": "workspace:*" + } +} diff --git a/packages/astro/test/fixtures/astro-check-watch/src/pages/index.astro b/packages/astro/test/fixtures/astro-check-watch/src/pages/index.astro new file mode 100644 index 000000000000..71379cddce4d --- /dev/null +++ b/packages/astro/test/fixtures/astro-check-watch/src/pages/index.astro @@ -0,0 +1 @@ +

diff --git a/packages/astro/test/fixtures/astro-check-watch/tsconfig.json b/packages/astro/test/fixtures/astro-check-watch/tsconfig.json new file mode 100644 index 000000000000..d64f4787597c --- /dev/null +++ b/packages/astro/test/fixtures/astro-check-watch/tsconfig.json @@ -0,0 +1,2 @@ +// Having a `tsconfig.json`, even empty speeds up the test massively since TypeScript does not have to look for one +{} diff --git a/packages/astro/test/test-utils.js b/packages/astro/test/test-utils.js index bb06f90e429a..e6a9d648ac7b 100644 --- a/packages/astro/test/test-utils.js +++ b/packages/astro/test/test-utils.js @@ -12,6 +12,7 @@ import { createSettings } from '../dist/core/config/index.js'; import dev from '../dist/core/dev/index.js'; import { nodeLogDestination } from '../dist/core/logger/node.js'; import preview from '../dist/core/preview/index.js'; +import { check } from '../dist/cli/check/index.js'; // polyfill WebAPIs to globalThis for Node v12, Node v14, and Node v16 polyfill(globalThis, { @@ -24,6 +25,8 @@ polyfill(globalThis, { * @typedef {import('../src/@types/astro').AstroConfig} AstroConfig * @typedef {import('../src/core/preview/index').PreviewServer} PreviewServer * @typedef {import('../src/core/app/index').App} App + * @typedef {import('../src/cli/check/index').AstroChecker} AstroChecker + * @typedef {import('../src/cli/check/index').CheckPayload} CheckPayload * * * @typedef {Object} Fixture @@ -39,6 +42,24 @@ polyfill(globalThis, { * @property {() => Promise} clean * @property {() => Promise} loadTestAdapterApp * @property {() => Promise} onNextChange + * @property {(opts: CheckPayload) => Promise} check + * + * This function returns an instance of the Check + * + * + * When used in a test suite: + * ```js + * let fixture = await loadFixture({ + * root: './fixtures/astro-check-watch/', + * }); + * ``` + * `opts` will override the options passed to the `AstroChecker` + * + * ```js + * let { check, stop, watch } = fixture.check({ + * flags: { watch: true }, + * }); + * ``` */ /** @type {import('../src/core/logger/core').LogOptions} */ @@ -148,6 +169,9 @@ export async function loadFixture(inlineConfig) { return build(settings, { logging, telemetry, ...opts }); }, sync: (opts) => sync(settings, { logging, fs, ...opts }), + check: async (opts) => { + return await check(settings, { logging, ...opts }); + }, startDevServer: async (opts = {}) => { process.env.NODE_ENV = 'development'; devServer = await dev(settings, { logging, telemetry, ...opts }); @@ -160,7 +184,11 @@ export async function loadFixture(inlineConfig) { fetch: (url, init) => fetch(resolveUrl(url), init), preview: async (opts = {}) => { process.env.NODE_ENV = 'production'; - const previewServer = await preview(settings, { logging, telemetry, ...opts }); + const previewServer = await preview(settings, { + logging, + telemetry, + ...opts, + }); config.server.host = parseAddressToHost(previewServer.host); // update host config.server.port = previewServer.port; // update port return previewServer; @@ -177,7 +205,11 @@ export async function loadFixture(inlineConfig) { cwd: fileURLToPath(config.outDir), }), clean: async () => { - await fs.promises.rm(config.outDir, { maxRetries: 10, recursive: true, force: true }); + await fs.promises.rm(config.outDir, { + maxRetries: 10, + recursive: true, + force: true, + }); }, loadTestAdapterApp: async (streaming) => { const url = new URL(`./server/entry.mjs?id=${fixtureId}`, config.outDir); @@ -250,7 +282,9 @@ const cliPath = fileURLToPath(new URL('../astro.js', import.meta.url)); /** Returns a process running the Astro CLI. */ export function cli(/** @type {string[]} */ ...args) { - const spawned = execa('node', [cliPath, ...args], { env: { ASTRO_TELEMETRY_DISABLED: true } }); + const spawned = execa('node', [cliPath, ...args], { + env: { ASTRO_TELEMETRY_DISABLED: true }, + }); spawned.stdout.setEncoding('utf8'); diff --git a/packages/webapi/mod.d.ts b/packages/webapi/mod.d.ts index ecc90236488d..1447ccea0646 100644 --- a/packages/webapi/mod.d.ts +++ b/packages/webapi/mod.d.ts @@ -10,4 +10,4 @@ interface PolyfillOptions { override?: Record; -} \ No newline at end of file +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e5385ccf3ef9..46c7b4e19b4b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -455,6 +455,7 @@ importers: boxen: ^6.2.1 chai: ^4.3.6 cheerio: ^1.0.0-rc.11 + chokidar: ^3.5.3 ci-info: ^3.3.1 common-ancestor-path: ^1.0.1 cookie: ^0.5.0 @@ -521,6 +522,7 @@ importers: '@types/yargs-parser': 21.0.0 acorn: 8.8.2 boxen: 6.2.1 + chokidar: 3.5.3 ci-info: 3.7.1 common-ancestor-path: 1.0.1 cookie: 0.5.0 @@ -1237,6 +1239,12 @@ importers: dependencies: astro: link:../../.. + packages/astro/test/fixtures/astro-check-watch: + specifiers: + astro: workspace:* + dependencies: + astro: link:../../.. + packages/astro/test/fixtures/astro-children: specifiers: '@astrojs/preact': workspace:*