Fix hydration path for circular imports (#6244)

This commit is contained in:
Bjorn Lu 2023-02-15 09:49:57 +01:00 committed by GitHub
parent ac3649bb58
commit 1c678f7ebf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 106 additions and 6 deletions

View file

@ -0,0 +1,5 @@
---
'astro': patch
---
Fix hydrate loading path to prevent multiple instance loaded for circular imports

View file

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

View file

@ -0,0 +1,12 @@
{
"name": "@e2e/solid-circular",
"version": "0.0.0",
"private": true,
"dependencies": {
"@astrojs/solid-js": "workspace:*",
"astro": "workspace:*"
},
"devDependencies": {
"solid-js": "^1.4.3"
}
}

View file

@ -0,0 +1,12 @@
import { Component, createContext } from 'solid-js';
import { SimpleDiv } from './SimpleDiv';
export const ApplicationContext = createContext([{ lng: 'en' }, {}]);
export const ContextProvider: Component = () => {
return (
<ApplicationContext.Provider value={[{ lng: 'fr' }]}>
<SimpleDiv />
</ApplicationContext.Provider>
);
};

View file

@ -0,0 +1,8 @@
import { Component, useContext } from 'solid-js';
import { ApplicationContext } from './ContextProvider';
export const SimpleDiv: Component = () => {
const [context] = useContext(ApplicationContext);
return <div id="context">{context.lng}</div>;
};

View file

@ -0,0 +1 @@
/// <reference types="astro/client" />

View file

@ -0,0 +1,15 @@
---
import { ContextProvider } from "../components/ContextProvider.tsx";
---
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
</head>
<body>
<main>
<ContextProvider client:only />
</main>
</body>
</html>

View file

@ -0,0 +1,23 @@
import { expect } from '@playwright/test';
import { testFactory } from './test-utils.js';
const test = testFactory({ root: './fixtures/solid-circular/' });
let devServer;
test.beforeAll(async ({ astro }) => {
devServer = await astro.startDevServer();
});
test.afterAll(async () => {
await devServer.stop();
});
test.describe('Circular imports with Solid', () => {
test('Context', async ({ astro, page }) => {
await page.goto('/');
const wrapper = page.locator('#context');
await expect(wrapper, 'context should not be duplicated').toHaveText('fr');
});
});

View file

@ -29,7 +29,7 @@ export function createDevelopmentEnvironment(
mode,
// This will be overridden in the dev server
renderers: [],
resolve: createResolve(loader),
resolve: createResolve(loader, settings.config.root),
routeCache: new RouteCache(logging, mode),
site: settings.config.site,
ssr: settings.config.output === 'server',

View file

@ -1,17 +1,17 @@
import type { ModuleLoader } from '../../module-loader/index';
import { resolveIdToUrl } from '../../util.js';
export function createResolve(loader: ModuleLoader) {
export function createResolve(loader: ModuleLoader, root: URL) {
// Resolves specifiers in the inline hydrated scripts, such as:
// - @astrojs/preact/client.js
// - @/components/Foo.vue
// - /Users/macos/project/src/Foo.vue
// - C:/Windows/project/src/Foo.vue (normalized slash)
return async function (s: string) {
const url = await resolveIdToUrl(loader, s);
const url = await resolveIdToUrl(loader, s, root);
// Vite does not resolve .jsx -> .tsx when coming from hydration script import,
// clip it so Vite is able to resolve implicitly.
if (url.startsWith('/@fs') && url.endsWith('.jsx')) {
if (url.startsWith('/') && url.endsWith('.jsx')) {
return url.slice(0, -4);
} else {
return url;

View file

@ -161,7 +161,7 @@ export function emoji(char: string, fallback: string) {
*/
// NOTE: `/@id/` should only be used when the id is fully resolved
// TODO: Export a helper util from Vite
export async function resolveIdToUrl(loader: ModuleLoader, id: string) {
export async function resolveIdToUrl(loader: ModuleLoader, id: string, root?: URL) {
let resultId = await loader.resolveId(id, undefined);
// Try resolve jsx to tsx
if (!resultId && id.endsWith('.jsx')) {
@ -171,7 +171,13 @@ export async function resolveIdToUrl(loader: ModuleLoader, id: string) {
return VALID_ID_PREFIX + id;
}
if (path.isAbsolute(resultId)) {
return '/@fs' + prependForwardSlash(resultId);
const normalizedRoot = root && normalizePath(fileURLToPath(root));
// Convert to root-relative path if path is inside root
if (normalizedRoot && resultId.startsWith(normalizedRoot)) {
return resultId.slice(normalizedRoot.length - 1);
} else {
return '/@fs' + prependForwardSlash(resultId);
}
}
return VALID_ID_PREFIX + resultId;
}

View file

@ -1014,6 +1014,17 @@ importers:
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
packages/astro/e2e/fixtures/solid-circular:
specifiers:
'@astrojs/solid-js': workspace:*
astro: workspace:*
solid-js: ^1.4.3
dependencies:
'@astrojs/solid-js': link:../../../../integrations/solid
astro: link:../../..
devDependencies:
solid-js: 1.6.10
packages/astro/e2e/fixtures/solid-component:
specifiers:
'@astrojs/mdx': workspace:*