Resolve .jsx -> .tsx in hydrated components (#3663)

This commit is contained in:
Matthew Phillips 2022-06-21 12:35:11 -04:00 committed by GitHub
parent e13ce4d751
commit c20b93c484
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 169 additions and 4 deletions

View file

@ -0,0 +1,5 @@
---
'astro': patch
---
Resolve .jsx -> .tsx in hydrated components

View file

@ -0,0 +1,7 @@
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
// https://astro.build/config
export default defineConfig({
integrations: [react()],
});

View file

@ -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"
}
}

View file

@ -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 (
<>
<div id={id} className="counter">
<button className="decrement" onClick={subtract}>-</button>
<pre>{count}</pre>
<button className="increment" onClick={add}>+</button>
</div>
<div className="counter-message">{children}</div>
</>
);
}

View file

@ -0,0 +1,18 @@
---
import Counter from '../components/Counter.jsx';
const someProps = {
count: 0,
};
---
<html>
<head>
<!-- Head Stuff -->
</head>
<body>
<Counter id="client-idle" {...someProps} client:idle>
<h1>Hello, client:idle!</h1>
</Counter>
</body>
</html>

View file

@ -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);
})
});

View file

@ -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);
}

View file

@ -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);
},

View file

@ -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;
}

View file

@ -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 {

View file

@ -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: