All files / packages/design-system-2/scripts/buildCss sort-imports.js

77.77% Statements 21/27
73.33% Branches 11/15
85.71% Functions 6/7
76% Lines 19/25

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                          1x                                     1x 24x   24x 7x     17x                                 1x 9x 16x   9x 10x 10x 10x         9x                   16x                     1x 2x 2x 2x    
/**
 * Import sorting and manifest generation for the CSS build.
 *
 * Sorts file lists by the canonical import order and optionally writes an
 * @import manifest file for PostCSS consumption.
 */
 
import path from 'node:path';
import fs from 'fs-extra';
import { args, THEME } from './args.js';
import { buildImportOrder } from './import-order.js';
 
/** Default import order, materialized once for the active CLI theme. */
export const importOrder = buildImportOrder(THEME);
 
/**
 * Returns the lookup key used against `importOrder` for a given CSS file.
 *
 * For most files the key is the basename minus its leading underscore and
 * extension (so `_reset.css` -> `reset`, `cosmos.global.tokens.css` ->
 * `cosmos.global.tokens`).
 *
 * Theme-layer files live at `<component>/themes/<theme>.css`. Keying by their
 * basename would collapse every component's `cosmos.css` (or `base.css`) to a
 * single key that is absent from `importOrder`, dropping all theme-layer files
 * to the end of the manifest where their order is decided by glob's alphabetic
 * enumeration. Instead, key these files by their component directory so they
 * inherit the canonical component order from `import-order.js`. This makes the
 * theme-layer build's source order match the legacy build's.
 *
 * @param {string} file Absolute file path.
 */
export const fileToOrderKey = (file) => {
  const segments = file.split(path.sep);
 
  if (segments.at(-2) === 'themes' && segments.length >= 3) {
    return segments.at(-3);
  }
 
  return segments
    .at(-1)
    .replace('_', '')
    .replace(/\.[^/.]+$/, '');
};
 
/**
 * Sorts `files` by import order and returns the @import manifest as a string.
 *
 * Files absent from the order sort to the end. Theme-layer files at
 * `<component>/themes/<theme>.css` are keyed by their component directory so
 * they participate in the canonical component order; see `fileToOrderKey`.
 *
 * @param {string[]} files Absolute paths to CSS files.
 * @param {{ theme?: string }} [options] Override the sort theme (defaults to CLI --theme).
 * @returns {string} The @import manifest content.
 */
export const sortImports = (files, { theme } = {}) => {
  const order = theme ? buildImportOrder(theme) : importOrder;
  const namedImports = files.map((file) => ({ name: fileToOrderKey(file), file }));
 
  namedImports.sort((a, b) => {
    const aOrder = order.indexOf(a.name);
    const bOrder = order.indexOf(b.name);
    return (
      (aOrder === -1 ? Number.MAX_SAFE_INTEGER : aOrder) - (bOrder === -1 ? Number.MAX_SAFE_INTEGER : bOrder)
    );
  });
 
  Iif (args['--debug-imports']) {
    console.info('\n── Import order ──');
    namedImports.forEach((ni, i) => {
      const orderIdx = order.indexOf(ni.name);
      const tag = orderIdx === -1 ? '(unordered)' : `[${orderIdx}]`;
      console.info(`  ${String(i).padStart(3)}  ${tag.padEnd(14)} ${ni.name.padEnd(30)} ${ni.file}`);
    });
    console.info('──────────────────\n');
  }
 
  return namedImports.map((ni) => `@import "${ni.file}";`).join('\n');
};
 
/**
 * Sorts `files` by `importOrder`, writes an @import manifest to `importsPath`,
 * and returns the manifest content.
 *
 * @param {string[]} files       Absolute paths to CSS files.
 * @param {string}   importsPath Absolute path for the generated manifest file.
 * @returns {string} The @import manifest content.
 */
export const sortAndWriteImports = (files, importsPath) => {
  const imports = sortImports(files);
  fs.outputFileSync(importsPath, imports);
  return imports;
};