chore: structure refactor, linting, and monorepo setup (#629)

* chore: use monorepo to manage demo, test and other applications and internal packages

* style: add eslint support and lint code

* chore: refactor project structure and add versioning package replace of fixit-releaser

* chore(deps-dev): replace husky with simple-git-hooks

* Potential fix for code scanning alert no. 13: Shell command built from environment values

* chore(deps-dev): update the final fixit-releaser version

* docs: update contributing guidelines for new structure and commands

---------

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
This commit is contained in:
Cell
2025-08-31 15:07:00 +08:00
committed by GitHub
parent c923116b9b
commit d8624386c7
43 changed files with 3721 additions and 304 deletions
+15
View File
@@ -0,0 +1,15 @@
{
"name": "@hugo-fixit/integration",
"version": "0.1.0",
"private": true,
"description": "Integration utilities for the FixIt theme",
"author": "Lruihao",
"scripts": {
"start": "tsx src/index.ts"
},
"devDependencies": {
"@hugo-fixit/shared": "workspace:*",
"@types/fs-extra": "^11.0.4",
"fs-extra": "^11.3.1"
}
}
+9
View File
@@ -0,0 +1,9 @@
import path from 'node:path'
import { workspaceRoot } from '@hugo-fixit/shared'
import fsExtra from 'fs-extra'
const { copySync, removeSync } = fsExtra
removeSync(path.join(workspaceRoot, 'public'))
copySync(path.join(workspaceRoot, 'apps/demo/public'), path.join(workspaceRoot, 'public'), { overwrite: true })
copySync(path.join(workspaceRoot, 'apps/test/public'), path.join(workspaceRoot, 'public/test'), { overwrite: true })
+20
View File
@@ -0,0 +1,20 @@
{
"compilerOptions": {
"target": "ESNext",
"jsx": "preserve",
"lib": ["DOM", "ESNext"],
"baseUrl": ".",
"module": "ESNext",
"moduleResolution": "node",
"resolveJsonModule": true,
"types": ["node"],
"strict": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
+8
View File
@@ -0,0 +1,8 @@
{
"name": "@hugo-fixit/shared",
"version": "0.1.0",
"private": true,
"description": "Internal utilities shared across @hugo-fixit packages",
"author": "Lruihao",
"main": "src/index.ts"
}
+3
View File
@@ -0,0 +1,3 @@
import path from 'node:path'
export const workspaceRoot = path.resolve(__dirname, '../../../')
+20
View File
@@ -0,0 +1,20 @@
{
"compilerOptions": {
"target": "ESNext",
"jsx": "preserve",
"lib": ["DOM", "ESNext"],
"baseUrl": ".",
"module": "ESNext",
"moduleResolution": "node",
"resolveJsonModule": true,
"types": ["node"],
"strict": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
+13
View File
@@ -0,0 +1,13 @@
{
"name": "@hugo-fixit/versioning",
"private": true,
"description": "Versioning utilities for the FixIt theme",
"author": "Lruihao",
"main": "src/index.ts",
"scripts": {
"start": "tsx src/index.ts"
},
"devDependencies": {
"@hugo-fixit/shared": "workspace:*"
}
}
+10
View File
@@ -0,0 +1,10 @@
import process from 'node:process'
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".')
process.exit(1)
}
updateVersion(type)
+78
View File
@@ -0,0 +1,78 @@
/* eslint-disable no-console */
import { execSync, execFileSync } 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'
/**
* Update the version of the FixIt
* @param type version type
*/
export function updateVersion(type: 'dev' | 'prod') {
const branch: string = execSync('git rev-parse --abbrev-ref HEAD').toString().trim()
const match: string[] = [
'archetypes/',
'assets/',
'i18n/',
'layouts/',
'static/',
'go.mod',
'hugo.toml',
'package.json',
'package-lock.json',
'pnpm-lock.yaml',
'theme.toml',
]
const gitDiff: string = execSync('git diff --cached --name-only').toString().trim()
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.`)
process.exit(0)
}
if (!match.some(item => gitDiff.includes(item))) {
console.log('No need to update the FixIt version.')
process.exit(0)
}
}
const initHtmlPath: string = join(workspaceRoot, 'layouts/_partials/init/index.html')
const packageJsonPath: string = join(workspaceRoot, 'package.json')
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'))
const version: string = packageJson.version
// Get the short hash of the last commit (can not get this commit hash at pre-commit hook)
const shortHash: string = execSync('git rev-parse --short HEAD').toString().trim()
// Build the development version v{major}.{minor}.{patch+1}-{timestamp}-{shortHash}
// e.g. v0.3.21-20250702061540-abcdefg
const timestamp: string = new Date().toISOString().replace(/[-:TZ]/g, '').slice(0, -4)
const devVersion: string = `${version.replace(/(\d+)$/, (match, part) => (Number.parseInt(part) + 1).toString())}-${timestamp}-${shortHash}`
const initHtml: string = fs.readFileSync(initHtmlPath, 'utf8')
const latestVersion: string = type === 'prod' ? version : devVersion
const versionRegex: RegExp = /v\d+\.\d+\.\d+(-[\w.\-]+)?/
const lastVersion: string = initHtml.match(versionRegex)![0].slice(1)
const newInitHtml: string = initHtml.replace(versionRegex, `v${latestVersion}`)
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}.`)
process.exit(0)
}
// Update the version number in layouts/_partials/init/index.html
fs.writeFileSync(initHtmlPath, newInitHtml)
// Add the updated files to the git stage
const toStageFiles: string[] = [
'layouts/_partials/init/index.html',
'package.json',
'package-lock.json',
'pnpm-lock.yaml',
]
toStageFiles.forEach((file) => {
const stageFile = join(workspaceRoot, file)
if (fs.existsSync(stageFile)) {
execFileSync('git', ['add', stageFile])
}
})
console.log(`Update the FixIt version from v${lastVersion} to v${latestVersion}.`)
}
+20
View File
@@ -0,0 +1,20 @@
{
"compilerOptions": {
"target": "ESNext",
"jsx": "preserve",
"lib": ["DOM", "ESNext"],
"baseUrl": ".",
"module": "ESNext",
"moduleResolution": "node",
"resolveJsonModule": true,
"types": ["node"],
"strict": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}