@posthog/wizard 0.2.3 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -5
- package/dist/src/lib/constants.d.ts +2 -1
- package/dist/src/lib/constants.js +3 -6
- package/dist/src/lib/constants.js.map +1 -1
- package/dist/src/nextjs/docs.js +33 -25
- package/dist/src/nextjs/docs.js.map +1 -1
- package/dist/src/nextjs/nextjs-wizard.d.ts +4 -4
- package/dist/src/nextjs/nextjs-wizard.js +71 -34
- package/dist/src/nextjs/nextjs-wizard.js.map +1 -1
- package/dist/src/nextjs/prompts.d.ts +1 -1
- package/dist/src/nextjs/prompts.js +9 -3
- package/dist/src/nextjs/prompts.js.map +1 -1
- package/dist/src/nextjs/utils.d.ts +3 -1
- package/dist/src/nextjs/utils.js +30 -7
- package/dist/src/nextjs/utils.js.map +1 -1
- package/dist/src/run.d.ts +1 -0
- package/dist/src/run.js +14 -10
- package/dist/src/run.js.map +1 -1
- package/dist/src/telemetry.js +1 -1
- package/dist/src/telemetry.js.map +1 -1
- package/dist/src/utils/analytics.d.ts +9 -1
- package/dist/src/utils/analytics.js +52 -3
- package/dist/src/utils/analytics.js.map +1 -1
- package/dist/src/utils/clack-utils.d.ts +14 -11
- package/dist/src/utils/clack-utils.js +41 -36
- package/dist/src/utils/clack-utils.js.map +1 -1
- package/dist/src/utils/clack.js.map +1 -1
- package/dist/src/utils/debug.js.map +1 -1
- package/dist/src/utils/environment.js.map +1 -1
- package/dist/src/utils/file-utils.js +7 -7
- package/dist/src/utils/file-utils.js.map +1 -1
- package/dist/src/utils/package-manager.d.ts +4 -3
- package/dist/src/utils/package-manager.js +26 -27
- package/dist/src/utils/package-manager.js.map +1 -1
- package/dist/src/utils/query.js.map +1 -1
- package/dist/src/utils/types.d.ts +4 -4
- package/dist/src/utils/types.js.map +1 -1
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
|
|
2
1
|
<p align="center">
|
|
3
2
|
<img alt="posthoglogo" src="https://user-images.githubusercontent.com/65415371/205059737-c8a4f836-4889-4654-902e-f302b187b6a0.png">
|
|
4
3
|
</p>
|
|
5
4
|
|
|
6
|
-
> **⚠️ Experimental:** This wizard is still in an experimental phase.
|
|
7
|
-
>
|
|
5
|
+
> **⚠️ Experimental:** This wizard is still in an experimental phase. If you
|
|
6
|
+
> have any feedback, please drop an email to **joshua** [at] **posthog** [dot]
|
|
7
|
+
> **com**.
|
|
8
8
|
|
|
9
9
|
<h1>PostHog Wizard</h1>
|
|
10
10
|
<h4>The PostHog Wizard helps you quickly add PostHog to your project.</h4>
|
|
@@ -17,7 +17,9 @@ To use the wizard, you can run it directly using:
|
|
|
17
17
|
npx @posthog/wizard
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
Currently the wizard can be used for Next.js only. If you have other platforms
|
|
20
|
+
Currently the wizard can be used for Next.js only. If you have other platforms
|
|
21
|
+
you would like the wizard to support, please open a
|
|
22
|
+
[GitHub issue](https://github.com/posthog/wizard/issues)!
|
|
21
23
|
|
|
22
24
|
# Options
|
|
23
25
|
|
|
@@ -30,5 +32,8 @@ The following CLI arguments are available:
|
|
|
30
32
|
| `--debug` | Enable verbose logging | boolean | `false` | | `POSTHOG_WIZARD_DEBUG` |
|
|
31
33
|
| `--integration` | Choose the integration to setup | choices | Select integration during setup | "nextjs" | `POSTHOG_WIZARD_INTEGRATION` |
|
|
32
34
|
| `--force-install` | Force install the SDK NPM package (use with caution!) | boolean | `false` | | |
|
|
35
|
+
| `--install-dir` | Relative path to install in | string | `.` | | `POSTHOG_WIZARD_INSTALL_DIR` |
|
|
33
36
|
|
|
34
|
-
> Note: A large amount of the scaffolding for this came from the amazing Sentry
|
|
37
|
+
> Note: A large amount of the scaffolding for this came from the amazing Sentry
|
|
38
|
+
> wizard, which you can find [here](https://github.com/getsentry/sentry-wizard)
|
|
39
|
+
> 💖
|
|
@@ -13,8 +13,9 @@ export interface Args {
|
|
|
13
13
|
}
|
|
14
14
|
export declare const DEFAULT_URL = "http://us.posthog.com";
|
|
15
15
|
export declare const ISSUES_URL = "https://github.com/posthog/wizard/issues";
|
|
16
|
-
export declare const INSTALL_DIR: string;
|
|
17
16
|
export declare const CLOUD_URL = "https://us.posthog.com";
|
|
18
17
|
export declare const DEFAULT_HOST_URL = "https://us.i.posthog.com";
|
|
18
|
+
export declare const ANALYTICS_POSTHOG_PUBLIC_PROJECT_WRITE_KEY = "sTMFPsFhdP1Ssg";
|
|
19
|
+
export declare const ANALYTICS_HOST_URL = "https://internal-t.posthog.com";
|
|
19
20
|
export declare const DUMMY_PROJECT_API_KEY = "_YOUR_POSTHOG_PROJECT_API_KEY_";
|
|
20
21
|
export {};
|
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.DUMMY_PROJECT_API_KEY = exports.
|
|
3
|
+
exports.DUMMY_PROJECT_API_KEY = exports.ANALYTICS_HOST_URL = exports.ANALYTICS_POSTHOG_PUBLIC_PROJECT_WRITE_KEY = exports.DEFAULT_HOST_URL = exports.CLOUD_URL = exports.ISSUES_URL = exports.DEFAULT_URL = exports.Integration = void 0;
|
|
7
4
|
exports.getIntegrationDescription = getIntegrationDescription;
|
|
8
5
|
exports.getIntegrationChoices = getIntegrationChoices;
|
|
9
|
-
const path_1 = __importDefault(require("path"));
|
|
10
6
|
var Integration;
|
|
11
7
|
(function (Integration) {
|
|
12
8
|
Integration["nextjs"] = "nextjs";
|
|
@@ -27,8 +23,9 @@ function getIntegrationChoices() {
|
|
|
27
23
|
}
|
|
28
24
|
exports.DEFAULT_URL = 'http://us.posthog.com';
|
|
29
25
|
exports.ISSUES_URL = 'https://github.com/posthog/wizard/issues';
|
|
30
|
-
exports.INSTALL_DIR = path_1.default.join(process.cwd(), process.env.POSTHOG_WIZARD_INSTALL_DIR ?? '');
|
|
31
26
|
exports.CLOUD_URL = 'https://us.posthog.com';
|
|
32
27
|
exports.DEFAULT_HOST_URL = 'https://us.i.posthog.com';
|
|
28
|
+
exports.ANALYTICS_POSTHOG_PUBLIC_PROJECT_WRITE_KEY = 'sTMFPsFhdP1Ssg';
|
|
29
|
+
exports.ANALYTICS_HOST_URL = 'https://internal-t.posthog.com';
|
|
33
30
|
exports.DUMMY_PROJECT_API_KEY = '_YOUR_POSTHOG_PROJECT_API_KEY_';
|
|
34
31
|
//# sourceMappingURL=constants.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/lib/constants.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/lib/constants.ts"],"names":[],"mappings":";;;AAIA,8DAOC;AAOD,sDAKC;AAvBD,IAAY,WAEX;AAFD,WAAY,WAAW;IACrB,gCAAiB,CAAA;AACnB,CAAC,EAFW,WAAW,2BAAX,WAAW,QAEtB;AAED,SAAgB,yBAAyB,CAAC,IAAY;IACpD,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,WAAW,CAAC,MAAM;YACrB,OAAO,SAAS,CAAC;QACnB;YACE,MAAM,IAAI,KAAK,CAAC,uBAAuB,IAAI,EAAE,CAAC,CAAC;IACnD,CAAC;AACH,CAAC;AAOD,SAAgB,qBAAqB;IACnC,OAAO,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC;QACrD,IAAI,EAAE,yBAAyB,CAAC,IAAI,CAAC;QACrC,KAAK,EAAE,IAAI;KACZ,CAAC,CAAC,CAAC;AACN,CAAC;AAOY,QAAA,WAAW,GAAG,uBAAuB,CAAC;AACtC,QAAA,UAAU,GAAG,0CAA0C,CAAC;AACxD,QAAA,SAAS,GAAG,wBAAwB,CAAC;AACrC,QAAA,gBAAgB,GAAG,0BAA0B,CAAC;AAC9C,QAAA,0CAA0C,GAAG,gBAAgB,CAAC;AAC9D,QAAA,kBAAkB,GAAG,gCAAgC,CAAC;AACtD,QAAA,qBAAqB,GAAG,gCAAgC,CAAC","sourcesContent":["export enum Integration {\n nextjs = 'nextjs',\n}\n\nexport function getIntegrationDescription(type: string): string {\n switch (type) {\n case Integration.nextjs:\n return 'Next.js';\n default:\n throw new Error(`Unknown integration ${type}`);\n }\n}\n\ntype IntegrationChoice = {\n name: string;\n value: string;\n};\n\nexport function getIntegrationChoices(): IntegrationChoice[] {\n return Object.keys(Integration).map((type: string) => ({\n name: getIntegrationDescription(type),\n value: type,\n }));\n}\n\nexport interface Args {\n debug: boolean;\n integration: Integration;\n}\n\nexport const DEFAULT_URL = 'http://us.posthog.com';\nexport const ISSUES_URL = 'https://github.com/posthog/wizard/issues';\nexport const CLOUD_URL = 'https://us.posthog.com';\nexport const DEFAULT_HOST_URL = 'https://us.i.posthog.com';\nexport const ANALYTICS_POSTHOG_PUBLIC_PROJECT_WRITE_KEY = 'sTMFPsFhdP1Ssg';\nexport const ANALYTICS_HOST_URL = 'https://internal-t.posthog.com';\nexport const DUMMY_PROJECT_API_KEY = '_YOUR_POSTHOG_PROJECT_API_KEY_';\n"]}
|
package/dist/src/nextjs/docs.js
CHANGED
|
@@ -5,25 +5,26 @@ const utils_1 = require("./utils");
|
|
|
5
5
|
const getNextjsAppRouterDocs = ({ host, language, }) => {
|
|
6
6
|
return `
|
|
7
7
|
==============================
|
|
8
|
-
FILE:
|
|
8
|
+
FILE: PostHogProvider.${language === 'typescript' ? 'tsx' : 'jsx'} (put it somewhere where client files are, like the components folder)
|
|
9
|
+
LOCATION: Wherever other providers are, or the components folder
|
|
9
10
|
==============================
|
|
10
11
|
Changes:
|
|
11
12
|
- Create a PostHogProvider component that will be imported into the layout file.
|
|
12
13
|
|
|
13
14
|
Example:
|
|
14
15
|
--------------------------------------------------
|
|
15
|
-
|
|
16
|
+
"use client"
|
|
16
17
|
|
|
17
|
-
import posthog from
|
|
18
|
-
import { PostHogProvider as PHProvider, usePostHog } from
|
|
19
|
-
import { Suspense, useEffect } from
|
|
18
|
+
import posthog from "posthog-js"
|
|
19
|
+
import { PostHogProvider as PHProvider, usePostHog } from "posthog-js/react"
|
|
20
|
+
import { Suspense, useEffect } from "react"
|
|
20
21
|
import { usePathname, useSearchParams } from "next/navigation"
|
|
21
22
|
|
|
22
23
|
export function PostHogProvider({ children }: { children: React.ReactNode }) {
|
|
23
24
|
useEffect(() => {
|
|
24
25
|
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {
|
|
25
26
|
api_host: "/ingest",
|
|
26
|
-
ui_host:
|
|
27
|
+
ui_host: "${(0, utils_1.getUiHostFromHost)(host)}",
|
|
27
28
|
capture_pageview: false, // We capture pageviews manually
|
|
28
29
|
capture_pageleave: true, // Enable pageleave capture
|
|
29
30
|
})
|
|
@@ -50,7 +51,7 @@ function PostHogPageView() {
|
|
|
50
51
|
if (search) {
|
|
51
52
|
url += "?" + search
|
|
52
53
|
}
|
|
53
|
-
posthog.capture(
|
|
54
|
+
posthog.capture("$pageview", { "$current_url": url })
|
|
54
55
|
}
|
|
55
56
|
}, [pathname, searchParams, posthog])
|
|
56
57
|
|
|
@@ -67,7 +68,8 @@ function SuspendedPostHogPageView() {
|
|
|
67
68
|
--------------------------------------------------
|
|
68
69
|
|
|
69
70
|
==============================
|
|
70
|
-
FILE:
|
|
71
|
+
FILE: layout.${language === 'typescript' ? 'tsx' : 'jsx'}
|
|
72
|
+
LOCATION: Wherever the root layout is
|
|
71
73
|
==============================
|
|
72
74
|
Changes:
|
|
73
75
|
- Import the PostHogProvider from the providers file and wrap the app in it.
|
|
@@ -75,7 +77,7 @@ Changes:
|
|
|
75
77
|
Example:
|
|
76
78
|
--------------------------------------------------
|
|
77
79
|
// other imports
|
|
78
|
-
import { PostHogProvider } from
|
|
80
|
+
import { PostHogProvider } from "LOCATION_OF_POSTHOG_PROVIDER"
|
|
79
81
|
|
|
80
82
|
export default function RootLayout({ children }) {
|
|
81
83
|
return (
|
|
@@ -93,18 +95,19 @@ export default function RootLayout({ children }) {
|
|
|
93
95
|
--------------------------------------------------
|
|
94
96
|
|
|
95
97
|
==============================
|
|
96
|
-
FILE:
|
|
98
|
+
FILE: posthog.${language === 'typescript' ? 'ts' : 'js'}
|
|
99
|
+
LOCATION: Wherever works best given the project structure
|
|
97
100
|
==============================
|
|
98
101
|
Changes:
|
|
99
102
|
- Initialize the PostHog Node.js client
|
|
100
103
|
|
|
101
104
|
Example:
|
|
102
105
|
--------------------------------------------------
|
|
103
|
-
import { PostHog } from
|
|
106
|
+
import { PostHog } from "posthog-node"
|
|
104
107
|
|
|
105
108
|
export default function PostHogClient() {
|
|
106
109
|
const posthogClient = new PostHog(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {
|
|
107
|
-
host:
|
|
110
|
+
host: "${host}",
|
|
108
111
|
flushAt: 1,
|
|
109
112
|
flushInterval: 0,
|
|
110
113
|
})
|
|
@@ -114,6 +117,7 @@ export default function PostHogClient() {
|
|
|
114
117
|
|
|
115
118
|
==============================
|
|
116
119
|
FILE: next.config.{js,ts,mjs,cjs}
|
|
120
|
+
LOCATION: Wherever the root next config is
|
|
117
121
|
==============================
|
|
118
122
|
Changes:
|
|
119
123
|
- Add rewrites to the Next.js config to support PostHog, if there are existing rewrites, add the PostHog rewrites to them.
|
|
@@ -149,7 +153,8 @@ exports.getNextjsAppRouterDocs = getNextjsAppRouterDocs;
|
|
|
149
153
|
const getNextjsPagesRouterDocs = ({ host, language, }) => {
|
|
150
154
|
return `
|
|
151
155
|
==============================
|
|
152
|
-
FILE:
|
|
156
|
+
FILE: _app.${language === 'typescript' ? 'tsx' : 'jsx'}
|
|
157
|
+
LOCATION: Wherever the root _app.${language === 'typescript' ? 'tsx' : 'jsx'} file is
|
|
153
158
|
==============================
|
|
154
159
|
Changes:
|
|
155
160
|
- Initialize PostHog in _app.js.
|
|
@@ -158,25 +163,26 @@ Changes:
|
|
|
158
163
|
|
|
159
164
|
Example:
|
|
160
165
|
--------------------------------------------------
|
|
161
|
-
import { useEffect } from
|
|
162
|
-
import { Router } from
|
|
163
|
-
import posthog from
|
|
164
|
-
import { PostHogProvider } from
|
|
166
|
+
import { useEffect } from "react"
|
|
167
|
+
import { Router } from "next/router"
|
|
168
|
+
import posthog from "posthog-js"
|
|
169
|
+
import { PostHogProvider } from "posthog-js/react"
|
|
165
170
|
|
|
166
171
|
export default function App({ Component, pageProps }) {
|
|
167
172
|
useEffect(() => {
|
|
168
173
|
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY, {
|
|
169
|
-
api_host:
|
|
174
|
+
api_host: "/ingest",
|
|
175
|
+
ui_host: "${(0, utils_1.getUiHostFromHost)(host)}",
|
|
170
176
|
loaded: (posthog) => {
|
|
171
|
-
if (process.env.NODE_ENV ===
|
|
177
|
+
if (process.env.NODE_ENV === "development") posthog.debug()
|
|
172
178
|
},
|
|
173
179
|
})
|
|
174
180
|
|
|
175
|
-
const handleRouteChange = () => posthog?.capture(
|
|
176
|
-
Router.events.on(
|
|
181
|
+
const handleRouteChange = () => posthog?.capture("$pageview")
|
|
182
|
+
Router.events.on("routeChangeComplete", handleRouteChange)
|
|
177
183
|
|
|
178
184
|
return () => {
|
|
179
|
-
Router.events.off(
|
|
185
|
+
Router.events.off("routeChangeComplete", handleRouteChange)
|
|
180
186
|
}
|
|
181
187
|
}, [])
|
|
182
188
|
|
|
@@ -189,18 +195,19 @@ export default function App({ Component, pageProps }) {
|
|
|
189
195
|
--------------------------------------------------
|
|
190
196
|
|
|
191
197
|
==============================
|
|
192
|
-
FILE:
|
|
198
|
+
FILE: posthog.${language === 'typescript' ? 'ts' : 'js'}
|
|
199
|
+
LOCATION: Wherever works best given the project structure
|
|
193
200
|
==============================
|
|
194
201
|
Changes:
|
|
195
202
|
- Initialize the PostHog Node.js client for server-side tracking.
|
|
196
203
|
|
|
197
204
|
Example:
|
|
198
205
|
--------------------------------------------------
|
|
199
|
-
import { PostHog } from
|
|
206
|
+
import { PostHog } from "posthog-node"
|
|
200
207
|
|
|
201
208
|
export default function PostHogClient() {
|
|
202
209
|
return new PostHog(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {
|
|
203
|
-
host:
|
|
210
|
+
host: "${host}",
|
|
204
211
|
flushAt: 1,
|
|
205
212
|
flushInterval: 0,
|
|
206
213
|
})
|
|
@@ -209,6 +216,7 @@ export default function PostHogClient() {
|
|
|
209
216
|
|
|
210
217
|
==============================
|
|
211
218
|
FILE: next.config.{js,ts,mjs,cjs}
|
|
219
|
+
LOCATION: Wherever the root next config is
|
|
212
220
|
==============================
|
|
213
221
|
Changes:
|
|
214
222
|
- Add rewrites to support PostHog tracking.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"docs.js","sourceRoot":"","sources":["../../../src/nextjs/docs.ts"],"names":[],"mappings":";;;AAAA,
|
|
1
|
+
{"version":3,"file":"docs.js","sourceRoot":"","sources":["../../../src/nextjs/docs.ts"],"names":[],"mappings":";;;AAAA,mCAAkE;AAE3D,MAAM,sBAAsB,GAAG,CAAC,EACrC,IAAI,EACJ,QAAQ,GAIT,EAAE,EAAE;IACH,OAAO;;wBAGL,QAAQ,KAAK,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KACtC;;;;;;;;;;;;;;;;;;;kBAmBgB,IAAA,yBAAiB,EAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;eA4C1B,QAAQ,KAAK,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;gBA2BxC,QAAQ,KAAK,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI;;;;;;;;;;;;aAY1C,IAAI;;;;;;;;;;;;;;;;;;;;;;;;wBAwBO,IAAA,4BAAoB,EAAC,IAAI,CAAC;;;;wBAI1B,IAAI;;;;wBAIJ,IAAI;;;;;;;;mDAQuB,CAAC;AACpD,CAAC,CAAC;AA1JW,QAAA,sBAAsB,0BA0JjC;AAEK,MAAM,wBAAwB,GAAG,CAAC,EACvC,IAAI,EACJ,QAAQ,GAIT,EAAE,EAAE;IACH,OAAO;;aAEI,QAAQ,KAAK,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK;mCAElD,QAAQ,KAAK,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KACtC;;;;;;;;;;;;;;;;;;kBAkBgB,IAAA,yBAAiB,EAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;gBAuBzB,QAAQ,KAAK,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI;;;;;;;;;;;;aAY1C,IAAI;;;;;;;;;;;;;;;;;;;;yDAoBwC,IAAA,4BAAoB,EACrE,IAAI,CACL;kDAC2C,IAAI;kDACJ,IAAI;;;;;;;;CAQrD,CAAC;AACF,CAAC,CAAC;AAlGW,QAAA,wBAAwB,4BAkGnC","sourcesContent":["import { getAssetHostFromHost, getUiHostFromHost } from './utils';\n\nexport const getNextjsAppRouterDocs = ({\n host,\n language,\n}: {\n host: string;\n language: 'typescript' | 'javascript';\n}) => {\n return `\n==============================\nFILE: PostHogProvider.${\n language === 'typescript' ? 'tsx' : 'jsx'\n } (put it somewhere where client files are, like the components folder)\nLOCATION: Wherever other providers are, or the components folder\n==============================\nChanges:\n- Create a PostHogProvider component that will be imported into the layout file.\n\nExample:\n--------------------------------------------------\n\"use client\"\n\nimport posthog from \"posthog-js\"\nimport { PostHogProvider as PHProvider, usePostHog } from \"posthog-js/react\"\nimport { Suspense, useEffect } from \"react\"\nimport { usePathname, useSearchParams } from \"next/navigation\"\n\nexport function PostHogProvider({ children }: { children: React.ReactNode }) {\n useEffect(() => {\n posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {\n api_host: \"/ingest\",\n ui_host: \"${getUiHostFromHost(host)}\",\n capture_pageview: false, // We capture pageviews manually\n capture_pageleave: true, // Enable pageleave capture\n })\n }, [])\n\n return (\n <PHProvider client={posthog}>\n <SuspendedPostHogPageView />\n {children}\n </PHProvider>\n )\n}\n\n\nfunction PostHogPageView() {\n const pathname = usePathname()\n const searchParams = useSearchParams()\n const posthog = usePostHog()\n\n useEffect(() => {\n if (pathname && posthog) {\n let url = window.origin + pathname\n const search = searchParams.toString()\n if (search) {\n url += \"?\" + search\n }\n posthog.capture(\"$pageview\", { \"$current_url\": url })\n }\n }, [pathname, searchParams, posthog])\n\n return null\n}\n\nfunction SuspendedPostHogPageView() {\n return (\n <Suspense fallback={null}>\n <PostHogPageView />\n </Suspense>\n )\n}\n--------------------------------------------------\n\n==============================\nFILE: layout.${language === 'typescript' ? 'tsx' : 'jsx'}\nLOCATION: Wherever the root layout is\n==============================\nChanges:\n- Import the PostHogProvider from the providers file and wrap the app in it.\n\nExample:\n--------------------------------------------------\n// other imports\nimport { PostHogProvider } from \"LOCATION_OF_POSTHOG_PROVIDER\"\n\nexport default function RootLayout({ children }) {\n return (\n <html lang=\"en\">\n <body>\n <PostHogProvider>\n {/* other providers */}\n {children}\n {/* other providers */}\n </PostHogProvider>\n </body>\n </html>\n )\n}\n--------------------------------------------------\n\n==============================\nFILE: posthog.${language === 'typescript' ? 'ts' : 'js'}\nLOCATION: Wherever works best given the project structure\n==============================\nChanges:\n- Initialize the PostHog Node.js client\n\nExample:\n--------------------------------------------------\nimport { PostHog } from \"posthog-node\"\n\nexport default function PostHogClient() {\n const posthogClient = new PostHog(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {\n host: \"${host}\",\n flushAt: 1,\n flushInterval: 0,\n })\n return posthogClient\n}\n--------------------------------------------------\n\n==============================\nFILE: next.config.{js,ts,mjs,cjs}\nLOCATION: Wherever the root next config is\n==============================\nChanges:\n- Add rewrites to the Next.js config to support PostHog, if there are existing rewrites, add the PostHog rewrites to them.\n- Add skipTrailingSlashRedirect to the Next.js config to support PostHog trailing slash API requests.\n- This can be of type js, ts, mjs, cjs etc. You should adapt the file according\nExample:\n--------------------------------------------------\nconst nextConfig = {\n // other config\n async rewrites() {\n return [\n {\n source: \"/ingest/static/:path*\",\n destination: \"${getAssetHostFromHost(host)}/static/:path*\",\n },\n {\n source: \"/ingest/:path*\",\n destination: \"${host}/:path*\",\n },\n {\n source: \"/ingest/decide\",\n destination: \"${host}/decide\",\n },\n ];\n },\n // This is required to support PostHog trailing slash API requests\n skipTrailingSlashRedirect: true,\n}\nmodule.exports = nextConfig\n--------------------------------------------------`;\n};\n\nexport const getNextjsPagesRouterDocs = ({\n host,\n language,\n}: {\n host: string;\n language: 'typescript' | 'javascript';\n}) => {\n return `\n==============================\nFILE: _app.${language === 'typescript' ? 'tsx' : 'jsx'}\nLOCATION: Wherever the root _app.${\n language === 'typescript' ? 'tsx' : 'jsx'\n } file is\n==============================\nChanges:\n- Initialize PostHog in _app.js.\n- Wrap the application in PostHogProvider.\n- Manually capture $pageview events.\n\nExample:\n--------------------------------------------------\nimport { useEffect } from \"react\"\nimport { Router } from \"next/router\"\nimport posthog from \"posthog-js\"\nimport { PostHogProvider } from \"posthog-js/react\"\n\nexport default function App({ Component, pageProps }) {\n useEffect(() => {\n posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY, {\n api_host: \"/ingest\",\n ui_host: \"${getUiHostFromHost(host)}\",\n loaded: (posthog) => {\n if (process.env.NODE_ENV === \"development\") posthog.debug()\n },\n })\n\n const handleRouteChange = () => posthog?.capture(\"$pageview\")\n Router.events.on(\"routeChangeComplete\", handleRouteChange)\n\n return () => {\n Router.events.off(\"routeChangeComplete\", handleRouteChange)\n }\n }, [])\n\n return (\n <PostHogProvider client={posthog}>\n <Component {...pageProps} />\n </PostHogProvider>\n )\n}\n--------------------------------------------------\n\n==============================\nFILE: posthog.${language === 'typescript' ? 'ts' : 'js'}\nLOCATION: Wherever works best given the project structure\n==============================\nChanges:\n- Initialize the PostHog Node.js client for server-side tracking.\n\nExample:\n--------------------------------------------------\nimport { PostHog } from \"posthog-node\"\n\nexport default function PostHogClient() {\n return new PostHog(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {\n host: \"${host}\",\n flushAt: 1,\n flushInterval: 0,\n })\n}\n--------------------------------------------------\n\n==============================\nFILE: next.config.{js,ts,mjs,cjs}\nLOCATION: Wherever the root next config is\n==============================\nChanges:\n- Add rewrites to support PostHog tracking.\n- Enable support for PostHog API trailing slash requests.\n\nExample:\n--------------------------------------------------\nconst nextConfig = {\n async rewrites() {\n return [\n { source: \"/ingest/static/:path*\", destination: \"${getAssetHostFromHost(\n host,\n )}/static/:path*\" },\n { source: \"/ingest/:path*\", destination: \"${host}/:path*\" },\n { source: \"/ingest/decide\", destination: \"${host}/decide\" },\n ]\n },\n skipTrailingSlashRedirect: true,\n}\n\nmodule.exports = nextConfig\n--------------------------------------------------\n`;\n};\n"]}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { WizardOptions } from '../utils/types';
|
|
2
2
|
import { Integration } from '../lib/constants';
|
|
3
3
|
export declare function runNextjsWizard(options: WizardOptions): Promise<void>;
|
|
4
|
-
export declare function detectNextJs(): Promise<Integration.nextjs | undefined>;
|
|
5
|
-
export declare function addOrUpdateEnvironmentVariables({ projectApiKey,
|
|
4
|
+
export declare function detectNextJs(options: Pick<WizardOptions, 'installDir'>): Promise<Integration.nextjs | undefined>;
|
|
5
|
+
export declare function addOrUpdateEnvironmentVariables({ projectApiKey, installDir, }: {
|
|
6
6
|
projectApiKey: string;
|
|
7
|
-
|
|
7
|
+
installDir: string;
|
|
8
8
|
}): Promise<void>;
|
|
9
|
-
export declare function getDotGitignore(): string | undefined;
|
|
9
|
+
export declare function getDotGitignore({ installDir, }: Pick<WizardOptions, 'installDir'>): string | undefined;
|
|
@@ -57,30 +57,31 @@ const constants_1 = require("../lib/constants");
|
|
|
57
57
|
const docs_1 = require("./docs");
|
|
58
58
|
const analytics_1 = require("../utils/analytics");
|
|
59
59
|
async function runNextjsWizard(options) {
|
|
60
|
-
const {
|
|
60
|
+
const { forceInstall } = options;
|
|
61
61
|
(0, clack_utils_1.printWelcome)({
|
|
62
62
|
wizardName: 'PostHog Next.js Wizard',
|
|
63
|
-
telemetryEnabled,
|
|
64
63
|
});
|
|
65
64
|
const aiConsent = await askForAIConsent();
|
|
66
65
|
if (!aiConsent) {
|
|
67
66
|
await (0, clack_utils_1.abort)('The Next.js wizard requires AI to get setup right now. Please view the docs to setup Next.js manually instead: https://posthog.com/docs/libraries/next-js', 0);
|
|
68
67
|
}
|
|
69
|
-
const typeScriptDetected = (0, clack_utils_1.isUsingTypeScript)();
|
|
68
|
+
const typeScriptDetected = (0, clack_utils_1.isUsingTypeScript)(options);
|
|
70
69
|
await (0, clack_utils_1.confirmContinueIfNoOrDirtyGitRepo)();
|
|
71
|
-
const packageJson = await (0, clack_utils_1.getPackageDotJson)();
|
|
70
|
+
const packageJson = await (0, clack_utils_1.getPackageDotJson)(options);
|
|
72
71
|
await (0, clack_utils_1.ensurePackageIsInstalled)(packageJson, 'next', 'Next.js');
|
|
73
72
|
const nextVersion = (0, package_json_1.getPackageVersion)('next', packageJson);
|
|
74
|
-
analytics_1.
|
|
73
|
+
analytics_1.analytics.setTag('nextjs-version', (0, utils_1.getNextJsVersionBucket)(nextVersion));
|
|
75
74
|
const { projectApiKey, wizardHash, host } = await (0, clack_utils_1.getOrAskForProjectData)(options);
|
|
76
75
|
const sdkAlreadyInstalled = (0, package_json_1.hasPackageInstalled)('posthog-js', packageJson);
|
|
77
|
-
analytics_1.
|
|
76
|
+
analytics_1.analytics.setTag('sdk-already-installed', sdkAlreadyInstalled);
|
|
78
77
|
const { packageManager: packageManagerFromInstallStep } = await (0, clack_utils_1.installPackage)({
|
|
79
78
|
packageName: 'posthog-js',
|
|
80
79
|
packageNameDisplayLabel: 'posthog-js',
|
|
81
80
|
alreadyInstalled: !!packageJson?.dependencies?.['posthog-js'],
|
|
82
81
|
forceInstall,
|
|
83
82
|
askBeforeUpdating: false,
|
|
83
|
+
installDir: options.installDir,
|
|
84
|
+
integration: constants_1.Integration.nextjs,
|
|
84
85
|
});
|
|
85
86
|
await (0, clack_utils_1.installPackage)({
|
|
86
87
|
packageName: 'posthog-node',
|
|
@@ -89,9 +90,16 @@ async function runNextjsWizard(options) {
|
|
|
89
90
|
alreadyInstalled: !!packageJson?.dependencies?.['posthog-node'],
|
|
90
91
|
forceInstall,
|
|
91
92
|
askBeforeUpdating: false,
|
|
93
|
+
installDir: options.installDir,
|
|
94
|
+
integration: constants_1.Integration.nextjs,
|
|
95
|
+
});
|
|
96
|
+
const router = await (0, utils_1.getNextJsRouter)(options);
|
|
97
|
+
const relevantFiles = await getRelevantFilesForNextJs(options);
|
|
98
|
+
analytics_1.analytics.capture('wizard interaction', {
|
|
99
|
+
action: 'detected relevant files',
|
|
100
|
+
integration: constants_1.Integration.nextjs,
|
|
101
|
+
number_of_files: relevantFiles.length,
|
|
92
102
|
});
|
|
93
|
-
const router = await (0, utils_1.getNextJsRouter)();
|
|
94
|
-
const relevantFiles = await getRelevantFilesForNextJs();
|
|
95
103
|
const installationDocumentation = getInstallationDocumentation({
|
|
96
104
|
router,
|
|
97
105
|
host,
|
|
@@ -103,13 +111,23 @@ async function runNextjsWizard(options) {
|
|
|
103
111
|
installationDocumentation,
|
|
104
112
|
wizardHash,
|
|
105
113
|
});
|
|
114
|
+
analytics_1.analytics.capture('wizard interaction', {
|
|
115
|
+
action: 'detected files to change',
|
|
116
|
+
integration: constants_1.Integration.nextjs,
|
|
117
|
+
files: filesToChange,
|
|
118
|
+
});
|
|
106
119
|
const changes = [];
|
|
107
120
|
for (const filePath of filesToChange) {
|
|
108
121
|
const fileChangeSpinner = clack_1.default.spinner();
|
|
122
|
+
analytics_1.analytics.capture('wizard interaction', {
|
|
123
|
+
action: 'processing file',
|
|
124
|
+
integration: constants_1.Integration.nextjs,
|
|
125
|
+
file: filePath,
|
|
126
|
+
});
|
|
109
127
|
try {
|
|
110
128
|
let oldContent = undefined;
|
|
111
129
|
try {
|
|
112
|
-
oldContent = await fs.promises.readFile(path_1.default.join(
|
|
130
|
+
oldContent = await fs.promises.readFile(path_1.default.join(options.installDir, filePath), 'utf8');
|
|
113
131
|
}
|
|
114
132
|
catch (readError) {
|
|
115
133
|
if (readError.code !== 'ENOENT') {
|
|
@@ -128,10 +146,15 @@ async function runNextjsWizard(options) {
|
|
|
128
146
|
wizardHash,
|
|
129
147
|
});
|
|
130
148
|
if (newContent !== oldContent) {
|
|
131
|
-
await updateFile({ filePath, oldContent, newContent });
|
|
149
|
+
await updateFile({ filePath, oldContent, newContent }, options);
|
|
132
150
|
changes.push({ filePath, oldContent, newContent });
|
|
133
151
|
}
|
|
134
152
|
fileChangeSpinner.stop(`${oldContent ? 'Updated' : 'Created'} file ${filePath}`);
|
|
153
|
+
analytics_1.analytics.capture('wizard interaction', {
|
|
154
|
+
action: 'processed file',
|
|
155
|
+
integration: constants_1.Integration.nextjs,
|
|
156
|
+
file: filePath,
|
|
157
|
+
});
|
|
135
158
|
}
|
|
136
159
|
catch (error) {
|
|
137
160
|
await (0, clack_utils_1.abort)(`Error processing file ${filePath}`);
|
|
@@ -139,16 +162,24 @@ async function runNextjsWizard(options) {
|
|
|
139
162
|
}
|
|
140
163
|
await addOrUpdateEnvironmentVariables({
|
|
141
164
|
projectApiKey,
|
|
142
|
-
|
|
165
|
+
installDir: options.installDir,
|
|
166
|
+
});
|
|
167
|
+
analytics_1.analytics.capture('wizard interaction', {
|
|
168
|
+
action: 'added environment variables',
|
|
169
|
+
integration: constants_1.Integration.nextjs,
|
|
170
|
+
});
|
|
171
|
+
const packageManagerForOutro = packageManagerFromInstallStep ?? (await (0, clack_utils_1.getPackageManager)(options));
|
|
172
|
+
await (0, clack_utils_1.runPrettierIfInstalled)({
|
|
173
|
+
installDir: options.installDir,
|
|
174
|
+
integration: constants_1.Integration.nextjs,
|
|
143
175
|
});
|
|
144
|
-
const packageManagerForOutro = packageManagerFromInstallStep ?? (await (0, clack_utils_1.getPackageManager)());
|
|
145
|
-
await (0, clack_utils_1.runPrettierIfInstalled)();
|
|
146
176
|
clack_1.default.outro(`
|
|
147
177
|
${chalk_1.default.green('Successfully installed PostHog!')} ${`\n\n${aiConsent
|
|
148
178
|
? `Note: This uses experimental AI to setup your project. It might have got it wrong, pleaes check!\n`
|
|
149
179
|
: ``}You should validate your setup by (re)starting your dev environment (e.g. ${chalk_1.default.cyan(`${packageManagerForOutro.runScriptCommand} dev`)})`}
|
|
150
180
|
|
|
151
181
|
${chalk_1.default.dim(`If you encounter any issues, let us know here: ${constants_1.ISSUES_URL}`)}`);
|
|
182
|
+
await analytics_1.analytics.shutdown('success');
|
|
152
183
|
}
|
|
153
184
|
async function askForAIConsent() {
|
|
154
185
|
return await (0, telemetry_1.traceStep)('ask-for-ai-consent', async () => {
|
|
@@ -171,7 +202,7 @@ async function askForAIConsent() {
|
|
|
171
202
|
return aiConsent;
|
|
172
203
|
});
|
|
173
204
|
}
|
|
174
|
-
async function getRelevantFilesForNextJs() {
|
|
205
|
+
async function getRelevantFilesForNextJs({ installDir, }) {
|
|
175
206
|
const filterPatterns = ['**/*.{tsx,ts,jsx,js,mjs,cjs}'];
|
|
176
207
|
const ignorePatterns = [
|
|
177
208
|
'node_modules',
|
|
@@ -182,13 +213,13 @@ async function getRelevantFilesForNextJs() {
|
|
|
182
213
|
'next-env.d.*',
|
|
183
214
|
];
|
|
184
215
|
const filteredFiles = await (0, fast_glob_1.default)(filterPatterns, {
|
|
185
|
-
cwd:
|
|
216
|
+
cwd: installDir,
|
|
186
217
|
ignore: ignorePatterns,
|
|
187
218
|
});
|
|
188
219
|
return filteredFiles;
|
|
189
220
|
}
|
|
190
|
-
async function detectNextJs() {
|
|
191
|
-
const packageJson = await (0, clack_utils_1.getPackageDotJson)();
|
|
221
|
+
async function detectNextJs(options) {
|
|
222
|
+
const packageJson = await (0, clack_utils_1.getPackageDotJson)(options);
|
|
192
223
|
const hasNextInstalled = (0, package_json_1.hasPackageInstalled)('next', packageJson);
|
|
193
224
|
if (hasNextInstalled)
|
|
194
225
|
return constants_1.Integration.nextjs;
|
|
@@ -238,32 +269,38 @@ async function generateFileChanges({ filePath, content, changedFiles, unchangedF
|
|
|
238
269
|
});
|
|
239
270
|
return response.newContent;
|
|
240
271
|
}
|
|
241
|
-
async function updateFile(change) {
|
|
242
|
-
const dir = path_1.default.dirname(path_1.default.join(
|
|
272
|
+
async function updateFile(change, { installDir }) {
|
|
273
|
+
const dir = path_1.default.dirname(path_1.default.join(installDir, change.filePath));
|
|
243
274
|
await fs.promises.mkdir(dir, { recursive: true });
|
|
244
|
-
await fs.promises.writeFile(path_1.default.join(
|
|
275
|
+
await fs.promises.writeFile(path_1.default.join(installDir, change.filePath), change.newContent);
|
|
245
276
|
}
|
|
246
|
-
async function addOrUpdateEnvironmentVariables({ projectApiKey,
|
|
247
|
-
const envVarContent = `# Posthog\nNEXT_PUBLIC_POSTHOG_KEY=${projectApiKey}
|
|
248
|
-
const dotEnvLocalFilePath = path_1.default.join(
|
|
249
|
-
const dotEnvFilePath = path_1.default.join(
|
|
277
|
+
async function addOrUpdateEnvironmentVariables({ projectApiKey, installDir, }) {
|
|
278
|
+
const envVarContent = `# Posthog\nNEXT_PUBLIC_POSTHOG_KEY=${projectApiKey}`;
|
|
279
|
+
const dotEnvLocalFilePath = path_1.default.join(installDir, '.env.local');
|
|
280
|
+
const dotEnvFilePath = path_1.default.join(installDir, '.env');
|
|
250
281
|
const targetEnvFilePath = fs.existsSync(dotEnvLocalFilePath)
|
|
251
282
|
? dotEnvLocalFilePath
|
|
252
283
|
: dotEnvFilePath;
|
|
253
284
|
const dotEnvFileExists = fs.existsSync(targetEnvFilePath);
|
|
254
|
-
const relativeEnvFilePath = path_1.default.relative(
|
|
285
|
+
const relativeEnvFilePath = path_1.default.relative(installDir, targetEnvFilePath);
|
|
255
286
|
if (dotEnvFileExists) {
|
|
256
287
|
const dotEnvFileContent = fs.readFileSync(targetEnvFilePath, 'utf8');
|
|
257
288
|
const hasProjectApiKey = dotEnvFileContent.includes(`NEXT_PUBLIC_POSTHOG_KEY=${projectApiKey}`);
|
|
258
|
-
|
|
259
|
-
if (hasProjectApiKey && hasHost) {
|
|
289
|
+
if (hasProjectApiKey) {
|
|
260
290
|
clack_1.default.log.success(`${chalk_1.default.bold.cyan(relativeEnvFilePath)} already has the necessary environment variables.`);
|
|
261
291
|
}
|
|
262
292
|
else {
|
|
263
293
|
try {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
.replace(/^
|
|
294
|
+
let newContent = dotEnvFileContent;
|
|
295
|
+
if (dotEnvFileContent.match(/^NEXT_PUBLIC_POSTHOG_KEY=.*$/m)) {
|
|
296
|
+
newContent = dotEnvFileContent.replace(/^NEXT_PUBLIC_POSTHOG_KEY=.*$/m, `NEXT_PUBLIC_POSTHOG_KEY=${projectApiKey}`);
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
if (!dotEnvFileContent.endsWith('\n')) {
|
|
300
|
+
newContent += '\n';
|
|
301
|
+
}
|
|
302
|
+
newContent += envVarContent;
|
|
303
|
+
}
|
|
267
304
|
await fs.promises.writeFile(targetEnvFilePath, newContent, {
|
|
268
305
|
encoding: 'utf8',
|
|
269
306
|
flag: 'w',
|
|
@@ -287,7 +324,7 @@ async function addOrUpdateEnvironmentVariables({ projectApiKey, host, }) {
|
|
|
287
324
|
clack_1.default.log.warning(`Failed to create ${chalk_1.default.bold.cyan(relativeEnvFilePath)} with environment variables for PostHog. Please add them manually. Error: ${error.message}`);
|
|
288
325
|
}
|
|
289
326
|
}
|
|
290
|
-
const gitignorePath = getDotGitignore();
|
|
327
|
+
const gitignorePath = getDotGitignore({ installDir });
|
|
291
328
|
if (gitignorePath) {
|
|
292
329
|
const gitignoreContent = fs.readFileSync(gitignorePath, 'utf8');
|
|
293
330
|
const envFiles = ['.env', '.env.local'];
|
|
@@ -309,7 +346,7 @@ async function addOrUpdateEnvironmentVariables({ projectApiKey, host, }) {
|
|
|
309
346
|
else {
|
|
310
347
|
try {
|
|
311
348
|
const newGitignoreContent = `.env\n.env.local\n`;
|
|
312
|
-
await fs.promises.writeFile(path_1.default.join(
|
|
349
|
+
await fs.promises.writeFile(path_1.default.join(installDir, '.gitignore'), newGitignoreContent, {
|
|
313
350
|
encoding: 'utf8',
|
|
314
351
|
flag: 'w',
|
|
315
352
|
});
|
|
@@ -320,8 +357,8 @@ async function addOrUpdateEnvironmentVariables({ projectApiKey, host, }) {
|
|
|
320
357
|
}
|
|
321
358
|
}
|
|
322
359
|
}
|
|
323
|
-
function getDotGitignore() {
|
|
324
|
-
const gitignorePath = path_1.default.join(
|
|
360
|
+
function getDotGitignore({ installDir, }) {
|
|
361
|
+
const gitignorePath = path_1.default.join(installDir, '.gitignore');
|
|
325
362
|
const gitignoreExists = fs.existsSync(gitignorePath);
|
|
326
363
|
if (gitignoreExists) {
|
|
327
364
|
return gitignorePath;
|