Address feedback from review
This commit is contained in:
parent
97511dd714
commit
30003d7b54
7 changed files with 70 additions and 232 deletions
|
@ -111,10 +111,10 @@ export default defineConfig({
|
||||||
|
|
||||||
#### `beforeSend`
|
#### `beforeSend`
|
||||||
|
|
||||||
Since functions can't be passed down via `astro.config.mjs`, you need to export the `beforeSend` function in a separate file inside your root called `vercel-web-analytics.ts`.
|
To define the `beforeSend` function, you need to create a separate file inside your root called `vercel-web-analytics.ts`.
|
||||||
If you're not using TypeScript, you can define the function inside `vercel-web-analytics.js`.
|
If you're not using TypeScript, you can define the function inside `vercel-web-analytics.js`.
|
||||||
|
|
||||||
```js
|
```ts
|
||||||
// vercel-web-analytics.ts
|
// vercel-web-analytics.ts
|
||||||
import type { VercelWebAnalyticsBeforeSend } from '@astrojs/vercel';
|
import type { VercelWebAnalyticsBeforeSend } from '@astrojs/vercel';
|
||||||
|
|
||||||
|
@ -128,8 +128,6 @@ export const beforeSend: VercelWebAnalyticsBeforeSend = (event) => {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
````js
|
|
||||||
|
|
||||||
### Speed Insights
|
### Speed Insights
|
||||||
You can enable [Vercel Speed Insights](https://vercel.com/docs/concepts/speed-insights) by setting `speedInsights: { enabled: true }`. This will collect and send Web Vital data to Vercel.
|
You can enable [Vercel Speed Insights](https://vercel.com/docs/concepts/speed-insights) by setting `speedInsights: { enabled: true }`. This will collect and send Web Vital data to Vercel.
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/internal-helpers": "workspace:*",
|
"@astrojs/internal-helpers": "workspace:*",
|
||||||
"@vercel/analytics": "~1.0.0",
|
"@vercel/analytics": "^1.0.2",
|
||||||
"@vercel/nft": "^0.23.1",
|
"@vercel/nft": "^0.23.1",
|
||||||
"esbuild": "^0.19.2",
|
"esbuild": "^0.19.2",
|
||||||
"fast-glob": "^3.3.1",
|
"fast-glob": "^3.3.1",
|
||||||
|
|
|
@ -1,186 +0,0 @@
|
||||||
import type { AstroAdapter, AstroConfig, AstroIntegration } from 'astro';
|
|
||||||
|
|
||||||
import esbuild from 'esbuild';
|
|
||||||
import { relative as relativePath } from 'node:path';
|
|
||||||
import { fileURLToPath } from 'node:url';
|
|
||||||
|
|
||||||
import { defaultImageConfig, getImageConfig, type VercelImageConfig } from '../image/shared.js';
|
|
||||||
import {
|
|
||||||
copyFilesToFunction,
|
|
||||||
getFilesFromFolder,
|
|
||||||
getVercelOutput,
|
|
||||||
removeDir,
|
|
||||||
writeJson,
|
|
||||||
} from '../lib/fs.js';
|
|
||||||
import { getRedirects } from '../lib/redirects.js';
|
|
||||||
import {
|
|
||||||
getInjectableWebAnalyticsContent,
|
|
||||||
type VercelWebAnalyticsConfig,
|
|
||||||
} from '../lib/web-analytics.js';
|
|
||||||
import {
|
|
||||||
getSpeedInsightsViteConfig,
|
|
||||||
type VercelSpeedInsightsConfig,
|
|
||||||
} from '../lib/speed-insights.js';
|
|
||||||
|
|
||||||
const PACKAGE_NAME = '@astrojs/vercel/edge';
|
|
||||||
|
|
||||||
function getAdapter(): AstroAdapter {
|
|
||||||
return {
|
|
||||||
name: PACKAGE_NAME,
|
|
||||||
serverEntrypoint: `${PACKAGE_NAME}/entrypoint`,
|
|
||||||
exports: ['default'],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface VercelEdgeConfig {
|
|
||||||
webAnalytics?: VercelWebAnalyticsConfig;
|
|
||||||
speedInsights?: VercelSpeedInsightsConfig;
|
|
||||||
includeFiles?: string[];
|
|
||||||
imageService?: boolean;
|
|
||||||
imagesConfig?: VercelImageConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function vercelEdge({
|
|
||||||
webAnalytics,
|
|
||||||
speedInsights,
|
|
||||||
includeFiles = [],
|
|
||||||
imageService,
|
|
||||||
imagesConfig,
|
|
||||||
}: VercelEdgeConfig = {}): AstroIntegration {
|
|
||||||
let _config: AstroConfig;
|
|
||||||
let buildTempFolder: URL;
|
|
||||||
let functionFolder: URL;
|
|
||||||
let serverEntry: string;
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: PACKAGE_NAME,
|
|
||||||
hooks: {
|
|
||||||
'astro:config:setup': async ({ command, config, updateConfig, injectScript }) => {
|
|
||||||
if (webAnalytics?.enabled) {
|
|
||||||
injectScript(
|
|
||||||
'page',
|
|
||||||
await getInjectableWebAnalyticsContent(
|
|
||||||
{
|
|
||||||
...webAnalytics.config,
|
|
||||||
mode: command === 'dev' ? 'development' : 'production',
|
|
||||||
},
|
|
||||||
config.root
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (command === 'build' && speedInsights?.enabled) {
|
|
||||||
injectScript('page', 'import "@astrojs/vercel/speed-insights"');
|
|
||||||
}
|
|
||||||
const outDir = getVercelOutput(config.root);
|
|
||||||
updateConfig({
|
|
||||||
outDir,
|
|
||||||
build: {
|
|
||||||
serverEntry: 'entry.mjs',
|
|
||||||
client: new URL('./static/', outDir),
|
|
||||||
server: new URL('./dist/', config.root),
|
|
||||||
},
|
|
||||||
vite: {
|
|
||||||
...getSpeedInsightsViteConfig(speedInsights?.enabled),
|
|
||||||
ssr: {
|
|
||||||
external: ['@vercel/nft'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
...getImageConfig(imageService, imagesConfig, command),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
'astro:config:done': ({ setAdapter, config }) => {
|
|
||||||
setAdapter(getAdapter());
|
|
||||||
_config = config;
|
|
||||||
buildTempFolder = config.build.server;
|
|
||||||
functionFolder = new URL('./functions/render.func/', config.outDir);
|
|
||||||
serverEntry = config.build.serverEntry;
|
|
||||||
|
|
||||||
if (config.output === 'static') {
|
|
||||||
throw new Error(`
|
|
||||||
[@astrojs/vercel] \`output: "server"\` or \`output: "hybrid"\` is required to use the edge adapter.
|
|
||||||
|
|
||||||
`);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'astro:build:setup': ({ vite, target }) => {
|
|
||||||
if (target === 'server') {
|
|
||||||
vite.resolve ||= {};
|
|
||||||
vite.resolve.alias ||= {};
|
|
||||||
|
|
||||||
const aliases = [{ find: 'react-dom/server', replacement: 'react-dom/server.browser' }];
|
|
||||||
|
|
||||||
if (Array.isArray(vite.resolve.alias)) {
|
|
||||||
vite.resolve.alias = [...vite.resolve.alias, ...aliases];
|
|
||||||
} else {
|
|
||||||
for (const alias of aliases) {
|
|
||||||
(vite.resolve.alias as Record<string, string>)[alias.find] = alias.replacement;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vite.ssr ||= {};
|
|
||||||
vite.ssr.target = 'webworker';
|
|
||||||
|
|
||||||
// Vercel edge runtime is a special webworker-ish environment that supports process.env,
|
|
||||||
// but Vite would replace away `process.env` in webworkers, so we set a define here to prevent it
|
|
||||||
vite.define = {
|
|
||||||
'process.env': 'process.env',
|
|
||||||
...vite.define,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'astro:build:done': async ({ routes }) => {
|
|
||||||
const entry = new URL(serverEntry, buildTempFolder);
|
|
||||||
const generatedFiles = await getFilesFromFolder(buildTempFolder);
|
|
||||||
const entryPath = fileURLToPath(entry);
|
|
||||||
|
|
||||||
await esbuild.build({
|
|
||||||
target: 'es2020',
|
|
||||||
platform: 'browser',
|
|
||||||
// https://runtime-keys.proposal.wintercg.org/#edge-light
|
|
||||||
conditions: ['edge-light', 'worker', 'browser'],
|
|
||||||
entryPoints: [entryPath],
|
|
||||||
outfile: entryPath,
|
|
||||||
allowOverwrite: true,
|
|
||||||
format: 'esm',
|
|
||||||
bundle: true,
|
|
||||||
minify: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Copy entry and other server files
|
|
||||||
const commonAncestor = await copyFilesToFunction(
|
|
||||||
[...generatedFiles, ...includeFiles.map((file) => new URL(file, _config.root))],
|
|
||||||
functionFolder
|
|
||||||
);
|
|
||||||
|
|
||||||
// Remove temporary folder
|
|
||||||
await removeDir(buildTempFolder);
|
|
||||||
|
|
||||||
// Edge function config
|
|
||||||
// https://vercel.com/docs/build-output-api/v3#vercel-primitives/edge-functions/configuration
|
|
||||||
await writeJson(new URL(`./.vc-config.json`, functionFolder), {
|
|
||||||
runtime: 'edge',
|
|
||||||
entrypoint: relativePath(commonAncestor, entryPath),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Output configuration
|
|
||||||
// https://vercel.com/docs/build-output-api/v3#build-output-configuration
|
|
||||||
await writeJson(new URL(`./config.json`, _config.outDir), {
|
|
||||||
version: 3,
|
|
||||||
routes: [
|
|
||||||
...getRedirects(routes, _config),
|
|
||||||
{
|
|
||||||
src: `^/${_config.build.assets}/(.*)$`,
|
|
||||||
headers: { 'cache-control': 'public, max-age=31536000, immutable' },
|
|
||||||
continue: true,
|
|
||||||
},
|
|
||||||
{ handle: 'filesystem' },
|
|
||||||
{ src: '/.*', dest: 'render' },
|
|
||||||
],
|
|
||||||
...(imageService || imagesConfig
|
|
||||||
? { images: imagesConfig ? imagesConfig : defaultImageConfig }
|
|
||||||
: {}),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,51 +1,71 @@
|
||||||
|
import { AstroError } from 'astro/errors';
|
||||||
import type { AnalyticsProps } from '@vercel/analytics';
|
import type { AnalyticsProps } from '@vercel/analytics';
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
|
import { existsSync } from 'node:fs';
|
||||||
|
import type { AstroIntegrationLogger } from 'astro';
|
||||||
|
|
||||||
export type VercelWebAnalyticsConfig = {
|
export type VercelWebAnalyticsConfig = {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
config?: Omit<AnalyticsProps, 'beforeSend'>;
|
config?: Omit<AnalyticsProps, 'beforeSend'>;
|
||||||
};
|
};
|
||||||
|
|
||||||
async function getWebAnalyticsFunctions(root: URL) {
|
async function getWebAnalyticsFunctions({
|
||||||
try {
|
root,
|
||||||
const files = await Promise.all([
|
logger,
|
||||||
import(/* @vite-ignore */ fileURLToPath(new URL('./vercel-web-analytics.ts', root))).catch(
|
}: {
|
||||||
() => undefined
|
root: URL;
|
||||||
),
|
logger: AstroIntegrationLogger;
|
||||||
import(/* @vite-ignore */ fileURLToPath(new URL('./vercel-web-analytics.js', root))).catch(
|
}) {
|
||||||
() => undefined
|
const tsPath = fileURLToPath(new URL('./vercel-web-analytics.ts', root));
|
||||||
),
|
const jsPath = fileURLToPath(new URL('./vercel-web-analytics.js', root));
|
||||||
]);
|
|
||||||
|
|
||||||
const functions = files[0] || files[1];
|
const tsFileExists = existsSync(tsPath);
|
||||||
|
const jsFileExists = existsSync(jsPath);
|
||||||
|
|
||||||
if (functions?.default) {
|
if (tsFileExists && jsFileExists) {
|
||||||
if (typeof functions.default.beforeSend !== 'function') {
|
logger.warn(
|
||||||
throw new Error(
|
`@astrojs/vercel: Both \`vercel-web-analytics.ts\` and \`vercel-web-analytics.js\` exist. Using \`vercel-web-analytics.ts\`.`
|
||||||
`@astrojs/vercel: ./vercel-web-analytics.js should export a \`beforeSend\` function.`
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tsFileExists && !jsFileExists) {
|
||||||
|
logger.debug(
|
||||||
|
`@astrojs/vercel: \`vercel-web-analytics.ts\` or \`vercel-web-analytics.js\` not found.`
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
beforeSend: undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const functions = await import(
|
||||||
|
tsFileExists ? /* @vite-ignore */ tsPath : /* @vite-ignore */ jsPath
|
||||||
|
);
|
||||||
|
|
||||||
|
if (typeof functions.beforeSend !== 'function') {
|
||||||
|
throw new AstroError(
|
||||||
|
`@astrojs/vercel: \`vercel-web-analytics.${
|
||||||
|
tsFileExists ? 'ts' : 'js'
|
||||||
|
}\` must export a \`beforeSend\` function.`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
beforeSend: functions.default.beforeSend,
|
beforeSend: functions.beforeSend,
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
beforeSend: undefined,
|
|
||||||
};
|
|
||||||
} catch (e) {
|
|
||||||
return {
|
|
||||||
beforeSend: undefined,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getInjectableWebAnalyticsContent(
|
export async function getInjectableWebAnalyticsContent({
|
||||||
config: Omit<AnalyticsProps, 'beforeSend'> | undefined,
|
config,
|
||||||
root: URL
|
astro,
|
||||||
) {
|
}: {
|
||||||
const { beforeSend } = await getWebAnalyticsFunctions(root);
|
config: Omit<AnalyticsProps, 'beforeSend'> | undefined;
|
||||||
|
astro: {
|
||||||
|
root: URL;
|
||||||
|
logger: AstroIntegrationLogger;
|
||||||
|
};
|
||||||
|
}) {
|
||||||
|
const { beforeSend } = await getWebAnalyticsFunctions(astro);
|
||||||
|
|
||||||
return `import { inject } from '@vercel/analytics';
|
return `import { inject } from '@vercel/analytics';
|
||||||
inject({
|
inject({
|
||||||
|
|
|
@ -116,17 +116,20 @@ export default function vercelServerless({
|
||||||
return {
|
return {
|
||||||
name: PACKAGE_NAME,
|
name: PACKAGE_NAME,
|
||||||
hooks: {
|
hooks: {
|
||||||
'astro:config:setup': async ({ command, config, updateConfig, injectScript }) => {
|
'astro:config:setup': async ({ command, config, updateConfig, injectScript, logger }) => {
|
||||||
if (webAnalytics?.enabled) {
|
if (webAnalytics?.enabled) {
|
||||||
injectScript(
|
injectScript(
|
||||||
'page',
|
'page',
|
||||||
await getInjectableWebAnalyticsContent(
|
await getInjectableWebAnalyticsContent({
|
||||||
{
|
config: {
|
||||||
...webAnalytics.config,
|
...webAnalytics.config,
|
||||||
mode: command === 'dev' ? 'development' : 'production',
|
mode: command === 'dev' ? 'development' : 'production',
|
||||||
},
|
},
|
||||||
config.root
|
astro: {
|
||||||
)
|
root: config.root,
|
||||||
|
logger,
|
||||||
|
},
|
||||||
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (command === 'build' && speedInsights?.enabled) {
|
if (command === 'build' && speedInsights?.enabled) {
|
||||||
|
|
|
@ -37,17 +37,20 @@ export default function vercelStatic({
|
||||||
return {
|
return {
|
||||||
name: '@astrojs/vercel',
|
name: '@astrojs/vercel',
|
||||||
hooks: {
|
hooks: {
|
||||||
'astro:config:setup': async ({ command, config, injectScript, updateConfig }) => {
|
'astro:config:setup': async ({ command, config, injectScript, updateConfig, logger }) => {
|
||||||
if (webAnalytics?.enabled) {
|
if (webAnalytics?.enabled) {
|
||||||
injectScript(
|
injectScript(
|
||||||
'page',
|
'page',
|
||||||
await getInjectableWebAnalyticsContent(
|
await getInjectableWebAnalyticsContent({
|
||||||
{
|
config: {
|
||||||
...webAnalytics.config,
|
...webAnalytics.config,
|
||||||
mode: command === 'dev' ? 'development' : 'production',
|
mode: command === 'dev' ? 'development' : 'production',
|
||||||
},
|
},
|
||||||
config.root
|
astro: {
|
||||||
)
|
root: config.root,
|
||||||
|
logger,
|
||||||
|
},
|
||||||
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (command === 'build' && speedInsights?.enabled) {
|
if (command === 'build' && speedInsights?.enabled) {
|
||||||
|
|
|
@ -4679,7 +4679,7 @@ importers:
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../internal-helpers
|
version: link:../../internal-helpers
|
||||||
'@vercel/analytics':
|
'@vercel/analytics':
|
||||||
specifier: ~1.0.0
|
specifier: ^1.0.2
|
||||||
version: 1.0.2
|
version: 1.0.2
|
||||||
'@vercel/nft':
|
'@vercel/nft':
|
||||||
specifier: ^0.23.1
|
specifier: ^0.23.1
|
||||||
|
|
Loading…
Reference in a new issue