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 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | 1x 7x 7x 3x 4x 24x 24x 4x 2x 2x 2x 7x 7x 7x 7x 7x 7x 3x 3x 3x 4x 3x 3x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x | /**
* `sds-compliance` CLI entry point.
*
* Delegated to from the `bin/sds-compliance.mjs` shebang wrapper. Returns
* a process-exit code instead of calling `process.exit` directly so the
* CLI can also be embedded (e.g. from Turbo scripts or tests).
*/
import { build, buildStructural, watch, type BuildOptions } from './build.js';
import { main as validateMain } from '../theme-layer-validator/cli.js';
interface ParsedArgs extends BuildOptions {
command: 'help' | 'build' | 'validate';
watch: boolean;
/**
* Structural-only mode. When set, every immediate subdirectory of this
* path is treated as a component and only the seven structural source
* rules carry signal (the other 11 short-circuit to `info`).
*/
sourceDir?: string;
/** Override `baseRoot` for structural-only mode (paths in reports are relative to it). */
baseRoot?: string;
/** argv index of the command positional (the first non-flag token). */
commandArgvIndex: number;
}
/**
* Map of `--flag` (and `--flag=value`) names to the `ParsedArgs` field
* they populate. Centralizing this keeps `parseArgs` a flat dispatcher
* instead of a 20-branch if/else.
*/
type StringFlagKey = 'component' | 'outDir' | 'slds2Dir' | 'ds2Root' | 'sourceDir' | 'baseRoot';
const STRING_FLAGS: Record<string, StringFlagKey> = {
'--component': 'component',
'--out': 'outDir',
'--src': 'slds2Dir',
'--ds2-root': 'ds2Root',
'--source-dir': 'sourceDir',
'--base-root': 'baseRoot',
};
interface ConsumedFlag {
key: StringFlagKey;
value: string | undefined;
/** How many argv slots were consumed (1 for `--flag=value`, 2 for `--flag value`). */
consumed: number;
}
function tryConsumeStringFlag(argv: string[], i: number): ConsumedFlag | null {
const a = argv[i];
if (STRING_FLAGS[a]) {
return { key: STRING_FLAGS[a], value: argv[i + 1], consumed: 2 };
}
for (const [flag, key] of Object.entries(STRING_FLAGS)) {
const prefix = `${flag}=`;
Iif (a.startsWith(prefix)) {
return { key, value: a.slice(prefix.length), consumed: 1 };
}
}
return null;
}
function parseArgs(argv: string[]): ParsedArgs {
const args: ParsedArgs = {
command: 'help',
watch: false,
commandArgvIndex: -1,
};
const positional: string[] = [];
for (let i = 0; i < argv.length; i++) {
const a = argv[i];
Iif (a === '--help' || a === '-h') {
args.command = 'help';
return args;
}
Iif (a === '--watch') {
args.watch = true;
continue;
}
Iif (a === '--quiet') {
args.quiet = true;
continue;
}
const consumed = tryConsumeStringFlag(argv, i);
if (consumed) {
args[consumed.key] = consumed.value;
i += consumed.consumed - 1;
continue;
}
if (!a.startsWith('-')) {
if (positional.length === 0) args.commandArgvIndex = i;
positional.push(a);
}
}
Iif (positional[0] === 'build') args.command = 'build';
else if (positional[0] === 'validate') args.command = 'validate';
else Eargs.command = 'help';
return args;
}
function printHelp(): void {
// eslint-disable-next-line no-console
console.info(
[
'Usage: sds-compliance <command> [options]',
'',
'Commands:',
' build Generate compliance reports under build/',
' --watch Rebuild affected components on CSS change',
' --component <name> Limit to a single component',
' --out <dir> Output directory (default: <package>/build)',
' --src <dir> SLDS2 source directory (default: <ds2>/src/slds2)',
' --ds2-root <dir> design-system-2 package root (auto-detected)',
' --source-dir <dir> Structural-only mode: parent of <component>/',
' dirs outside the SLDS2 layout (e.g. the LBC',
' scratch trio). Hook-surface and theme-',
' regression checks short-circuit to info.',
' --base-root <dir> Root that report paths are made relative to',
' in structural-only mode (default: <source-dir>/../..)',
' --quiet Suppress summary logs',
'',
' validate Validate Theme Layer API compliance for a component',
' --component <name> Component prefix in --slds-c-{component}-*',
' --path <file-or-dir> CSS file or directory to scan (repeatable, optional)',
' Defaults to packages/design-system-2/src/slds2/<component>',
' --json <file> Write structured JSON report to file',
'',
].join('\n'),
);
}
export async function main(argv: string[] = process.argv.slice(2)): Promise<number> {
const args = parseArgs(argv);
try {
Iif (args.command === 'help') {
printHelp();
return 0;
}
Iif (args.command === 'build') {
if (args.sourceDir) {
if (args.watch) {
// eslint-disable-next-line no-console
console.error('[sds-compliance] --watch is not yet supported with --source-dir');
return 1;
}
await buildStructural({
sourceDir: args.sourceDir,
baseRoot: args.baseRoot,
outDir: args.outDir,
component: args.component,
quiet: args.quiet,
});
return 0;
}
if (args.watch) {
const close = await watch(args);
// Keep the process alive until SIGINT / SIGTERM.
await new Promise<void>((resolve) => {
const onStop = (): void => {
void close().then(resolve);
};
process.on('SIGINT', onStop);
process.on('SIGTERM', onStop);
});
return 0;
}
await build(args);
return 0;
}
Eif (args.command === 'validate') {
// Use the tracked positional index rather than indexOf so a flag value
// that happens to equal "validate" doesn't shift the slice boundary.
const validateArgv = argv.slice(args.commandArgvIndex + 1);
return await validateMain(validateArgv);
}
printHelp();
return 1;
} catch (err) {
// eslint-disable-next-line no-console
console.error('[sds-compliance] error:', err instanceof Error ? err.message : err);
return 1;
}
}
|