Reorder theme files + sync script rebuilds custom vars in standard order
Standard themes reorganized: Foundation → Layout → Navigation → Forms → Components (alphabetical) → Bootstrap → Custom → Extensions Sync script now fully rebuilds the custom file's :root block in starter file order, preserving user values while reordering. Missing variables are inserted at their correct position. Bump 03.10.04 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -97,28 +97,24 @@ final class MokoCssVarSync
|
||||
private static function syncFile(string $starterPath, string $userPath): array
|
||||
{
|
||||
$starterVars = self::extractVarsWithContext($starterPath);
|
||||
$userVars = self::extractVarNames($userPath);
|
||||
$userVarsMap = self::extractVarsWithContext($userPath);
|
||||
$userNames = self::extractVarNames($userPath);
|
||||
|
||||
// Find missing variables
|
||||
$missing = [];
|
||||
foreach ($starterVars as $name => $declaration) {
|
||||
if (!isset($userVars[$name])) {
|
||||
if (!isset($userNames[$name])) {
|
||||
$missing[$name] = $declaration;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($missing)) {
|
||||
return ['added' => [], 'skipped' => []];
|
||||
}
|
||||
// Rebuild the entire :root block in starter file order.
|
||||
// User's custom values are preserved; missing vars get starter defaults.
|
||||
$reordered = self::rebuildInStarterOrder($starterPath, $userVarsMap, $missing);
|
||||
|
||||
// Group missing variables by their section comment header.
|
||||
$sections = self::groupBySection($missing, $starterPath);
|
||||
|
||||
// Build the injection block.
|
||||
$injection = self::buildInjectionBlock($sections);
|
||||
|
||||
// Insert before the closing } of the :root rule.
|
||||
// Replace the :root block in the user file with the reordered version.
|
||||
$userCss = file_get_contents($userPath);
|
||||
$userCss = self::injectBeforeRootClose($userCss, $injection);
|
||||
$userCss = self::replaceRootBlock($userCss, $reordered);
|
||||
|
||||
// Write back (atomic: write to .tmp then rename).
|
||||
$tmpPath = $userPath . '.tmp';
|
||||
@@ -128,6 +124,104 @@ final class MokoCssVarSync
|
||||
return ['added' => array_keys($missing), 'skipped' => []];
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuild all variables in the order they appear in the starter file.
|
||||
* User values are preserved; missing vars use starter defaults.
|
||||
*
|
||||
* @param string $starterPath Path to starter file.
|
||||
* @param array $userVars User's variable name => declaration.
|
||||
* @param array $missing Missing variable name => starter declaration.
|
||||
* @return string Complete CSS content for inside :root { }.
|
||||
*/
|
||||
private static function rebuildInStarterOrder(string $starterPath, array $userVars, array $missing): string
|
||||
{
|
||||
$lines = file($starterPath, FILE_IGNORE_NEW_LINES);
|
||||
$output = [];
|
||||
$inRoot = false;
|
||||
$depth = 0;
|
||||
|
||||
foreach ($lines as $line) {
|
||||
// Track when we enter :root
|
||||
if (!$inRoot && preg_match('/:root/', $line)) {
|
||||
$inRoot = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$inRoot) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Track braces
|
||||
if (strpos($line, '{') !== false) {
|
||||
$depth++;
|
||||
continue;
|
||||
}
|
||||
if (strpos($line, '}') !== false) {
|
||||
$depth--;
|
||||
if ($depth < 0) {
|
||||
break; // End of :root
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Section comment headers — always include
|
||||
if (preg_match('/\/\*\s*=+\s*.+?\s*=+\s*\*\//', $line)) {
|
||||
$output[] = $line;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Regular comments — include
|
||||
if (preg_match('/^\s*\/\*/', $line) || preg_match('/^\s*\*/', $line)) {
|
||||
$output[] = $line;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Blank lines — include
|
||||
if (trim($line) === '') {
|
||||
$output[] = '';
|
||||
continue;
|
||||
}
|
||||
|
||||
// Variable declaration
|
||||
if (preg_match('/^\s*(--[\w-]+)\s*:/', $line, $m)) {
|
||||
$name = trim($m[1]);
|
||||
if (isset($userVars[$name])) {
|
||||
// Use the user's custom value
|
||||
$output[] = $userVars[$name];
|
||||
} elseif (isset($missing[$name])) {
|
||||
// New variable — use starter default
|
||||
$output[] = $missing[$name];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Other lines (e.g. color-scheme) — include as-is
|
||||
$output[] = $line;
|
||||
}
|
||||
|
||||
return implode("\n", $output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the content inside :root { ... } with new content.
|
||||
*/
|
||||
private static function replaceRootBlock(string $css, string $newContent): string
|
||||
{
|
||||
$rootStart = preg_match('/:root[^{]*\{/', $css, $m, PREG_OFFSET_CAPTURE);
|
||||
if (!$rootStart) {
|
||||
return $css;
|
||||
}
|
||||
|
||||
$openBrace = $m[0][1] + strlen($m[0][0]);
|
||||
$closeBrace = self::findRootClosingBrace($css);
|
||||
|
||||
if ($closeBrace === false) {
|
||||
return $css;
|
||||
}
|
||||
|
||||
return substr($css, 0, $openBrace) . "\n" . $newContent . "\n" . substr($css, $closeBrace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract CSS custom property declarations with their full text (name: value).
|
||||
* Only extracts from the first :root block.
|
||||
|
||||
Reference in New Issue
Block a user