Skip to content

Commit 8477826

Browse files
authored
Upgrade analytics.js to analytics-next (browser SDK) (#297)
1 parent ff7ba5f commit 8477826

File tree

13 files changed

+1192
-287
lines changed

13 files changed

+1192
-287
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,7 @@ test-env/**
9191

9292
# intelliJ
9393
.idea
94+
95+
96+
# all build artifacts
97+
**/build/**

jest.config.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
module.exports = {
22
preset: 'ts-jest',
3-
testPathIgnorePatterns: [
4-
'./src/__tests__/__helpers__/',
5-
'./src/__tests__/__data__/'
6-
],
3+
testMatch: ['**/?(*.)+(test).[jt]s?(x)'],
74
modulePathIgnorePatterns: ['/dist/'],
5+
setupFilesAfterEnv: ['./jest.setup.js'],
86
globals: {
97
'ts-jest': {
108
tsconfig: 'tsconfig.json'

jest.setup.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// eslint-disable-next-line no-undef
2+
const supressLog = (...args) => {
3+
// avoid Warning: No API token found at test-env/build
4+
if (typeof args[0] === 'string' && args[0].includes('API token')) {
5+
return undefined
6+
} else {
7+
console.error(...args)
8+
}
9+
}
10+
11+
console.error = supressLog

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
},
4343
"devDependencies": {
4444
"@oclif/test": "^2.1",
45+
"@segment/analytics-next": "^1.51.6",
4546
"@segment/analytics-react-native": "^2.2.1",
4647
"@types/analytics-node": "^3.1.9",
4748
"@types/chai": "^4",
@@ -99,7 +100,8 @@
99100
"postpack": "shx rm -f oclif.manifest.json",
100101
"posttest": "yarn lint",
101102
"prepack": "yarn build && oclif manifest && oclif readme",
102-
"test": "jest --runInBand",
103+
"test": "yarn test:typedef && jest --runInBand",
104+
"test:typedef": "npx ts-node src/__tests__/ts-typedef-tests/run.ts",
103105
"version": "oclif readme && git add README.md"
104106
},
105107
"engines": {

src/__tests__/commands/__snapshots__/build.test.ts.snap

Lines changed: 10 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -5174,102 +5174,19 @@ import Ajv, { ErrorObject } from 'ajv'
51745174
* You can install it by following instructions at: https://segment.com/docs/sources/website/analytics.js/quickstart/
51755175
* Make sure to also include the TypeScript declarations with: \`npm install --dev @types/segment-analytics\`
51765176
*/
5177-
declare global {
5177+
import type { AnalyticsSnippet, Analytics, AnalyticsBrowser, Options } from '@segment/analytics-next'
5178+
5179+
declare global {
51785180
interface Window {
5179-
analytics: SegmentAnalytics.AnalyticsJS
5181+
analytics: AnalyticsSnippet;
51805182
}
51815183
}
51825184

5185+
type AnyAnalytics = AnalyticsSnippet | Analytics | AnalyticsBrowser
5186+
51835187
/** The callback exposed by analytics.js. */
51845188
export type Callback = () => void
51855189

5186-
/** A dictionary of options. For example, enable or disable specific destinations for the call. */
5187-
export interface Options {
5188-
/**
5189-
* Selectivly filter destinations. By default all destinations are enabled.
5190-
* https://segment.com/docs/sources/website/analytics.js/#selecting-destinations
5191-
*/
5192-
integrations?: {
5193-
[key: string]: boolean | { [key: string]: any }
5194-
}
5195-
/**
5196-
* A dictionary of extra context to attach to the call.
5197-
* https://segment.com/docs/spec/common/#context
5198-
*/
5199-
context?: Context
5200-
}
5201-
5202-
/**
5203-
* Context is a dictionary of extra information that provides useful context about a datapoint.
5204-
* @see {@link https://segment.com/docs/spec/common/#context}
5205-
*/
5206-
export interface Context extends Record<string, any> {
5207-
active?: boolean
5208-
app?: {
5209-
name?: string
5210-
version?: string
5211-
build?: string
5212-
}
5213-
campaign?: {
5214-
name?: string
5215-
source?: string
5216-
medium?: string
5217-
term?: string
5218-
content?: string
5219-
}
5220-
device?: {
5221-
id?: string
5222-
manufacturer?: string
5223-
model?: string
5224-
name?: string
5225-
type?: string
5226-
version?: string
5227-
}
5228-
ip?: string
5229-
locale?: string
5230-
location?: {
5231-
city?: string
5232-
country?: string
5233-
latitude?: string
5234-
longitude?: string
5235-
region?: string
5236-
speed?: string
5237-
}
5238-
network?: {
5239-
bluetooth?: string
5240-
carrier?: string
5241-
cellular?: string
5242-
wifi?: string
5243-
}
5244-
os?: {
5245-
name?: string
5246-
version?: string
5247-
}
5248-
page?: {
5249-
hash?: string
5250-
path?: string
5251-
referrer?: string
5252-
search?: string
5253-
title?: string
5254-
url?: string
5255-
}
5256-
referrer?: {
5257-
type?: string
5258-
name?: string
5259-
url?: string
5260-
link?: string
5261-
}
5262-
screen?: {
5263-
density?: string
5264-
height?: string
5265-
width?: string
5266-
}
5267-
timezone?: string
5268-
groupId?: string
5269-
traits?: Record<string, any>
5270-
userAgent?: string
5271-
}
5272-
52735190
export type ViolationHandler = (
52745191
message: Record<string, any>,
52755192
violations: ErrorObject[]
@@ -5297,7 +5214,7 @@ export const defaultValidationErrorHandler: ViolationHandler = (message, violati
52975214

52985215
let onViolation = defaultValidationErrorHandler
52995216

5300-
let analytics: () => SegmentAnalytics.AnalyticsJS | undefined = () => {
5217+
let analytics: () => AnyAnalytics | undefined = () => {
53015218
return window.analytics;
53025219
};
53035220

@@ -5307,7 +5224,7 @@ export interface TypewriterOptions {
53075224
* Underlying analytics instance where analytics calls are forwarded on to.
53085225
* Defaults to window.analytics.
53095226
*/
5310-
analytics?: SegmentAnalytics.AnalyticsJS;
5227+
analytics?: AnyAnalytics;
53115228
/**
53125229
* Handler fired when if an event does not match its spec. This handler
53135230
* does not fire in production mode, because it requires inlining the full
@@ -5325,7 +5242,7 @@ export interface TypewriterOptions {
53255242
* @param {TypewriterOptions} options - the options to upsert
53265243
*
53275244
* @typedef {Object} TypewriterOptions
5328-
* @property {AnalyticsJS} [analytics] - Underlying analytics instance where analytics
5245+
* @property {AnyAnalytics} [analytics] - Underlying analytics instance where analytics
53295246
* calls are forwarded on to. Defaults to window.analytics.
53305247
* @property {Function} [onViolation] - Handler fired when if an event does not match its spec. This handler does not fire in
53315248
* production mode, because it requires inlining the full JSON Schema spec for each event in your Tracking Plan. By default,
@@ -5785,7 +5702,7 @@ const clientAPI = {
57855702
* @param {TypewriterOptions} options - the options to upsert
57865703
*
57875704
* @typedef {Object} TypewriterOptions
5788-
* @property {AnalyticsJS} [analytics] - Underlying analytics instance where analytics
5705+
* @property {AnyAnalytics} [analytics] - Underlying analytics instance where analytics
57895706
* calls are forwarded on to. Defaults to window.analytics.
57905707
* @property {Function} [onViolation] - Handler fired when if an event does not match its spec. This handler does not fire in
57915708
* production mode, because it requires inlining the full JSON Schema spec for each event in your Tracking Plan. By default,

src/__tests__/commands/__snapshots__/production.test.ts.snap

Lines changed: 10 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -679,102 +679,19 @@ export interface NoIDType {
679679
* You can install it by following instructions at: https://segment.com/docs/sources/website/analytics.js/quickstart/
680680
* Make sure to also include the TypeScript declarations with: \`npm install --dev @types/segment-analytics\`
681681
*/
682-
declare global {
682+
import type { AnalyticsSnippet, Analytics, AnalyticsBrowser, Options } from '@segment/analytics-next'
683+
684+
declare global {
683685
interface Window {
684-
analytics: SegmentAnalytics.AnalyticsJS
686+
analytics: AnalyticsSnippet;
685687
}
686688
}
687689
690+
type AnyAnalytics = AnalyticsSnippet | Analytics | AnalyticsBrowser
691+
688692
/** The callback exposed by analytics.js. */
689693
export type Callback = () => void
690694
691-
/** A dictionary of options. For example, enable or disable specific destinations for the call. */
692-
export interface Options {
693-
/**
694-
* Selectivly filter destinations. By default all destinations are enabled.
695-
* https://segment.com/docs/sources/website/analytics.js/#selecting-destinations
696-
*/
697-
integrations?: {
698-
[key: string]: boolean | { [key: string]: any }
699-
}
700-
/**
701-
* A dictionary of extra context to attach to the call.
702-
* https://segment.com/docs/spec/common/#context
703-
*/
704-
context?: Context
705-
}
706-
707-
/**
708-
* Context is a dictionary of extra information that provides useful context about a datapoint.
709-
* @see {@link https://segment.com/docs/spec/common/#context}
710-
*/
711-
export interface Context extends Record<string, any> {
712-
active?: boolean
713-
app?: {
714-
name?: string
715-
version?: string
716-
build?: string
717-
}
718-
campaign?: {
719-
name?: string
720-
source?: string
721-
medium?: string
722-
term?: string
723-
content?: string
724-
}
725-
device?: {
726-
id?: string
727-
manufacturer?: string
728-
model?: string
729-
name?: string
730-
type?: string
731-
version?: string
732-
}
733-
ip?: string
734-
locale?: string
735-
location?: {
736-
city?: string
737-
country?: string
738-
latitude?: string
739-
longitude?: string
740-
region?: string
741-
speed?: string
742-
}
743-
network?: {
744-
bluetooth?: string
745-
carrier?: string
746-
cellular?: string
747-
wifi?: string
748-
}
749-
os?: {
750-
name?: string
751-
version?: string
752-
}
753-
page?: {
754-
hash?: string
755-
path?: string
756-
referrer?: string
757-
search?: string
758-
title?: string
759-
url?: string
760-
}
761-
referrer?: {
762-
type?: string
763-
name?: string
764-
url?: string
765-
link?: string
766-
}
767-
screen?: {
768-
density?: string
769-
height?: string
770-
width?: string
771-
}
772-
timezone?: string
773-
groupId?: string
774-
traits?: Record<string, any>
775-
userAgent?: string
776-
}
777-
778695
export type ViolationHandler = (
779696
message: Record<string, any>,
780697
violations: any[]
@@ -801,7 +718,7 @@ export const defaultValidationErrorHandler: ViolationHandler = (message, violati
801718
};
802719
803720
804-
let analytics: () => SegmentAnalytics.AnalyticsJS | undefined = () => {
721+
let analytics: () => AnyAnalytics | undefined = () => {
805722
return window.analytics;
806723
};
807724
@@ -811,7 +728,7 @@ export interface TypewriterOptions {
811728
* Underlying analytics instance where analytics calls are forwarded on to.
812729
* Defaults to window.analytics.
813730
*/
814-
analytics?: SegmentAnalytics.AnalyticsJS;
731+
analytics?: AnyAnalytics;
815732
/**
816733
* Handler fired when if an event does not match its spec. This handler
817734
* does not fire in production mode, because it requires inlining the full
@@ -829,7 +746,7 @@ export interface TypewriterOptions {
829746
* @param {TypewriterOptions} options - the options to upsert
830747
*
831748
* @typedef {Object} TypewriterOptions
832-
* @property {AnalyticsJS} [analytics] - Underlying analytics instance where analytics
749+
* @property {AnyAnalytics} [analytics] - Underlying analytics instance where analytics
833750
* calls are forwarded on to. Defaults to window.analytics.
834751
* @property {Function} [onViolation] - Handler fired when if an event does not match its spec. This handler does not fire in
835752
* production mode, because it requires inlining the full JSON Schema spec for each event in your Tracking Plan. By default,
@@ -1240,7 +1157,7 @@ const clientAPI = {
12401157
* @param {TypewriterOptions} options - the options to upsert
12411158
*
12421159
* @typedef {Object} TypewriterOptions
1243-
* @property {AnalyticsJS} [analytics] - Underlying analytics instance where analytics
1160+
* @property {AnyAnalytics} [analytics] - Underlying analytics instance where analytics
12441161
* calls are forwarded on to. Defaults to window.analytics.
12451162
* @property {Function} [onViolation] - Handler fired when if an event does not match its spec. This handler does not fire in
12461163
* production mode, because it requires inlining the full JSON Schema spec for each event in your Tracking Plan. By default,
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Type definition "integration" tests for the typescript SDKs
2+
3+
```
4+
npx ts-node src/__tests__/ts-typedef-tests/run.ts
5+
```
6+
These tests confirm:
7+
- That the typescript clients have no type errors
8+
- That the typescript clients typings behaves as expected
9+
10+
Running the tests...
11+
1. Build typescript client using cli
12+
2. Imports that client into a type definition test that asserts that it's typed correctly
13+
3. Type checks both the client (artifact) and the type definition
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import fs from "node:fs";
2+
import path from "node:path";
3+
import { setupEnv } from "../__helpers__/environment";
4+
import { run } from "../__helpers__/oclif-runner";
5+
6+
const TEST_ENV_PATH = "production";
7+
8+
const configurations = [
9+
["typescript", "analytics-node", "segment.ts"],
10+
["typescript", "analytics-js", "segment.ts"],
11+
];
12+
13+
export const buildSDKs = async () => {
14+
for (const config of configurations) {
15+
const [language, sdk, filename, plan, id, outputPath, legacyId] = config;
16+
const testPath = await setupEnv(
17+
TEST_ENV_PATH,
18+
language,
19+
sdk,
20+
plan,
21+
id,
22+
outputPath,
23+
legacyId
24+
);
25+
await run(["production", "-c", testPath]);
26+
// basically, we write the test files to a new build path relative to this script.
27+
// typechecking ./test-env directly is a little complicated because of .tsconfig configuration + hashed directory name
28+
const output = fs
29+
.readFileSync(path.join(testPath, filename), {
30+
encoding: "utf-8",
31+
})
32+
.replace(/version:.*\d.*/g, "");
33+
34+
const BUILD_PATH = path.resolve(__dirname, "build");
35+
if (!fs.existsSync(BUILD_PATH)) {
36+
fs.mkdirSync(BUILD_PATH);
37+
}
38+
fs.writeFileSync(
39+
path.resolve(BUILD_PATH, `${language}-${sdk}.ts`),
40+
output,
41+
{ encoding: "utf-8" }
42+
);
43+
}
44+
};

0 commit comments

Comments
 (0)