Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .bundlewatch.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,19 @@
},
{
"path": "./dist/css/bootstrap-reboot.css",
"maxSize": "5.25 kB"
"maxSize": "5.5 kB"
},
{
"path": "./dist/css/bootstrap-reboot.min.css",
"maxSize": "4.5 kB"
},
{
"path": "./dist/css/bootstrap-utilities.css",
"maxSize": "14.0 kB"
"maxSize": "15.0 kB"
},
{
"path": "./dist/css/bootstrap-utilities.min.css",
"maxSize": "12.25 kB"
"maxSize": "13.25 kB"
},
{
"path": "./dist/css/bootstrap.css",
Expand Down
1 change: 1 addition & 0 deletions .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"favicons",
"fieldsets",
"flexbox",
"frontmatter",
"fullscreen",
"getbootstrap",
"Grayscale",
Expand Down
91 changes: 91 additions & 0 deletions build/generate-utilities-json.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#!/usr/bin/env node

/**
* Generate utilities metadata JSON from Sass
* This script compiles a special Sass file that outputs utility information as CSS comments,
* then extracts and saves it as JSON for documentation use.
*/

import { readFileSync, writeFileSync, unlinkSync } from 'node:fs'
import { execSync } from 'node:child_process'
import { fileURLToPath } from 'node:url'
import path from 'node:path'

const __dirname = path.dirname(fileURLToPath(import.meta.url))
const rootDir = path.join(__dirname, '..')

// Compile the metadata generator SCSS file
console.log('Compiling utilities metadata...')

try {
execSync(
'sass --style expanded --no-source-map build/generate-utilities-metadata.scss:dist/css/utilities-metadata.tmp.css',
{ cwd: rootDir, stdio: 'inherit' }
)
} catch {
console.error('Failed to compile metadata SCSS')
process.exit(1)
}

// Read the compiled CSS
const cssPath = path.join(rootDir, 'dist/css/utilities-metadata.tmp.css')
const cssContent = readFileSync(cssPath, 'utf8')

// Extract JSON from the CSS comment
const startMarker = 'BOOTSTRAP-UTILITIES-METADATA-START'
const endMarker = 'BOOTSTRAP-UTILITIES-METADATA-END'

const startIndex = cssContent.indexOf(startMarker)
const endIndex = cssContent.indexOf(endMarker)

if (startIndex === -1 || endIndex === -1) {
console.error('Could not find metadata markers in compiled CSS')
process.exit(1)
}

// Extract JSON content between markers
const jsonContent = cssContent
.slice(startIndex + startMarker.length, endIndex)
.trim()

// Validate JSON
try {
const parsed = JSON.parse(jsonContent)
console.log(`✓ Extracted metadata for ${Object.keys(parsed.utilities).length} utilities`)

// Write to JSON file
const outputPath = path.join(rootDir, 'dist/css/bootstrap-utilities.metadata.json')
writeFileSync(outputPath, JSON.stringify(parsed, null, 2))
console.log(`✓ Wrote metadata to ${outputPath}`)

// Clean up temporary CSS files (including RTL variants that may have been generated)
try {
unlinkSync(cssPath)
} catch {
// File may not exist
}

// Also clean up any RTL variants that postcss may have created
const rtlFiles = [
'dist/css/utilities-metadata.tmp.rtl.css',
'dist/css/utilities-metadata.tmp.rtl.css.map',
'dist/css/utilities-metadata.tmp.rtl.min.css',
'dist/css/utilities-metadata.tmp.rtl.min.css.map',
'dist/css/utilities-metadata.tmp.min.css',
'dist/css/utilities-metadata.tmp.min.css.map'
]

for (const file of rtlFiles) {
try {
unlinkSync(path.join(rootDir, file))
} catch {
// File may not exist, ignore
}
}

console.log('✓ Cleaned up temporary files')
} catch (error) {
console.error('Failed to parse extracted JSON:', error.message)
console.error('Extracted content:', jsonContent.slice(0, 500))
process.exit(1)
}
112 changes: 112 additions & 0 deletions build/generate-utilities-metadata.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// Generate utilities metadata JSON for documentation
// This file is compiled to extract utility information without generating CSS

@use "sass:map";
@use "sass:list";
@use "sass:string";
@use "sass:meta";
@use "../scss/config" as *;
@use "../scss/colors" as *;
@use "../scss/variables" as *;
@use "../scss/functions" as *;
@use "../scss/theme" as *;
@use "../scss/utilities" as *;

// Access the utilities map
$utilities-map: $utilities !default;

// Start JSON output
$json: '{"utilities":{' !default;

$utility-count: 0 !default;
$total-utilities: list.length(map.keys($utilities-map)) !default;

@each $key, $utility in $utilities-map {
$utility-count: $utility-count + 1;

// Skip if utility is null or false (disabled)
@if $utility {
// Extract class prefix
$class: if(map.has-key($utility, "class"), map.get($utility, "class"), $key);

// Extract property
$property: if(map.has-key($utility, "property"), map.get($utility, "property"), null);

// Extract values
$values: if(map.has-key($utility, "values"), map.get($utility, "values"), null);

// Generate class list
$classes: "";
@if $values {
@if meta.type-of($values) == "map" {
$value-keys: map.keys($values);
$first: true;
@each $value-key in $value-keys {
@if not $first {
$classes: $classes + ", ";
}
$class-name: if($value-key == "null" or $value-key == null, $class, "#{$class}-#{$value-key}");
$classes: $classes + '"' + $class-name + '"';
$first: false;
}
} @else if meta.type-of($values) == "list" {
$first: true;
@each $value in $values {
@if not $first {
$classes: $classes + ", ";
}
$class-name: "#{$class}-#{$value}";
$classes: $classes + '"' + $class-name + '"';
$first: false;
}
}
}

// Build JSON entry
$json: $json + '"' + $key + '":{"class":"' + $class + '"';

@if $property {
@if meta.type-of($property) == "string" {
$json: $json + ',"property":"' + $property + '"';
} @else if meta.type-of($property) == "list" {
$property-str: "";
$first: true;
@each $prop in $property {
@if not $first {
$property-str: $property-str + " ";
}
$property-str: $property-str + $prop;
$first: false;
}
$json: $json + ',"property":"' + $property-str + '"';
}
// Skip map properties as they're complex and don't translate to JSON well
}

@if $classes != "" {
$json: $json + ',"classes":[' + $classes + "]";
} @else {
$json: $json + ',"classes":[]';
}

$json: $json + "}";

@if $utility-count < $total-utilities {
$json: $json + ",";
}
}
}

// stylelint-disable-next-line scss/dollar-variable-default
$json: $json + "}}";

// Output as CSS comment so it appears in compiled file

/*! BOOTSTRAP-UTILITIES-METADATA-START
#{$json}
BOOTSTRAP-UTILITIES-METADATA-END */

// Prevent any actual CSS output
.bootstrap-utilities-metadata-generator {
content: "This file should not generate CSS, only metadata comments";
}
Loading