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`
|
||||
|
||||
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`.
|
||||
|
||||
```js
|
||||
```ts
|
||||
// vercel-web-analytics.ts
|
||||
import type { VercelWebAnalyticsBeforeSend } from '@astrojs/vercel';
|
||||
|
||||
|
@ -128,8 +128,6 @@ export const beforeSend: VercelWebAnalyticsBeforeSend = (event) => {
|
|||
}
|
||||
```
|
||||
|
||||
````js
|
||||
|
||||
### 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.
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@astrojs/internal-helpers": "workspace:*",
|
||||
"@vercel/analytics": "~1.0.0",
|
||||
"@vercel/analytics": "^1.0.2",
|
||||
"@vercel/nft": "^0.23.1",
|
||||
"esbuild": "^0.19.2",
|
||||
"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 { fileURLToPath } from 'url';
|
||||
import { existsSync } from 'node:fs';
|
||||
import type { AstroIntegrationLogger } from 'astro';
|
||||
|
||||
export type VercelWebAnalyticsConfig = {
|
||||
enabled: boolean;
|
||||
config?: Omit<AnalyticsProps, 'beforeSend'>;
|
||||
};
|
||||
|
||||
async function getWebAnalyticsFunctions(root: URL) {
|
||||
try {
|
||||
const files = await Promise.all([
|
||||
import(/* @vite-ignore */ fileURLToPath(new URL('./vercel-web-analytics.ts', root))).catch(
|
||||
() => undefined
|
||||
),
|
||||
import(/* @vite-ignore */ fileURLToPath(new URL('./vercel-web-analytics.js', root))).catch(
|
||||
() => undefined
|
||||
),
|
||||
]);
|
||||
async function getWebAnalyticsFunctions({
|
||||
root,
|
||||
logger,
|
||||
}: {
|
||||
root: URL;
|
||||
logger: AstroIntegrationLogger;
|
||||
}) {
|
||||
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 (typeof functions.default.beforeSend !== 'function') {
|
||||
throw new Error(
|
||||
`@astrojs/vercel: ./vercel-web-analytics.js should export a \`beforeSend\` function.`
|
||||
);
|
||||
}
|
||||
if (tsFileExists && jsFileExists) {
|
||||
logger.warn(
|
||||
`@astrojs/vercel: Both \`vercel-web-analytics.ts\` and \`vercel-web-analytics.js\` exist. Using \`vercel-web-analytics.ts\`.`
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
beforeSend: functions.default.beforeSend,
|
||||
};
|
||||
}
|
||||
if (!tsFileExists && !jsFileExists) {
|
||||
logger.debug(
|
||||
`@astrojs/vercel: \`vercel-web-analytics.ts\` or \`vercel-web-analytics.js\` not found.`
|
||||
);
|
||||
|
||||
return {
|
||||
beforeSend: undefined,
|
||||
};
|
||||
} catch (e) {
|
||||
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 {
|
||||
beforeSend: functions.beforeSend,
|
||||
};
|
||||
}
|
||||
|
||||
export async function getInjectableWebAnalyticsContent(
|
||||
config: Omit<AnalyticsProps, 'beforeSend'> | undefined,
|
||||
root: URL
|
||||
) {
|
||||
const { beforeSend } = await getWebAnalyticsFunctions(root);
|
||||
export async function getInjectableWebAnalyticsContent({
|
||||
config,
|
||||
astro,
|
||||
}: {
|
||||
config: Omit<AnalyticsProps, 'beforeSend'> | undefined;
|
||||
astro: {
|
||||
root: URL;
|
||||
logger: AstroIntegrationLogger;
|
||||
};
|
||||
}) {
|
||||
const { beforeSend } = await getWebAnalyticsFunctions(astro);
|
||||
|
||||
return `import { inject } from '@vercel/analytics';
|
||||
inject({
|
||||
|
|
|
@ -116,17 +116,20 @@ export default function vercelServerless({
|
|||
return {
|
||||
name: PACKAGE_NAME,
|
||||
hooks: {
|
||||
'astro:config:setup': async ({ command, config, updateConfig, injectScript }) => {
|
||||
'astro:config:setup': async ({ command, config, updateConfig, injectScript, logger }) => {
|
||||
if (webAnalytics?.enabled) {
|
||||
injectScript(
|
||||
'page',
|
||||
await getInjectableWebAnalyticsContent(
|
||||
{
|
||||
await getInjectableWebAnalyticsContent({
|
||||
config: {
|
||||
...webAnalytics.config,
|
||||
mode: command === 'dev' ? 'development' : 'production',
|
||||
},
|
||||
config.root
|
||||
)
|
||||
astro: {
|
||||
root: config.root,
|
||||
logger,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
if (command === 'build' && speedInsights?.enabled) {
|
||||
|
|
|
@ -37,17 +37,20 @@ export default function vercelStatic({
|
|||
return {
|
||||
name: '@astrojs/vercel',
|
||||
hooks: {
|
||||
'astro:config:setup': async ({ command, config, injectScript, updateConfig }) => {
|
||||
'astro:config:setup': async ({ command, config, injectScript, updateConfig, logger }) => {
|
||||
if (webAnalytics?.enabled) {
|
||||
injectScript(
|
||||
'page',
|
||||
await getInjectableWebAnalyticsContent(
|
||||
{
|
||||
await getInjectableWebAnalyticsContent({
|
||||
config: {
|
||||
...webAnalytics.config,
|
||||
mode: command === 'dev' ? 'development' : 'production',
|
||||
},
|
||||
config.root
|
||||
)
|
||||
astro: {
|
||||
root: config.root,
|
||||
logger,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
if (command === 'build' && speedInsights?.enabled) {
|
||||
|
|
|
@ -4679,7 +4679,7 @@ importers:
|
|||
specifier: workspace:*
|
||||
version: link:../../internal-helpers
|
||||
'@vercel/analytics':
|
||||
specifier: ~1.0.0
|
||||
specifier: ^1.0.2
|
||||
version: 1.0.2
|
||||
'@vercel/nft':
|
||||
specifier: ^0.23.1
|
||||
|
|
Loading…
Reference in a new issue