From e074704c60d9084b527041f4dcc89031297199ab Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Thu, 25 Jul 2024 12:18:07 +0200 Subject: [PATCH 01/44] feat: make inline config 1st arg --- packages/astro/src/cli/check/index.ts | 2 +- packages/astro/src/cli/sync/index.ts | 2 +- packages/astro/src/core/index.ts | 2 +- packages/astro/src/core/sync/index.ts | 9 ++++----- packages/astro/test/astro-sync.test.js | 10 ++++++---- .../units/dev/collections-mixed-content-errors.test.js | 10 ++++++---- 6 files changed, 19 insertions(+), 16 deletions(-) diff --git a/packages/astro/src/cli/check/index.ts b/packages/astro/src/cli/check/index.ts index 00bc3d11a285..b6c0daa4ca36 100644 --- a/packages/astro/src/cli/check/index.ts +++ b/packages/astro/src/cli/check/index.ts @@ -30,7 +30,7 @@ export async function check(flags: Arguments) { // For now, we run this once as usually `astro check --watch` is ran alongside `astro dev` which also calls `astro sync`. const { default: sync } = await import('../../core/sync/index.js'); try { - await sync({ inlineConfig: flagsToAstroInlineConfig(flags) }); + await sync(flagsToAstroInlineConfig(flags)); } catch (_) { return process.exit(1); } diff --git a/packages/astro/src/cli/sync/index.ts b/packages/astro/src/cli/sync/index.ts index 6849fee70844..54f0985ecd9b 100644 --- a/packages/astro/src/cli/sync/index.ts +++ b/packages/astro/src/cli/sync/index.ts @@ -21,7 +21,7 @@ export async function sync({ flags }: SyncOptions) { } try { - await _sync({ inlineConfig: flagsToAstroInlineConfig(flags), telemetry: true }); + await _sync(flagsToAstroInlineConfig(flags), { telemetry: true }); return 0; } catch (_) { return 1; diff --git a/packages/astro/src/core/index.ts b/packages/astro/src/core/index.ts index e0f9f6c82412..bdd7c7f05939 100644 --- a/packages/astro/src/core/index.ts +++ b/packages/astro/src/core/index.ts @@ -23,4 +23,4 @@ export const build = (inlineConfig: AstroInlineConfig) => _build(inlineConfig); * @experimental The JavaScript API is experimental */ // Wrap `_sync` to prevent exposing internal options -export const sync = (inlineConfig: AstroInlineConfig) => _sync({ inlineConfig }); +export const sync = (inlineConfig: AstroInlineConfig) => _sync(inlineConfig); diff --git a/packages/astro/src/core/sync/index.ts b/packages/astro/src/core/sync/index.ts index 7441aabc7ea2..75816934b50b 100644 --- a/packages/astro/src/core/sync/index.ts +++ b/packages/astro/src/core/sync/index.ts @@ -46,11 +46,10 @@ type DBPackage = { typegen?: (args: Pick) => Promise; }; -export default async function sync({ - inlineConfig, - fs, - telemetry: _telemetry = false, -}: { inlineConfig: AstroInlineConfig; fs?: typeof fsMod; telemetry?: boolean }) { +export default async function sync( + inlineConfig: AstroInlineConfig, + { fs, telemetry: _telemetry = false }: { fs?: typeof fsMod; telemetry?: boolean } = {} +) { ensureProcessNodeEnv('production'); const logger = createNodeLogger(inlineConfig); const { astroConfig, userConfig } = await resolveConfig(inlineConfig ?? {}, 'sync'); diff --git a/packages/astro/test/astro-sync.test.js b/packages/astro/test/astro-sync.test.js index f6faa6722551..8d47e51205f3 100644 --- a/packages/astro/test/astro-sync.test.js +++ b/packages/astro/test/astro-sync.test.js @@ -48,10 +48,12 @@ const createFixture = () => { }, }; - await astroFixture.sync({ - inlineConfig: { root: fileURLToPath(new URL(root, import.meta.url)) }, - fs: fsMock, - }); + await astroFixture.sync( + { root: fileURLToPath(new URL(root, import.meta.url)) }, + { + fs: fsMock, + } + ); }, /** @param {string} path */ thenFileShouldExist(path) { diff --git a/packages/astro/test/units/dev/collections-mixed-content-errors.test.js b/packages/astro/test/units/dev/collections-mixed-content-errors.test.js index d63e42d53323..379670babef1 100644 --- a/packages/astro/test/units/dev/collections-mixed-content-errors.test.js +++ b/packages/astro/test/units/dev/collections-mixed-content-errors.test.js @@ -8,13 +8,15 @@ const root = new URL('../../fixtures/content-mixed-errors/', import.meta.url); async function sync({ fs }) { try { - await _sync({ - inlineConfig: { + await _sync( + { root: fileURLToPath(root), logLevel: 'silent', }, - fs, - }); + { + fs, + } + ); return 0; } catch (_) { return 1; From e28c8ed7f4ef7f61ac3dbe24465c90d682ee1042 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Thu, 25 Jul 2024 12:19:41 +0200 Subject: [PATCH 02/44] fix: run config done in sync --- packages/astro/src/core/sync/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/astro/src/core/sync/index.ts b/packages/astro/src/core/sync/index.ts index 75816934b50b..ef824c4f2f4f 100644 --- a/packages/astro/src/core/sync/index.ts +++ b/packages/astro/src/core/sync/index.ts @@ -10,7 +10,7 @@ import { globalContentConfigObserver } from '../../content/utils.js'; import { syncAstroEnv } from '../../env/sync.js'; import { telemetry } from '../../events/index.js'; import { eventCliSession } from '../../events/session.js'; -import { runHookConfigSetup } from '../../integrations/hooks.js'; +import { runHookConfigDone, runHookConfigSetup } from '../../integrations/hooks.js'; import { getTimeStat } from '../build/util.js'; import { resolveConfig } from '../config/config.js'; import { createNodeLogger } from '../config/logging.js'; @@ -62,6 +62,7 @@ export default async function sync( settings, logger, }); + await runHookConfigDone({ settings, logger }); return await syncInternal({ settings, logger, fs }); } From 8a5c2a244a88a7edcfde5ae2a5608c627dc8df4b Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Thu, 25 Jul 2024 12:45:51 +0200 Subject: [PATCH 03/44] feat: start working on injectTypes --- packages/astro/src/@types/astro.ts | 7 ++++++ packages/astro/src/core/config/settings.ts | 1 + packages/astro/src/integrations/hooks.ts | 28 ++++++++++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index ddffe51df4ad..8aaa21af212d 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -2433,6 +2433,11 @@ export interface AstroAdapterFeatures { functionPerRoute: boolean; } +export interface InjectedType { + filename: string; + content: string; +} + export interface AstroSettings { config: AstroConfig; adapter: AstroAdapter | undefined; @@ -2468,6 +2473,7 @@ export interface AstroSettings { latestAstroVersion: string | undefined; serverIslandMap: NonNullable; serverIslandNameMap: NonNullable; + injectedTypes: Array; } export type AsyncRendererComponentFn = ( @@ -3155,6 +3161,7 @@ declare global { 'astro:config:done': (options: { config: AstroConfig; setAdapter: (adapter: AstroAdapter) => void; + injectTypes: (injectedType: InjectedType) => URL; logger: AstroIntegrationLogger; }) => void | Promise; 'astro:server:setup': (options: { diff --git a/packages/astro/src/core/config/settings.ts b/packages/astro/src/core/config/settings.ts index 9c82118b75e7..cfc60a9b72fe 100644 --- a/packages/astro/src/core/config/settings.ts +++ b/packages/astro/src/core/config/settings.ts @@ -108,6 +108,7 @@ export function createBaseSettings(config: AstroConfig): AstroSettings { timer: new AstroTimer(), dotAstroDir: new URL('.astro/', config.root), latestAstroVersion: undefined, // Will be set later if applicable when the dev server starts + injectedTypes: [], }; } diff --git a/packages/astro/src/integrations/hooks.ts b/packages/astro/src/integrations/hooks.ts index 33a7c7c0c5ac..b7a0401590bd 100644 --- a/packages/astro/src/integrations/hooks.ts +++ b/packages/astro/src/integrations/hooks.ts @@ -21,6 +21,7 @@ import { mergeConfig } from '../core/config/index.js'; import type { AstroIntegrationLogger, Logger } from '../core/logger/core.js'; import { isServerLikeOutput } from '../core/util.js'; import { validateSupportedFeatures } from './features-validation.js'; +import { z } from 'zod'; async function withTakingALongTimeMsg({ name, @@ -100,6 +101,20 @@ export function getToolbarServerCommunicationHelpers(server: ViteDevServer) { }; } +const validInjectedTypeFilenameSchema = z + .string() + .regex(/^[\w.-]+$/, 'Invalid chars') + .endsWith('.d.ts'); + +// TODO: test +export function normalizeInjectedTypeFilename(filename: string, integrationName: string): string { + // TODO: validate filename + const result = validInjectedTypeFilenameSchema.safeParse(filename); + if (!result.success) { + } + return `${integrationName.replace(/[^\w.-]/g, '_')}/${filename}`; +} + export async function runHookConfigSetup({ settings, command, @@ -327,6 +342,19 @@ export async function runHookConfigDone({ } settings.adapter = adapter; }, + injectTypes(injectedType) { + const normalizedFilename = normalizeInjectedTypeFilename( + injectedType.filename, + integration.name + ); + + settings.injectedTypes.push({ + filename: injectedType.filename, + content: injectedType.content, + }); + + return new URL(`./${normalizedFilename}`, settings.config.root); + }, logger: getLogger(integration, logger), }), logger, From 4fb6bbed55700e8e5a142a2ad9ca923eaba26517 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Thu, 25 Jul 2024 14:45:21 +0200 Subject: [PATCH 04/44] feat: write files --- packages/astro/src/core/sync/index.ts | 26 +++++++++++++++++++++++- packages/astro/src/integrations/hooks.ts | 7 ++++--- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/packages/astro/src/core/sync/index.ts b/packages/astro/src/core/sync/index.ts index ef824c4f2f4f..3c565de30c01 100644 --- a/packages/astro/src/core/sync/index.ts +++ b/packages/astro/src/core/sync/index.ts @@ -2,7 +2,7 @@ import fsMod from 'node:fs'; import { performance } from 'node:perf_hooks'; import { fileURLToPath } from 'node:url'; import { dim } from 'kleur/colors'; -import { type HMRPayload, createServer } from 'vite'; +import { type HMRPayload, createServer, normalizePath } from 'vite'; import type { AstroConfig, AstroInlineConfig, AstroSettings } from '../../@types/astro.js'; import { getPackage } from '../../cli/install-package.js'; import { createContentTypesGenerator } from '../../content/index.js'; @@ -28,6 +28,7 @@ import type { Logger } from '../logger/core.js'; import { formatErrorMessage } from '../messages.js'; import { ensureProcessNodeEnv } from '../util.js'; import { setUpEnvTs } from './setup-env-ts.js'; +import { dirname, relative } from 'node:path'; export type SyncOptions = { /** @@ -98,6 +99,8 @@ export async function syncInternal({ } syncAstroEnv(settings, fs); + writeInjectedTypes(settings, fs); + await setUpEnvTs({ settings, logger, fs }); logger.info('types', `Generated ${dim(getTimeStat(timerStart, performance.now()))}`); } catch (err) { @@ -111,6 +114,27 @@ export async function syncInternal({ } } +function writeInjectedTypes(settings: AstroSettings, fs: typeof fsMod) { + const references: Array = []; + + // TODO: special handling for actions + // TODO: special handling for db + // TODO: special handling for content + // TODO: special handling for env + + for (const { filename, content } of settings.injectedTypes) { + const path = fileURLToPath(new URL(filename, settings.dotAstroDir)); + fs.mkdirSync(dirname(path), { recursive: true }); + // TODO: format content using recast + fs.writeFileSync(path, content, 'utf-8'); + references.push(normalizePath(relative(fileURLToPath(settings.dotAstroDir), path))); + } + + const astroDtsContent = `/// \n${references.map((reference) => `/// `).join('\n')}`; + // TODO: use types.d.ts for backward compatibility. Use astro.d.ts in Astro 5.0 + fs.writeFileSync(new URL('./astro.d.ts', settings.dotAstroDir), astroDtsContent, 'utf-8'); +} + /** * Generate content collection types, and then returns the process exit signal. * diff --git a/packages/astro/src/integrations/hooks.ts b/packages/astro/src/integrations/hooks.ts index b7a0401590bd..23628c3f5683 100644 --- a/packages/astro/src/integrations/hooks.ts +++ b/packages/astro/src/integrations/hooks.ts @@ -111,8 +111,9 @@ export function normalizeInjectedTypeFilename(filename: string, integrationName: // TODO: validate filename const result = validInjectedTypeFilenameSchema.safeParse(filename); if (!result.success) { + throw new Error("TODO: astro error") } - return `${integrationName.replace(/[^\w.-]/g, '_')}/${filename}`; + return `./integrations/${integrationName.replace(/[^\w.-]/g, '_')}/${filename}`; } export async function runHookConfigSetup({ @@ -349,11 +350,11 @@ export async function runHookConfigDone({ ); settings.injectedTypes.push({ - filename: injectedType.filename, + filename: normalizedFilename, content: injectedType.content, }); - return new URL(`./${normalizedFilename}`, settings.config.root); + return new URL(normalizedFilename, settings.config.root); }, logger: getLogger(integration, logger), }), From 9f2ccea4ba96ddfddf4f72a6314cc9152c362bcf Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Thu, 25 Jul 2024 15:18:05 +0200 Subject: [PATCH 05/44] feat: adapt core features --- examples/basics/astro.config.mjs | 28 +++++++++- examples/basics/package.json | 1 + examples/basics/src/actions.ts | 7 +++ examples/basics/src/content/config.ts | 10 ++++ examples/basics/src/content/posts/foo.json | 3 ++ examples/basics/src/env.d.ts | 2 +- packages/astro/src/actions/index.ts | 39 ++++---------- packages/astro/src/content/consts.ts | 2 +- packages/astro/src/content/types-generator.ts | 10 ++-- packages/astro/src/core/sync/constants.ts | 2 + packages/astro/src/core/sync/index.ts | 23 ++++++--- packages/astro/src/core/sync/setup-env-ts.ts | 51 ++++--------------- packages/astro/src/env/sync.ts | 14 +++-- pnpm-lock.yaml | 4 ++ 14 files changed, 105 insertions(+), 91 deletions(-) create mode 100644 examples/basics/src/actions.ts create mode 100644 examples/basics/src/content/config.ts create mode 100644 examples/basics/src/content/posts/foo.json create mode 100644 packages/astro/src/core/sync/constants.ts diff --git a/examples/basics/astro.config.mjs b/examples/basics/astro.config.mjs index 882e6515a67e..8babba9e4fcd 100644 --- a/examples/basics/astro.config.mjs +++ b/examples/basics/astro.config.mjs @@ -1,4 +1,30 @@ +// @ts-check import { defineConfig } from 'astro/config'; +import node from '@astrojs/node' // https://astro.build/config -export default defineConfig({}); +export default defineConfig({ + output: "server", + integrations: [ + node({ + mode: "standalone" + }), + { + name: "test", + hooks: { + "astro:config:done": ({ injectTypes }) => { + injectTypes({ + filename: "types.d.ts", + content: "declare const FOO: string;" + }) + } + } + } + ], + experimental: { + env: { + schema: {} + }, + actions: true + } +}); diff --git a/examples/basics/package.json b/examples/basics/package.json index 0ce97377a2c7..0a20792ade5a 100644 --- a/examples/basics/package.json +++ b/examples/basics/package.json @@ -11,6 +11,7 @@ "astro": "astro" }, "dependencies": { + "@astrojs/node": "^8.3.2", "astro": "^4.12.2" } } diff --git a/examples/basics/src/actions.ts b/examples/basics/src/actions.ts new file mode 100644 index 000000000000..a4cddd759eaa --- /dev/null +++ b/examples/basics/src/actions.ts @@ -0,0 +1,7 @@ +import { defineAction } from "astro:actions"; + +export const server = { + foo: defineAction({ + handler: () => {} + }) +} \ No newline at end of file diff --git a/examples/basics/src/content/config.ts b/examples/basics/src/content/config.ts new file mode 100644 index 000000000000..7a3027878f35 --- /dev/null +++ b/examples/basics/src/content/config.ts @@ -0,0 +1,10 @@ +import { defineCollection, z } from "astro:content"; + +export const collections = { + posts: defineCollection({ + type: "data", + schema: z.object({ + title: z.string() + }) + }), +} \ No newline at end of file diff --git a/examples/basics/src/content/posts/foo.json b/examples/basics/src/content/posts/foo.json new file mode 100644 index 000000000000..b2d53d2926d4 --- /dev/null +++ b/examples/basics/src/content/posts/foo.json @@ -0,0 +1,3 @@ +{ + "title": "Foo" +} \ No newline at end of file diff --git a/examples/basics/src/env.d.ts b/examples/basics/src/env.d.ts index f964fe0cffd8..9bc5cb41c24e 100644 --- a/examples/basics/src/env.d.ts +++ b/examples/basics/src/env.d.ts @@ -1 +1 @@ -/// +/// \ No newline at end of file diff --git a/packages/astro/src/actions/index.ts b/packages/astro/src/actions/index.ts index f176988ff0c1..2f0469359838 100644 --- a/packages/astro/src/actions/index.ts +++ b/packages/astro/src/actions/index.ts @@ -30,9 +30,6 @@ export default function astroActions({ throw error; } - const stringifiedActionsImport = JSON.stringify( - viteID(new URL('./actions', params.config.srcDir)) - ); params.updateConfig({ vite: { plugins: [vitePluginUserActions({ settings }), vitePluginActions(fs)], @@ -49,11 +46,18 @@ export default function astroActions({ entrypoint: 'astro/actions/runtime/middleware.js', order: 'post', }); + }, + 'astro:config:done': (params) => { + const stringifiedActionsImport = JSON.stringify( + viteID(new URL('./actions', params.config.srcDir)) + ); + params.injectTypes({ + filename: ACTIONS_TYPES_FILE, + content: `declare module "astro:actions" { + type Actions = typeof import(${stringifiedActionsImport})["server"]; - await typegen({ - stringifiedActionsImport, - root: params.config.root, - fs, + export const actions: Actions; +}`, }); }, }, @@ -119,24 +123,3 @@ const vitePluginActions = (fs: typeof fsMod): VitePlugin => ({ return code; }, }); - -async function typegen({ - stringifiedActionsImport, - root, - fs, -}: { - stringifiedActionsImport: string; - root: URL; - fs: typeof fsMod; -}) { - const content = `declare module "astro:actions" { - type Actions = typeof import(${stringifiedActionsImport})["server"]; - - export const actions: Actions; -}`; - - const dotAstroDir = new URL('.astro/', root); - - await fs.promises.mkdir(dotAstroDir, { recursive: true }); - await fs.promises.writeFile(new URL(ACTIONS_TYPES_FILE, dotAstroDir), content); -} diff --git a/packages/astro/src/content/consts.ts b/packages/astro/src/content/consts.ts index f65652453b60..377cf4e86e08 100644 --- a/packages/astro/src/content/consts.ts +++ b/packages/astro/src/content/consts.ts @@ -16,4 +16,4 @@ export const CONTENT_FLAGS = [ PROPAGATED_ASSET_FLAG, ] as const; -export const CONTENT_TYPES_FILE = 'types.d.ts'; +export const CONTENT_TYPES_FILE = 'astro/content.d.ts'; diff --git a/packages/astro/src/content/types-generator.ts b/packages/astro/src/content/types-generator.ts index b970eadb9011..33cb5b48cdd4 100644 --- a/packages/astro/src/content/types-generator.ts +++ b/packages/astro/src/content/types-generator.ts @@ -499,7 +499,7 @@ async function writeContentFiles({ } const configPathRelativeToCacheDir = normalizeConfigPath( - settings.dotAstroDir.pathname, + new URL('astro', settings.dotAstroDir).pathname, contentPaths.config.url.pathname ); @@ -515,8 +515,8 @@ async function writeContentFiles({ contentConfig ? `typeof import(${configPathRelativeToCacheDir})` : 'never' ); - await fs.promises.writeFile( - new URL(CONTENT_TYPES_FILE, settings.dotAstroDir), - typeTemplateContent - ); + const filepath = new URL(CONTENT_TYPES_FILE, settings.dotAstroDir); + + await fs.promises.mkdir(path.dirname(fileURLToPath(filepath)), { recursive: true }); + await fs.promises.writeFile(filepath, typeTemplateContent); } diff --git a/packages/astro/src/core/sync/constants.ts b/packages/astro/src/core/sync/constants.ts new file mode 100644 index 000000000000..71963c336648 --- /dev/null +++ b/packages/astro/src/core/sync/constants.ts @@ -0,0 +1,2 @@ +// TODO: use types.d.ts for backward compatibility. Use astro.d.ts in Astro 5.0 +export const REFERENCE_FILE = './types.d.ts'; \ No newline at end of file diff --git a/packages/astro/src/core/sync/index.ts b/packages/astro/src/core/sync/index.ts index 3c565de30c01..1261f7259bae 100644 --- a/packages/astro/src/core/sync/index.ts +++ b/packages/astro/src/core/sync/index.ts @@ -29,6 +29,9 @@ import { formatErrorMessage } from '../messages.js'; import { ensureProcessNodeEnv } from '../util.js'; import { setUpEnvTs } from './setup-env-ts.js'; import { dirname, relative } from 'node:path'; +import { CONTENT_TYPES_FILE } from '../../content/consts.js'; +import { ACTIONS_TYPES_FILE, VIRTUAL_MODULE_ID } from '../../actions/consts.js'; +import { REFERENCE_FILE } from './constants.js'; export type SyncOptions = { /** @@ -93,11 +96,12 @@ export async function syncInternal({ ); try { + // TODO: remove and use injectTypes instead await dbPackage?.typegen?.(settings.config); if (!skip?.content) { await syncContentCollections(settings, { fs, logger }); } - syncAstroEnv(settings, fs); + settings = syncAstroEnv(settings, fs); writeInjectedTypes(settings, fs); @@ -117,13 +121,17 @@ export async function syncInternal({ function writeInjectedTypes(settings: AstroSettings, fs: typeof fsMod) { const references: Array = []; - // TODO: special handling for actions - // TODO: special handling for db - // TODO: special handling for content - // TODO: special handling for env + if (fs.existsSync(new URL(CONTENT_TYPES_FILE, settings.dotAstroDir))) { + references.push(CONTENT_TYPES_FILE); + } for (const { filename, content } of settings.injectedTypes) { - const path = fileURLToPath(new URL(filename, settings.dotAstroDir)); + // TODO: reuse logic + const newFilename = + filename === `./integrations/${VIRTUAL_MODULE_ID.replace(':', '_')}/${ACTIONS_TYPES_FILE}` + ? `./astro/${ACTIONS_TYPES_FILE}` + : filename; + const path = fileURLToPath(new URL(newFilename, settings.dotAstroDir)); fs.mkdirSync(dirname(path), { recursive: true }); // TODO: format content using recast fs.writeFileSync(path, content, 'utf-8'); @@ -131,8 +139,7 @@ function writeInjectedTypes(settings: AstroSettings, fs: typeof fsMod) { } const astroDtsContent = `/// \n${references.map((reference) => `/// `).join('\n')}`; - // TODO: use types.d.ts for backward compatibility. Use astro.d.ts in Astro 5.0 - fs.writeFileSync(new URL('./astro.d.ts', settings.dotAstroDir), astroDtsContent, 'utf-8'); + fs.writeFileSync(new URL(REFERENCE_FILE, settings.dotAstroDir), astroDtsContent, 'utf-8'); } /** diff --git a/packages/astro/src/core/sync/setup-env-ts.ts b/packages/astro/src/core/sync/setup-env-ts.ts index 5a380271a297..a24bfccae2a5 100644 --- a/packages/astro/src/core/sync/setup-env-ts.ts +++ b/packages/astro/src/core/sync/setup-env-ts.ts @@ -4,10 +4,8 @@ import { fileURLToPath } from 'node:url'; import { bold } from 'kleur/colors'; import { normalizePath } from 'vite'; import type { AstroSettings } from '../../@types/astro.js'; -import { ACTIONS_TYPES_FILE } from '../../actions/consts.js'; -import { CONTENT_TYPES_FILE } from '../../content/consts.js'; -import { ENV_TYPES_FILE } from '../../env/constants.js'; -import { type Logger } from '../logger/core.js'; +import type { Logger } from '../logger/core.js'; +import { REFERENCE_FILE } from './constants.js'; function getDotAstroTypeReference({ settings, @@ -23,8 +21,6 @@ function getDotAstroTypeReference({ return `/// `; } -type InjectedType = { filename: string; meetsCondition?: () => boolean | Promise }; - export async function setUpEnvTs({ settings, logger, @@ -38,38 +34,18 @@ export async function setUpEnvTs({ const envTsPathRelativetoRoot = normalizePath( path.relative(fileURLToPath(settings.config.root), fileURLToPath(envTsPath)) ); - - const injectedTypes: Array = [ - { - filename: CONTENT_TYPES_FILE, - meetsCondition: () => fs.existsSync(new URL(CONTENT_TYPES_FILE, settings.dotAstroDir)), - }, - { - filename: ACTIONS_TYPES_FILE, - meetsCondition: () => fs.existsSync(new URL(ACTIONS_TYPES_FILE, settings.dotAstroDir)), - }, - ]; - if (settings.config.experimental.env) { - injectedTypes.push({ - filename: ENV_TYPES_FILE, - }); - } + const expectedTypeReference = getDotAstroTypeReference({ + settings, + filename: REFERENCE_FILE, + }); if (fs.existsSync(envTsPath)) { const initialEnvContents = await fs.promises.readFile(envTsPath, 'utf-8'); let typesEnvContents = initialEnvContents; - for (const injectedType of injectedTypes) { - if (!injectedType.meetsCondition || (await injectedType.meetsCondition?.())) { - const expectedTypeReference = getDotAstroTypeReference({ - settings, - filename: injectedType.filename, - }); - if (!typesEnvContents.includes(expectedTypeReference)) { - typesEnvContents = `${expectedTypeReference}\n${typesEnvContents}`; - } - } + if (!typesEnvContents.includes(expectedTypeReference)) { + typesEnvContents = `${expectedTypeReference}\n${typesEnvContents}`; } if (initialEnvContents !== typesEnvContents) { @@ -78,17 +54,8 @@ export async function setUpEnvTs({ } } else { // Otherwise, inject the `env.d.ts` file - let referenceDefs: string[] = []; - referenceDefs.push('/// '); - - for (const injectedType of injectedTypes) { - if (!injectedType.meetsCondition || (await injectedType.meetsCondition?.())) { - referenceDefs.push(getDotAstroTypeReference({ settings, filename: injectedType.filename })); - } - } - await fs.promises.mkdir(settings.config.srcDir, { recursive: true }); - await fs.promises.writeFile(envTsPath, referenceDefs.join('\n'), 'utf-8'); + await fs.promises.writeFile(envTsPath, expectedTypeReference, 'utf-8'); logger.info('types', `Added ${bold(envTsPathRelativetoRoot)} type declarations`); } } diff --git a/packages/astro/src/env/sync.ts b/packages/astro/src/env/sync.ts index 9ba11469ad7a..d80b73089a7f 100644 --- a/packages/astro/src/env/sync.ts +++ b/packages/astro/src/env/sync.ts @@ -1,11 +1,11 @@ import fsMod from 'node:fs'; import type { AstroSettings } from '../@types/astro.js'; -import { ENV_TYPES_FILE, TYPES_TEMPLATE_URL } from './constants.js'; +import { TYPES_TEMPLATE_URL } from './constants.js'; import { getEnvFieldType } from './validators.js'; -export function syncAstroEnv(settings: AstroSettings, fs = fsMod) { +export function syncAstroEnv(settings: AstroSettings, fs = fsMod): AstroSettings { if (!settings.config.experimental.env) { - return; + return settings; } const schema = settings.config.experimental.env.schema ?? {}; @@ -25,6 +25,10 @@ export function syncAstroEnv(settings: AstroSettings, fs = fsMod) { const template = fs.readFileSync(TYPES_TEMPLATE_URL, 'utf-8'); const dts = template.replace('// @@CLIENT@@', client).replace('// @@SERVER@@', server); - fs.mkdirSync(settings.dotAstroDir, { recursive: true }); - fs.writeFileSync(new URL(ENV_TYPES_FILE, settings.dotAstroDir), dts, 'utf-8'); + settings.injectedTypes.push({ + filename: 'astro/env.d.ts', + content: dts, + }); + + return settings; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2490205c4990..c22e972e8d58 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -127,6 +127,9 @@ importers: examples/basics: dependencies: + '@astrojs/node': + specifier: ^8.3.2 + version: link:../../packages/integrations/node astro: specifier: ^4.12.2 version: link:../../packages/astro @@ -9420,6 +9423,7 @@ packages: libsql@0.3.12: resolution: {integrity: sha512-to30hj8O3DjS97wpbKN6ERZ8k66MN1IaOfFLR6oHqd25GMiPJ/ZX0VaZ7w+TsPmxcFS3p71qArj/hiedCyvXCg==} + cpu: [x64, arm64, wasm32] os: [darwin, linux, win32] lilconfig@2.1.0: From b2c14e9cd51344842ceca2ac765ee5c1ca9babb7 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Thu, 25 Jul 2024 15:22:25 +0200 Subject: [PATCH 06/44] feat: migrate db to injectTypes --- packages/astro/src/core/sync/index.ts | 20 +---------------- packages/db/src/core/integration/index.ts | 9 +++++--- packages/db/src/core/integration/typegen.ts | 24 +++------------------ packages/db/src/index.ts | 1 - 4 files changed, 10 insertions(+), 44 deletions(-) diff --git a/packages/astro/src/core/sync/index.ts b/packages/astro/src/core/sync/index.ts index 1261f7259bae..1ff8001fcfec 100644 --- a/packages/astro/src/core/sync/index.ts +++ b/packages/astro/src/core/sync/index.ts @@ -3,8 +3,7 @@ import { performance } from 'node:perf_hooks'; import { fileURLToPath } from 'node:url'; import { dim } from 'kleur/colors'; import { type HMRPayload, createServer, normalizePath } from 'vite'; -import type { AstroConfig, AstroInlineConfig, AstroSettings } from '../../@types/astro.js'; -import { getPackage } from '../../cli/install-package.js'; +import type { AstroInlineConfig, AstroSettings } from '../../@types/astro.js'; import { createContentTypesGenerator } from '../../content/index.js'; import { globalContentConfigObserver } from '../../content/utils.js'; import { syncAstroEnv } from '../../env/sync.js'; @@ -46,10 +45,6 @@ export type SyncOptions = { }; }; -type DBPackage = { - typegen?: (args: Pick) => Promise; -}; - export default async function sync( inlineConfig: AstroInlineConfig, { fs, telemetry: _telemetry = false }: { fs?: typeof fsMod; telemetry?: boolean } = {} @@ -82,22 +77,9 @@ export async function syncInternal({ settings, skip, }: SyncOptions): Promise { - const cwd = fileURLToPath(settings.config.root); - const timerStart = performance.now(); - const dbPackage = await getPackage( - '@astrojs/db', - logger, - { - optional: true, - cwd, - }, - [] - ); try { - // TODO: remove and use injectTypes instead - await dbPackage?.typegen?.(settings.config); if (!skip?.content) { await syncContentCollections(settings, { fs, logger }); } diff --git a/packages/db/src/core/integration/index.ts b/packages/db/src/core/integration/index.ts index 9be71956f212..3b93f82fac90 100644 --- a/packages/db/src/core/integration/index.ts +++ b/packages/db/src/core/integration/index.ts @@ -22,7 +22,7 @@ import { resolveDbConfig } from '../load-file.js'; import { SEED_DEV_FILE_NAME } from '../queries.js'; import { type VitePlugin, getDbDirectoryUrl } from '../utils.js'; import { fileURLIntegration } from './file-url.js'; -import { typegenInternal } from './typegen.js'; +import { getDtsContent } from './typegen.js'; import { type LateSeedFiles, type LateTables, @@ -106,7 +106,7 @@ function astroDBIntegration(): AstroIntegration { }, }); }, - 'astro:config:done': async ({ config }) => { + 'astro:config:done': async ({ config, injectTypes }) => { if (command === 'preview') return; // TODO: refine where we load tables @@ -122,7 +122,10 @@ function astroDBIntegration(): AstroIntegration { await writeFile(localDbUrl, ''); } - await typegenInternal({ tables: tables.get() ?? {}, root: config.root }); + injectTypes({ + filename: 'types.d.ts', + content: getDtsContent(tables.get() ?? {}), + }); }, 'astro:server:setup': async ({ server, logger }) => { seedHandler.execute = async (fileUrl) => { diff --git a/packages/db/src/core/integration/typegen.ts b/packages/db/src/core/integration/typegen.ts index 725738fc5a51..91364b3c3dc1 100644 --- a/packages/db/src/core/integration/typegen.ts +++ b/packages/db/src/core/integration/typegen.ts @@ -1,18 +1,7 @@ -import { existsSync } from 'node:fs'; -import { mkdir, writeFile } from 'node:fs/promises'; -import type { AstroConfig } from 'astro'; -import { DB_TYPES_FILE, RUNTIME_IMPORT } from '../consts.js'; -import { resolveDbConfig } from '../load-file.js'; +import { RUNTIME_IMPORT } from '../consts.js'; import type { DBTable, DBTables } from '../types.js'; -// Exported for use in Astro core CLI -export async function typegen(astroConfig: Pick) { - const { dbConfig } = await resolveDbConfig(astroConfig); - - await typegenInternal({ tables: dbConfig.tables, root: astroConfig.root }); -} - -export async function typegenInternal({ tables, root }: { tables: DBTables; root: URL }) { +export function getDtsContent(tables: DBTables) { const content = `// This file is generated by Astro DB declare module 'astro:db' { ${Object.entries(tables) @@ -20,14 +9,7 @@ ${Object.entries(tables) .join('\n')} } `; - - const dotAstroDir = new URL('.astro/', root); - - if (!existsSync(dotAstroDir)) { - await mkdir(dotAstroDir); - } - - await writeFile(new URL(DB_TYPES_FILE, dotAstroDir), content); + return content; } function generateTableType(name: string, table: DBTable): string { diff --git a/packages/db/src/index.ts b/packages/db/src/index.ts index b26507130471..f7022a24a236 100644 --- a/packages/db/src/index.ts +++ b/packages/db/src/index.ts @@ -1,4 +1,3 @@ export type { TableConfig } from './core/types.js'; export { cli } from './core/cli/index.js'; export { integration as default } from './core/integration/index.js'; -export { typegen } from './core/integration/typegen.js'; From 04b828c7c00669558ab6687b18a839602f49acf6 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Thu, 25 Jul 2024 15:26:52 +0200 Subject: [PATCH 07/44] feat: special db handling --- packages/astro/src/core/sync/index.ts | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/packages/astro/src/core/sync/index.ts b/packages/astro/src/core/sync/index.ts index 1ff8001fcfec..2c8b7711001f 100644 --- a/packages/astro/src/core/sync/index.ts +++ b/packages/astro/src/core/sync/index.ts @@ -100,6 +100,18 @@ export async function syncInternal({ } } +function handleFilename(filename: string) { + // TODO: reuse logic + if (filename === `./integrations/${VIRTUAL_MODULE_ID.replace(':', '_')}/${ACTIONS_TYPES_FILE}`) { + return `./astro/${ACTIONS_TYPES_FILE}`; + } + if (filename === `./integrations/astro_db/db-types.d.ts`) { + return './astro/db.d.ts' + } + + return filename +} + function writeInjectedTypes(settings: AstroSettings, fs: typeof fsMod) { const references: Array = []; @@ -108,12 +120,7 @@ function writeInjectedTypes(settings: AstroSettings, fs: typeof fsMod) { } for (const { filename, content } of settings.injectedTypes) { - // TODO: reuse logic - const newFilename = - filename === `./integrations/${VIRTUAL_MODULE_ID.replace(':', '_')}/${ACTIONS_TYPES_FILE}` - ? `./astro/${ACTIONS_TYPES_FILE}` - : filename; - const path = fileURLToPath(new URL(newFilename, settings.dotAstroDir)); + const path = fileURLToPath(new URL(handleFilename(filename), settings.dotAstroDir)); fs.mkdirSync(dirname(path), { recursive: true }); // TODO: format content using recast fs.writeFileSync(path, content, 'utf-8'); From dc53d1a15539768edbeebcb9329b71139d7165e3 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Fri, 26 Jul 2024 12:25:54 +0200 Subject: [PATCH 08/44] feat: update settings instead of workarounds --- packages/astro/src/actions/consts.ts | 2 +- packages/astro/src/actions/index.ts | 2 +- packages/astro/src/content/types-generator.ts | 8 ++++---- packages/astro/src/core/sync/index.ts | 10 +--------- 4 files changed, 7 insertions(+), 15 deletions(-) diff --git a/packages/astro/src/actions/consts.ts b/packages/astro/src/actions/consts.ts index e1324f248d19..37d8c97521de 100644 --- a/packages/astro/src/actions/consts.ts +++ b/packages/astro/src/actions/consts.ts @@ -1,6 +1,6 @@ export const VIRTUAL_MODULE_ID = 'astro:actions'; export const RESOLVED_VIRTUAL_MODULE_ID = '\0' + VIRTUAL_MODULE_ID; -export const ACTIONS_TYPES_FILE = 'actions.d.ts'; +export const ACTIONS_TYPES_FILE = 'astro/actions.d.ts'; export const VIRTUAL_INTERNAL_MODULE_ID = 'astro:internal-actions'; export const RESOLVED_VIRTUAL_INTERNAL_MODULE_ID = '\0astro:internal-actions'; export const NOOP_ACTIONS = '\0noop-actions'; diff --git a/packages/astro/src/actions/index.ts b/packages/astro/src/actions/index.ts index 2f0469359838..29822670b400 100644 --- a/packages/astro/src/actions/index.ts +++ b/packages/astro/src/actions/index.ts @@ -51,7 +51,7 @@ export default function astroActions({ const stringifiedActionsImport = JSON.stringify( viteID(new URL('./actions', params.config.srcDir)) ); - params.injectTypes({ + settings.injectedTypes.push({ filename: ACTIONS_TYPES_FILE, content: `declare module "astro:actions" { type Actions = typeof import(${stringifiedActionsImport})["server"]; diff --git a/packages/astro/src/content/types-generator.ts b/packages/astro/src/content/types-generator.ts index 33cb5b48cdd4..94677bb09898 100644 --- a/packages/astro/src/content/types-generator.ts +++ b/packages/astro/src/content/types-generator.ts @@ -515,8 +515,8 @@ async function writeContentFiles({ contentConfig ? `typeof import(${configPathRelativeToCacheDir})` : 'never' ); - const filepath = new URL(CONTENT_TYPES_FILE, settings.dotAstroDir); - - await fs.promises.mkdir(path.dirname(fileURLToPath(filepath)), { recursive: true }); - await fs.promises.writeFile(filepath, typeTemplateContent); + settings.injectedTypes.push({ + filename: CONTENT_TYPES_FILE, + content: typeTemplateContent, + }); } diff --git a/packages/astro/src/core/sync/index.ts b/packages/astro/src/core/sync/index.ts index 2c8b7711001f..9b88b29b955b 100644 --- a/packages/astro/src/core/sync/index.ts +++ b/packages/astro/src/core/sync/index.ts @@ -28,8 +28,6 @@ import { formatErrorMessage } from '../messages.js'; import { ensureProcessNodeEnv } from '../util.js'; import { setUpEnvTs } from './setup-env-ts.js'; import { dirname, relative } from 'node:path'; -import { CONTENT_TYPES_FILE } from '../../content/consts.js'; -import { ACTIONS_TYPES_FILE, VIRTUAL_MODULE_ID } from '../../actions/consts.js'; import { REFERENCE_FILE } from './constants.js'; export type SyncOptions = { @@ -102,9 +100,6 @@ export async function syncInternal({ function handleFilename(filename: string) { // TODO: reuse logic - if (filename === `./integrations/${VIRTUAL_MODULE_ID.replace(':', '_')}/${ACTIONS_TYPES_FILE}`) { - return `./astro/${ACTIONS_TYPES_FILE}`; - } if (filename === `./integrations/astro_db/db-types.d.ts`) { return './astro/db.d.ts' } @@ -112,13 +107,10 @@ function handleFilename(filename: string) { return filename } +// TODO: move to other file function writeInjectedTypes(settings: AstroSettings, fs: typeof fsMod) { const references: Array = []; - if (fs.existsSync(new URL(CONTENT_TYPES_FILE, settings.dotAstroDir))) { - references.push(CONTENT_TYPES_FILE); - } - for (const { filename, content } of settings.injectedTypes) { const path = fileURLToPath(new URL(handleFilename(filename), settings.dotAstroDir)); fs.mkdirSync(dirname(path), { recursive: true }); From adeeaa2cd05c030899aeee2aabb446b7785f59c2 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Fri, 26 Jul 2024 12:43:09 +0200 Subject: [PATCH 09/44] fix: create dotAstroDir --- packages/astro/src/core/sync/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/astro/src/core/sync/index.ts b/packages/astro/src/core/sync/index.ts index 9b88b29b955b..7c969a927795 100644 --- a/packages/astro/src/core/sync/index.ts +++ b/packages/astro/src/core/sync/index.ts @@ -118,8 +118,9 @@ function writeInjectedTypes(settings: AstroSettings, fs: typeof fsMod) { fs.writeFileSync(path, content, 'utf-8'); references.push(normalizePath(relative(fileURLToPath(settings.dotAstroDir), path))); } - + const astroDtsContent = `/// \n${references.map((reference) => `/// `).join('\n')}`; + fs.mkdirSync(settings.dotAstroDir, { recursive: true }); fs.writeFileSync(new URL(REFERENCE_FILE, settings.dotAstroDir), astroDtsContent, 'utf-8'); } From bf1686bff8eeaa6e6d32f32e893d6b5ec2444a44 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Fri, 26 Jul 2024 15:19:33 +0200 Subject: [PATCH 10/44] feat: refactor sync tests --- packages/astro/src/core/sync/index.ts | 16 ++- packages/astro/test/astro-sync.test.js | 187 +++++++++++++++---------- packages/astro/test/test-utils.js | 2 + 3 files changed, 125 insertions(+), 80 deletions(-) diff --git a/packages/astro/src/core/sync/index.ts b/packages/astro/src/core/sync/index.ts index 7c969a927795..224d7e55c554 100644 --- a/packages/astro/src/core/sync/index.ts +++ b/packages/astro/src/core/sync/index.ts @@ -101,10 +101,10 @@ export async function syncInternal({ function handleFilename(filename: string) { // TODO: reuse logic if (filename === `./integrations/astro_db/db-types.d.ts`) { - return './astro/db.d.ts' + return './astro/db.d.ts'; } - return filename + return filename; } // TODO: move to other file @@ -112,15 +112,17 @@ function writeInjectedTypes(settings: AstroSettings, fs: typeof fsMod) { const references: Array = []; for (const { filename, content } of settings.injectedTypes) { - const path = fileURLToPath(new URL(handleFilename(filename), settings.dotAstroDir)); - fs.mkdirSync(dirname(path), { recursive: true }); + const path = new URL(handleFilename(filename), settings.dotAstroDir); + fs.mkdirSync(dirname(normalizePath(fileURLToPath(path))), { recursive: true }); // TODO: format content using recast fs.writeFileSync(path, content, 'utf-8'); - references.push(normalizePath(relative(fileURLToPath(settings.dotAstroDir), path))); + references.push(normalizePath(relative(fileURLToPath(settings.dotAstroDir), fileURLToPath(path)))); } - + const astroDtsContent = `/// \n${references.map((reference) => `/// `).join('\n')}`; - fs.mkdirSync(settings.dotAstroDir, { recursive: true }); + if (references.length === 0) { + fs.mkdirSync(settings.dotAstroDir, { recursive: true }); + } fs.writeFileSync(new URL(REFERENCE_FILE, settings.dotAstroDir), astroDtsContent, 'utf-8'); } diff --git a/packages/astro/test/astro-sync.test.js b/packages/astro/test/astro-sync.test.js index 8d47e51205f3..d32c3f9e1830 100644 --- a/packages/astro/test/astro-sync.test.js +++ b/packages/astro/test/astro-sync.test.js @@ -1,6 +1,7 @@ +// @ts-check import assert from 'node:assert/strict'; import * as fs from 'node:fs'; -import { before, describe, it } from 'node:test'; +import { beforeEach, describe, it } from 'node:test'; import { fileURLToPath } from 'node:url'; import ts from 'typescript'; import { loadFixture } from './test-utils.js'; @@ -13,44 +14,44 @@ const createFixture = () => { return { /** @param {string} root */ - async whenSyncing(root) { + async load(root) { astroFixture = await loadFixture({ root }); - - const envPath = new URL('env.d.ts', astroFixture.config.srcDir).href; - const typesDtsPath = new URL('.astro/types.d.ts', astroFixture.config.root).href; - + }, + clean() { + const envPath = new URL('env.d.ts', astroFixture.config.srcDir); + if (fs.existsSync(envPath)) { + fs.unlinkSync(new URL('env.d.ts', astroFixture.config.srcDir)); + } + fs.rmSync(new URL('./.astro/', astroFixture.config.root), { force: true, recursive: true }); + }, + async whenSyncing() { const fsMock = { ...fs, - existsSync(path, ...args) { - if (path.toString() === envPath) { - return false; - } - if (path.toString() === typesDtsPath) { - return true; - } - return fs.existsSync(path, ...args); - }, + /** + * @param {fs.PathLike} path + * @param {string} contents + */ writeFileSync(path, contents) { writtenFiles[path.toString()] = contents; + return fs.writeFileSync(path, contents); }, promises: { ...fs.promises, - async readFile(path, ...args) { - if (path.toString() === envPath) { - return `/// `; - } else { - return fs.promises.readFile(path, ...args); - } - }, - async writeFile(path, contents) { + /** + * @param {fs.PathLike} path + * @param {string} contents + */ + writeFile(path, contents) { writtenFiles[path.toString()] = contents; + return fs.promises.writeFile(path, contents); }, }, }; await astroFixture.sync( - { root: fileURLToPath(new URL(root, import.meta.url)) }, + { root: fileURLToPath(astroFixture.config.root) }, { + // @ts-ignore fs: fsMock, } ); @@ -65,10 +66,13 @@ const createFixture = () => { * @param {string} content * @param {string | undefined} error */ - thenFileContentShouldInclude(path, content, error) { + thenFileContentShouldInclude(path, content, error = undefined) { const expectedPath = new URL(path, astroFixture.config.root).href; assert.equal(writtenFiles[expectedPath].includes(content), true, error); }, + /** + * @param {string} path + */ thenFileShouldBeValidTypescript(path) { const expectedPath = new URL(path, astroFixture.config.root).href; try { @@ -93,33 +97,75 @@ const createFixture = () => { describe('astro sync', () => { /** @type {ReturnType} */ let fixture; - before(async () => { + beforeEach(async () => { fixture = createFixture(); }); - it('Writes `src/env.d.ts` if none exists', async () => { - await fixture.whenSyncing('./fixtures/astro-basic/'); - fixture.thenFileShouldExist('src/env.d.ts'); - fixture.thenFileContentShouldInclude('src/env.d.ts', `/// `); + describe('References', () => { + it('Writes `src/env.d.ts` if none exists', async () => { + await fixture.load('./fixtures/astro-basic/'); + fixture.clean(); + await fixture.whenSyncing(); + fixture.thenFileShouldExist('src/env.d.ts'); + fixture.thenFileContentShouldInclude( + 'src/env.d.ts', + `/// ` + ); + }); + + it('Updates `src/env.d.ts` if one exists', async () => { + await fixture.load('./fixtures/astro-basic/'); + fixture.clean(); + fs.writeFileSync( + new URL('./fixtures/astro-basics/src/env.d.ts', import.meta.url), + '// whatever', + 'utf-8' + ); + await fixture.whenSyncing(); + fixture.thenFileShouldExist('src/env.d.ts'); + fixture.thenFileContentShouldInclude( + 'src/env.d.ts', + `/// ` + ); + }); + + it('Writes `src/types.d.ts`', async () => { + await fixture.load('./fixtures/astro-basic/'); + fixture.clean(); + await fixture.whenSyncing(); + fixture.thenFileShouldExist('.astro/types.d.ts'); + fixture.thenFileContentShouldInclude( + '.astro/types.d.ts', + `/// ` + ); + }); }); describe('Content collections', () => { - it('Writes types to `.astro`', async () => { - await fixture.whenSyncing('./fixtures/content-collections/'); + it('Adds reference to `.astro/types.d.ts`', async () => { + await fixture.load('./fixtures/content-collections/'); + fixture.clean(); + await fixture.whenSyncing(); fixture.thenFileShouldExist('.astro/types.d.ts'); fixture.thenFileContentShouldInclude( '.astro/types.d.ts', + `/// ` + ); + fixture.thenFileShouldExist('.astro/astro/content.d.ts'); + fixture.thenFileContentShouldInclude( + '.astro/astro/content.d.ts', `declare module 'astro:content' {`, 'Types file does not include `astro:content` module declaration' ); - fixture.thenFileShouldBeValidTypescript('.astro/types.d.ts'); + fixture.thenFileShouldBeValidTypescript('.astro/astro/content.d.ts'); }); it('Writes types for empty collections', async () => { - await fixture.whenSyncing('./fixtures/content-collections-empty-dir/'); - fixture.thenFileShouldExist('.astro/types.d.ts'); + await fixture.load('./fixtures/content-collections-empty-dir/'); + fixture.clean(); + await fixture.whenSyncing(); fixture.thenFileContentShouldInclude( - '.astro/types.d.ts', + '.astro/astro/content.d.ts', `"blog": Record { 'Types file does not include empty collection type' ); fixture.thenFileContentShouldInclude( - '.astro/types.d.ts', + '.astro/astro/content.d.ts', `"blogMeta": Record { 'Types file does not include empty collection type' ); }); + }); - it('Adds type reference to `src/env.d.ts`', async () => { - await fixture.whenSyncing('./fixtures/content-collections/'); - fixture.thenFileShouldExist('src/env.d.ts'); + describe('astro:env', () => { + it('Adds reference to `.astro/types.d.ts`', async () => { + await fixture.load('./fixtures/astro-env/'); + fixture.clean(); + await fixture.whenSyncing(); + fixture.thenFileShouldExist('.astro/types.d.ts'); fixture.thenFileContentShouldInclude( - 'src/env.d.ts', - `/// ` + '.astro/types.d.ts', + `/// ` ); - }); - }); - - describe('Astro Env', () => { - it('Writes types to `.astro`', async () => { - await fixture.whenSyncing('./fixtures/astro-env/'); - fixture.thenFileShouldExist('.astro/env.d.ts'); + fixture.thenFileShouldExist('.astro/astro/env.d.ts'); fixture.thenFileContentShouldInclude( - '.astro/env.d.ts', + '.astro/astro/env.d.ts', `declare module 'astro:env/client' {`, 'Types file does not include `astro:env` module declaration' ); }); - it('Adds type reference to `src/env.d.ts`', async () => { - await fixture.whenSyncing('./fixtures/astro-env/'); - fixture.thenFileShouldExist('src/env.d.ts'); - fixture.thenFileContentShouldInclude( - 'src/env.d.ts', - `/// ` - ); - }); - it('Does not throw if a public variable is required', async () => { - let error = null; try { - await fixture.whenSyncing('./fixtures/astro-env-required-public/'); + await fixture.load('./fixtures/astro-env-required-public/'); + fixture.clean(); + await fixture.whenSyncing(); + assert.ok(true); } catch (e) { - error = e; + assert.fail(); } - - assert.equal(error, null, 'Syncing should not throw astro:env validation errors'); }); }); - describe('Astro Actions', () => { - // We can't check for the file existence or content yet because - // it's an integration and does not use the fs mock - - it('Adds type reference to `src/env.d.ts`', async () => { - await fixture.whenSyncing('./fixtures/actions/'); - fixture.thenFileShouldExist('src/env.d.ts'); + describe('astro:actions', () => { + it('Adds reference to `.astro/types.d.ts`', async () => { + await fixture.load('./fixtures/actions/'); + fixture.clean(); + await fixture.whenSyncing(); + fixture.thenFileShouldExist('.astro/types.d.ts'); fixture.thenFileContentShouldInclude( - 'src/env.d.ts', - `/// ` + '.astro/types.d.ts', + `/// ` + ); + fixture.thenFileShouldExist('.astro/astro/actions.d.ts'); + fixture.thenFileContentShouldInclude( + '.astro/astro/actions.d.ts', + `declare module "astro:actions" {`, + 'Types file does not include `astro:actions` module declaration' ); + fixture.thenFileShouldBeValidTypescript('.astro/astro/actions.d.ts'); }); }); }); diff --git a/packages/astro/test/test-utils.js b/packages/astro/test/test-utils.js index 025fe63359e6..3d54c3570f04 100644 --- a/packages/astro/test/test-utils.js +++ b/packages/astro/test/test-utils.js @@ -22,6 +22,7 @@ process.env.ASTRO_TELEMETRY_DISABLED = true; /** * @typedef {import('../src/core/dev/dev').DevServer} DevServer * @typedef {import('../src/@types/astro').AstroInlineConfig & { root?: string | URL }} AstroInlineConfig + * @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 @@ -47,6 +48,7 @@ process.env.ASTRO_TELEMETRY_DISABLED = true; * @property {() => Promise} onNextChange * @property {typeof check} check * @property {typeof sync} sync + * @property {AstroConfig} config * * This function returns an instance of the Check * From 9fe686e82cfd966095b636796f41984b15f5e07f Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Fri, 26 Jul 2024 17:47:23 +0200 Subject: [PATCH 11/44] fix: path --- packages/astro/test/astro-sync.test.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/astro/test/astro-sync.test.js b/packages/astro/test/astro-sync.test.js index d32c3f9e1830..61870c472189 100644 --- a/packages/astro/test/astro-sync.test.js +++ b/packages/astro/test/astro-sync.test.js @@ -16,6 +16,7 @@ const createFixture = () => { /** @param {string} root */ async load(root) { astroFixture = await loadFixture({ root }); + return astroFixture.config; }, clean() { const envPath = new URL('env.d.ts', astroFixture.config.srcDir); @@ -114,10 +115,10 @@ describe('astro sync', () => { }); it('Updates `src/env.d.ts` if one exists', async () => { - await fixture.load('./fixtures/astro-basic/'); + const config = await fixture.load('./fixtures/astro-basic/'); fixture.clean(); fs.writeFileSync( - new URL('./fixtures/astro-basics/src/env.d.ts', import.meta.url), + new URL('./env.d.ts', config.srcDir), '// whatever', 'utf-8' ); From f3f02f7cba2db380d63c7b48c9e2e226ca5bbe9d Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Fri, 26 Jul 2024 18:24:29 +0200 Subject: [PATCH 12/44] fix: paths --- packages/astro/src/core/sync/index.ts | 10 +++++---- packages/astro/src/core/sync/setup-env-ts.ts | 4 ++-- packages/astro/test/astro-sync.test.js | 22 +++++++++---------- .../collections-mixed-content-errors.test.js | 8 ++----- 4 files changed, 21 insertions(+), 23 deletions(-) diff --git a/packages/astro/src/core/sync/index.ts b/packages/astro/src/core/sync/index.ts index 224d7e55c554..7f6c4fc3ad7f 100644 --- a/packages/astro/src/core/sync/index.ts +++ b/packages/astro/src/core/sync/index.ts @@ -112,18 +112,20 @@ function writeInjectedTypes(settings: AstroSettings, fs: typeof fsMod) { const references: Array = []; for (const { filename, content } of settings.injectedTypes) { - const path = new URL(handleFilename(filename), settings.dotAstroDir); - fs.mkdirSync(dirname(normalizePath(fileURLToPath(path))), { recursive: true }); + const path = normalizePath( + fileURLToPath(new URL(handleFilename(filename), settings.dotAstroDir)) + ); + fs.mkdirSync(dirname(path), { recursive: true }); // TODO: format content using recast fs.writeFileSync(path, content, 'utf-8'); - references.push(normalizePath(relative(fileURLToPath(settings.dotAstroDir), fileURLToPath(path)))); + references.push(normalizePath(relative(fileURLToPath(settings.dotAstroDir), path))); } const astroDtsContent = `/// \n${references.map((reference) => `/// `).join('\n')}`; if (references.length === 0) { fs.mkdirSync(settings.dotAstroDir, { recursive: true }); } - fs.writeFileSync(new URL(REFERENCE_FILE, settings.dotAstroDir), astroDtsContent, 'utf-8'); + fs.writeFileSync(normalizePath(fileURLToPath(new URL(REFERENCE_FILE, settings.dotAstroDir))), astroDtsContent, 'utf-8'); } /** diff --git a/packages/astro/src/core/sync/setup-env-ts.ts b/packages/astro/src/core/sync/setup-env-ts.ts index a24bfccae2a5..66f45e5c252b 100644 --- a/packages/astro/src/core/sync/setup-env-ts.ts +++ b/packages/astro/src/core/sync/setup-env-ts.ts @@ -30,9 +30,9 @@ export async function setUpEnvTs({ logger: Logger; fs: typeof fsMod; }) { - const envTsPath = new URL('env.d.ts', settings.config.srcDir); + const envTsPath = normalizePath(fileURLToPath(new URL('env.d.ts', settings.config.srcDir))); const envTsPathRelativetoRoot = normalizePath( - path.relative(fileURLToPath(settings.config.root), fileURLToPath(envTsPath)) + path.relative(fileURLToPath(settings.config.root), envTsPath) ); const expectedTypeReference = getDotAstroTypeReference({ settings, diff --git a/packages/astro/test/astro-sync.test.js b/packages/astro/test/astro-sync.test.js index 61870c472189..a367d06d6054 100644 --- a/packages/astro/test/astro-sync.test.js +++ b/packages/astro/test/astro-sync.test.js @@ -5,6 +5,7 @@ import { beforeEach, describe, it } from 'node:test'; import { fileURLToPath } from 'node:url'; import ts from 'typescript'; import { loadFixture } from './test-utils.js'; +import { normalizePath } from 'vite'; const createFixture = () => { /** @type {Awaited>} */ @@ -12,6 +13,12 @@ const createFixture = () => { /** @type {Record} */ const writtenFiles = {}; + /** + * @param {string} path + */ + const getExpectedPath = (path) => + normalizePath(fileURLToPath(new URL(path, astroFixture.config.root))); + return { /** @param {string} root */ async load(root) { @@ -59,8 +66,7 @@ const createFixture = () => { }, /** @param {string} path */ thenFileShouldExist(path) { - const expectedPath = new URL(path, astroFixture.config.root).href; - assert.equal(writtenFiles.hasOwnProperty(expectedPath), true, `${path} does not exist`); + assert.equal(writtenFiles.hasOwnProperty(getExpectedPath(path)), true, `${path} does not exist`); }, /** * @param {string} path @@ -68,16 +74,14 @@ const createFixture = () => { * @param {string | undefined} error */ thenFileContentShouldInclude(path, content, error = undefined) { - const expectedPath = new URL(path, astroFixture.config.root).href; - assert.equal(writtenFiles[expectedPath].includes(content), true, error); + assert.equal(writtenFiles[getExpectedPath(path)].includes(content), true, error); }, /** * @param {string} path */ thenFileShouldBeValidTypescript(path) { - const expectedPath = new URL(path, astroFixture.config.root).href; try { - const content = writtenFiles[expectedPath]; + const content = writtenFiles[getExpectedPath(path)]; const result = ts.transpileModule(content, { compilerOptions: { module: ts.ModuleKind.ESNext, @@ -117,11 +121,7 @@ describe('astro sync', () => { it('Updates `src/env.d.ts` if one exists', async () => { const config = await fixture.load('./fixtures/astro-basic/'); fixture.clean(); - fs.writeFileSync( - new URL('./env.d.ts', config.srcDir), - '// whatever', - 'utf-8' - ); + fs.writeFileSync(new URL('./env.d.ts', config.srcDir), '// whatever', 'utf-8'); await fixture.whenSyncing(); fixture.thenFileShouldExist('src/env.d.ts'); fixture.thenFileContentShouldInclude( diff --git a/packages/astro/test/units/dev/collections-mixed-content-errors.test.js b/packages/astro/test/units/dev/collections-mixed-content-errors.test.js index 379670babef1..230148c66288 100644 --- a/packages/astro/test/units/dev/collections-mixed-content-errors.test.js +++ b/packages/astro/test/units/dev/collections-mixed-content-errors.test.js @@ -123,11 +123,7 @@ title: Post root ); - try { - const res = await sync({ fs }); - assert.equal(res, 0); - } catch (e) { - assert.fail(`Did not expect sync to throw: ${e.message}`); - } + const res = await sync({ fs }); + assert.equal(res, 0); }); }); From c08bff95875fadcbb91f3fea7c9e8b0842f10340 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Fri, 26 Jul 2024 18:28:42 +0200 Subject: [PATCH 13/44] chore: add comments --- packages/astro/src/core/dev/restart.ts | 1 + packages/astro/src/preferences/index.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/astro/src/core/dev/restart.ts b/packages/astro/src/core/dev/restart.ts index 36ad701fb505..4416fd9e8802 100644 --- a/packages/astro/src/core/dev/restart.ts +++ b/packages/astro/src/core/dev/restart.ts @@ -30,6 +30,7 @@ async function createRestartedContainer( } const configRE = /.*astro.config.(?:mjs|cjs|js|ts)$/; +// TODO: can we use dotAstroDir? const preferencesRE = /.*\.astro\/settings.json$/; export function shouldRestartContainer( diff --git a/packages/astro/src/preferences/index.ts b/packages/astro/src/preferences/index.ts index ef16d32fd032..99cfee17ff19 100644 --- a/packages/astro/src/preferences/index.ts +++ b/packages/astro/src/preferences/index.ts @@ -84,6 +84,7 @@ export function coerce(key: string, value: unknown) { export default function createPreferences(config: AstroConfig): AstroPreferences { const global = new PreferenceStore(getGlobalPreferenceDir()); + // TODO: use dotAstroDir const project = new PreferenceStore(fileURLToPath(new URL('./.astro/', config.root))); const stores: Record = { global, project }; From fe62fe5798e7323e55408089d0da4087bde5c4ae Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Mon, 5 Aug 2024 14:31:05 +0200 Subject: [PATCH 14/44] feat: overwrite content file if exists --- packages/astro/src/content/types-generator.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/astro/src/content/types-generator.ts b/packages/astro/src/content/types-generator.ts index 94677bb09898..0fb78488174d 100644 --- a/packages/astro/src/content/types-generator.ts +++ b/packages/astro/src/content/types-generator.ts @@ -515,8 +515,16 @@ async function writeContentFiles({ contentConfig ? `typeof import(${configPathRelativeToCacheDir})` : 'never' ); - settings.injectedTypes.push({ - filename: CONTENT_TYPES_FILE, - content: typeTemplateContent, - }); + if (settings.injectedTypes.some((t) => t.filename === CONTENT_TYPES_FILE)) { + fs.promises.writeFile( + new URL(CONTENT_TYPES_FILE, settings.dotAstroDir), + typeTemplateContent, + 'utf-8' + ); + } else { + settings.injectedTypes.push({ + filename: CONTENT_TYPES_FILE, + content: typeTemplateContent, + }); + } } From 4b89dbc7744da3655d988b31c1ad110e6aaffe98 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Mon, 5 Aug 2024 14:33:08 +0200 Subject: [PATCH 15/44] chore: remove unused db env related code --- packages/db/src/core/integration/index.ts | 3 +- .../integration/vite-plugin-inject-env-ts.ts | 65 ------------------- 2 files changed, 1 insertion(+), 67 deletions(-) delete mode 100644 packages/db/src/core/integration/vite-plugin-inject-env-ts.ts diff --git a/packages/db/src/core/integration/index.ts b/packages/db/src/core/integration/index.ts index 3b93f82fac90..3907d5d15afc 100644 --- a/packages/db/src/core/integration/index.ts +++ b/packages/db/src/core/integration/index.ts @@ -30,7 +30,6 @@ import { resolved, vitePluginDb, } from './vite-plugin-db.js'; -import { vitePluginInjectEnvTs } from './vite-plugin-inject-env-ts.js'; function astroDBIntegration(): AstroIntegration { let connectToStudio = false; @@ -102,7 +101,7 @@ function astroDBIntegration(): AstroIntegration { updateConfig({ vite: { assetsInclude: [DB_PATH], - plugins: [dbPlugin, vitePluginInjectEnvTs(config, logger)], + plugins: [dbPlugin], }, }); }, diff --git a/packages/db/src/core/integration/vite-plugin-inject-env-ts.ts b/packages/db/src/core/integration/vite-plugin-inject-env-ts.ts deleted file mode 100644 index 58faa704559a..000000000000 --- a/packages/db/src/core/integration/vite-plugin-inject-env-ts.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { existsSync } from 'node:fs'; -import { readFile, writeFile } from 'node:fs/promises'; -import path from 'node:path'; -import { fileURLToPath } from 'node:url'; -import type { AstroIntegrationLogger } from 'astro'; -import { bold } from 'kleur/colors'; -import { normalizePath } from 'vite'; -import { DB_TYPES_FILE } from '../consts.js'; -import type { VitePlugin } from '../utils.js'; - -export function vitePluginInjectEnvTs( - { srcDir, root }: { srcDir: URL; root: URL }, - logger: AstroIntegrationLogger -): VitePlugin { - return { - name: 'db-inject-env-ts', - enforce: 'post', - async config() { - await setUpEnvTs({ srcDir, root, logger }); - }, - }; -} - -export async function setUpEnvTs({ - srcDir, - root, - logger, -}: { - srcDir: URL; - root: URL; - logger: AstroIntegrationLogger; -}) { - const envTsPath = getEnvTsPath({ srcDir }); - const envTsPathRelativetoRoot = normalizePath( - path.relative(fileURLToPath(root), fileURLToPath(envTsPath)) - ); - - if (existsSync(envTsPath)) { - let typesEnvContents = await readFile(envTsPath, 'utf-8'); - const dotAstroDir = new URL('.astro/', root); - - if (!existsSync(dotAstroDir)) return; - - const dbTypeReference = getDBTypeReference({ srcDir, dotAstroDir }); - - if (!typesEnvContents.includes(dbTypeReference)) { - typesEnvContents = `${dbTypeReference}\n${typesEnvContents}`; - await writeFile(envTsPath, typesEnvContents, 'utf-8'); - logger.info(`Added ${bold(envTsPathRelativetoRoot)} types`); - } - } -} - -function getDBTypeReference({ srcDir, dotAstroDir }: { srcDir: URL; dotAstroDir: URL }) { - const dbTypesFile = new URL(DB_TYPES_FILE, dotAstroDir); - const contentTypesRelativeToSrcDir = normalizePath( - path.relative(fileURLToPath(srcDir), fileURLToPath(dbTypesFile)) - ); - - return `/// `; -} - -function getEnvTsPath({ srcDir }: { srcDir: URL }) { - return new URL('env.d.ts', srcDir); -} From d7df994c98395dba26e083bdaa043b4a9efada6e Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Mon, 5 Aug 2024 14:59:21 +0200 Subject: [PATCH 16/44] feat: use dotAstroDir for settings --- packages/astro/src/core/dev/restart.ts | 12 ++++----- packages/astro/test/units/dev/restart.test.js | 27 +++++++++++++++++++ 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/packages/astro/src/core/dev/restart.ts b/packages/astro/src/core/dev/restart.ts index 4416fd9e8802..fbad9af17b29 100644 --- a/packages/astro/src/core/dev/restart.ts +++ b/packages/astro/src/core/dev/restart.ts @@ -30,8 +30,6 @@ async function createRestartedContainer( } const configRE = /.*astro.config.(?:mjs|cjs|js|ts)$/; -// TODO: can we use dotAstroDir? -const preferencesRE = /.*\.astro\/settings.json$/; export function shouldRestartContainer( { settings, inlineConfig, restartInFlight }: Container, @@ -40,17 +38,19 @@ export function shouldRestartContainer( if (restartInFlight) return false; let shouldRestart = false; + const normalizedChangedFile = vite.normalizePath(changedFile); // If the config file changed, reload the config and restart the server. if (inlineConfig.configFile) { - shouldRestart = vite.normalizePath(inlineConfig.configFile) === vite.normalizePath(changedFile); + shouldRestart = vite.normalizePath(inlineConfig.configFile) === normalizedChangedFile; } // Otherwise, watch for any astro.config.* file changes in project root else { - const normalizedChangedFile = vite.normalizePath(changedFile); shouldRestart = configRE.test(normalizedChangedFile); - - if (preferencesRE.test(normalizedChangedFile)) { + const settingsPath = vite.normalizePath( + fileURLToPath(new URL('settings.json', settings.dotAstroDir)) + ); + if (settingsPath.match(normalizedChangedFile)) { shouldRestart = settings.preferences.ignoreNextPreferenceReload ? false : true; settings.preferences.ignoreNextPreferenceReload = false; diff --git a/packages/astro/test/units/dev/restart.test.js b/packages/astro/test/units/dev/restart.test.js index a2a858965dc5..9b59ea94eb25 100644 --- a/packages/astro/test/units/dev/restart.test.js +++ b/packages/astro/test/units/dev/restart.test.js @@ -194,4 +194,31 @@ describe('dev container restarts', () => { await restart.container.close(); } }); + + it('Is able to restart project on .astro/settings.json changes', async () => { + const fs = createFs( + { + '/src/pages/index.astro': ``, + '/.astro/settings.json': `{}`, + }, + root + ); + + const restart = await createContainerWithAutomaticRestart({ + fs, + inlineConfig: { root: fileURLToPath(root), logLevel: 'silent' }, + }); + await startContainer(restart.container); + assert.equal(isStarted(restart.container), true); + + try { + let restartComplete = restart.restarted(); + fs.mkdirSync('/.astro/', { recursive: true }); + fs.writeFileSync('/.astro/settings.json', `{ }`); + triggerFSEvent(restart.container, fs, '/.astro/settings.json', 'change'); + await restartComplete; + } finally { + await restart.container.close(); + } + }); }); From 6a85ce6481df0ce654740614cfbd02bfa7143abb Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Mon, 5 Aug 2024 15:00:41 +0200 Subject: [PATCH 17/44] chore: simplify astro env sync --- packages/astro/src/core/sync/index.ts | 2 +- packages/astro/src/env/sync.ts | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/astro/src/core/sync/index.ts b/packages/astro/src/core/sync/index.ts index 7f6c4fc3ad7f..9d99a24bed18 100644 --- a/packages/astro/src/core/sync/index.ts +++ b/packages/astro/src/core/sync/index.ts @@ -81,7 +81,7 @@ export async function syncInternal({ if (!skip?.content) { await syncContentCollections(settings, { fs, logger }); } - settings = syncAstroEnv(settings, fs); + syncAstroEnv(settings, fs); writeInjectedTypes(settings, fs); diff --git a/packages/astro/src/env/sync.ts b/packages/astro/src/env/sync.ts index d80b73089a7f..27436f967be7 100644 --- a/packages/astro/src/env/sync.ts +++ b/packages/astro/src/env/sync.ts @@ -3,9 +3,9 @@ import type { AstroSettings } from '../@types/astro.js'; import { TYPES_TEMPLATE_URL } from './constants.js'; import { getEnvFieldType } from './validators.js'; -export function syncAstroEnv(settings: AstroSettings, fs = fsMod): AstroSettings { +export function syncAstroEnv(settings: AstroSettings, fs = fsMod): void { if (!settings.config.experimental.env) { - return settings; + return; } const schema = settings.config.experimental.env.schema ?? {}; @@ -23,12 +23,10 @@ export function syncAstroEnv(settings: AstroSettings, fs = fsMod): AstroSettings } const template = fs.readFileSync(TYPES_TEMPLATE_URL, 'utf-8'); - const dts = template.replace('// @@CLIENT@@', client).replace('// @@SERVER@@', server); + const content = template.replace('// @@CLIENT@@', client).replace('// @@SERVER@@', server); settings.injectedTypes.push({ filename: 'astro/env.d.ts', - content: dts, + content, }); - - return settings; } From ea1bde4f86d1c5f38b581f12987b0d1196016dbf Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Mon, 5 Aug 2024 15:02:40 +0200 Subject: [PATCH 18/44] feat: use dotAstroDir for preferences --- packages/astro/src/core/config/settings.ts | 5 +++-- packages/astro/src/preferences/index.ts | 5 ++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/astro/src/core/config/settings.ts b/packages/astro/src/core/config/settings.ts index cfc60a9b72fe..fd8901d4e49e 100644 --- a/packages/astro/src/core/config/settings.ts +++ b/packages/astro/src/core/config/settings.ts @@ -14,7 +14,8 @@ import { loadTSConfig } from './tsconfig.js'; export function createBaseSettings(config: AstroConfig): AstroSettings { const { contentDir } = getContentPaths(config); - const preferences = createPreferences(config); + const dotAstroDir = new URL('.astro/', config.root); + const preferences = createPreferences(config, dotAstroDir); return { config, preferences, @@ -106,7 +107,7 @@ export function createBaseSettings(config: AstroConfig): AstroSettings { watchFiles: [], devToolbarApps: [], timer: new AstroTimer(), - dotAstroDir: new URL('.astro/', config.root), + dotAstroDir, latestAstroVersion: undefined, // Will be set later if applicable when the dev server starts injectedTypes: [], }; diff --git a/packages/astro/src/preferences/index.ts b/packages/astro/src/preferences/index.ts index 99cfee17ff19..cd66fea6d2e9 100644 --- a/packages/astro/src/preferences/index.ts +++ b/packages/astro/src/preferences/index.ts @@ -82,10 +82,9 @@ export function coerce(key: string, value: unknown) { return value as any; } -export default function createPreferences(config: AstroConfig): AstroPreferences { +export default function createPreferences(config: AstroConfig, dotAstroDir: URL): AstroPreferences { const global = new PreferenceStore(getGlobalPreferenceDir()); - // TODO: use dotAstroDir - const project = new PreferenceStore(fileURLToPath(new URL('./.astro/', config.root))); + const project = new PreferenceStore(fileURLToPath(dotAstroDir)); const stores: Record = { global, project }; return { From badc2a5eae97e304d7a79ca9d95d98e55ab2d2ee Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Mon, 5 Aug 2024 15:09:13 +0200 Subject: [PATCH 19/44] feat: handle db in integration api --- packages/astro/src/core/sync/index.ts | 19 ++++++------------- packages/astro/src/integrations/hooks.ts | 11 ++++++++++- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/packages/astro/src/core/sync/index.ts b/packages/astro/src/core/sync/index.ts index 9d99a24bed18..38552603ad6c 100644 --- a/packages/astro/src/core/sync/index.ts +++ b/packages/astro/src/core/sync/index.ts @@ -98,23 +98,12 @@ export async function syncInternal({ } } -function handleFilename(filename: string) { - // TODO: reuse logic - if (filename === `./integrations/astro_db/db-types.d.ts`) { - return './astro/db.d.ts'; - } - - return filename; -} - // TODO: move to other file function writeInjectedTypes(settings: AstroSettings, fs: typeof fsMod) { const references: Array = []; for (const { filename, content } of settings.injectedTypes) { - const path = normalizePath( - fileURLToPath(new URL(handleFilename(filename), settings.dotAstroDir)) - ); + const path = normalizePath(fileURLToPath(new URL(filename, settings.dotAstroDir))); fs.mkdirSync(dirname(path), { recursive: true }); // TODO: format content using recast fs.writeFileSync(path, content, 'utf-8'); @@ -125,7 +114,11 @@ function writeInjectedTypes(settings: AstroSettings, fs: typeof fsMod) { if (references.length === 0) { fs.mkdirSync(settings.dotAstroDir, { recursive: true }); } - fs.writeFileSync(normalizePath(fileURLToPath(new URL(REFERENCE_FILE, settings.dotAstroDir))), astroDtsContent, 'utf-8'); + fs.writeFileSync( + normalizePath(fileURLToPath(new URL(REFERENCE_FILE, settings.dotAstroDir))), + astroDtsContent, + 'utf-8' + ); } /** diff --git a/packages/astro/src/integrations/hooks.ts b/packages/astro/src/integrations/hooks.ts index 23628c3f5683..22da1ae5ec08 100644 --- a/packages/astro/src/integrations/hooks.ts +++ b/packages/astro/src/integrations/hooks.ts @@ -111,7 +111,7 @@ export function normalizeInjectedTypeFilename(filename: string, integrationName: // TODO: validate filename const result = validInjectedTypeFilenameSchema.safeParse(filename); if (!result.success) { - throw new Error("TODO: astro error") + throw new Error('TODO: astro error'); } return `./integrations/${integrationName.replace(/[^\w.-]/g, '_')}/${filename}`; } @@ -344,6 +344,15 @@ export async function runHookConfigDone({ settings.adapter = adapter; }, injectTypes(injectedType) { + if (integration.name === 'astro:db' && injectedType.filename === 'db-types.d.ts') { + const filename = './astro/db.d.ts'; + settings.injectedTypes.push({ + filename, + content: injectedType.content, + }); + return new URL(filename, settings.dotAstroDir); + } + const normalizedFilename = normalizeInjectedTypeFilename( injectedType.filename, integration.name From 2bee83df6f76f53095f9c15b1918bb7c4d635091 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Mon, 5 Aug 2024 15:18:26 +0200 Subject: [PATCH 20/44] chore: reorganize --- packages/astro/src/core/sync/index.ts | 36 ++-------- packages/astro/src/core/sync/setup-env-ts.ts | 61 ---------------- packages/astro/src/core/sync/write-files.ts | 74 ++++++++++++++++++++ 3 files changed, 78 insertions(+), 93 deletions(-) delete mode 100644 packages/astro/src/core/sync/setup-env-ts.ts create mode 100644 packages/astro/src/core/sync/write-files.ts diff --git a/packages/astro/src/core/sync/index.ts b/packages/astro/src/core/sync/index.ts index 38552603ad6c..bb0bc3e064b2 100644 --- a/packages/astro/src/core/sync/index.ts +++ b/packages/astro/src/core/sync/index.ts @@ -1,8 +1,7 @@ +import { dim } from 'kleur/colors'; import fsMod from 'node:fs'; import { performance } from 'node:perf_hooks'; -import { fileURLToPath } from 'node:url'; -import { dim } from 'kleur/colors'; -import { type HMRPayload, createServer, normalizePath } from 'vite'; +import { type HMRPayload, createServer } from 'vite'; import type { AstroInlineConfig, AstroSettings } from '../../@types/astro.js'; import { createContentTypesGenerator } from '../../content/index.js'; import { globalContentConfigObserver } from '../../content/utils.js'; @@ -26,9 +25,7 @@ import { import type { Logger } from '../logger/core.js'; import { formatErrorMessage } from '../messages.js'; import { ensureProcessNodeEnv } from '../util.js'; -import { setUpEnvTs } from './setup-env-ts.js'; -import { dirname, relative } from 'node:path'; -import { REFERENCE_FILE } from './constants.js'; +import { writeFiles } from './write-files.js'; export type SyncOptions = { /** @@ -83,9 +80,7 @@ export async function syncInternal({ } syncAstroEnv(settings, fs); - writeInjectedTypes(settings, fs); - - await setUpEnvTs({ settings, logger, fs }); + await writeFiles(settings, fs, logger); logger.info('types', `Generated ${dim(getTimeStat(timerStart, performance.now()))}`); } catch (err) { const error = createSafeError(err); @@ -98,29 +93,6 @@ export async function syncInternal({ } } -// TODO: move to other file -function writeInjectedTypes(settings: AstroSettings, fs: typeof fsMod) { - const references: Array = []; - - for (const { filename, content } of settings.injectedTypes) { - const path = normalizePath(fileURLToPath(new URL(filename, settings.dotAstroDir))); - fs.mkdirSync(dirname(path), { recursive: true }); - // TODO: format content using recast - fs.writeFileSync(path, content, 'utf-8'); - references.push(normalizePath(relative(fileURLToPath(settings.dotAstroDir), path))); - } - - const astroDtsContent = `/// \n${references.map((reference) => `/// `).join('\n')}`; - if (references.length === 0) { - fs.mkdirSync(settings.dotAstroDir, { recursive: true }); - } - fs.writeFileSync( - normalizePath(fileURLToPath(new URL(REFERENCE_FILE, settings.dotAstroDir))), - astroDtsContent, - 'utf-8' - ); -} - /** * Generate content collection types, and then returns the process exit signal. * diff --git a/packages/astro/src/core/sync/setup-env-ts.ts b/packages/astro/src/core/sync/setup-env-ts.ts deleted file mode 100644 index 66f45e5c252b..000000000000 --- a/packages/astro/src/core/sync/setup-env-ts.ts +++ /dev/null @@ -1,61 +0,0 @@ -import type fsMod from 'node:fs'; -import path from 'node:path'; -import { fileURLToPath } from 'node:url'; -import { bold } from 'kleur/colors'; -import { normalizePath } from 'vite'; -import type { AstroSettings } from '../../@types/astro.js'; -import type { Logger } from '../logger/core.js'; -import { REFERENCE_FILE } from './constants.js'; - -function getDotAstroTypeReference({ - settings, - filename, -}: { settings: AstroSettings; filename: string }) { - const relativePath = normalizePath( - path.relative( - fileURLToPath(settings.config.srcDir), - fileURLToPath(new URL(filename, settings.dotAstroDir)) - ) - ); - - return `/// `; -} - -export async function setUpEnvTs({ - settings, - logger, - fs, -}: { - settings: AstroSettings; - logger: Logger; - fs: typeof fsMod; -}) { - const envTsPath = normalizePath(fileURLToPath(new URL('env.d.ts', settings.config.srcDir))); - const envTsPathRelativetoRoot = normalizePath( - path.relative(fileURLToPath(settings.config.root), envTsPath) - ); - const expectedTypeReference = getDotAstroTypeReference({ - settings, - filename: REFERENCE_FILE, - }); - - if (fs.existsSync(envTsPath)) { - const initialEnvContents = await fs.promises.readFile(envTsPath, 'utf-8'); - let typesEnvContents = initialEnvContents; - - - if (!typesEnvContents.includes(expectedTypeReference)) { - typesEnvContents = `${expectedTypeReference}\n${typesEnvContents}`; - } - - if (initialEnvContents !== typesEnvContents) { - logger.info('types', `Updated ${bold(envTsPathRelativetoRoot)} type declarations.`); - await fs.promises.writeFile(envTsPath, typesEnvContents, 'utf-8'); - } - } else { - // Otherwise, inject the `env.d.ts` file - await fs.promises.mkdir(settings.config.srcDir, { recursive: true }); - await fs.promises.writeFile(envTsPath, expectedTypeReference, 'utf-8'); - logger.info('types', `Added ${bold(envTsPathRelativetoRoot)} type declarations`); - } -} diff --git a/packages/astro/src/core/sync/write-files.ts b/packages/astro/src/core/sync/write-files.ts new file mode 100644 index 000000000000..ea18662696c6 --- /dev/null +++ b/packages/astro/src/core/sync/write-files.ts @@ -0,0 +1,74 @@ +import type fsMod from 'node:fs'; +import { dirname, relative } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { bold } from 'kleur/colors'; +import { normalizePath } from 'vite'; +import type { AstroSettings } from '../../@types/astro.js'; +import type { Logger } from '../logger/core.js'; +import { REFERENCE_FILE } from './constants.js'; + +export async function writeFiles(settings: AstroSettings, fs: typeof fsMod, logger: Logger) { + writeInjectedTypes(settings, fs); + await setUpEnvTs(settings, fs, logger); +} + +function getTsReference(type: 'path' | 'types', value: string) { + return `/// `; +} + +const CLIENT_TYPES_REFERENCE = getTsReference('types', 'astro/client'); + +function writeInjectedTypes(settings: AstroSettings, fs: typeof fsMod) { + const references: Array = []; + + for (const { filename, content } of settings.injectedTypes) { + const filepath = normalizePath(fileURLToPath(new URL(filename, settings.dotAstroDir))); + fs.mkdirSync(dirname(filepath), { recursive: true }); + // TODO: format content using recast + fs.writeFileSync(filepath, content, 'utf-8'); + references.push(normalizePath(relative(fileURLToPath(settings.dotAstroDir), filepath))); + } + + const astroDtsContent = `${CLIENT_TYPES_REFERENCE}\n${references.map((reference) => getTsReference('path', reference)).join('\n')}`; + if (references.length === 0) { + fs.mkdirSync(settings.dotAstroDir, { recursive: true }); + } + fs.writeFileSync( + normalizePath(fileURLToPath(new URL(REFERENCE_FILE, settings.dotAstroDir))), + astroDtsContent, + 'utf-8' + ); +} + +async function setUpEnvTs(settings: AstroSettings, fs: typeof fsMod, logger: Logger) { + const envTsPath = normalizePath(fileURLToPath(new URL('env.d.ts', settings.config.srcDir))); + const envTsPathRelativetoRoot = normalizePath( + relative(fileURLToPath(settings.config.root), envTsPath) + ); + const relativePath = normalizePath( + relative( + fileURLToPath(settings.config.srcDir), + fileURLToPath(new URL(REFERENCE_FILE, settings.dotAstroDir)) + ) + ); + const expectedTypeReference = getTsReference('path', relativePath); + + if (fs.existsSync(envTsPath)) { + const initialEnvContents = await fs.promises.readFile(envTsPath, 'utf-8'); + let typesEnvContents = initialEnvContents; + + if (!typesEnvContents.includes(expectedTypeReference)) { + typesEnvContents = `${expectedTypeReference}\n${typesEnvContents}`; + } + + if (initialEnvContents !== typesEnvContents) { + logger.info('types', `Updated ${bold(envTsPathRelativetoRoot)} type declarations.`); + await fs.promises.writeFile(envTsPath, typesEnvContents, 'utf-8'); + } + } else { + // Otherwise, inject the `env.d.ts` file + await fs.promises.mkdir(settings.config.srcDir, { recursive: true }); + await fs.promises.writeFile(envTsPath, expectedTypeReference, 'utf-8'); + logger.info('types', `Added ${bold(envTsPathRelativetoRoot)} type declarations`); + } +} From fe82d08cfb5ef73559bfbb23a8dcc2fa090de3e8 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Mon, 5 Aug 2024 15:44:58 +0200 Subject: [PATCH 21/44] feat: format --- packages/astro/src/core/sync/write-files.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/packages/astro/src/core/sync/write-files.ts b/packages/astro/src/core/sync/write-files.ts index ea18662696c6..c70dbee8e5e0 100644 --- a/packages/astro/src/core/sync/write-files.ts +++ b/packages/astro/src/core/sync/write-files.ts @@ -6,6 +6,8 @@ import { normalizePath } from 'vite'; import type { AstroSettings } from '../../@types/astro.js'; import type { Logger } from '../logger/core.js'; import { REFERENCE_FILE } from './constants.js'; +import { parse } from '@babel/parser'; +import generate from '@babel/generator'; export async function writeFiles(settings: AstroSettings, fs: typeof fsMod, logger: Logger) { writeInjectedTypes(settings, fs); @@ -24,8 +26,7 @@ function writeInjectedTypes(settings: AstroSettings, fs: typeof fsMod) { for (const { filename, content } of settings.injectedTypes) { const filepath = normalizePath(fileURLToPath(new URL(filename, settings.dotAstroDir))); fs.mkdirSync(dirname(filepath), { recursive: true }); - // TODO: format content using recast - fs.writeFileSync(filepath, content, 'utf-8'); + fs.writeFileSync(filepath, formatContent(content), 'utf-8'); references.push(normalizePath(relative(fileURLToPath(settings.dotAstroDir), filepath))); } @@ -72,3 +73,16 @@ async function setUpEnvTs(settings: AstroSettings, fs: typeof fsMod, logger: Log logger.info('types', `Added ${bold(envTsPathRelativetoRoot)} type declarations`); } } + +function formatContent(source: string) { + const ast = parse(source, { + sourceType: 'module', + plugins: ['typescript'], + }); + + const result = generate.default(ast, { + retainLines: false, + }); + + return result.code; +} From c4745949c25633062bd0ae745a75f515fdce9f42 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Mon, 5 Aug 2024 16:02:25 +0200 Subject: [PATCH 22/44] feat: add test --- packages/astro/src/integrations/hooks.ts | 17 +++++--------- .../astro/test/units/integrations/api.test.js | 23 ++++++++++++++++++- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/packages/astro/src/integrations/hooks.ts b/packages/astro/src/integrations/hooks.ts index 22da1ae5ec08..aa8468d318a2 100644 --- a/packages/astro/src/integrations/hooks.ts +++ b/packages/astro/src/integrations/hooks.ts @@ -21,7 +21,6 @@ import { mergeConfig } from '../core/config/index.js'; import type { AstroIntegrationLogger, Logger } from '../core/logger/core.js'; import { isServerLikeOutput } from '../core/util.js'; import { validateSupportedFeatures } from './features-validation.js'; -import { z } from 'zod'; async function withTakingALongTimeMsg({ name, @@ -101,19 +100,15 @@ export function getToolbarServerCommunicationHelpers(server: ViteDevServer) { }; } -const validInjectedTypeFilenameSchema = z - .string() - .regex(/^[\w.-]+$/, 'Invalid chars') - .endsWith('.d.ts'); +const SAFE_CHARS_RE = /[^\w.-]/g -// TODO: test export function normalizeInjectedTypeFilename(filename: string, integrationName: string): string { - // TODO: validate filename - const result = validInjectedTypeFilenameSchema.safeParse(filename); - if (!result.success) { - throw new Error('TODO: astro error'); + if (!filename.endsWith('.d.ts')) { + throw new Error( + `Integration ${bold(integrationName)} is injecting a type that does not end with "${bold('.d.ts')}"` + ); } - return `./integrations/${integrationName.replace(/[^\w.-]/g, '_')}/${filename}`; + return `./integrations/${integrationName.replace(SAFE_CHARS_RE, '_')}/${filename.replace(SAFE_CHARS_RE, '_')}`; } export async function runHookConfigSetup({ diff --git a/packages/astro/test/units/integrations/api.test.js b/packages/astro/test/units/integrations/api.test.js index 06785e11b3d3..d69cd286b4c5 100644 --- a/packages/astro/test/units/integrations/api.test.js +++ b/packages/astro/test/units/integrations/api.test.js @@ -1,7 +1,11 @@ import * as assert from 'node:assert/strict'; import { describe, it } from 'node:test'; import { validateSupportedFeatures } from '../../../dist/integrations/features-validation.js'; -import { runHookBuildSetup, runHookConfigSetup } from '../../../dist/integrations/hooks.js'; +import { + runHookBuildSetup, + runHookConfigSetup, + normalizeInjectedTypeFilename, +} from '../../../dist/integrations/hooks.js'; import { defaultLogger } from '../test-utils.js'; describe('Integration API', () => { @@ -311,3 +315,20 @@ describe('Astro feature map', function () { }); }); }); + +describe('normalizeInjectedTypeFilename', () => { + // invalid filename + assert.throws(() => normalizeInjectedTypeFilename('types', 'integration')); + // valid filename + assert.doesNotThrow(() => normalizeInjectedTypeFilename('types.d.ts', 'integration')); + // filename normalization + assert.equal( + normalizeInjectedTypeFilename('aA1-*/_"~.d.ts', 'integration'), + './integrations/integration/aA1-_____.d.ts' + ); + // integration name normalization + assert.equal( + normalizeInjectedTypeFilename('types.d.ts', 'aA1-*/_"~.'), + './integrations/aA1-_____./types.d.ts' + ); +}); From 8d8a32e930083d0b051405220843ba61d2f37dea Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Mon, 5 Aug 2024 16:20:31 +0200 Subject: [PATCH 23/44] Discard changes to examples/basics/astro.config.mjs --- examples/basics/astro.config.mjs | 28 +--------------------------- 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/examples/basics/astro.config.mjs b/examples/basics/astro.config.mjs index 8babba9e4fcd..882e6515a67e 100644 --- a/examples/basics/astro.config.mjs +++ b/examples/basics/astro.config.mjs @@ -1,30 +1,4 @@ -// @ts-check import { defineConfig } from 'astro/config'; -import node from '@astrojs/node' // https://astro.build/config -export default defineConfig({ - output: "server", - integrations: [ - node({ - mode: "standalone" - }), - { - name: "test", - hooks: { - "astro:config:done": ({ injectTypes }) => { - injectTypes({ - filename: "types.d.ts", - content: "declare const FOO: string;" - }) - } - } - } - ], - experimental: { - env: { - schema: {} - }, - actions: true - } -}); +export default defineConfig({}); From 157238a17c49397ad2ecf82ae0af6d6ff2422abc Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Mon, 5 Aug 2024 16:20:37 +0200 Subject: [PATCH 24/44] Discard changes to examples/basics/package.json --- examples/basics/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/basics/package.json b/examples/basics/package.json index 0a20792ade5a..0ce97377a2c7 100644 --- a/examples/basics/package.json +++ b/examples/basics/package.json @@ -11,7 +11,6 @@ "astro": "astro" }, "dependencies": { - "@astrojs/node": "^8.3.2", "astro": "^4.12.2" } } From d5907043d4fe0aed7176a6796bfa514d438c9474 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Mon, 5 Aug 2024 16:21:52 +0200 Subject: [PATCH 25/44] Discard changes to pnpm-lock.yaml --- pnpm-lock.yaml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f884fe31864a..45f6a2824c02 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -127,9 +127,6 @@ importers: examples/basics: dependencies: - '@astrojs/node': - specifier: ^8.3.2 - version: link:../../packages/integrations/node astro: specifier: ^4.12.2 version: link:../../packages/astro @@ -9429,7 +9426,6 @@ packages: libsql@0.3.12: resolution: {integrity: sha512-to30hj8O3DjS97wpbKN6ERZ8k66MN1IaOfFLR6oHqd25GMiPJ/ZX0VaZ7w+TsPmxcFS3p71qArj/hiedCyvXCg==} - cpu: [x64, arm64, wasm32] os: [darwin, linux, win32] lilconfig@2.1.0: From 81578f2cf89113981d8e185c516ddf5bfc115079 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Mon, 5 Aug 2024 16:22:39 +0200 Subject: [PATCH 26/44] chore: remove test files --- examples/basics/src/actions.ts | 7 ------- examples/basics/src/content/config.ts | 10 ---------- examples/basics/src/content/posts/foo.json | 3 --- 3 files changed, 20 deletions(-) delete mode 100644 examples/basics/src/actions.ts delete mode 100644 examples/basics/src/content/config.ts delete mode 100644 examples/basics/src/content/posts/foo.json diff --git a/examples/basics/src/actions.ts b/examples/basics/src/actions.ts deleted file mode 100644 index a4cddd759eaa..000000000000 --- a/examples/basics/src/actions.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { defineAction } from "astro:actions"; - -export const server = { - foo: defineAction({ - handler: () => {} - }) -} \ No newline at end of file diff --git a/examples/basics/src/content/config.ts b/examples/basics/src/content/config.ts deleted file mode 100644 index 7a3027878f35..000000000000 --- a/examples/basics/src/content/config.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { defineCollection, z } from "astro:content"; - -export const collections = { - posts: defineCollection({ - type: "data", - schema: z.object({ - title: z.string() - }) - }), -} \ No newline at end of file diff --git a/examples/basics/src/content/posts/foo.json b/examples/basics/src/content/posts/foo.json deleted file mode 100644 index b2d53d2926d4..000000000000 --- a/examples/basics/src/content/posts/foo.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "title": "Foo" -} \ No newline at end of file From d0838c49d295c9e456f885cab2ff837710f4dffb Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Mon, 5 Aug 2024 16:23:45 +0200 Subject: [PATCH 27/44] feat: update examples dts --- examples/blog/src/env.d.ts | 2 +- examples/framework-alpine/src/env.d.ts | 2 +- examples/framework-lit/src/env.d.ts | 2 +- examples/framework-multiple/src/env.d.ts | 2 +- examples/framework-preact/src/env.d.ts | 2 +- examples/framework-react/src/env.d.ts | 2 +- examples/framework-solid/src/env.d.ts | 2 +- examples/framework-svelte/src/env.d.ts | 2 +- examples/framework-vue/src/env.d.ts | 2 +- examples/hackernews/src/env.d.ts | 2 +- examples/middleware/src/env.d.ts | 2 +- examples/minimal/src/env.d.ts | 2 +- examples/non-html-pages/src/env.d.ts | 2 +- examples/portfolio/src/env.d.ts | 2 +- examples/ssr/src/env.d.ts | 2 +- examples/with-markdoc/src/env.d.ts | 2 +- examples/with-markdown-plugins/src/env.d.ts | 2 +- examples/with-markdown-shiki/src/env.d.ts | 2 +- examples/with-mdx/src/env.d.ts | 2 +- examples/with-nanostores/src/env.d.ts | 2 +- examples/with-tailwindcss/src/env.d.ts | 2 +- 21 files changed, 21 insertions(+), 21 deletions(-) diff --git a/examples/blog/src/env.d.ts b/examples/blog/src/env.d.ts index acef35f175aa..63322afcf9cc 100644 --- a/examples/blog/src/env.d.ts +++ b/examples/blog/src/env.d.ts @@ -1,2 +1,2 @@ /// -/// +/// diff --git a/examples/framework-alpine/src/env.d.ts b/examples/framework-alpine/src/env.d.ts index f964fe0cffd8..e16c13c6952a 100644 --- a/examples/framework-alpine/src/env.d.ts +++ b/examples/framework-alpine/src/env.d.ts @@ -1 +1 @@ -/// +/// diff --git a/examples/framework-lit/src/env.d.ts b/examples/framework-lit/src/env.d.ts index f964fe0cffd8..e16c13c6952a 100644 --- a/examples/framework-lit/src/env.d.ts +++ b/examples/framework-lit/src/env.d.ts @@ -1 +1 @@ -/// +/// diff --git a/examples/framework-multiple/src/env.d.ts b/examples/framework-multiple/src/env.d.ts index f964fe0cffd8..e16c13c6952a 100644 --- a/examples/framework-multiple/src/env.d.ts +++ b/examples/framework-multiple/src/env.d.ts @@ -1 +1 @@ -/// +/// diff --git a/examples/framework-preact/src/env.d.ts b/examples/framework-preact/src/env.d.ts index f964fe0cffd8..e16c13c6952a 100644 --- a/examples/framework-preact/src/env.d.ts +++ b/examples/framework-preact/src/env.d.ts @@ -1 +1 @@ -/// +/// diff --git a/examples/framework-react/src/env.d.ts b/examples/framework-react/src/env.d.ts index f964fe0cffd8..e16c13c6952a 100644 --- a/examples/framework-react/src/env.d.ts +++ b/examples/framework-react/src/env.d.ts @@ -1 +1 @@ -/// +/// diff --git a/examples/framework-solid/src/env.d.ts b/examples/framework-solid/src/env.d.ts index f964fe0cffd8..e16c13c6952a 100644 --- a/examples/framework-solid/src/env.d.ts +++ b/examples/framework-solid/src/env.d.ts @@ -1 +1 @@ -/// +/// diff --git a/examples/framework-svelte/src/env.d.ts b/examples/framework-svelte/src/env.d.ts index f964fe0cffd8..e16c13c6952a 100644 --- a/examples/framework-svelte/src/env.d.ts +++ b/examples/framework-svelte/src/env.d.ts @@ -1 +1 @@ -/// +/// diff --git a/examples/framework-vue/src/env.d.ts b/examples/framework-vue/src/env.d.ts index f964fe0cffd8..e16c13c6952a 100644 --- a/examples/framework-vue/src/env.d.ts +++ b/examples/framework-vue/src/env.d.ts @@ -1 +1 @@ -/// +/// diff --git a/examples/hackernews/src/env.d.ts b/examples/hackernews/src/env.d.ts index f964fe0cffd8..e16c13c6952a 100644 --- a/examples/hackernews/src/env.d.ts +++ b/examples/hackernews/src/env.d.ts @@ -1 +1 @@ -/// +/// diff --git a/examples/middleware/src/env.d.ts b/examples/middleware/src/env.d.ts index 44f67965a3cc..74b9019e5746 100644 --- a/examples/middleware/src/env.d.ts +++ b/examples/middleware/src/env.d.ts @@ -1,4 +1,4 @@ -/// +/// declare namespace App { interface Locals { user: { diff --git a/examples/minimal/src/env.d.ts b/examples/minimal/src/env.d.ts index f964fe0cffd8..e16c13c6952a 100644 --- a/examples/minimal/src/env.d.ts +++ b/examples/minimal/src/env.d.ts @@ -1 +1 @@ -/// +/// diff --git a/examples/non-html-pages/src/env.d.ts b/examples/non-html-pages/src/env.d.ts index f964fe0cffd8..e16c13c6952a 100644 --- a/examples/non-html-pages/src/env.d.ts +++ b/examples/non-html-pages/src/env.d.ts @@ -1 +1 @@ -/// +/// diff --git a/examples/portfolio/src/env.d.ts b/examples/portfolio/src/env.d.ts index acef35f175aa..63322afcf9cc 100644 --- a/examples/portfolio/src/env.d.ts +++ b/examples/portfolio/src/env.d.ts @@ -1,2 +1,2 @@ /// -/// +/// diff --git a/examples/ssr/src/env.d.ts b/examples/ssr/src/env.d.ts index f964fe0cffd8..e16c13c6952a 100644 --- a/examples/ssr/src/env.d.ts +++ b/examples/ssr/src/env.d.ts @@ -1 +1 @@ -/// +/// diff --git a/examples/with-markdoc/src/env.d.ts b/examples/with-markdoc/src/env.d.ts index acef35f175aa..63322afcf9cc 100644 --- a/examples/with-markdoc/src/env.d.ts +++ b/examples/with-markdoc/src/env.d.ts @@ -1,2 +1,2 @@ /// -/// +/// diff --git a/examples/with-markdown-plugins/src/env.d.ts b/examples/with-markdown-plugins/src/env.d.ts index f964fe0cffd8..e16c13c6952a 100644 --- a/examples/with-markdown-plugins/src/env.d.ts +++ b/examples/with-markdown-plugins/src/env.d.ts @@ -1 +1 @@ -/// +/// diff --git a/examples/with-markdown-shiki/src/env.d.ts b/examples/with-markdown-shiki/src/env.d.ts index f964fe0cffd8..e16c13c6952a 100644 --- a/examples/with-markdown-shiki/src/env.d.ts +++ b/examples/with-markdown-shiki/src/env.d.ts @@ -1 +1 @@ -/// +/// diff --git a/examples/with-mdx/src/env.d.ts b/examples/with-mdx/src/env.d.ts index f964fe0cffd8..e16c13c6952a 100644 --- a/examples/with-mdx/src/env.d.ts +++ b/examples/with-mdx/src/env.d.ts @@ -1 +1 @@ -/// +/// diff --git a/examples/with-nanostores/src/env.d.ts b/examples/with-nanostores/src/env.d.ts index f964fe0cffd8..e16c13c6952a 100644 --- a/examples/with-nanostores/src/env.d.ts +++ b/examples/with-nanostores/src/env.d.ts @@ -1 +1 @@ -/// +/// diff --git a/examples/with-tailwindcss/src/env.d.ts b/examples/with-tailwindcss/src/env.d.ts index f964fe0cffd8..e16c13c6952a 100644 --- a/examples/with-tailwindcss/src/env.d.ts +++ b/examples/with-tailwindcss/src/env.d.ts @@ -1 +1 @@ -/// +/// From 956d5cb3158483d4375f60531d63256ff39cd9ac Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Mon, 5 Aug 2024 16:24:34 +0200 Subject: [PATCH 28/44] fix: dts --- examples/blog/src/env.d.ts | 1 - examples/portfolio/src/env.d.ts | 1 - examples/with-markdoc/src/env.d.ts | 1 - 3 files changed, 3 deletions(-) diff --git a/examples/blog/src/env.d.ts b/examples/blog/src/env.d.ts index 63322afcf9cc..e16c13c6952a 100644 --- a/examples/blog/src/env.d.ts +++ b/examples/blog/src/env.d.ts @@ -1,2 +1 @@ /// -/// diff --git a/examples/portfolio/src/env.d.ts b/examples/portfolio/src/env.d.ts index 63322afcf9cc..e16c13c6952a 100644 --- a/examples/portfolio/src/env.d.ts +++ b/examples/portfolio/src/env.d.ts @@ -1,2 +1 @@ /// -/// diff --git a/examples/with-markdoc/src/env.d.ts b/examples/with-markdoc/src/env.d.ts index 63322afcf9cc..e16c13c6952a 100644 --- a/examples/with-markdoc/src/env.d.ts +++ b/examples/with-markdoc/src/env.d.ts @@ -1,2 +1 @@ /// -/// From f18ff9da3a394d2b4ec1a398e0c17f35b668dc61 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Mon, 5 Aug 2024 17:15:46 +0200 Subject: [PATCH 29/44] chore: changesets --- .changeset/fresh-fans-study.md | 18 ++++++++++++++++++ .changeset/mean-horses-kiss.md | 26 ++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 .changeset/fresh-fans-study.md create mode 100644 .changeset/mean-horses-kiss.md diff --git a/.changeset/fresh-fans-study.md b/.changeset/fresh-fans-study.md new file mode 100644 index 000000000000..c019a6af1018 --- /dev/null +++ b/.changeset/fresh-fans-study.md @@ -0,0 +1,18 @@ +--- +'@astrojs/db': minor +--- + +Changes how typegen works + +The generated dts file is now at a new location: + +```diff +- .astro/db-types.d.ts ++ .astro/astro/db.d.ts +``` + +The update made to `src/env.d.ts` can be undone: + +```diff +- /// +``` diff --git a/.changeset/mean-horses-kiss.md b/.changeset/mean-horses-kiss.md new file mode 100644 index 000000000000..8e6508b642fb --- /dev/null +++ b/.changeset/mean-horses-kiss.md @@ -0,0 +1,26 @@ +--- +'astro': minor +--- + +Adds a new `injectTypes` utility to the Integration API and refactors how typegen works + +Integrations can now use a new `injectTypes` utility exposed on the `astro:config:done` hook. It allows to inject types easily in the user's project. `filename` must ends with `".d.ts"` and `content` must be valid TypeScript (it will be formatted). + +Under the hood, it will create a file at `/.astro/integrations//.d.ts` and create references to it. `injectTypes` returns a URL to the normalized path. + +```js +const path = injectTypes({ + filename: "types.d.ts", + content: "declare module 'virtual:integration' {}" +}) +console.log(path) // URL +``` + +Codegen has been refactored. As a user, you just need to update `src/env.d.ts`: + +```diff +- /// ++ /// +- /// +- /// +``` \ No newline at end of file From 4a64b5c344d629348daa43080af05c523b25d6d0 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Mon, 5 Aug 2024 17:20:46 +0200 Subject: [PATCH 30/44] fix: indentation --- packages/astro/test/astro-sync.test.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/astro/test/astro-sync.test.js b/packages/astro/test/astro-sync.test.js index a367d06d6054..6d1f90d8f10f 100644 --- a/packages/astro/test/astro-sync.test.js +++ b/packages/astro/test/astro-sync.test.js @@ -168,22 +168,22 @@ describe('astro sync', () => { fixture.thenFileContentShouldInclude( '.astro/astro/content.d.ts', `"blog": Record; - render(): Render[".md"]; -}>;`, + id: string; + slug: string; + body: string; + collection: "blog"; + data: InferEntrySchema<"blog">; + render(): Render[".md"]; + }>;`, 'Types file does not include empty collection type' ); fixture.thenFileContentShouldInclude( '.astro/astro/content.d.ts', `"blogMeta": Record; -}>;`, + id: string; + collection: "blogMeta"; + data: InferEntrySchema<"blogMeta">; + }>`, 'Types file does not include empty collection type' ); }); From 6115c104d3aa07e2df67000dbe440525fc6fa4dd Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Tue, 6 Aug 2024 15:39:53 +0200 Subject: [PATCH 31/44] Apply suggestions from code review Co-authored-by: Chris Swithinbank --- .changeset/fresh-fans-study.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.changeset/fresh-fans-study.md b/.changeset/fresh-fans-study.md index c019a6af1018..c11551091a5d 100644 --- a/.changeset/fresh-fans-study.md +++ b/.changeset/fresh-fans-study.md @@ -2,16 +2,16 @@ '@astrojs/db': minor --- -Changes how typegen works +Changes how type generation works -The generated dts file is now at a new location: +The generated `.d.ts` file is now at a new location: ```diff - .astro/db-types.d.ts + .astro/astro/db.d.ts ``` -The update made to `src/env.d.ts` can be undone: +The following line can now be removed from `src/env.d.ts`: ```diff - /// From b2662747e727dc40cb93420605f1da4e05764861 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Tue, 6 Aug 2024 15:40:11 +0200 Subject: [PATCH 32/44] Apply suggestions from code review Co-authored-by: Chris Swithinbank --- .changeset/mean-horses-kiss.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.changeset/mean-horses-kiss.md b/.changeset/mean-horses-kiss.md index 8e6508b642fb..78aeb3ead634 100644 --- a/.changeset/mean-horses-kiss.md +++ b/.changeset/mean-horses-kiss.md @@ -2,11 +2,11 @@ 'astro': minor --- -Adds a new `injectTypes` utility to the Integration API and refactors how typegen works +Adds a new `injectTypes()` utility to the Integration API and refactors how type generation works -Integrations can now use a new `injectTypes` utility exposed on the `astro:config:done` hook. It allows to inject types easily in the user's project. `filename` must ends with `".d.ts"` and `content` must be valid TypeScript (it will be formatted). +Integrations can now use a new `injectTypes()` utility in the `astro:config:done` hook. This utility allows an integration to inject types into a user's project. `filename` must end with `".d.ts"` and `content` must be valid TypeScript (it will be formatted). -Under the hood, it will create a file at `/.astro/integrations//.d.ts` and create references to it. `injectTypes` returns a URL to the normalized path. +Under the hood, it will create a file at `/.astro/integrations//.d.ts` and create references to it. `injectTypes()` returns a URL to the normalized path. ```js const path = injectTypes({ From 29bcfc37fb096f7d9ea64c757563d74eecf56e81 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Thu, 8 Aug 2024 14:21:50 +0200 Subject: [PATCH 33/44] chore: format --- packages/astro/src/actions/index.ts | 2 +- packages/astro/src/content/types-generator.ts | 4 +-- packages/astro/src/core/dev/restart.ts | 2 +- packages/astro/src/core/sync/constants.ts | 2 +- packages/astro/src/core/sync/index.ts | 4 +-- packages/astro/src/core/sync/write-files.ts | 12 ++++---- packages/astro/src/integrations/hooks.ts | 6 ++-- packages/astro/test/astro-sync.test.js | 28 +++++++++++-------- .../collections-mixed-content-errors.test.js | 2 +- packages/astro/test/units/dev/restart.test.js | 2 +- .../astro/test/units/integrations/api.test.js | 6 ++-- 11 files changed, 37 insertions(+), 33 deletions(-) diff --git a/packages/astro/src/actions/index.ts b/packages/astro/src/actions/index.ts index 0216f78d0c47..2423b7017d43 100644 --- a/packages/astro/src/actions/index.ts +++ b/packages/astro/src/actions/index.ts @@ -49,7 +49,7 @@ export default function astroActions({ }, 'astro:config:done': (params) => { const stringifiedActionsImport = JSON.stringify( - viteID(new URL('./actions', params.config.srcDir)) + viteID(new URL('./actions', params.config.srcDir)), ); settings.injectedTypes.push({ filename: ACTIONS_TYPES_FILE, diff --git a/packages/astro/src/content/types-generator.ts b/packages/astro/src/content/types-generator.ts index 7526bce484d0..0c6fbd06e05c 100644 --- a/packages/astro/src/content/types-generator.ts +++ b/packages/astro/src/content/types-generator.ts @@ -500,7 +500,7 @@ async function writeContentFiles({ const configPathRelativeToCacheDir = normalizeConfigPath( new URL('astro', settings.dotAstroDir).pathname, - contentPaths.config.url.pathname + contentPaths.config.url.pathname, ); for (const contentEntryType of contentEntryTypes) { @@ -519,7 +519,7 @@ async function writeContentFiles({ fs.promises.writeFile( new URL(CONTENT_TYPES_FILE, settings.dotAstroDir), typeTemplateContent, - 'utf-8' + 'utf-8', ); } else { settings.injectedTypes.push({ diff --git a/packages/astro/src/core/dev/restart.ts b/packages/astro/src/core/dev/restart.ts index c05e6699ae42..f5d55a903168 100644 --- a/packages/astro/src/core/dev/restart.ts +++ b/packages/astro/src/core/dev/restart.ts @@ -48,7 +48,7 @@ function shouldRestartContainer( else { shouldRestart = configRE.test(normalizedChangedFile); const settingsPath = vite.normalizePath( - fileURLToPath(new URL('settings.json', settings.dotAstroDir)) + fileURLToPath(new URL('settings.json', settings.dotAstroDir)), ); if (settingsPath.match(normalizedChangedFile)) { shouldRestart = settings.preferences.ignoreNextPreferenceReload ? false : true; diff --git a/packages/astro/src/core/sync/constants.ts b/packages/astro/src/core/sync/constants.ts index 71963c336648..7ff603105a75 100644 --- a/packages/astro/src/core/sync/constants.ts +++ b/packages/astro/src/core/sync/constants.ts @@ -1,2 +1,2 @@ // TODO: use types.d.ts for backward compatibility. Use astro.d.ts in Astro 5.0 -export const REFERENCE_FILE = './types.d.ts'; \ No newline at end of file +export const REFERENCE_FILE = './types.d.ts'; diff --git a/packages/astro/src/core/sync/index.ts b/packages/astro/src/core/sync/index.ts index b5f984114824..c6c9690fc4b0 100644 --- a/packages/astro/src/core/sync/index.ts +++ b/packages/astro/src/core/sync/index.ts @@ -1,6 +1,6 @@ -import { dim } from 'kleur/colors'; import fsMod from 'node:fs'; import { performance } from 'node:perf_hooks'; +import { dim } from 'kleur/colors'; import { type HMRPayload, createServer } from 'vite'; import type { AstroInlineConfig, AstroSettings } from '../../@types/astro.js'; import { createContentTypesGenerator } from '../../content/index.js'; @@ -42,7 +42,7 @@ export type SyncOptions = { export default async function sync( inlineConfig: AstroInlineConfig, - { fs, telemetry: _telemetry = false }: { fs?: typeof fsMod; telemetry?: boolean } = {} + { fs, telemetry: _telemetry = false }: { fs?: typeof fsMod; telemetry?: boolean } = {}, ) { ensureProcessNodeEnv('production'); const logger = createNodeLogger(inlineConfig); diff --git a/packages/astro/src/core/sync/write-files.ts b/packages/astro/src/core/sync/write-files.ts index c70dbee8e5e0..1b842d950ed2 100644 --- a/packages/astro/src/core/sync/write-files.ts +++ b/packages/astro/src/core/sync/write-files.ts @@ -1,13 +1,13 @@ import type fsMod from 'node:fs'; import { dirname, relative } from 'node:path'; import { fileURLToPath } from 'node:url'; +import generate from '@babel/generator'; +import { parse } from '@babel/parser'; import { bold } from 'kleur/colors'; import { normalizePath } from 'vite'; import type { AstroSettings } from '../../@types/astro.js'; import type { Logger } from '../logger/core.js'; import { REFERENCE_FILE } from './constants.js'; -import { parse } from '@babel/parser'; -import generate from '@babel/generator'; export async function writeFiles(settings: AstroSettings, fs: typeof fsMod, logger: Logger) { writeInjectedTypes(settings, fs); @@ -37,20 +37,20 @@ function writeInjectedTypes(settings: AstroSettings, fs: typeof fsMod) { fs.writeFileSync( normalizePath(fileURLToPath(new URL(REFERENCE_FILE, settings.dotAstroDir))), astroDtsContent, - 'utf-8' + 'utf-8', ); } async function setUpEnvTs(settings: AstroSettings, fs: typeof fsMod, logger: Logger) { const envTsPath = normalizePath(fileURLToPath(new URL('env.d.ts', settings.config.srcDir))); const envTsPathRelativetoRoot = normalizePath( - relative(fileURLToPath(settings.config.root), envTsPath) + relative(fileURLToPath(settings.config.root), envTsPath), ); const relativePath = normalizePath( relative( fileURLToPath(settings.config.srcDir), - fileURLToPath(new URL(REFERENCE_FILE, settings.dotAstroDir)) - ) + fileURLToPath(new URL(REFERENCE_FILE, settings.dotAstroDir)), + ), ); const expectedTypeReference = getTsReference('path', relativePath); diff --git a/packages/astro/src/integrations/hooks.ts b/packages/astro/src/integrations/hooks.ts index 4615c874afb7..1229ed3b5b19 100644 --- a/packages/astro/src/integrations/hooks.ts +++ b/packages/astro/src/integrations/hooks.ts @@ -100,12 +100,12 @@ export function getToolbarServerCommunicationHelpers(server: ViteDevServer) { }; } -const SAFE_CHARS_RE = /[^\w.-]/g +const SAFE_CHARS_RE = /[^\w.-]/g; export function normalizeInjectedTypeFilename(filename: string, integrationName: string): string { if (!filename.endsWith('.d.ts')) { throw new Error( - `Integration ${bold(integrationName)} is injecting a type that does not end with "${bold('.d.ts')}"` + `Integration ${bold(integrationName)} is injecting a type that does not end with "${bold('.d.ts')}"`, ); } return `./integrations/${integrationName.replace(SAFE_CHARS_RE, '_')}/${filename.replace(SAFE_CHARS_RE, '_')}`; @@ -350,7 +350,7 @@ export async function runHookConfigDone({ const normalizedFilename = normalizeInjectedTypeFilename( injectedType.filename, - integration.name + integration.name, ); settings.injectedTypes.push({ diff --git a/packages/astro/test/astro-sync.test.js b/packages/astro/test/astro-sync.test.js index 1d7c8150c581..293dd8fecae4 100644 --- a/packages/astro/test/astro-sync.test.js +++ b/packages/astro/test/astro-sync.test.js @@ -4,8 +4,8 @@ import * as fs from 'node:fs'; import { beforeEach, describe, it } from 'node:test'; import { fileURLToPath } from 'node:url'; import ts from 'typescript'; -import { loadFixture } from './test-utils.js'; import { normalizePath } from 'vite'; +import { loadFixture } from './test-utils.js'; const createFixture = () => { /** @type {Awaited>} */ @@ -61,12 +61,16 @@ const createFixture = () => { { // @ts-ignore fs: fsMock, - } + }, ); }, /** @param {string} path */ thenFileShouldExist(path) { - assert.equal(writtenFiles.hasOwnProperty(getExpectedPath(path)), true, `${path} does not exist`); + assert.equal( + writtenFiles.hasOwnProperty(getExpectedPath(path)), + true, + `${path} does not exist`, + ); }, /** * @param {string} path @@ -126,7 +130,7 @@ describe('astro sync', () => { fixture.thenFileShouldExist('src/env.d.ts'); fixture.thenFileContentShouldInclude( 'src/env.d.ts', - `/// ` + `/// `, ); }); @@ -137,7 +141,7 @@ describe('astro sync', () => { fixture.thenFileShouldExist('.astro/types.d.ts'); fixture.thenFileContentShouldInclude( '.astro/types.d.ts', - `/// ` + `/// `, ); }); }); @@ -150,13 +154,13 @@ describe('astro sync', () => { fixture.thenFileShouldExist('.astro/types.d.ts'); fixture.thenFileContentShouldInclude( '.astro/types.d.ts', - `/// ` + `/// `, ); fixture.thenFileShouldExist('.astro/astro/content.d.ts'); fixture.thenFileContentShouldInclude( '.astro/astro/content.d.ts', `declare module 'astro:content' {`, - 'Types file does not include `astro:content` module declaration' + 'Types file does not include `astro:content` module declaration', ); fixture.thenFileShouldBeValidTypescript('.astro/astro/content.d.ts'); }); @@ -175,7 +179,7 @@ describe('astro sync', () => { data: InferEntrySchema<"blog">; render(): Render[".md"]; }>;`, - 'Types file does not include empty collection type' + 'Types file does not include empty collection type', ); fixture.thenFileContentShouldInclude( '.astro/astro/content.d.ts', @@ -184,7 +188,7 @@ describe('astro sync', () => { collection: "blogMeta"; data: InferEntrySchema<"blogMeta">; }>`, - 'Types file does not include empty collection type' + 'Types file does not include empty collection type', ); }); }); @@ -197,7 +201,7 @@ describe('astro sync', () => { fixture.thenFileShouldExist('.astro/types.d.ts'); fixture.thenFileContentShouldInclude( '.astro/types.d.ts', - `/// ` + `/// `, ); fixture.thenFileShouldExist('.astro/astro/env.d.ts'); fixture.thenFileContentShouldInclude( @@ -227,13 +231,13 @@ describe('astro sync', () => { fixture.thenFileShouldExist('.astro/types.d.ts'); fixture.thenFileContentShouldInclude( '.astro/types.d.ts', - `/// ` + `/// `, ); fixture.thenFileShouldExist('.astro/astro/actions.d.ts'); fixture.thenFileContentShouldInclude( '.astro/astro/actions.d.ts', `declare module "astro:actions" {`, - 'Types file does not include `astro:actions` module declaration' + 'Types file does not include `astro:actions` module declaration', ); fixture.thenFileShouldBeValidTypescript('.astro/astro/actions.d.ts'); }); diff --git a/packages/astro/test/units/dev/collections-mixed-content-errors.test.js b/packages/astro/test/units/dev/collections-mixed-content-errors.test.js index 4215471d6f9b..3417650fc963 100644 --- a/packages/astro/test/units/dev/collections-mixed-content-errors.test.js +++ b/packages/astro/test/units/dev/collections-mixed-content-errors.test.js @@ -15,7 +15,7 @@ async function sync({ fs }) { }, { fs, - } + }, ); return 0; } catch (_) { diff --git a/packages/astro/test/units/dev/restart.test.js b/packages/astro/test/units/dev/restart.test.js index 3362575afe8f..339b95fc1acb 100644 --- a/packages/astro/test/units/dev/restart.test.js +++ b/packages/astro/test/units/dev/restart.test.js @@ -201,7 +201,7 @@ describe('dev container restarts', () => { '/src/pages/index.astro': ``, '/.astro/settings.json': `{}`, }, - root + root, ); const restart = await createContainerWithAutomaticRestart({ diff --git a/packages/astro/test/units/integrations/api.test.js b/packages/astro/test/units/integrations/api.test.js index ea4f69608171..6122ba6408f6 100644 --- a/packages/astro/test/units/integrations/api.test.js +++ b/packages/astro/test/units/integrations/api.test.js @@ -2,9 +2,9 @@ import * as assert from 'node:assert/strict'; import { describe, it } from 'node:test'; import { validateSupportedFeatures } from '../../../dist/integrations/features-validation.js'; import { + normalizeInjectedTypeFilename, runHookBuildSetup, runHookConfigSetup, - normalizeInjectedTypeFilename, } from '../../../dist/integrations/hooks.js'; import { defaultLogger } from '../test-utils.js'; @@ -324,11 +324,11 @@ describe('normalizeInjectedTypeFilename', () => { // filename normalization assert.equal( normalizeInjectedTypeFilename('aA1-*/_"~.d.ts', 'integration'), - './integrations/integration/aA1-_____.d.ts' + './integrations/integration/aA1-_____.d.ts', ); // integration name normalization assert.equal( normalizeInjectedTypeFilename('types.d.ts', 'aA1-*/_"~.'), - './integrations/aA1-_____./types.d.ts' + './integrations/aA1-_____./types.d.ts', ); }); From a5fbfe911133d4c3d6244ea00c177beeded6755b Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Thu, 8 Aug 2024 14:27:59 +0200 Subject: [PATCH 34/44] Update packages/astro/src/integrations/hooks.ts --- packages/astro/src/integrations/hooks.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/astro/src/integrations/hooks.ts b/packages/astro/src/integrations/hooks.ts index 1229ed3b5b19..bb892a864d8f 100644 --- a/packages/astro/src/integrations/hooks.ts +++ b/packages/astro/src/integrations/hooks.ts @@ -100,6 +100,7 @@ export function getToolbarServerCommunicationHelpers(server: ViteDevServer) { }; } +// Will match any invalid characters (will be converted to _). We only allow a-zA-Z0-9.-_ const SAFE_CHARS_RE = /[^\w.-]/g; export function normalizeInjectedTypeFilename(filename: string, integrationName: string): string { From 082553c965c8077bdeea2dac408456e2e8b78e60 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Fri, 9 Aug 2024 11:58:47 +0200 Subject: [PATCH 35/44] Update .changeset/mean-horses-kiss.md --- .changeset/mean-horses-kiss.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/mean-horses-kiss.md b/.changeset/mean-horses-kiss.md index 78aeb3ead634..5db46ff098e4 100644 --- a/.changeset/mean-horses-kiss.md +++ b/.changeset/mean-horses-kiss.md @@ -16,7 +16,7 @@ const path = injectTypes({ console.log(path) // URL ``` -Codegen has been refactored. As a user, you just need to update `src/env.d.ts`: +Codegen has been refactored. Although `src/env.d.ts` will continue to work as is, we recommend you update it: ```diff - /// From 6602653d7b49c960c49234aee713e8f9b9037b46 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Fri, 9 Aug 2024 12:04:47 +0200 Subject: [PATCH 36/44] feat: remove formatting --- packages/astro/src/core/sync/write-files.ts | 17 +--------------- packages/astro/test/astro-sync.test.js | 22 ++++++++++----------- 2 files changed, 12 insertions(+), 27 deletions(-) diff --git a/packages/astro/src/core/sync/write-files.ts b/packages/astro/src/core/sync/write-files.ts index 1b842d950ed2..bdf6b2bcbafa 100644 --- a/packages/astro/src/core/sync/write-files.ts +++ b/packages/astro/src/core/sync/write-files.ts @@ -1,8 +1,6 @@ import type fsMod from 'node:fs'; import { dirname, relative } from 'node:path'; import { fileURLToPath } from 'node:url'; -import generate from '@babel/generator'; -import { parse } from '@babel/parser'; import { bold } from 'kleur/colors'; import { normalizePath } from 'vite'; import type { AstroSettings } from '../../@types/astro.js'; @@ -26,7 +24,7 @@ function writeInjectedTypes(settings: AstroSettings, fs: typeof fsMod) { for (const { filename, content } of settings.injectedTypes) { const filepath = normalizePath(fileURLToPath(new URL(filename, settings.dotAstroDir))); fs.mkdirSync(dirname(filepath), { recursive: true }); - fs.writeFileSync(filepath, formatContent(content), 'utf-8'); + fs.writeFileSync(filepath, content, 'utf-8'); references.push(normalizePath(relative(fileURLToPath(settings.dotAstroDir), filepath))); } @@ -73,16 +71,3 @@ async function setUpEnvTs(settings: AstroSettings, fs: typeof fsMod, logger: Log logger.info('types', `Added ${bold(envTsPathRelativetoRoot)} type declarations`); } } - -function formatContent(source: string) { - const ast = parse(source, { - sourceType: 'module', - plugins: ['typescript'], - }); - - const result = generate.default(ast, { - retainLines: false, - }); - - return result.code; -} diff --git a/packages/astro/test/astro-sync.test.js b/packages/astro/test/astro-sync.test.js index 293dd8fecae4..dfe4755d1102 100644 --- a/packages/astro/test/astro-sync.test.js +++ b/packages/astro/test/astro-sync.test.js @@ -172,22 +172,22 @@ describe('astro sync', () => { fixture.thenFileContentShouldInclude( '.astro/astro/content.d.ts', `"blog": Record; - render(): Render[".md"]; - }>;`, + id: string; + slug: string; + body: string; + collection: "blog"; + data: InferEntrySchema<"blog">; + render(): Render[".md"]; +}>;`, 'Types file does not include empty collection type', ); fixture.thenFileContentShouldInclude( '.astro/astro/content.d.ts', `"blogMeta": Record; - }>`, + id: string; + collection: "blogMeta"; + data: InferEntrySchema<"blogMeta">; +}>;`, 'Types file does not include empty collection type', ); }); From e53f9ab1ec1cd8daf57fca6a35db3ab063827dce Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Fri, 9 Aug 2024 12:07:47 +0200 Subject: [PATCH 37/44] feat: handle fs errors --- packages/astro/src/core/sync/write-files.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/astro/src/core/sync/write-files.ts b/packages/astro/src/core/sync/write-files.ts index bdf6b2bcbafa..594f459a2df2 100644 --- a/packages/astro/src/core/sync/write-files.ts +++ b/packages/astro/src/core/sync/write-files.ts @@ -6,10 +6,15 @@ import { normalizePath } from 'vite'; import type { AstroSettings } from '../../@types/astro.js'; import type { Logger } from '../logger/core.js'; import { REFERENCE_FILE } from './constants.js'; +import { AstroError, AstroErrorData } from '../errors/index.js'; export async function writeFiles(settings: AstroSettings, fs: typeof fsMod, logger: Logger) { - writeInjectedTypes(settings, fs); - await setUpEnvTs(settings, fs, logger); + try { + writeInjectedTypes(settings, fs); + await setUpEnvTs(settings, fs, logger); + } catch (e) { + throw new AstroError(AstroErrorData.UnknownError, { cause: e }); + } } function getTsReference(type: 'path' | 'types', value: string) { From 934406a66f0da9e1cc16ca0181d1f94a7afa222e Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Fri, 9 Aug 2024 12:10:45 +0200 Subject: [PATCH 38/44] feat: remove astro:db special path handling --- .changeset/fresh-fans-study.md | 2 +- packages/astro/src/integrations/hooks.ts | 9 --------- packages/db/src/core/integration/index.ts | 2 +- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/.changeset/fresh-fans-study.md b/.changeset/fresh-fans-study.md index c11551091a5d..9a837b1fd7a7 100644 --- a/.changeset/fresh-fans-study.md +++ b/.changeset/fresh-fans-study.md @@ -8,7 +8,7 @@ The generated `.d.ts` file is now at a new location: ```diff - .astro/db-types.d.ts -+ .astro/astro/db.d.ts ++ .astro/integrations/astro_db/db.d.ts ``` The following line can now be removed from `src/env.d.ts`: diff --git a/packages/astro/src/integrations/hooks.ts b/packages/astro/src/integrations/hooks.ts index bb892a864d8f..36682fe2b256 100644 --- a/packages/astro/src/integrations/hooks.ts +++ b/packages/astro/src/integrations/hooks.ts @@ -340,15 +340,6 @@ export async function runHookConfigDone({ settings.adapter = adapter; }, injectTypes(injectedType) { - if (integration.name === 'astro:db' && injectedType.filename === 'db-types.d.ts') { - const filename = './astro/db.d.ts'; - settings.injectedTypes.push({ - filename, - content: injectedType.content, - }); - return new URL(filename, settings.dotAstroDir); - } - const normalizedFilename = normalizeInjectedTypeFilename( injectedType.filename, integration.name, diff --git a/packages/db/src/core/integration/index.ts b/packages/db/src/core/integration/index.ts index 0409aeaae160..054c74d74156 100644 --- a/packages/db/src/core/integration/index.ts +++ b/packages/db/src/core/integration/index.ts @@ -122,7 +122,7 @@ function astroDBIntegration(): AstroIntegration { } injectTypes({ - filename: 'types.d.ts', + filename: 'db.d.ts', content: getDtsContent(tables.get() ?? {}), }); }, From f512569f609aa105765771e6c1cd1ee32b56005c Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Mon, 12 Aug 2024 14:38:16 +0200 Subject: [PATCH 39/44] feat: add fs error --- packages/astro/src/core/errors/errors-data.ts | 10 ++++++++++ packages/astro/src/core/sync/write-files.ts | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/astro/src/core/errors/errors-data.ts b/packages/astro/src/core/errors/errors-data.ts index 8415a9c0a31e..041abaa98427 100644 --- a/packages/astro/src/core/errors/errors-data.ts +++ b/packages/astro/src/core/errors/errors-data.ts @@ -1291,6 +1291,16 @@ export const RewriteWithBodyUsed = { 'Astro.rewrite() cannot be used if the request body has already been read. If you need to read the body, first clone the request.', } satisfies ErrorData; +/** + * @docs + * @description + * An unknown error occured while reading or writing files to disk. + */ +export const UnknownFilesystemError = { + name: 'UnknownFilesystemError', + title: 'An unknown error occured while reading or writing files to disk.', +} satisfies ErrorData; + /** * @docs * @kind heading diff --git a/packages/astro/src/core/sync/write-files.ts b/packages/astro/src/core/sync/write-files.ts index 594f459a2df2..56ab131f1be7 100644 --- a/packages/astro/src/core/sync/write-files.ts +++ b/packages/astro/src/core/sync/write-files.ts @@ -13,7 +13,7 @@ export async function writeFiles(settings: AstroSettings, fs: typeof fsMod, logg writeInjectedTypes(settings, fs); await setUpEnvTs(settings, fs, logger); } catch (e) { - throw new AstroError(AstroErrorData.UnknownError, { cause: e }); + throw new AstroError(AstroErrorData.UnknownFilesystemError, { cause: e }); } } From ca93448adab56429d6c90903591536b438a9b6f9 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Mon, 12 Aug 2024 15:18:17 +0200 Subject: [PATCH 40/44] Update packages/astro/src/content/types-generator.ts --- packages/astro/src/content/types-generator.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/astro/src/content/types-generator.ts b/packages/astro/src/content/types-generator.ts index 0c6fbd06e05c..f7af8475aa4e 100644 --- a/packages/astro/src/content/types-generator.ts +++ b/packages/astro/src/content/types-generator.ts @@ -515,6 +515,7 @@ async function writeContentFiles({ contentConfig ? `typeof import(${configPathRelativeToCacheDir})` : 'never', ); + // If it's the first time, we inject types the usual way. sync() will handle creating files and references. If it's not the first time, we just override the dts content if (settings.injectedTypes.some((t) => t.filename === CONTENT_TYPES_FILE)) { fs.promises.writeFile( new URL(CONTENT_TYPES_FILE, settings.dotAstroDir), From 24089e20c4b9ef68e64618415a167cf7cc442fd3 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Tue, 13 Aug 2024 13:44:44 +0200 Subject: [PATCH 41/44] Update .changeset/mean-horses-kiss.md --- .changeset/mean-horses-kiss.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/.changeset/mean-horses-kiss.md b/.changeset/mean-horses-kiss.md index 5db46ff098e4..f807632bdf00 100644 --- a/.changeset/mean-horses-kiss.md +++ b/.changeset/mean-horses-kiss.md @@ -9,11 +9,16 @@ Integrations can now use a new `injectTypes()` utility in the `astro:config:done Under the hood, it will create a file at `/.astro/integrations//.d.ts` and create references to it. `injectTypes()` returns a URL to the normalized path. ```js -const path = injectTypes({ - filename: "types.d.ts", - content: "declare module 'virtual:integration' {}" -}) -console.log(path) // URL +// my-integration/index.js +export default { + name: 'my-integration', + 'astro:config:done': ({ injectTypes }) => { + injectTypes({ + filename: "types.d.ts", + content: "declare module 'virtual:my-integration' {}" + }) + } +}; ``` Codegen has been refactored. Although `src/env.d.ts` will continue to work as is, we recommend you update it: From 7f702b0ed84dfd4965bc6e3d8dd13d2d90d550f6 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Tue, 13 Aug 2024 13:50:17 +0200 Subject: [PATCH 42/44] Update errors-data.ts --- packages/astro/src/core/errors/errors-data.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/astro/src/core/errors/errors-data.ts b/packages/astro/src/core/errors/errors-data.ts index 53c0d9ba64ad..21ab487f0931 100644 --- a/packages/astro/src/core/errors/errors-data.ts +++ b/packages/astro/src/core/errors/errors-data.ts @@ -1294,11 +1294,12 @@ export const RewriteWithBodyUsed = { /** * @docs * @description - * An unknown error occured while reading or writing files to disk. + * An unknown error occured while reading or writing files to disk. It can be caused by many things, eg. missing permissions or a file not existing we attempt to read. */ export const UnknownFilesystemError = { name: 'UnknownFilesystemError', title: 'An unknown error occured while reading or writing files to disk.', + hint: 'It can be caused by many things, eg. missing permissions or a file not existing we attempt to read. Check the error cause for more details.', } satisfies ErrorData; /** From 27426b50b6aff974e77f4fd602120b0a61dab7b3 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Tue, 13 Aug 2024 15:31:59 +0200 Subject: [PATCH 43/44] Update .changeset/mean-horses-kiss.md Co-authored-by: Sarah Rainsberger --- .changeset/mean-horses-kiss.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.changeset/mean-horses-kiss.md b/.changeset/mean-horses-kiss.md index f807632bdf00..f002ba42a980 100644 --- a/.changeset/mean-horses-kiss.md +++ b/.changeset/mean-horses-kiss.md @@ -4,9 +4,13 @@ Adds a new `injectTypes()` utility to the Integration API and refactors how type generation works -Integrations can now use a new `injectTypes()` utility in the `astro:config:done` hook. This utility allows an integration to inject types into a user's project. `filename` must end with `".d.ts"` and `content` must be valid TypeScript (it will be formatted). +Use `injectTypes()` in the `astro:config:done` hook to inject types into your user's project by adding a new a `*.d.ts` file. -Under the hood, it will create a file at `/.astro/integrations//.d.ts` and create references to it. `injectTypes()` returns a URL to the normalized path. +The `filename` property will be used to generate a file at `/.astro/integrations//.d.ts` and must end with `".d.ts"`. + +The `content` property will create the body of the file, and must be valid TypeScript. + +Additionally, `injectTypes()` returns a URL to the normalized path so you can overwrite its content later on, or manipulate it in any way you want. ```js // my-integration/index.js From 8f38a9d334212a9df42046ef4eb52ec153f16b87 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Tue, 13 Aug 2024 16:03:16 +0200 Subject: [PATCH 44/44] Update .changeset/mean-horses-kiss.md Co-authored-by: Sarah Rainsberger --- .changeset/mean-horses-kiss.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/mean-horses-kiss.md b/.changeset/mean-horses-kiss.md index f002ba42a980..7d211e62674f 100644 --- a/.changeset/mean-horses-kiss.md +++ b/.changeset/mean-horses-kiss.md @@ -2,7 +2,7 @@ 'astro': minor --- -Adds a new `injectTypes()` utility to the Integration API and refactors how type generation works +Adds a new [`injectTypes()` utility](https://docs.astro.build/en/reference/integrations-reference/#injecttypes-options) to the Integration API and refactors how type generation works Use `injectTypes()` in the `astro:config:done` hook to inject types into your user's project by adding a new a `*.d.ts` file.