/// import * as fs from 'node:fs' import * as path from 'node:path' import { dirname } from 'node:path' import { fileURLToPath } from 'node:url' import { flattenTree, type FlattenedToken } from './flattenTree.js' const __filename = fileURLToPath(import.meta.url) const __dirname = dirname(__filename) type TokenTree = Parameters[0] type GenerationLogger = { log(message: string): void write(): void } function createGenerationLogger(logFilePath: string): GenerationLogger { const messages = [`Token SCSS generation log`, `Generated at ${new Date().toISOString()}`, ''] return { log(message: string) { messages.push(message) }, write() { fs.writeFileSync(logFilePath, `${messages.join('\n')}\n`) }, } } function processJson(jsonData: TokenTree, prefix: string, lineFormatter: (line: string) => string, fileBaseName: string) { const flattened = flattenTree(jsonData, prefix) return flattened.map((obj: FlattenedToken) => { let { name, value, isDeprecated, comment } = obj let line = '' if (!name) { name = fileBaseName || 'root-token' } line = `${name}: ${value};` if (isDeprecated && comment) { line += ` // ${comment} DEPRECATED` } else if (isDeprecated) { line += ` // DEPRECATED` } else if (comment) { line += ` // ${comment}` } return lineFormatter(line) }) } function processJsonFile(filePath: string, targetDir: string, moduleTargetDir: string, logger: GenerationLogger) { const data = fs.readFileSync(filePath, 'utf8') const jsonData = JSON.parse(data) as TokenTree const fileBaseName = path.basename(filePath, '.json') const sourceFileName = path.basename(filePath) const scssLines = processJson(jsonData, '', (line) => '$' + line, fileBaseName) const scssFilePath = path.join(targetDir, `_${fileBaseName}.scss`) const scssHeader = `// This file is generated from ${path.basename(filePath)}` const scssContent = `${scssHeader}\n\n${scssLines.join('\n')}\n` fs.writeFileSync(scssFilePath, scssContent) logger.log(`Generated ${path.relative(targetDir, scssFilePath)} from src/${sourceFileName}`) const moduleScssFilePath = path.join(moduleTargetDir, `_${fileBaseName}.module.scss`) const moduleScssLines = processJson(jsonData, '', (line) => ' ' + line, fileBaseName) const moduleScssHeader = `// This file is generated from ${path.basename(filePath)}` const moduleScssContent = `${moduleScssHeader}\n\n:export {\n${moduleScssLines.join('\n')}\n}\n` fs.writeFileSync(moduleScssFilePath, moduleScssContent) logger.log(`Generated ${path.relative(targetDir, moduleScssFilePath)} from src/${sourceFileName}`) const combinedScssFilePath = path.join(targetDir, '_combined.scss') const combinedModuleScssFilePath = path.join(moduleTargetDir, '_combined.module.scss') if (!fs.existsSync(combinedScssFilePath)) { fs.writeFileSync(combinedScssFilePath, '') } if (!fs.existsSync(combinedModuleScssFilePath)) { fs.writeFileSync(combinedModuleScssFilePath, '') } const combinedScssContent = fs.readFileSync(combinedScssFilePath, 'utf8') const newHeader = `\n// This part is generated from ${path.basename(filePath)}\n` const newContent = processJson(jsonData, '', (line) => '$' + fileBaseName + '-' + line, fileBaseName).join('\n') + '\n' fs.writeFileSync(combinedScssFilePath, combinedScssContent + newHeader + newContent) logger.log(`Added src/${sourceFileName} tokens to ${path.relative(targetDir, combinedScssFilePath)}`) const newModuleContent = processJson(jsonData, '', (line) => ' ' + fileBaseName + '-' + line, fileBaseName).join('\n') + '\n' const combinedModuleScssContent = fs.readFileSync(combinedModuleScssFilePath, 'utf8') const updatedModuleScssContent = combinedModuleScssContent.replace('}\n', newModuleContent + '}\n') fs.writeFileSync(combinedModuleScssFilePath, updatedModuleScssContent) logger.log(`Added src/${sourceFileName} tokens to ${path.relative(targetDir, combinedModuleScssFilePath)}`) } function main() { const dir = path.join(__dirname, '../src') const targetDir = path.join(__dirname, '../') const moduleTargetDir = path.join(targetDir, 'generated-modules') const logFilePath = path.join(targetDir, 'generation.log') const logger = createGenerationLogger(logFilePath) fs.mkdirSync(moduleTargetDir, { recursive: true }) const combinedScssFilePath = path.join(targetDir, '_combined.scss') fs.writeFileSync(combinedScssFilePath, '') const combinedModuleScssFilePath = path.join(moduleTargetDir, '_combined.module.scss') fs.writeFileSync(combinedModuleScssFilePath, ':export {\n}\n') const tokenFiles = fs.readdirSync(dir).filter((file) => path.extname(file) === '.json') tokenFiles.forEach((file) => processJsonFile(path.join(dir, file), targetDir, moduleTargetDir, logger)) logger.write() console.log('\x1b[32m', `Generated ${tokenFiles.length} token SCSS partials and module files. Details: generation.log`, '\x1b[0m') } main()