From 7de056155bc8c60e6c8198c4b4086b0d4d7f295d Mon Sep 17 00:00:00 2001 From: David Rios Date: Mon, 20 Jul 2026 14:17:27 -0400 Subject: [PATCH] feat(apollo-wind): expose --apollo-wind-version CSS variable Adds a `--apollo-wind-version` CSS custom property to the Wind stylesheet so consumers can detect at runtime both that apollo-wind's styles are loaded and which version is present. Motivated by traceview-ui#413, where a dedicated, non-generic token is needed to reliably probe for the sheet (and enable version-matching) instead of reusing a common token. The value is generated from package.json by scripts/generate-version-css.mjs as the first step of `build`, so package.json remains the single source of truth and the published token can never drift from the released version. The generated file is committed as well because the source CSS is imported directly by Storybook and apollo-react's canvas styles, which don't run the build script. A vitest guard fails if the committed value drifts from package.json. Co-Authored-By: Claude Opus 4.8 (1M context) --- packages/apollo-wind/package.json | 2 +- packages/apollo-wind/rslib.config.ts | 3 +- .../scripts/generate-version-css.mjs | 50 +++++++++++++++++++ .../src/styles/tailwind.consumer.css | 3 ++ .../src/styles/version.generated.css | 8 +++ .../apollo-wind/src/styles/version.test.ts | 21 ++++++++ 6 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 packages/apollo-wind/scripts/generate-version-css.mjs create mode 100644 packages/apollo-wind/src/styles/version.generated.css create mode 100644 packages/apollo-wind/src/styles/version.test.ts diff --git a/packages/apollo-wind/package.json b/packages/apollo-wind/package.json index b578d810b..50d583c69 100644 --- a/packages/apollo-wind/package.json +++ b/packages/apollo-wind/package.json @@ -62,7 +62,7 @@ ], "scripts": { "dev": "rslib build --watch", - "build": "rslib build && npm run build:css", + "build": "node scripts/generate-version-css.mjs && rslib build && npm run build:css", "build:css": "npx @tailwindcss/cli -i ./src/styles/tailwind.css -o ./dist/styles.css", "test": "vitest run", "test:ui": "vitest --ui", diff --git a/packages/apollo-wind/rslib.config.ts b/packages/apollo-wind/rslib.config.ts index 135cb72c6..8c5cdf301 100644 --- a/packages/apollo-wind/rslib.config.ts +++ b/packages/apollo-wind/rslib.config.ts @@ -63,8 +63,9 @@ export default defineConfig({ copy: [ // Copy tailwind.consumer.css to dist/tailwind.css { from: './src/styles/tailwind.consumer.css', to: './tailwind.css' }, - // tailwind.consumer.css imports this via relative path + // tailwind.consumer.css imports these via relative path { from: './src/styles/tailwind.utilities.css', to: './tailwind.utilities.css' }, + { from: './src/styles/version.generated.css', to: './version.generated.css' }, ], }, tools: { diff --git a/packages/apollo-wind/scripts/generate-version-css.mjs b/packages/apollo-wind/scripts/generate-version-css.mjs new file mode 100644 index 000000000..868722361 --- /dev/null +++ b/packages/apollo-wind/scripts/generate-version-css.mjs @@ -0,0 +1,50 @@ +// Generates src/styles/version.generated.css from this package's version. +// +// Exposes the current @uipath/apollo-wind version as a CSS custom property +// (`--apollo-wind-version`) so consumers can detect at runtime both that the +// Wind stylesheet is present and which version is loaded (e.g. traceview +// checks this before self-injecting its utility sheet in light-DOM hosts). +// +// package.json is the single source of truth. This runs as the first step of +// `build`, so the published value always matches the released version. The +// generated file is committed too, because the source CSS is imported directly +// by Storybook and apollo-react's canvas styles, which never run this script. + +import { readFileSync, writeFileSync } from 'node:fs'; +import { dirname, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const scriptDir = dirname(fileURLToPath(import.meta.url)); +const packageRoot = resolve(scriptDir, '..'); +const packageJsonPath = resolve(packageRoot, 'package.json'); +const outputPath = resolve(packageRoot, 'src/styles/version.generated.css'); + +/** Render the version stylesheet body for a given version string. */ +export function renderVersionCss(version) { + return `/** + * AUTO-GENERATED by scripts/generate-version-css.mjs — do not edit by hand. + * Regenerated from package.json on every build. See the script for rationale. + */ + +:root { + --apollo-wind-version: "${version}"; +} +`; +} + +/** Read package.json, write version.generated.css, and return the version. */ +export function generateVersionCss() { + const { version } = JSON.parse(readFileSync(packageJsonPath, 'utf8')); + if (!version) { + throw new Error(`No "version" field found in ${packageJsonPath}`); + } + writeFileSync(outputPath, renderVersionCss(version)); + return version; +} + +// Run when invoked directly (e.g. `node scripts/generate-version-css.mjs`), +// but not when imported by a test. +if (import.meta.url === `file://${process.argv[1]}`) { + const version = generateVersionCss(); + console.log(`Wrote --apollo-wind-version: "${version}" to src/styles/version.generated.css`); +} diff --git a/packages/apollo-wind/src/styles/tailwind.consumer.css b/packages/apollo-wind/src/styles/tailwind.consumer.css index 6efd04b0d..e5cfc3eab 100644 --- a/packages/apollo-wind/src/styles/tailwind.consumer.css +++ b/packages/apollo-wind/src/styles/tailwind.consumer.css @@ -15,6 +15,9 @@ @import "./tailwind.utilities.css"; +/* Exposes --apollo-wind-version (auto-generated from package.json on build). */ +@import "./version.generated.css"; + :root { --radius: 0.75rem; /* 12px — base border-radius for buttons and UI elements */ diff --git a/packages/apollo-wind/src/styles/version.generated.css b/packages/apollo-wind/src/styles/version.generated.css new file mode 100644 index 000000000..f7a71b8c3 --- /dev/null +++ b/packages/apollo-wind/src/styles/version.generated.css @@ -0,0 +1,8 @@ +/** + * AUTO-GENERATED by scripts/generate-version-css.mjs — do not edit by hand. + * Regenerated from package.json on every build. See the script for rationale. + */ + +:root { + --apollo-wind-version: "2.29.0"; +} diff --git a/packages/apollo-wind/src/styles/version.test.ts b/packages/apollo-wind/src/styles/version.test.ts new file mode 100644 index 000000000..8b34155d8 --- /dev/null +++ b/packages/apollo-wind/src/styles/version.test.ts @@ -0,0 +1,21 @@ +import { readFileSync } from 'node:fs'; +import { resolve } from 'node:path'; +import { describe, expect, it } from 'vitest'; + +import { renderVersionCss } from '../../scripts/generate-version-css.mjs'; + +const packageRoot = resolve(__dirname, '../..'); +const version = JSON.parse(readFileSync(resolve(packageRoot, 'package.json'), 'utf8')) + .version as string; +const generatedCss = readFileSync(resolve(packageRoot, 'src/styles/version.generated.css'), 'utf8'); + +describe('--apollo-wind-version', () => { + it('exposes the token in the generated stylesheet', () => { + expect(generatedCss).toContain('--apollo-wind-version:'); + }); + + it('matches the current package.json version (regenerate via `pnpm build` if this fails)', () => { + expect(generatedCss).toBe(renderVersionCss(version)); + expect(generatedCss).toContain(`--apollo-wind-version: "${version}";`); + }); +});