UNPKG

preamble

Version:

Automated License & Metadata applicators for Codebases.

197 lines (161 loc) 5.89 kB
/* ******************************************************* * preamble * * @license * * Apache-2.0 * * Copyright 2024 Alex Stevovich * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * @meta * * package_name: preamble * file_name: src/index.js * purpose: {{PURPOSE}} * * @system * * generated_on: 2025-03-09T02:08:39.988Z * certified_version: 1.0.0 * file_uuid: bb69f848-fd48-4ff3-9b5c-a992a6686166 * file_size: 5891 bytes * file_hash: 647c8710ebd7adb86e7abda1888282c54a12d2aaa3835fb6509654e9682c2492 * mast_hash: 6f79d08ffd90cdb749f3348bb8e10e369cb0ff03613b932a196fd874bf3d0945 * generated_by: preamble on npm! * * [Preamble Metadata] ********************************************************/ import path from 'path'; import { createHash, randomUUID } from 'crypto'; function isValidUUID(uuid) { return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test( uuid, ); } export function extractLicenseFields(matter) { const fields = {}; const lines = matter.split('\n'); for (const line of lines) { const trimmedLine = line.trim(); if (!trimmedLine) continue; const match = trimmedLine.match(/(?:.*\s)?([a-zA-Z0-9_]+)\s*:\s*(.*)$/); if (match) { const key = match[1].trim(); const value = match[2].trim(); fields[key] = value; } } return fields; } function hash(input) { return createHash('sha256').update(input, 'utf8').digest('hex'); } function templateLicense(license, data) { for (const [key, value] of Object.entries(data)) { const pattern = new RegExp(`{{\\s*${key.toUpperCase()}\\s*}}`, 'g'); license = license.replace(pattern, value); } return license; } /** * Extracts a block comment containing a specific identifier. * Ensures the block is properly removed and can be reinserted later. */ function extractDecoratorByContains(content, identifier) { const blockCommentRegex = /\/\*[\s\S]*?\*\//g; const shebangRegex = /^#![^\n]+\n/; // Extract shebang first before processing anything else let shebangMatch = content.match(shebangRegex); let shebang = shebangMatch ? shebangMatch[0] : null; // Remove the shebang from content before extracting the decorator let newContent = shebang ? content.replace(shebangRegex, '').trimStart() : content; // Extract block comment containing the identifier let matches = newContent.match(blockCommentRegex) || []; let decorator = null; for (let match of matches) { if (match.includes(identifier)) { decorator = match; break; } } if (!decorator) { console.error( `⚠️ Warning: No decorator containing '${identifier}' was found.`, ); return { decorator: null, content: newContent, shebang }; } // Remove the decorator block newContent = newContent.replace(decorator, '').trimStart(); return { decorator, content: newContent, shebang }; } export async function inscribeJs(content, template, data) { const preambleMarker = '[Preamble Metadata]'; const { decorator: previousLicense, content: strippedContent, shebang, } = extractDecoratorByContains(content, preambleMarker); let DATA = {}; if (previousLicense) { Object.assign(DATA, extractLicenseFields(previousLicense)); } const date = new Date(); DATA.gen_package_name = data.pkg.name || 'Unknown'; DATA.gen_package_description = data.pkg.description || 'Unknown'; // Normalize file paths const baseDir = process.cwd(); DATA.gen_file_name = path.posix.normalize( path.relative(baseDir, data.file.path).replace(/\\/g, '/'), ); DATA.gen_full_year = date.getFullYear(); if (!isValidUUID(DATA.file_uuid)) { DATA.gen_file_uuid = randomUUID(); } else { DATA.gen_file_uuid = DATA.file_uuid; } DATA.preamble_marker = preambleMarker; const oldDocHash = DATA.file_hash; const oldBlockHash = DATA.mast_hash; const newDocHash = hash(strippedContent); let docUpdated = newDocHash !== oldDocHash; template = templateLicense(template, DATA); const newBlockHash = hash(template); DATA = {}; DATA.gen_generated_on = new Date().toISOString(); DATA.gen_certified_version = data.pkg.version || '0.0.0'; DATA.gen_file_hash = newDocHash; DATA.gen_file_size = data.file.size + ' bytes'; //how can i get the version of preamble here? DATA.gen_generated_by = 'preamble on npm!'; template = templateLicense(template, DATA); let blockUpdated = newBlockHash !== oldBlockHash; DATA.gen_mast_hash = newBlockHash; const finalBlock = templateLicense(template, DATA); if (!finalBlock.includes(preambleMarker)) { throw new Error( `Masthead insertion failed: The generated masthead block does not contain the required identifier. Place {{DECORATOR}} somewhere in your template'.`, ); } let updated = docUpdated || blockUpdated; if (updated) { console.log('Result: Changes.'); } else { console.log('Result: No Changes.'); } let finalContent = `${shebang ? shebang : ''}/* ${finalBlock}*/ \n${strippedContent}`; return { result: finalContent, updated }; }