UNPKG

add-dist-header

Version:

Prepend a one-line banner comment (with license notice) to distribution files

94 lines (92 loc) 4.39 kB
//! add-dist-header v1.5.1 ~~ https://github.com/center-key/add-dist-header ~~ MIT License import { EOL } from 'node:os'; import { isBinary } from 'istextorbinary'; import chalk from 'chalk'; import fs from 'fs'; import log from 'fancy-log'; import path from 'path'; import slash from 'slash'; const addDistHeader = { prepend(filename, options) { const defaults = { allFiles: false, dist: 'dist', extension: null, delimiter: '~~', replaceComment: true, setVersion: true, }; const settings = { ...defaults, ...options }; if (!filename) throw new Error('[add-dist-header] Must specify the "filename" option.'); const commentStyle = { js: { start: '//! ', end: '' }, ml: { start: '<!-- ', end: ' -->' }, other: { start: '/*! ', end: ' */' }, }; const firstLine = { js: /^(\/\/[^!].*|\/[*][^!].*[*]\/)/, ml: /^<!--.*-->/, other: /^\/[*][^!].*[*]\//, }; const pkg = JSON.parse(fs.readFileSync('package.json', 'utf-8')); const inputFile = path.parse(filename); const fileExt = settings.extension ?? inputFile.ext; const jsStyle = /\.(js|ts|cjs|mjs|less)$/.test(fileExt); const mlStyle = /\.(html|htm|sgml|xml|php)$/.test(fileExt); const type = jsStyle ? 'js' : mlStyle ? 'ml' : 'other'; const doctypePattern = /^<(!doctype|\?xml).*/i; const versionPattern = /{{package\.version}}/g; const isTextFile = !isBinary(filename); const input = fs.readFileSync(filename, 'utf-8').trim(); const out1 = settings.replaceComment ? input.replace(firstLine[type], '').trim() : input; const out2 = mlStyle ? out1.replace(doctypePattern, '').trim() : out1; const out3 = settings.setVersion ? out2.replace(versionPattern, pkg.version) : out2; const doctypeLine = out1.match(doctypePattern)?.[0]; const doctype = mlStyle && doctypeLine ? doctypeLine + EOL : ''; const info = pkg.homepage ?? pkg.docs ?? pkg.repository; const unlicensed = !pkg.license || pkg.license === 'UNLICENSED'; const license = unlicensed ? 'All Rights Reserved' : `${pkg.license} License`; const delimiter = ' ' + settings.delimiter + ' '; const banner = [`${pkg.name} v${pkg.version}`, info, license].join(delimiter); const header = commentStyle[type].start + banner + commentStyle[type].end; const fixedDigits = { minimumFractionDigits: 2, maximumFractionDigits: 2 }; const distFolder = fs.mkdirSync(settings.dist, { recursive: true }) ?? settings.dist; const formatOptions = { dir: settings.dist, name: inputFile.name, ext: fileExt }; const outputPath = slash(path.format(formatOptions)); const numLines = input.match(/^/gm).length; const isMinified = outputPath.includes('.min.') || (numLines < 5 && fileExt !== '.ts'); const spacerLines = EOL.repeat(isMinified || mlStyle ? 1 : 2); const platformEol = (text) => text.replace(/\r?\n/g, EOL); const final = platformEol(doctype + header + spacerLines + out3 + EOL); if (isTextFile) fs.writeFileSync(outputPath, final); else if (settings.allFiles) fs.copyFileSync(filename, outputPath); return { valid: isTextFile || settings.allFiles, text: isTextFile, dist: distFolder, header: isTextFile ? header : null, source: slash(filename), file: outputPath, length: isTextFile ? final.length : null, size: isTextFile ? (final.length / 1024).toLocaleString([], fixedDigits) + ' KB' : null, }; }, reporter(result, options) { const defaults = { quiet: false, }; const settings = { ...defaults, ...options }; const name = chalk.gray('add-dist-header'); const arrow = chalk.gray.bold('→'); const source = chalk.blue.bold(result.source); const target = chalk.magenta(result.file); const size = chalk.white('(' + (result.size || 'binary') + ')'); if (!settings.quiet && result.valid) log(name, source, arrow, target, size); return result; }, }; export { addDistHeader };