feat(assets): add helpers package for chroma lexer map generation (#781)

- Add `packages/helpers` with CLI dispatcher pattern for code generation
- Add `chroma-lexers` helper that fetches lexer definitions from alecthomas/chroma
- Rename `$code-type-map` to `$chroma-lexers`, `_code-type.scss` to `_chroma-lexers.scss`
- Fix `fi-var` and `set-fi-var` to preserve quotes for string fallbacks
- Add `capitalize` utility to `packages/shared`
- Replace `console.*` with `consola` in packages
- Add `pnpm gen:lexers` script and CI workflow for auto-regeneration
- Update CLAUDE.md and CONTRIBUTING.md
This commit is contained in:
Cell
2026-06-03 16:41:07 +08:00
committed by GitHub
parent eeff00f8c7
commit 03ca90b49f
24 changed files with 537 additions and 30 deletions
+2 -1
View File
@@ -17,7 +17,8 @@
"lruihao.hugo-partials-refs",
"tamasfe.even-better-toml",
"casualjim.gotemplate",
"SomewhatStationery.some-sass"
"SomewhatStationery.some-sass",
"gruntfuggly.todo-tree"
]
}
}
+39
View File
@@ -0,0 +1,39 @@
name: Update Chroma lexer map
on:
schedule:
# Run at 00:00, on day 1 of the month
- cron: '0 0 1 * *'
workflow_dispatch:
permissions:
contents: write
pull-requests: write
jobs:
update:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- uses: pnpm/action-setup@v6
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Generate lexer map
run: pnpm gen:lexers
- name: Create Pull Request
uses: peter-evans/create-pull-request@v8
with:
title: 'chore(assets): regenerate chroma lexer map'
commit-message: 'chore(assets): regenerate chroma lexer map'
body: |
Auto-generated `assets/scss/core/maps/_chroma-lexers.scss` via `pnpm gen:lexers`.
Data from: [alecthomas/chroma](https://github.com/alecthomas/chroma/tree/master/lexers) latest release.
branch: auto/chroma-lexers
base: main
labels: dependencies
reviewers: Lruihao
author: Cell[bot] <lrhx@users.noreply.github.com>
committer: Cell[bot] <lrhx@users.noreply.github.com>
+8 -1
View File
@@ -32,6 +32,12 @@ pnpm build # Build all sites (demo + test, merged into public/)
pnpm preview # Preview built site (requires build first)
```
### Code Generation
```bash
pnpm gen:lexers # Regenerate assets/scss/core/maps/_chroma-lexers.scss from Chroma source
```
### Code Quality
```bash
@@ -49,9 +55,10 @@ Root `package.json` is `@hugo-fixit/core`. pnpm workspaces include `apps/*` and
- `apps/demo/` — Demo site (deployed to `demo.fixit.lruihao.cn`)
- `apps/test/` — Test site for exercising theme features
- `packages/shared` — Shared utilities (exports `workspaceRoot`)
- `packages/shared` — Shared utilities (exports `workspaceRoot`, `capitalize`, etc.)
- `packages/versioning` — Version management (auto-updates version in `layouts/_partials/init/index.html` during pre-commit)
- `packages/integration` — Post-build: merges demo/test output into `public/`
- `packages/helpers` — Code generation helpers (CLI dispatcher pattern, sub-commands in `src/<name>/`)
### JavaScript Module System (`assets/js/`)
+5 -1
View File
@@ -75,6 +75,9 @@ pnpm lint
# TypeScript type checking
pnpm typecheck
# Regenerate Chroma lexer map (assets/scss/core/maps/_chroma-lexers.scss)
pnpm gen:lexers
# Preview the built site locally (requires build first)
pnpm preview
```
@@ -120,7 +123,8 @@ FixIt/
├── packages/ # Theme-related packages (pnpm workspaces)
│ ├── shared/ # Shared utilities
│ ├── versioning/ # Version management (pre-commit)
── integration/ # Post-build site merging
── integration/ # Post-build site merging
│ └── helpers/ # Code generation helpers
├── static/ # Static files
├── hugo.toml # Default theme configuration
└── package.json # npm scripts and dependencies
+4
View File
@@ -1,3 +1,4 @@
@use "sass:meta";
@use "sass:string";
@use "override" as *;
@@ -10,6 +11,9 @@
/// background: fi-var(custom-bg, #fff);
@function var($name, $fallback: null) {
@if $fallback != null {
@if meta.type-of($fallback) == string {
@return string.unquote('var(#{$rootPrefix}#{$name}, "#{$fallback}")');
}
@return string.unquote("var(#{$rootPrefix}#{$name}, #{$fallback})");
}
@@ -1,6 +1,8 @@
/// Map of code identifiers/languages to Chroma supported languages.
/// @generator https://github.com/hugo-fixit/.tools/blob/main/tampermonkey/code-type-map-fetcher.user.js
$code-type-map: (
/// This file is auto-generated by `pnpm gen:lexers`, do not edit manually.
/// @see https://github.com/hugo-fixit/FixIt/tree/main/packages/helpers/src/chroma-lexers
$chroma-lexers: (
"🔥": "Mojo",
"1s": "OnesEnterprise",
"1s\\:enterprise": "OnesEnterprise",
"abap": "ABAP",
@@ -16,6 +18,8 @@ $code-type-map: (
"ahk": "AutoHotkey",
"al": "AL",
"alloy": "Alloy",
"ampl": "AMPL",
"ansible": "YAML+Jinja",
"antlr": "ANTLR",
"apache": "ApacheConf",
"apacheconf": "ApacheConf",
@@ -25,6 +29,8 @@ $code-type-map: (
"arduino": "Arduino",
"arexx": "Rexx",
"armasm": "ArmAsm",
"art": "Arturo",
"arturo": "Arturo",
"as": "ActionScript",
"as3": "ActionScript 3",
"asm": "GAS",
@@ -54,6 +60,7 @@ $code-type-map: (
"c": "C",
"c\\#": "C#",
"c\\+\\+": "C++",
"c3": "C3",
"caddy": "Caddyfile",
"caddy-d": "Caddyfile Directives",
"caddyfile": "Caddyfile",
@@ -101,6 +108,7 @@ $code-type-map: (
"dax": "Dax",
"desktop": "Desktop file",
"desktop_entry": "Desktop file",
"devicetree": "Devicetree",
"diff": "Diff",
"django": "Django/Jinja",
"docker": "Docker",
@@ -108,6 +116,7 @@ $code-type-map: (
"dosbatch": "Batchfile",
"dosini": "INI",
"dtd": "DTD",
"dts": "Devicetree",
"duby": "Ruby",
"dylan": "Dylan",
"ebnf": "EBNF",
@@ -117,6 +126,7 @@ $code-type-map: (
"elm": "Elm",
"emacs": "EmacsLisp",
"emacs-lisp": "EmacsLisp",
"erb": "ERB",
"erlang": "Erlang",
"ex": "Elixir",
"exs": "Elixir",
@@ -136,6 +146,8 @@ $code-type-map: (
"gd3": "GDScript3",
"gdscript": "GDScript",
"gdscript3": "GDScript3",
"gemfile-lock": "Gemfile.lock",
"gemfilelock": "Gemfile.lock",
"gemini": "Gemtext",
"gemtext": "Gemtext",
"genshi": "Genshi",
@@ -147,9 +159,7 @@ $code-type-map: (
"gmni": "Gemtext",
"gnuplot": "Gnuplot",
"go": "Go",
"go-html-template": "Go HTML Template",
"go-template": "Go Template",
"go-text-template": "Go Text Template",
"golang": "Go",
"gql": "GraphQL",
"graphql": "GraphQL",
@@ -162,15 +172,17 @@ $code-type-map: (
"haskell": "Haskell",
"haxe": "Haxe",
"hbs": "Handlebars",
"hcl": "Terraform",
"hcl": "HCL",
"hexdump": "Hexdump",
"hlb": "HLB",
"hlsl": "HLSL",
"holyc": "HolyC",
"hs": "Haskell",
"html": "HTML",
"html\\+erb": "ERB",
"html\\+genshi": "Genshi HTML",
"html\\+kid": "Genshi HTML",
"html\\+ruby": "ERB",
"http": "HTTP",
"hx": "Haxe",
"hxsl": "Haxe",
@@ -192,22 +204,31 @@ $code-type-map: (
"js": "JavaScript",
"json": "JSON",
"jsonata": "JSONata",
"jsonl": "JSON",
"jsonnet": "Jsonnet",
"jsx": "React",
"julia": "Julia",
"jungle": "Jungle",
"kak": "Kakoune",
"kakoune": "Kakoune",
"kakrc": "Kakoune",
"kakscript": "Kakoune",
"kdl": "KDL",
"kid": "Genshi",
"kotlin": "Kotlin",
"ksh": "Bash",
"lateralus": "Lateralus",
"latex": "TeX",
"lean": "Lean4",
"lean4": "Lean4",
"lighttpd": "Lighttpd configuration file",
"lighty": "Lighttpd configuration file",
"lilypond": "LilyPond",
"lisp": "Common Lisp",
"llvm": "LLVM",
"ltl": "Lateralus",
"lua": "Lua",
"luau": "Lua",
"luau": "Luau",
"m2": "Modula-2",
"make": "Makefile",
"makefile": "Makefile",
@@ -219,21 +240,25 @@ $code-type-map: (
"mathematica": "Mathematica",
"matlab": "Matlab",
"mawk": "Awk",
"mbt": "MoonBit",
"mcf": "MCFunction",
"mcfunction": "MCFunction",
"md": "Markdown",
"meson": "Meson",
"meson\\.build": "Meson",
"mess": "Markless",
"metal": "Metal",
"mf": "Makefile",
"minizinc": "MiniZinc",
"mkd": "Markdown",
"mlir": "MLIR",
"mma": "Mathematica",
"modelica": "Modelica",
"modula2": "Modula-2",
"mojo": "Mojo",
"monkeyc": "MonkeyC",
"moon": "MoonScript",
"moonbit": "MoonBit",
"moonscript": "MoonScript",
"morrowind": "MorrowindScript",
"mwscript": "MorrowindScript",
@@ -290,12 +315,14 @@ $code-type-map: (
"plc": "Plutus Core",
"plpgsql": "PL/pgSQL",
"plutus-core": "Plutus Core",
"po": "Gettext",
"pony": "Pony",
"posh": "PowerShell",
"postgres": "PostgreSQL SQL dialect",
"postgresql": "PostgreSQL SQL dialect",
"postscr": "PostScript",
"postscript": "PostScript",
"pot": "Gettext",
"pov": "POVRay",
"powerquery": "PowerQuery",
"powershell": "PowerShell",
@@ -337,6 +364,9 @@ $code-type-map: (
"rest": "ReStructuredText",
"restructuredtext": "ReStructuredText",
"rexx": "Rexx",
"rgbasm": "RGBDS Assembly",
"rhtml": "ERB",
"ring": "Ring",
"rkt": "Racket",
"rs": "Rust",
"rst": "ReStructuredText",
@@ -344,9 +374,11 @@ $code-type-map: (
"rust": "Rust",
"s": "R",
"sage": "Python",
"salt": "YAML+Jinja",
"sas": "SAS",
"sass": "Sass",
"scala": "Scala",
"scdoc": "Scdoc",
"scheme": "Scheme",
"scilab": "Scilab",
"scm": "Scheme",
@@ -356,6 +388,7 @@ $code-type-map: (
"shell": "Bash",
"shell-session": "Bash Session",
"sieve": "Sieve",
"sls": "YAML+Jinja",
"smali": "Smali",
"smalltalk": "Smalltalk",
"smarty": "Smarty",
@@ -365,6 +398,7 @@ $code-type-map: (
"sol": "Solidity",
"solidity": "Solidity",
"sp": "SourcePawn",
"spade": "Spade",
"sparql": "SPARQL",
"spec": "RPMSpec",
"spitfire": "Cheetah",
@@ -377,6 +411,7 @@ $code-type-map: (
"squidconf": "SquidConf",
"ssed": "Sed",
"st": "Smalltalk",
"starlark": "Python",
"stylus": "Stylus",
"sv": "Systemverilog",
"svelte": "Svelte",
@@ -406,6 +441,7 @@ $code-type-map: (
"turtle": "Turtle",
"tv": "TradingView",
"twig": "Twig",
"txtpb": "Protocol Buffer Text Format",
"typescript": "TypeScript",
"typoscript": "TypoScript",
"typoscriptcssdata": "TypoScriptCssData",
@@ -413,7 +449,7 @@ $code-type-map: (
"typst": "Typst",
"udiff": "Diff",
"uxntal": "Tal",
"v": "Verilog",
"v": "V",
"vala": "Vala",
"vapi": "Vala",
"vb\\.net": "VB.net",
@@ -428,6 +464,8 @@ $code-type-map: (
"vtt": "WebVTT",
"vue": "Vue",
"vuejs": "Vue",
"wast": "WebAssembly Text Format",
"wat": "WebAssembly Text Format",
"wgsl": "WebGPU Shading Language",
"whiley": "Whiley",
"winbatch": "Batchfile",
@@ -436,13 +474,14 @@ $code-type-map: (
"xml\\+kid": "Genshi",
"xorg\\.conf": "Xorg",
"yaml": "YAML",
"yaml\\+jinja": "YAML+Jinja",
"yang": "YANG",
"z80": "Z80 Assembly",
"zed": "Zed",
"zig": "Zig",
"zone": "Dns",
"zsh": "Bash",
"🔥": "Mojo",
"µcad": "Microcad",
// Custom code type
"markdown": "Markdown",
"yml": "YAML"
+1 -1
View File
@@ -1,5 +1,5 @@
@forward 'admonition';
@forward 'alert';
@forward 'code-type';
@forward 'chroma-lexers';
@forward 'colors';
@forward 'utilities';
+2 -2
View File
@@ -11,7 +11,7 @@
/// @example
/// @include set-fi-var(custom-color, #ff0000);
@mixin set-fi-var($name, $value) {
#{$rootPrefix}#{$name}: #{$value};
#{$rootPrefix}#{$name}: $value;
}
/// Batch set CSS variables
@@ -23,7 +23,7 @@
/// ));
@mixin set-fi-vars($vars) {
@each $name, $value in $vars {
#{$rootPrefix}#{$name}: #{$value};
#{$rootPrefix}#{$name}: $value;
}
}
+3 -3
View File
@@ -205,7 +205,7 @@
@include set-fi-var(code-expand-offset, 1.875rem + 0.5rem);
}
}
.code-header {
display: flex;
justify-content: space-between;
@@ -290,10 +290,10 @@
color: fi-var(global-link-hover-color);
}
@each $type, $text in $code-type-map {
@each $type, $text in $chroma-lexers {
&.language-#{string.to-lower-case($type)} {
// e.g., --fi-code-type: "JavaScript";
@include set-fi-var(code-type, '"#{$text}"');
@include set-fi-var(code-type, $text);
}
}
+1
View File
@@ -41,6 +41,7 @@
"typecheck": "tsc --noEmit",
"version": "pnpm -F versioning start prod",
"changelog": "auto-changelog-plus --starting-date 2021-12-18",
"gen:lexers": "pnpm -F helpers start chroma-lexers",
"prepare": "simple-git-hooks"
},
"devDependencies": {
+15
View File
@@ -0,0 +1,15 @@
{
"name": "@hugo-fixit/helpers",
"private": true,
"description": "Code generation helpers for the FixIt theme",
"author": "Lruihao",
"main": "src/index.ts",
"scripts": {
"start": "tsx src/index.ts"
},
"devDependencies": {
"@hugo-fixit/shared": "workspace:*",
"consola": "^3.4.2",
"ora": "^9.4.0"
}
}
@@ -0,0 +1,5 @@
/** Custom code type entries not present in Chroma's embedded lexers. */
export const customCodeTypes: Record<string, string> = {
markdown: 'Markdown',
yml: 'YAML',
}
@@ -0,0 +1,201 @@
import { Buffer } from 'node:buffer'
import { execSync } from 'node:child_process'
import process from 'node:process'
import consola from 'consola'
import ora from 'ora'
export interface LexerEntry {
name: string
aliases: string[]
}
interface GitHubContentEntry {
name: string
type: string
sha: string
}
interface GitHubBlobResponse {
content: string
encoding: string
}
const CONCURRENCY = 10
async function getLatestChromaRef(): Promise<string> {
if (process.env.CHROMA_REF) {
return process.env.CHROMA_REF
}
try {
const res = await fetch('https://api.github.com/repos/alecthomas/chroma/releases/latest', {
headers: getHeaders(),
})
if (res.ok) {
const data = await res.json() as { tag_name: string }
return data.tag_name
}
}
catch {}
return 'v2.24.1'
}
function getGitHubToken(): string | undefined {
if (process.env.GITHUB_TOKEN) {
return process.env.GITHUB_TOKEN
}
try {
return execSync('gh auth token', { encoding: 'utf-8' }).trim()
}
catch {
return undefined
}
}
function getHeaders(): Record<string, string> {
const headers: Record<string, string> = {
'Accept': 'application/vnd.github.v3+json',
'User-Agent': '@hugo-fixit/helpers',
}
const token = getGitHubToken()
if (token) {
headers.Authorization = `Bearer ${token}`
}
return headers
}
async function fetchBlobContent(sha: string): Promise<string> {
const url = `https://api.github.com/repos/alecthomas/chroma/git/blobs/${sha}`
const res = await fetch(url, { headers: getHeaders() })
if (!res.ok) {
throw new Error(`HTTP ${res.status} for blob ${sha}`)
}
const data: GitHubBlobResponse = await res.json() as GitHubBlobResponse
if (data.encoding === 'base64') {
return Buffer.from(data.content, 'base64').toString('utf-8')
}
return data.content
}
async function fetchDirectoryListing(dir: string, ref: string): Promise<Array<{ name: string, sha: string }>> {
const url = `https://api.github.com/repos/alecthomas/chroma/contents/${dir}?ref=${ref}&per_page=300`
const res = await fetch(url, { headers: getHeaders() })
if (!res.ok) {
throw new Error(`Failed to fetch directory listing for ${dir}: HTTP ${res.status}`)
}
const entries: GitHubContentEntry[] = await res.json() as GitHubContentEntry[]
return entries
.filter(e => e.type === 'file')
.map(e => ({ name: e.name, sha: e.sha }))
}
function decodeHtmlEntities(str: string): string {
return str
.replace(/&#39;/g, '\'')
.replace(/&amp;/g, '&')
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>')
.replace(/&quot;/g, '"')
}
function parseLexerXml(xml: string): LexerEntry[] {
const nameMatch = xml.match(/<name>(.*?)<\/name>/)
if (!nameMatch)
return []
const name = decodeHtmlEntities(nameMatch[1])
const aliases: string[] = []
const aliasRegex = /<alias>(.*?)<\/alias>/g
for (let match = aliasRegex.exec(xml); match !== null; match = aliasRegex.exec(xml)) {
aliases.push(match[1])
}
return [{ name, aliases }]
}
function parseLexerGo(content: string): LexerEntry[] {
const results: LexerEntry[] = []
// Split by MustNewLexer to handle each lexer definition separately
const blocks = content.split('MustNewLexer(')
for (const block of blocks) {
const nameMatch = block.match(/Name:\s*"([^"]+)"/)
if (!nameMatch)
continue
const name = nameMatch[1]
const aliasesMatch = block.match(/Aliases:\s*\[\]string\{([^}]*)\}/)
const aliases = aliasesMatch
? aliasesMatch[1]
.split(',')
.map(s => s.trim().replace(/^"|"$/g, ''))
.filter(s => s.length > 0)
: []
results.push({ name, aliases })
}
return results
}
async function processBatch(
files: Array<{ name: string, sha: string }>,
parser: (content: string) => LexerEntry[],
): Promise<LexerEntry[]> {
const results: LexerEntry[] = []
const failed: string[] = []
await Promise.all(
files.map(async ({ name, sha }) => {
try {
const content = await fetchBlobContent(sha)
const entries = parser(content)
results.push(...entries)
}
catch {
failed.push(name)
}
}),
)
if (failed.length > 0) {
consola.warn(`Warning: failed to fetch ${failed.length} file(s): ${failed.join(', ')}`)
}
return results
}
export async function fetchLexers(): Promise<LexerEntry[]> {
const ref = await getLatestChromaRef()
consola.info(`Loading lexer definitions from chroma@${ref}`)
const spinner = ora('Fetching lexers...').start()
// Fetch XML-defined lexers from embedded/ directory
const xmlFiles = (await fetchDirectoryListing('lexers/embedded', ref))
.filter(f => f.name.endsWith('.xml'))
// Fetch Go-implemented lexers from lexers/ directory
const goFiles = (await fetchDirectoryListing('lexers', ref))
.filter(f => f.name.endsWith('.go') && !f.name.endsWith('_test.go') && f.name !== 'lexers.go')
const total = xmlFiles.length + goFiles.length
const results: LexerEntry[] = []
let fetched = 0
for (let i = 0; i < xmlFiles.length; i += CONCURRENCY) {
const batch = xmlFiles.slice(i, i + CONCURRENCY)
results.push(...await processBatch(batch, parseLexerXml))
fetched += batch.length
spinner.text = `Fetching lexers... (${fetched}/${total})`
}
for (let i = 0; i < goFiles.length; i += CONCURRENCY) {
const batch = goFiles.slice(i, i + CONCURRENCY)
results.push(...await processBatch(batch, parseLexerGo))
fetched += batch.length
spinner.text = `Fetching lexers... (${fetched}/${total})`
}
results.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()))
spinner.succeed(`Fetched ${results.length} lexers from ${xmlFiles.length} XML + ${goFiles.length} Go files.`)
return results
}
@@ -0,0 +1,56 @@
import type { LexerEntry } from './fetch-lexers'
import fs from 'node:fs'
import path from 'node:path'
import { capitalize, workspaceRoot } from '@hugo-fixit/shared'
import consola from 'consola'
import { customCodeTypes } from './custom-types'
function escapeScssAlias(alias: string): string {
// Match the old Tampermonkey script: replace . + : # with \\ + char
return alias.replace(/([+.:#])/g, '\\\\$1')
}
export function generateScss(lexers: LexerEntry[]): void {
const aliasMap = new Map<string, string>()
for (const lexer of lexers) {
for (const alias of lexer.aliases) {
if (alias.includes(' ') || alias === '')
continue
const key = alias.toLowerCase()
if (!aliasMap.has(key)) {
aliasMap.set(key, capitalize(lexer.name))
}
}
}
const sorted = [...aliasMap.entries()].sort((a, b) =>
a[0].localeCompare(b[0]),
)
const lines: string[] = [
'/// Map of code identifiers/languages to Chroma supported languages.',
'/// This file is auto-generated by `pnpm gen:lexers`, do not edit manually.',
'/// @see https://github.com/hugo-fixit/FixIt/tree/main/packages/helpers/src/chroma-lexers',
'$chroma-lexers: (',
]
for (const [alias, name] of sorted) {
lines.push(` "${escapeScssAlias(alias)}": "${name}",`)
}
lines.push(' // Custom code type')
const customEntries = Object.entries(customCodeTypes)
for (let i = 0; i < customEntries.length; i++) {
const [key, value] = customEntries[i]
const comma = i < customEntries.length - 1 ? ',' : ''
lines.push(` "${key}": "${value}"${comma}`)
}
lines.push(') !default;')
const outputPath = path.join(workspaceRoot, 'assets/scss/core/maps/_chroma-lexers.scss')
fs.writeFileSync(outputPath, `${lines.join('\n')}\n`, 'utf-8')
consola.success(`Written ${sorted.length + customEntries.length} entries to ${path.relative(workspaceRoot, outputPath)}`)
}
@@ -0,0 +1,7 @@
import { fetchLexers } from './fetch-lexers'
import { generateScss } from './generate-scss'
export default async function main() {
const lexers = await fetchLexers()
generateScss(lexers)
}
+21
View File
@@ -0,0 +1,21 @@
import process from 'node:process'
import consola from 'consola'
async function main() {
const helper = process.argv[2]
if (!helper) {
consola.error('Usage: pnpm -F helpers start <helper-name>')
process.exit(1)
}
const mod = await import(`./${helper}/index.ts`).catch(() => {
consola.error(`Unknown helper: ${helper}`)
process.exit(1)
})
if (typeof mod.default === 'function') {
await mod.default()
}
}
main()
+18
View File
@@ -0,0 +1,18 @@
{
"compilerOptions": {
"target": "ESNext",
"lib": ["ESNext"],
"module": "nodenext",
"moduleResolution": "nodenext",
"resolveJsonModule": true,
"types": ["node"],
"strict": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
+2
View File
@@ -1,3 +1,5 @@
import path from 'node:path'
export const workspaceRoot = path.resolve(__dirname, '../../../')
export * from './utils'
+3
View File
@@ -0,0 +1,3 @@
export function capitalize(str: string): string {
return str.charAt(0).toUpperCase() + str.slice(1)
}
+1
View File
@@ -0,0 +1 @@
export * from './capitalize'
+2 -1
View File
@@ -8,6 +8,7 @@
"start": "tsx src/index.ts"
},
"devDependencies": {
"@hugo-fixit/shared": "workspace:*"
"@hugo-fixit/shared": "workspace:*",
"consola": "^3.4.2"
}
}
+2 -1
View File
@@ -1,9 +1,10 @@
import process from 'node:process'
import consola from 'consola'
import { updateVersion } from './update-version'
const type: string = process.argv[2]
if (type !== 'dev' && type !== 'prod') {
console.error('Invalid argument. Please specify "dev" or "prod".')
consola.error('Invalid argument. Please specify "dev" or "prod".')
process.exit(1)
}
+11 -11
View File
@@ -1,9 +1,9 @@
/* eslint-disable no-console */
import { execFileSync, execSync } from 'node:child_process'
import fs from 'node:fs'
import { join } from 'node:path'
import process from 'node:process'
import { workspaceRoot } from '@hugo-fixit/shared'
import consola from 'consola'
const ISO_TIMESTAMP_CLEAN_RE = /[-:TZ]/g
const VERSION_PATCH_RE = /(\d+)$/
@@ -30,20 +30,20 @@ export function updateVersion(type: 'dev' | 'prod') {
]
const gitDiff: string = execSync('git diff --cached --name-only').toString().trim()
// console.log(
// 'Node.js:',
// execSync('which node').toString().trim(),
// process.version,
// )
consola.info(
'Node.js:',
execSync('which node').toString().trim(),
process.version,
)
if (type !== 'prod') {
// Avoid conflicts when creating a Pull Request
if (!['dev', 'main'].includes(branch)) {
console.log(`The current branch is ${branch}, no need to update the FixIt version.`)
consola.info(`The current branch is ${branch}, no need to update the FixIt version.`)
process.exit(0)
}
if (!match.some(item => gitDiff.includes(item))) {
console.log('No need to update the FixIt version.')
consola.info('No need to update the FixIt version.')
process.exit(0)
}
}
@@ -71,7 +71,7 @@ export function updateVersion(type: 'dev' | 'prod') {
if (lastVersion === version && gitDiff.includes('layouts/_partials/init/index.html')) {
// After running `npm version` or manually modifying the version number, skip the update
console.log(`The FixIt version has been updated to v${lastVersion}.`)
consola.info(`The FixIt version has been updated to v${lastVersion}.`)
process.exit(0)
}
@@ -81,7 +81,7 @@ export function updateVersion(type: 'dev' | 'prod') {
if (type === 'prod' && nextVersion) {
delete packageJson.nextVersion
fs.writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`)
console.log('Removed nextVersion field from package.json.')
consola.info('Removed nextVersion field from package.json.')
}
// Add the updated files to the git stage
const toStageFiles: string[] = [
@@ -96,5 +96,5 @@ export function updateVersion(type: 'dev' | 'prod') {
execFileSync('git', ['add', stageFile])
}
})
console.log(`Update the FixIt version from v${lastVersion} to v${latestVersion}.`)
consola.info(`Update the FixIt version from v${lastVersion} to v${latestVersion}.`)
}
+81
View File
@@ -43,6 +43,18 @@ importers:
apps/test: {}
packages/helpers:
devDependencies:
'@hugo-fixit/shared':
specifier: workspace:*
version: link:../shared
consola:
specifier: ^3.4.2
version: 3.4.2
ora:
specifier: ^9.4.0
version: 9.4.0
packages/integration:
devDependencies:
'@hugo-fixit/shared':
@@ -62,6 +74,9 @@ importers:
'@hugo-fixit/shared':
specifier: workspace:*
version: link:../shared
consola:
specifier: ^3.4.2
version: 3.4.2
packages:
@@ -740,6 +755,10 @@ packages:
resolution: {integrity: sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==}
engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
chalk@5.6.2:
resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==}
engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
change-case@5.4.4:
resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==}
@@ -762,6 +781,10 @@ packages:
resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==}
engines: {node: '>=18'}
cli-spinners@3.4.0:
resolution: {integrity: sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw==}
engines: {node: '>=18.20'}
cli-truncate@5.2.0:
resolution: {integrity: sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==}
engines: {node: '>=20'}
@@ -819,6 +842,10 @@ packages:
confbox@0.2.4:
resolution: {integrity: sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==}
consola@3.4.2:
resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==}
engines: {node: ^14.18.0 || >=16.10.0}
content-disposition@0.5.2:
resolution: {integrity: sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==}
engines: {node: '>= 0.6'}
@@ -1305,6 +1332,10 @@ packages:
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
engines: {node: '>=0.10.0'}
is-interactive@2.0.0:
resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==}
engines: {node: '>=12'}
is-port-reachable@4.0.0:
resolution: {integrity: sha512-9UoipoxYmSk6Xy7QFgRv2HDyaysmgSG75TFQs6S+3pDM7ZhKTF/bskZV+0UlABHzKjNVhPjYCLfeZUEg1wXxig==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
@@ -1313,6 +1344,10 @@ packages:
resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
engines: {node: '>=8'}
is-unicode-supported@2.1.0:
resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==}
engines: {node: '>=18'}
is-wsl@2.2.0:
resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==}
engines: {node: '>=8'}
@@ -1383,6 +1418,10 @@ packages:
lodash.merge@4.6.2:
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
log-symbols@7.0.1:
resolution: {integrity: sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==}
engines: {node: '>=18'}
log-update@6.1.0:
resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==}
engines: {node: '>=18'}
@@ -1618,6 +1657,10 @@ packages:
resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
engines: {node: '>= 0.8.0'}
ora@9.4.0:
resolution: {integrity: sha512-84cglkRILFxdtA8hAvLNdMrtBpPNBTrQ9/ulg0FA7xLMnD6mifv+enAIeRmvtv+WgdCE+LPGOfQmtJRrVaIVhQ==}
engines: {node: '>=20'}
p-limit@3.1.0:
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
engines: {node: '>=10'}
@@ -1831,6 +1874,10 @@ packages:
spdx-license-ids@3.0.23:
resolution: {integrity: sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==}
stdin-discarder@0.3.2:
resolution: {integrity: sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A==}
engines: {node: '>=18'}
string-argv@0.3.2:
resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==}
engines: {node: '>=0.6.19'}
@@ -2053,6 +2100,10 @@ packages:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
engines: {node: '>=10'}
yoctocolors@2.1.2:
resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==}
engines: {node: '>=18'}
zwitch@2.0.4:
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
@@ -2713,6 +2764,8 @@ snapshots:
chalk@5.0.1: {}
chalk@5.6.2: {}
change-case@5.4.4: {}
character-entities@2.0.2: {}
@@ -2729,6 +2782,8 @@ snapshots:
dependencies:
restore-cursor: 5.1.0
cli-spinners@3.4.0: {}
cli-truncate@5.2.0:
dependencies:
slice-ansi: 8.0.0
@@ -2791,6 +2846,8 @@ snapshots:
confbox@0.2.4: {}
consola@3.4.2: {}
content-disposition@0.5.2: {}
core-js-compat@3.49.0:
@@ -3304,10 +3361,14 @@ snapshots:
dependencies:
is-extglob: 2.1.1
is-interactive@2.0.0: {}
is-port-reachable@4.0.0: {}
is-stream@2.0.1: {}
is-unicode-supported@2.1.0: {}
is-wsl@2.2.0:
dependencies:
is-docker: 2.2.1
@@ -3382,6 +3443,11 @@ snapshots:
lodash.merge@4.6.2: {}
log-symbols@7.0.1:
dependencies:
is-unicode-supported: 2.1.0
yoctocolors: 2.1.2
log-update@6.1.0:
dependencies:
ansi-escapes: 7.3.0
@@ -3809,6 +3875,17 @@ snapshots:
type-check: 0.4.0
word-wrap: 1.2.5
ora@9.4.0:
dependencies:
chalk: 5.6.2
cli-cursor: 5.0.0
cli-spinners: 3.4.0
is-interactive: 2.0.0
is-unicode-supported: 2.1.0
log-symbols: 7.0.1
stdin-discarder: 0.3.2
string-width: 8.2.1
p-limit@3.1.0:
dependencies:
yocto-queue: 0.1.0
@@ -4007,6 +4084,8 @@ snapshots:
spdx-license-ids@3.0.23: {}
stdin-discarder@0.3.2: {}
string-argv@0.3.2: {}
string-width@4.2.3:
@@ -4226,4 +4305,6 @@ snapshots:
yocto-queue@0.1.0: {}
yoctocolors@2.1.2: {}
zwitch@2.0.4: {}