Skip to main content

@acromedia/build-test

Local tool that validates the create-nextjs template tree by scaffolding one Next.js app per CI build-option combination into a single Turborepo inside this project, wiring every @acromedia/* dep to the live monorepo source, and running type-check / lint / build across all of them with a single root pnpm install.

Why this exists

The .ts/.tsx files under starter-kits/create-nextjs/templates/ are never type-checked or linted as part of the monorepo. They only become a valid Next.js app after create-nextjs composes them:

  • The template dir has no node_modules — imports of next, react, @acromedia/gesso-next, etc. can't resolve.
  • Templates import generated sibling modules (src/cms.ts, src/commerce.ts, src/erp.ts, src/ai.ts, gesso.config.json) that only exist after pnpm gesso construct / pnpm gesso config runs.
  • Files contain //_|TAG|_ / //_|!TAG|_ conditional blocks; a tagged file isn't valid in any single configuration until parseTemplates strips the unused blocks.

@acromedia/build-test fills that gap.

This package is local-only

  • "private": true — never published.
  • Scripts are intentionally named compile, check, fix, test:fast, test:full, test:clean so they don't match the root turbo tasks (build, lint, lint:fix, test, test:ci, type-check). Running pnpm build / pnpm lint / pnpm test from the repo root does not run anything from this package.
  • Invoke explicitly via pnpm --filter @acromedia/build-test <script>.

Quick start

# 1. Build all gesso packages once so their dist/ trees are populated.
# (Or pass --build to do it automatically.)
pnpm build

# 2. (Optional) Copy .env.example to .env and fill in any values you want
# threaded into every generated app's .env / gesso.config.json.
cp starter-kits/build-test/.env.example starter-kits/build-test/.env

# 3. Fast tier — type-check + lint across every app in parallel:
pnpm --filter @acromedia/build-test test:fast

# 4. Full tier — also runs next build per app:
pnpm --filter @acromedia/build-test test:full

Typical cold-run time: ~25 s. Warm re-runs (workspace + node_modules already on disk) hit Turbo cache: 100s of ms for the validation tasks themselves, ~10–15 s end-to-end including the surrounding scaffold + lint:fix work.

Where the scratch workspace lives

<repo>/.build-test/workspace/ — inside this project, gitignored. Picked deliberately so a failure is one cd away in your IDE.

<repo>/
├── .build-test/ ← gitignored, all generated output goes here
│ └── workspace/
│ ├── apps/ ← wiped + rescaffolded every run
│ │ ├── bigcommerce-storyblok-acumatica-vertex/
│ │ ├── shopify-storyblok-acumatica-vertex/
│ │ └── drupal-commerce-drupal-acumatica-vertex/
│ ├── gesso-local/ ← refreshed from live source every run
│ │ ├── packages/
│ │ └── starter-kits/
│ ├── node_modules/ ← persists across runs (single shared install)
│ ├── .turbo/ ← persists (turbo task cache)
│ └── pnpm-lock.yaml ← persists
└── starter-kits/build-test/ ← this package's source

The workspace is persistent: re-running test:fast after test:fast reuses node_modules/ and the turbo cache, so a successful re-run usually takes only seconds. apps/ and gesso-local/ are wiped on each run so stale generated files (from gesso construct) never bleed between runs.

To start cold: pnpm --filter @acromedia/build-test test:clean (or node dist/bin.js --clean).

"Live" gesso source

Every @acromedia/* package in each generated app's package.json is rewritten to a file: dep pointing at <workspace>/gesso-local/<type>/<dir>/. Those entries are fresh copies of the live source under <repo>/packages/.../ and <repo>/starter-kits/.../, recomposed every run. The workflow:

  1. Edit <repo>/packages/ui/next/src/Foo.tsx (or any gesso package source).
  2. Rebuild that package's dist: pnpm --filter @acromedia/gesso-next build (only the changed package, not the whole tree).
  3. Re-run — fresh dist/ is copied into gesso-local/ and pnpm install re-links it into every app.

Compared to the previous .tgz-based approach: no pnpm pack round-trip, no whole-tree rebuild — just rebuild the changed package and re-run. Total per-run overhead from the copy step: ~1 sec (gesso's combined dist/ footprint is ~64 MB).

pnpm 9.x packs file: deps into its store before linking them into node_modules. Top-level directory symlinks aren't followed during that pack step, so a symlinked dist/ arrives empty in the installed copy. Bytewise copy is the smallest reliable fix and is fast enough that the trade-off is uninteresting.

CLI flags

node dist/bin.js [--tasks <list>] [--scratchDir <path>] [--only <combo>] [--build] [--clean]
FlagDefaultMeaning
--taskstype-check,lintComma-separated list of Turbo tasks. Any of type-check, lint, build.
--scratchDir<repo>/.build-test/Override the scratch parent dir.
--only <combo>allRun a single combo (e.g. shopify-storyblok-acumatica-vertex).
--buildoffRun pnpm build at repo root before copying deps. Useful when dist/ is stale.
--cleanoffRemove the scratch dir tree and exit.

Maintenance scripts (this package's own source)

pnpm --filter @acromedia/build-test compile   # tsc → dist/
pnpm --filter @acromedia/build-test check # tsc --noEmit + eslint + prettier --check
pnpm --filter @acromedia/build-test fix # eslint --fix + prettier --write

Environment variables

.env.example documents every var each generated test app's .env and gesso.config.json consume. Names match .gitlab-ci.yml and templates/default/example.env, so a .env populated for one of the demos can be dropped in here as-is. Unset values fall back to stubs declared in src/env.ts (fine for type-check / lint; next build paths that fetch at compile time may need real values).

Dataflow: .envsrc/loadEnv.ts (loaded at CLI startup) → src/env.ts (maps env vars to create-nextjs options) → create-nextjs/src/utils.ts:setEnv/setConfig (writes options into the scaffolded app's .env and gesso.config.json).

Combo matrix (today)

Mirrors the demos CI deploys via .lagoon/{bigcommerce,shopify,drupal-commerce}-demo/Dockerfile:

NameCMSCommerceERPAI
bigcommerce-storyblok-acumatica-vertexstoryblokbigcommerceacumaticavertex
shopify-storyblok-acumatica-vertexstoryblokshopifyacumaticavertex
drupal-commerce-drupal-acumatica-vertexdrupaldrupal-commerceacumaticavertex

Add a new combo by editing src/combos.ts.

How it works

  1. Scaffold a Turborepo at <repo>/.build-test/workspace/ with @acromedia/create-mono-repo (only on the first run; subsequent runs reuse the existing workspace).
  2. Rewrite <workspace>/turbo.json with our task graph (build, lint, type-check).
  3. Wipe <workspace>/gesso-local/ and refill it from the live source: for each gesso package, copy every published top-level entry (dist/, bin/, etc.) and write a rewritten package.json whose deps point at sibling gesso-local/ entries via file: paths.
  4. Wipe <workspace>/apps/ and scaffold each combo there with @acromedia/create-nextjs, passing useLocal: '<workspace>/gesso-local', skipInstall: true, and skipPostInstall: true.
  5. Patch each app's package.json to add a type-check script (tsc --noEmit) if missing.
  6. Run pnpm install once at the workspace root — pnpm dedupes everything because all apps share identical file: paths.
  7. Run pnpm gesso construct {provider} {platform} for each provider, then pnpm gesso config per app. Generated sibling modules now exist; the //_|TAG|_ blocks were already stripped during copyTemplateFiles inside createProject.
  8. Run pnpm lint:fix per app to format generated files and post-parseTemplates code (mirrors the lintFix step at the tail of create-nextjs).
  9. Run pnpm turbo run <tasks> at the workspace root. Turbo reports per-app pass/fail natively.

Iterating on a failure

When something fails, the scratch workspace stays put. To dig in:

cd .build-test/workspace/apps/shopify-storyblok-acumatica-vertex
pnpm type-check # re-run just this app's type-check
pnpm lint
pnpm build

To rerun against a single combo:

node dist/bin.js --only shopify-storyblok-acumatica-vertex