diff --git a/.changeset/lucky-donuts-repeat.md b/.changeset/lucky-donuts-repeat.md new file mode 100644 index 00000000..84e3c74a --- /dev/null +++ b/.changeset/lucky-donuts-repeat.md @@ -0,0 +1,5 @@ +--- +'@fingerprint/react': minor +--- + +Remove the `postinstall` script for detecting framework version. diff --git a/__tests__/detect-env.test.ts b/__tests__/detect-env.test.ts index 78d7f5e8..db39012a 100644 --- a/__tests__/detect-env.test.ts +++ b/__tests__/detect-env.test.ts @@ -1,3 +1,5 @@ +import { version as reactVersion } from 'react' + import { detectEnvironment } from '../src/detect-env' import { Env } from '../src/env.types' import { afterEach, describe, expect, it, vi } from 'vitest' @@ -33,6 +35,7 @@ describe('Detect user env', () => { expect(env).toEqual({ name: Env.React, + version: reactVersion, }) }) }) @@ -88,6 +91,7 @@ describe('Detect user env', () => { expect(env).toEqual({ name: Env.React, + version: reactVersion, }) } finally { if (originalWindowDescriptor) { @@ -99,63 +103,3 @@ describe('Detect user env', () => { }) }) }) - -describe('getEnvironment', () => { - afterEach(() => { - vi.resetModules() - vi.doUnmock('../src/env') - }) - - it('returns parsed env details when the build-time env placeholder is valid JSON', async () => { - vi.resetModules() - vi.doMock('../src/env', () => ({ - env: JSON.stringify({ name: 'react', version: '18.0.0' }), - })) - - const { getEnvironment: getEnvironmentFresh } = await import('../src/get-env') - - expect( - getEnvironmentFresh({ - context: { classRenderReceivesAnyArguments: false }, - }) - ).toEqual({ - name: 'react', - version: '18.0.0', - }) - }) - - it('falls back to detection when the build-time env JSON is not env details', async () => { - vi.resetModules() - vi.doMock('../src/env', () => ({ - env: JSON.stringify({ foo: 'bar' }), - })) - - const { getEnvironment: getEnvironmentFresh } = await import('../src/get-env') - - expect( - getEnvironmentFresh({ - // absence of classRenderReceivesAnyArguments is React signal - context: { classRenderReceivesAnyArguments: false }, - }) - ).toEqual({ - name: Env.React, - }) - }) - - it('falls back to detection when the build-time env is invalid', async () => { - vi.resetModules() - vi.doMock('../src/env', () => ({ - env: '%DETECTED_ENV%', - })) - - const { getEnvironment: getEnvironmentFresh } = await import('../src/get-env') - - expect( - getEnvironmentFresh({ - context: { classRenderReceivesAnyArguments: false }, - }) - ).toEqual({ - name: Env.React, - }) - }) -}) diff --git a/__tests__/fpjs-provider.test.tsx b/__tests__/fpjs-provider.test.tsx index a09068db..3c96e3cc 100644 --- a/__tests__/fpjs-provider.test.tsx +++ b/__tests__/fpjs-provider.test.tsx @@ -1,4 +1,4 @@ -import { PropsWithChildren, useContext } from 'react' +import { PropsWithChildren, useContext, version as reactVersion } from 'react' import { act, render, renderHook } from '@testing-library/react' import { FingerprintContext, FingerprintProvider, FingerprintProviderOptions, useVisitorData } from '../src' import { createWrapper, getDefaultStartOptions } from './helpers' @@ -7,6 +7,8 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import * as agent from '@fingerprint/agent' import type { GetOptions } from '@fingerprint/agent' import * as ssr from '../src/ssr' +import * as detectEnv from '../src/detect-env' +import { Env } from '../src/env.types' vi.mock('@fingerprint/agent', { spy: true }) @@ -67,7 +69,7 @@ describe('FingerprintProvider', () => { }) expect(mockStart).toHaveBeenCalledWith({ ...loadOptions, - integrationInfo: [`react-sdk/${version}/react`], + integrationInfo: [`react-sdk/${version}/react/${reactVersion}`], cache: { cachePrefix: 'cache', storage: 'sessionStorage', @@ -88,6 +90,18 @@ describe('FingerprintProvider', () => { ) }) + it('should omit the version in integrationInfo when the framework exposes none', () => { + vi.spyOn(detectEnv, 'detectEnvironment').mockReturnValue({ name: Env.Preact }) + + renderProvider() + + expect(mockStart).toHaveBeenCalledWith( + expect.objectContaining({ + integrationInfo: [`react-sdk/${version}/preact`], + }) + ) + }) + it('should rebuild the agent when forceRebuild is enabled and options change', () => { const { rerender } = renderProvider({ apiKey: 'key-a', forceRebuild: true }) diff --git a/__tests__/with-environment.preact.test.tsx b/__tests__/with-environment.preact.test.tsx index 690d9f78..94da9150 100644 --- a/__tests__/with-environment.preact.test.tsx +++ b/__tests__/with-environment.preact.test.tsx @@ -22,5 +22,18 @@ describe('WithEnvironment', () => { expect(container.innerHTML).toContain('preact') }) + + it('should not report a version', async () => { + const { WithEnvironment } = await import('../src/components/with-environment') + const PrintEnv = (props: { env: { version?: string } }) => + h('div', null, JSON.stringify(props.env.version ?? null)) + + // @ts-expect-error -- preact's render signature does not match React's @testing-library/react types + const { container } = preactRender(h(WithEnvironment, { children: (env) => h(PrintEnv, { env }) })) + + // `compat.version` is the hardcoded React-compatibility number and must never leak through + expect(compat.version).toBeDefined() + expect(container.innerHTML).toBe('
null
') + }) }) }) diff --git a/package.json b/package.json index f14edf40..005c614d 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,6 @@ "privacy" ], "scripts": { - "postinstall": "node scripts/detect-env.js", "prepare": "husky", "watch": "vite build --watch", "build": "vite build", @@ -45,8 +44,7 @@ "changeset:publish": "HUSKY=0 changeset publish" }, "files": [ - "dist", - "scripts" + "dist" ], "repository": { "type": "git", diff --git a/scripts/detect-env.js b/scripts/detect-env.js deleted file mode 100644 index e1462dbd..00000000 --- a/scripts/detect-env.js +++ /dev/null @@ -1,58 +0,0 @@ -/* eslint-disable @typescript-eslint/no-require-imports */ -const fs = require('fs') -const pkg = require('../package.json') -const path = require('path') -const cwd = process.env.INIT_CWD || path.resolve('../../', __dirname) - -const token = '%DETECTED_ENV%' - -const Env = { - React: 'react', - Preact: 'preact', - Next: 'next', - Unknown: 'unknown', -} - -const distPaths = [pkg.main, pkg.module] - -const envDetectOrder = [Env.Next, Env.Preact, Env.React] - -function detectEnv(env) { - try { - const pkgPath = path.join(cwd, `node_modules/${env}/package.json`) - const pkg = require(pkgPath) - - return pkg.version - } catch { - return null - } -} - -function writeDetectedEnv(env, version) { - distPaths.forEach((distPath) => { - const fullDistPath = path.resolve(__dirname, '../', distPath) - - const contents = fs.readFileSync(fullDistPath).toString() - const newContents = contents.replace(token, JSON.stringify({ name: env, version }).replace(/"/g, '\\"')) - - fs.writeFileSync(fullDistPath, newContents) - }) -} - -function main() { - for (const env of envDetectOrder) { - const version = detectEnv(env) - - if (version) { - writeDetectedEnv(env, version) - - return - } - } -} - -try { - main() -} catch { - // Nothing here... -} diff --git a/src/components/with-environment.tsx b/src/components/with-environment.tsx index 833fd07c..037001d4 100644 --- a/src/components/with-environment.tsx +++ b/src/components/with-environment.tsx @@ -1,7 +1,6 @@ import { Component, type JSX } from 'react' -import { getEnvironment } from '../get-env' -import { type DetectEnvParams } from '../detect-env' +import { detectEnvironment, type DetectEnvParams } from '../detect-env' import { type EnvDetails } from '../env.types' export interface WithEnvironmentProps { @@ -31,7 +30,7 @@ class WithEnvironment extends Component { context: { classRenderReceivesAnyArguments: hasAnyArguments }, } - this.detectedEnv = getEnvironment(detectParams) + this.detectedEnv = detectEnvironment(detectParams) } return this.props.children(this.detectedEnv) diff --git a/src/detect-env.ts b/src/detect-env.ts index 52ceeed4..2c2a76ad 100644 --- a/src/detect-env.ts +++ b/src/detect-env.ts @@ -1,3 +1,5 @@ +import { version as reactVersion } from 'react' + import { Env, type EnvDetails } from './env.types' export interface DetectEnvContext { @@ -34,13 +36,6 @@ function isPreact(context: DetectEnvContext) { return context.classRenderReceivesAnyArguments } -/** - * Checks if user is using react. - * */ -function isReact(context: DetectEnvContext) { - return !context.classRenderReceivesAnyArguments -} - /** * Runs checks that determine if user is using next. * Those checks should have almost 100% accuracy. @@ -78,13 +73,8 @@ export function detectEnvironment({ context }: DetectEnvParams): EnvDetails { } } - if (isReact(context)) { - return { - name: Env.React, - } - } - return { - name: Env.Unknown, + name: Env.React, + version: reactVersion, } } diff --git a/src/env.ts b/src/env.ts deleted file mode 100644 index 42517067..00000000 --- a/src/env.ts +++ /dev/null @@ -1 +0,0 @@ -export const env = '%DETECTED_ENV%' diff --git a/src/env.types.ts b/src/env.types.ts index 95038743..44182133 100644 --- a/src/env.types.ts +++ b/src/env.types.ts @@ -2,14 +2,9 @@ export enum Env { React = 'react', Preact = 'preact', Next = 'next', - Unknown = 'unknown', } export interface EnvDetails { name: Env version?: string } - -export function isEnvDetails(value: unknown): value is EnvDetails { - return typeof value === 'object' && value !== null && 'name' in value -} diff --git a/src/get-env.ts b/src/get-env.ts deleted file mode 100644 index 65f4f0cf..00000000 --- a/src/get-env.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { env } from './env' -import { EnvDetails, isEnvDetails } from './env.types' -import { detectEnvironment, type DetectEnvParams } from './detect-env' - -export function getEnvironment(params: DetectEnvParams): EnvDetails { - try { - const jsonEnv: unknown = JSON.parse(env) - if (isEnvDetails(jsonEnv)) { - return jsonEnv - } - } catch { - // Nothing here... - } - - return detectEnvironment(params) -}