Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(plugin-vercel-analytics): add new vercel analytics plugin #9687

Merged
merged 23 commits into from
Feb 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/docusaurus-plugin-vercel-analytics/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.tsbuildinfo*
tsconfig*
__tests__
7 changes: 7 additions & 0 deletions packages/docusaurus-plugin-vercel-analytics/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# `@docusaurus/plugin-vercel-analytics`

[Vercel analytics](https://vercel.com/docs/analytics) plugin for Docusaurus.

## Usage

See [plugin-vercel-analytics documentation](https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-vercel-analytics).
36 changes: 36 additions & 0 deletions packages/docusaurus-plugin-vercel-analytics/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"name": "@docusaurus/plugin-vercel-analytics",
"version": "3.0.0",
"description": "Global vercel analytics plugin for Docusaurus.",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"publishConfig": {
"access": "public"
},
"scripts": {
"build": "tsc --build",
"watch": "tsc --build --watch"
},
"repository": {
"type": "git",
"url": "https://github.com/facebook/docusaurus.git",
"directory": "packages/docusaurus-plugin-vercel-analytics"
},
"license": "MIT",
"dependencies": {
"@docusaurus/core": "3.0.0",
"@docusaurus/logger": "3.0.0",
"@docusaurus/types": "3.0.0",
"@docusaurus/utils-validation": "3.0.0",
"@docusaurus/utils": "3.0.0",
"@vercel/analytics": "^1.1.1",
"tslib": "^2.6.0"
},
"peerDependencies": {
"react": "^18.0.0",
"react-dom": "^18.0.0"
},
"engines": {
"node": ">=18.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import {normalizePluginOptions} from '@docusaurus/utils-validation';
import {validateOptions, type PluginOptions, type Options} from '../options';
import type {Validate} from '@docusaurus/types';

function testValidateOptions(options: Options) {
return validateOptions({
validate: normalizePluginOptions as Validate<Options, PluginOptions>,
options,
});
}

function validationResult(options: Options) {
return {
id: 'default',
...options,
};
}

describe('validateOptions', () => {
it('accepts for undefined options', () => {
// @ts-expect-error: TS should error
expect(testValidateOptions(undefined)).toEqual(validationResult(undefined));
});

it('throws for custom id', () => {
const config: Options = {id: 'custom', mode: 'auto', debug: false};
expect(() => testValidateOptions(config))
.toThrowErrorMatchingInlineSnapshot(`
"You site uses the Vercel Analytics plugin with a custom plugin id (custom).
But this plugin is only supposed to be used at most once per site. Therefore providing a custom plugin id is unsupported."
`);
});

it('accept for default id', () => {
const config: Options = {id: 'default', mode: 'auto', debug: false};
expect(testValidateOptions(config)).toEqual(validationResult(config));
});

it('throws for null options', () => {
// @ts-expect-error: TS should error
expect(() => testValidateOptions(null)).toThrowErrorMatchingInlineSnapshot(
`""value" must be of type object"`,
);
});

it('accept for empty object options', () => {
const config: Options = {};
expect(testValidateOptions(config)).toEqual(validationResult(config));
});

it('throws for number options', () => {
expect(
// @ts-expect-error: TS should error
() => testValidateOptions(42),
).toThrowErrorMatchingInlineSnapshot(`""value" must be of type object"`);
});

it('throws for null mode', () => {
expect(
// @ts-expect-error: TS should error
() => testValidateOptions({mode: null}),
).toThrowErrorMatchingInlineSnapshot(
`""mode" must be one of [auto, production, development]"`,
);
});
it('throws for number mode', () => {
expect(
// @ts-expect-error: TS should error
() => testValidateOptions({mode: 42}),
).toThrowErrorMatchingInlineSnapshot(
`""mode" must be one of [auto, production, development]"`,
);
});
it('throws for empty mode', () => {
expect(() =>
// @ts-expect-error: TS should error
testValidateOptions({mode: ''}),
).toThrowErrorMatchingInlineSnapshot(
`""mode" must be one of [auto, production, development]"`,
);
});

it('accepts debug true', () => {
const config: Options = {
debug: true,
};
expect(testValidateOptions(config)).toEqual(validationResult(config));
});

it('accepts debug false', () => {
const config: Options = {
debug: false,
};
expect(testValidateOptions(config)).toEqual(validationResult(config));
});

it('accepts mode prod', () => {
const config: Options = {
mode: 'production',
debug: false,
};
expect(testValidateOptions(config)).toEqual(validationResult(config));
});

it('accepts mode dev', () => {
const config: Options = {
mode: 'development',
debug: false,
};
expect(testValidateOptions(config)).toEqual(validationResult(config));
});

it('accepts mode prod with debug', () => {
const config: Options = {
mode: 'production',
debug: true,
};
expect(testValidateOptions(config)).toEqual(validationResult(config));
});

it('accepts mode dev with debug', () => {
const config: Options = {
mode: 'development',
debug: true,
};
expect(testValidateOptions(config)).toEqual(validationResult(config));
});
});
17 changes: 17 additions & 0 deletions packages/docusaurus-plugin-vercel-analytics/src/analytics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import {inject} from '@vercel/analytics';
import globalData from '@generated/globalData';
import type {PluginOptions} from './options';

const {debug, mode} = globalData['docusaurus-plugin-vercel-analytics']
?.default as PluginOptions;

slorber marked this conversation as resolved.
Show resolved Hide resolved
inject({
mode,
debug,
});
32 changes: 32 additions & 0 deletions packages/docusaurus-plugin-vercel-analytics/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import type {LoadContext, Plugin} from '@docusaurus/types';
import type {PluginOptions, Options} from './options';

export default function pluginVercelAnalytics(
context: LoadContext,
options: PluginOptions,
): Plugin {
const isProd = process.env.NODE_ENV === 'production';

return {
name: 'docusaurus-plugin-vercel-analytics',

getClientModules() {
return isProd ? ['./analytics'] : [];
},

contentLoaded({actions}) {
actions.setGlobalData(options);
},
};
}

export {validateOptions} from './options';

export type {PluginOptions, Options};
42 changes: 42 additions & 0 deletions packages/docusaurus-plugin-vercel-analytics/src/options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import {DEFAULT_PLUGIN_ID} from '@docusaurus/utils';
import logger from '@docusaurus/logger';
import {Joi} from '@docusaurus/utils-validation';
import type {OptionValidationContext} from '@docusaurus/types';

export type PluginOptions = {
id: string;
mode: 'auto' | 'production' | 'development' | undefined;
debug: boolean | undefined;
};

export type Options = Partial<PluginOptions>;

const pluginOptionsSchema = Joi.object<PluginOptions>({
mode: Joi.string().valid('auto', 'production', 'development').optional(),
debug: Joi.boolean().optional(),
});

// We can't validate this through the schema
// Docusaurus core auto registers the id field to the schema already
function ensureNoMultiInstance(options: Options) {
if (options?.id && options.id !== DEFAULT_PLUGIN_ID) {
throw new Error(
logger.interpolate`You site uses the Vercel Analytics plugin with a custom plugin id (name=${options.id}).
But this plugin is only supposed to be used at most once per site. Therefore providing a custom plugin id is unsupported.`,
);
}
}

export function validateOptions({
validate,
options,
}: OptionValidationContext<Options, PluginOptions>): PluginOptions {
ensureNoMultiInstance(options);
return validate(pluginOptionsSchema, options);
}
8 changes: 8 additions & 0 deletions packages/docusaurus-plugin-vercel-analytics/src/types.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

/// <reference types="@docusaurus/module-type-aliases" />
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"noEmit": false,
"composite": true,
"incremental": true,
"tsBuildInfoFile": "./lib/.tsbuildinfo-client",
"moduleResolution": "bundler",
"module": "esnext",
"target": "esnext",
"rootDir": "src",
"outDir": "lib"
},
"include": ["src/analytics.ts", "src/options.ts", "src/*.d.ts"],
"exclude": ["**/__tests__/**"]
}
13 changes: 13 additions & 0 deletions packages/docusaurus-plugin-vercel-analytics/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"extends": "../../tsconfig.json",
"references": [{"path": "./tsconfig.client.json"}],
"compilerOptions": {
"noEmit": false,
"incremental": true,
"tsBuildInfoFile": "./lib/.tsbuildinfo",
"rootDir": "src",
"outDir": "lib"
},
"include": ["src"],
"exclude": ["src/analytics.ts", "**/__tests__/**"]
}
57 changes: 57 additions & 0 deletions website/docs/api/plugins/plugin-vercel-analytics.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
sidebar_position: 11
slug: /api/plugins/@docusaurus/plugin-vercel-analytics
---

# 📦 plugin-vercel-analytics

import APITable from '@site/src/components/APITable';

[Vercel Analytics](https://vercel.com/docs/analytics) provides comprehensive insights into your website's visitors, tracking top pages, referrers, and demographics like location, operating systems, and browser info.

:::warning production only

This plugin is always inactive in development and **only active in production** (`docusaurus build`) to avoid polluting the analytics statistics.

:::

## Installation {#installation}

```bash npm2yarn
npm install --save @docusaurus/plugin-vercel-analytics
```

## Configuration {#configuration}

Accepted fields:

```mdx-code-block
<APITable>
```

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | `string` | `'auto'` | Override the automatic environment detection. Read the [official docs](https://vercel.com/docs/analytics/package#mode) for details. |
| `debug` | `boolean` | `undefined` | Enable browser console logging of analytics events. SRead the [official docs](https://vercel.com/docs/analytics/package#debug) for details. |

```mdx-code-block
</APITable>
```

### Example configuration {#ex-config}

You can configure this plugin through plugin options.

```js title="docusaurus.config.js"
export default {
plugins: [
[
'vercel-analytics',
{
debug: true,
mode: 'auto',
},
],
],
};
```