Files
upstream-twenty/packages/twenty-ui/scripts/generateThemeTokens.ts
upstream-mirror e7da97194a
Some checks failed
App Prod-Parity E2E Dispatch / dispatch (push) Failing after 10s
CD Storybook UI / build (push) Failing after 3m25s
CD Storybook UI / dispatch (push) Has been skipped
CD deploy main / website-changed-files (push) Successful in 18m8s
CI Agent Skills / changed-files-check (push) Successful in 18m9s
CI Create App E2E minimal / changed-files-check (push) Successful in 24s
CI Create App / changed-files-check (push) Successful in 26s
CI Docs / changed-files-check (push) Successful in 26s
CI Emails / changed-files-check (push) Successful in 28s
CI Example App Hello World / changed-files-check (push) Successful in 23s
CI Front / changed-files-check (push) Has been skipped
CI Example App Postcard / changed-files-check (push) Successful in 27s
CI UI / changed-files-check (push) Has been skipped
CI Utils Test / changed-files-check (push) Successful in 45s
CI Twenty Apps / discover (push) Successful in 1m3s
CD deploy main / deploy-main (push) Failing after 3s
CI Agent Skills / agent-skills-validate (push) Has been skipped
CI Create App / create-app-test (lint) (push) Has been skipped
CI Emails / emails-test (push) Has been skipped
CI Front / front-task (lingui:compile) (push) Has been skipped
CI Front / front-task (lint) (push) Has been skipped
CI Example App Postcard / example-app-postcard (push) Has been skipped
CI UI / ui-task (lint) (push) Has been skipped
CI UI / ui-task (pack-size) (push) Has been skipped
CI UI / ui-task (size) (push) Has been skipped
CI UI / ui-task (test) (push) Has been skipped
CI UI / ui-task (typecheck) (push) Has been skipped
CI Utils Test / utils-test (push) Has been skipped
CI Twenty Apps / ${{ matrix.app.name }} (push) Has been skipped
CI Emails / ci-emails-status-check (push) Successful in 2s
CI Example App Hello World / ci-example-app-hello-world-status-check (push) Successful in 2s
CI Example App Postcard / ci-example-app-postcard-status-check (push) Successful in 3s
CI UI / ui-sb-test (push) Has been skipped
CI Utils Test / ci-utils-test-status-check (push) Successful in 2s
CI Twenty Apps / ci-twenty-apps-status-check (push) Successful in 2s
CI UI / ci-ui-status-check (push) Successful in 2s
CI Desktop / desktop (push) Failing after 1m8s
Push docs to Crowdin / Push documentation to Crowdin (push) Failing after 55s
Push translations to Crowdin / Extract and upload translations (push) Failing after 5s
Push website translations to Crowdin / Extract and upload website translations (push) Failing after 5s
CI Create App / create-app-test (test) (push) Has been skipped
CI Create App / create-app-test (typecheck) (push) Has been skipped
CI Docs / docs-lint (push) Has been skipped
CI Example App Hello World / example-app-hello-world (push) Has been skipped
CI Front / front-task (lingui:extract) (push) Has been skipped
CI Front / front-task (test) (push) Has been skipped
CI Front / front-task (typecheck) (push) Has been skipped
CI UI / ui-sb-build (push) Failing after 1m32s
CI Twenty Apps / ${{ matrix.app.name }} (${{ matrix.server-source }}) (push) Has been skipped
CI Agent Skills / agent-skills-publish (push) Has been skipped
CI Create App / ci-create-app-status-check (push) Successful in 2s
CI Agent Skills / agent-skills-remote-source (push) Has been skipped
CI Create App E2E minimal / create-app-e2e-minimal (push) Has been cancelled
CI E2E Main / e2e-test (push) Has been cancelled
CI Front / front-sb-build (push) Has been cancelled
CI Front / front-build (push) Has been cancelled
CI Create App E2E minimal / ci-create-app-e2e-minimal-status-check (push) Has been cancelled
CI E2E Main / ci-e2e-main-status-check (push) Has been cancelled
CI E2E Main / notify-main-ci-failure (push) Has been cancelled
CI E2E Main / qa-scout-comment (push) Has been cancelled
CI Front / front-sb-test (push) Has been cancelled
CI Front / front-sb-screenshots (push) Has been cancelled
CI Front / ci-front-status-check (push) Has been cancelled
DPA Sub-Processor Sync / sync-subprocessors (push) Failing after 3s
AI Catalog Sync / sync-catalog (push) Failing after 2s
snapshot of https://github.com/twentyhq/twenty.git (main, 2026-09-17; blobs > 131072B omitted)
2026-09-17 23:28:13 +05:00

155 lines
5.2 KiB
TypeScript

import { spawnSync } from 'node:child_process';
import { existsSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
import { dirname, relative, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { MAIN_COLOR_TOKENS } from '../design-tokens/color/mainColors';
import { DESIGN_TOKENS } from '../design-tokens/designTokens';
import { buildMainColorNames } from '../design-tokens/pipeline/buildMainColorNames';
import { buildThemeConstants } from '../design-tokens/pipeline/buildThemeConstants';
import { buildThemeCss } from '../design-tokens/pipeline/buildThemeCss';
import { buildThemeCommon } from '../design-tokens/pipeline/buildThemeCommon';
import { buildThemeCssVariables } from '../design-tokens/pipeline/buildThemeCssVariables';
import { buildThemeSpacing } from '../design-tokens/pipeline/buildThemeSpacing';
import { buildThemeSubtreeConstant } from '../design-tokens/pipeline/buildThemeSubtreeConstant';
import { buildThemeTypes } from '../design-tokens/pipeline/buildThemeTypes';
import { collectLeaves } from '../design-tokens/pipeline/collectLeaves';
import { THEME_COMMON_ROOT_KEYS } from '../design-tokens/themeCommonRootKeys';
import { THEME_CSS_FILE_NAME_BY_SCHEME } from '../design-tokens/themeCssFileNameByScheme';
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
const themeConstantsDirectory = resolve(packageRoot, 'src/theme-constants');
const themeDirectory = resolve(packageRoot, 'src/theme');
const leaves = collectLeaves(DESIGN_TOKENS);
const sourceOutputs = [
{
path: resolve(themeConstantsDirectory, THEME_CSS_FILE_NAME_BY_SCHEME.light),
content: buildThemeCss({ leaves, scheme: 'light' }),
},
{
path: resolve(themeConstantsDirectory, THEME_CSS_FILE_NAME_BY_SCHEME.dark),
content: buildThemeCss({ leaves, scheme: 'dark' }),
},
{
path: resolve(themeConstantsDirectory, 'themeCssVariables.ts'),
content: buildThemeCssVariables(leaves),
},
{
path: resolve(themeConstantsDirectory, 'themeTypes.ts'),
content: buildThemeTypes(leaves),
},
{
path: resolve(themeDirectory, 'internal/themeSpacing.ts'),
content: buildThemeSpacing(leaves),
},
{
path: resolve(themeDirectory, 'constants/ThemeLight.ts'),
content: buildThemeConstants({ leaves, scheme: 'light' }),
},
{
path: resolve(themeDirectory, 'constants/ThemeDark.ts'),
content: buildThemeConstants({ leaves, scheme: 'dark' }),
},
{
path: resolve(themeDirectory, 'constants/ThemeCommon.ts'),
content: buildThemeCommon({ leaves, rootKeys: THEME_COMMON_ROOT_KEYS }),
},
{
path: resolve(themeDirectory, 'constants/MainColorNames.ts'),
content: buildMainColorNames(Object.keys(MAIN_COLOR_TOKENS)),
},
{
path: resolve(themeDirectory, 'constants/Animation.ts'),
content: buildThemeSubtreeConstant({
leaves,
rootKey: 'animation',
scheme: 'light',
exportName: 'ANIMATION',
}),
},
{
path: resolve(themeDirectory, 'constants/GrayScaleLight.ts'),
content: buildThemeSubtreeConstant({
leaves,
rootKey: 'grayScale',
scheme: 'light',
exportName: 'GRAY_SCALE_LIGHT',
}),
},
{
path: resolve(themeDirectory, 'constants/GrayScaleDark.ts'),
content: buildThemeSubtreeConstant({
leaves,
rootKey: 'grayScale',
scheme: 'dark',
exportName: 'GRAY_SCALE_DARK',
}),
},
];
const isCheckMode = process.argv.includes('--check');
const outputPaths = sourceOutputs.map(({ path }) => path);
const readIfPresent = (path: string) =>
existsSync(path) ? readFileSync(path, 'utf-8') : undefined;
const writeAndFormatOutputs = () => {
for (const { path, content } of sourceOutputs) {
writeFileSync(path, content, 'utf-8');
}
// oxfmt reformats CSS as well as TypeScript, so the committed artifacts are
// its output rather than the builders'.
const formatResult = spawnSync('npx', ['oxfmt', ...outputPaths], {
cwd: packageRoot,
stdio: 'inherit',
});
if (formatResult.status !== 0) {
throw new Error('oxfmt failed on the generated theme files');
}
};
// Formatting has to happen on the real paths, because oxfmt resolves the
// repository .oxfmtrc.jsonc from the file location, so check mode restores
// what was there rather than leaving the working tree edited.
const committedContents = isCheckMode ? outputPaths.map(readIfPresent) : [];
const restoreCommittedOutputs = () => {
for (const [index, path] of outputPaths.entries()) {
const committedContent = committedContents[index];
if (committedContent === undefined) {
rmSync(path, { force: true });
continue;
}
writeFileSync(path, committedContent, 'utf-8');
}
};
let staleOutputPaths: string[] = [];
if (isCheckMode) {
try {
writeAndFormatOutputs();
staleOutputPaths = outputPaths.filter(
(path, index) => readIfPresent(path) !== committedContents[index],
);
} finally {
restoreCommittedOutputs();
}
} else {
writeAndFormatOutputs();
}
if (staleOutputPaths.length > 0) {
process.stderr.write(
`::error::Generated theme artifacts are stale: ${staleOutputPaths
.map((path) => relative(packageRoot, path))
.join(', ')}. Run: npx nx generateTokens twenty-ui\n`,
);
process.exit(1);
}
console.log(`Generated theme artifacts from ${leaves.length} design tokens.`);