Skip to content

Instantly share code, notes, and snippets.

@derekmc
Last active March 30, 2025 15:13
Show Gist options
  • Save derekmc/60f171ffce8516ed6845595ef095a72f to your computer and use it in GitHub Desktop.
Save derekmc/60f171ffce8516ed6845595ef095a72f to your computer and use it in GitHub Desktop.

Multifile Archive Syntax

File names start with 4 equal signs, then an optional space. Optionally, they can have equal signs at the end of the line. You escape a line by adding an extra equals to all lines that begin with 4 equals signs. An empty filename is used for comments

testMultidoc()
function writeMultidoc(x){
  let s = ""
  if("" in x){
    s += escapeEquals(x[""])
  }
  for(let k in x){
    if(k == "") continue
    if(s.length) s += "\n"
    s += `==== ${k} ====\n`
    s += escapeEquals(x[k])
  }
  return s
  function escapeEquals(s){
    let lines = s.split("\n")
    for(let i=0; i<lines.length; ++i){
      if(lines[i].indexOf("====") == 0){
        lines[i] = "=" + lines[i]
      }
    }
    return lines.join("\n")
  }
}
function readMultidoc(s){
  let docs = {}
  let lines = s.split("\n")
  let filename = ""
  docs[""] = ""

  for(let i=0; i<lines.length; ++i){
    let line = lines[i]
    if(line.indexOf("====") == 0){
      if(line.indexOf("=====") == 0){ // escaped line
        line = line.substring(1)
      }else{
        while(line.length > 4 && line[line.length-1] == "="){
          line = line.substring(0, line.length - 1)
        }
        line = line.substring(4).trim()
        filename = line
        console.log('newfile', filename)
        if(!docs.hasOwnProperty(filename)){
          docs[filename] = ""
        }
        continue
      }
    }
    if(docs[filename].length > 0){
      docs[filename] += "\n"
    }
    docs[filename] += line
  }
  return docs
}
function trimlines(parts, args){ // tagged template literal
  let tabindent = 4
  let s = ""
  for(let i=0; i<parts.length; ++i){
    s += parts[i]
    if(i < args.length) s += args[i]
  }
  let lines = s.split('\n')
  let minindent = 1000*1000
  for(let i=0; i<lines.length; ++i){
    let line = lines[i]
    let indent = 0
    for(let j=0; j<line.length; ++j){
      let c = line[j]
      if(c == '\t'){ indent += tabindent }
      else if(c == ' '){ ++indent }
      else{
        minindent = Math.min(indent, minindent)
        break
      }
    }
  }
  let result = ""
  for(let i=0; i<lines.length; ++i){
    let line = lines[i]
    if(result.length > 0) result += "\n"
    result += line.substring(minindent)
  }
  return result
}
function testMultidoc(){
  let src = trimlines`
    Here is a demo of a multifile archive
    ==== hello.html ====
    <${''}!doctype html>
    <${''}html>
      <${''}head>
        <${''}title> Hello Demo <${''}/title>
        <${''}meta name="viewport" content= "width=device-width, initial-scale=1.0">
        <${''}link rel="stylesheet" href="css/style.css'>
      <${''}/head>
      <${''}body>
        <${''}h1> Hello <${''}/h1>
        <${''}h3> Demo App <${''}/h3>
        <${''}script src="js/main.js"><${''}/script>
      <${''}/body>
    <${''}/html>
    ==== js/main.js ====
    console.log("Hello")
    ==== css/style.css ====
    body{
      font-family: sans-serif;
    }
    ====
    More comments go here in an empty file name
    Five equals signs escapes an equals sign line
    ===== This is escaped so it will stay in the content.
  `
  console.info('src', src.split('\n').join('||'))
  let doc = readMultidoc(src)
  console.log('test multidoc', JSON.stringify(doc))
  console.info('test multidoc', doc)
  console.info('write', writeMultidoc(doc))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment