All files / lib/env renderer.ts

100% Statements 51/51
94.44% Branches 17/18
100% Functions 10/10
100% Lines 42/42

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 821x                     1x     1x 12x   1x 12x 12x 7x   5x 5x   2x     1x 7x 7x   7x   3x   1x 7x 7x   6x 6x 2x 2x 2x   1x     4x     1x 1x         1x     1x 5x 5x   5x 5x 5x   5x     1x 5x 5x   2x     2x 2x  
import {
  DocumentNode,
  StatementNode,
  NodeType,
  VariableDeclarationNode,
  LiteralNode,
  RawLiteralNode,
  QuotedLiteralNode,
  CommentNode,
  QuoteType,
} from "lib/env/parser";
import { NewlineType, Options } from "lib/options";
 
export type Render = typeof render
export const render = (abstractSyntaxTree: DocumentNode, options: Options): string =>
  abstractSyntaxTree.statements.map(node => renderStatement(node, options)).join("");
 
const renderStatement = (node: StatementNode, options: Options) => {
  const isVariableDeclaration = node.type === NodeType.variableDeclaration;
  if (isVariableDeclaration)
    return renderVariableDeclaration(node as VariableDeclarationNode);
 
  const isNewline = node.type === NodeType.newline;
  if (isNewline) return renderNewline(options);
 
  return renderComment(node as CommentNode);
};
 
const renderVariableDeclaration = ({
  identifier,
  value,
}: VariableDeclarationNode): string =>
  `${identifier.name}=${renderLiteral(value)}`;
 
const renderNewline = ({ newlineType }: Options) => newlineType === NewlineType.windows ? '\r\n' : '\n'
 
const renderLiteral = (literal?: LiteralNode): string => {
  const isEmpty = !literal;
  if (isEmpty) return "";
 
  const isRawLiteral = literal.type === NodeType.literal;
  if (isRawLiteral) {
    const { value } = (literal as RawLiteralNode)
    const hasEscapeChars = /\\./.test(value)
    if (hasEscapeChars) return renderEscapedRawLiteral(literal as RawLiteralNode)
 
    return value
  }
 
  return renderQuotedLiteral(literal as QuotedLiteralNode);
};
 
const renderEscapedRawLiteral = (escapedRawLiteral: RawLiteralNode): string => {
  const quotedLiteral: QuotedLiteralNode = {
    type: NodeType.quotedLiteral,
    quoteType: QuoteType.double,
    content: escapedRawLiteral as RawLiteralNode
  }
  return renderQuotedLiteral(quotedLiteral as QuotedLiteralNode);
}
 
const renderQuotedLiteral = ({
  quoteType,
  content,
}: QuotedLiteralNode): string => {
    const isEmpty = !content
    const value = isEmpty ? '' : content.value
    const escapedValue = escapeQuotes(quoteType, value)
 
    return `${quoteType}${escapedValue}${quoteType}`;
}
 
const escapeQuotes = (quoteType: QuoteType, content: string): string => {
  const isDoubleQuotes = quoteType === QuoteType.double
  if (isDoubleQuotes) return content.replace(/"/g, '\\"')
 
  return content.replace(/'/g, "\\'")
}
 
const renderComment = ({ body }: CommentNode): string =>
  `#${!!body ? body : ""}`;