Last active
April 14, 2024 05:28
-
-
Save svallory/a06232775829045ef7c80323ff89403f to your computer and use it in GitHub Desktop.
My utilities
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { expandToChalkString } from './template-strings.ts'; | |
const exit = (error: string, code: number = -1) => { | |
console.info(eval(`expandToChalkString\`${error}\``)); | |
process.exit(code); | |
} | |
import type { ForegroundColorName } from 'chalk'; | |
const info = (msg: string, color: ForegroundColorName = 'yellow') => { | |
console.info(eval(`expandToChalkString\`{${color} ${msg}}\``)); | |
} | |
// OR | |
const info = (staticParts: TemplateStringsArray, ...substitutions: unknown[]) => { | |
console.info(expandToChalkString(staticParts, ...substitutions)); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/****************************************************************************** | |
* Copyright 2021 TypeFox GmbH | |
* This program and the accompanying materials are made available under the | |
* terms of the MIT License, which is available in the project root. | |
******************************************************************************/ | |
import { chalkTemplateStderr } from 'chalk-template'; | |
export const NEWLINE_REGEXP = /\r?\n/gm; | |
export const EOL = (typeof process === 'undefined') ? '\n' : (process.platform === 'win32') ? '\r\n' : '\n'; | |
export const SNLE = Object.freeze('__«SKIP^NEW^LINE^IF^EMPTY»__'); | |
export const nonWhitespace = /\S|$/; | |
/** | |
* A tag function that automatically aligns embedded multiline strings. | |
* Multiple lines are joined with the platform-specific line separator. | |
* | |
* @param staticParts the static parts of a tagged template literal | |
* @param substitutions the variable parts of a tagged template literal | |
* @returns an aligned string that consists of the given parts | |
*/ | |
export function expandToString(staticParts: TemplateStringsArray, ...substitutions: unknown[]): string { | |
return internalExpandToString(EOL, staticParts, ...substitutions); | |
} | |
/** | |
* The same as {@link expandToString} but with support for coloring | |
* via the `chalk-template` package. | |
* | |
* A tag function that automatically aligns embedded multiline strings. | |
* Multiple lines are joined with the platform-specific line separator. | |
* | |
* | |
* | |
* @param staticParts the static parts of a tagged template literal | |
* @param substitutions the variable parts of a tagged template literal | |
* @returns an aligned string that consists of the given parts | |
*/ | |
export function expandToChalkString(staticParts: TemplateStringsArray, ...substitutions: unknown[]): string { | |
return expandToString`${chalkTemplateStderr(staticParts, ...substitutions)}`; | |
} | |
function internalExpandToString(lineSep: string, staticParts: TemplateStringsArray, ...substitutions: unknown[]): string { | |
let lines = substitutions | |
// align substitutions and fuse them with static parts | |
.reduce((acc: string, subst: unknown, i: number) => acc + (subst === undefined ? SNLE : align(String(subst), acc)) + (staticParts[i + 1] ?? ''), staticParts[0]) | |
// converts text to lines | |
.split(NEWLINE_REGEXP) | |
.filter(l => l.trim() !== SNLE) | |
// whitespace-only lines are empty (preserving leading whitespace) | |
.map(l => l.replace(SNLE, '').trimEnd()); | |
// in order to nicely handle single line templates with the leading and trailing termintators (``) on separate lines, like | |
// expandToString`foo | |
// `, | |
// expandToString` | |
// foo | |
// `, | |
// expandToString` | |
// foo`, | |
// the same way as true single line templates like | |
// expandToString`foo` | |
// ... | |
// ... drop initial linebreak if the first line is empty or contains white space only, ... | |
const containsLeadingLinebreak = lines.length > 1 && lines[0].trim().length === 0; | |
lines = containsLeadingLinebreak ? lines.slice(1) : lines; | |
// .. and drop the last linebreak if it's the last charactor or is followed by white space | |
const containsTrailingLinebreak = lines.length !== 0 && lines[lines.length-1].trimEnd().length === 0; | |
lines = containsTrailingLinebreak ? lines.slice(0, lines.length-1) : lines; | |
// finds the minimum indentation | |
const indent = findIndentation(lines); | |
return lines | |
// shifts lines to the left | |
.map(line => line.slice(indent).trimEnd()) | |
// convert lines to string | |
.join(lineSep); | |
} | |
// add the alignment of the previous static part to all lines of the following substitution | |
function align(subst: string, acc: string): string { | |
const length = Math.max(0, acc.length - acc.lastIndexOf('\n') - 1); | |
const indent = ' '.repeat(length); | |
return subst.replace(NEWLINE_REGEXP, EOL + indent); | |
} | |
// finds the indentation of a text block represented by a sequence of lines | |
export function findIndentation(lines: string[]): number { | |
const indents = lines.filter(line => line.length > 0).map(line => line.search(nonWhitespace)); | |
const min = indents.length === 0 ? 0 : Math.min(...indents); // min(...[]) = min() = Infinity | |
return Math.max(0, min); | |
} | |
export function normalizeEOL(input: string): string { | |
return input.replace(NEWLINE_REGEXP, EOL); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment