From c20b93c48448861f2b5c324d81dc30b601a0be0d Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Tue, 21 Jun 2022 12:35:11 -0400 Subject: [PATCH] Resolve .jsx -> .tsx in hydrated components (#3663) --- .changeset/warm-panthers-clap.md | 5 ++ .../fixtures/ts-resolution/astro.config.mjs | 7 ++ .../e2e/fixtures/ts-resolution/package.json | 11 ++++ .../ts-resolution/src/components/Counter.tsx | 18 ++++++ .../ts-resolution/src/pages/index.astro | 18 ++++++ packages/astro/e2e/ts-resolution.test.js | 64 +++++++++++++++++++ .../src/core/build/vite-plugin-analyzer.ts | 6 +- packages/astro/src/core/render/dev/index.ts | 3 +- packages/astro/src/core/render/dev/resolve.ts | 10 +++ packages/astro/src/runtime/server/metadata.ts | 10 ++- pnpm-lock.yaml | 21 ++++++ 11 files changed, 169 insertions(+), 4 deletions(-) create mode 100644 .changeset/warm-panthers-clap.md create mode 100644 packages/astro/e2e/fixtures/ts-resolution/astro.config.mjs create mode 100644 packages/astro/e2e/fixtures/ts-resolution/package.json create mode 100644 packages/astro/e2e/fixtures/ts-resolution/src/components/Counter.tsx create mode 100644 packages/astro/e2e/fixtures/ts-resolution/src/pages/index.astro create mode 100644 packages/astro/e2e/ts-resolution.test.js create mode 100644 packages/astro/src/core/render/dev/resolve.ts diff --git a/.changeset/warm-panthers-clap.md b/.changeset/warm-panthers-clap.md new file mode 100644 index 000000000..d773b1f5d --- /dev/null +++ b/.changeset/warm-panthers-clap.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Resolve .jsx -> .tsx in hydrated components diff --git a/packages/astro/e2e/fixtures/ts-resolution/astro.config.mjs b/packages/astro/e2e/fixtures/ts-resolution/astro.config.mjs new file mode 100644 index 000000000..8a6f1951c --- /dev/null +++ b/packages/astro/e2e/fixtures/ts-resolution/astro.config.mjs @@ -0,0 +1,7 @@ +import { defineConfig } from 'astro/config'; +import react from '@astrojs/react'; + +// https://astro.build/config +export default defineConfig({ + integrations: [react()], +}); diff --git a/packages/astro/e2e/fixtures/ts-resolution/package.json b/packages/astro/e2e/fixtures/ts-resolution/package.json new file mode 100644 index 000000000..7544800f9 --- /dev/null +++ b/packages/astro/e2e/fixtures/ts-resolution/package.json @@ -0,0 +1,11 @@ +{ + "name": "@e2e/ts-resolution", + "version": "0.0.0", + "private": true, + "dependencies": { + "@astrojs/react": "workspace:*", + "astro": "workspace:*", + "react": "^18.1.0", + "react-dom": "^18.1.0" + } +} diff --git a/packages/astro/e2e/fixtures/ts-resolution/src/components/Counter.tsx b/packages/astro/e2e/fixtures/ts-resolution/src/components/Counter.tsx new file mode 100644 index 000000000..9d91921f4 --- /dev/null +++ b/packages/astro/e2e/fixtures/ts-resolution/src/components/Counter.tsx @@ -0,0 +1,18 @@ +import React, { useState } from 'react'; + +export default function Counter({ children, count: initialCount, id }) { + const [count, setCount] = useState(initialCount); + const add = () => setCount((i) => i + 1); + const subtract = () => setCount((i) => i - 1); + + return ( + <> +
+ +
{count}
+ +
+
{children}
+ + ); +} diff --git a/packages/astro/e2e/fixtures/ts-resolution/src/pages/index.astro b/packages/astro/e2e/fixtures/ts-resolution/src/pages/index.astro new file mode 100644 index 000000000..38133a646 --- /dev/null +++ b/packages/astro/e2e/fixtures/ts-resolution/src/pages/index.astro @@ -0,0 +1,18 @@ +--- +import Counter from '../components/Counter.jsx'; + +const someProps = { + count: 0, +}; +--- + + + + + + + +

Hello, client:idle!

+
+ + diff --git a/packages/astro/e2e/ts-resolution.test.js b/packages/astro/e2e/ts-resolution.test.js new file mode 100644 index 000000000..5fb729a5e --- /dev/null +++ b/packages/astro/e2e/ts-resolution.test.js @@ -0,0 +1,64 @@ +import { test as base, expect } from '@playwright/test'; +import { loadFixture } from './test-utils.js'; + +const test = base.extend({ + astro: async ({}, use) => { + const fixture = await loadFixture({ root: './fixtures/ts-resolution/' }); + await use(fixture); + }, +}); + +function runTest(test) { + test('client:idle', async ({ page, astro }) => { + await page.goto(astro.resolveUrl('/')); + + const counter = page.locator('#client-idle'); + await expect(counter, 'component is visible').toBeVisible(); + + const count = counter.locator('pre'); + await expect(count, 'initial count is 0').toHaveText('0'); + + const inc = counter.locator('.increment'); + await inc.click(); + + await expect(count, 'count incremented by 1').toHaveText('1'); + }); +} + +test.describe('TypeScript resolution -', () => { + test.describe('Development', () => { + const t = test.extend({}); + + let devServer; + + t.beforeEach(async ({ astro }) => { + devServer = await astro.startDevServer(); + }); + + t.afterEach(async () => { + await devServer.stop(); + }); + + runTest(t); + }); + + test.describe('Production', () => { + const t = test.extend({}); + + let previewServer; + + t.beforeAll(async ({ astro }) => { + await astro.build(); + }) + + t.beforeEach(async ({ astro }) => { + previewServer = await astro.preview(); + }); + + t.afterEach(async () => { + await previewServer.stop(); + }); + + runTest(t); + }) +}); diff --git a/packages/astro/src/core/build/vite-plugin-analyzer.ts b/packages/astro/src/core/build/vite-plugin-analyzer.ts index 5759cf6bd..04b439b69 100644 --- a/packages/astro/src/core/build/vite-plugin-analyzer.ts +++ b/packages/astro/src/core/build/vite-plugin-analyzer.ts @@ -5,6 +5,7 @@ import type { BuildInternals } from '../../core/build/internal.js'; import type { PluginMetadata as AstroPluginMetadata } from '../../vite-plugin-astro/types'; import { prependForwardSlash } from '../../core/path.js'; +import { resolveClientDevPath } from '../../core/render/dev/resolve.js'; import { getTopLevelPages } from './graph.js'; import { getPageDataByViteID, trackClientOnlyPageDatas } from './internal.js'; @@ -84,7 +85,8 @@ export function vitePluginAnalyzer( const astro = info.meta.astro as AstroPluginMetadata['astro']; for (const c of astro.hydratedComponents) { - internals.discoveredHydratedComponents.add(c.resolvedPath || c.specifier); + const rid = c.resolvedPath ? resolveClientDevPath(c.resolvedPath) : c.specifier; + internals.discoveredHydratedComponents.add(rid); } // Scan hoisted scripts @@ -94,7 +96,7 @@ export function vitePluginAnalyzer( const clientOnlys: string[] = []; for (const c of astro.clientOnlyComponents) { - const cid = c.resolvedPath || c.specifier; + const cid = c.resolvedPath ? resolveClientDevPath(c.resolvedPath) : c.specifier; internals.discoveredClientOnlyComponents.add(cid); clientOnlys.push(cid); } diff --git a/packages/astro/src/core/render/dev/index.ts b/packages/astro/src/core/render/dev/index.ts index 4921fb9d6..86e4840f8 100644 --- a/packages/astro/src/core/render/dev/index.ts +++ b/packages/astro/src/core/render/dev/index.ts @@ -18,6 +18,7 @@ import { createModuleScriptElementWithSrcSet } from '../ssr-element.js'; import { collectMdMetadata } from '../util.js'; import { getStylesForURL } from './css.js'; import { injectTags } from './html.js'; +import { resolveClientDevPath } from './resolve.js'; export interface SSROptions { /** an instance of the AstroConfig */ @@ -178,7 +179,7 @@ export async function render( // Resolves specifiers in the inline hydrated scripts, such as "@astrojs/preact/client.js" async resolve(s: string) { if (s.startsWith('/@fs')) { - return s; + return resolveClientDevPath(s); } return '/@id' + prependForwardSlash(s); }, diff --git a/packages/astro/src/core/render/dev/resolve.ts b/packages/astro/src/core/render/dev/resolve.ts new file mode 100644 index 000000000..a36ea2a4b --- /dev/null +++ b/packages/astro/src/core/render/dev/resolve.ts @@ -0,0 +1,10 @@ + +export function resolveClientDevPath(id: string) { + if(id.startsWith('/@fs')) { + // Vite does not resolve .jsx -> .tsx when coming from the client, so clip the extension. + if(id.endsWith('.jsx')) { + return id.slice(0, id.length - 4); + } + } + return id; +} diff --git a/packages/astro/src/runtime/server/metadata.ts b/packages/astro/src/runtime/server/metadata.ts index c2bf0407f..0cae29b63 100644 --- a/packages/astro/src/runtime/server/metadata.ts +++ b/packages/astro/src/runtime/server/metadata.ts @@ -37,7 +37,15 @@ export class Metadata { } resolvePath(specifier: string): string { - return specifier.startsWith('.') ? new URL(specifier, this.mockURL).pathname : specifier; + if(specifier.startsWith('.')) { + const resolved = new URL(specifier, this.mockURL).pathname; + // Vite does not resolve .jsx -> .tsx when coming from the client, so clip the extension. + if(resolved.startsWith('/@fs') && resolved.endsWith('.jsx')) { + return resolved.slice(0, resolved.length - 4); + } + return resolved; + } + return specifier; } getPath(Component: any): string | null { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 19663cb90..3bb507726 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -956,6 +956,18 @@ importers: '@astrojs/tailwind': link:../../../../integrations/tailwind astro: link:../../.. + packages/astro/e2e/fixtures/ts-resolution: + specifiers: + '@astrojs/react': workspace:* + astro: workspace:* + react: ^18.1.0 + react-dom: ^18.1.0 + dependencies: + '@astrojs/react': link:../../../../integrations/react + astro: link:../../.. + react: 18.1.0 + react-dom: 18.1.0_react@18.1.0 + packages/astro/e2e/fixtures/vue-component: specifiers: '@astrojs/vue': workspace:* @@ -8188,6 +8200,11 @@ packages: /debug/3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true dependencies: ms: 2.1.3 dev: false @@ -11104,6 +11121,8 @@ packages: debug: 3.2.7 iconv-lite: 0.4.24 sax: 1.2.4 + transitivePeerDependencies: + - supports-color dev: false /netmask/2.0.2: @@ -11187,6 +11206,8 @@ packages: rimraf: 2.7.1 semver: 5.7.1 tar: 4.4.19 + transitivePeerDependencies: + - supports-color dev: false /node-releases/2.0.5: