@royli/hygen
Version:
The scalable code generator that saves you time.
82 lines (71 loc) • 2.58 kB
text/typescript
import type { RenderedAction } from '../types'
import newline from '../newline'
const EOLRegex = /\r?\n/
const getPragmaticIndex = (pattern, lines, isBefore) => {
const oneLineMatchIndex = lines.findIndex((l) => l.match(pattern))
// joins the text and looks for line number,
// we dont care about platform line-endings correctness other than joining/splitting
// for all platforms
if (oneLineMatchIndex < 0) {
const fullText = lines.join('\n')
const fullMatch = fullText.match(new RegExp(pattern, 'm'))
if (fullMatch && fullMatch.length) {
if (isBefore) {
const fullTextUntilMatchStart = fullText.substring(0, fullMatch.index)
return fullTextUntilMatchStart.split(EOLRegex).length - 1
}
const matchEndIndex = fullMatch.index + fullMatch.toString().length
const fullTextUntilMatchEnd = fullText.substring(0, matchEndIndex)
return fullTextUntilMatchEnd.split(EOLRegex).length
}
}
return oneLineMatchIndex + (isBefore ? 0 : 1)
}
const locations = {
at_line: (_) => _,
prepend: (_) => 0,
append: (_, lines) => lines.length - 1,
before: (_, lines) => getPragmaticIndex(_, lines, true),
after: (_, lines) => getPragmaticIndex(_, lines, false),
}
const indexByLocation = (attributes: any, lines: string[]): number => {
const pair = Object.entries(attributes).find(([k, _]) => locations[k])
if (pair) {
const [k, v] = pair
return locations[k](v, lines)
}
return -1
}
const injector = (action: RenderedAction, content: string): string => {
const {
attributes: { skip_if, eof_last },
attributes,
body,
} = action
const shouldSkip = skip_if && !!content.match(skip_if)
if (!shouldSkip) {
//
// we care about producing platform-correct line endings.
// however the "correct" line endings should be detected from the actual
// CONTENT given, and not the underlying operating system.
// this is similar to how a text editor behaves.
//
const NL = newline(content)
const lines = content.split(NL)
// returns -1 (end) if no attrs
const idx = indexByLocation(attributes, lines)
const trimEOF = idx >= 0 && eof_last === false && /\r?\n$/.test(body)
const insertEOF = idx >= 0 && eof_last === true && !/\r?\n$/.test(body)
if (trimEOF) {
lines.splice(idx, 0, body.replace(/\r?\n$/, ''))
} else if (insertEOF) {
lines.splice(idx, 0, `${body}${NL}`)
} else if (idx >= 0) {
lines.splice(idx, 0, body)
}
return lines.join(NL)
} else {
return content
}
}
export default injector