All files / packages/design-system-2/scripts/buildCss index.ts

91.17% Statements 31/34
68.75% Branches 11/16
100% Functions 3/3
91.17% Lines 31/34

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147                                                                            1x 1x 1x                                 1x 1x               1x 1x                         1x                         1x 1x 1x                 1x 8x 1x 1x 1x       7x 2x 2x 2x     5x 5x 1x 4x 1x 3x 1x   2x     1x 1x               1x                
/**
 * CLI boundary and dispatcher for the CSS build.
 *
 * runBuild reads the CLI flags and dispatches to the builders:
 *   - buildStandard.ts:   standard CSS, base structure plus themes
 *   - buildThemeLayer.ts: theme-layer CSS, base plus per-theme plus LBC
 *
 * Build modes:
 *   --dev-standard:    modular standard CSS for Storybook
 *   --dev-theme-layer: modular theme-layer CSS for Storybook
 *   --production:      every consumer-facing output to dist/css/
 *   --watch:           incremental rebuilds (see watcher.ts)
 *
 * runBuild is exported and runs nothing on import; the entry point at the bottom
 * runs only when this file is executed as a script, so tests import runBuild
 * without side effects.
 *
 * Usage. See package.json scripts for the canonical invocations:
 *   tsx scripts/buildCss/index.ts --system=slds2 --dev-standard        (dev standard CSS)
 *   tsx scripts/buildCss/index.ts --system=slds2 --dev-theme-layer     (dev theme-layer CSS)
 *   tsx scripts/buildCss/index.ts --system=slds2 --production          (all dist outputs)
 *   tsx scripts/buildCss/index.ts --system=slds2 --watch               (incremental watch)
 */
 
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { writeReleaseArtifactReceiptIfRequested } from '../../../design-system/scripts/scs/artifact-provenance.js';
import { args } from './args.ts';
import { designTokensDist } from './config.ts';
import { addFiles, buildDevStandard, buildProductionStandard } from './buildStandard.ts';
import { buildThemeLayerBase, buildThemeLayerDist } from './buildThemeLayer.ts';
import { buildThemeData } from './buildThemeData.ts';
import { buildPearl } from './buildPearl.ts';
import { startWatch } from './watcher.ts';
 
/** Parsed CLI flags. Passed explicitly so callers (and tests) drive the build. */
type Args = typeof args;
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const packageRoot = path.resolve(__dirname, '../..');
const packageJson = JSON.parse(fs.readFileSync(path.join(packageRoot, 'package.json'), 'utf8'));
 
// ─── Production build ────────────────────────────────────────────────────────
 
// Build the production CSS outputs.
//
// Consumer-facing outputs into dist/css/:
//   modular/slds2.base.css                                       Structure only
//   modular/slds2.scoped.base.css                                Scoped structure only
//   modular/slds2.theme.{theme}.css                              Theme tokens per theme
//   bundled/slds2.{theme}.css                                    Structure and theme, baked
//   bundled/slds2.scoped.{theme}.css                             Scoped structure and theme, baked
//
// Internal pipeline outputs into build/css/. The SCS release consumes these. They are not published:
//   theme-layer/modular/slds2.theme-layer.base.css               Theme-layer base building block
//   theme-layer/modular/slds2[.lbc].theme.{theme}.css            Theme-layer per-theme building blocks
//   theme-layer/bundled/slds2[.lbc].theme-layer.{theme}.css      Theme-layer monolith, baked
const buildProduction = async () => {
  await Promise.all([
    buildProductionStandard(),
    buildPearl({ production: true }),
    buildThemeLayerBase({ forDist: true }),
    buildThemeLayerBase({ forDist: true, lbc: true }),
    buildThemeLayerDist(),
    buildThemeLayerDist({ lbc: true }),
  ]);
  await buildThemeData();
  writeReleaseArtifactReceiptIfRequested({
    packageName: packageJson.name,
    packageVersion: packageJson.version,
    packageRoot,
    trustedRoot: path.resolve(packageRoot, '..'),
    receiptPath: path.join(packageRoot, '.scs-slds2-artifact-provenance.json'),
    files: [
      path.join(packageRoot, 'dist/css/bundled/slds2.cosmos.css'),
      path.join(packageRoot, 'dist/css/bundled/slds2.scoped.cosmos.css'),
      path.join(packageRoot, 'build/css/theme-layer/bundled/slds2.theme-layer.cosmos.css'),
    ],
  });
  // Pearl is released independently (pearl-only mode), so it gets its own receipt.
  writeReleaseArtifactReceiptIfRequested({
    packageName: packageJson.name,
    packageVersion: packageJson.version,
    packageRoot,
    trustedRoot: path.resolve(packageRoot, '..'),
    receiptPath: path.join(packageRoot, '.scs-pearl-artifact-provenance.json'),
    files: [path.join(packageRoot, 'build/pearl-theme/modular/slds2.pearl.core.css')],
  });
};
 
// ─── Dev builds ──────────────────────────────────────────────────────────────
 
// Dev build. Writes modular theme-layer CSS: base plus per-theme, including LBC variants.
const buildDevThemeLayer = async () => {
  await Promise.all([buildThemeLayerBase(), buildThemeLayerBase({ lbc: true })]);
  await buildThemeData();
};
 
// ─── Dispatch ────────────────────────────────────────────────────────────────
 
/**
 * Read the CLI flags and run the matching build.
 * @param args - Parsed CLI flags that select the build mode.
 */
export const runBuild = async (args: Args) => {
  if (!args['--system']) {
    console.error('Error: --system is required.');
    process.exitCode = 1;
    return;
  }
 
  // Ensure design-tokens/dist exists to prevent silently emiting CSS without token definitions.
  if (!fs.existsSync(designTokensDist)) {
    console.error(`Error: ${designTokensDist} not found. Build design-tokens first.`);
    process.exitCode = 1;
    return;
  }
 
  try {
    if (args['--production']) {
      await buildProduction();
    } else if (args['--dev-standard']) {
      await buildDevStandard();
    } else if (args['--dev-theme-layer']) {
      await buildDevThemeLayer();
    } else {
      await addFiles();
    }
  } catch (err) {
    console.error(err);
    process.exitCode = 1;
  }
};
 
// ─── Entry point ─────────────────────────────────────────────────────────────
 
// The tests load this file to reuse runBuild, and loading it should not start a
// build. Only build when this file is the one being run.
Iif (fileURLToPath(import.meta.url) === process.argv[1]) {
  await runBuild(args);
 
  // Skip the watcher if the build failed.
  if (args['--watch'] && !process.exitCode) {
    startWatch();
  }
}