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
155 lines
5.2 KiB
TypeScript
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.`);
|