Apply theme to 5.3.8

This commit is contained in:
ThePetrovich 2026-08-17 22:24:19 +08:00
parent f203f0c103
commit be164cfe0a
930 changed files with 53160 additions and 42297 deletions

View file

@ -1,15 +0,0 @@
{
"env": {
"browser": false,
"node": true
},
"parserOptions": {
"sourceType": "script"
},
"extends": "../.eslintrc.json",
"rules": {
"no-console": "off",
"strict": "error",
"unicorn/prefer-top-level-await": "off"
}
}

View file

@ -1,6 +1,12 @@
'use strict'
import fs from 'node:fs/promises'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const pkgJson = path.join(__dirname, '../package.json')
const pkg = JSON.parse(await fs.readFile(pkgJson, 'utf8'))
const pkg = require('../package.json')
const year = new Date().getFullYear()
function getBanner(pluginFilename) {
@ -11,4 +17,4 @@ function getBanner(pluginFilename) {
*/`
}
module.exports = getBanner
export default getBanner

View file

@ -2,21 +2,22 @@
/*!
* Script to build our plugins to use them separately.
* Copyright 2020-2022 The Bootstrap Authors
* Copyright 2020-2022 Twitter, Inc.
* Copyright 2020-2025 The Bootstrap Authors
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
'use strict'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import { babel } from '@rollup/plugin-babel'
import { globby } from 'globby'
import { rollup } from 'rollup'
import banner from './banner.mjs'
const path = require('node:path')
const rollup = require('rollup')
const globby = require('globby')
const { babel } = require('@rollup/plugin-babel')
const banner = require('./banner.js')
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const sourcePath = path.resolve(__dirname, '../js/src/').replace(/\\/g, '/')
const jsFiles = globby.sync(sourcePath + '/**/*.js')
const jsFiles = await globby(`${sourcePath}/**/*.js`)
// Array which holds the resolved plugins
const resolvedPlugins = []
@ -27,7 +28,7 @@ const filenameToEntity = filename => filename.replace('.js', '')
for (const file of jsFiles) {
resolvedPlugins.push({
src: file.replace('.js', ''),
src: file,
dist: file.replace('src', 'dist'),
fileName: path.basename(file),
className: filenameToEntity(path.basename(file))
@ -36,9 +37,12 @@ for (const file of jsFiles) {
}
const build = async plugin => {
/**
* @type {import('rollup').GlobalsOption}
*/
const globals = {}
const bundle = await rollup.rollup({
const bundle = await rollup({
input: plugin.src,
plugins: [
babel({

View file

@ -1,81 +0,0 @@
#!/usr/bin/env node
/*!
* Script to update version number references in the project.
* Copyright 2017-2022 The Bootstrap Authors
* Copyright 2017-2022 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
'use strict'
const fs = require('node:fs').promises
const path = require('node:path')
const globby = require('globby')
const VERBOSE = process.argv.includes('--verbose')
const DRY_RUN = process.argv.includes('--dry') || process.argv.includes('--dry-run')
// These are the filetypes we only care about replacing the version
const GLOB = [
'**/*.{css,html,js,json,md,scss,txt,yml}'
]
const GLOBBY_OPTIONS = {
cwd: path.join(__dirname, '..'),
gitignore: true
}
// Blame TC39... https://github.com/benjamingr/RegExp.escape/issues/37
function regExpQuote(string) {
return string.replace(/[$()*+-.?[\\\]^{|}]/g, '\\$&')
}
function regExpQuoteReplacement(string) {
return string.replace(/\$/g, '$$')
}
async function replaceRecursively(file, oldVersion, newVersion) {
const originalString = await fs.readFile(file, 'utf8')
const newString = originalString.replace(
new RegExp(regExpQuote(oldVersion), 'g'), regExpQuoteReplacement(newVersion)
)
// No need to move any further if the strings are identical
if (originalString === newString) {
return
}
if (VERBOSE) {
console.log(`FILE: ${file}`)
}
if (DRY_RUN) {
return
}
await fs.writeFile(file, newString, 'utf8')
}
async function main(args) {
let [oldVersion, newVersion] = args
if (!oldVersion || !newVersion) {
console.error('USAGE: change-version old_version new_version [--verbose] [--dry[-run]]')
console.error('Got arguments:', args)
process.exit(1)
}
// Strip any leading `v` from arguments because otherwise we will end up with duplicate `v`s
[oldVersion, newVersion] = [oldVersion, newVersion].map(arg => arg.startsWith('v') ? arg.slice(1) : arg)
try {
const files = await globby(GLOB, GLOBBY_OPTIONS)
await Promise.all(files.map(file => replaceRecursively(file, oldVersion, newVersion)))
} catch (error) {
console.error(error)
process.exit(1)
}
}
main(process.argv.slice(2))

113
build/change-version.mjs Normal file
View file

@ -0,0 +1,113 @@
#!/usr/bin/env node
/*!
* Script to update version number references in the project.
* Copyright 2017-2025 The Bootstrap Authors
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
import { execFile } from 'node:child_process'
import fs from 'node:fs/promises'
import process from 'node:process'
const VERBOSE = process.argv.includes('--verbose')
const DRY_RUN = process.argv.includes('--dry') || process.argv.includes('--dry-run')
// These are the files we only care about replacing the version
const FILES = [
'README.md',
'config.yml',
'js/src/base-component.js',
'package.js',
'scss/mixins/_banner.scss',
'site/data/docs-versions.yml'
]
// Blame TC39... https://github.com/benjamingr/RegExp.escape/issues/37
function regExpQuote(string) {
return string.replace(/[$()*+-.?[\\\]^{|}]/g, '\\$&')
}
function regExpQuoteReplacement(string) {
return string.replace(/\$/g, '$$')
}
async function replaceRecursively(file, oldVersion, newVersion) {
const originalString = await fs.readFile(file, 'utf8')
const newString = originalString
.replace(
new RegExp(regExpQuote(oldVersion), 'g'),
regExpQuoteReplacement(newVersion)
)
// Also replace the version used by the rubygem,
// which is using periods (`.`) instead of hyphens (`-`)
.replace(
new RegExp(regExpQuote(oldVersion.replace(/-/g, '.')), 'g'),
regExpQuoteReplacement(newVersion.replace(/-/g, '.'))
)
// No need to move any further if the strings are identical
if (originalString === newString) {
return
}
if (VERBOSE) {
console.log(`Found ${oldVersion} in ${file}`)
}
if (DRY_RUN) {
return
}
await fs.writeFile(file, newString, 'utf8')
}
function bumpNpmVersion(newVersion) {
if (DRY_RUN) {
return
}
execFile('npm', ['version', newVersion, '--no-git-tag'], { shell: true }, error => {
if (error) {
console.error(error)
process.exit(1)
}
})
}
function showUsage(args) {
console.error('USAGE: change-version old_version new_version [--verbose] [--dry[-run]]')
console.error('Got arguments:', args)
process.exit(1)
}
async function main(args) {
let [oldVersion, newVersion] = args
if (!oldVersion || !newVersion) {
showUsage(args)
}
// Strip any leading `v` from arguments because
// otherwise we will end up with duplicate `v`s
[oldVersion, newVersion] = [oldVersion, newVersion].map(arg => {
return arg.startsWith('v') ? arg.slice(1) : arg
})
if (oldVersion === newVersion) {
showUsage(args)
}
bumpNpmVersion(newVersion)
try {
await Promise.all(
FILES.map(file => replaceRecursively(file, oldVersion, newVersion))
)
} catch (error) {
console.error(error)
process.exit(1)
}
}
main(process.argv.slice(2))

157
build/docs-prep.sh Executable file
View file

@ -0,0 +1,157 @@
#!/bin/bash
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Default branch suffix
BRANCH_SUFFIX="release"
# Check if a custom version parameter was provided
if [ $# -eq 1 ]; then
BRANCH_SUFFIX="$1"
fi
# Branch name to create
NEW_BRANCH="gh-pages-${BRANCH_SUFFIX}"
# Get the current docs version from config
DOCS_VERSION=$(node -p "require('js-yaml').load(require('fs').readFileSync('config.yml', 'utf8')).docs_version")
# Function to print colored messages
print_success() {
echo -e "${GREEN}$1${NC}"
}
print_error() {
echo -e "${RED}$1${NC}"
exit 1
}
print_info() {
echo -e "${BLUE} $1${NC}"
}
print_warning() {
echo -e "${YELLOW}$1${NC}"
}
# Function to execute command with error handling
execute() {
print_info "Running: $1"
eval $1
if [ $? -ne 0 ]; then
print_error "Failed to execute: $1"
else
print_success "Successfully executed: $1"
fi
}
# Check if /tmp/_site directory exists from a previous run
if [ -d "/tmp/_site" ]; then
print_warning "Found existing /tmp/_site directory. Removing it…"
rm -rf /tmp/_site
fi
# Main process
print_info "Starting documentation deployment process…"
# Step 1: Build documentation
print_info "Building documentation with npm run docs…"
npm run docs
if [ $? -ne 0 ]; then
print_error "Documentation build failed!"
fi
print_success "Documentation built successfully"
# Step 2: Move _site to /tmp/
print_info "Moving _site to temporary location…"
execute "mv _site /tmp/"
# Step 3: Switch to gh-pages branch
print_info "Checking out gh-pages branch…"
git checkout gh-pages
if [ $? -ne 0 ]; then
print_error "Failed to checkout gh-pages branch. Make sure it exists."
fi
print_success "Switched to gh-pages branch"
git reset --hard origin/gh-pages
if [ $? -ne 0 ]; then
print_error "Failed to reset to origin/gh-pages. Check your git configuration."
fi
print_success "Reset to origin/gh-pages"
git pull origin gh-pages
if [ $? -ne 0 ]; then
print_error "Failed to pull from origin/gh-pages. Check your network connection and git configuration."
fi
print_success "Pulled latest changes from origin/gh-pages"
# Step 4: Create a new branch for the update
print_info "Checking if branch ${NEW_BRANCH} exists and deleting it if it does…"
if git show-ref --verify --quiet refs/heads/${NEW_BRANCH}; then
execute "git branch -D ${NEW_BRANCH}"
else
print_info "Branch ${NEW_BRANCH} does not exist, proceeding with creation…"
fi
print_info "Creating new branch ${NEW_BRANCH}"
execute "git checkout -b ${NEW_BRANCH}"
# Step 5: Move all root-level files from Astro build
find /tmp/_site -maxdepth 1 -type f -exec mv {} . \;
# Step 6: Move all top-level directories except 'docs' (which needs special handling)
find /tmp/_site -maxdepth 1 -type d ! -name "_site" ! -name "docs" -exec sh -c 'dir=$(basename "$1"); rm -rf "$dir"; mv "$1" .' _ {} \;
# Step 7: Handle docs directory specially
if [ -d "/tmp/_site/docs" ]; then
# Replace only the current version's docs
if [ -d "docs/$DOCS_VERSION" ]; then
rm -rf "docs/$DOCS_VERSION"
fi
mv "/tmp/_site/docs/$DOCS_VERSION" "docs/"
# Handle docs root files
find /tmp/_site/docs -maxdepth 1 -type f -exec mv {} docs/ \;
# Handle special docs directories (getting-started, versions)
for special_dir in getting-started versions; do
if [ -d "/tmp/_site/docs/$special_dir" ]; then
rm -rf "docs/$special_dir"
mv "/tmp/_site/docs/$special_dir" "docs/"
fi
done
fi
# Clean up remaining files in /tmp/_site if any
if [ -d "/tmp/_site" ]; then
remaining_files=$(find /tmp/_site -type f | wc -l)
remaining_dirs=$(find /tmp/_site -type d | wc -l)
if [ $remaining_files -gt 0 ] || [ $remaining_dirs -gt 1 ]; then
print_warning "There are still some files or directories in /tmp/_site that weren't moved."
print_warning "You may want to inspect /tmp/_site to see if anything important was missed."
else
print_info "Cleaning up temporary directory…"
rm -rf /tmp/_site
print_success "Temporary directory cleaned up"
fi
fi
# Step 10: Remove empty site directory if it exists
if [ -d "site" ]; then
print_info "Removing empty site directory…"
execute "rm -rf site"
fi
print_success "Docs prep complete!"
print_info "Review changes before committing and pushing."
print_info "Next steps:"
print_info " 1. Run a local server to review changes"
print_info " 2. Check browser and web inspector for any errors"
print_info " 3. git add ."
print_info " 4. git commit -m \"Update documentation\""
print_info " 5. git push origin ${NEW_BRANCH}"

View file

@ -5,17 +5,17 @@
* Remember to use the same vendor files as the CDN ones,
* otherwise the hashes won't match!
*
* Copyright 2017-2022 The Bootstrap Authors
* Copyright 2017-2022 Twitter, Inc.
* Copyright 2017-2025 The Bootstrap Authors
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
'use strict'
import crypto from 'node:crypto'
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import sh from 'shelljs'
const crypto = require('node:crypto')
const fs = require('node:fs')
const path = require('node:path')
const sh = require('shelljs')
const __dirname = path.dirname(fileURLToPath(import.meta.url))
sh.config.fatal = true
@ -47,18 +47,18 @@ const files = [
}
]
for (const file of files) {
fs.readFile(file.file, 'utf8', (error, data) => {
for (const { file, configPropertyName } of files) {
fs.readFile(file, 'utf8', (error, data) => {
if (error) {
throw error
}
const algo = 'sha384'
const hash = crypto.createHash(algo).update(data, 'utf8').digest('base64')
const integrity = `${algo}-${hash}`
const algorithm = 'sha384'
const hash = crypto.createHash(algorithm).update(data, 'utf8').digest('base64')
const integrity = `${algorithm}-${hash}`
console.log(`${file.configPropertyName}: ${integrity}`)
console.log(`${configPropertyName}: ${integrity}`)
sh.sed('-i', new RegExp(`^(\\s+${file.configPropertyName}:\\s+["'])\\S*(["'])`), `$1${integrity}$2`, configFile)
sh.sed('-i', new RegExp(`^(\\s+${configPropertyName}:\\s+["'])\\S*(["'])`), `$1${integrity}$2`, configFile)
})
}

View file

@ -1,12 +1,10 @@
'use strict'
const mapConfig = {
inline: false,
annotation: true,
sourcesContent: true
}
module.exports = context => {
export default context => {
return {
map: context.file.dirname.includes('examples') ? false : mapConfig,
plugins: {

View file

@ -1,15 +1,17 @@
'use strict'
import path from 'node:path'
import process from 'node:process'
import { fileURLToPath } from 'node:url'
import { babel } from '@rollup/plugin-babel'
import { nodeResolve } from '@rollup/plugin-node-resolve'
import replace from '@rollup/plugin-replace'
import banner from './banner.mjs'
const path = require('node:path')
const { babel } = require('@rollup/plugin-babel')
const { nodeResolve } = require('@rollup/plugin-node-resolve')
const replace = require('@rollup/plugin-replace')
const banner = require('./banner.js')
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const BUNDLE = process.env.BUNDLE === 'true'
const ESM = process.env.ESM === 'true'
let fileDestination = `bootstrap${ESM ? '.esm' : ''}`
let destinationFile = `bootstrap${ESM ? '.esm' : ''}`
const external = ['@popperjs/core']
const plugins = [
babel({
@ -24,7 +26,7 @@ const globals = {
}
if (BUNDLE) {
fileDestination += '.bundle'
destinationFile += '.bundle'
// Remove last entry in external array to bundle Popper
external.pop()
delete globals['@popperjs/core']
@ -40,8 +42,8 @@ if (BUNDLE) {
const rollupConfig = {
input: path.resolve(__dirname, `../js/index.${ESM ? 'esm' : 'umd'}.js`),
output: {
banner,
file: path.resolve(__dirname, `../dist/js/${fileDestination}.js`),
banner: banner(),
file: path.resolve(__dirname, `../dist/js/${destinationFile}.js`),
format: ESM ? 'esm' : 'umd',
globals,
generatedCode: 'es2015'
@ -54,4 +56,4 @@ if (!ESM) {
rollupConfig.output.name = 'bootstrap'
}
module.exports = rollupConfig
export default rollupConfig

View file

@ -2,22 +2,22 @@
/*!
* Script to run vnu-jar if Java is available.
* Copyright 2017-2022 The Bootstrap Authors
* Copyright 2017-2022 Twitter, Inc.
* Copyright 2017-2025 The Bootstrap Authors
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
'use strict'
const { execFile, spawn } = require('node:child_process')
const vnu = require('vnu-jar')
import { execFile, spawn } from 'node:child_process'
import vnu from 'vnu-jar'
execFile('java', ['-version'], (error, stdout, stderr) => {
if (error) {
console.error('Skipping vnu-jar test; Java is missing.')
console.error('Skipping vnu-jar test; Java is probably missing.')
console.error(error)
return
}
console.log('Running vnu-jar validation...')
const is32bitJava = !/64-Bit/.test(stderr)
// vnu-jar accepts multiple ignores joined with a `|`.
@ -30,7 +30,14 @@ execFile('java', ['-version'], (error, stdout, stderr) => {
// Per https://www.w3.org/TR/html-aria/#docconformance having "aria-disabled" on a link is
// NOT RECOMMENDED, but it's still valid - we explain in the docs that it's not ideal,
// and offer more robust alternatives, but also need to show a less-than-ideal example
'An “aria-disabled” attribute whose value is “true” should not be specified on an “a” element that has an “href” attribute.'
'An “aria-disabled” attribute whose value is “true” should not be specified on an “a” element that has an “href” attribute.',
// A `code` element with the `is:raw` attribute coming from remark-prismjs (Astro upstream possible bug)
'Attribute “is:raw” is not serializable as XML 1.0.',
'Attribute “is:raw” not allowed on element “code” at this point.',
// Astro's expecting trailing slashes on HTML tags such as <br />
'Trailing slash on void elements has no effect and interacts badly with unquoted attribute values.',
// Allow `switch` attribute.
'Attribute “switch” not allowed on element “input” at this point.'
].join('|')
const args = [
@ -49,6 +56,8 @@ execFile('java', ['-version'], (error, stdout, stderr) => {
args.splice(0, 0, '-Xss512k')
}
console.log(`command used: java ${args.join(' ')}`)
return spawn('java', args, {
shell: true,
stdio: 'inherit'

View file

@ -3,16 +3,20 @@
/*!
* Script to create the built examples zip archive;
* requires the `zip` command to be present!
* Copyright 2020-2022 The Bootstrap Authors
* Copyright 2020-2025 The Bootstrap Authors
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
'use strict'
import fs from 'node:fs/promises'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import sh from 'shelljs'
import { format } from 'prettier'
const path = require('node:path')
const sh = require('shelljs')
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const pkg = require('../package.json')
const pkgJson = path.join(__dirname, '../package.json')
const pkg = JSON.parse(await fs.readFile(pkgJson, 'utf8'))
const versionShort = pkg.config.version_short
const distFolder = `bootstrap-${pkg.version}-examples`
@ -34,6 +38,9 @@ const imgFiles = [
'bootstrap-logo.svg',
'bootstrap-logo-white.svg'
]
const staticJsFiles = [
'color-modes.js'
]
sh.config.fatal = true
@ -52,7 +59,8 @@ sh.mkdir('-p', [
distFolder,
`${distFolder}/assets/brand/`,
`${distFolder}/assets/dist/css/`,
`${distFolder}/assets/dist/js/`
`${distFolder}/assets/dist/js/`,
`${distFolder}/assets/js/`
])
sh.cp('-Rf', `${docsDir}/examples/*`, distFolder)
@ -69,22 +77,44 @@ for (const file of imgFiles) {
sh.cp('-f', `${docsDir}/assets/brand/${file}`, `${distFolder}/assets/brand/`)
}
for (const file of staticJsFiles) {
sh.cp('-f', `${docsDir}/assets/js/${file}`, `${distFolder}/assets/js/`)
}
sh.rm(`${distFolder}/index.html`)
// get all examples' HTML files
for (const file of sh.find(`${distFolder}/**/*.html`)) {
const htmlFiles = sh.find(`${distFolder}/**/*.html`)
const formatPromises = htmlFiles.map(async file => {
const fileContents = sh.cat(file)
.toString()
.replace(new RegExp(`"/docs/${versionShort}/`, 'g'), '"../')
.replace(/"..\/dist\//g, '"../assets/dist/')
.replace(/(<link href="\.\.\/.*) integrity=".*>/g, '$1>')
.replace(/(<script src="\.\.\/.*) integrity=".*>/g, '$1></script>')
.replace(/( +)<!-- favicons(.|\n)+<style>/i, ' <style>')
new sh.ShellString(fileContents).to(file)
}
.replace(/(<link href="\.\.\/[^"]*"[^>]*) integrity="[^"]*"/g, '$1')
.replace(/<link[^>]*href="\.\.\/assets\/img\/favicons\/[^"]*"[^>]*>/g, '')
.replace(/(<script src="\.\.\/[^"]*"[^>]*) integrity="[^"]*"/g, '$1')
let formattedHTML
try {
formattedHTML = await format(fileContents, {
parser: 'html',
filepath: file
})
} catch (error) {
console.error(`\nError formatting ${file}:`)
console.error(`Message: ${error.message}`)
console.error('\nSkipping formatting for this file...\n')
formattedHTML = fileContents
}
new sh.ShellString(formattedHTML).to(file)
})
await Promise.all(formatPromises)
// create the zip file
sh.exec(`zip -r9 "${distFolder}.zip" "${distFolder}"`)
sh.exec(`zip -qr9 "${distFolder}.zip" "${distFolder}"`)
// remove the folder we created
sh.rm('-rf', distFolder)