mirror of
https://github.com/tabler/tabler.git
synced 2026-08-28 21:01:59 +04:00
170 lines
7.2 KiB
JavaScript
170 lines
7.2 KiB
JavaScript
// @ts-check
|
|
import { defineConfig } from 'astro/config'
|
|
import mdx from '@astrojs/mdx'
|
|
import { satteri } from '@astrojs/markdown-satteri'
|
|
import beautify from 'js-beautify'
|
|
import { globSync, statSync } from 'node:fs'
|
|
import { availableParallelism } from 'node:os'
|
|
import { fileURLToPath } from 'node:url'
|
|
import { Worker } from 'node:worker_threads'
|
|
// Imported statically: a dynamic import() inside the hook below would go through
|
|
// Vite's module runner, which is already closed by the time astro:build:done runs
|
|
// (the config is bundled by vite-node because it imports a .ts module).
|
|
import prettier from 'prettier'
|
|
import { copyAssets } from '../.build/copy-assets'
|
|
|
|
/** @param {string} p */
|
|
const path = (p) => fileURLToPath(new URL(p, import.meta.url))
|
|
|
|
/**
|
|
* After build, format page HTML with prettier (users copy it 1:1).
|
|
* The formatting itself happens in .build/prettify-html-worker.mjs — see there
|
|
* for why it is worth spreading over threads.
|
|
* @returns {import('astro').AstroIntegration}
|
|
*/
|
|
function prettifyHtml() {
|
|
return {
|
|
name: 'prettify-html',
|
|
hooks: {
|
|
'astro:build:done': async ({ dir, logger }) => {
|
|
const outDir = fileURLToPath(dir)
|
|
// dist/preview/ and dist/dist/ are copy-assets.mjs's copies of public/{preview,dist}
|
|
// (demo css/js and @tabler/core's dist, including vendored libs) — not pages, and
|
|
// some vendored libs ship their own malformed docs/*.html that trips the parser below.
|
|
/** @param {string} file */
|
|
const isVendorCopy = (file) => file.includes(`${outDir}preview/`) || file.includes(`${outDir}dist/`)
|
|
const files = globSync(`${outDir}**/*.html`, { exclude: isVendorCopy })
|
|
const firstFile = files[0]
|
|
if (firstFile === undefined) return
|
|
|
|
// Resolved once for the whole run: every page sits in the same directory
|
|
// tree and no .prettierrc override matches *.html. The CLI pass this
|
|
// replaced also needed --ignore-path devNull, because .gitignore and
|
|
// .prettierignore both list "dist" (so normal lint/format passes skip build
|
|
// output) and the CLI would have skipped the very directory it targeted.
|
|
// format() reads no ignore files, so the workers need no such opt-out.
|
|
const options = { ...(await prettier.resolveConfig(firstFile)), parser: 'html' }
|
|
|
|
// Page sizes span two orders of magnitude, so an even split by file count
|
|
// leaves one worker alone with the biggest page long after the others are
|
|
// done. Dealing the size-ordered list round-robin evens the batches out.
|
|
const workerCount = Math.min(availableParallelism(), 8, files.length)
|
|
const ordered = files
|
|
.map((file) => ({ file, size: statSync(file).size }))
|
|
.sort((a, b) => b.size - a.size)
|
|
.map(({ file }) => file)
|
|
const batches = Array.from({ length: workerCount }, (_, worker) => ordered.filter((_, index) => index % workerCount === worker))
|
|
|
|
await Promise.all(
|
|
batches.map(
|
|
(batch) =>
|
|
new Promise((resolve, reject) => {
|
|
const worker = new Worker(path('./.build/prettify-html-worker.mjs'), { workerData: { files: batch, options } })
|
|
worker.on('error', reject)
|
|
worker.on('exit', (code) => (code === 0 ? resolve(undefined) : reject(new Error(`prettify-html worker exited with code ${code}`))))
|
|
}),
|
|
),
|
|
)
|
|
logger.info(`HTML formatted with prettier (${files.length} pages, ${workerCount} workers)`)
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// https://astro.build/config
|
|
export default defineConfig({
|
|
// pages live at the package root (./pages) — all components/lib/data are
|
|
// shared (see the @shared alias)
|
|
srcDir: '.',
|
|
server: {
|
|
port: 3000,
|
|
// bind on all interfaces so the dev server is reachable from Docker
|
|
// port mappings and other devices on the local network
|
|
host: true,
|
|
},
|
|
vite: {
|
|
resolve: {
|
|
alias: {
|
|
// Demo data in shared/data (single source of truth).
|
|
'@data': fileURLToPath(new URL('../shared/data', import.meta.url)),
|
|
// Components/lib shared with docs.
|
|
'@shared': fileURLToPath(new URL('../shared', import.meta.url)),
|
|
'@ui': fileURLToPath(new URL('../shared/ui', import.meta.url)),
|
|
'@components': fileURLToPath(new URL('../shared/components', import.meta.url)),
|
|
},
|
|
},
|
|
},
|
|
build: {
|
|
// Emit sign-in.html instead of sign-in/index.html (HTML is the product).
|
|
format: 'file',
|
|
},
|
|
// Keep readable HTML; prettier formats after the build.
|
|
compressHTML: false,
|
|
integrations: [
|
|
copyAssets({
|
|
repo: path('..'),
|
|
publicDir: path('./public'),
|
|
copies: [
|
|
{
|
|
// @tabler/core dist (css/js/fonts/img/libs) — same as the Eleventy passthrough.
|
|
// Fallback keeps current assets if core rebuilds dist mid-copy in turbo dev.
|
|
from: path('./node_modules/@tabler/core/dist'),
|
|
to: path('./public/dist'),
|
|
label: '@tabler/core',
|
|
allowDestinationFallback: true,
|
|
},
|
|
{
|
|
// demo css/js built by this package's sass/vite pipeline. Source is
|
|
// tmp-assets/ (NOT dist/) on purpose — dist/ is Astro's own build output,
|
|
// and copying from a path Astro also writes to caused unbounded growth
|
|
// across repeated builds. See preview/.build/vite.config.mts.
|
|
from: path('./tmp-assets'),
|
|
to: path('./public/preview'),
|
|
label: '@tabler/preview',
|
|
},
|
|
{
|
|
// static assets (photos, avatars, tracks, brand svgs...). The real source,
|
|
// because preview/static is a symlink that may not survive deployment packaging.
|
|
from: path('../shared/static'),
|
|
to: path('./public/static'),
|
|
label: 'shared assets',
|
|
},
|
|
// favicons (source assets of @tabler/preview)
|
|
{ from: path('./assets/favicon.ico'), to: path('./public/favicon.ico'), label: '@tabler/preview' },
|
|
{ from: path('./assets/favicon-dev.ico'), to: path('./public/favicon-dev.ico'), label: '@tabler/preview' },
|
|
],
|
|
// Watch the real core/dist, not the node_modules symlink the startup copy
|
|
// reads from — same directory.
|
|
syncDirs: [
|
|
{ from: path('../core/dist'), to: path('./public/dist') },
|
|
{ from: path('../shared/static'), to: path('./public/static') },
|
|
],
|
|
// watch-css/watch-js write straight into public/preview — the file is already
|
|
// in place, but Astro does not reload the browser on public/ changes.
|
|
reloadDirs: [path('./public/preview')],
|
|
}),
|
|
mdx(),
|
|
prettifyHtml(),
|
|
],
|
|
markdown: {
|
|
// No typographic quote rewriting.
|
|
processor: satteri({ features: { smartPunctuation: false } }),
|
|
shikiConfig: {
|
|
theme: 'github-dark',
|
|
transformers: [
|
|
{
|
|
// Beautify html fences before highlighting.
|
|
preprocess(code) {
|
|
return this.options.lang === 'html' ? beautify.html(code, { indent_size: 2, wrap_line_length: 80 }) : code
|
|
},
|
|
// Keep shiki classes only (drop Astro's astro-code / data-language).
|
|
pre(node) {
|
|
node.properties.class = 'shiki github-dark'
|
|
delete node.properties.dataLanguage
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
})
|