Files
twenty/packages/twenty-website/scripts/check-lottie-frames.mjs
T
Félix MalfaitandGitHub 658bdf3e57 chore(website): rename twenty-website-new → twenty-website (#20745)
## Summary
Follow-up to the Cloudflare/OpenNext migration (#20741). Now that the
legacy `twenty-website` package was already removed in #20270, the
`-new` suffix on the marketing site package is no longer meaningful.

## What changes
- **Directory rename**: `git mv packages/twenty-website-new
packages/twenty-website` (1213 files moved, no content change)
- **Package + nx config**: `package.json` and `project.json` name fields
updated, `sourceRoot` repointed
- **Source refs**: `load-local-articles.ts` and
`load-local-release-notes.ts` had a hardcoded `'twenty-website-new'`
segment in their monorepo-root fallback path;
`app/[locale]/releases/page.tsx` had display strings showing where to
add content
- **External refs**: root `package.json` workspaces, root `CLAUDE.md` /
`README.md`, `twenty-sdk` + `create-twenty-app` READMEs,
`.vscode/twenty.code-workspace`, `.cursor/rules/changelog-process.mdc`,
Crowdin config + the three `website-i18n-*` CI workflows +
`ci-website.yaml`
- **Docker cleanup**:
`packages/twenty-docker/twenty-website-new/Dockerfile` deleted; the two
Makefile targets (`prod-website-new-build` / `prod-website-new-run`)
that referenced it removed — EKS deploy was retired in the Cloudflare
migration
- **`yarn.lock`** regenerated against the new workspace path

## What's deliberately not in this PR
The dev hostname `website-new.twenty-main.com` in `wrangler.jsonc` stays
for now. Migrating it to `website.twenty-main.com` needs coordinated DNS
deletion (current CNAME points at the legacy Docusaurus NLB and serves
503s) and removal of the matching legacy `website` Helm chart in
`twenty-infra`. Flagged as a separate cleanup.

Companion infra PR: https://github.com/twentyhq/twenty-infra/pull/682
(workflow paths + Terraform ECR + docs)

## Test plan
- [x] `yarn install --immutable` resolves clean against the new path
- [x] `npx nx typecheck twenty-website` passes
- [x] `npx nx lint twenty-website` passes
- [ ] CI on this PR confirms the same on a fresh checkout
- [ ] After merge: trigger `Deploy Website` workflow against
`environment=dev` to confirm the renamed working-directory deploys
correctly
2026-05-19 23:42:09 +02:00

120 lines
3.5 KiB
JavaScript

#!/usr/bin/env node
import { execFile } from 'node:child_process';
import { readFile } from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { promisify } from 'node:util';
const execFileAsync = promisify(execFile);
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const ROOT = path.resolve(__dirname, '..');
const LOTTIE_PATH = path.join(
ROOT,
'public',
'lottie',
'stepper',
'stepper.lottie',
);
const ANIMATION_ENTRY = 'animations/main.json';
const FRAME_MAP_PATH = path.join(
ROOT,
'src',
'sections',
'HomeStepper',
'components',
'Visual',
'use-dot-lottie-scroll-sync.ts',
);
const EXPECTED_CONSTANT_REGEX =
/(?:export\s+)?const\s+HOME_STEPPER_LOTTIE_EXPECTED_TOTAL_FRAMES\s*=\s*(\d+)\s*;/;
function fail(message) {
// eslint-disable-next-line no-console
console.error(`\n check-lottie-frames: ${message}\n`);
process.exitCode = 1;
}
async function readExpectedTotalFrames() {
const source = await readFile(FRAME_MAP_PATH, 'utf8');
const match = source.match(EXPECTED_CONSTANT_REGEX);
if (match === null) {
throw new Error(
`couldn't find HOME_STEPPER_LOTTIE_EXPECTED_TOTAL_FRAMES in ${path.relative(ROOT, FRAME_MAP_PATH)}. ` +
'Has the constant been renamed? Update both the TS file and the regex in this script.',
);
}
return Number.parseInt(match[1], 10);
}
async function readActualTotalFrames() {
let stdout;
try {
const result = await execFileAsync(
'unzip',
['-p', LOTTIE_PATH, ANIMATION_ENTRY],
{ maxBuffer: 32 * 1024 * 1024, encoding: 'buffer' },
);
stdout = result.stdout;
} catch (error) {
if (error?.code === 'ENOENT') {
throw new Error(
'`unzip` binary not found on PATH. Install it (Debian/Ubuntu: `apt install unzip`, macOS ships with it) — ' +
'this script needs it to read the Lottie animation JSON.',
);
}
throw new Error(
`failed to extract ${ANIMATION_ENTRY} from ${path.relative(ROOT, LOTTIE_PATH)}: ${error?.message ?? error}`,
);
}
let animation;
try {
animation = JSON.parse(stdout.toString('utf8'));
} catch (error) {
throw new Error(
`${ANIMATION_ENTRY} inside the .lottie file is not valid JSON: ${error?.message ?? error}`,
);
}
const ip = animation?.ip;
const op = animation?.op;
if (typeof ip !== 'number' || typeof op !== 'number') {
throw new Error(
`${ANIMATION_ENTRY} is missing numeric \`ip\` / \`op\` fields ` +
`(got ip=${JSON.stringify(ip)}, op=${JSON.stringify(op)}). ` +
'Has the Lottie schema changed?',
);
}
return Math.floor(op - ip);
}
async function main() {
const [expected, actual] = await Promise.all([
readExpectedTotalFrames(),
readActualTotalFrames(),
]);
if (expected !== actual) {
fail(
`Lottie totalFrames mismatch — expected ${expected} (per HOME_STEPPER_LOTTIE_EXPECTED_TOTAL_FRAMES), ` +
`got ${actual} from ${path.relative(ROOT, LOTTIE_PATH)}.\n ` +
' The home-stepper scroll → frame map is keyed to the authored timeline; ' +
'either:\n 1. revert the Lottie re-export, or\n 2. update HOME_STEPPER_LOTTIE_EXPECTED_TOTAL_FRAMES ' +
'and the STEP_*_END anchors in home-stepper-lottie-frame-map.ts together.',
);
return;
}
// eslint-disable-next-line no-console
console.log(
`check-lottie-frames: OK (stepper.lottie totalFrames = ${actual}).`,
);
}
main().catch((error) => {
fail(error?.message ?? String(error));
});