@sogody/experiment-framework
v2.2.0
Published
A/B Testing Experiment Project Generator
Downloads
109
Readme
@sogody/experiment-framework
CLI that scaffolds a Vite + Preact A/B experiment project for Adobe Target. Generates a self-contained IIFE bundle per variation, wired up with @sogody/experiment-framework/framework, with clipboard copy on save so the built code can be pasted directly into Adobe Target.
Prerequisites
- Node ≥ 20.19 (recommended: 24 — see
.nvmrc) - pnpm 10.30.1
Generated projects support pnpm >=10.26.0. Node.js 20 users must use pnpm >=10.26.0 <11; pnpm 11 requires Node.js 22 or newer. This repository remains pinned to pnpm 10.30.1 for maintainer workflows.
Usage
npx @sogody/experiment-framework <project-name>
npx @sogody/experiment-framework <project-name> --controlPass --control to include an impression-only src/js/control/index.jsx entry
for the control experience.
Prompts:
| Prompt | Default | Notes |
|--------|---------|-------|
| Window namespace | sgd | JS identifier registered on window (e.g. window.sgd) |
| Emergency brake | true | Enables Adobe Target kill-switch via experiment.config.js |
| Variation count | 1 | Number of variations to scaffold (v1..vN) |
| E2E testing | false | Generates Playwright smoke tests for the selected markets when enabled |
After scaffolding, dependencies are installed automatically.
When E2E testing is enabled during scaffolding, select one or more market groups and/or individual markets. Groups expand to all member markets; mixed selections are unioned, deduplicated, and resolved in canonical order. Playwright runs once per resolved market, while the generated targetUrl uses the first resolved market as a single live-preview default.
What gets generated
Generic Adobe Target / Preact experiment boilerplate with a locale-aware button component:
<project-name>/
src/
components/
ExperimentButton/
index.jsx
styles.module.scss
js/
v1/ # Variation 1 entry point
index.jsx # runScript → render → track
v2/ # Additional variations (when variationCount > 1)
control/ # Control impression entry (when --control is used)
config.js # selectors, buttonText
# Runtime/build/new-variation tooling is provided by
# @sogody/experiment-framework dependency via exp-* binaries
experiment.config.js # runtime globalObject, emergencyBrake, targetUrl
vite.config.js # IIFE lib mode, Preact bundled, legacy aliases, CSS Modules
biome.json # JS/JSX formatter + linter (2-space indentation)Generated project source, configuration, E2E files, and formatter examples use two-space indentation.
Dev workflow
pnpm start 0 # watch v1 — rebuilds on save, copies bundle to clipboard
pnpm start # watch all variations
pnpm dev # watch all variations
pnpm build # production IIFE bundles → dist/vN-index.jsx (and dist/control-index.jsx with --control)
pnpm live # runs package-owned exp-live; opens targetUrl and injects the watched variation
pnpm live -- --profile shared # reuse one OS cache profile across experiments
pnpm add-e2e # add Playwright E2E setup to an existing generated project
pnpm lint # Biome for JS/JSX/JSONpnpm start 0 maps to src/js/v1/index.jsx, pnpm start 1 to v2, and so on. The clipboard copy keeps development logging enabled so you can paste and debug in Adobe Target's custom code editor. Production output uses pnpm build and the legacy flat shape, for example dist/v1-index.jsx.
CSS Modules class prefix is auto-derived from the project folder name (sanitized), so no extra CLI prompt is required. Generated class names use the legacy separator, for example project-name--button.
CSS is compiled by Vite with Sass support. Generated projects do not include Stylelint or cssnano; production CSS minification uses Vite's built-in esbuild CSS minifier.
Generated Vite config includes legacy import aliases: @src, @js, @components, @services, and @helpers. Build and live commands read experiment.config.js first and fall back to legacy config.json.
Runtime helpers from @sogody/experiment-framework/framework include legacy-compatible exports such as runScript, withPreact, waitForJquery, domReady, createElementFromHTML, setCookie, getCookie, emergencyBrakeEnabled, classes, trackInView, and setupTracking. withPreact is a compatibility shim because Preact remains bundled.
Use @sogody/experiment-framework/framework as the canonical runtime import path:
getPathSegments(path?)returns non-empty pathname segments and ignores the query string and hash.getMarket(path?)returns the lowercase first path segment, ornullwhen it is missing.log(...args)writes package-prefixed messages only in development builds.debug(...args)writes package-prefixed messages whenexpDebug=1is set in the URL or local storage.
New experiments include tracked AGENTS.md and CLAUDE.md files by default.
The thin agent rules discover version-matched documentation through
node_modules/@sogody/experiment-framework/llms.txt; no network lookup is required.
Older generated projects can create or repair these files with
pnpm exec exp-init-agents and pnpm exec exp-init-claude. Agent refreshes replace
only the framework-managed block and preserve project rules outside it. Use
--force only to replace an unmarked legacy or custom file intentionally.
Adding E2E Tests Later
Generated projects include an add-e2e script:
pnpm add-e2eFor older projects that do not have that script yet, run the package binary directly:
pnpm exec exp-add-e2eInteractive E2E setup accepts one or more market groups and/or individual markets. Group selections expand to all member markets; mixed selections are unioned, deduplicated, and resolved in canonical order. Playwright runs once per resolved market. The generated targetUrl uses the first resolved market as a single live-preview default; override it with pnpm live -- --url <url> when needed.
Options:
--forcereplaces generated E2E source files when a partial setup already exists.--dry-runprints planned file changes and commands without writing files.--yesaccepts the default base URL andUKmarket.--skip-installskips dependency installation.--run-smokerunspnpm test:e2eafter setup.
pnpm add-e2e does not install Playwright browsers. If the first smoke run reports
missing browsers, install them on demand with pnpm playwright install.
Adding a variation
pnpm new-variation 2 # scaffolds src/js/v2/ from v1
pnpm new-variation control # scaffolds src/js/control/ from v1Then update selectors, props, and tracking label in the new src/js/*/index.jsx entry and run pnpm build.
The start command uses zero-based indexes. Numeric variation folders are ordered first, then named folders such as control.
Use the generator's --control flag to create the control entry during initial scaffolding:
npx @sogody/experiment-framework <project-name> --controlTracking
Generated projects include src/helpers.js with a page-aware getTrackingLabel
helper backed by the framework's getPathSegments utility. Keep label
construction there, customize TRACKING_PROJECT and TRACKING_DESCRIPTION, and
use the shared runtime tracking functions after Preact render:
import { setupTracking, trackInView } from '@sogody/experiment-framework/framework';
import { getTrackingLabel } from '../../helpers';
trackInView(container.querySelector('button'), {
label: getTrackingLabel('v', 'scrolled into view'),
onceKey: 'experiment-name:v1:button-impression',
});
setupTracking(container, {
label: getTrackingLabel('v', 'cta clicked'),
selector: 'button',
});When generated with --control, src/js/control/index.jsx waits for
selectors.primary and tracks the native control element with:
getTrackingLabel('c', 'scrolled into view');The control bundle is written to dist/control-index.jsx by pnpm build.
CI
No CI pipeline is configured yet. Run pnpm test locally before opening a PR.
Release handoff
Maintainers should publish with Node 24 and pnpm 10.30.1:
nvm use 24
pnpm install
pnpm release:check
npm view @sogody/experiment-framework version
pnpm publish --access publicIf npm view @sogody/experiment-framework version returns 404 Not Found, the scoped package is not currently published or is not visible to the current account. Confirm package-name ownership before publishing. If local npm cache permissions block npm pack, rerun the pack check with a temporary cache:
npm --cache /tmp/experiment-framework-npm-cache pack --dry-runContributing
nvm use 24 # match .nvmrc
pnpm install
pnpm test # run integration tests before committingMigration Note
pnpm build approvals
Projects generated with the old pnpm approval format should:
- Remove
package.json.pnpm.onlyBuiltDependencies. - Change
engines.pnpminpackage.jsonto>=10.26.0. - Add
pnpm-workspace.yaml:
allowBuilds:
'@biomejs/biome': true
'@parcel/watcher': true
esbuild: true
strictDepBuilds: trueThis migration is intentionally manual so existing project-specific approvals are not overwritten.
Package scripts
If you generated a project with older package conventions, replace these script names in your generated project:
sogody-start->exp-startsogody-build->exp-buildsogody-new-variation->exp-new-variation
