mirror of
https://github.com/shadcn-ui/ui.git
synced 2026-06-11 09:51:40 +00:00
feat: update registry build commands (#10880)
* feat: update registry build commands * fix
This commit is contained in:
@@ -141,6 +141,11 @@ When adding or modifying components, please ensure that:
|
|||||||
2. You update the documentation.
|
2. You update the documentation.
|
||||||
3. You run `pnpm registry:build` to update the registry.
|
3. You run `pnpm registry:build` to update the registry.
|
||||||
|
|
||||||
|
See [`apps/v4/registry/README.md`](apps/v4/registry/README.md) for how the
|
||||||
|
registry pipeline is structured and for the faster targeted build modes
|
||||||
|
(`--style`, `--registry`, `--examples`, `--indexes`) you can use while
|
||||||
|
iterating locally. Always run the full `pnpm registry:build` before committing.
|
||||||
|
|
||||||
## Commit Convention
|
## Commit Convention
|
||||||
|
|
||||||
Before you create a Pull Request, please check whether your commits comply with
|
Before you create a Pull Request, please check whether your commits comply with
|
||||||
|
|||||||
100
apps/v4/registry/README.md
Normal file
100
apps/v4/registry/README.md
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
# Registry
|
||||||
|
|
||||||
|
This directory is the source of truth for the v4 component registry. The build
|
||||||
|
pipeline (`../scripts/build-registry.mts`) reads the authored source here and
|
||||||
|
generates the runtime indexes, the local `styles/` consumed by the docs app, and
|
||||||
|
the installable output under `public/r/`.
|
||||||
|
|
||||||
|
## Source of truth (authored by hand)
|
||||||
|
|
||||||
|
- **`bases/base/`, `bases/radix/`** — the two authored base registries (Base UI
|
||||||
|
and Radix). Each holds a `registry.ts` plus `ui/`, `lib/`, `hooks/`, `blocks/`,
|
||||||
|
`examples/`, and `internal/`. Shared surfaces should stay in sync across both
|
||||||
|
bases — see [`bases/README.md`](./bases/README.md).
|
||||||
|
- **`styles/style-*.css`** — the style token files (`nova`, `sera`, `vega`, …).
|
||||||
|
Each defines the design tokens for one style.
|
||||||
|
- **`new-york-v4/`** — the legacy source registry. Unlike the generated
|
||||||
|
combinations below, its `registry.ts` and component files are authored
|
||||||
|
directly and committed.
|
||||||
|
- **`../examples/base`, `../examples/radix`** — authored component demos. See
|
||||||
|
[`../examples/README.md`](../examples/README.md).
|
||||||
|
|
||||||
|
## Generated output (do not edit by hand)
|
||||||
|
|
||||||
|
Persistent (committed):
|
||||||
|
|
||||||
|
- `bases/__index__.tsx` — runtime lookup for the authored bases.
|
||||||
|
- `__index__.tsx` — runtime lookup across legacy styles and every base/style
|
||||||
|
combination.
|
||||||
|
- `__blocks__.json` — block metadata index.
|
||||||
|
- `../examples/__index__.tsx` — runtime lookup for demos.
|
||||||
|
- `../styles/<style>/ui/*` — compiled components for each base/style
|
||||||
|
combination, imported by the docs app.
|
||||||
|
- `../styles/<style>/ui-rtl/*` — RTL variants, generated for `base-nova` and
|
||||||
|
`radix-nova` only.
|
||||||
|
- `../public/r/*` — installable registry JSON served by the website and the CLI.
|
||||||
|
|
||||||
|
Temporary (created during the build, then cleaned up):
|
||||||
|
|
||||||
|
- `<style>/*` — per-combination registries (e.g. `base-nova/`).
|
||||||
|
- `../registry-<style>.json`
|
||||||
|
|
||||||
|
## The style model
|
||||||
|
|
||||||
|
There are two kinds of "styles", and the distinction drives the build flags:
|
||||||
|
|
||||||
|
- **Generated combinations** — every base (`base`, `radix`) crossed with every
|
||||||
|
style token (`nova`, `sera`, …) produces a combination like `base-nova` or
|
||||||
|
`radix-sera`. These are generated from the authored bases plus the style CSS;
|
||||||
|
nothing under `registry/<combination>/` is committed.
|
||||||
|
- **Legacy source registry** — `new-york-v4` is authored directly and committed.
|
||||||
|
It is not generated from a base/style combination.
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
Run from `apps/v4`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm registry:build
|
||||||
|
```
|
||||||
|
|
||||||
|
This runs the full pipeline: build the bases, generate every combination, write
|
||||||
|
the runtime indexes, export `public/r/` for every style, copy the compiled UI
|
||||||
|
into `styles/`, and build the RTL styles. It is the canonical build — generated
|
||||||
|
output is prettier-formatted. **Run this before committing or for production.**
|
||||||
|
|
||||||
|
### Fast targeted builds
|
||||||
|
|
||||||
|
The targeted flags below are for quick local iteration. To stay fast they
|
||||||
|
**skip formatting** the generated output, so they can leave generated files
|
||||||
|
unformatted (and produce large but harmless `git diff` churn). The full
|
||||||
|
`pnpm registry:build` above re-canonicalizes everything, so run it before you
|
||||||
|
commit.
|
||||||
|
|
||||||
|
For local iteration you can rebuild only the artifact you changed:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm registry:build --examples # examples/__index__.tsx
|
||||||
|
pnpm registry:build --indexes # runtime registry indexes
|
||||||
|
pnpm registry:build --style base-nova # styles/base-nova/ui (+ ui-rtl)
|
||||||
|
pnpm registry:build --style all # every generated combination
|
||||||
|
pnpm registry:build --registry base-nova # public/r/styles/base-nova
|
||||||
|
pnpm registry:build --registry all # every style, incl. new-york-v4
|
||||||
|
```
|
||||||
|
|
||||||
|
| Flag | Rebuilds | Run after |
|
||||||
|
| ------------------------- | -------------------------------------------------------------------------------- | ------------------------------------ |
|
||||||
|
| `--examples` | `../examples/__index__.tsx` | adding, removing, or renaming a demo |
|
||||||
|
| `--indexes` | `bases/__index__.tsx`, `__index__.tsx`, `__blocks__.json`, `public/r/index.json` | changing registry or block metadata |
|
||||||
|
| `--style <style\|all>` | `../styles/<style>/ui` and `ui-rtl` | editing authored base UI/components |
|
||||||
|
| `--registry <style\|all>` | `../public/r/styles/<style>` | changing what the CLI installs |
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
|
||||||
|
- Flags can be combined, e.g. `--style base-nova --registry base-nova`.
|
||||||
|
- `all` targets every supported style.
|
||||||
|
- Editing an existing example file usually does **not** need a rebuild — only
|
||||||
|
adding, removing, or renaming one (which changes the index) does.
|
||||||
|
- `--style new-york-v4` is rejected because it is a legacy source registry, not a
|
||||||
|
generated combination. Use `--registry new-york-v4` instead.
|
||||||
|
- Unknown targets fail with the list of valid style ids.
|
||||||
@@ -5,6 +5,7 @@ import { createRequire } from "module"
|
|||||||
import { availableParallelism } from "os"
|
import { availableParallelism } from "os"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import { fileURLToPath } from "url"
|
import { fileURLToPath } from "url"
|
||||||
|
import { parseArgs } from "util"
|
||||||
import prettier from "prettier"
|
import prettier from "prettier"
|
||||||
import { rimraf } from "rimraf"
|
import { rimraf } from "rimraf"
|
||||||
import { registrySchema, type RegistryItem } from "shadcn/schema"
|
import { registrySchema, type RegistryItem } from "shadcn/schema"
|
||||||
@@ -55,6 +56,13 @@ import { STYLES } from "@/registry/styles"
|
|||||||
* 7. Build styles/<style>/ui-rtl for base-nova and radix-nova only.
|
* 7. Build styles/<style>/ui-rtl for base-nova and radix-nova only.
|
||||||
* 8. Format the generated persistent outputs.
|
* 8. Format the generated persistent outputs.
|
||||||
* 9. Clean up the temporary registry/<base-style> trees and registry-*.json.
|
* 9. Clean up the temporary registry/<base-style> trees and registry-*.json.
|
||||||
|
*
|
||||||
|
* Targeted modes (see parseBuildOptions):
|
||||||
|
* - --examples rebuilds examples/__index__.tsx only.
|
||||||
|
* - --indexes rebuilds the runtime registry indexes only.
|
||||||
|
* - --style <style|all> rebuilds local styles/<style>/ui (+ ui-rtl).
|
||||||
|
* - --registry <style|all> rebuilds installable public/r/styles/<style>.
|
||||||
|
* Running with no options performs the full build described above.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const STYLE_COMBINATIONS = Array.from(BASES).flatMap((base) =>
|
const STYLE_COMBINATIONS = Array.from(BASES).flatMap((base) =>
|
||||||
@@ -98,6 +106,12 @@ type TransformCacheManifestEntry = {
|
|||||||
const transformCacheManifest = new Map<string, TransformCacheManifestEntry>()
|
const transformCacheManifest = new Map<string, TransformCacheManifestEntry>()
|
||||||
let transformCacheDirty = false
|
let transformCacheDirty = false
|
||||||
let prettierConfigPromise: Promise<prettier.Options | null> | null = null
|
let prettierConfigPromise: Promise<prettier.Options | null> | null = null
|
||||||
|
|
||||||
|
// Generated output is prettier-formatted in the full (prod) build. Targeted dev
|
||||||
|
// builds skip formatting for speed; the next full build re-canonicalizes
|
||||||
|
// everything. The transform cache always stores formatted content (see
|
||||||
|
// getCachedStyledContent), so a full build never reads an unformatted entry.
|
||||||
|
let shouldFormatOutput = true
|
||||||
const resolveFromScript = createRequire(import.meta.url).resolve
|
const resolveFromScript = createRequire(import.meta.url).resolve
|
||||||
|
|
||||||
const iconProject = new Project({
|
const iconProject = new Project({
|
||||||
@@ -125,6 +139,101 @@ function getStyleCombination(styleName: string) {
|
|||||||
return STYLE_COMBINATIONS.find((style) => style.name === styleName) ?? null
|
return STYLE_COMBINATIONS.find((style) => style.name === styleName) ?? null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BuildOptions = {
|
||||||
|
examples: boolean
|
||||||
|
indexes: boolean
|
||||||
|
style: "all" | string | null
|
||||||
|
registry: "all" | string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
const USAGE = `Usage: registry:build [options]
|
||||||
|
|
||||||
|
Run with no options for a full registry build, or target a single artifact:
|
||||||
|
|
||||||
|
--examples Rebuild examples/__index__.tsx only.
|
||||||
|
--indexes Rebuild the runtime registry indexes only.
|
||||||
|
--style <style|all> Rebuild local generated style files under styles/<style>/ui.
|
||||||
|
--registry <style|all> Rebuild installable registry JSON under public/r/styles/<style>.
|
||||||
|
|
||||||
|
<style> must be "all" or a known final style id (e.g. base-nova, radix-nova, base-sera, new-york-v4).
|
||||||
|
Flags can be combined, e.g. --style base-nova --registry base-nova.`
|
||||||
|
|
||||||
|
function getKnownStyleNames() {
|
||||||
|
return new Set(getStylesToBuild().map((style) => style.name))
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertKnownTarget(flag: "--style" | "--registry", target: string) {
|
||||||
|
if (target === "all") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const knownStyleNames = getKnownStyleNames()
|
||||||
|
if (!knownStyleNames.has(target)) {
|
||||||
|
const valid = ["all", ...Array.from(knownStyleNames)].join(", ")
|
||||||
|
throw new Error(
|
||||||
|
`Unknown ${flag} target "${target}". Valid targets: ${valid}.\n\n${USAGE}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseBuildOptions(argv: string[]): BuildOptions {
|
||||||
|
let values: {
|
||||||
|
examples?: boolean
|
||||||
|
indexes?: boolean
|
||||||
|
style?: string
|
||||||
|
registry?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
;({ values } = parseArgs({
|
||||||
|
args: argv,
|
||||||
|
options: {
|
||||||
|
examples: { type: "boolean" },
|
||||||
|
indexes: { type: "boolean" },
|
||||||
|
style: { type: "string" },
|
||||||
|
registry: { type: "string" },
|
||||||
|
},
|
||||||
|
allowPositionals: false,
|
||||||
|
strict: true,
|
||||||
|
}))
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`${(error as Error).message}\n\n${USAGE}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (values.style !== undefined) {
|
||||||
|
assertKnownTarget("--style", values.style)
|
||||||
|
}
|
||||||
|
if (values.registry !== undefined) {
|
||||||
|
assertKnownTarget("--registry", values.registry)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
examples: values.examples ?? false,
|
||||||
|
indexes: values.indexes ?? false,
|
||||||
|
style: values.style ?? null,
|
||||||
|
registry: values.registry ?? null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isFullBuild(options: BuildOptions) {
|
||||||
|
return (
|
||||||
|
!options.examples &&
|
||||||
|
!options.indexes &&
|
||||||
|
options.style === null &&
|
||||||
|
options.registry === null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTargetStyles(target: "all" | string | null) {
|
||||||
|
const stylesToBuild = getStylesToBuild()
|
||||||
|
|
||||||
|
if (target === "all") {
|
||||||
|
return stylesToBuild
|
||||||
|
}
|
||||||
|
|
||||||
|
return stylesToBuild.filter((style) => style.name === target)
|
||||||
|
}
|
||||||
|
|
||||||
function stripFileExtension(filePath: string) {
|
function stripFileExtension(filePath: string) {
|
||||||
return filePath.replace(/\.(tsx|ts|json|mdx)$/, "")
|
return filePath.replace(/\.(tsx|ts|json|mdx)$/, "")
|
||||||
}
|
}
|
||||||
@@ -272,7 +381,7 @@ async function writeIfChanged(filePath: string, content: string) {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
async function formatGeneratedSource(content: string, filePath: string) {
|
async function formatSource(content: string, filePath: string) {
|
||||||
prettierConfigPromise ??= prettier.resolveConfig(
|
prettierConfigPromise ??= prettier.resolveConfig(
|
||||||
path.join(process.cwd(), "package.json")
|
path.join(process.cwd(), "package.json")
|
||||||
)
|
)
|
||||||
@@ -285,6 +394,14 @@ async function formatGeneratedSource(content: string, filePath: string) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function formatGeneratedSource(content: string, filePath: string) {
|
||||||
|
if (!shouldFormatOutput) {
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
|
||||||
|
return formatSource(content, filePath)
|
||||||
|
}
|
||||||
|
|
||||||
async function formatGeneratedJson(value: unknown, filePath: string) {
|
async function formatGeneratedJson(value: unknown, filePath: string) {
|
||||||
return formatGeneratedSource(JSON.stringify(value, null, 2), filePath)
|
return formatGeneratedSource(JSON.stringify(value, null, 2), filePath)
|
||||||
}
|
}
|
||||||
@@ -383,7 +500,9 @@ async function getCachedStyledContent({
|
|||||||
new RegExp(`@/registry/bases/${baseName}/`, "g"),
|
new RegExp(`@/registry/bases/${baseName}/`, "g"),
|
||||||
`@/registry/${styleName}/`
|
`@/registry/${styleName}/`
|
||||||
)
|
)
|
||||||
transformedContent = await formatGeneratedSource(
|
// Always format cached content so a later full build never reads an
|
||||||
|
// unformatted entry produced by a targeted dev build.
|
||||||
|
transformedContent = await formatSource(
|
||||||
transformedContent,
|
transformedContent,
|
||||||
path.join(getTemporaryRegistryRoot(styleName), filePath)
|
path.join(getTemporaryRegistryRoot(styleName), filePath)
|
||||||
)
|
)
|
||||||
@@ -430,10 +549,26 @@ async function runWithConcurrency<T, R>(
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const totalStart = performance.now()
|
const totalStart = performance.now()
|
||||||
|
const options = parseBuildOptions(process.argv.slice(2))
|
||||||
|
|
||||||
|
if (isFullBuild(options)) {
|
||||||
|
await runFullBuild()
|
||||||
|
} else {
|
||||||
|
await runTargetedBuild(options)
|
||||||
|
}
|
||||||
|
|
||||||
|
const elapsed = ((performance.now() - totalStart) / 1000).toFixed(2)
|
||||||
|
console.log(`\n✅ Build complete in ${elapsed}s!`)
|
||||||
|
} catch (error) {
|
||||||
|
await saveTransformCache().catch(console.error)
|
||||||
|
console.error(error)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runFullBuild() {
|
||||||
await loadTransformCache()
|
await loadTransformCache()
|
||||||
|
|
||||||
console.log("🏗️ Building bases...")
|
console.log("\n🏗️ Building bases...")
|
||||||
await buildBasesIndex(Array.from(BASES))
|
await buildBasesIndex(Array.from(BASES))
|
||||||
await buildBases(Array.from(BASES))
|
await buildBases(Array.from(BASES))
|
||||||
|
|
||||||
@@ -475,15 +610,120 @@ try {
|
|||||||
await buildRtlStyles()
|
await buildRtlStyles()
|
||||||
|
|
||||||
console.log("\n🧹 Cleaning up...")
|
console.log("\n🧹 Cleaning up...")
|
||||||
await cleanUp(stylesToBuild)
|
await cleanUpTemporaryFiles(stylesToBuild.map((style) => style.name))
|
||||||
await saveTransformCache()
|
await saveTransformCache()
|
||||||
|
}
|
||||||
|
|
||||||
const elapsed = ((performance.now() - totalStart) / 1000).toFixed(2)
|
async function runTargetedBuild(options: BuildOptions) {
|
||||||
console.log(`\n✅ Build complete in ${elapsed}s!`)
|
// Targeted builds are for quick dev iteration: skip prettier on generated
|
||||||
} catch (error) {
|
// output. The full (prod) build re-formats everything to its canonical state.
|
||||||
await saveTransformCache().catch(console.error)
|
shouldFormatOutput = false
|
||||||
console.error(error)
|
|
||||||
process.exit(1)
|
await loadTransformCache()
|
||||||
|
|
||||||
|
// Phases run in dependency-safe order: indexes and examples write the runtime
|
||||||
|
// lookup files first, the targeted style build copies compiled ui into
|
||||||
|
// styles/<style>, and the targeted registry build exports public/r last.
|
||||||
|
if (options.indexes) {
|
||||||
|
await runIndexesBuild()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.examples) {
|
||||||
|
await runExamplesBuild()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.style !== null) {
|
||||||
|
await runTargetedStyleBuild(options.style)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.registry !== null) {
|
||||||
|
await runTargetedRegistryBuild(options.registry)
|
||||||
|
}
|
||||||
|
|
||||||
|
await saveTransformCache()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runIndexesBuild() {
|
||||||
|
console.log("🏗️ Building registry/bases/__index__.tsx...")
|
||||||
|
await buildBasesIndex(Array.from(BASES))
|
||||||
|
|
||||||
|
// buildBlocksIndex imports @/registry/__index__, so the registry index must
|
||||||
|
// be regenerated before the blocks index.
|
||||||
|
console.log("\n📦 Building registry/__index__.tsx...")
|
||||||
|
await buildRegistryIndex(getStylesToBuild())
|
||||||
|
|
||||||
|
console.log("\n🗂️ Building registry/__blocks__.json...")
|
||||||
|
await buildBlocksIndex()
|
||||||
|
|
||||||
|
console.log("\n📦 Building public/r/index.json...")
|
||||||
|
await buildIndex()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runExamplesBuild() {
|
||||||
|
console.log("📋 Building examples/__index__.tsx...")
|
||||||
|
await buildExamplesIndex()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runTargetedStyleBuild(target: "all" | string) {
|
||||||
|
if (target !== "all" && !getStyleCombination(target)) {
|
||||||
|
throw new Error(
|
||||||
|
`--style ${target} is not supported because it is a legacy source registry. Use --registry ${target}.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// styles/<style>/ui only exists for generated base/style combinations, so we
|
||||||
|
// skip legacy source styles (e.g. new-york-v4) when targeting "all".
|
||||||
|
const targetStyles = getTargetStyles(target).filter((style) =>
|
||||||
|
getStyleCombination(style.name)
|
||||||
|
)
|
||||||
|
const targetStyleNames = new Set(targetStyles.map((style) => style.name))
|
||||||
|
|
||||||
|
if (targetStyleNames.size === 0) {
|
||||||
|
console.log(" No generated styles to build.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("💅 Building styles...")
|
||||||
|
await buildBases(Array.from(BASES), targetStyleNames)
|
||||||
|
|
||||||
|
console.log("\n📋 Copying compiled ui to styles...")
|
||||||
|
await copyUIToStyles(targetStyleNames)
|
||||||
|
|
||||||
|
console.log("\n🔄 Building RTL styles...")
|
||||||
|
await buildRtlStyles(targetStyleNames)
|
||||||
|
|
||||||
|
console.log("\n🧹 Cleaning up...")
|
||||||
|
await cleanUpTemporaryFiles(Array.from(targetStyleNames))
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runTargetedRegistryBuild(target: "all" | string) {
|
||||||
|
const targetStyles = getTargetStyles(target)
|
||||||
|
const comboStyleNames = new Set(
|
||||||
|
targetStyles
|
||||||
|
.filter((style) => getStyleCombination(style.name))
|
||||||
|
.map((style) => style.name)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Only generated base/style combinations need a temporary registry/<style>
|
||||||
|
// tree. Legacy source styles (e.g. new-york-v4) already ship registry.ts.
|
||||||
|
if (comboStyleNames.size > 0) {
|
||||||
|
console.log("🏗️ Building bases...")
|
||||||
|
await buildBases(Array.from(BASES), comboStyleNames)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("\n💅 Building registry...")
|
||||||
|
await runWithConcurrency(
|
||||||
|
targetStyles,
|
||||||
|
CLI_BUILD_CONCURRENCY,
|
||||||
|
async (style) => {
|
||||||
|
await buildRegistryJsonFile(style.name)
|
||||||
|
await buildRegistry(style.name)
|
||||||
|
console.log(` ✅ ${style.name}`)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
console.log("\n🧹 Cleaning up...")
|
||||||
|
await cleanUpTemporaryFiles(targetStyles.map((style) => style.name))
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildBasesIndex(bases: Base[]) {
|
async function buildBasesIndex(bases: Base[]) {
|
||||||
@@ -575,10 +815,21 @@ export const Index: Record<string, Record<string, any>> = {`
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildBases(bases: Base[]) {
|
async function buildBases(bases: Base[], targetStyleNames?: Set<string>) {
|
||||||
|
// For targeted builds, only load bases that contribute a requested
|
||||||
|
// combination. Otherwise a single-base target (e.g. --style base-nova) would
|
||||||
|
// still import and read every source file for the other base.
|
||||||
|
const basesToBuild = targetStyleNames
|
||||||
|
? bases.filter((base) =>
|
||||||
|
STYLES.some((style) =>
|
||||||
|
targetStyleNames.has(`${base.name}-${style.name}`)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
: bases
|
||||||
|
|
||||||
const [baseImports, styleMaps, transformCacheHash] = await Promise.all([
|
const [baseImports, styleMaps, transformCacheHash] = await Promise.all([
|
||||||
Promise.all(
|
Promise.all(
|
||||||
bases.map(async (base) => {
|
basesToBuild.map(async (base) => {
|
||||||
const { registry: baseRegistry } = await import(
|
const { registry: baseRegistry } = await import(
|
||||||
`../registry/bases/${base.name}/registry.ts`
|
`../registry/bases/${base.name}/registry.ts`
|
||||||
)
|
)
|
||||||
@@ -656,6 +907,11 @@ async function buildBases(bases: Base[]) {
|
|||||||
sourceFiles,
|
sourceFiles,
|
||||||
} of baseImports) {
|
} of baseImports) {
|
||||||
for (const { style, styleHash, styleMap } of styleMaps) {
|
for (const { style, styleHash, styleMap } of styleMaps) {
|
||||||
|
const styleName = `${base.name}-${style.name}`
|
||||||
|
if (targetStyleNames && !targetStyleNames.has(styleName)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
combinations.push({
|
combinations.push({
|
||||||
base,
|
base,
|
||||||
style,
|
style,
|
||||||
@@ -995,15 +1251,21 @@ async function buildBlocksIndex() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function cleanUp(stylesToBuild: { name: string; title: string }[]) {
|
async function cleanUpTemporaryFiles(styleNames: string[]) {
|
||||||
const cleanupTasks: Promise<boolean>[] = stylesToBuild.map((style) =>
|
const cleanupTasks: Promise<boolean>[] = []
|
||||||
rimraf(path.join(process.cwd(), `registry-${style.name}.json`))
|
|
||||||
)
|
|
||||||
|
|
||||||
for (const style of STYLE_COMBINATIONS) {
|
for (const styleName of styleNames) {
|
||||||
const tempRegistryRoot = getTemporaryRegistryRoot(style.name)
|
cleanupTasks.push(
|
||||||
console.log(` 🗑️ registry/${style.name}`)
|
rimraf(path.join(process.cwd(), `registry-${styleName}.json`))
|
||||||
cleanupTasks.push(rimraf(tempRegistryRoot))
|
)
|
||||||
|
|
||||||
|
// Only generated combinations have a temporary registry/<style> tree.
|
||||||
|
// Legacy source styles (e.g. new-york-v4) own registry/<style> and must
|
||||||
|
// never be removed.
|
||||||
|
if (getStyleCombination(styleName)) {
|
||||||
|
console.log(` 🗑️ registry/${styleName}`)
|
||||||
|
cleanupTasks.push(rimraf(getTemporaryRegistryRoot(styleName)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await Promise.all(cleanupTasks)
|
await Promise.all(cleanupTasks)
|
||||||
@@ -1047,9 +1309,13 @@ async function applyIconTransform(content: string, filename: string) {
|
|||||||
return sourceFile.getText()
|
return sourceFile.getText()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function copyUIToStyles() {
|
async function copyUIToStyles(targetStyleNames?: Set<string>) {
|
||||||
|
const styleCombinations = targetStyleNames
|
||||||
|
? STYLE_COMBINATIONS.filter((style) => targetStyleNames.has(style.name))
|
||||||
|
: STYLE_COMBINATIONS
|
||||||
|
|
||||||
await runWithConcurrency(
|
await runWithConcurrency(
|
||||||
STYLE_COMBINATIONS,
|
styleCombinations,
|
||||||
COPY_CONCURRENCY,
|
COPY_CONCURRENCY,
|
||||||
async ({ name: styleName }) => {
|
async ({ name: styleName }) => {
|
||||||
const sourceDir = path.join(getTemporaryRegistryRoot(styleName), "ui")
|
const sourceDir = path.join(getTemporaryRegistryRoot(styleName), "ui")
|
||||||
@@ -1093,9 +1359,13 @@ async function copyUIToStyles() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildRtlStyles() {
|
async function buildRtlStyles(targetStyleNames?: Set<string>) {
|
||||||
await runWithConcurrency(
|
await runWithConcurrency(
|
||||||
STYLE_COMBINATIONS.filter((style) => shouldGenerateRtlStyles(style.name)),
|
STYLE_COMBINATIONS.filter(
|
||||||
|
(style) =>
|
||||||
|
shouldGenerateRtlStyles(style.name) &&
|
||||||
|
(!targetStyleNames || targetStyleNames.has(style.name))
|
||||||
|
),
|
||||||
COPY_CONCURRENCY,
|
COPY_CONCURRENCY,
|
||||||
async ({ name: styleName }) => {
|
async ({ name: styleName }) => {
|
||||||
const sourceDir = path.join(getPersistentStyleRoot(styleName), "ui")
|
const sourceDir = path.join(getPersistentStyleRoot(styleName), "ui")
|
||||||
|
|||||||
Reference in New Issue
Block a user