mirror of
https://github.com/johndoe6345789/low-code-react-app-b.git
synced 2026-04-24 13:44:54 +00:00
Add supported component validation check
This commit is contained in:
9
Jenkinsfile
vendored
9
Jenkinsfile
vendored
@@ -68,6 +68,15 @@ pipeline {
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Component Registry Check') {
|
||||
steps {
|
||||
script {
|
||||
nodejs(nodeJSInstallationName: "Node ${NODE_VERSION}") {
|
||||
sh 'npm run components:validate'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,8 @@
|
||||
"pages:validate": "tsx src/config/validate-config.ts",
|
||||
"pages:generate": "node scripts/generate-page.js",
|
||||
"components:list": "node scripts/list-json-components.cjs",
|
||||
"components:scan": "node scripts/scan-and-update-registry.cjs"
|
||||
"components:scan": "node scripts/scan-and-update-registry.cjs",
|
||||
"components:validate": "node scripts/validate-supported-components.cjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"@heroicons/react": "^2.2.0",
|
||||
|
||||
182
scripts/validate-supported-components.cjs
Normal file
182
scripts/validate-supported-components.cjs
Normal file
@@ -0,0 +1,182 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
const rootDir = path.resolve(__dirname, '..')
|
||||
const registryPath = path.join(rootDir, 'json-components-registry.json')
|
||||
const definitionsPath = path.join(rootDir, 'src/lib/component-definitions.json')
|
||||
const componentTypesPath = path.join(rootDir, 'src/types/json-ui.ts')
|
||||
const uiRegistryPath = path.join(rootDir, 'src/lib/json-ui/component-registry.ts')
|
||||
const atomIndexPath = path.join(rootDir, 'src/components/atoms/index.ts')
|
||||
const moleculeIndexPath = path.join(rootDir, 'src/components/molecules/index.ts')
|
||||
|
||||
const readJson = (filePath) => JSON.parse(fs.readFileSync(filePath, 'utf8'))
|
||||
const readText = (filePath) => fs.readFileSync(filePath, 'utf8')
|
||||
|
||||
const registryData = readJson(registryPath)
|
||||
const supportedComponents = (registryData.components ?? []).filter(
|
||||
(component) => component.status === 'supported'
|
||||
)
|
||||
|
||||
const componentDefinitions = readJson(definitionsPath)
|
||||
const definitionTypes = new Set(componentDefinitions.map((def) => def.type))
|
||||
|
||||
const componentTypesContent = readText(componentTypesPath)
|
||||
const componentTypesStart = componentTypesContent.indexOf('export type ComponentType')
|
||||
const componentTypesEnd = componentTypesContent.indexOf('export type ActionType')
|
||||
if (componentTypesStart === -1 || componentTypesEnd === -1) {
|
||||
throw new Error('Unable to locate ComponentType union in src/types/json-ui.ts')
|
||||
}
|
||||
const componentTypesBlock = componentTypesContent.slice(componentTypesStart, componentTypesEnd)
|
||||
const componentTypeSet = new Set()
|
||||
const componentTypeRegex = /'([^']+)'/g
|
||||
let match
|
||||
while ((match = componentTypeRegex.exec(componentTypesBlock)) !== null) {
|
||||
componentTypeSet.add(match[1])
|
||||
}
|
||||
|
||||
const extractObjectLiteral = (content, marker) => {
|
||||
const markerIndex = content.indexOf(marker)
|
||||
if (markerIndex === -1) {
|
||||
throw new Error(`Unable to locate ${marker} in component registry file`)
|
||||
}
|
||||
const braceStart = content.indexOf('{', markerIndex)
|
||||
if (braceStart === -1) {
|
||||
throw new Error(`Unable to locate opening brace for ${marker}`)
|
||||
}
|
||||
let depth = 0
|
||||
for (let i = braceStart; i < content.length; i += 1) {
|
||||
const char = content[i]
|
||||
if (char === '{') depth += 1
|
||||
if (char === '}') depth -= 1
|
||||
if (depth === 0) {
|
||||
return content.slice(braceStart, i + 1)
|
||||
}
|
||||
}
|
||||
throw new Error(`Unable to locate closing brace for ${marker}`)
|
||||
}
|
||||
|
||||
const extractKeysFromObjectLiteral = (literal) => {
|
||||
const body = literal.trim().replace(/^\{/, '').replace(/\}$/, '')
|
||||
const entries = body
|
||||
.split(',')
|
||||
.map((entry) => entry.trim())
|
||||
.filter(Boolean)
|
||||
const keys = new Set()
|
||||
|
||||
entries.forEach((entry) => {
|
||||
if (entry.startsWith('...')) {
|
||||
return
|
||||
}
|
||||
const [keyPart] = entry.split(':')
|
||||
const key = keyPart.trim()
|
||||
if (key) {
|
||||
keys.add(key)
|
||||
}
|
||||
})
|
||||
|
||||
return keys
|
||||
}
|
||||
|
||||
const uiRegistryContent = readText(uiRegistryPath)
|
||||
const primitiveKeys = extractKeysFromObjectLiteral(
|
||||
extractObjectLiteral(uiRegistryContent, 'export const primitiveComponents')
|
||||
)
|
||||
const shadcnKeys = extractKeysFromObjectLiteral(
|
||||
extractObjectLiteral(uiRegistryContent, 'export const shadcnComponents')
|
||||
)
|
||||
const wrapperKeys = extractKeysFromObjectLiteral(
|
||||
extractObjectLiteral(uiRegistryContent, 'export const jsonWrapperComponents')
|
||||
)
|
||||
const iconKeys = extractKeysFromObjectLiteral(
|
||||
extractObjectLiteral(uiRegistryContent, 'export const iconComponents')
|
||||
)
|
||||
|
||||
const extractExports = (content) => {
|
||||
const exportsSet = new Set()
|
||||
const exportRegex = /export\s+\{([^}]+)\}\s+from/g
|
||||
let exportMatch
|
||||
while ((exportMatch = exportRegex.exec(content)) !== null) {
|
||||
const names = exportMatch[1]
|
||||
.split(',')
|
||||
.map((name) => name.trim())
|
||||
.filter(Boolean)
|
||||
names.forEach((name) => {
|
||||
const [exportName] = name.split(/\s+as\s+/)
|
||||
if (exportName) {
|
||||
exportsSet.add(exportName.trim())
|
||||
}
|
||||
})
|
||||
}
|
||||
return exportsSet
|
||||
}
|
||||
|
||||
const atomExports = extractExports(readText(atomIndexPath))
|
||||
const moleculeExports = extractExports(readText(moleculeIndexPath))
|
||||
|
||||
const uiRegistryKeys = new Set([
|
||||
...primitiveKeys,
|
||||
...shadcnKeys,
|
||||
...wrapperKeys,
|
||||
...iconKeys,
|
||||
...atomExports,
|
||||
...moleculeExports,
|
||||
])
|
||||
|
||||
const missingInTypes = []
|
||||
const missingInDefinitions = []
|
||||
const missingInRegistry = []
|
||||
|
||||
supportedComponents.forEach((component) => {
|
||||
const typeName = component.type ?? component.name ?? component.export
|
||||
const registryName = component.export ?? component.name ?? component.type
|
||||
|
||||
if (!typeName) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!componentTypeSet.has(typeName)) {
|
||||
missingInTypes.push(typeName)
|
||||
}
|
||||
|
||||
if (!definitionTypes.has(typeName)) {
|
||||
missingInDefinitions.push(typeName)
|
||||
}
|
||||
|
||||
const source = component.source ?? 'unknown'
|
||||
let registryHasComponent = uiRegistryKeys.has(registryName)
|
||||
|
||||
if (source === 'atoms') {
|
||||
registryHasComponent = atomExports.has(registryName)
|
||||
}
|
||||
if (source === 'molecules') {
|
||||
registryHasComponent = moleculeExports.has(registryName)
|
||||
}
|
||||
if (source === 'ui') {
|
||||
registryHasComponent = shadcnKeys.has(registryName)
|
||||
}
|
||||
|
||||
if (!registryHasComponent) {
|
||||
missingInRegistry.push(`${registryName} (${source})`)
|
||||
}
|
||||
})
|
||||
|
||||
const unique = (list) => Array.from(new Set(list)).sort()
|
||||
|
||||
const errors = []
|
||||
if (missingInTypes.length > 0) {
|
||||
errors.push(`Missing in ComponentType union: ${unique(missingInTypes).join(', ')}`)
|
||||
}
|
||||
if (missingInDefinitions.length > 0) {
|
||||
errors.push(`Missing in component definitions: ${unique(missingInDefinitions).join(', ')}`)
|
||||
}
|
||||
if (missingInRegistry.length > 0) {
|
||||
errors.push(`Missing in UI registry mapping: ${unique(missingInRegistry).join(', ')}`)
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
console.error('Supported component validation failed:')
|
||||
errors.forEach((error) => console.error(`- ${error}`))
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
console.log('Supported component validation passed.')
|
||||
Reference in New Issue
Block a user