Skip to content

Commit 0a8725d

Browse files
authored
chore(astro): Clean up integration scripts (#7830)
1 parent 07deae3 commit 0a8725d

3 files changed

Lines changed: 119 additions & 47 deletions

File tree

.changeset/fast-ways-notice.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
---
2+
---

packages/astro/src/integration/create-integration.ts

Lines changed: 17 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { envField } from 'astro/config';
44

55
import { name as packageName, version as packageVersion } from '../../package.json';
66
import type { AstroClerkIntegrationParams } from '../types';
7+
import { buildBeforeHydrationSnippet, buildPageLoadSnippet } from './snippets';
78
import { vitePluginAstroConfig } from './vite-plugin-astro-config';
89

910
const buildEnvVarFromOption = (valueToBeStored: unknown, envName: keyof InternalEnv) => {
@@ -94,63 +95,32 @@ function createIntegration<Params extends HotloadAstroClerkIntegrationParams>()
9495
*/
9596

9697
/**
97-
* The above script will run before client frameworks like React hydrate.
98+
* The before-hydration script will run before client frameworks like React hydrate.
9899
* This makes sure that we have initialized a Clerk instance and populated stores in order to avoid hydration issues.
99100
*/
100101
injectScript(
101102
'before-hydration',
102-
`
103-
${command === 'dev' ? `console.log('${packageName}',"Initialize Clerk: before-hydration")` : ''}
104-
import { runInjectionScript } from "${buildImportPath}";
105-
await runInjectionScript(${JSON.stringify(internalParams)});`,
103+
buildBeforeHydrationSnippet({
104+
command,
105+
packageName,
106+
buildImportPath,
107+
internalParams,
108+
}),
106109
);
107110

108111
/**
109-
* The above script only executes if a client framework like React needs to hydrate.
110-
* We need to run the same script again for each page in order to initialize Clerk even if no UI framework is used in the client
111-
* If no UI framework is used in the client, the above script with `before-hydration` will never run
112+
* The page script only executes if a client framework like React needs to hydrate.
113+
* We need to run the same script again for each page in order to initialize Clerk even if no UI framework is used in the client.
114+
* If no UI framework is used in the client, the before-hydration script will never run.
112115
*/
113-
114116
injectScript(
115117
'page',
116-
`
117-
${command === 'dev' ? `console.log("${packageName}","Initialize Clerk: page")` : ''}
118-
import { runInjectionScript, swapDocument } from "${buildImportPath}";
119-
120-
// Taken from https://github.com/withastro/astro/blob/e10b03e88c22592fbb42d7245b65c4f486ab736d/packages/astro/src/transitions/router.ts#L39.
121-
// Importing it directly from astro:transitions/client breaks custom client-side routing
122-
// even when View Transitions is disabled.
123-
const transitionEnabledOnThisPage = () => {
124-
return !!document.querySelector('[name="astro-view-transitions-enabled"]');
125-
}
126-
127-
if (transitionEnabledOnThisPage()) {
128-
// We must do the dynamic imports within the event listeners because otherwise we may race and miss initial astro:page-load
129-
document.addEventListener('astro:before-swap', async (e) => {
130-
const { swapFunctions } = await import('astro:transitions/client');
131-
132-
const clerkComponents = document.querySelector('#clerk-components');
133-
// Keep the div element added by Clerk
134-
if (clerkComponents) {
135-
const clonedEl = clerkComponents.cloneNode(true);
136-
e.newDocument.body.appendChild(clonedEl);
137-
}
138-
139-
e.swap = () => swapDocument(swapFunctions, e.newDocument);
140-
});
141-
142-
document.addEventListener('astro:page-load', async (e) => {
143-
const { navigate } = await import('astro:transitions/client');
144-
145-
await runInjectionScript({
146-
...${JSON.stringify(internalParams)},
147-
routerPush: navigate,
148-
routerReplace: (url) => navigate(url, { history: 'replace' }),
149-
});
150-
})
151-
} else {
152-
await runInjectionScript(${JSON.stringify(internalParams)});
153-
}`,
118+
buildPageLoadSnippet({
119+
command,
120+
packageName,
121+
buildImportPath,
122+
internalParams,
123+
}),
154124
);
155125
},
156126
'astro:config:done': ({ injectTypes }) => {
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import type { ClerkOptions } from '@clerk/shared/types';
2+
3+
/**
4+
* Creates a snippet that initializes Clerk before client-side framework hydration occurs.
5+
*
6+
* This script runs before frameworks like React, Vue, or Svelte hydrate their components,
7+
* ensuring the Clerk instance is ready and stores are populated to prevent hydration mismatches.
8+
* It performs a simple, synchronous initialization without handling view transitions.
9+
*
10+
* @param command - The Astro command being run ('dev' or 'build')
11+
* @param packageName - The name of the Clerk package for debug logging
12+
* @param buildImportPath - The import path to the internal Clerk utilities
13+
* @param internalParams - Clerk configuration options including SDK metadata
14+
* @returns A script string to be injected via Astro's 'before-hydration' stage
15+
*/
16+
export function buildBeforeHydrationSnippet({
17+
command,
18+
packageName,
19+
buildImportPath,
20+
internalParams,
21+
}: {
22+
command: string;
23+
packageName: string;
24+
buildImportPath: string;
25+
internalParams: ClerkOptions;
26+
}) {
27+
return `
28+
${command === 'dev' ? `console.log("${packageName}","Initialize Clerk: before-hydration")` : ''}
29+
import { runInjectionScript } from "${buildImportPath}";
30+
await runInjectionScript(${JSON.stringify(internalParams)});`;
31+
}
32+
33+
/**
34+
* Creates a snippet that initializes Clerk on page load with support for Astro View Transitions.
35+
*
36+
* This script handles two scenarios:
37+
* 1. **With View Transitions enabled**: Listens for astro:page-load and astro:before-swap events
38+
* to properly initialize Clerk and preserve its DOM elements during page transitions.
39+
* 2. **Without View Transitions**: Performs standard initialization on initial page load.
40+
*
41+
* This script is necessary for pages without client-side frameworks, as the before-hydration
42+
* script only runs when framework hydration occurs. This ensures Clerk is always initialized,
43+
* regardless of whether UI frameworks are present.
44+
*
45+
* @param command - The Astro command being run ('dev' or 'build')
46+
* @param packageName - The name of the Clerk package for debug logging
47+
* @param buildImportPath - The import path to the internal Clerk utilities
48+
* @param internalParams - Clerk configuration options including SDK metadata
49+
* @returns A script string to be injected via Astro's 'page' stage
50+
*/
51+
export function buildPageLoadSnippet({
52+
command,
53+
packageName,
54+
buildImportPath,
55+
internalParams,
56+
}: {
57+
command: string;
58+
packageName: string;
59+
buildImportPath: string;
60+
internalParams: ClerkOptions;
61+
}) {
62+
return `
63+
${command === 'dev' ? `console.log("${packageName}","Initialize Clerk: page")` : ''}
64+
import { runInjectionScript, swapDocument } from "${buildImportPath}";
65+
66+
// Taken from https://github.com/withastro/astro/blob/e10b03e88c22592fbb42d7245b65c4f486ab736d/packages/astro/src/transitions/router.ts#L39.
67+
// Importing it directly from astro:transitions/client breaks custom client-side routing
68+
// even when View Transitions is disabled.
69+
const transitionEnabledOnThisPage = () => {
70+
return !!document.querySelector('[name="astro-view-transitions-enabled"]');
71+
}
72+
73+
if (transitionEnabledOnThisPage()) {
74+
// We must do the dynamic imports within the event listeners because otherwise we may race and miss initial astro:page-load
75+
document.addEventListener('astro:before-swap', async (e) => {
76+
const { swapFunctions } = await import('astro:transitions/client');
77+
78+
const clerkComponents = document.querySelector('#clerk-components');
79+
// Keep the div element added by Clerk
80+
if (clerkComponents) {
81+
const clonedEl = clerkComponents.cloneNode(true);
82+
e.newDocument.body.appendChild(clonedEl);
83+
}
84+
85+
e.swap = () => swapDocument(swapFunctions, e.newDocument);
86+
});
87+
88+
document.addEventListener('astro:page-load', async (e) => {
89+
const { navigate } = await import('astro:transitions/client');
90+
91+
await runInjectionScript({
92+
...${JSON.stringify(internalParams)},
93+
routerPush: navigate,
94+
routerReplace: (url) => navigate(url, { history: 'replace' }),
95+
});
96+
})
97+
} else {
98+
await runInjectionScript(${JSON.stringify(internalParams)});
99+
}`;
100+
}

0 commit comments

Comments
 (0)