// img-src-placeholder ~~ MIT License // // Usage in package.json: // "scripts": { // "stage-web": "img-src-placeholder src/web build/website", // }, // // Usage from command line: // $ npm install --save-dev img-src-placeholder // $ npx img-src-placeholder src/web docs --quiet // // Contributors to this project: // $ cd img-src-placeholder // $ npm install // $ npm test // $ node bin/cli.js --cd=spec fixtures target/cd --summary // Imports import { cliArgvUtil } from 'cli-argv-util'; import { replacer, Results, ResultsFile } from 'replacer-util'; import chalk from 'chalk'; import fs from 'node:fs'; import log from 'fancy-log'; import path from 'node:path'; // Types export type Settings = { cd: string | null, //change working directory before starting search extensions: string[], //filter files by file extensions, example: ['.html'] filename: string | null, //single file in the source folder to be processed }; export type ReporterSettings = { summaryOnly: boolean, //only print out the single line summary message }; const imgSrcPlaceholder = { version: '{{package.version}}', htmlExts: ['.html', '.htm', '.php', '.aspx', '.asp', '.jsp'], assertOk(ok: unknown, message: string | null) { if (!ok) throw new Error(`[img-src-placeholder] ${message}`); }, cli() { const validFlags = ['cd', 'ext', 'note', 'quiet', 'summary']; const cli = cliArgvUtil.parse(validFlags); const source = cli.params[0]; //origin file or folder const target = cli.params[1]; //destination folder const error = cli.invalidFlag ? cli.invalidFlagMsg : !source ? 'Missing source folder.' : !target ? 'Missing target folder.' : cli.paramCount > 2 ? 'Extraneous parameter: ' + cli.params[2]! : null; imgSrcPlaceholder.assertOk(!error, error); const sourceFile = path.join(cli.flagMap.cd ?? '', source!); const isFile = fs.existsSync(sourceFile) && fs.statSync(sourceFile).isFile(); const sourceFolder = isFile ? path.dirname(source!) : source; const options: Settings = { cd: cli.flagMap.cd ?? null, extensions: cli.flagMap.ext?.split(',') ?? [], filename: isFile ? path.basename(source!) : null, }; const results = imgSrcPlaceholder.transform(sourceFolder!, target!, options); if (!cli.flagOn.quiet) imgSrcPlaceholder.reporter(results, { summaryOnly: cli.flagOn.summary! }); }, transform(sourceFolder: string, targetFolder: string, options?: Partial): Results { const defaults: Settings = { cd: null, extensions: [], filename: null, }; const settings = { ...defaults, ...options }; const onePixelSvg = ''; const dataImage = 'data:image/svg+xml;base64,' + Buffer.from(onePixelSvg).toString('base64'); const replacerSettings = { cd: settings.cd!, extensions: settings.extensions.length ? settings.extensions : imgSrcPlaceholder.htmlExts, regex: /src=["']?#["']?/gm, replacement: `src="${dataImage}"`, }; return replacer.transform(sourceFolder, targetFolder, replacerSettings); }, reporter(results: Results, options?: Partial): Results { const defaults: ReporterSettings = { summaryOnly: false, }; const settings = { ...defaults, ...options }; const name = chalk.gray('img-src-placeholder'); const version = chalk.gray('v' + imgSrcPlaceholder.version); const infoColor = results.count ? chalk.white : chalk.red.bold; const info = infoColor(`(files: ${results.count}, ${results.duration}ms)`); log(name, version, results.source, info); const logFile = (file: ResultsFile, index: number) => log(name, chalk.magenta(index + 1), cliArgvUtil.colorizePath(file.destPath)); const logSingleFile = (file: ResultsFile) => log(name, cliArgvUtil.colorizePath(file.destPath)); if (!settings.summaryOnly) results.files.forEach(results.count > 1 ? logFile : logSingleFile); return results; }, }; export { imgSrcPlaceholder };